![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/soundstudiopro.com/private_html/includes/ |
<?php
// Include translation system
require_once __DIR__ . '/translations.php';
// Include security tracking (logs page visits for online users display)
require_once __DIR__ . '/security_tracking.php';
// Get current language
$current_lang = getCurrentLanguage();
// Get current page for active navigation
// Allow pages to override this, but provide a fallback
if (!isset($current_page)) {
$current_page = basename(isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : 'index.php', '.php');
}
// Facebook App ID - Set this to your actual Facebook App ID
// You can override this per page by setting $fb_app_id before including header.php
if (!isset($fb_app_id)) {
// TODO: Replace with your actual Facebook App ID
// Get it from: https://developers.facebook.com/apps/
$fb_app_id = ''; // Leave empty if you don't have a Facebook App ID yet
}
// Compute a sane default canonical URL (path only, no query strings)
$host = $_SERVER['HTTP_HOST'] ?? 'soundstudiopro.com';
$requestUri = $_SERVER['REQUEST_URI'] ?? '/';
$parsedUri = parse_url($requestUri);
$canonicalPath = $parsedUri['path'] ?? '/';
if ($canonicalPath === '') {
$canonicalPath = '/';
}
$defaultCanonicalUrl = 'https://' . $host . $canonicalPath;
if (!isset($canonical_url) || !filter_var($canonical_url, FILTER_VALIDATE_URL)) {
$canonical_url = $defaultCanonicalUrl;
}
// Refresh user credits and profile image from database if user is logged in
// This works for both regular users and when admin is impersonating (fetches impersonated user's data)
if (isset($_SESSION['user_id'])) {
require_once __DIR__ . '/../config/database.php';
$pdo = getDBConnection();
// Always fetch data for the current user_id (which is the impersonated user when impersonating)
$stmt = $pdo->prepare("
SELECT
u.credits,
COALESCE(up.profile_image, u.profile_image, NULL) as profile_image,
up.profile_position
FROM users u
LEFT JOIN user_profiles up ON u.id = up.user_id
WHERE u.id = ?
");
$stmt->execute([$_SESSION['user_id']]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user) {
// Only update credits if not impersonating (to preserve admin's original credits)
// But always update profile_image to show the impersonated user's image
if (!isset($_SESSION['is_impersonating']) || !$_SESSION['is_impersonating']) {
$_SESSION['credits'] = $user['credits'];
// Update last_activity timestamp for online tracking (only if not impersonating)
try {
$pdo->exec("ALTER TABLE users ADD COLUMN IF NOT EXISTS last_activity TIMESTAMP NULL DEFAULT NULL");
$updateStmt = $pdo->prepare("UPDATE users SET last_activity = NOW() WHERE id = ?");
$updateStmt->execute([$_SESSION['user_id']]);
} catch (Exception $e) {
// Ignore errors - column might not exist yet or update might fail
}
}
// Always set profile_image for the current user (impersonated user when impersonating)
if (!empty($user['profile_image']) && trim($user['profile_image']) !== '') {
$_SESSION['profile_image'] = trim($user['profile_image']);
} else {
// Clear profile_image from session if it's empty/null
unset($_SESSION['profile_image']);
}
// Set profile_position for object-position styling
$_SESSION['profile_position'] = $user['profile_position'] ?? 'center center';
// PERMANENT FIX: Check if subscription needs syncing from Stripe
// This ensures subscriptions are synced even if webhooks fail
// IMMEDIATE SYNC: Sync right away if needed (non-blocking, fast timeout)
try {
require_once __DIR__ . '/../utils/subscription_helpers.php';
require_once __DIR__ . '/../utils/sync_user_subscription.php';
// Get user's Stripe customer ID
$user_stmt = $pdo->prepare("SELECT stripe_customer_id FROM users WHERE id = ?");
$user_stmt->execute([$_SESSION['user_id']]);
$user_data = $user_stmt->fetch(PDO::FETCH_ASSOC);
if ($user_data && !empty($user_data['stripe_customer_id'])) {
$has_active_sub = hasActiveSubscription($_SESSION['user_id']);
$effective_plan = getEffectivePlan($_SESSION['user_id']);
// If user has Stripe customer ID but no active subscription in DB and plan is 'free',
// IMMEDIATELY sync (non-blocking, fast timeout)
if (!$has_active_sub && $effective_plan === 'free') {
// Only sync once per session to avoid repeated API calls
if (!isset($_SESSION['subscription_sync_attempted'])) {
$_SESSION['subscription_sync_attempted'] = true;
// Trigger immediate sync (non-blocking, 5 second timeout)
// This ensures user's plan is fixed within seconds, not 24 hours
$sync_result = syncUserSubscriptionFromStripe($_SESSION['user_id'], $user_data['stripe_customer_id']);
if ($sync_result['success'] && $sync_result['synced'] > 0) {
// Successfully synced - clear flag
unset($_SESSION['subscription_sync_attempted']);
error_log("Header: Successfully synced {$sync_result['synced']} subscription(s) for user {$_SESSION['user_id']}");
} else {
// Sync failed or no subscriptions found - will retry on next page load or via cron
error_log("Header: Subscription sync for user {$_SESSION['user_id']}: {$sync_result['message']}");
}
}
} else {
// Subscription is properly synced - clear flags
unset($_SESSION['subscription_sync_needed']);
unset($_SESSION['subscription_sync_attempted']);
}
}
} catch (Exception $e) {
// Silently fail - don't break page load if subscription check fails
error_log("Header subscription sync check error: " . $e->getMessage());
}
}
}
// Get return URL for language switcher
$return_url = urlencode($_SERVER['REQUEST_URI'] ?? '/');
// Pre-calculate notification count for header badges
$nav_notification_count = 0;
if (isset($_SESSION['user_id'])) {
try {
$pdo = getDBConnection();
if ($pdo) {
// Ensure notification tracking tables exist
try {
$pdo->exec("
CREATE TABLE IF NOT EXISTS notification_reads (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
notification_type ENUM('friend_request', 'like', 'comment', 'artist_rating', 'track_rating', 'follow') NOT NULL,
notification_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY unique_notification (user_id, notification_type, notification_id),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_user_type (user_id, notification_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
");
$pdo->exec("
CREATE TABLE IF NOT EXISTS notification_clear_state (
user_id INT PRIMARY KEY,
last_cleared_at TIMESTAMP NULL DEFAULT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
");
} catch (PDOException $e) {
// ignore table creation race conditions
}
$stmt = $pdo->prepare("SELECT last_cleared_at FROM notification_clear_state WHERE user_id = ?");
$stmt->execute([$_SESSION['user_id']]);
$nav_last_cleared = $stmt->fetchColumn() ?: null;
// Friend requests
$stmt = $pdo->prepare("
SELECT COUNT(*)
FROM user_friends uf
LEFT JOIN notification_reads nr ON nr.user_id = ? AND nr.notification_type = 'friend_request' AND nr.notification_id = uf.id
WHERE uf.friend_id = ? AND uf.status = 'pending' AND nr.id IS NULL
AND (? IS NULL OR uf.created_at > ?)
");
$stmt->execute([$_SESSION['user_id'], $_SESSION['user_id'], $nav_last_cleared, $nav_last_cleared]);
$friend_requests = (int)$stmt->fetchColumn();
// Likes
$stmt = $pdo->prepare("
SELECT COUNT(*)
FROM track_likes tl
INNER JOIN music_tracks mt ON tl.track_id = mt.id
LEFT JOIN notification_reads nr ON nr.user_id = ? AND nr.notification_type = 'like' AND nr.notification_id = tl.id
WHERE mt.user_id = ? AND tl.user_id != ? AND nr.id IS NULL
AND (? IS NULL OR tl.created_at > ?)
");
$stmt->execute([$_SESSION['user_id'], $_SESSION['user_id'], $_SESSION['user_id'], $nav_last_cleared, $nav_last_cleared]);
$likes = (int)$stmt->fetchColumn();
// Comments
$stmt = $pdo->prepare("
SELECT COUNT(*)
FROM track_comments tc
INNER JOIN music_tracks mt ON tc.track_id = mt.id
LEFT JOIN notification_reads nr ON nr.user_id = ? AND nr.notification_type = 'comment' AND nr.notification_id = tc.id
WHERE mt.user_id = ? AND tc.user_id != ? AND nr.id IS NULL
AND (? IS NULL OR tc.created_at > ?)
");
$stmt->execute([$_SESSION['user_id'], $_SESSION['user_id'], $_SESSION['user_id'], $nav_last_cleared, $nav_last_cleared]);
$comments = (int)$stmt->fetchColumn();
// Artist ratings
$stmt = $pdo->prepare("
SELECT COUNT(*)
FROM artist_ratings ar
LEFT JOIN notification_reads nr
ON nr.user_id = ?
AND nr.notification_type = 'artist_rating'
AND nr.notification_id = ar.id
WHERE ar.artist_id = ?
AND ar.user_id != ?
AND nr.id IS NULL
AND (? IS NULL OR ar.updated_at > ?)
");
$stmt->execute([$_SESSION['user_id'], $_SESSION['user_id'], $_SESSION['user_id'], $nav_last_cleared, $nav_last_cleared]);
$artist_ratings = (int)$stmt->fetchColumn();
// Track ratings
$stmt = $pdo->prepare("
SELECT COUNT(*)
FROM track_ratings tr
INNER JOIN music_tracks mt ON tr.track_id = mt.id
LEFT JOIN notification_reads nr
ON nr.user_id = ?
AND nr.notification_type = 'track_rating'
AND nr.notification_id = tr.id
WHERE mt.user_id = ?
AND tr.user_id != ?
AND nr.id IS NULL
AND (? IS NULL OR tr.updated_at > ?)
");
$stmt->execute([$_SESSION['user_id'], $_SESSION['user_id'], $_SESSION['user_id'], $nav_last_cleared, $nav_last_cleared]);
$track_ratings = (int)$stmt->fetchColumn();
// Follows
$stmt = $pdo->prepare("
SELECT COUNT(*)
FROM user_follows uf
LEFT JOIN notification_reads nr
ON nr.user_id = ?
AND nr.notification_type = 'follow'
AND nr.notification_id = uf.id
WHERE uf.following_id = ?
AND uf.follower_id != ?
AND nr.id IS NULL
AND (? IS NULL OR uf.created_at > ?)
");
$stmt->execute([$_SESSION['user_id'], $_SESSION['user_id'], $_SESSION['user_id'], $nav_last_cleared, $nav_last_cleared]);
$follows = (int)$stmt->fetchColumn();
// Purchase notifications
$purchase_notifications = 0;
$ticket_notifications = 0;
try {
require_once __DIR__ . '/../utils/artist_notifications.php';
$purchase_notifications = getArtistPurchaseNotificationsCount($_SESSION['user_id']);
$ticket_notifications = getOrganizerTicketNotificationsCount($_SESSION['user_id']);
} catch (Exception $e) {
error_log("Error counting purchase notifications in header: " . $e->getMessage());
}
$nav_notification_count = $friend_requests + $likes + $comments + $artist_ratings + $track_ratings + $follows + $purchase_notifications + $ticket_notifications;
}
} catch (Exception $e) {
// silently ignore header badge errors
}
}
$headerDisplayName = trim($_SESSION['user_name'] ?? 'User');
if ($headerDisplayName === '') {
$headerDisplayName = 'User';
}
// Shared cart count for header/cart badges
$total_cart_items = 0;
if (!empty($_SESSION['cart'])) {
foreach ($_SESSION['cart'] as $item) {
$total_cart_items += (int)($item['quantity'] ?? 0);
}
}
if (!empty($_SESSION['credit_cart'])) {
foreach ($_SESSION['credit_cart'] as $item) {
$total_cart_items += (int)($item['quantity'] ?? 0);
}
}
if (!empty($_SESSION['ticket_cart'])) {
foreach ($_SESSION['ticket_cart'] as $item) {
$total_cart_items += (int)($item['quantity'] ?? 0);
}
}
?>
<!doctype html>
<html lang="<?= $current_lang ?>">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
<title><?= $page_title ?? 'SoundStudioPro - Advanced AI Music Creation Platform' ?></title>
<meta name="description" content="<?= $page_description ?? 'Create professional, watermark-free music with AI. Generate original tracks, lyrics, and music videos for your projects.' ?>">
<!-- Open Graph / Social Media Meta Tags -->
<meta property="og:type" content="<?= $og_type ?? 'website' ?>">
<meta property="og:title" content="<?= $og_title ?? ($page_title ?? 'SoundStudioPro - AI Music Creation') ?>">
<meta property="og:description" content="<?= $og_description ?? ($page_description ?? 'Discover amazing AI-generated music on SoundStudioPro') ?>">
<meta property="og:url" content="<?= $og_url ?? ('https://' . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'soundstudiopro.com') . (isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '')) ?>">
<?php
// Determine the OG image URL
// For track pages (music.song), always prioritize the track image
// For other pages, use the provided $og_image or fall back to the default static OG image
if (isset($og_type) && $og_type === 'music.song') {
// Track page - use track image if set, otherwise fallback (track.php should always set it)
// Ensure the image URL is absolute (Facebook requires absolute URLs)
if (!empty($og_image)) {
$final_og_image = $og_image;
// Ensure it's absolute
if (strpos($final_og_image, 'http://') !== 0 && strpos($final_og_image, 'https://') !== 0) {
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'soundstudiopro.com';
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$final_og_image = $protocol . '://' . $host . '/' . ltrim($og_image, '/');
}
} else {
$final_og_image = 'https://' . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'soundstudiopro.com') . '/uploads/track_covers/track_45_1754191923.jpg';
}
$is_default_og = false; // Track images have variable dimensions, let Facebook detect them
} elseif (!empty($og_image)) {
// Other page with custom OG image
$final_og_image = $og_image;
$is_default_og = strpos($final_og_image, '/assets/images/og-image.png') !== false;
} else {
// Fallback to default static OG image (homepage)
$final_og_image = 'https://' . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'soundstudiopro.com') . '/assets/images/og-image.png';
$is_default_og = true;
}
?>
<meta property="og:image" content="<?= htmlspecialchars($final_og_image, ENT_QUOTES, 'UTF-8') ?>">
<?php if ($is_default_og): ?>
<meta property="og:image:width" content="1536">
<meta property="og:image:height" content="1024">
<?php elseif (isset($og_type) && $og_type === 'music.song'): ?>
<!-- Track images - let Facebook detect dimensions, but provide type hint -->
<meta property="og:image:type" content="image/jpeg">
<?php endif; ?>
<meta property="og:image:alt" content="<?= htmlspecialchars($og_title ?? ($page_title ?? 'SoundStudioPro'), ENT_QUOTES, 'UTF-8') ?>">
<!-- Ensure image URL uses https for Facebook compatibility -->
<?php if (strpos($final_og_image, 'http://') === 0): ?>
<meta property="og:image:secure_url" content="<?= htmlspecialchars(str_replace('http://', 'https://', $final_og_image), ENT_QUOTES, 'UTF-8') ?>">
<?php endif; ?>
<meta property="og:site_name" content="SoundStudioPro">
<?php if (!empty($fb_app_id)): ?>
<meta property="fb:app_id" content="<?= htmlspecialchars($fb_app_id, ENT_QUOTES, 'UTF-8') ?>">
<?php endif; ?>
<!-- Twitter Card Meta Tags -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="<?= $twitter_title ?? ($og_title ?? ($page_title ?? 'SoundStudioPro')) ?>">
<meta name="twitter:description" content="<?= $twitter_description ?? ($og_description ?? ($page_description ?? 'Amazing AI music creation platform')) ?>">
<meta name="twitter:image" content="<?= $twitter_image ?? ($og_image ?? 'https://' . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'soundstudiopro.com') . '/assets/images/og-image.png') ?>">
<!-- Additional Meta Tags -->
<meta name="robots" content="<?= $robots ?? ($additional_meta['robots'] ?? 'index, follow') ?>">
<?php if (isset($canonical_url)): ?>
<link rel="canonical" href="<?= htmlspecialchars($canonical_url, ENT_QUOTES, 'UTF-8') ?>">
<?php else: ?>
<link rel="canonical" href="<?= htmlspecialchars('https://' . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'soundstudiopro.com') . (isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''), ENT_QUOTES, 'UTF-8') ?>">
<?php endif; ?>
<?php if (isset($additional_meta['keywords'])): ?>
<meta name="keywords" content="<?= htmlspecialchars($additional_meta['keywords'], ENT_QUOTES, 'UTF-8') ?>">
<?php endif; ?>
<?php if (isset($additional_meta['author'])): ?>
<meta name="author" content="<?= htmlspecialchars($additional_meta['author'], ENT_QUOTES, 'UTF-8') ?>">
<?php endif; ?>
<?php if (isset($additional_meta['theme-color'])): ?>
<meta name="theme-color" content="<?= htmlspecialchars($additional_meta['theme-color'], ENT_QUOTES, 'UTF-8') ?>">
<?php endif; ?>
<?php if (isset($track_id)): ?>
<!-- Track-Specific Open Graph Meta Tags -->
<meta property="music:song" content="<?= $track_url ?? '' ?>">
<meta property="music:duration" content="<?= $track_duration ?? '' ?>">
<meta property="music:musician" content="<?= $track_artist ?? '' ?>">
<?php endif; ?>
<!-- Fonts - Optimized: Async loading with fallback -->
<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;800;900&display=swap" rel="stylesheet" media="print" onload="this.media='all'">
<noscript><link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet"></noscript>
<script>
// Helper for async CSS loading
!function(e){"use strict";var t=function(t,n,o){var i,r=e.document,a=r.createElement("link");if(n)i=n;else{var l=(r.body||r.getElementsByTagName("head")[0]).childNodes;i=l[l.length-1]}var d=r.styleSheets;a.rel="stylesheet",a.href=t,a.media="only x",function e(t){if(r.body)return t();setTimeout(function(){e(t)})}(function(){i.parentNode.insertBefore(a,n?i:i.nextSibling)});var f=function(e){for(var t=a.href,n=d.length;n--;)if(d[n].href===t)return e();setTimeout(function(){f(e)})};return a.addEventListener&&a.addEventListener("load",function(){this.media=o||"all"}),a.onloadcssdefined=f,f(function(){a.media!==o&&(a.media=o||"all")}),a};"undefined"!=typeof exports?exports.loadCSS=t:e.loadCSS=t}("undefined"!=typeof global?global:this);
</script>
<!-- Icons - Optimized: Async loading -->
<link rel="preload" href="/assets/fontawesome/fontawesome-free-6.5.1-web/css/all.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/assets/fontawesome/fontawesome-free-6.5.1-web/css/all.min.css"></noscript>
<!-- Mobile CSS - Optimized: Preload for faster rendering -->
<link rel="preload" href="/assets/css/mobile.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/assets/css/mobile.css"></noscript>
<script src="/assets/js/wishlist.js" defer></script>
<style>
/* Reset & Base Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-size: 62.5%;
scroll-behavior: smooth;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
color: #1a202c;
background: #0a0a0a;
min-height: 100vh;
overflow-x: hidden;
padding-top: 100px; /* Ensure content doesn't overlap fixed header */
}
/* Ensure main content stays below header */
main {
position: relative;
z-index: 1;
}
/* Mobile-specific global player adjustments */
@media (max-width: 768px) {
body {
padding-bottom: 140px; /* Space for global player */
}
/* Ensure global player is above all content on mobile */
.global-bottom-player {
z-index: 10000 !important;
}
/* Prevent body scroll when player is minimized */
body.player-minimized {
padding-bottom: 80px;
}
}
/* Extra small mobile devices */
@media (max-width: 360px) {
body {
padding-bottom: 120px;
}
body.player-minimized {
padding-bottom: 70px;
}
}
/* Custom Scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #1a1a1a;
}
::-webkit-scrollbar-thumb {
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 4px;
}
/* Container & Layout */
.container {
max-width: 140rem;
margin: 0 auto;
padding: 0 2rem;
}
/* Header */
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
background: rgba(10, 10, 10, 0.98);
backdrop-filter: blur(20px);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
z-index: 9999;
padding: 2rem 0;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
will-change: transform;
transform: translateZ(0);
height: auto;
min-height: 80px;
overflow: visible;
margin: 0;
}
/* Ensure header content is always on top */
.header-content {
position: relative;
z-index: 10000;
display: flex;
justify-content: space-between;
align-items: center;
max-width: 1400px;
margin: 0 auto;
padding: 0 2rem;
gap: 1rem;
height: 100%;
min-height: 80px;
}
/* Prevent any content from overlapping the header */
.header * {
position: relative;
z-index: 10000;
}
/* Ensure dropdown doesn't affect header layout */
.header {
contain: layout style;
}
.header-left {
display: flex;
align-items: center;
}
.header-right {
display: flex;
align-items: center;
gap: 1rem;
flex-shrink: 0;
}
/* Language Switcher */
.language-switcher {
position: relative;
display: flex;
align-items: center;
}
.language-switcher-btn {
display: flex;
align-items: center;
gap: 0.6rem;
padding: 0.75rem 1.25rem;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2));
border: 1px solid rgba(102, 126, 234, 0.3);
border-radius: 12px;
color: #ffffff;
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
text-decoration: none;
position: relative;
overflow: hidden;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.2);
}
.language-switcher-btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s ease;
}
.language-switcher-btn:hover::before {
left: 100%;
}
.language-switcher-btn:hover {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.35), rgba(118, 75, 162, 0.35));
border-color: rgba(102, 126, 234, 0.5);
transform: translateY(-2px) scale(1.02);
box-shadow: 0 6px 25px rgba(102, 126, 234, 0.4);
}
.language-switcher-btn:active {
transform: translateY(0) scale(0.98);
}
.language-switcher-btn i {
font-size: 1.3rem;
animation: globeRotate 3s linear infinite;
background: linear-gradient(135deg,
#002654 0%, /* French blue */
#0055a4 20%, /* French blue lighter */
#3b82f6 30%, /* English blue */
#ffffff 50%, /* White (both flags) */
#dc2626 70%, /* English red */
#ef4135 80%, /* French red */
#002654 100% /* Back to French blue */
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
filter: drop-shadow(0 0 4px rgba(102, 126, 234, 0.5));
position: relative;
}
@keyframes globeRotate {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.language-switcher-btn span {
background: linear-gradient(135deg, #ffffff, #a0aec0);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
font-weight: 700;
letter-spacing: 1px;
}
/* Mobile quick action container */
.mobile-quick-actions {
display: none;
align-items: center;
gap: 0.5rem;
margin: 0;
padding: 0;
}
.mobile-search-button {
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
border-radius: 14px;
border: 1px solid rgba(102, 126, 234, 0.3);
background: linear-gradient(135deg, rgba(102, 126, 234, 0.12), rgba(118, 75, 162, 0.12));
color: #667eea;
font-size: 1.4rem;
position: relative;
transition: all 0.3s ease;
cursor: pointer;
pointer-events: auto;
z-index: 1000;
padding: 0;
margin: 0;
}
.mobile-search-button:hover {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2));
transform: scale(1.05);
}
.mobile-cart-button {
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
border-radius: 14px;
border: 1px solid rgba(102, 126, 234, 0.3);
background: linear-gradient(135deg, rgba(102, 126, 234, 0.12), rgba(118, 75, 162, 0.12));
color: #667eea;
font-size: 1.8rem;
position: relative;
transition: all 0.3s ease;
}
.mobile-cart-button .cart-count {
position: absolute;
top: -6px;
right: -6px;
width: 22px;
height: 22px;
border-radius: 50%;
background: linear-gradient(135deg, #f56565, #ed64a6);
color: white;
font-size: 1.1rem;
font-weight: 700;
display: none;
align-items: center;
justify-content: center;
box-shadow: 0 4px 10px rgba(245, 101, 101, 0.4);
}
/* Global Search Overlay */
.global-search-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(15, 15, 15, 0.95);
backdrop-filter: blur(10px);
z-index: 10000;
animation: fadeIn 0.3s ease;
}
.global-search-overlay.active {
display: flex;
flex-direction: column;
}
.global-search-container {
width: 100%;
max-width: 800px;
margin: 80px auto 0;
padding: 20px;
display: flex;
flex-direction: column;
gap: 20px;
}
.global-search-header {
display: flex;
align-items: center;
gap: 15px;
margin-bottom: 10px;
}
.global-search-input-wrapper {
flex: 1;
position: relative;
}
.global-search-input {
width: 100%;
padding: 16px 50px 16px 20px;
font-size: 1.1rem;
border: 2px solid rgba(102, 126, 234, 0.3);
border-radius: 14px;
background: rgba(30, 30, 30, 0.8);
color: #fff;
outline: none;
transition: all 0.3s ease;
}
.global-search-input:focus {
border-color: #667eea;
background: rgba(40, 40, 40, 0.9);
box-shadow: 0 0 20px rgba(102, 126, 234, 0.3);
}
.global-search-input::placeholder {
color: rgba(255, 255, 255, 0.5);
}
.global-search-close {
width: 48px;
height: 48px;
border-radius: 14px;
border: 2px solid rgba(102, 126, 234, 0.3);
background: rgba(30, 30, 30, 0.8);
color: #fff;
font-size: 1.2rem;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.global-search-close:hover {
background: rgba(102, 126, 234, 0.2);
border-color: #667eea;
}
.global-search-results {
max-height: calc(100vh - 200px);
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 20px;
}
.global-search-section {
background: rgba(30, 30, 30, 0.6);
border-radius: 14px;
padding: 20px;
border: 1px solid rgba(102, 126, 234, 0.2);
}
.global-search-section-title {
font-size: 1rem;
font-weight: 600;
color: #a5b4fc;
margin-bottom: 15px;
text-transform: uppercase;
letter-spacing: 1px;
}
.global-search-item {
display: flex;
align-items: center;
gap: 15px;
padding: 12px;
border-radius: 10px;
cursor: pointer;
transition: all 0.2s ease;
margin-bottom: 8px;
text-decoration: none;
color: #fff;
}
.global-search-item:hover {
background: rgba(102, 126, 234, 0.15);
transform: translateX(5px);
}
.global-search-item-image {
width: 50px;
height: 50px;
min-width: 50px;
min-height: 50px;
border-radius: 8px;
background: linear-gradient(135deg, #667eea, #764ba2);
background-size: cover;
background-position: center;
background-repeat: no-repeat;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 1.2rem;
flex-shrink: 0;
position: relative;
overflow: hidden;
}
.global-search-item-image img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: inherit;
}
.global-search-item-image i {
position: relative;
z-index: 1;
}
.global-search-item-image[style*="background-image"] {
background-color: rgba(102, 126, 234, 0.2);
}
.global-search-item-image[style*="background-image"] i {
display: none;
}
.global-search-item-image:has(img) i {
display: none;
}
.global-search-item-content {
flex: 1;
min-width: 0;
}
.global-search-item-title {
font-size: 0.95rem;
font-weight: 600;
color: #fff;
margin-bottom: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.global-search-item-subtitle {
font-size: 0.85rem;
color: rgba(255, 255, 255, 0.6);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.global-search-item-icon {
color: #667eea;
font-size: 1.2rem;
}
.global-search-empty {
text-align: center;
padding: 40px 20px;
color: rgba(255, 255, 255, 0.5);
}
.global-search-loading {
text-align: center;
padding: 40px 20px;
color: #667eea;
}
/* Search History */
.global-search-history {
max-height: calc(100vh - 200px);
overflow-y: auto;
}
.global-search-history-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 15px;
border-radius: 10px;
cursor: pointer;
transition: all 0.2s ease;
margin-bottom: 6px;
color: #fff;
background: rgba(30, 30, 30, 0.4);
border: 1px solid rgba(102, 126, 234, 0.1);
}
.global-search-history-item:hover {
background: rgba(102, 126, 234, 0.15);
border-color: rgba(102, 126, 234, 0.3);
transform: translateX(5px);
}
.global-search-history-item i.fa-clock {
color: #a5b4fc;
font-size: 0.9rem;
flex-shrink: 0;
}
.global-search-history-item span {
flex: 1;
font-size: 0.95rem;
color: rgba(255, 255, 255, 0.9);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.history-delete-btn {
background: transparent;
border: none;
color: rgba(255, 255, 255, 0.5);
cursor: pointer;
padding: 4px 8px;
border-radius: 6px;
transition: all 0.2s ease;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
}
.history-delete-btn:hover {
background: rgba(245, 101, 101, 0.2);
color: #f56565;
}
.history-delete-btn i {
font-size: 0.85rem;
}
@media (max-width: 768px) {
.global-search-history-item {
padding: 10px 12px;
font-size: 0.9rem;
}
.global-search-history-item span {
font-size: 0.9rem;
}
}
@media (max-width: 768px) {
.global-search-container {
margin: 60px auto 0;
padding: 15px;
}
.global-search-input {
padding: 14px 45px 14px 18px;
font-size: 1rem;
}
.global-search-close {
width: 44px;
height: 44px;
font-size: 1.1rem;
}
.global-search-results {
max-height: calc(100vh - 180px);
}
.global-search-section {
padding: 15px;
}
.global-search-item {
padding: 10px;
}
.global-search-item-image {
width: 45px;
height: 45px;
font-size: 1rem;
}
}
@media (max-width: 768px) {
.language-switcher-btn {
padding: 0.5rem 0.8rem;
font-size: 1.2rem;
}
.language-switcher-btn span {
display: none;
}
}
/* Responsive adjustments for smaller screens */
@media (max-width: 1200px) {
.nav {
gap: 0.8rem;
padding: 0 0.5rem;
}
.nav-icon-container {
width: 44px;
height: 44px;
}
.nav-icon {
font-size: 1.8rem !important;
}
.quick-create-btn {
padding: 0.5rem 1rem;
font-size: 0.9rem;
}
.create-text {
display: none;
}
.quick-create-btn i {
font-size: 1.4rem;
}
}
/* Tablet portrait and smaller - show mobile menu */
@media (max-width: 1024px) {
.nav {
display: none;
}
.user-menu,
.header-right {
display: none;
}
.mobile-quick-actions {
display: flex !important;
}
.mobile-menu-toggle {
display: flex;
visibility: visible !important;
opacity: 1 !important;
position: relative !important;
z-index: 10001 !important;
}
}
/* Mobile-First Header Redesign */
@media (max-width: 768px) {
.header {
padding: 1rem 0;
min-height: 80px;
}
.header-content {
padding: 0 1rem;
flex-direction: row;
gap: 1rem;
align-items: center;
justify-content: space-between;
min-height: 80px;
}
.header-left {
flex: 1;
}
.header-right {
flex-shrink: 0;
}
/* Logo Section */
.logo {
display: flex;
justify-content: flex-start;
align-items: center;
padding: 0.5rem 0;
}
.logo-text {
font-size: 2.2rem;
}
.logo-icon {
font-size: 2.8rem;
}
/* Navigation Section */
.nav {
display: none;
}
.nav-icon-container {
width: 48px;
height: 48px;
border-radius: 12px;
background: rgba(102, 126, 234, 0.1);
border: 1px solid rgba(102, 126, 234, 0.2);
transition: all 0.3s ease;
}
.nav-icon-container:hover {
background: rgba(102, 126, 234, 0.2);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}
.nav-icon {
font-size: 1.8rem !important;
color: #667eea;
}
/* Action Buttons Section */
.header-right {
display: flex;
justify-content: center;
gap: 1rem;
padding: 0.5rem 0;
}
.header-right .btn {
padding: 1rem 1.5rem;
font-size: 1.4rem;
border-radius: 12px;
font-weight: 600;
min-width: 120px;
text-align: center;
transition: all 0.3s ease;
}
.btn-ghost {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: #ffffff;
}
.btn-ghost:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-2px);
}
.btn-secondary {
background: linear-gradient(135deg, #667eea, #764ba2);
border: none;
color: #ffffff;
}
.btn-secondary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.btn-primary {
background: linear-gradient(135deg, #48bb78, #38a169);
border: none;
color: #ffffff;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(72, 187, 120, 0.4);
}
/* User Menu for Logged-in Users */
.user-menu {
display: flex;
justify-content: center;
gap: 1rem;
padding: 0.5rem 0;
}
.profile-section {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.profile-avatar {
width: 4rem;
height: 4rem;
font-size: 1.8rem;
border-radius: 50%;
background: linear-gradient(135deg, #667eea, #764ba2);
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-weight: 700;
}
.profile-info {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.profile-username {
font-size: 1.4rem;
font-weight: 600;
color: #ffffff;
}
.profile-credits {
font-size: 1.2rem;
color: #48bb78;
font-weight: 500;
}
/* Hide desktop elements on mobile */
.mobile-menu-toggle {
display: none;
}
.mobile-menu {
display: none;
}
}
/* Extra Small Mobile Devices */
@media (max-width: 480px) {
.header-content {
padding: 0 0.5rem;
gap: 0.8rem;
}
.logo-text {
font-size: 2rem;
}
.nav {
gap: 0.8rem;
}
.nav-icon-container {
width: 44px;
height: 44px;
}
.nav-icon {
font-size: 1.6rem !important;
}
.header-right {
gap: 0.8rem;
}
.header-right .btn {
padding: 0.8rem 1.2rem;
font-size: 1.2rem;
min-width: 100px;
}
.profile-avatar {
width: 3.5rem;
height: 3.5rem;
font-size: 1.6rem;
}
.profile-username {
font-size: 1.2rem;
}
.profile-credits {
font-size: 1rem;
}
}
.logo {
display: flex;
align-items: center;
gap: 1.2rem;
text-decoration: none;
color: white;
}
.logo-icon {
font-size: 3rem;
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.logo-text {
font-size: 2.4rem;
font-weight: 800;
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* PREMIUM SOCIAL NETWORK NAVIGATION */
.nav {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0 0.5rem;
flex-shrink: 0;
}
.nav-icon-link {
position: relative;
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
border-radius: 16px;
padding: 0.8rem;
}
.nav-icon-container {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
border-radius: 16px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
overflow: visible; /* Allow badges to show outside container */
}
.nav-icon-link {
position: relative; /* Ensure badge can be positioned relative to link */
}
.nav-icon {
font-size: 2rem !important;
color: #a0aec0;
transition: all 0.4s ease;
z-index: 2;
position: relative;
}
.nav-icon-glow {
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
background: radial-gradient(circle, rgba(102, 126, 234, 0.4) 0%, transparent 70%);
border-radius: 50%;
transform: translate(-50%, -50%);
transition: all 0.4s ease;
z-index: 1;
}
/* Hover Effects */
.nav-icon-link:hover .nav-icon-container {
background: rgba(102, 126, 234, 0.15);
border-color: rgba(102, 126, 234, 0.3);
transform: translateY(-3px) scale(1.05);
box-shadow:
0 8px 25px rgba(102, 126, 234, 0.2),
0 4px 15px rgba(0, 0, 0, 0.1);
}
.nav-icon-link:hover .nav-icon {
color: #667eea;
transform: scale(1.1);
}
.nav-icon-link:hover .nav-icon-glow {
width: 60px;
height: 60px;
}
/* Active State */
.nav-icon-link.active .nav-icon-container {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2));
border-color: rgba(102, 126, 234, 0.5);
box-shadow:
0 8px 25px rgba(102, 126, 234, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.2);
}
.nav-icon-link.active .nav-icon {
color: #ffd700;
text-shadow: 0 0 20px rgba(255, 215, 0, 0.5);
}
/* Notification Badges */
.notification-badge {
position: absolute;
top: -6px;
right: -6px;
background: linear-gradient(135deg, #ff4757, #ff3742);
color: #fff;
font-weight: 800;
min-width: 24px;
height: 24px;
padding: 0 6px;
border-radius: 999px;
display: flex;
align-items: center;
justify-content: center;
border: 2px solid #05050a;
z-index: 10001; /* Ensure badge is above all other elements */
animation: notificationPulse 2s infinite;
line-height: 1;
box-sizing: border-box;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
letter-spacing: 0.02em;
font-size: 0.82rem;
}
.notification-badge.double-digit {
min-width: 28px;
padding: 0 8px;
font-size: 0.78rem;
}
.notification-badge.triple-digit {
min-width: 32px;
padding: 0 9px;
font-size: 0.75rem;
}
.notification-badge.pulse {
animation: notificationPulse 1.5s infinite;
}
@keyframes notificationPulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.15); }
}
/* Special Icon Animations */
.trending-icon {
animation: fireFlicker 2s ease-in-out infinite alternate;
}
@keyframes fireFlicker {
0% {
color: #ff6b35;
text-shadow: 0 0 10px rgba(255, 107, 53, 0.5);
}
100% {
color: #ff8e53;
text-shadow: 0 0 20px rgba(255, 142, 83, 0.8);
}
}
.notification-icon {
animation: bellRing 3s ease-in-out infinite;
}
@keyframes bellRing {
0%, 90%, 100% { transform: rotate(0deg); }
5%, 15% { transform: rotate(15deg); }
10% { transform: rotate(-15deg); }
}
.creator-icon {
animation: crownGlow 3s ease-in-out infinite alternate;
}
@keyframes crownGlow {
0% {
color: #ffd700;
text-shadow: 0 0 10px rgba(255, 215, 0, 0.5);
}
100% {
color: #ffed4e;
text-shadow: 0 0 25px rgba(255, 237, 78, 0.8);
}
}
.party-icon {
animation: partyGlow 3s ease-in-out infinite alternate;
}
@keyframes partyGlow {
0% {
color: #9f7aea;
text-shadow: 0 0 15px rgba(159, 122, 234, 0.6);
}
100% {
color: #b794f4;
text-shadow: 0 0 25px rgba(183, 148, 244, 0.9);
}
}
.charts-icon {
animation: chartPulse 2.5s ease-in-out infinite;
}
@keyframes chartPulse {
0%, 100% {
color: #4ecdc4;
text-shadow: 0 0 10px rgba(78, 205, 196, 0.5);
}
50% {
color: #44a08d;
text-shadow: 0 0 20px rgba(68, 160, 141, 0.8);
}
}
.community-icon {
animation: communityGlow 2s ease-in-out infinite alternate;
}
@keyframes communityGlow {
0% {
color: #ff6b9d;
text-shadow: 0 0 15px rgba(255, 107, 157, 0.6);
}
100% {
color: #ff8fab;
text-shadow: 0 0 25px rgba(255, 143, 171, 0.9);
}
}
/* Enhanced Community Icon Hover Effect */
.nav-icon-link:hover .community-icon {
animation: communityHover 0.3s ease-in-out;
}
@keyframes communityHover {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
/* Enhanced Events Icon Hover Effect */
.nav-icon-link:hover .party-icon {
animation: partyHover 0.3s ease-in-out;
}
@keyframes partyHover {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
/* Community Pulse Animation */
.community-pulse {
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border: 3px solid rgba(255, 107, 157, 0.8);
border-radius: 50%;
transform: translate(-50%, -50%);
animation: communityPulse 2s infinite;
}
@keyframes communityPulse {
0% {
width: 0;
height: 0;
opacity: 1;
}
100% {
width: 60px;
height: 60px;
opacity: 0;
}
}
/* Events Pulse Animation */
.events-pulse {
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border: 3px solid rgba(159, 122, 234, 0.8);
border-radius: 50%;
transform: translate(-50%, -50%);
animation: eventsPulse 2.5s infinite;
}
@keyframes eventsPulse {
0% {
width: 0;
height: 0;
opacity: 1;
}
100% {
width: 60px;
height: 60px;
opacity: 0;
}
}
.admin-icon {
animation: adminGlow 2s ease-in-out infinite alternate;
}
@keyframes adminGlow {
0% {
color: #e74c3c;
text-shadow: 0 0 10px rgba(231, 76, 60, 0.5);
}
100% {
color: #ff6b6b;
text-shadow: 0 0 20px rgba(255, 107, 107, 0.8);
}
}
/* Home Icon Animation */
.home-icon {
animation: homeGlow 2.5s ease-in-out infinite alternate;
}
@keyframes homeGlow {
0% {
color: #48bb78;
text-shadow: 0 0 10px rgba(72, 187, 120, 0.5);
}
100% {
color: #38a169;
text-shadow: 0 0 20px rgba(56, 161, 105, 0.8);
}
}
/* Messages Icon Animation */
.messages-icon {
animation: messagesGlow 2.8s ease-in-out infinite alternate;
}
@keyframes messagesGlow {
0% {
color: #9f7aea;
text-shadow: 0 0 10px rgba(159, 122, 234, 0.5);
}
100% {
color: #b794f4;
text-shadow: 0 0 20px rgba(183, 148, 244, 0.8);
}
}
/* Library Icon Animation */
.library-icon {
animation: libraryGlow 2.3s ease-in-out infinite alternate;
}
@keyframes libraryGlow {
0% {
color: #38b2ac;
text-shadow: 0 0 10px rgba(56, 178, 172, 0.5);
}
100% {
color: #319795;
text-shadow: 0 0 20px rgba(49, 151, 149, 0.8);
}
}
/* Pulse Ring for Trending */
.pulse-ring {
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border: 2px solid rgba(255, 107, 53, 0.6);
border-radius: 50%;
transform: translate(-50%, -50%);
animation: pulseRing 2s infinite;
}
@keyframes pulseRing {
0% {
width: 0;
height: 0;
opacity: 1;
}
100% {
width: 60px;
height: 60px;
opacity: 0;
}
}
/* Tooltip Styles */
.nav-icon-link[data-tooltip]:hover::after {
content: attr(data-tooltip);
position: absolute;
bottom: -45px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.9);
color: white;
padding: 0.8rem 1.2rem;
border-radius: 8px;
font-size: 1.2rem;
font-weight: 500;
white-space: nowrap;
z-index: 1000;
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
}
.nav-icon-link[data-tooltip]:hover::before {
content: "";
position: absolute;
bottom: -8px;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-bottom: 8px solid rgba(0, 0, 0, 0.9);
z-index: 1001;
}
/* Social Network Style Navigation */
.nav-link i {
margin-right: 0.5rem;
font-size: 1.2rem;
}
.creator-nav {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1));
border: 1px solid rgba(102, 126, 234, 0.3);
border-radius: 20px;
padding: 0.8rem 1.5rem !important;
backdrop-filter: blur(10px);
transition: all 0.3s ease;
}
.creator-nav:hover {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2));
border-color: rgba(102, 126, 234, 0.5);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
transform: translateY(-3px);
}
.party-nav {
background: linear-gradient(135deg, rgba(255, 102, 196, 0.1), rgba(255, 154, 0, 0.1));
border: 1px solid rgba(255, 102, 196, 0.3);
border-radius: 20px;
padding: 0.8rem 1.5rem !important;
backdrop-filter: blur(10px);
transition: all 0.3s ease;
}
.party-nav:hover {
background: linear-gradient(135deg, rgba(255, 102, 196, 0.2), rgba(255, 154, 0, 0.2));
border-color: rgba(255, 102, 196, 0.5);
box-shadow: 0 8px 25px rgba(255, 102, 196, 0.3);
transform: translateY(-3px);
color: #ff66c4;
}
.cart-nav-link {
position: relative;
display: flex !important;
align-items: center;
justify-content: center;
min-width: 44px;
min-height: 44px;
padding: 0.8rem;
border-radius: 12px;
transition: all 0.3s ease;
flex-shrink: 0;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
visibility: visible !important;
opacity: 1 !important;
z-index: 1000;
}
.cart-nav-link:hover {
background: rgba(102, 126, 234, 0.15);
border-color: rgba(102, 126, 234, 0.3);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
}
.cart-nav-link i {
font-size: 1.2rem;
color: #a0aec0;
transition: color 0.3s ease;
}
.cart-nav-link:hover i {
color: #667eea;
}
.cart-count {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border-radius: 50%;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
font-weight: 700;
position: absolute;
top: -6px;
right: -6px;
border: 2px solid rgba(15, 15, 15, 0.9);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
transition: all 0.3s ease;
}
.cart-nav-link:hover .cart-count {
transform: scale(1.1);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.user-menu {
display: flex;
align-items: center;
gap: 0.5rem;
flex-shrink: 0;
position: relative;
}
/* Ensure cart icon is always visible */
.user-menu .cart-nav-link {
display: flex !important;
visibility: visible !important;
opacity: 1 !important;
position: relative;
z-index: 1000;
}
/* Desktop Search Button */
.desktop-search-button {
position: relative;
display: flex !important;
align-items: center;
justify-content: center;
min-width: 44px;
min-height: 44px;
padding: 0.8rem;
border-radius: 12px;
transition: all 0.3s ease;
flex-shrink: 0;
cursor: pointer;
pointer-events: auto;
z-index: 1000;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
visibility: visible !important;
opacity: 1 !important;
z-index: 1000;
cursor: pointer;
margin: 0;
}
.desktop-search-button:hover {
background: rgba(102, 126, 234, 0.15);
border-color: rgba(102, 126, 234, 0.3);
transform: scale(1.05);
}
.desktop-search-button i {
color: #a5b4fc;
font-size: 1.4rem;
transition: all 0.3s ease;
}
.desktop-search-button:hover i {
color: #667eea;
transform: scale(1.1);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
/* Profile Section */
.profile-section {
display: flex;
align-items: center;
gap: 1.5rem;
position: relative;
height: 100%;
flex-shrink: 0;
}
.profile-avatar {
width: 4rem;
height: 4rem;
border-radius: 50%;
background: linear-gradient(135deg, #667eea, #764ba2);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 1.8rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
border: 2px solid rgba(255, 255, 255, 0.1);
overflow: hidden;
position: relative;
}
.profile-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 50%;
}
.profile-avatar:hover {
border-color: rgba(102, 126, 234, 0.5);
transform: scale(1.05);
}
.profile-info {
display: flex;
flex-direction: column;
gap: 0.2rem;
justify-content: center;
}
.profile-username {
color: white;
font-size: 1.4rem;
font-weight: 600;
line-height: 1.2;
}
.profile-credits-container {
display: flex;
align-items: center;
gap: 0.5rem;
flex-wrap: wrap;
}
.profile-credits {
color: #a0aec0;
font-size: 1.2rem;
font-weight: 500;
line-height: 1.2;
}
.profile-tracks-link {
text-decoration: none;
transition: opacity 0.3s ease;
}
.profile-tracks-link:hover {
opacity: 0.8;
}
.profile-tracks-link span {
font-size: 1.2rem;
font-weight: 500;
line-height: 1.2;
}
.profile-dropdown {
position: absolute;
top: calc(100% + 1rem);
right: 0;
background: rgba(20, 20, 20, 0.98);
backdrop-filter: blur(30px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
padding: 1rem;
min-width: 350px;
max-width: 400px;
max-height: 70vh;
overflow-y: auto;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
opacity: 0;
visibility: hidden;
transform: translateY(-10px);
transition: opacity 0.3s ease, visibility 0.3s ease, transform 0.3s ease;
z-index: 10001;
pointer-events: none;
}
.profile-dropdown.active {
opacity: 1;
visibility: visible;
transform: translateY(0);
pointer-events: auto;
}
.dropdown-item {
display: flex;
align-items: center;
gap: 0.8rem;
padding: 0.8rem 1rem;
color: white;
text-decoration: none;
font-size: 1.3rem;
font-weight: 500;
border-radius: 8px;
transition: all 0.3s ease;
cursor: pointer;
}
.dropdown-item:hover {
background: rgba(102, 126, 234, 0.1);
transform: translateX(5px);
}
.dropdown-item i {
width: 1.6rem;
text-align: center;
color: #667eea;
}
.dropdown-divider {
height: 1px;
background: rgba(255, 255, 255, 0.1);
margin: 0.5rem 0;
}
/* Social Network Dropdown Sections */
.dropdown-section {
margin-bottom: 1rem;
}
.dropdown-section:last-child {
margin-bottom: 0;
}
.dropdown-section-header {
display: flex;
align-items: center;
gap: 0.6rem;
padding: 0.6rem 0.8rem;
color: #a0aec0;
font-size: 1.1rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
margin-bottom: 0.4rem;
}
.dropdown-section-header i {
color: #667eea !important;
font-size: 1.3rem;
}
.creator-hub .dropdown-section-header i {
color: #667eea !important;
}
.social-hub .dropdown-section-header i {
color: #ff66c4 !important;
}
.my-stuff .dropdown-section-header i {
color: #48bb78 !important;
}
/* Custom scrollbar for dropdown */
.profile-dropdown::-webkit-scrollbar {
width: 6px;
}
.profile-dropdown::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
border-radius: 3px;
}
.profile-dropdown::-webkit-scrollbar-thumb {
background: rgba(102, 126, 234, 0.5);
border-radius: 3px;
}
.profile-dropdown::-webkit-scrollbar-thumb:hover {
background: rgba(102, 126, 234, 0.7);
}
.item-details {
display: flex;
flex-direction: column;
gap: 0.2rem;
}
.item-title {
font-size: 1.4rem;
font-weight: 600;
color: white;
}
.item-subtitle {
font-size: 1.1rem;
color: #a0aec0;
font-weight: 400;
}
.creator-item:hover {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.15), rgba(118, 75, 162, 0.15));
border-left: 3px solid #667eea;
}
.social-item:hover {
background: linear-gradient(135deg, rgba(255, 102, 196, 0.15), rgba(255, 154, 0, 0.15));
border-left: 3px solid #ff66c4;
}
.creator-item:hover .item-title,
.social-item:hover .item-title {
color: #ffffff;
}
/* Quick Create Button */
.quick-create-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
border-radius: 20px;
padding: 0.6rem 1.2rem;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.5rem;
position: relative;
overflow: hidden;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
white-space: nowrap;
text-decoration: none;
flex-shrink: 0;
}
.quick-create-btn:hover {
transform: translateY(-3px);
box-shadow: 0 8px 30px rgba(102, 126, 234, 0.5);
}
.quick-create-btn i {
font-size: 1.6rem;
transition: transform 0.3s ease;
}
.quick-create-btn:hover i {
transform: rotate(90deg);
}
.create-btn-glow {
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s ease;
}
.quick-create-btn:hover .create-btn-glow {
left: 100%;
}
.create-text {
font-weight: 700;
letter-spacing: 0.5px;
}
.credits {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
padding: 1rem 2rem;
border-radius: 12px;
font-size: 1.4rem;
font-weight: 600;
display: flex;
align-items: center;
gap: 0.8rem;
}
.logout-btn {
background: linear-gradient(135deg, rgba(239, 68, 68, 0.1), rgba(220, 38, 38, 0.1));
color: #ef4444;
border: 1px solid rgba(239, 68, 68, 0.3);
padding: 1.2rem 1.8rem;
border-radius: 12px;
text-decoration: none;
font-size: 1.4rem;
font-weight: 600;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.8rem;
position: relative;
overflow: hidden;
backdrop-filter: blur(10px);
box-shadow: 0 4px 15px rgba(239, 68, 68, 0.1);
}
.logout-btn:hover {
background: linear-gradient(135deg, rgba(239, 68, 68, 0.2), rgba(220, 38, 38, 0.2));
border-color: rgba(239, 68, 68, 0.5);
color: #fca5a5;
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(239, 68, 68, 0.2);
}
.logout-btn:active {
transform: translateY(0);
box-shadow: 0 4px 15px rgba(239, 68, 68, 0.15);
}
.logout-btn i {
font-size: 1.6rem;
transition: transform 0.3s ease;
}
.logout-btn:hover i {
transform: rotate(180deg);
}
.logout-btn-glow {
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(239, 68, 68, 0.2), transparent);
transition: left 0.5s ease;
}
.logout-btn:hover .logout-btn-glow {
left: 100%;
}
/* Credits Display */
.credits-display {
display: flex;
align-items: center;
gap: 1rem;
padding: 1.2rem 1.5rem;
background: linear-gradient(135deg, rgba(255, 193, 7, 0.1), rgba(255, 152, 0, 0.1));
border: 1px solid rgba(255, 193, 7, 0.3);
border-radius: 12px;
margin: 0.5rem 0;
position: relative;
}
.credits-display i {
color: #ffc107;
font-size: 1.6rem;
}
.credits-amount {
font-weight: 700;
color: #ffc107;
font-size: 1.6rem;
text-shadow: 0 0 10px rgba(255, 193, 7, 0.3);
}
.refresh-credits-btn {
background: transparent;
border: none;
color: #ffc107;
cursor: pointer;
padding: 0.5rem;
border-radius: 6px;
transition: all 0.3s ease;
margin-left: auto;
}
.refresh-credits-btn:hover {
background: rgba(255, 193, 7, 0.2);
transform: rotate(180deg);
}
.refresh-credits-btn i {
font-size: 1.2rem;
}
/* Buttons */
.btn {
display: inline-flex;
align-items: center;
gap: 0.6rem;
padding: 1rem 2.5rem;
border-radius: 10px;
text-decoration: none;
font-size: 1.4rem;
font-weight: 500;
transition: all 0.3s ease;
border: none;
cursor: pointer;
letter-spacing: 0.2px;
}
.btn-primary {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
padding: 1rem 2rem;
font-size: 1.5rem;
font-weight: 700;
text-align: center;
justify-content: center;
min-width: 190px;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
background: linear-gradient(135deg, #5a67d8, #6b46c1);
}
.btn-danger {
background: linear-gradient(135deg, #ff6b6b, #ee5a52);
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 8px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 0.5rem;
box-shadow: 0 3px 10px rgba(255, 107, 107, 0.2);
}
.btn-danger:hover {
transform: translateY(-1px);
box-shadow: 0 5px 15px rgba(255, 107, 107, 0.3);
background: linear-gradient(135deg, #ff5252, #d32f2f);
}
.btn-warning {
background: linear-gradient(135deg, #ffa726, #ff9800);
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 8px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 0.5rem;
box-shadow: 0 3px 10px rgba(255, 167, 38, 0.2);
}
.btn-warning:hover {
transform: translateY(-1px);
box-shadow: 0 5px 15px rgba(255, 167, 38, 0.3);
background: linear-gradient(135deg, #ff9800, #f57c00);
}
/* Premium Cart Modal Styles */
.cart-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.85);
display: flex;
align-items: center;
justify-content: center;
z-index: 99999;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
animation: modalFadeIn 0.3s ease-out;
/* Fix potential CSS issues */
box-sizing: border-box;
overflow: hidden;
}
@keyframes modalFadeIn {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
.cart-modal-content {
background: linear-gradient(135deg, rgba(15, 15, 15, 0.98) 0%, rgba(25, 25, 25, 0.98) 100%);
border: 1px solid rgba(102, 126, 234, 0.2);
border-radius: 20px;
width: 90%;
max-width: 550px;
max-height: 80vh;
display: flex;
flex-direction: column;
backdrop-filter: blur(40px);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(102, 126, 234, 0.1);
position: relative;
overflow: hidden;
/* Fix positioning issues */
box-sizing: border-box;
margin: auto;
}
.cart-modal-content::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(102, 126, 234, 0.5), transparent);
}
.cart-modal-content::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse"><path d="M 10 0 L 0 0 0 10" fill="none" stroke="rgba(102,126,234,0.1)" stroke-width="0.5"/></pattern></defs><rect width="100" height="100" fill="url(%23grid)"/></svg>');
opacity: 0.3;
pointer-events: none;
}
.cart-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.8rem 2rem 1.2rem;
border-bottom: 1px solid rgba(102, 126, 234, 0.15);
position: relative;
z-index: 2;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.05), rgba(118, 75, 162, 0.05));
}
.cart-modal-header h3 {
color: white;
margin: 0;
display: flex;
align-items: center;
gap: 0.8rem;
font-size: 2rem;
font-weight: 600;
letter-spacing: -0.5px;
}
.cart-modal-header h3 i {
font-size: 1.8rem;
color: #667eea;
opacity: 0.9;
}
.close-btn {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: #a0aec0;
font-size: 1.4rem;
cursor: pointer;
padding: 0.8rem;
border-radius: 50%;
transition: all 0.3s ease;
width: 44px;
height: 44px;
display: flex;
align-items: center;
justify-content: center;
}
.close-btn:hover {
background: rgba(102, 126, 234, 0.2);
border-color: rgba(102, 126, 234, 0.5);
color: white;
transform: scale(1.1);
}
.cart-modal-body {
padding: 2rem 2.5rem;
position: relative;
z-index: 2;
flex: 1;
overflow-y: auto;
max-height: 50vh;
min-height: 200px;
/* Fix scrolling issues */
box-sizing: border-box;
-webkit-overflow-scrolling: touch;
}
.cart-modal-body::-webkit-scrollbar {
width: 8px;
}
.cart-modal-body::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
}
.cart-modal-body::-webkit-scrollbar-thumb {
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 4px;
}
.cart-modal-body::-webkit-scrollbar-thumb:hover {
background: linear-gradient(135deg, #5a6fd8, #6a4190);
}
/* Mobile responsiveness fixes */
@media (max-width: 768px) {
.cart-modal-content {
width: 95%;
max-width: none;
max-height: 90vh;
margin: 1rem;
}
.cart-modal-body {
padding: 1.5rem;
max-height: 60vh;
}
.cart-modal-header {
padding: 1.5rem 1.5rem 1rem;
}
.cart-modal-header h3 {
font-size: 1.6rem;
}
/* Make close button more visible on mobile */
.close-btn {
width: 48px !important;
height: 48px !important;
font-size: 1.6rem !important;
background: rgba(239, 68, 68, 0.2) !important;
border: 2px solid rgba(239, 68, 68, 0.5) !important;
color: #ef4444 !important;
z-index: 10001;
position: relative;
}
.close-btn:hover,
.close-btn:active {
background: rgba(239, 68, 68, 0.3) !important;
border-color: #ef4444 !important;
color: white !important;
}
/* Make remove button mobile-friendly */
.remove-item-btn {
width: 44px !important;
height: 44px !important;
min-width: 44px !important;
min-height: 44px !important;
font-size: 1.4rem !important;
border-width: 2px !important;
}
.remove-item-btn i {
font-size: 1.4rem !important;
}
/* Make cart items more mobile-friendly */
.cart-item {
padding: 1rem;
flex-wrap: wrap;
gap: 0.75rem;
}
.item-info {
flex: 1;
min-width: 0;
}
.item-info h4 {
font-size: 1.3rem;
}
.item-info p {
font-size: 1.1rem;
}
.item-price {
flex-direction: column;
align-items: flex-start;
gap: 0.75rem;
}
.item-price > div:not(.quantity-controls) {
font-size: 1.4rem;
}
.quantity-controls {
padding: 0.3rem;
order: -1;
}
.qty-btn {
width: 40px !important;
height: 40px !important;
font-size: 1.2rem !important;
min-width: 40px !important;
min-height: 40px !important;
}
.quantity-display {
font-size: 1.3rem;
min-width: 25px;
}
.remove-item-btn {
align-self: flex-end;
}
.quantity-controls {
padding: 0.3rem;
}
.qty-btn {
width: 36px !important;
height: 36px !important;
font-size: 1.2rem !important;
}
.quantity-display {
font-size: 1.2rem;
min-width: 20px;
}
/* Cart modal footer mobile */
.cart-modal-footer {
flex-direction: column;
gap: 0.75rem;
padding: 1.25rem 1.5rem;
}
.cart-modal-footer .btn-secondary,
.cart-modal-footer .btn-primary,
.cart-modal-footer .btn-danger {
width: 100%;
justify-content: center;
min-height: 48px;
font-size: 1.2rem;
}
}
/* Extra small mobile devices */
@media (max-width: 480px) {
.remove-item-btn {
width: 40px !important;
height: 40px !important;
min-width: 40px !important;
min-height: 40px !important;
font-size: 1.3rem !important;
}
.remove-item-btn i {
font-size: 1.3rem !important;
}
.cart-item {
padding: 0.875rem;
}
.item-info h4 {
font-size: 1.2rem;
}
.item-price {
font-size: 1.4rem;
}
}
.empty-cart {
text-align: center;
color: #a0aec0;
padding: 4rem 2rem;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 300px;
}
.empty-cart i {
font-size: 3.5rem;
margin-bottom: 1.5rem;
opacity: 0.4;
color: #667eea;
filter: drop-shadow(0 4px 8px rgba(102, 126, 234, 0.3));
}
.empty-cart p {
font-size: 1.4rem;
margin-bottom: 2rem;
color: #8a9ba8;
font-weight: 400;
letter-spacing: 0.2px;
}
.empty-cart .empty-highlight {
font-size: 1.2rem;
color: #c3dafe;
margin-bottom: 1.5rem;
letter-spacing: 0.2px;
}
.empty-cart-cta-group {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 1rem;
width: 100%;
}
.empty-cart-cta {
display: flex;
align-items: center;
gap: 0.7rem;
padding: 0.85rem 1.6rem;
border-radius: 999px;
text-decoration: none;
font-size: 1.3rem;
font-weight: 600;
letter-spacing: 0.2px;
border: 1px solid rgba(255, 255, 255, 0.12);
color: white;
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
background: rgba(255, 255, 255, 0.05);
}
.empty-cart-cta .cta-icon {
width: 34px;
height: 34px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.4rem;
background: rgba(255, 255, 255, 0.08);
}
.empty-cart-cta.primary {
background: linear-gradient(135deg, #667eea, #764ba2);
border-color: rgba(102, 126, 234, 0.4);
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}
.empty-cart-cta.primary .cta-icon {
background: rgba(255, 255, 255, 0.2);
}
.empty-cart-cta.secondary {
background: linear-gradient(135deg, rgba(59, 130, 246, 0.15), rgba(14, 165, 233, 0.15));
border-color: rgba(59, 130, 246, 0.3);
}
.empty-cart-cta.secondary .cta-icon {
background: rgba(59, 130, 246, 0.25);
}
.empty-cart-cta.tertiary {
background: linear-gradient(135deg, rgba(16, 185, 129, 0.18), rgba(56, 189, 248, 0.18));
border-color: rgba(16, 185, 129, 0.35);
}
.empty-cart-cta.tertiary .cta-icon {
background: rgba(16, 185, 129, 0.25);
}
.empty-cart-cta:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.25);
border-color: rgba(255, 255, 255, 0.3);
}
.cart-items {
margin-bottom: 1rem;
}
.cart-total {
padding: 1.5rem 1.8rem;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.08), rgba(118, 75, 162, 0.08));
border: 1px solid rgba(102, 126, 234, 0.15);
border-radius: 16px;
margin: 1rem 0;
position: sticky;
bottom: 0;
backdrop-filter: blur(15px);
box-shadow: 0 4px 20px rgba(102, 126, 234, 0.1);
}
.total-row {
display: flex;
justify-content: space-between;
align-items: center;
color: white;
font-size: 1.4rem;
font-weight: 500;
margin-bottom: 0.8rem;
}
.total-row:last-child {
margin-bottom: 0;
font-size: 1.8rem;
font-weight: 700;
padding-top: 1rem;
margin-top: 1rem;
border-top: 2px solid rgba(102, 126, 234, 0.2);
}
.total-row span:first-child {
color: #a0aec0;
display: flex;
align-items: center;
gap: 0.5rem;
}
.total-row span:last-child {
color: white;
font-weight: 600;
}
.total-row:last-child span:last-child {
font-size: 2.2rem;
font-weight: 700;
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.cart-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.2rem 1.5rem;
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(102, 126, 234, 0.08);
border-radius: 14px;
margin-bottom: 0.8rem;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.cart-item::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.02), rgba(118, 75, 162, 0.02));
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
}
.cart-item:hover {
background: rgba(255, 255, 255, 0.04);
border-color: rgba(102, 126, 234, 0.15);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.cart-item:hover::before {
opacity: 1;
}
.cart-item:last-child {
margin-bottom: 0;
}
.item-info h4 {
color: white;
margin: 0 0 0.3rem 0;
font-size: 1.5rem;
font-weight: 600;
background: linear-gradient(135deg, #ffffff, #667eea);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
line-height: 1.3;
}
.item-info p {
color: #a0aec0;
margin: 0;
font-size: 1.2rem;
display: flex;
align-items: center;
gap: 0.5rem;
line-height: 1.2;
}
.item-info p i {
color: #667eea;
font-size: 1.2rem;
}
.item-price {
color: white;
font-weight: 700;
font-size: 1.8rem;
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
display: flex;
align-items: center;
gap: 1rem;
}
.remove-item-btn {
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.2);
color: #a0aec0;
border-radius: 12px;
padding: 0;
cursor: pointer;
transition: all 0.3s ease;
font-size: 1.1rem;
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
min-width: 36px;
min-height: 36px;
position: relative;
z-index: 10;
flex-shrink: 0;
}
.remove-item-btn i {
color: currentColor;
font-size: 1.1rem;
display: block;
}
.remove-item-btn:hover {
background: rgba(102, 126, 234, 0.15);
border-color: rgba(102, 126, 234, 0.45);
color: #ffffff;
transform: translateY(-1px);
}
.remove-item-btn:hover i {
color: white;
}
.remove-item-btn:active {
transform: scale(0.96);
}
/* Quantity Controls */
.quantity-controls {
display: flex;
align-items: center;
gap: 0.5rem;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(102, 126, 234, 0.2);
border-radius: 12px;
padding: 0.4rem;
}
.qty-btn {
background: rgba(102, 126, 234, 0.2);
border: 1px solid rgba(102, 126, 234, 0.3);
color: #667eea;
width: 32px;
height: 32px;
border-radius: 8px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
font-size: 1.1rem;
flex-shrink: 0;
}
.qty-btn:hover {
background: rgba(102, 126, 234, 0.3);
border-color: #667eea;
transform: scale(1.1);
}
.qty-btn:active {
transform: scale(0.95);
}
.qty-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.quantity-display {
color: white;
font-weight: 600;
font-size: 1.3rem;
min-width: 25px;
text-align: center;
}
.single-quantity-note {
font-size: 0.85rem;
color: #94a3b8;
white-space: nowrap;
}
.cart-modal-footer {
display: flex;
gap: 0.8rem;
padding: 1.5rem 2rem;
border-top: 1px solid rgba(102, 126, 234, 0.12);
position: relative;
z-index: 2;
background: linear-gradient(135deg, rgba(15, 15, 15, 0.95), rgba(20, 20, 20, 0.95));
}
.btn-secondary, .btn-primary, .btn-danger {
padding: 0.8rem 1.5rem;
border: none;
border-radius: 8px;
cursor: pointer;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-weight: 500;
font-size: 1.3rem;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.6rem;
flex: 1;
justify-content: center;
min-height: 40px;
}
.btn-secondary {
background: rgba(255, 255, 255, 0.03);
color: #a0aec0;
border: 1px solid rgba(255, 255, 255, 0.08);
}
.btn-secondary:hover {
background: rgba(255, 255, 255, 0.08);
color: white;
border-color: rgba(255, 255, 255, 0.15);
transform: translateY(-1px);
}
.btn-primary {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
padding: 1rem 2rem;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 1.4rem;
font-weight: 700;
text-align: center;
justify-content: center;
min-width: 180px;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
background: linear-gradient(135deg, #5a67d8, #6b46c1);
}
.btn-danger {
background: linear-gradient(135deg, #f56565, #e53e3e);
color: white;
border: none;
box-shadow: 0 2px 8px rgba(245, 101, 101, 0.2);
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.btn-danger:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(245, 101, 101, 0.3);
background: linear-gradient(135deg, #e53e3e, #c53030);
}
/* Main Content Transitions */
.main-content {
transition: opacity 0.3s ease;
}
#pageContainer {
transition: opacity 0.3s ease;
}
/* Mobile Navigation */
.mobile-menu-toggle {
display: none;
background: linear-gradient(135deg, #667eea, #764ba2);
border: 2px solid rgba(255, 255, 255, 0.2);
color: white;
font-size: 2rem;
cursor: pointer;
padding: 1rem;
width: 50px;
height: 50px;
align-items: center;
justify-content: center;
border-radius: 12px;
transition: all 0.3s ease;
z-index: 10001;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
margin: 0;
flex-shrink: 0;
}
.mobile-menu-toggle:hover {
background: linear-gradient(135deg, #5a67d8, #6b46c1);
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}
.mobile-menu-toggle:active {
transform: translateY(0);
box-shadow: 0 2px 10px rgba(102, 126, 234, 0.3);
}
.mobile-menu {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(10, 10, 10, 0.98), rgba(26, 32, 44, 0.98));
backdrop-filter: blur(25px);
z-index: 12050;
padding: 2rem;
overflow-y: auto;
transform: translateX(-100%);
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
flex-direction: column;
}
.mobile-menu.active {
display: flex;
transform: translateX(0);
}
/* Mobile Language Switcher */
.mobile-language-switcher {
padding: 1rem 1.5rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
margin: 1rem 0;
}
.mobile-language-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 0.8rem;
padding: 1rem 1.5rem;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2));
border: 2px solid rgba(102, 126, 234, 0.3);
border-radius: 12px;
color: #ffffff;
font-size: 1.3rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
width: 100%;
}
.mobile-language-btn:hover {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.35), rgba(118, 75, 162, 0.35));
border-color: rgba(102, 126, 234, 0.5);
transform: translateY(-2px);
box-shadow: 0 6px 25px rgba(102, 126, 234, 0.4);
}
.mobile-language-btn i {
font-size: 1.5rem;
animation: globeRotate 3s linear infinite;
background: linear-gradient(135deg,
#002654 0%,
#0055a4 20%,
#3b82f6 30%,
#ffffff 50%,
#dc2626 70%,
#ef4135 80%,
#002654 100%
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.mobile-language-btn span {
background: linear-gradient(135deg, #ffffff, #a0aec0);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
font-weight: 700;
letter-spacing: 1px;
}
.mobile-menu-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 3rem;
padding: 2rem;
border-bottom: 2px solid rgba(255, 255, 255, 0.1);
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.02));
border-radius: 16px;
margin: -2rem -2rem 3rem -2rem;
}
.mobile-menu-close {
background: linear-gradient(135deg, #e53e3e, #c53030);
border: 2px solid rgba(255, 255, 255, 0.2);
color: white;
font-size: 2rem;
cursor: pointer;
padding: 1rem;
border-radius: 12px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 15px rgba(229, 62, 62, 0.3);
}
.mobile-menu-close:hover {
background: linear-gradient(135deg, #dc2626, #b91c1c);
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(229, 62, 62, 0.4);
}
.mobile-nav {
display: flex;
flex-direction: column;
gap: 1rem;
margin-bottom: 3rem;
}
.mobile-nav-link {
display: flex;
align-items: center;
gap: 1.5rem;
padding: 1.8rem 2.5rem;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.05));
border-radius: 16px;
text-decoration: none;
color: white;
font-size: 1.8rem;
font-weight: 600;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
border: 2px solid rgba(255, 255, 255, 0.1);
position: relative;
overflow: hidden;
}
.mobile-nav-link::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(102, 126, 234, 0.1), transparent);
transition: left 0.5s ease;
}
.mobile-nav-link:hover::before {
left: 100%;
}
.mobile-nav-link:hover,
.mobile-nav-link.active {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2));
border-color: rgba(102, 126, 234, 0.4);
transform: translateX(8px) translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.2);
}
.mobile-menu-section-header {
display: flex;
align-items: center;
gap: 1rem;
padding: 1.5rem 2.5rem 1rem;
color: #a0aec0;
font-size: 1.3rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-top: 1rem;
}
.mobile-menu-section-header:first-child {
margin-top: 0;
}
.mobile-menu-section-header i {
font-size: 1.4rem;
color: #667eea;
}
@media (max-width: 480px) {
.mobile-menu {
padding: 1.5rem;
}
.mobile-menu-header {
padding: 1.5rem;
margin: -1.5rem -1.5rem 2rem -1.5rem;
}
.mobile-nav-link {
padding: 1.5rem 2rem;
font-size: 1.6rem;
}
.mobile-nav-link i {
font-size: 1.8rem;
width: 2.2rem;
}
.mobile-menu-section-header {
padding: 1.2rem 2rem 0.8rem;
font-size: 1.2rem;
}
.mobile-menu-section-header i {
font-size: 1.3rem;
}
.mobile-profile-section {
padding: 1.2rem 1.5rem;
}
.mobile-profile-avatar {
width: 4rem;
height: 4rem;
font-size: 1.8rem;
}
.mobile-profile-username {
font-size: 1.4rem;
}
.mobile-profile-credits {
font-size: 1.2rem;
}
.mobile-credits {
padding: 1.2rem 1.5rem;
font-size: 1.4rem;
}
.mobile-logout-btn {
padding: 1.2rem 1.5rem;
font-size: 1.4rem;
}
}
.mobile-nav-link i {
font-size: 2rem;
width: 2.5rem;
text-align: center;
color: #667eea;
}
.mobile-nav-link i {
font-size: 2rem;
color: #667eea;
width: 2.5rem;
text-align: center;
}
.mobile-nav-badge {
position: absolute;
right: 2rem;
top: 50%;
transform: translateY(-50%);
background: linear-gradient(135deg, #e53e3e, #c53030);
color: white;
font-size: 1rem;
font-weight: 700;
padding: 0.3rem 0.6rem;
border-radius: 12px;
min-width: 24px;
text-align: center;
box-shadow: 0 2px 8px rgba(229, 62, 62, 0.4);
animation: badgePulse 2s infinite;
}
@keyframes badgePulse {
0%, 100% {
transform: translateY(-50%) scale(1);
}
50% {
transform: translateY(-50%) scale(1.1);
}
}
.mobile-user-menu {
display: flex;
flex-direction: column;
gap: 1rem;
padding-top: 2rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.mobile-profile-section {
display: flex;
align-items: center;
gap: 1.5rem;
padding: 1.5rem 2rem;
background: rgba(102, 126, 234, 0.1);
border-radius: 12px;
border: 1px solid rgba(102, 126, 234, 0.2);
}
.mobile-profile-avatar {
width: 4.5rem;
height: 4.5rem;
border-radius: 50%;
background: linear-gradient(135deg, #667eea, #764ba2);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 2rem;
font-weight: 600;
overflow: hidden;
position: relative;
}
.mobile-profile-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 50%;
}
.mobile-profile-info {
display: flex;
flex-direction: column;
gap: 0.3rem;
justify-content: center;
}
.mobile-profile-username {
color: white;
font-size: 1.6rem;
font-weight: 600;
line-height: 1.2;
}
.mobile-profile-username-link {
text-decoration: none;
color: inherit;
transition: all 0.3s ease;
}
.mobile-profile-username-link:hover {
color: #667eea;
transform: translateY(-1px);
}
.mobile-profile-credits-container {
display: flex;
flex-direction: column;
gap: 0.2rem;
}
.mobile-profile-credits {
color: #a0aec0;
font-size: 1.3rem;
font-weight: 500;
line-height: 1.2;
}
.mobile-profile-credits-link {
text-decoration: none;
color: inherit;
transition: all 0.3s ease;
}
.mobile-profile-credits-link:hover {
color: #48bb78;
transform: translateY(-1px);
}
.mobile-profile-credits-container a[href="/manage_subscription.php"] {
text-decoration: none;
transition: opacity 0.3s ease;
}
.mobile-profile-credits-container a[href="/manage_subscription.php"]:hover {
opacity: 0.8;
}
.mobile-profile-credits-container a[href="/manage_subscription.php"] span {
font-size: 1.3rem;
font-weight: 500;
line-height: 1.2;
}
.mobile-credits {
display: flex;
align-items: center;
gap: 1rem;
padding: 1.5rem 2rem;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 12px;
color: white;
font-size: 1.6rem;
font-weight: 600;
}
.mobile-logout-btn {
display: flex;
align-items: center;
gap: 1rem;
padding: 1.5rem 2rem;
background: linear-gradient(135deg, rgba(239, 68, 68, 0.1), rgba(220, 38, 38, 0.1));
border: 1px solid rgba(239, 68, 68, 0.3);
border-radius: 12px;
text-decoration: none;
color: #ef4444;
font-size: 1.6rem;
font-weight: 600;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
backdrop-filter: blur(10px);
box-shadow: 0 4px 15px rgba(239, 68, 68, 0.1);
}
.mobile-logout-btn:hover {
background: linear-gradient(135deg, rgba(239, 68, 68, 0.2), rgba(220, 38, 38, 0.2));
border-color: rgba(239, 68, 68, 0.5);
color: #fca5a5;
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(239, 68, 68, 0.2);
}
@media (max-width: 768px) {
.nav {
display: none;
}
.user-menu {
display: none;
}
.mobile-menu-toggle {
display: flex !important;
visibility: visible !important;
opacity: 1 !important;
position: relative !important;
z-index: 10001 !important;
}
.container {
padding: 0 1.5rem;
}
.header-content {
padding: 0 1rem;
min-height: 80px;
display: flex;
justify-content: space-between;
align-items: center;
}
.logo-text {
font-size: 2.2rem;
}
.logo-icon {
font-size: 2.8rem;
}
/* Mobile dropdown adjustments */
.profile-dropdown {
max-height: 60vh;
min-width: 300px;
max-width: 350px;
right: -1rem;
}
.dropdown-item {
padding: 0.7rem 0.8rem;
font-size: 1.2rem;
}
.dropdown-section-header {
padding: 0.5rem 0.7rem;
font-size: 1rem;
}
/* Hide desktop buttons on mobile, but show mobile menu button */
.header-right {
display: none;
}
/* Mobile menu button is now outside header-right, ensure it's visible */
.mobile-menu-toggle {
display: flex !important;
visibility: visible !important;
opacity: 1 !important;
position: relative !important;
z-index: 10001 !important;
margin-left: auto !important;
flex-shrink: 0 !important;
background: linear-gradient(135deg, #667eea, #764ba2) !important;
border: 2px solid rgba(255, 255, 255, 0.2) !important;
color: white !important;
font-size: 2rem !important;
cursor: pointer !important;
padding: 1rem !important;
width: 50px !important;
height: 50px !important;
align-items: center !important;
justify-content: center !important;
border-radius: 12px !important;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3) !important;
}
/* Ensure button is visible in header-content */
.header-content > .mobile-menu-toggle {
display: flex !important;
visibility: visible !important;
opacity: 1 !important;
}
}
@media (max-width: 480px) {
.header-content {
padding: 0 0.8rem;
min-height: 70px;
}
.logo-text {
font-size: 1.8rem;
}
.logo-icon {
font-size: 2.4rem;
}
.mobile-menu-toggle {
width: 45px;
height: 45px;
font-size: 1.8rem;
}
}
/* Additional responsive adjustments for better icon spacing */
@media (max-width: 1200px) {
.nav {
gap: 0.3rem;
padding: 0 0.3rem;
}
.nav-icon-container {
width: 40px;
height: 40px;
}
.nav-icon {
font-size: 1.4rem;
}
}
@media (max-width: 1400px) {
.nav {
gap: 0.4rem;
padding: 0 0.4rem;
}
}
.logo-text {
font-size: 2rem;
}
.logo-icon {
font-size: 2.5rem;
}
}
/* Logged-out Navigation */
.logged-out-nav {
gap: 1rem;
padding: 0 1rem;
}
/* Enhanced Button Styles for Logged-out Users */
.btn-ghost {
background: transparent;
color: #a0aec0;
border: 1px solid rgba(255, 255, 255, 0.1);
padding: 1rem 1.5rem;
border-radius: 12px;
text-decoration: none;
font-size: 1.4rem;
font-weight: 500;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.8rem;
}
.btn-ghost:hover {
background: rgba(255, 255, 255, 0.05);
color: #667eea;
border-color: rgba(102, 126, 234, 0.3);
transform: translateY(-2px);
}
.btn-secondary {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05));
color: white;
border: 1px solid rgba(255, 255, 255, 0.2);
padding: 1rem 1.5rem;
border-radius: 12px;
text-decoration: none;
font-size: 1.4rem;
font-weight: 600;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.8rem;
backdrop-filter: blur(20px);
}
.btn-secondary:hover {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.1));
border-color: rgba(255, 255, 255, 0.4);
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(255, 255, 255, 0.1);
}
.btn-primary {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 1.2rem 2.5rem;
border-radius: 12px;
text-decoration: none;
font-size: 1.6rem;
font-weight: 700;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 1rem;
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
position: relative;
overflow: hidden;
text-align: center;
justify-content: center;
min-width: 200px;
}
.btn-primary:hover {
background: linear-gradient(135deg, #5a67d8, #6b46c1);
transform: translateY(-3px);
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.5);
}
.btn-primary::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
transition: left 0.5s ease;
}
.btn-primary:hover::before {
left: 100%;
}
</style>
<?php if (isset($page_custom_styles) && !empty($page_custom_styles)): ?>
<style>
<?= $page_custom_styles ?>
</style>
<?php endif; ?>
</head>
<body>
<!-- Header -->
<header class="header">
<div class="container">
<div class="header-content">
<div class="header-left">
<a href="/" class="logo">
<i class="fas fa-music logo-icon"></i>
<span class="logo-text">SoundStudioPro</span>
</a>
</div>
<?php if (isset($_SESSION['user_id'])): ?>
<!-- Full Navigation for Logged-in Users -->
<nav class="nav">
<!-- Home -->
<a href="/" class="nav-icon-link <?= $current_page === 'index' ? 'active' : '' ?>" data-tooltip="<?= t('nav.home') ?>">
<div class="nav-icon-container">
<i class="fas fa-home nav-icon home-icon"></i>
<div class="nav-icon-glow"></div>
</div>
</a>
<?php if (isset($_SESSION['user_id']) && isset($_SESSION['is_admin']) && $_SESSION['is_admin']): ?>
<!-- Studio -->
<a href="/studio.php" class="nav-icon-link <?= $current_page === 'studio' ? 'active' : '' ?>" data-tooltip="<?= t('nav.studio') ?>">
<div class="nav-icon-container">
<i class="fas fa-sliders-h nav-icon studio-icon"></i>
<div class="nav-icon-glow"></div>
</div>
</a>
<?php endif; ?>
<!-- Music Feed / Trending -->
<a href="/community_fixed.php" class="nav-icon-link <?= ($current_page === 'community' || $current_page === 'community_fixed') ? 'active' : '' ?>" data-tooltip="<?= t('nav.music_feed') ?>">
<div class="nav-icon-container">
<i class="fas fa-fire nav-icon trending-icon"></i>
<div class="nav-icon-glow"></div>
<div class="pulse-ring"></div>
</div>
</a>
<!-- Library -->
<a href="/library.php" class="nav-icon-link <?= $current_page === 'library_new' ? 'active' : '' ?>" data-tooltip="<?= t('nav.library') ?>">
<div class="nav-icon-container">
<i class="fas fa-music nav-icon library-icon"></i>
<div class="nav-icon-glow"></div>
</div>
</a>
<!-- Crates -->
<a href="/crates.php" class="nav-icon-link <?= $current_page === 'crates' ? 'active' : '' ?>" data-tooltip="<?= t('nav.crates') ?>">
<div class="nav-icon-container">
<i class="fas fa-box-open nav-icon"></i>
<div class="nav-icon-glow"></div>
</div>
</a>
<!-- Wishlist -->
<a href="/wishlist.php" class="nav-icon-link <?= $current_page === 'wishlist' ? 'active' : '' ?>" data-tooltip="<?= t('nav.wishlist') ?>">
<div class="nav-icon-container">
<i class="fas fa-heart nav-icon wishlist-icon"></i>
<div class="nav-icon-glow"></div>
</div>
</a>
<!-- Creator Hub -->
<a href="/artist_dashboard.php" class="nav-icon-link creator-nav <?= $current_page === 'artist_dashboard' ? 'active' : '' ?>" data-tooltip="<?= t('dropdown.creator_hub') ?>">
<div class="nav-icon-container">
<i class="fas fa-crown nav-icon creator-icon"></i>
<div class="nav-icon-glow"></div>
</div>
</a>
<!-- Events -->
<a href="/events.php" class="nav-icon-link party-nav <?= $current_page === 'events' ? 'active' : '' ?>" data-tooltip="<?= t('dropdown.party_events') ?>">
<div class="nav-icon-container">
<i class="fas fa-ticket-alt nav-icon party-icon"></i>
<div class="nav-icon-glow"></div>
<div class="events-pulse"></div>
</div>
</a>
<!-- Community -->
<a href="/artists.php" class="nav-icon-link <?= $current_page === 'artists' ? 'active' : '' ?>" data-tooltip="<?= t('nav.artists') ?>">
<div class="nav-icon-container">
<i class="fas fa-users nav-icon community-icon"></i>
<div class="nav-icon-glow"></div>
<div class="community-pulse"></div>
</div>
</a>
<!-- Messages -->
<a href="/messages.php" class="nav-icon-link <?= $current_page === 'messages' ? 'active' : '' ?>" data-tooltip="<?= t('mobile.messages') ?>" id="messagesNavLink">
<div class="nav-icon-container">
<i class="fas fa-comments nav-icon messages-icon"></i>
<div class="nav-icon-glow"></div>
<span class="notification-badge" id="messagesBadge" style="display: none;">0</span>
</div>
</a>
<!-- Notifications -->
<a href="/notifications.php" class="nav-icon-link <?= $current_page === 'notifications' ? 'active' : '' ?>" data-tooltip="<?= t('nav.notifications') ?>" id="notificationsNavLink">
<div class="nav-icon-container">
<i class="fas fa-bell nav-icon notification-icon"></i>
<div class="nav-icon-glow"></div>
<span class="notification-badge <?= $nav_notification_count > 99 ? 'triple-digit' : ($nav_notification_count > 9 ? 'double-digit' : '') ?>" id="notificationsBadge" style="<?= $nav_notification_count > 0 ? 'display: flex;' : 'display: none;' ?>">
<?= $nav_notification_count > 99 ? '99+' : $nav_notification_count ?>
</span>
</div>
</a>
<?php if (isset($_SESSION['user_id']) && isset($_SESSION['is_admin']) && $_SESSION['is_admin']): ?>
<!-- Admin -->
<a href="/admin.php" class="nav-icon-link admin-nav" data-tooltip="<?= t('nav.admin_panel') ?>">
<div class="nav-icon-container">
<i class="fas fa-shield-alt nav-icon admin-icon"></i>
<div class="nav-icon-glow"></div>
</div>
</a>
<?php endif; ?>
</nav>
<!-- Mobile quick actions -->
<div class="mobile-quick-actions">
<button class="nav-link mobile-search-button" onclick="openGlobalSearch(); return false;" title="Search">
<i class="fas fa-search"></i>
</button>
<a href="javascript:void(0)" class="nav-link cart-nav-link mobile-cart-button" onclick="showCartModal(); return false;">
<i class="fas fa-shopping-bag"></i>
<span class="cart-count" style="<?= $total_cart_items > 0 ? 'display:flex;' : 'display:none;' ?>">
<?= $total_cart_items > 99 ? '99+' : $total_cart_items ?>
</span>
</a>
<button class="mobile-menu-toggle" id="mobileMenuToggle" onclick="toggleMobileMenu()">
<i class="fas fa-bars"></i>
</button>
</div>
<div class="header-right">
<!-- Language Switcher -->
<div class="language-switcher" id="languageSwitcher">
<a href="/lang_switch.php?lang=<?= $current_lang === 'en' ? 'fr' : 'en' ?>&return=<?= $return_url ?>" class="language-switcher-btn">
<i class="fas fa-globe"></i>
<span><?= strtoupper($current_lang === 'en' ? 'FR' : 'EN') ?></span>
</a>
</div>
<div class="user-menu">
<?php if (isset($_SESSION['is_impersonating']) && $_SESSION['is_impersonating']): ?>
<a href="fix_impersonation.php?return=1" class="btn btn-danger" style="margin-right: 1rem; padding: 0.5rem 1rem; font-size: 1.2rem; text-decoration: none;">
<i class="fas fa-user-shield"></i>
Admin
</a>
<?php endif; ?>
<!-- Search Button -->
<button class="nav-link desktop-search-button" onclick="openGlobalSearch(); return false;" title="Search" style="display: flex !important; visibility: visible !important; opacity: 1 !important; z-index: 1000;">
<i class="fas fa-search"></i>
</button>
<!-- Profile Section -->
<div class="profile-section">
<div class="profile-avatar" onclick="toggleProfileDropdown(event)">
<?php
$profile_img = !empty($_SESSION['profile_image']) ? trim($_SESSION['profile_image']) : null;
$profile_pos = $_SESSION['profile_position'] ?? 'center center';
if ($profile_img):
// Add cache-busting parameter to prevent browser caching
$img_url = htmlspecialchars($profile_img);
$separator = strpos($img_url, '?') !== false ? '&' : '?';
$img_url .= $separator . 'v=' . time();
?>
<img src="<?= $img_url ?>" alt="<?= htmlspecialchars($headerDisplayName) ?>" loading="lazy" style="object-position: <?= htmlspecialchars($profile_pos) ?>;" onerror="this.style.display='none'; const fallback = this.nextElementSibling; if(fallback) { fallback.style.display='flex'; }">
<span style="display:none; width: 100%; height: 100%; align-items: center; justify-content: center;"><?= strtoupper(substr($headerDisplayName, 0, 1)) ?></span>
<?php else: ?>
<?= strtoupper(substr($headerDisplayName, 0, 1)) ?>
<?php endif; ?>
</div>
<div class="profile-info">
<a href="/artist_profile.php?id=<?= $_SESSION['user_id'] ?>" class="profile-username-link no-ajax">
<div class="profile-username"><?= htmlspecialchars($headerDisplayName) ?></div>
</a>
<div class="profile-credits-container">
<?php
// Show credits and subscription tracks together in topbar
if (isset($_SESSION['user_id'])) {
try {
$pdo = getDBConnection();
// Get user plan directly
// FIX: Use getEffectivePlan() to get the correct plan (checks subscription first, then users.plan)
require_once __DIR__ . '/../utils/subscription_helpers.php';
$user_plan = getEffectivePlan($_SESSION['user_id']);
// Always show credits
$user_credits = $_SESSION['credits'] ?? 5;
// Check if it's a valid subscription plan
if (in_array($user_plan, ['essential', 'starter', 'pro', 'premium', 'enterprise'])) {
// Get plan config
$plans_config = require __DIR__ . '/../config/subscription_plans.php';
require_once __DIR__ . '/translations.php';
$track_limit = $plans_config[$user_plan]['tracks_per_month'] ?? 5;
$tracks_created = 0;
// Try to get usage
try {
require_once __DIR__ . '/../utils/subscription_helpers.php';
$subscription = hasActiveSubscription($_SESSION['user_id']);
if (!$subscription) {
$subscription = getSubscriptionInfo($_SESSION['user_id']);
}
if ($subscription && in_array($subscription['status'] ?? '', ['active', 'trialing'])) {
$usage = getMonthlyTrackUsage($_SESSION['user_id'], $user_plan);
if ($usage) {
$tracks_created = (int)($usage['tracks_created'] ?? 0);
$track_limit = (int)($usage['track_limit'] ?? $track_limit);
}
}
} catch (Exception $usage_e) {
// Ignore usage errors, use defaults
}
// Calculate remaining
$tracks_remaining = max(0, $track_limit - $tracks_created);
$tracks_color = $tracks_remaining > 0 ? '#48bb78' : '#e53e3e';
$plan_display_name = getPlanLabel($user_plan, $plans_config[$user_plan]['name'] ?? ucfirst($user_plan));
// DISPLAY BOTH: Credits and subscription tracks together
echo '<div style="display: flex; align-items: center; gap: 12px; flex-wrap: nowrap;">';
// Credits display
echo '<a href="/credits.php" class="profile-credits-link" style="display: inline-block;">';
echo '<span style="color: #667eea; font-size: 1.2rem; white-space: nowrap; line-height: 1.2;"><i class="fas fa-coins" style="font-size: 0.9rem; margin-right: 3px;"></i>' . number_format($user_credits) . ' ' . t('user.credits') . '</span>';
echo '</a>';
// Subscription tracks display - "Morceaux utilisés 0 / 5" format
echo '<a href="/manage_subscription.php" class="profile-tracks-link" style="display: inline-block;">';
echo '<span style="color: ' . $tracks_color . '; font-size: 1.2rem; white-space: nowrap; line-height: 1.2;"><i class="fas fa-crown" style="font-size: 0.9rem; margin-right: 3px;"></i>' . t('subscription.tracks_used') . ' ' . $tracks_created . ' / ' . $track_limit . '</span>';
echo '</a>';
echo '</div>';
} else {
// No subscription - just show credits
echo '<a href="/credits.php" class="profile-credits-link" style="display: inline-block;">';
echo '<span style="color: #667eea; font-size: 1.2rem; white-space: nowrap; line-height: 1.2;"><i class="fas fa-coins" style="font-size: 0.9rem; margin-right: 3px;"></i>' . number_format($user_credits) . ' ' . t('user.credits') . '</span>';
echo '</a>';
}
} catch (Exception $e) {
error_log("Header tracks display error: " . $e->getMessage());
// Fallback: just show credits
$user_credits = $_SESSION['credits'] ?? 5;
echo '<a href="/credits.php" class="profile-credits-link" style="display: inline-block;">';
echo '<span style="color: #667eea; font-size: 1.2rem; white-space: nowrap; line-height: 1.2;"><i class="fas fa-coins" style="font-size: 0.9rem; margin-right: 3px;"></i>' . number_format($user_credits) . ' ' . t('user.credits') . '</span>';
echo '</a>';
}
} else {
// Not logged in - show nothing or default
}
?>
</div>
</div>
<!-- Profile Dropdown -->
<div class="profile-dropdown" id="profileDropdown">
<?php if (isset($_SESSION['is_impersonating']) && $_SESSION['is_impersonating']): ?>
<a href="fix_impersonation.php?return=1" class="dropdown-item" style="background: #e74c3c; color: white; text-decoration: none;">
<i class="fas fa-user-shield"></i>
<?= t('dropdown.return_to_admin') ?>
</a>
<div class="dropdown-divider"></div>
<?php endif; ?>
<!-- Creator Hub Section -->
<div class="dropdown-section creator-hub">
<div class="dropdown-section-header">
<i class="fas fa-magic"></i>
<?= t('dropdown.creator_hub') ?>
</div>
<a href="/artist_dashboard.php" class="dropdown-item creator-item">
<i class="fas fa-chart-line"></i>
<div class="item-details">
<span class="item-title"><?= t('dropdown.my_dashboard') ?></span>
<span class="item-subtitle"><?= t('dropdown.dashboard_subtitle') ?></span>
</div>
</a>
<a href="/artist_dashboard.php?tab=tracks" class="dropdown-item creator-item">
<i class="fas fa-edit"></i>
<div class="item-details">
<span class="item-title"><?= t('dropdown.manage_tracks') ?></span>
<span class="item-subtitle"><?= t('dropdown.manage_tracks_subtitle') ?></span>
</div>
</a>
<a href="/artist_dashboard.php?tab=earnings" class="dropdown-item creator-item">
<i class="fas fa-dollar-sign"></i>
<div class="item-details">
<span class="item-title"><?= t('dropdown.earnings') ?></span>
<span class="item-subtitle"><?= t('dropdown.earnings_subtitle') ?></span>
</div>
</a>
<a href="/manage_subscription.php" class="dropdown-item creator-item">
<i class="fas fa-sync-alt"></i>
<div class="item-details">
<span class="item-title"><?= t('dropdown.subscription') ?></span>
<span class="item-subtitle"><?= t('dropdown.subscription_subtitle') ?></span>
</div>
</a>
<a href="/invoices.php" class="dropdown-item creator-item">
<i class="fas fa-file-invoice"></i>
<div class="item-details">
<span class="item-title"><?= t('dropdown.invoices') ?></span>
<span class="item-subtitle"><?= t('dropdown.invoices_subtitle') ?></span>
</div>
</a>
<a href="/credits.php#extra-credits" class="dropdown-item creator-item">
<i class="fas fa-coins"></i>
<div class="item-details">
<span class="item-title"><?= t('credits.extra_credits') ?></span>
<span class="item-subtitle"><?= t('credits.extra_credits_desc') ?></span>
</div>
</a>
<a href="/tickets.php" class="dropdown-item creator-item">
<i class="fas fa-ticket-alt"></i>
<div class="item-details">
<span class="item-title"><?= t('dropdown.tickets') ?></span>
<span class="item-subtitle"><?= t('dropdown.tickets_subtitle') ?></span>
</div>
</a>
<a href="/account_settings.php?tab=purchases" class="dropdown-item creator-item">
<i class="fas fa-shopping-bag"></i>
<div class="item-details">
<span class="item-title"><?= t('dropdown.purchases') ?></span>
<span class="item-subtitle"><?= t('dropdown.order_history') ?></span>
</div>
</a>
</div>
<!-- Social & Discovery Section -->
<div class="dropdown-section social-hub">
<div class="dropdown-section-header">
<i class="fas fa-users"></i>
<?= t('dropdown.social_discovery') ?>
</div>
<a href="/artist_profile.php?id=<?= $_SESSION['user_id'] ?>" class="dropdown-item social-item no-ajax">
<i class="fas fa-user-circle"></i>
<div class="item-details">
<span class="item-title"><?= t('dropdown.my_profile') ?></span>
<span class="item-subtitle"><?= t('dropdown.profile_subtitle') ?></span>
</div>
</a>
<a href="/community_fixed.php" class="dropdown-item social-item">
<i class="fas fa-fire"></i>
<div class="item-details">
<span class="item-title"><?= t('dropdown.trending') ?></span>
<span class="item-subtitle"><?= t('dropdown.trending_subtitle') ?></span>
</div>
</a>
<a href="/wishlist.php" class="dropdown-item social-item">
<i class="fas fa-heart"></i>
<div class="item-details">
<span class="item-title"><?= t('dropdown.wishlist') ?></span>
<span class="item-subtitle"><?= t('dropdown.wishlist_subtitle') ?></span>
</div>
</a>
<a href="/events.php" class="dropdown-item social-item">
<i class="fas fa-users"></i>
<div class="item-details">
<span class="item-title"><?= t('dropdown.party_events') ?></span>
<span class="item-subtitle"><?= t('dropdown.events_subtitle') ?></span>
</div>
</a>
<a href="/charts.php" class="dropdown-item social-item">
<i class="fas fa-chart-line"></i>
<div class="item-details">
<span class="item-title"><?= t('nav.charts') ?></span>
<span class="item-subtitle"><?= t('nav.charts') ?></span>
</div>
</a>
</div>
<!-- My Stuff Section -->
<div class="dropdown-section my-stuff">
<div class="dropdown-section-header">
<i class="fas fa-folder"></i>
<?= t('dropdown.my_collection') ?>
</div>
<a href="/library.php" class="dropdown-item">
<i class="fas fa-music"></i>
<div class="item-details">
<span class="item-title"><?= t('nav.library') ?></span>
<span class="item-subtitle"><?= t('dropdown.downloaded_tracks') ?></span>
</div>
</a>
<a href="/crates.php" class="dropdown-item">
<i class="fas fa-box-open"></i>
<div class="item-details">
<span class="item-title"><?= t('nav.crates') ?></span>
<span class="item-subtitle"><?= t('nav.crates') ?></span>
</div>
</a>
<a href="/account_settings.php" class="dropdown-item">
<i class="fas fa-cog"></i>
<div class="item-details">
<span class="item-title"><?= t('user.settings') ?></span>
<span class="item-subtitle"><?= t('dropdown.account_preferences') ?></span>
</div>
</a>
<a href="/profile_settings.php" class="dropdown-item">
<i class="fas fa-user-edit"></i>
<div class="item-details">
<span class="item-title"><?= t('dropdown.profile_settings') ?? 'Profile Settings' ?></span>
<span class="item-subtitle"><?= t('dropdown.edit_profile_info') ?? 'Edit profile information' ?></span>
</div>
</a>
</div>
<div class="dropdown-divider"></div>
<div class="dropdown-item credits-display">
<i class="fas fa-coins"></i>
<span class="credits-amount"><?= $_SESSION['credits'] ?? 5 ?></span> <?= t('user.credits_available') ?>
<button onclick="refreshCredits()" class="refresh-credits-btn" title="<?= t('btn.refresh_credits') ?>">
<i class="fas fa-sync-alt"></i>
</button>
</div>
<?php
// Show subscription tracks if user has subscription
try {
require_once __DIR__ . '/../utils/subscription_helpers.php';
// Use unified function to get effective plan
$header_effective_plan = getEffectivePlan($_SESSION['user_id'] ?? null);
$header_subscription = getEffectiveSubscription($_SESSION['user_id'] ?? null);
// Only show if user has a valid subscription plan (not 'free')
if ($header_effective_plan !== 'free' && in_array($header_effective_plan, ['essential', 'starter', 'pro', 'premium', 'enterprise'])) {
// Get usage for effective plan
$header_usage = null;
// Only show usage if subscription is active
if ($header_subscription && in_array($header_subscription['status'] ?? '', ['active', 'trialing'])) {
$header_usage = getMonthlyTrackUsage($_SESSION['user_id'], $header_effective_plan);
// If helper function returns null, get from plan config
if (!$header_usage) {
$plans_config = require __DIR__ . '/../config/subscription_plans.php';
$track_limit = $plans_config[$header_effective_plan]['tracks_per_month'] ?? 5;
$header_usage = [
'track_limit' => $track_limit,
'tracks_created' => 0
];
}
if ($header_usage) {
$header_tracks_created = (int)($header_usage['tracks_created'] ?? 0);
$header_track_limit = (int)($header_usage['track_limit'] ?? 5);
$tracks_remaining = $header_track_limit - $header_tracks_created;
$tracks_color = $tracks_remaining > 0 ? '#48bb78' : '#e53e3e';
echo '<a href="/manage_subscription.php" class="dropdown-item" style="padding: 12px 16px; color: ' . $tracks_color . '; text-decoration: none; display: block;">';
echo '<i class="fas fa-crown" style="margin-right: 8px;"></i>';
echo '<span>' . t('subscription.tracks_used') . ' ' . $header_tracks_created . ' / ' . $header_track_limit . '</span>';
echo '</a>';
}
}
}
} catch (Exception $e) {
// Silently fail - don't break header if subscription check fails
error_log("Header dropdown subscription display error: " . $e->getMessage());
}
?>
<div class="dropdown-divider"></div>
<button onclick="handleLogout()" class="dropdown-item logout-btn">
<i class="fas fa-power-off"></i>
<span><?= t('user.logout') ?></span>
<div class="logout-btn-glow"></div>
</button>
</div>
</div>
</div>
<!-- Cart Button positioned prominently -->
<a href="javascript:void(0)" class="nav-link cart-nav-link" onclick="showCartModal(); return false;" style="display: flex !important; visibility: visible !important; opacity: 1 !important; z-index: 1000;">
<i class="fas fa-shopping-bag"></i>
<span class="cart-count" style="<?= $total_cart_items > 0 ? 'display:flex;' : 'display:none;' ?>">
<?= $total_cart_items > 99 ? '99+' : $total_cart_items ?>
</span>
</a>
<!-- Quick Create Button -->
<a href="/index.php#create" class="quick-create-btn">
<i class="fas fa-plus"></i>
<span class="create-text"><?= t('nav.create') ?></span>
<div class="create-btn-glow"></div>
</a>
</div>
<?php else: ?>
<!-- Simple Navigation for Logged-out Users -->
<nav class="nav logged-out-nav">
<!-- Home -->
<a href="/" class="nav-icon-link <?= $current_page === 'index' ? 'active' : '' ?>" data-tooltip="<?= t('nav.home') ?>">
<div class="nav-icon-container">
<i class="fas fa-home nav-icon"></i>
<div class="nav-icon-glow"></div>
</div>
</a>
<!-- Music Feed -->
<a href="/community_fixed.php" class="nav-icon-link <?= ($current_page === 'community' || $current_page === 'community_fixed') ? 'active' : '' ?>" data-tooltip="<?= t('nav.discover') ?>">
<div class="nav-icon-container">
<i class="fas fa-fire nav-icon trending-icon"></i>
<div class="nav-icon-glow"></div>
<div class="pulse-ring"></div>
</div>
</a>
<!-- Artists -->
<a href="/artists.php" class="nav-icon-link <?= $current_page === 'artists' ? 'active' : '' ?>" data-tooltip="<?= t('nav.artists') ?>">
<div class="nav-icon-container">
<i class="fas fa-users nav-icon community-icon"></i>
<div class="nav-icon-glow"></div>
</div>
</a>
<!-- Events -->
<a href="/events.php" class="nav-icon-link <?= $current_page === 'events' ? 'active' : '' ?>" data-tooltip="<?= t('mobile.party_events') ?>">
<div class="nav-icon-container">
<i class="fas fa-ticket-alt nav-icon party-icon"></i>
<div class="nav-icon-glow"></div>
</div>
</a>
<!-- Crates -->
<a href="/crates.php" class="nav-icon-link <?= $current_page === 'crates' ? 'active' : '' ?>" data-tooltip="<?= t('nav.crates') ?>">
<div class="nav-icon-container">
<i class="fas fa-box-open nav-icon"></i>
<div class="nav-icon-glow"></div>
</div>
</a>
</nav>
<div class="header-right">
<!-- Language Switcher -->
<div class="language-switcher" id="languageSwitcherMobile">
<a href="/lang_switch.php?lang=<?= $current_lang === 'en' ? 'fr' : 'en' ?>&return=<?= $return_url ?>" class="language-switcher-btn">
<i class="fas fa-globe"></i>
<span><?= strtoupper($current_lang === 'en' ? 'FR' : 'EN') ?></span>
</a>
</div>
<!-- Search Button -->
<button class="nav-link desktop-search-button" onclick="openGlobalSearch(); return false;" title="Search" style="display: flex !important; visibility: visible !important; opacity: 1 !important; z-index: 1000;">
<i class="fas fa-search"></i>
</button>
<!-- Cart Button -->
<a href="javascript:void(0)" class="nav-link cart-nav-link" onclick="showCartModal(); return false;" style="display: flex !important; visibility: visible !important; opacity: 1 !important; z-index: 1000;">
<i class="fas fa-shopping-bag"></i>
<span class="cart-count" style="<?= $total_cart_items > 0 ? 'display:flex;' : 'display:none;' ?>">
<?= $total_cart_items > 99 ? '99+' : $total_cart_items ?>
</span>
</a>
<a href="/auth/login.php" class="btn btn-ghost">
<i class="fas fa-sign-in-alt"></i>
<?= t('user.login') ?>
</a>
<a href="/auth/register.php" class="btn btn-secondary">
<i class="fas fa-user-plus"></i>
<?= t('user.sign_up') ?>
</a>
<a href="/index.php#create" class="btn btn-primary">
<i class="fas fa-music"></i>
<?= t('btn.start_creating_short') ?>
</a>
</div>
<!-- Mobile quick actions -->
<div class="mobile-quick-actions">
<button class="nav-link mobile-search-button" onclick="openGlobalSearch(); return false;" title="Search">
<i class="fas fa-search"></i>
</button>
<a href="javascript:void(0)" class="nav-link cart-nav-link mobile-cart-button" onclick="showCartModal(); return false;">
<i class="fas fa-shopping-bag"></i>
<span class="cart-count" style="<?= $total_cart_items > 0 ? 'display:flex;' : 'display:none;' ?>">
<?= $total_cart_items > 99 ? '99+' : $total_cart_items ?>
</span>
</a>
<button class="mobile-menu-toggle" id="mobileMenuToggle" onclick="toggleMobileMenu()">
<i class="fas fa-bars"></i>
</button>
</div>
<?php endif; ?>
</div>
</div>
</header>
<!-- Global Search Overlay -->
<div class="global-search-overlay" id="globalSearchOverlay">
<div class="global-search-container">
<div class="global-search-header">
<div class="global-search-input-wrapper">
<input
type="text"
class="global-search-input"
id="globalSearchInput"
placeholder="<?= t('search.placeholder') ?>"
autocomplete="off"
/>
</div>
<button class="global-search-close" onclick="closeGlobalSearch()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="global-search-results" id="globalSearchResults">
<div class="global-search-empty">
<i class="fas fa-search" style="font-size: 2rem; margin-bottom: 10px; opacity: 0.5;"></i>
<p><?= t('search.start_typing') ?></p>
</div>
</div>
<?php if (isset($_SESSION['user_id'])): ?>
<div class="global-search-history" id="globalSearchHistory" style="display: none;">
<div class="global-search-section">
<div class="global-search-section-title">
<i class="fas fa-history"></i> <?= t('search.recent_searches') ?>
</div>
<div id="searchHistoryList"></div>
</div>
</div>
<?php endif; ?>
</div>
</div>
<!-- Mobile Menu -->
<div class="mobile-menu" id="mobileMenu">
<div class="mobile-menu-header">
<a href="/" class="logo">
<i class="fas fa-music logo-icon"></i>
<span class="logo-text">SoundStudioPro</span>
</a>
<button class="mobile-menu-close" id="mobileMenuClose" onclick="toggleMobileMenu()">
<i class="fas fa-times"></i>
</button>
</div>
<?php if (isset($_SESSION['user_id'])): ?>
<!-- SoundStudioPro Profile Section - Moved to Top -->
<div style="order: 1;">
<!-- Mobile Profile Section -->
<div class="mobile-profile-section">
<div class="mobile-profile-avatar">
<?php
$profile_img = !empty($_SESSION['profile_image']) ? trim($_SESSION['profile_image']) : null;
$profile_pos = $_SESSION['profile_position'] ?? 'center center';
if ($profile_img):
// Add cache-busting parameter to prevent browser caching
$img_url = htmlspecialchars($profile_img);
$separator = strpos($img_url, '?') !== false ? '&' : '?';
$img_url .= $separator . 'v=' . time();
?>
<img src="<?= $img_url ?>" alt="<?= htmlspecialchars($headerDisplayName) ?>" loading="lazy" style="object-position: <?= htmlspecialchars($profile_pos) ?>;" onerror="this.style.display='none'; const fallback = this.nextElementSibling; if(fallback) { fallback.style.display='flex'; }">
<span style="display:none; width: 100%; height: 100%; align-items: center; justify-content: center;"><?= strtoupper(substr($headerDisplayName, 0, 1)) ?></span>
<?php else: ?>
<?= strtoupper(substr($headerDisplayName, 0, 1)) ?>
<?php endif; ?>
</div>
<div class="mobile-profile-info">
<a href="/artist_profile.php?id=<?= $_SESSION['user_id'] ?>" class="mobile-profile-username-link no-ajax">
<div class="mobile-profile-username"><?= htmlspecialchars($headerDisplayName) ?></div>
</a>
<div class="mobile-profile-credits-container">
<a href="/credits.php" class="mobile-profile-credits-link">
<div class="mobile-profile-credits">
<?= $_SESSION['credits'] ?? 5 ?> Credits
</div>
</a>
<?php
// Show subscription tracks in mobile topbar if user has subscription
try {
require_once __DIR__ . '/../utils/subscription_helpers.php';
// Try active subscription first
$mobile_topbar_subscription = hasActiveSubscription($_SESSION['user_id'] ?? null);
// If no active, get most recent subscription (even if expired/canceled)
if (!$mobile_topbar_subscription) {
$mobile_topbar_subscription = getSubscriptionInfo($_SESSION['user_id'] ?? null);
}
if ($mobile_topbar_subscription && in_array($mobile_topbar_subscription['plan_name'] ?? '', ['essential', 'starter', 'pro', 'premium', 'enterprise'])) {
// Try to get usage - if active subscription, use getMonthlyTrackUsage
// Otherwise, query directly for the subscription period
$mobile_topbar_usage = null;
if (in_array($mobile_topbar_subscription['status'] ?? '', ['active', 'trialing'])) {
// Active subscription - use helper function
$mobile_topbar_usage = getMonthlyTrackUsage($_SESSION['user_id'], $mobile_topbar_subscription['plan_name']);
} else {
// Inactive subscription - query directly
$pdo = getDBConnection();
$period_start = $mobile_topbar_subscription['current_period_start'] ?? null;
if ($period_start) {
if (is_numeric($period_start)) {
$period_start = date('Y-m-d H:i:s', $period_start);
}
$usage_stmt = $pdo->prepare("
SELECT * FROM monthly_track_usage
WHERE user_id = ? AND subscription_period_start = ?
ORDER BY created_at DESC
LIMIT 1
");
$usage_stmt->execute([$_SESSION['user_id'], $period_start]);
$mobile_topbar_usage = $usage_stmt->fetch(PDO::FETCH_ASSOC);
// If no usage record, get from plan config
if (!$mobile_topbar_usage) {
$plans_config = require __DIR__ . '/../config/subscription_plans.php';
$track_limit = $plans_config[$mobile_topbar_subscription['plan_name']]['tracks_per_month'] ?? 5;
$mobile_topbar_usage = [
'track_limit' => $track_limit,
'tracks_created' => 0
];
}
}
}
if ($mobile_topbar_usage) {
$mobile_topbar_tracks_created = (int)($mobile_topbar_usage['tracks_created'] ?? 0);
$mobile_topbar_track_limit = (int)($mobile_topbar_usage['track_limit'] ?? 5);
$mobile_topbar_tracks_remaining = $mobile_topbar_track_limit - $mobile_topbar_tracks_created;
$mobile_tracks_color = $mobile_topbar_tracks_remaining > 0 ? '#48bb78' : '#e53e3e';
echo '<a href="/manage_subscription.php" style="display: block; margin-top: 4px;">';
echo '<span style="color: ' . $mobile_tracks_color . '; font-size: 1.1rem;"><i class="fas fa-crown" style="font-size: 0.9rem; margin-right: 3px;"></i>' . t('subscription.tracks_used') . ' ' . $mobile_topbar_tracks_created . ' / ' . $mobile_topbar_track_limit . '</span>';
echo '</a>';
}
}
} catch (Exception $e) {
// Silently fail - don't break header if subscription check fails
error_log("Mobile header subscription display error: " . $e->getMessage());
}
?>
</div>
</div>
</div>
</div>
<?php endif; ?>
<!-- Language Switcher - Moved to Top -->
<div class="mobile-language-switcher" style="order: 2;">
<a href="/lang_switch.php?lang=<?= $current_lang === 'en' ? 'fr' : 'en' ?>&return=<?= $return_url ?>" class="mobile-language-btn">
<i class="fas fa-globe"></i>
<span><?= strtoupper($current_lang === 'en' ? 'FR' : 'EN') ?></span>
</a>
</div>
<?php if (isset($_SESSION['user_id'])): ?>
<!-- Cart - Moved to Top -->
<?php
$total_items = 0;
// Count music cart items (primary)
if (isset($_SESSION['cart']) && !empty($_SESSION['cart'])) {
foreach ($_SESSION['cart'] as $item) {
$total_items += $item['quantity'];
}
}
// Count credit cart items
if (isset($_SESSION['credit_cart']) && !empty($_SESSION['credit_cart'])) {
foreach ($_SESSION['credit_cart'] as $item) {
$total_items += $item['quantity'];
}
}
// Count ticket cart items
if (isset($_SESSION['ticket_cart']) && !empty($_SESSION['ticket_cart'])) {
foreach ($_SESSION['ticket_cart'] as $item) {
$total_items += $item['quantity'];
}
}
?>
<div style="order: 3; padding: 0 1rem 1rem;">
<a href="javascript:void(0)" class="mobile-nav-link cart-nav-link" onclick="showCartModal(); return false;">
<i class="fas fa-shopping-bag"></i>
<?php
$cart_text = t('mobile.cart');
echo ($cart_text === 'mobile.cart') ? t('common.cart') : $cart_text;
?>
<?php if ($total_items > 0): ?>
<span class="mobile-nav-badge"><?= $total_items > 99 ? '99+' : $total_items ?></span>
<?php endif; ?>
</a>
</div>
<?php endif; ?>
<nav class="mobile-nav" style="order: 4;">
<a href="<?= isset($_SESSION['user_id']) ? '/index.php#create' : '/auth/login.php' ?>" class="mobile-nav-link <?= $current_page === 'create' ? 'active' : '' ?>">
<i class="fas fa-music"></i>
<?= t('nav.create_music') ?>
</a>
<a href="/" class="mobile-nav-link <?= $current_page === 'index' ? 'active' : '' ?>">
<i class="fas fa-home"></i>
<?= t('nav.home') ?>
</a>
<?php if (isset($_SESSION['user_id']) && isset($_SESSION['is_admin']) && $_SESSION['is_admin']): ?>
<a href="/studio.php" class="mobile-nav-link <?= $current_page === 'studio' ? 'active' : '' ?>">
<i class="fas fa-sliders-h"></i>
<?= t('mobile.studio') ?>
</a>
<?php endif; ?>
<a href="/community_fixed.php" class="mobile-nav-link <?= ($current_page === 'community' || $current_page === 'community_fixed') ? 'active' : '' ?>">
<i class="fas fa-music"></i>
<?= t('nav.music_feed') ?>
</a>
<a href="/library.php" class="mobile-nav-link <?= $current_page === 'library_new' ? 'active' : '' ?>">
<i class="fas fa-music"></i>
<?= t('nav.library') ?>
</a>
<a href="/crates.php" class="mobile-nav-link <?= $current_page === 'crates' ? 'active' : '' ?>">
<i class="fas fa-box-open"></i>
<?= t('nav.crates') ?>
</a>
<?php if (isset($_SESSION['user_id'])): ?>
<a href="/wishlist.php" class="mobile-nav-link <?= $current_page === 'wishlist' ? 'active' : '' ?>">
<i class="fas fa-heart"></i>
<?= t('nav.wishlist') ?>
</a>
<?php endif; ?>
<!-- Creator Hub -->
<a href="/artist_dashboard.php" class="mobile-nav-link <?= $current_page === 'artist_dashboard' ? 'active' : '' ?>">
<i class="fas fa-crown"></i>
<?= t('mobile.creator_hub') ?>
</a>
<!-- Events -->
<a href="/events.php" class="mobile-nav-link">
<i class="fas fa-ticket-alt"></i>
<?= t('mobile.party_events') ?>
</a>
<a href="/artists.php?_v=<?= file_exists(__DIR__ . '/../artists.php') ? filemtime(__DIR__ . '/../artists.php') : time() ?>" class="mobile-nav-link <?= $current_page === 'artists' ? 'active' : '' ?>">
<i class="fas fa-users"></i>
<?= t('nav.community') ?>
</a>
<!-- Charts -->
<a href="/charts.php" class="mobile-nav-link <?= $current_page === 'charts' ? 'active' : '' ?>">
<i class="fas fa-chart-line"></i>
<?= t('mobile.charts') ?>
</a>
<!-- Messages -->
<a href="/messages.php" class="mobile-nav-link <?= $current_page === 'messages' ? 'active' : '' ?>">
<i class="fas fa-comments"></i>
<?= t('mobile.messages') ?>
<?php
// Show unread message count if available
if (isset($_SESSION['user_id'])) {
try {
$pdo = getDBConnection();
if ($pdo) {
$stmt = $pdo->prepare("SELECT COUNT(*) FROM user_messages WHERE receiver_id = ? AND is_read = 0");
$stmt->execute([$_SESSION['user_id']]);
$unread_count = $stmt->fetchColumn();
if ($unread_count > 0) {
echo '<span class="mobile-nav-badge">' . ($unread_count > 99 ? '99+' : $unread_count) . '</span>';
}
}
} catch (Exception $e) {
// Silently handle errors
}
}
?>
</a>
<!-- Notifications -->
<?php $notification_count = $nav_notification_count; ?>
<a href="/notifications.php" class="mobile-nav-link <?= $current_page === 'notifications' ? 'active' : '' ?>">
<i class="fas fa-bell"></i>
<?= t('mobile.notifications') ?>
<?php if ($notification_count > 0): ?>
<span class="mobile-nav-badge"><?= $notification_count > 99 ? '99+' : $notification_count ?></span>
<?php endif; ?>
</a>
<?php if (isset($_SESSION['user_id']) && isset($_SESSION['is_admin']) && $_SESSION['is_admin']): ?>
<a href="/admin.php" class="mobile-nav-link <?= $current_page === 'admin' ? 'active' : '' ?>">
<i class="fas fa-shield-alt"></i>
<?= t('mobile.admin_panel') ?>
</a>
<?php endif; ?>
</nav>
<?php if (isset($_SESSION['user_id'])): ?>
<div class="mobile-user-menu" style="order: 5;">
<?php if (isset($_SESSION['logged_in_as_user']) && $_SESSION['logged_in_as_user']): ?>
<a href="/admin_login_as_user.php?return_to_admin=1" class="mobile-nav-link" style="background: #e74c3c; color: white; border-radius: 8px; margin-bottom: 1rem;">
<i class="fas fa-user-shield"></i>
<?= t('mobile.return_to_admin') ?>
</a>
<?php endif; ?>
<!-- Creator Hub Section -->
<div class="mobile-menu-section-header">
<i class="fas fa-magic"></i>
<?= t('dropdown.creator_hub') ?>
</div>
<a href="/artist_dashboard.php" class="mobile-nav-link">
<i class="fas fa-chart-line"></i>
<?= t('dropdown.my_dashboard') ?>
</a>
<a href="/artist_dashboard.php?tab=earnings" class="mobile-nav-link">
<i class="fas fa-dollar-sign"></i>
<?= t('dropdown.earnings') ?>
</a>
<a href="/manage_subscription.php" class="mobile-nav-link">
<i class="fas fa-sync-alt"></i>
<?= t('dropdown.subscription') ?>
</a>
<a href="/invoices.php" class="mobile-nav-link">
<i class="fas fa-file-invoice"></i>
<?= t('dropdown.invoices') ?>
</a>
<a href="/credits.php#extra-credits" class="mobile-nav-link">
<i class="fas fa-coins"></i>
<?= t('credits.extra_credits') ?>
</a>
<a href="/tickets.php" class="mobile-nav-link">
<i class="fas fa-ticket-alt"></i>
<?= t('dropdown.tickets') ?>
</a>
<a href="/account_settings.php?tab=purchases" class="mobile-nav-link">
<i class="fas fa-shopping-bag"></i>
<?= t('dropdown.purchases') ?>
</a>
<!-- Social & Discovery Section -->
<div class="mobile-menu-section-header">
<i class="fas fa-users"></i>
<?= t('dropdown.social_discovery') ?>
</div>
<a href="/artist_profile.php?id=<?= $_SESSION['user_id'] ?>" class="mobile-nav-link no-ajax">
<i class="fas fa-user-circle"></i>
<?= t('dropdown.my_profile') ?>
</a>
<!-- My Collection Section -->
<div class="mobile-menu-section-header">
<i class="fas fa-folder"></i>
<?= t('dropdown.my_collection') ?>
</div>
<a href="/library.php" class="mobile-nav-link">
<i class="fas fa-music"></i>
<?= t('nav.library') ?>
</a>
<a href="/account_settings.php" class="mobile-nav-link">
<i class="fas fa-cog"></i>
<?= t('user.settings') ?>
</a>
<a href="/profile_settings.php" class="mobile-nav-link">
<i class="fas fa-user-edit"></i>
<?= t('dropdown.profile_settings') ?? 'Profile Settings' ?>
</a>
<!-- Credits Display -->
<div class="mobile-credits">
<i class="fas fa-coins"></i>
<?= $_SESSION['credits'] ?? 5 ?> <?= t('user.credits_available') ?>
</div>
<!-- Logout -->
<button onclick="handleLogout()" class="mobile-logout-btn logout-btn">
<i class="fas fa-power-off"></i>
<span><?= t('user.logout') ?></span>
<div class="logout-btn-glow"></div>
</button>
</div>
<?php else: ?>
<div class="mobile-user-menu">
<a href="/auth/login.php" class="mobile-nav-link">
<i class="fas fa-sign-in-alt"></i>
<?= t('user.login') ?>
</a>
<a href="/auth/register.php" class="mobile-nav-link">
<i class="fas fa-user-plus"></i>
<?= t('user.sign_up') ?>
</a>
</div>
<?php endif; ?>
</div>
<script>
// Profile dropdown functionality
function toggleProfileDropdown(event) {
if (event) {
event.stopPropagation();
event.preventDefault();
}
const dropdown = document.getElementById('profileDropdown');
if (dropdown) {
dropdown.classList.toggle('active');
}
}
// Test function to verify button is working
function testMobileMenu() {
console.log('🎵 Test function called - button is working!');
alert('Mobile menu button is working!');
}
// Mobile menu functionality
function toggleMobileMenu() {
const mobileMenu = document.getElementById('mobileMenu');
const mobileMenuToggle = document.getElementById('mobileMenuToggle');
console.log('🎵 Toggle mobile menu called');
console.log('🎵 Mobile menu element:', mobileMenu);
console.log('🎵 Mobile menu toggle element:', mobileMenuToggle);
if (mobileMenu) {
mobileMenu.classList.toggle('active');
const isActive = mobileMenu.classList.contains('active');
console.log('🎵 Mobile menu active:', isActive);
// Update toggle button
if (mobileMenuToggle) {
const icon = mobileMenuToggle.querySelector('i');
if (icon) {
icon.className = isActive ? 'fas fa-times' : 'fas fa-bars';
console.log('🎵 Icon updated to:', icon.className);
}
}
// Prevent body scroll when menu is open
document.body.style.overflow = isActive ? 'hidden' : '';
} else {
console.error('🎵 Mobile menu element not found');
}
}
function closeMobileMenu() {
const mobileMenu = document.getElementById('mobileMenu');
const mobileMenuToggle = document.getElementById('mobileMenuToggle');
console.log('🎵 Close mobile menu called');
if (mobileMenu) {
mobileMenu.classList.remove('active');
// Reset toggle button
if (mobileMenuToggle) {
const icon = mobileMenuToggle.querySelector('i');
if (icon) {
icon.className = 'fas fa-bars';
}
}
// Restore body scroll
document.body.style.overflow = '';
console.log('🎵 Mobile menu closed');
} else {
console.error('🎵 Mobile menu element not found for closing');
}
}
// Refresh credits functionality
function refreshCredits() {
console.log('🎵 Refreshing credits...');
fetch('/api/refresh_credits.php', {
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.json())
.then(data => {
console.log('🎵 Credits refresh response:', data);
if (data.success) {
// Update all credit displays
const creditElements = document.querySelectorAll('.credits-amount, .profile-credits, .mobile-profile-credits');
creditElements.forEach(element => {
element.textContent = data.credits;
});
// Show success notification
if (typeof window.showNotification === 'function') {
window.showNotification(`Credits refreshed: ${data.credits} available`, 'success');
}
} else {
console.error('🎵 Credits refresh failed:', data.message);
if (typeof window.showNotification === 'function') {
window.showNotification('Failed to refresh credits', 'error');
}
}
})
.catch(error => {
console.error('🎵 Credits refresh error:', error);
if (typeof window.showNotification === 'function') {
window.showNotification('Error refreshing credits', 'error');
}
});
}
// Logout functionality
function handleLogout() {
console.log('🎵 Logout button clicked');
// Show confirmation dialog
if (confirm('Are you sure you want to logout?')) {
console.log('🎵 User confirmed logout');
// Try AJAX logout first
fetch('/auth/logout.php?ajax=1', {
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.json())
.then(data => {
console.log('🎵 Logout response:', data);
if (data.success) {
// Show success message
if (typeof window.showNotification === 'function') {
window.showNotification('Logged out successfully', 'success');
}
// Redirect after a short delay
setTimeout(() => {
window.location.href = data.redirect || '/auth/login.php?logout=1';
}, 1000);
} else {
// Fallback to direct redirect
window.location.href = '/auth/logout.php';
}
})
.catch(error => {
console.error('🎵 Logout error:', error);
// Fallback to direct redirect
window.location.href = '/auth/logout.php';
});
} else {
console.log('🎵 User cancelled logout');
}
}
// Initialize mobile menu event listeners
document.addEventListener('DOMContentLoaded', function() {
console.log('🎵 DOM Content Loaded - Initializing mobile menu');
const mobileMenuToggle = document.getElementById('mobileMenuToggle');
const mobileMenuClose = document.getElementById('mobileMenuClose');
console.log('🎵 Mobile menu toggle found:', mobileMenuToggle);
console.log('🎵 Mobile menu close found:', mobileMenuClose);
if (mobileMenuToggle) {
// Remove existing onclick to avoid conflicts
mobileMenuToggle.removeAttribute('onclick');
mobileMenuToggle.addEventListener('click', function(e) {
console.log('🎵 Mobile menu toggle clicked!');
e.preventDefault();
e.stopPropagation();
toggleMobileMenu();
});
// Also add touchstart for mobile devices
mobileMenuToggle.addEventListener('touchstart', function(e) {
console.log('🎵 Mobile menu toggle touched!');
e.preventDefault();
e.stopPropagation();
toggleMobileMenu();
});
}
if (mobileMenuClose) {
// Remove existing onclick to avoid conflicts
mobileMenuClose.removeAttribute('onclick');
mobileMenuClose.addEventListener('click', function(e) {
console.log('🎵 Mobile menu close clicked!');
e.preventDefault();
e.stopPropagation();
closeMobileMenu();
});
// Also add touchstart for mobile devices
mobileMenuClose.addEventListener('touchstart', function(e) {
console.log('🎵 Mobile menu close touched!');
e.preventDefault();
e.stopPropagation();
closeMobileMenu();
});
}
// Close mobile menu when clicking on a link
const mobileNavLinks = document.querySelectorAll('.mobile-nav-link');
mobileNavLinks.forEach(link => {
link.addEventListener('click', function() {
closeMobileMenu();
});
});
// Close mobile menu when clicking outside
document.addEventListener('click', function(event) {
const mobileMenu = document.getElementById('mobileMenu');
const mobileMenuToggle = document.getElementById('mobileMenuToggle');
if (mobileMenu && mobileMenu.classList.contains('active')) {
if (!mobileMenu.contains(event.target) && !mobileMenuToggle.contains(event.target)) {
closeMobileMenu();
}
}
});
// Close mobile menu on escape key
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape') {
closeMobileMenu();
}
});
});
// Close dropdown when clicking outside
document.addEventListener('click', function(event) {
const profileSection = document.querySelector('.profile-section');
const dropdown = document.getElementById('profileDropdown');
if (dropdown && dropdown.classList.contains('active')) {
// Check if click is outside profile section and not on a button that should prevent closing
if (!profileSection.contains(event.target)) {
// Don't close if clicking on modal overlays or other high-priority elements
const isModal = event.target.closest('.modal, .modal-content, .cart-modal');
const isButton = event.target.closest('button, .btn, .track-actions');
// Only close if not clicking on buttons in track actions or modals
if (!isModal && !isButton) {
dropdown.classList.remove('active');
} else if (isButton && !profileSection.contains(event.target)) {
// Close dropdown when clicking buttons outside profile section
dropdown.classList.remove('active');
}
}
}
});
// Close dropdown on escape key
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape') {
const dropdown = document.getElementById('profileDropdown');
if (dropdown && dropdown.classList.contains('active')) {
dropdown.classList.remove('active');
}
}
});
</script>
<!-- Cart Modal -->
<div id="cartModal" class="cart-modal" style="display: none;">
<div class="cart-modal-content">
<div class="cart-modal-header">
<h3><i class="fas fa-shopping-bag"></i> <?= t('common.cart') ?></h3>
<button onclick="hideCartModal()" class="close-btn" aria-label="<?= t('btn.close') ?>">
<i class="fas fa-times"></i>
</button>
</div>
<div class="cart-modal-body">
<?php
$credit_cart = $_SESSION['credit_cart'] ?? [];
$music_cart = $_SESSION['cart'] ?? [];
$ticket_cart = $_SESSION['ticket_cart'] ?? [];
$all_cart_items = array_merge($music_cart, $credit_cart, $ticket_cart);
$has_items = !empty($all_cart_items);
?>
<?php if (!$has_items): ?>
<div class="empty-cart">
<i class="fas fa-shopping-bag"></i>
<p><?= t('common.empty_cart') ?></p>
<p class="empty-highlight"><?= t('cart.empty_highlight') ?></p>
<div class="empty-cart-cta-group">
<a href="/community_fixed.php" class="empty-cart-cta primary">
<span class="cta-icon"><i class="fas fa-music"></i></span>
<span><?= t('cart.cta.browse_tracks') ?></span>
</a>
<a href="/credits.php" class="empty-cart-cta tertiary">
<span class="cta-icon"><i class="fas fa-coins"></i></span>
<span><?= t('cart.cta.browse_credits') ?></span>
</a>
<a href="/events.php" class="empty-cart-cta secondary">
<span class="cta-icon"><i class="fas fa-ticket-alt"></i></span>
<span><?= t('cart.cta.browse_tickets') ?></span>
</a>
</div>
</div>
<?php else: ?>
<div class="cart-items">
<?php foreach ($all_cart_items as $item): ?>
<div class="cart-item">
<div class="item-info">
<?php if (isset($item['type']) && $item['type'] === 'ticket'): ?>
<h4><?= htmlspecialchars($item['event_title'] ?? t('events.ticket') ?: 'Event Ticket') ?></h4>
<p><i class="fas fa-ticket-alt"></i> <?= t('events.ticket') ?: 'Event Ticket' ?><?= $item['quantity'] > 1 ? 's' : '' ?> × <?= $item['quantity'] ?>
<?php if ($item['is_free'] ?? false): ?>
<span style="color: #48bb78; margin-left: 0.5rem;">(<?= t('common.free') ?>)</span>
<?php endif; ?>
</p>
<?php elseif (isset($item['type']) && $item['type'] === 'credit'): ?>
<h4><?= htmlspecialchars(ucfirst($item['package'])) ?> <?= t('checkout.package') ?></h4>
<p><i class="fas fa-coins"></i> <?= $item['credits'] ?> <?= t('common.credits') ?> × <?= $item['quantity'] ?></p>
<?php else: ?>
<h4>
<?php if (isset($item['track_id'])): ?>
<a href="/track.php?id=<?= $item['track_id'] ?>" style="color: inherit; text-decoration: none; transition: color 0.3s;" onmouseover="this.style.color='#667eea';" onmouseout="this.style.color='inherit';">
<?= htmlspecialchars($item['title']) ?>
</a>
<?php else: ?>
<?= htmlspecialchars($item['title']) ?>
<?php endif; ?>
</h4>
<p><i class="fas fa-music"></i> <?= t('checkout.by') ?>
<?php if (isset($item['artist_id'])): ?>
<a href="/artist_profile.php?id=<?= $item['artist_id'] ?>" class="no-ajax" style="color: inherit; text-decoration: none; transition: color 0.3s;" onmouseover="this.style.color='#667eea';" onmouseout="this.style.color='inherit';">
<?= htmlspecialchars($item['artist_name'] ?? $item['artist'] ?? t('checkout.unknown_artist')) ?>
</a>
<?php else: ?>
<?= htmlspecialchars($item['artist_name'] ?? $item['artist'] ?? t('checkout.unknown_artist')) ?>
<?php endif; ?>
× <?= $item['quantity'] ?>
</p>
<?php endif; ?>
</div>
<div class="item-price">
<div class="quantity-controls">
<button class="qty-btn qty-decrease" onclick="updateCartModalQuantity('<?= isset($item['type']) ? $item['type'] : 'track' ?>', '<?= isset($item['type']) && $item['type'] === 'ticket' ? $item['event_id'] : (isset($item['track_id']) ? $item['track_id'] : ($item['package'] ?? '')) ?>', -1)" title="<?= t('checkout.decrease_quantity') ?>" <?= $item['quantity'] <= 1 ? 'disabled' : '' ?>>
<i class="fas fa-minus"></i>
</button>
<span class="quantity-display"><?= $item['quantity'] ?></span>
<button class="qty-btn qty-increase" onclick="updateCartModalQuantity('<?= isset($item['type']) ? $item['type'] : 'track' ?>', '<?= isset($item['type']) && $item['type'] === 'ticket' ? $item['event_id'] : (isset($item['track_id']) ? $item['track_id'] : ($item['package'] ?? '')) ?>', 1)" title="<?= t('checkout.increase_quantity') ?>">
<i class="fas fa-plus"></i>
</button>
</div>
<?php if (isset($item['price'])): ?>
<div style="font-size: 1.5rem; font-weight: 700; color: white;">
<?php if (($item['is_free'] ?? false) && isset($item['type']) && $item['type'] === 'ticket'): ?>
<?= t('common.free') ?>
<?php else: ?>
$<?= number_format($item['price'] * $item['quantity'], 2) ?>
<?php endif; ?>
</div>
<?php endif; ?>
<button class="remove-item-btn" onclick="
<?php
if (isset($item['type']) && $item['type'] === 'ticket'):
echo 'removeTicketFromCart(' . $item['event_id'] . ')';
elseif (isset($item['track_id'])):
echo 'removeFromCart(' . $item['track_id'] . ')';
else:
echo 'removeCreditFromCart(\'' . ($item['package'] ?? '') . '\')';
endif;
?>" title="<?= t('checkout.remove_item') ?>">
<i class="fas fa-times"></i>
</button>
</div>
</div>
<?php endforeach; ?>
</div>
<div class="cart-total">
<div class="total-row">
<span><i class="fas fa-receipt"></i> <?= t('checkout.grand_total') ?>:</span>
<span>
<?php
$grand_total = array_sum(array_map(function($item) {
return ($item['price'] ?? 0) * ($item['quantity'] ?? 0);
}, $all_cart_items));
echo $grand_total == 0 ? t('common.free') : '$' . number_format($grand_total, 2);
?>
</span>
</div>
</div>
<?php endif; ?>
</div>
<div class="cart-modal-footer">
<button onclick="hideCartModal()" class="btn-secondary">
<i class="fas fa-times"></i>
<?= t('btn.close') ?>
</button>
<?php if ($has_items): ?>
<button onclick="emptyCart()" class="btn-danger">
<i class="fas fa-trash"></i>
<?= t('credits.clear_cart') ?>
</button>
<button onclick="window.location.href='/checkout.php'" class="btn-primary">
<i class="fas fa-credit-card"></i>
<?= t('checkout.proceed_to_checkout') ?>
</button>
<?php endif; ?>
</div>
</div>
</div>
<script>
function showCartModal() {
console.log('🎯 showCartModal called');
const modal = document.getElementById('cartModal');
if (modal) {
console.log('🎯 Modal found, showing...');
modal.style.display = 'flex';
document.body.style.overflow = 'hidden';
// Refresh cart content when opening the modal
refreshCartModal();
} else {
console.error('❌ Cart modal not found!');
}
}
function hideCartModal() {
document.getElementById('cartModal').style.display = 'none';
document.body.style.overflow = 'auto';
}
function refreshCartModal() {
console.log('🔄 Refreshing cart modal content...');
const modalBody = document.querySelector('.cart-modal-body');
const modalFooter = document.querySelector('.cart-modal-footer');
if (!modalBody) {
console.error('❌ Cart modal body not found');
return;
}
// Show loading state
modalBody.innerHTML = '<div style="text-align: center; padding: 2rem;"><i class="fas fa-spinner fa-spin"></i> Loading cart...</div>';
// Fetch updated cart content
fetch('/get_cart_modal_content.php')
.then(response => response.json())
.then(data => {
// Update modal body
modalBody.innerHTML = data.body;
// Update modal footer
if (modalFooter) {
modalFooter.innerHTML = data.footer;
}
// Update cart counter
const cartCounts = document.querySelectorAll('.cart-count');
if (cartCounts.length > 0 && data.cart_count !== undefined) {
cartCounts.forEach(count => {
count.textContent = data.cart_count;
if (data.cart_count > 0) {
count.style.display = 'flex';
} else {
count.style.display = 'none';
}
});
}
console.log('✅ Cart modal refreshed');
})
.catch(error => {
console.error('❌ Error refreshing cart modal:', error);
modalBody.innerHTML = '<div style="text-align: center; padding: 2rem; color: #e53e3e;">Error loading cart content</div>';
});
}
const cartModalTranslations = {
removeTrack: '<?= addslashes(t('cart.confirm.remove_track')) ?>',
removeCredit: '<?= addslashes(t('cart.confirm.remove_credit')) ?>',
removeTicket: '<?= addslashes(t('cart.confirm.remove_ticket')) ?>',
clearCart: '<?= addslashes(t('cart.confirm.clear')) ?>',
singleTrack: '<?= addslashes(t('cart.single_quantity_error')) ?>'
};
function removeFromCart(trackId) {
if (!confirm(cartModalTranslations.removeTrack)) {
return;
}
console.log('🗑️ Removing item:', { trackId });
// Use FormData to match cart.php expectations
const formData = new FormData();
formData.append('action', 'remove');
formData.append('track_id', trackId);
fetch('/cart.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Refresh cart modal content
refreshCartModal();
// Update cart counter
const cartCounts = document.querySelectorAll('.cart-count');
cartCounts.forEach(count => {
const currentCount = parseInt(count.textContent) || 0;
count.textContent = Math.max(0, currentCount - 1);
});
// Show success notification
if (typeof window.showNotification === 'function') {
window.showNotification('Item removed from cart! 🗑️', 'success');
}
} else {
throw new Error(data.error || 'Failed to remove item');
}
})
.catch(error => {
console.error('❌ Error removing item:', error);
if (typeof window.showNotification === 'function') {
window.showNotification('Failed to remove item: ' + error.message, 'error');
}
});
}
function emptyCart() {
if (!confirm(cartModalTranslations.clearCart)) {
return;
}
console.log('🗑️ Emptying cart...');
// Show loading state
const button = event.target;
const originalText = button.innerHTML;
button.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Emptying...';
button.disabled = true;
// Clear both carts
Promise.all([
fetch('/cart.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'action=clear'
}),
fetch('/empty_cart.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
})
])
.then(responses => Promise.all(responses.map(r => r.json())))
.then(results => {
if (results.every(r => r.success)) {
// Refresh cart modal
refreshCartModal();
// Update cart counters
const cartCounts = document.querySelectorAll('.cart-count');
cartCounts.forEach(count => {
count.textContent = '0';
});
// Show success notification
if (typeof window.showNotification === 'function') {
window.showNotification('Cart emptied! 🗑️', 'success');
}
} else {
throw new Error('Failed to empty cart');
}
})
.catch(error => {
console.error('❌ Error emptying cart:', error);
if (typeof window.showNotification === 'function') {
window.showNotification('Failed to empty cart: ' + error.message, 'error');
}
})
.finally(() => {
// Reset button
button.innerHTML = originalText;
button.disabled = false;
});
}
// Close cart modal when clicking outside
document.addEventListener('DOMContentLoaded', function() {
const modal = document.getElementById('cartModal');
if (modal) {
modal.addEventListener('click', function(e) {
if (e.target === modal) {
hideCartModal();
}
});
}
// Add click event listeners to cart buttons
const cartButtons = document.querySelectorAll('.cart-nav-link');
cartButtons.forEach(button => {
button.addEventListener('click', function(e) {
console.log('🎯 Cart button clicked');
e.preventDefault();
showCartModal();
});
});
});
function removeCreditFromCart(package) {
if (!confirm(cartModalTranslations.removeCredit)) {
return;
}
console.log('🗑️ Removing credit package:', { package });
fetch('/remove_cart_item.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'credit',
identifier: package
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Refresh cart modal content
refreshCartModal();
// Update cart counter
const cartCounts = document.querySelectorAll('.cart-count');
cartCounts.forEach(count => {
const currentCount = parseInt(count.textContent) || 0;
count.textContent = Math.max(0, currentCount - 1);
});
// Show success notification
if (typeof window.showNotification === 'function') {
window.showNotification('Credit package removed from cart! 🗑️', 'success');
}
} else {
throw new Error(data.error || 'Failed to remove credit package');
}
})
.catch(error => {
console.error('❌ Error removing credit package:', error);
if (typeof window.showNotification === 'function') {
window.showNotification('Failed to remove credit package: ' + error.message, 'error');
}
});
}
function removeTicketFromCart(eventId) {
if (!confirm(cartModalTranslations.removeTicket)) {
return;
}
console.log('🗑️ Removing ticket:', { eventId });
fetch('/remove_cart_item.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'ticket',
identifier: eventId,
event_id: eventId
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Refresh cart modal content
refreshCartModal();
// Update cart counter
const cartCounts = document.querySelectorAll('.cart-count');
cartCounts.forEach(count => {
const currentCount = parseInt(count.textContent) || 0;
count.textContent = Math.max(0, currentCount - 1);
});
// Show success notification
if (typeof window.showNotification === 'function') {
window.showNotification('Ticket removed from cart! 🗑️', 'success');
}
} else {
throw new Error(data.error || 'Failed to remove ticket');
}
})
.catch(error => {
console.error('❌ Error removing ticket:', error);
if (typeof window.showNotification === 'function') {
window.showNotification('Failed to remove ticket: ' + error.message, 'error');
}
});
}
// Update quantity in cart modal
async function updateCartModalQuantity(type, identifier, change) {
try {
if (type === 'track') {
alert(cartModalTranslations.singleTrack ?? 'Tracks are limited to one per order.');
return;
}
// Determine cart key based on type
let cartKey = 'cart';
if (type === 'credit') {
cartKey = 'credit_cart';
} else if (type === 'ticket') {
cartKey = 'ticket_cart';
}
const response = await fetch('/api/update_cart_quantity.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: type,
identifier: identifier,
cart_key: cartKey,
change: change
})
});
const result = await response.json();
if (result.success) {
// Refresh cart modal to show updated quantities
refreshCartModal();
// Update cart counter
const cartCounts = document.querySelectorAll('.cart-count, .mobile-nav-badge');
if (cartCounts.length > 0) {
// Recalculate total count
fetch('/get_cart_modal_content.php')
.then(response => response.json())
.then(data => {
if (data.cart_count !== undefined) {
cartCounts.forEach(count => {
if (count.classList.contains('cart-count')) {
count.textContent = data.cart_count > 0 ? data.cart_count : '';
count.style.display = data.cart_count > 0 ? 'flex' : 'none';
} else if (count.classList.contains('mobile-nav-badge')) {
count.textContent = data.cart_count > 99 ? '99+' : data.cart_count;
count.style.display = data.cart_count > 0 ? 'flex' : 'none';
}
});
}
});
}
} else {
if (typeof window.showNotification === 'function') {
window.showNotification(result.error || 'Error updating quantity', 'error');
} else {
alert(result.error || 'Error updating quantity');
}
}
} catch (error) {
console.error('Error updating quantity:', error);
if (typeof window.showNotification === 'function') {
window.showNotification('Error updating quantity: ' + error.message, 'error');
} else {
alert('Error updating quantity: ' + error.message);
}
}
}
</script>
<!-- AJAX Navigation System - Optimized: Deferred loading -->
<script src="/js/ajax_navigation.js" defer></script>
<!-- Global Search Functionality - Basic implementation (always available) -->
<script>
// Define functions immediately to ensure they're available when buttons are clicked
// These basic versions are always available, even for logged-out users
window.openGlobalSearch = function() {
// Basic implementation to open search overlay
const overlay = document.getElementById('globalSearchOverlay');
if (overlay) {
overlay.classList.add('active');
document.body.style.overflow = 'hidden';
const input = document.getElementById('globalSearchInput');
if (input) {
setTimeout(() => input.focus(), 100);
}
}
};
window.closeGlobalSearch = function() {
// Basic implementation to close search overlay
const overlay = document.getElementById('globalSearchOverlay');
if (overlay) {
overlay.classList.remove('active');
document.body.style.overflow = '';
}
};
// Set up basic search functionality for ALL users (logged in or not)
(function() {
let searchTimeout = null;
function initBasicSearch() {
const input = document.getElementById('globalSearchInput');
const results = document.getElementById('globalSearchResults');
if (!input || !results) {
console.warn('Global search elements not ready, retrying...');
setTimeout(initBasicSearch, 100);
return;
}
console.log('Setting up basic global search event listeners');
// Input event listener for search
input.addEventListener('input', function(e) {
clearTimeout(searchTimeout);
const query = e.target.value.trim();
if (query.length < 2) {
results.innerHTML = '<div class="global-search-empty"><i class="fas fa-search" style="font-size: 2rem; margin-bottom: 10px; opacity: 0.5;"></i><p>Start typing to search...</p></div>';
return;
}
// Show loading
results.innerHTML = '<div class="global-search-loading"><i class="fas fa-spinner fa-spin" style="font-size: 2rem;"></i><p>Searching...</p></div>';
// Perform search after 300ms delay
searchTimeout = setTimeout(() => {
const searchUrl = `/api_global_search.php?q=${encodeURIComponent(query)}&limit=5`;
console.log('Basic search fetching:', searchUrl);
fetch(searchUrl)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
throw new Error('Response is not JSON');
}
return response.json();
})
.then(data => {
console.log('Basic search response:', data);
displayBasicResults(data, query, results);
})
.catch(error => {
console.error('Basic search error:', error);
results.innerHTML = '<div class="global-search-empty"><i class="fas fa-exclamation-triangle" style="font-size: 2rem; margin-bottom: 10px; opacity: 0.5;"></i><p>Error searching. Please try again.</p></div>';
});
}, 300);
});
// Enter key to navigate
input.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
window.closeGlobalSearch();
} else if (e.key === 'Enter') {
const query = input.value.trim();
if (query.length >= 2) {
window.location.href = `/community_fixed.php?sort=latest&time=all&genre=all&page=1&search=${encodeURIComponent(query)}`;
window.closeGlobalSearch();
}
}
});
console.log('Basic global search event listeners set up successfully');
}
// Simple results display function
function displayBasicResults(data, query, resultsEl) {
let html = '';
let hasResults = false;
// Tracks
if (data.tracks && data.tracks.length > 0) {
hasResults = true;
html += '<div class="global-search-section"><div class="global-search-section-title"><i class="fas fa-music"></i> Tracks</div>';
data.tracks.forEach(track => {
let image = track.image_url || null;
const artist = track.artist_name || 'Unknown Artist';
// Normalize image URL
let hasImage = false;
let imageStyle = '';
if (image && image.trim() !== '' && image !== 'null' && image !== 'NULL') {
// If it's a relative path, ensure it starts with /
if (!image.startsWith('http://') && !image.startsWith('https://') && !image.startsWith('/')) {
image = '/' + image;
}
// Don't use external URLs
if (!image.startsWith('http://') && !image.startsWith('https://')) {
hasImage = true;
imageStyle = `background-image: url('${image.replace(/'/g, "\\'")}'); background-size: cover; background-position: center;`;
}
}
// If no image but we have task_id, try to construct image path
// This is a fallback in case the API didn't find the image
if (!hasImage && track.task_id) {
// Try the most common pattern first
const fallbackImage = `/uploads/track_covers/track_${track.task_id}_cover.jpg`;
// We'll try to load it and if it fails, show the icon
// For now, just set it - the browser will handle 404s
hasImage = true;
imageStyle = `background-image: url('${fallbackImage}'); background-size: cover; background-position: center;`;
}
html += `<a href="/track.php?id=${track.id}" class="global-search-item">
<div class="global-search-item-image" style="${imageStyle}">
${!hasImage ? '<i class="fas fa-music"></i>' : ''}
</div>
<div class="global-search-item-content">
<div class="global-search-item-title">${escapeHtmlBasic(track.title || 'Untitled')}</div>
<div class="global-search-item-subtitle">${escapeHtmlBasic(artist)}</div>
</div>
<i class="fas fa-chevron-right global-search-item-icon"></i>
</a>`;
});
html += '</div>';
}
// Artists
if (data.artists && data.artists.length > 0) {
hasResults = true;
html += '<div class="global-search-section"><div class="global-search-section-title"><i class="fas fa-user"></i> Artists</div>';
data.artists.forEach(artist => {
let image = artist.profile_image || null;
const profilePosition = artist.profile_position || 'center center';
const trackCount = artist.track_count || 0;
const followers = artist.followers_count || 0;
const subtitle = `${trackCount} track${trackCount !== 1 ? 's' : ''} • ${followers} follower${followers !== 1 ? 's' : ''}`;
// Normalize image URL
let hasImage = false;
let imageUrl = '';
if (image && image.trim() !== '' && image !== 'null' && image !== 'NULL') {
// If it's a relative path, ensure it starts with /
if (!image.startsWith('http://') && !image.startsWith('https://') && !image.startsWith('/')) {
image = '/' + image;
}
// Don't use external URLs
if (!image.startsWith('http://') && !image.startsWith('https://')) {
hasImage = true;
imageUrl = image.replace(/'/g, "\\'");
}
}
html += `<a href="/artist_profile.php?id=${artist.id}" class="global-search-item">
<div class="global-search-item-image" style="border-radius: 50%; overflow: hidden;">
${hasImage ? `<img src="${imageUrl}" alt="${escapeHtmlBasic(artist.name || 'Unknown')}" style="width: 100%; height: 100%; object-fit: cover; object-position: ${profilePosition.replace(/'/g, "\\'")};" onerror="this.style.display='none'; const fallback = this.nextElementSibling; if(fallback) { fallback.style.display='flex'; }">` : ''}
${!hasImage ? '<i class="fas fa-user"></i>' : '<i class="fas fa-user" style="display:none;"></i>'}
</div>
<div class="global-search-item-content">
<div class="global-search-item-title">${escapeHtmlBasic(artist.name || 'Unknown')}</div>
<div class="global-search-item-subtitle">${subtitle}</div>
</div>
<i class="fas fa-chevron-right global-search-item-icon"></i>
</a>`;
});
html += '</div>';
}
// Genres
if (data.genres && data.genres.length > 0) {
hasResults = true;
html += '<div class="global-search-section"><div class="global-search-section-title"><i class="fas fa-compact-disc"></i> Genres</div>';
data.genres.forEach(genre => {
const genreText = genre.genre || genre;
const trackCount = genre.track_count || 0;
const subtitle = trackCount > 0 ? `${trackCount} track${trackCount !== 1 ? 's' : ''}` : 'Genre';
html += `<a href="/community_fixed.php?genre=${encodeURIComponent(genreText)}" class="global-search-item">
<div class="global-search-item-image">
<i class="fas fa-compact-disc"></i>
</div>
<div class="global-search-item-content">
<div class="global-search-item-title">${escapeHtmlBasic(genreText)}</div>
<div class="global-search-item-subtitle">${subtitle}</div>
</div>
<i class="fas fa-chevron-right global-search-item-icon"></i>
</a>`;
});
html += '</div>';
}
// Events
if (data.events && data.events.length > 0) {
hasResults = true;
html += '<div class="global-search-section"><div class="global-search-section-title"><i class="fas fa-calendar-alt"></i> Events</div>';
data.events.forEach(event => {
const image = event.cover_image || '/assets/images/default-event.png';
const date = new Date(event.start_date).toLocaleDateString();
const imageStyle = image ? `background-image: url('${image.replace(/'/g, "\\'")}'); background-size: cover; background-position: center;` : '';
html += `<a href="/event_details.php?id=${event.id}" class="global-search-item">
<div class="global-search-item-image" style="${imageStyle}">
${!image ? '<i class="fas fa-calendar-alt"></i>' : ''}
</div>
<div class="global-search-item-content">
<div class="global-search-item-title">${escapeHtmlBasic(event.title || 'Untitled Event')}</div>
<div class="global-search-item-subtitle">${date} • ${escapeHtmlBasic(event.location || event.venue_name || '')}</div>
</div>
<i class="fas fa-chevron-right global-search-item-icon"></i>
</a>`;
});
html += '</div>';
}
// Crates
if (data.crates && data.crates.length > 0) {
hasResults = true;
html += '<div class="global-search-section"><div class="global-search-section-title"><i class="fas fa-box-open"></i> Crates</div>';
data.crates.forEach(crate => {
const trackCount = crate.track_count || 0;
const duration = crate.duration_formatted || '';
const artistName = crate.artist_name || 'Unknown';
const subtitle = `${trackCount} track${trackCount !== 1 ? 's' : ''}${duration ? ' • ' + duration : ''} • by ${artistName}`;
// Use artist image as crate image
let hasImage = false;
let imageStyle = '';
const image = crate.artist_image;
if (image && image.trim() !== '' && image !== 'null' && image !== 'NULL') {
if (!image.startsWith('http://') && !image.startsWith('https://') && !image.startsWith('/')) {
hasImage = true;
imageStyle = `background-image: url('/${image.replace(/'/g, "\\'")}'); background-size: cover; background-position: center;`;
} else if (!image.startsWith('http://') && !image.startsWith('https://')) {
hasImage = true;
imageStyle = `background-image: url('${image.replace(/'/g, "\\'")}'); background-size: cover; background-position: center;`;
}
}
html += `<a href="/crate/${crate.id}" class="global-search-item">
<div class="global-search-item-image" style="${imageStyle}">
${!hasImage ? '<i class="fas fa-box-open"></i>' : ''}
</div>
<div class="global-search-item-content">
<div class="global-search-item-title">${escapeHtmlBasic(crate.name || 'Untitled Crate')}</div>
<div class="global-search-item-subtitle">${subtitle}</div>
</div>
<i class="fas fa-chevron-right global-search-item-icon"></i>
</a>`;
});
html += '</div>';
}
if (!hasResults) {
html = `<div class="global-search-empty">
<i class="fas fa-search" style="font-size: 2rem; margin-bottom: 10px; opacity: 0.5;"></i>
<p>No results found for "${escapeHtmlBasic(query)}"</p>
</div>`;
}
resultsEl.innerHTML = html;
}
function escapeHtmlBasic(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initBasicSearch);
} else {
setTimeout(initBasicSearch, 100);
}
})();
</script>
<!-- Messages Badge Update Script - Optimized: Deferred execution -->
<?php
// Only load badge script if user is logged in
// Script uses lazy loading - no API calls until user hovers/clicks messages icon
// This prevents any performance impact on page load
if (isset($_SESSION['user_id'])):
?>
<script>
// Defer execution to reduce blocking time
(function() {
// Wait for DOM to be ready, but don't block initial render
let previousUnreadCount = 0;
// Play notification sound for new messages
function playMessageSound() {
try {
// Create audio context for generating a notification sound
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
// Connect nodes
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
// Configure sound - pleasant notification tone
oscillator.frequency.setValueAtTime(800, audioContext.currentTime);
oscillator.frequency.exponentialRampToValueAtTime(600, audioContext.currentTime + 0.1);
// Volume envelope
gainNode.gain.setValueAtTime(0, audioContext.currentTime);
gainNode.gain.linearRampToValueAtTime(0.3, audioContext.currentTime + 0.01);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.15);
// Play two quick beeps
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.15);
// Second beep after short delay
setTimeout(() => {
const oscillator2 = audioContext.createOscillator();
const gainNode2 = audioContext.createGain();
oscillator2.connect(gainNode2);
gainNode2.connect(audioContext.destination);
oscillator2.frequency.setValueAtTime(800, audioContext.currentTime);
oscillator2.frequency.exponentialRampToValueAtTime(600, audioContext.currentTime + 0.1);
gainNode2.gain.setValueAtTime(0, audioContext.currentTime);
gainNode2.gain.linearRampToValueAtTime(0.3, audioContext.currentTime + 0.01);
gainNode2.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.15);
oscillator2.start(audioContext.currentTime);
oscillator2.stop(audioContext.currentTime + 0.15);
}, 150);
} catch (error) {
// Fallback: Try using HTML5 Audio API with data URI
try {
const audio = new Audio('data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmwhBSuBzvLZiTYIG2m98OScTQ8OUKjk8bVkHAU6kdfyzHksBSR3x/DdkEAKFF606euoVRQKRp/g8r5sIQUrgc7y2Yk2CBtpvfDknE0PDlCo5PG1ZBwFOpHX8sx5LAUkd8fw3ZBAC');
audio.volume = 0.3;
audio.play().catch(e => console.log('Could not play notification sound:', e));
} catch (e) {
console.log('Audio notification not available');
}
}
}
// Throttle badge updates to prevent excessive API calls
let lastBadgeUpdate = 0;
const BADGE_UPDATE_THROTTLE = 10000; // Minimum 10 seconds between updates
// Function to update messages badge
function updateMessagesBadge() {
const badge = document.getElementById('messagesBadge');
if (!badge) {
console.warn('Messages badge element not found - badge may not be in DOM yet');
return;
}
// Throttle: Don't update if called too recently
const now = Date.now();
if (now - lastBadgeUpdate < BADGE_UPDATE_THROTTLE) {
return; // Skip this update, too soon since last one
}
lastBadgeUpdate = now;
fetch('/api/messages.php?action=get_unread_count')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
if (data.success) {
const count = parseInt(data.unread_count) || 0;
// Removed console.log for better performance
// Check if count increased (new message received)
// Only play sound if:
// 1. Count increased
// 2. Previous count was > 0 (to avoid sound on first load)
// 3. Not on messages page (to avoid duplicate sounds)
// 4. Page is visible (not in background tab)
if (count > previousUnreadCount && previousUnreadCount > 0 &&
!window.location.pathname.includes('messages.php') &&
!document.hidden) {
playMessageSound();
}
previousUnreadCount = count;
if (count > 0) {
const displayCount = count > 99 ? '99+' : count.toString();
badge.textContent = displayCount;
badge.style.display = 'flex';
badge.style.color = 'white';
// Add proper sizing classes
badge.classList.remove('double-digit', 'triple-digit');
if (displayCount.length === 2) {
badge.classList.add('double-digit');
} else if (displayCount.length >= 3) {
badge.classList.add('triple-digit');
}
// Add pulse animation
if (!badge.classList.contains('pulse')) {
badge.classList.add('pulse');
}
// Force visibility - ensure badge is shown
badge.style.visibility = 'visible';
badge.style.opacity = '1';
badge.style.zIndex = '10001';
} else {
badge.style.display = 'none';
badge.classList.remove('pulse', 'double-digit', 'triple-digit');
}
} else {
console.error('Failed to get unread count:', data.message || 'Unknown error');
}
})
.catch(error => {
console.error('Error fetching unread messages:', error);
});
}
// LAZY LOADING: Badge only updates when user interacts with messages icon
// This prevents unnecessary API calls on every page load
// Badge will update:
// 1. When user hovers over messages icon (lazy load)
// 2. When user clicks messages icon
// 3. When explicitly called by messages.php or other pages
// 4. When page becomes visible (if badge was already loaded)
let badgeHasBeenLoaded = false;
// Lazy load badge on hover/click of messages icon
function lazyLoadBadge() {
if (!badgeHasBeenLoaded) {
badgeHasBeenLoaded = true;
updateMessagesBadge();
}
}
// Attach lazy load to messages icon
function attachLazyLoad() {
const messagesLink = document.getElementById('messagesNavLink');
if (messagesLink) {
// Load on hover (with small delay to avoid accidental triggers)
let hoverTimeout;
messagesLink.addEventListener('mouseenter', function() {
hoverTimeout = setTimeout(lazyLoadBadge, 500);
});
messagesLink.addEventListener('mouseleave', function() {
clearTimeout(hoverTimeout);
});
// Also load on click
messagesLink.addEventListener('click', lazyLoadBadge);
}
}
// Initialize lazy load attachment after DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', attachLazyLoad);
} else {
attachLazyLoad();
}
// Update badge when page becomes visible (only if already loaded)
document.addEventListener('visibilitychange', function() {
if (!document.hidden && badgeHasBeenLoaded) {
updateMessagesBadge();
}
});
// NO automatic page load update - removed to prevent performance issues
// Badge will only load when user shows interest (hover/click)
// Expose function globally so messages.php and other pages can call it
window.updateMessagesBadge = updateMessagesBadge;
})();
// Notifications Badge Update Script - Optimized: Deferred execution
(function() {
// Wait for DOM to be ready, but don't block initial render
function initNotificationsBadge() {
// Function to update notifications badge
function updateNotificationsBadge() {
const badge = document.getElementById('notificationsBadge');
if (!badge) return;
const badgeClasses = ['double-digit', 'triple-digit'];
fetch('/api/get_notification_count.php')
.then(response => response.json())
.then(data => {
if (data.success) {
const count = parseInt(data.unread_count) || 0;
if (count > 0) {
const displayCount = count > 99 ? '99+' : count.toString();
badge.textContent = displayCount;
badge.style.display = 'flex';
badgeClasses.forEach(cls => badge.classList.remove(cls));
if (displayCount.length === 2) {
badge.classList.add('double-digit');
} else if (displayCount.length >= 3) {
badge.classList.add('triple-digit');
}
badge.classList.add('pulse');
} else {
badge.style.display = 'none';
badge.classList.remove('pulse');
badgeClasses.forEach(cls => badge.classList.remove(cls));
}
}
})
.catch(error => {
console.error('Error fetching notification count:', error);
});
}
// Update badge on page load - Optimized: Use requestIdleCallback if available
function initBadgeUpdate() {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', updateNotificationsBadge);
} else {
// Use requestIdleCallback to defer non-critical work
if ('requestIdleCallback' in window) {
requestIdleCallback(updateNotificationsBadge, { timeout: 2000 });
} else {
setTimeout(updateNotificationsBadge, 0);
}
}
// Update badge every 60 seconds (optimized for performance)
// Only run when page is visible
let notificationBadgeInterval = null;
function startNotificationBadgeInterval() {
if (document.hidden) {
return;
}
if (!notificationBadgeInterval) {
notificationBadgeInterval = setInterval(function() {
if (!document.hidden) {
updateNotificationsBadge();
}
}, 60000); // Increased to 60 seconds for better performance
}
}
// Start interval after initial load
setTimeout(startNotificationBadgeInterval, 2000);
// Update badge when page becomes visible (user switches back to tab)
document.addEventListener('visibilitychange', function() {
if (!document.hidden) {
updateNotificationsBadge();
// Restart interval if it was stopped
if (!notificationBadgeInterval) {
startNotificationBadgeInterval();
}
} else {
// Stop interval when page is hidden to save resources
if (notificationBadgeInterval) {
clearInterval(notificationBadgeInterval);
notificationBadgeInterval = null;
}
}
});
// Expose function globally so other pages can call it
window.updateNotificationsBadge = updateNotificationsBadge;
}
// Initialize after a short delay to reduce blocking
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initBadgeUpdate);
} else {
setTimeout(initBadgeUpdate, 0);
}
}
// Start initialization
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initNotificationsBadge);
} else {
setTimeout(initNotificationsBadge, 0);
}
})();
// Global Search Functionality - Enhanced implementation (for logged-in users)
// This will override the basic function defined above with full functionality
(function() {
let searchTimeout = null;
let overlay, input, results, historyContainer, historyList;
const isLoggedIn = <?= isset($_SESSION['user_id']) ? 'true' : 'false' ?>;
const MAX_HISTORY = 8;
// Initialize elements - wait for DOM
function initElements() {
overlay = document.getElementById('globalSearchOverlay');
input = document.getElementById('globalSearchInput');
results = document.getElementById('globalSearchResults');
historyContainer = document.getElementById('globalSearchHistory');
historyList = document.getElementById('searchHistoryList');
// If elements don't exist, log error
if (!overlay || !input || !results) {
console.error('Global search elements not found:', {
overlay: !!overlay,
input: !!input,
results: !!results
});
return false;
}
return true;
}
// Translations
const translations = {
startTyping: '<?= t('search.start_typing') ?>',
searching: '<?= t('search.searching') ?>',
noResults: '<?= t('search.no_results') ?>',
error: '<?= t('search.error') ?>',
recentSearches: '<?= t('search.recent_searches') ?>',
tracks: '<?= t('search.tracks') ?>',
artists: '<?= t('search.artists') ?>',
genres: '<?= t('search.genres') ?>',
events: '<?= t('search.events') ?>',
crates: '<?= t('search.crates') ?: 'Crates' ?>',
remove: '<?= t('search.remove') ?>'
};
// Get search history from localStorage
function getSearchHistory() {
if (!isLoggedIn) return [];
try {
const history = localStorage.getItem('globalSearchHistory');
return history ? JSON.parse(history) : [];
} catch (e) {
return [];
}
}
// Save search to history
function saveToHistory(query) {
if (!isLoggedIn || !query || query.trim().length < 2) return;
try {
let history = getSearchHistory();
const trimmedQuery = query.trim();
// Remove if already exists
history = history.filter(item => item.toLowerCase() !== trimmedQuery.toLowerCase());
// Add to beginning
history.unshift(trimmedQuery);
// Limit to MAX_HISTORY
history = history.slice(0, MAX_HISTORY);
localStorage.setItem('globalSearchHistory', JSON.stringify(history));
} catch (e) {
console.error('Error saving search history:', e);
}
}
// Display search history
function displaySearchHistory() {
if (!isLoggedIn || !historyContainer || !historyList) return;
const history = getSearchHistory();
if (history.length === 0) {
historyContainer.style.display = 'none';
return;
}
historyContainer.style.display = 'block';
let html = '';
history.forEach((query, index) => {
html += `<div class="global-search-history-item" onclick="searchFromHistory('${query.replace(/'/g, "\\'")}')">
<i class="fas fa-clock"></i>
<span>${escapeHtml(query)}</span>
<button class="history-delete-btn" onclick="event.stopPropagation(); deleteSearchHistory(${index})" title="${translations.remove}">
<i class="fas fa-times"></i>
</button>
</div>`;
});
historyList.innerHTML = html;
}
// Search from history
function searchFromHistory(query) {
if (input) {
input.value = query;
performSearch(query);
}
}
// Delete from history
function deleteSearchHistory(index) {
if (!isLoggedIn) return;
try {
let history = getSearchHistory();
history.splice(index, 1);
localStorage.setItem('globalSearchHistory', JSON.stringify(history));
displaySearchHistory();
} catch (e) {
console.error('Error deleting search history:', e);
}
}
function openGlobalSearch() {
try {
const overlay = document.getElementById('globalSearchOverlay');
const input = document.getElementById('globalSearchInput');
const results = document.getElementById('globalSearchResults');
const historyContainer = document.getElementById('globalSearchHistory');
const isLoggedIn = <?= isset($_SESSION['user_id']) ? 'true' : 'false' ?>;
if (!overlay) {
console.error('Global search overlay not found');
return;
}
overlay.classList.add('active');
document.body.style.overflow = 'hidden'; // Prevent background scrolling
setTimeout(() => {
if (input) {
input.focus();
// Show history if input is empty and user is logged in
if (input.value.trim() === '' && isLoggedIn) {
if (historyContainer && historyList) {
displaySearchHistory();
}
if (results) {
results.innerHTML = '<div class="global-search-empty"><i class="fas fa-search" style="font-size: 2rem; margin-bottom: 10px; opacity: 0.5;"></i><p>' + translations.startTyping + '</p></div>';
}
} else if (input.value.trim() === '') {
if (results) {
results.innerHTML = '<div class="global-search-empty"><i class="fas fa-search" style="font-size: 2rem; margin-bottom: 10px; opacity: 0.5;"></i><p>' + translations.startTyping + '</p></div>';
}
}
}
}, 100);
} catch (error) {
console.error('Error opening global search:', error);
}
}
// Make function globally available immediately
window.openGlobalSearch = openGlobalSearch;
function closeGlobalSearch() {
try {
const overlay = document.getElementById('globalSearchOverlay');
const input = document.getElementById('globalSearchInput');
const results = document.getElementById('globalSearchResults');
const historyContainer = document.getElementById('globalSearchHistory');
if (overlay) {
overlay.classList.remove('active');
document.body.style.overflow = ''; // Restore scrolling
}
if (input) input.value = '';
if (results) results.innerHTML = '<div class="global-search-empty"><i class="fas fa-search" style="font-size: 2rem; margin-bottom: 10px; opacity: 0.5;"></i><p>' + translations.startTyping + '</p></div>';
if (historyContainer) historyContainer.style.display = 'none';
} catch (error) {
console.error('Error closing global search:', error);
}
}
function performSearch(query) {
if (!results) {
console.error('performSearch: results element not found');
return;
}
const trimmedQuery = query.trim();
console.log('performSearch called with query:', trimmedQuery);
if (trimmedQuery.length < 2) {
if (results) {
results.innerHTML = '<div class="global-search-empty"><i class="fas fa-search" style="font-size: 2rem; margin-bottom: 10px; opacity: 0.5;"></i><p>' + translations.startTyping + '</p></div>';
}
if (historyContainer) historyContainer.style.display = 'block';
displaySearchHistory();
return;
}
// Hide history when searching
if (historyContainer) historyContainer.style.display = 'none';
// Save to history
saveToHistory(trimmedQuery);
if (results) {
results.innerHTML = '<div class="global-search-loading"><i class="fas fa-spinner fa-spin" style="font-size: 2rem;"></i><p>' + translations.searching + '</p></div>';
}
const searchUrl = `/api_global_search.php?q=${encodeURIComponent(trimmedQuery)}&limit=5`;
console.log('Fetching from:', searchUrl);
fetch(searchUrl)
.then(response => {
// Check if response is OK (status 200-299)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// Check if response is actually JSON
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
throw new Error('Response is not JSON. Content-Type: ' + contentType);
}
return response.json();
})
.then(data => {
console.log('Search response received:', data);
// Validate response structure
if (!data || typeof data !== 'object') {
console.error('Invalid search response:', data);
throw new Error('Invalid response format');
}
console.log('Displaying results - Tracks:', data.tracks?.length || 0, 'Artists:', data.artists?.length || 0);
displayResults(data, trimmedQuery);
})
.catch(error => {
console.error('Global search error:', error);
console.error('Search query:', trimmedQuery);
console.error('Error details:', {
message: error.message,
stack: error.stack,
name: error.name
});
if (results) {
results.innerHTML = '<div class="global-search-empty"><i class="fas fa-exclamation-triangle" style="font-size: 2rem; margin-bottom: 10px; opacity: 0.5;"></i><p>' + translations.error + '</p><p style="font-size: 0.85rem; opacity: 0.7; margin-top: 10px;">Please try again or check your connection.</p><p style="font-size: 0.75rem; opacity: 0.5; margin-top: 5px;">Error: ' + error.message + '</p></div>';
}
});
}
function displayResults(data, query) {
if (!results) {
console.error('displayResults: results element not found');
return;
}
console.log('displayResults called with data:', data);
let html = '';
let hasResults = false;
// Tracks
if (data.tracks && data.tracks.length > 0) {
hasResults = true;
html += '<div class="global-search-section"><div class="global-search-section-title"><i class="fas fa-music"></i> ' + translations.tracks + '</div>';
data.tracks.forEach(track => {
let image = track.image_url || null;
const artist = track.artist_name || 'Unknown Artist';
// Normalize image URL
let hasImage = false;
let imageStyle = '';
if (image && image.trim() !== '' && image !== 'null' && image !== 'NULL') {
// If it's a relative path, ensure it starts with /
if (!image.startsWith('http://') && !image.startsWith('https://') && !image.startsWith('/')) {
image = '/' + image;
}
// Don't use external URLs
if (!image.startsWith('http://') && !image.startsWith('https://')) {
hasImage = true;
imageStyle = `background-image: url('${image.replace(/'/g, "\\'")}'); background-size: cover; background-position: center;`;
}
}
html += `<a href="/track.php?id=${track.id}" class="global-search-item">
<div class="global-search-item-image" style="${imageStyle}">
${!hasImage ? '<i class="fas fa-music"></i>' : ''}
</div>
<div class="global-search-item-content">
<div class="global-search-item-title">${escapeHtml(track.title)}</div>
<div class="global-search-item-subtitle">${escapeHtml(artist)}</div>
</div>
<i class="fas fa-chevron-right global-search-item-icon"></i>
</a>`;
});
html += '</div>';
}
// Artists
if (data.artists && data.artists.length > 0) {
hasResults = true;
html += '<div class="global-search-section"><div class="global-search-section-title"><i class="fas fa-user"></i> ' + translations.artists + '</div>';
data.artists.forEach(artist => {
let image = artist.profile_image || null;
const profilePosition = artist.profile_position || 'center center';
const trackCount = artist.track_count || 0;
const followers = artist.followers_count || 0;
const subtitle = `${trackCount} track${trackCount !== 1 ? 's' : ''} • ${followers} follower${followers !== 1 ? 's' : ''}`;
// Normalize image URL
let hasImage = false;
let imageUrl = '';
if (image && image.trim() !== '' && image !== 'null' && image !== 'NULL') {
// If it's a relative path, ensure it starts with /
if (!image.startsWith('http://') && !image.startsWith('https://') && !image.startsWith('/')) {
image = '/' + image;
}
// Don't use external URLs
if (!image.startsWith('http://') && !image.startsWith('https://')) {
hasImage = true;
imageUrl = image.replace(/'/g, "\\'");
}
}
html += `<a href="/artist_profile.php?id=${artist.id}" class="global-search-item">
<div class="global-search-item-image" style="border-radius: 50%; overflow: hidden;">
${hasImage ? `<img src="${imageUrl}" alt="${escapeHtml(artist.name || 'Unknown')}" style="width: 100%; height: 100%; object-fit: cover; object-position: ${profilePosition.replace(/'/g, "\\'")};" onerror="this.style.display='none'; const fallback = this.nextElementSibling; if(fallback) { fallback.style.display='flex'; }">` : ''}
${!hasImage ? '<i class="fas fa-user"></i>' : '<i class="fas fa-user" style="display:none;"></i>'}
</div>
<div class="global-search-item-content">
<div class="global-search-item-title">${escapeHtml(artist.name)}</div>
<div class="global-search-item-subtitle">${subtitle}</div>
</div>
<i class="fas fa-chevron-right global-search-item-icon"></i>
</a>`;
});
html += '</div>';
}
// Genres
if (data.genres && data.genres.length > 0) {
hasResults = true;
html += '<div class="global-search-section"><div class="global-search-section-title"><i class="fas fa-compact-disc"></i> ' + translations.genres + '</div>';
data.genres.forEach(genre => {
const genreText = genre.genre || genre;
const trackCount = genre.track_count || 0;
const subtitle = trackCount > 0 ? `${trackCount} track${trackCount !== 1 ? 's' : ''}` : 'Genre';
html += `<a href="/community_fixed.php?genre=${encodeURIComponent(genreText)}" class="global-search-item">
<div class="global-search-item-image">
<i class="fas fa-compact-disc"></i>
</div>
<div class="global-search-item-content">
<div class="global-search-item-title">${escapeHtml(genreText)}</div>
<div class="global-search-item-subtitle">${subtitle}</div>
</div>
<i class="fas fa-chevron-right global-search-item-icon"></i>
</a>`;
});
html += '</div>';
}
// Events
if (data.events && data.events.length > 0) {
hasResults = true;
html += '<div class="global-search-section"><div class="global-search-section-title"><i class="fas fa-calendar-alt"></i> ' + translations.events + '</div>';
data.events.forEach(event => {
const image = event.cover_image || '/assets/images/default-event.png';
const date = new Date(event.start_date).toLocaleDateString();
html += `<a href="/event_details.php?id=${event.id}" class="global-search-item">
<div class="global-search-item-image" style="background-image: url('${image}'); background-size: cover; background-position: center;">
${!event.cover_image ? '<i class="fas fa-calendar-alt"></i>' : ''}
</div>
<div class="global-search-item-content">
<div class="global-search-item-title">${escapeHtml(event.title)}</div>
<div class="global-search-item-subtitle">${date} • ${escapeHtml(event.location || event.venue_name || '')}</div>
</div>
<i class="fas fa-chevron-right global-search-item-icon"></i>
</a>`;
});
html += '</div>';
}
// Crates
if (data.crates && data.crates.length > 0) {
hasResults = true;
html += '<div class="global-search-section"><div class="global-search-section-title"><i class="fas fa-box-open"></i> ' + (translations.crates || 'Crates') + '</div>';
data.crates.forEach(crate => {
const trackCount = crate.track_count || 0;
const duration = crate.duration_formatted || '';
const artistName = crate.artist_name || 'Unknown';
const subtitle = `${trackCount} track${trackCount !== 1 ? 's' : ''}${duration ? ' • ' + duration : ''} • by ${escapeHtml(artistName)}`;
// Use artist image if available
let hasImage = false;
let imageStyle = 'background: linear-gradient(135deg, #667eea, #764ba2);';
const image = crate.artist_image;
if (image && image.trim() !== '' && image !== 'null' && image !== 'NULL') {
let normalizedImage = image;
if (!image.startsWith('http://') && !image.startsWith('https://') && !image.startsWith('/')) {
normalizedImage = '/' + image;
}
if (!normalizedImage.startsWith('http://') && !normalizedImage.startsWith('https://')) {
hasImage = true;
imageStyle = `background-image: url('${normalizedImage.replace(/'/g, "\\'")}'); background-size: cover; background-position: center;`;
}
}
html += `<a href="/crate/${crate.id}" class="global-search-item">
<div class="global-search-item-image" style="${imageStyle}">
${!hasImage ? '<i class="fas fa-box-open" style="color: white;"></i>' : ''}
</div>
<div class="global-search-item-content">
<div class="global-search-item-title">${escapeHtml(crate.name)}</div>
<div class="global-search-item-subtitle">${subtitle}</div>
</div>
<i class="fas fa-chevron-right global-search-item-icon"></i>
</a>`;
});
html += '</div>';
}
if (!hasResults) {
html = `<div class="global-search-empty">
<i class="fas fa-search" style="font-size: 2rem; margin-bottom: 10px; opacity: 0.5;"></i>
<p>${translations.noResults} "${escapeHtml(query)}"</p>
</div>`;
} else {
// Add "View All Results" link at the end
html += `<div class="global-search-section" style="padding-top: 15px; border-top: 1px solid rgba(255, 255, 255, 0.1);">
<a href="/community_fixed.php?sort=latest&time=all&genre=all&page=1&search=${encodeURIComponent(query)}"
class="global-search-item"
style="justify-content: center; background: rgba(102, 126, 234, 0.2); border-radius: 10px; margin-top: 10px;">
<div class="global-search-item-content" style="text-align: center;">
<div class="global-search-item-title" style="font-weight: 600;">
<i class="fas fa-arrow-right" style="margin-right: 8px;"></i>
View All Results for "${escapeHtml(query)}"
</div>
</div>
</a>
</div>`;
}
results.innerHTML = html;
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Set up event listeners - MUST be called AFTER elements are initialized
function setupEventListeners() {
if (!input || !results || !overlay) {
console.error('Cannot set up event listeners - elements not initialized');
return;
}
console.log('Setting up global search event listeners');
// Input event listener
input.addEventListener('input', function(e) {
clearTimeout(searchTimeout);
const query = e.target.value.trim();
if (query.length < 2) {
// Show history if input is empty or too short
if (historyContainer) historyContainer.style.display = 'block';
displaySearchHistory();
if (results) {
results.innerHTML = '<div class="global-search-empty"><i class="fas fa-search" style="font-size: 2rem; margin-bottom: 10px; opacity: 0.5;"></i><p>Start typing to search...</p></div>';
}
} else {
// Hide history when typing
if (historyContainer) historyContainer.style.display = 'none';
searchTimeout = setTimeout(() => {
performSearch(query);
}, 300);
}
});
// Keydown event listener
input.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeGlobalSearch();
} else if (e.key === 'Enter') {
const query = input.value.trim();
if (query.length >= 2) {
// Navigate to community_fixed.php with search parameter
const searchUrl = `/community_fixed.php?sort=latest&time=all&genre=all&page=1&search=${encodeURIComponent(query)}`;
// Use AJAX navigation if available, otherwise full page reload
if (window.ajaxNavigation && typeof window.ajaxNavigation.navigateToPage === 'function') {
window.ajaxNavigation.navigateToPage(searchUrl);
} else {
window.location.href = searchUrl;
}
closeGlobalSearch();
}
}
});
// Focus event listener
input.addEventListener('focus', function() {
if (input.value.trim() === '' && isLoggedIn) {
displaySearchHistory();
}
});
// Overlay click to close
overlay.addEventListener('click', function(e) {
if (e.target === overlay) {
closeGlobalSearch();
}
});
console.log('Global search event listeners set up successfully');
}
// Expose functions globally
window.openGlobalSearch = openGlobalSearch;
window.closeGlobalSearch = closeGlobalSearch;
window.searchFromHistory = searchFromHistory;
window.deleteSearchHistory = deleteSearchHistory;
// Initialize when DOM is ready
function initSearch() {
console.log('Initializing global search...');
if (!initElements()) {
console.warn('Global search: Retrying initialization...');
setTimeout(initSearch, 100);
return;
}
console.log('Global search elements initialized:', {
overlay: !!overlay,
input: !!input,
results: !!results
});
// Set up event listeners AFTER elements are initialized
setupEventListeners();
}
// Initialize when DOM is ready - optimized to reduce main-thread blocking
// Initialize critical elements immediately (lightweight)
initElements();
// Defer search initialization to reduce blocking
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
if ('requestIdleCallback' in window) {
requestIdleCallback(initSearch, { timeout: 1000 });
} else {
setTimeout(initSearch, 50);
}
});
} else {
// DOM already ready, defer initialization
if ('requestIdleCallback' in window) {
requestIdleCallback(initSearch, { timeout: 1000 });
} else {
setTimeout(initSearch, 50);
}
}
})();
</script>
<?php endif; ?>
<!-- Main Content -->
<main class="main-content" id="mainContent">
<div class="container" id="pageContainer">