![]() 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/ |
<?php
session_start();
// Prevent browser caching for this page (force fresh content on refresh)
if (!isset($_GET['ajax']) || $_GET['ajax'] != '1') {
header('Cache-Control: no-cache, no-store, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Expires: 0');
// Add ETag with timestamp to force revalidation
header('ETag: "' . md5(__FILE__ . filemtime(__FILE__)) . '"');
}
// Include translation system
require_once 'includes/translations.php';
// Include audio token system for signed URLs
require_once 'utils/audio_token.php';
// Handle AJAX requests
$is_ajax = isset($_GET['ajax']) && $_GET['ajax'] == '1';
if ($is_ajax) {
// For AJAX requests, wrap content in the proper container structure
echo '<div class="container" id="pageContainer">';
} else {
// Include header only for full page loads
// Add cache-busting meta tags before header
echo '<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">';
echo '<meta http-equiv="Pragma" content="no-cache">';
echo '<meta http-equiv="Expires" content="0">';
include 'includes/header.php';
}
// Global player is included via footer.php
require_once 'config/database.php';
$pdo = getDBConnection();
// Get all users with their music and social stats (including users with no tracks)
// Optimized query with better performance
$stmt = $pdo->prepare("
SELECT
u.id,
u.name as username,
u.email,
u.plan,
u.credits,
u.created_at as joined_date,
COALESCE(up.profile_image, u.profile_image) as profile_image,
COALESCE(up.profile_position, 'center center') as profile_position,
COALESCE(up.cover_image, '') as cover_image,
COALESCE(stats.total_tracks, 0) as total_tracks,
COALESCE(stats.completed_tracks, 0) as completed_tracks,
COALESCE(stats.processing_tracks, 0) as processing_tracks,
COALESCE(stats.failed_tracks, 0) as failed_tracks,
stats.last_activity,
COALESCE(stats.total_duration, 0) as total_duration,
COALESCE(stats.total_plays, 0) as total_plays,
COALESCE(followers.followers_count, 0) as followers_count,
COALESCE(following.following_count, 0) as following_count,
CASE
WHEN COALESCE(stats.completed_tracks, 0) >= 20 THEN '🎵 Pro Creator'
WHEN COALESCE(stats.completed_tracks, 0) >= 10 THEN '⭐ Rising Star'
WHEN COALESCE(stats.completed_tracks, 0) >= 5 THEN '🔥 Active Artist'
WHEN COALESCE(stats.completed_tracks, 0) >= 1 THEN '🎼 New Artist'
ELSE '👤 Member'
END as badge
FROM users u
LEFT JOIN user_profiles up ON u.id = up.user_id
LEFT JOIN (
SELECT
user_id,
COUNT(CASE WHEN is_public = 1 THEN 1 END) as total_tracks,
COUNT(CASE WHEN status = 'complete' AND is_public = 1 THEN 1 END) as completed_tracks,
COUNT(CASE WHEN status = 'processing' THEN 1 END) as processing_tracks,
COUNT(CASE WHEN status = 'failed' THEN 1 END) as failed_tracks,
MAX(created_at) as last_activity,
SUM(CASE WHEN status = 'complete' AND is_public = 1 THEN duration ELSE 0 END) as total_duration,
SUM(CASE WHEN status = 'complete' AND is_public = 1 THEN COALESCE(play_counts.total_track_plays, 0) ELSE 0 END) as total_plays
FROM music_tracks
LEFT JOIN (
SELECT track_id, COUNT(*) as total_track_plays
FROM track_plays
GROUP BY track_id
) play_counts ON music_tracks.id = play_counts.track_id
GROUP BY user_id
) stats ON u.id = stats.user_id
LEFT JOIN (
SELECT following_id, COUNT(*) as followers_count
FROM user_follows
GROUP BY following_id
) followers ON u.id = followers.following_id
LEFT JOIN (
SELECT follower_id, COUNT(*) as following_count
FROM user_follows
GROUP BY follower_id
) following ON u.id = following.follower_id
ORDER BY stats.completed_tracks DESC, stats.last_activity DESC
");
$stmt->execute();
$artists = $stmt->fetchAll();
// Translate badges after fetching
foreach ($artists as &$artist) {
if (!empty($artist['badge'])) {
$badge_text = $artist['badge'];
// Extract emoji and translate text
if (strpos($badge_text, 'Pro Creator') !== false) {
$artist['badge'] = '🎵 ' . t('artists.badge_pro_creator');
} elseif (strpos($badge_text, 'Rising Star') !== false) {
$artist['badge'] = '⭐ ' . t('artists.badge_rising_star');
} elseif (strpos($badge_text, 'Active Artist') !== false) {
$artist['badge'] = '🔥 ' . t('artists.badge_active_artist');
} elseif (strpos($badge_text, 'New Artist') !== false) {
$artist['badge'] = '🎼 ' . t('artists.badge_new_artist');
} elseif (strpos($badge_text, 'Member') !== false) {
$artist['badge'] = '👤 ' . t('artists.badge_member');
}
}
}
unset($artist); // Break reference
// Check follow status for each artist if user is logged in
if (isset($_SESSION['user_id'])) {
$current_user_id = $_SESSION['user_id'];
// Get all artists that the current user is following
$stmt = $pdo->prepare("
SELECT following_id
FROM user_follows
WHERE follower_id = ?
");
$stmt->execute([$current_user_id]);
$following_ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
// Add follow status to each artist
foreach ($artists as &$artist) {
$artist['is_following'] = in_array($artist['id'], $following_ids);
}
} else {
// If not logged in, set all artists as not following
foreach ($artists as &$artist) {
$artist['is_following'] = false;
}
}
// Calculate popularity score for each artist (combination of followers and tracks)
foreach ($artists as &$artist) {
$followers_score = ($artist['followers_count'] ?? 0) * 2; // Followers weighted more
$tracks_score = ($artist['completed_tracks'] ?? 0) * 1;
$artist['popularity_score'] = $followers_score + $tracks_score;
}
unset($artist);
// Sort by popularity score and get Hall of Fame artists (top 6-8)
usort($artists, function($a, $b) {
return ($b['popularity_score'] ?? 0) - ($a['popularity_score'] ?? 0);
});
// Get Hall of Fame artists (top 8, but we'll display 6-8 based on availability)
$hallOfFameArtists = [];
if (!empty($artists)) {
$hallOfFameArtists = array_slice($artists, 0, 8);
// Filter to only include artists with at least some activity
$hallOfFameArtists = array_filter($hallOfFameArtists, function($artist) {
return ($artist['popularity_score'] ?? 0) > 0;
});
// Reindex array after filtering to ensure sequential keys
$hallOfFameArtists = array_values($hallOfFameArtists);
$hallOfFameArtists = array_slice($hallOfFameArtists, 0, 8);
}
// Get current user's credits if logged in
$current_user_credits = 0;
if (isset($_SESSION['user_id'])) {
$stmt = $pdo->prepare("SELECT credits FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
$current_user_credits = $stmt->fetchColumn() ?? 0;
}
// Set page variables for header
$page_title = t('artists.page_title');
$page_description = t('artists.page_description');
$current_page = 'artists';
?>
<style>
/* Artists-specific styles - Clean and Modern */
/* Hero Section */
.hero {
padding: 8rem 0 6rem;
text-align: center;
color: white;
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0a0a0a 100%);
position: relative;
overflow: visible;
margin-bottom: 4rem;
margin-top: 0;
}
.hero::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;
}
.hero-content {
max-width: 90rem;
margin: 0 auto;
position: relative;
z-index: 2;
}
.hero-badge {
display: inline-block;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2));
color: #667eea;
padding: 1.2rem 2.4rem;
border-radius: 50px;
font-size: 1.4rem;
font-weight: 600;
margin-bottom: 2rem;
backdrop-filter: blur(10px);
border: 1px solid rgba(102, 126, 234, 0.3);
}
.hero-title {
font-size: 5.6rem;
font-weight: 900;
line-height: 1.1;
margin-bottom: 2.4rem;
background: linear-gradient(135deg, #ffffff, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero-subtitle {
font-size: 2rem;
font-weight: 400;
margin-bottom: 4rem;
opacity: 0.9;
max-width: 70rem;
margin-left: auto;
margin-right: auto;
color: #a0aec0;
}
/* Artists Content */
.artists-content {
background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);
padding: 6rem 0;
border-radius: 40px 40px 0 0;
margin-top: 0;
position: relative;
z-index: 10;
min-height: 100vh;
}
.artists-container {
max-width: 1400px;
margin: 0 auto;
padding: 0 20px;
}
/* Artists Grid - Community Fixed Style */
.artists-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 30px;
margin-top: 40px;
}
/* Artist Card - Community Fixed Style */
.artist-card-community {
background: rgba(255, 255, 255, 0.98);
border-radius: 20px;
overflow: hidden;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
cursor: pointer;
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
border: 1px solid rgba(255, 255, 255, 0.3);
animation: cardFadeIn 0.6s ease both;
}
@keyframes cardFadeIn {
from {
opacity: 0;
transform: translateY(20px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.artist-card-community:nth-child(1) { animation-delay: 0.1s; }
.artist-card-community:nth-child(2) { animation-delay: 0.15s; }
.artist-card-community:nth-child(3) { animation-delay: 0.2s; }
.artist-card-community:nth-child(4) { animation-delay: 0.25s; }
.artist-card-community:nth-child(5) { animation-delay: 0.3s; }
.artist-card-community:nth-child(6) { animation-delay: 0.35s; }
.artist-card-community:hover {
transform: translateY(-10px) scale(1.02);
box-shadow: 0 20px 40px rgba(0,0,0,0.3);
border-color: rgba(255, 255, 255, 0.5);
}
.artist-image-wrapper {
position: relative;
width: 100%;
padding-top: 100%;
overflow: hidden;
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 25%, #16213e 50%, #0f3460 75%, #0a0a0a 100%);
}
.artist-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.artist-card-community:hover .artist-image {
transform: scale(1.1) rotate(2deg);
}
.artist-image-placeholder {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 25%, #16213e 50%, #0f3460 75%, #0a0a0a 100%);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.artist-image-placeholder-pattern {
position: absolute;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
}
.artist-image-placeholder-pattern i {
font-size: 5rem;
color: rgba(102, 126, 234, 0.6);
animation: pulse 2s ease-in-out infinite;
text-shadow: 0 0 20px rgba(102, 126, 234, 0.5);
}
.artist-image-placeholder-gradient-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.3) 0%, rgba(118, 75, 162, 0.3) 50%, rgba(240, 147, 251, 0.2) 100%);
z-index: 2;
}
.artist-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.8) 0%, rgba(118, 75, 162, 0.8) 100%);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: all 0.4s ease;
}
.artist-card-community:hover .artist-overlay {
opacity: 1;
}
.view-profile-btn-overlay {
width: 70px;
height: 70px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.95);
border: 3px solid rgba(255, 255, 255, 0.5);
color: #667eea;
font-size: 1.8rem;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
transform: scale(0.9);
text-decoration: none;
}
.artist-card-community:hover .view-profile-btn-overlay {
transform: scale(1);
}
.view-profile-btn-overlay:hover {
transform: scale(1.15);
box-shadow: 0 15px 40px rgba(0,0,0,0.4);
background: white;
}
.artist-info-section {
padding: 20px;
background: white;
}
.artist-name-link {
font-size: 1.15rem;
font-weight: 700;
margin: 0 0 8px 0;
color: #1a1a1a;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
letter-spacing: -0.3px;
text-decoration: none;
display: block;
cursor: pointer;
transition: color 0.2s ease;
}
.artist-name-link:hover {
color: #667eea;
text-decoration: underline;
}
.artist-plan-link {
color: #666;
text-decoration: none;
font-size: 0.9rem;
font-weight: 500;
transition: all 0.3s ease;
display: inline-block;
}
.artist-plan-link:hover {
color: #667eea;
transform: translateX(3px);
}
.artist-stats-section {
display: flex;
padding: 12px 20px;
border-top: 1px solid #f0f0f0;
gap: 8px;
background: #fafafa;
}
.artist-stat-btn {
flex: 1;
padding: 10px;
border: none;
background: transparent;
color: #666;
cursor: pointer;
border-radius: 10px;
transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
font-size: 0.85rem;
font-weight: 600;
}
.artist-stat-btn:hover {
background: linear-gradient(135deg, #667eea15, #764ba215);
color: #667eea;
transform: translateY(-2px);
}
.artist-stat-btn.following {
color: #667eea;
background: rgba(102, 126, 234, 0.1);
}
.artist-follow-actions {
padding: 0 20px 20px;
background: #fafafa;
border-top: 1px solid #f0f0f0;
}
.artist-follow-actions .follow-btn {
width: 100%;
justify-content: center;
gap: 0.5rem;
}
.artist-follow-actions .follow-btn .label-text {
font-weight: 600;
}
.artist-card {
background: rgba(255, 255, 255, 0.05);
padding: 3rem;
border-radius: 24px;
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.artist-card.has-badge {
padding-top: 4rem;
}
.artist-card:hover {
transform: translateY(-10px);
border-color: rgba(102, 126, 234, 0.3);
box-shadow: 0 30px 80px rgba(102, 126, 234, 0.2);
}
/* Touch-friendly improvements */
@media (hover: none) and (pointer: coarse) {
.artist-card:hover {
transform: none;
box-shadow: none;
}
.artist-card:active {
transform: scale(0.98);
background: rgba(255, 255, 255, 0.08);
}
.view-profile-btn:active,
.follow-btn:active {
transform: scale(0.95);
}
.artist-card-community:hover {
transform: none;
}
.artist-card-community:active {
transform: scale(0.98);
}
.artist-stat-btn:active {
transform: scale(0.95);
}
.view-profile-btn-overlay:active {
transform: scale(0.9);
}
}
.artist-badge {
position: absolute;
top: 1rem;
right: 1rem;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
padding: 0.5rem 1rem;
border-radius: 20px;
font-size: 1.2rem;
font-weight: 600;
z-index: 10;
}
.artist-header {
display: flex;
align-items: center;
gap: 1.5rem;
margin-bottom: 2rem;
}
.artist-profile {
flex-shrink: 0;
}
.artist-avatar {
width: 6rem;
height: 6rem;
border-radius: 50%;
background: linear-gradient(135deg, #667eea, #764ba2);
display: flex;
align-items: center;
justify-content: center;
font-size: 2.4rem;
font-weight: 900;
color: white;
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
cursor: pointer;
transition: all 0.3s ease;
object-fit: cover;
}
.artist-avatar:hover {
transform: scale(1.1);
}
.default-avatar {
width: 6rem;
height: 6rem;
border-radius: 50%;
background: linear-gradient(135deg, #667eea, #764ba2);
display: flex;
align-items: center;
justify-content: center;
font-size: 2.4rem;
font-weight: 900;
color: white;
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
cursor: pointer;
transition: all 0.3s ease;
}
.default-avatar:hover {
transform: scale(1.1);
}
.clickable-avatar {
cursor: pointer;
}
.artist-info {
flex: 1;
text-align: left;
}
.artist-name {
font-size: 2rem;
font-weight: 700;
color: white;
margin-bottom: 0.5rem;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
}
.artist-plan {
margin-bottom: 1rem;
}
.plan-badge {
display: inline-block;
padding: 0.5rem 1rem;
border-radius: 12px;
font-size: 1.2rem;
font-weight: 600;
}
.plan-free {
background: rgba(102, 126, 234, 0.2);
color: #667eea;
}
.plan-premium {
background: rgba(255, 193, 7, 0.2);
color: #ffc107;
}
.plan-pro {
background: rgba(72, 187, 120, 0.2);
color: #48bb78;
}
.artist-stats {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
margin-bottom: 2rem;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding: 1rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
}
.stat-item:hover {
background: rgba(102, 126, 234, 0.1);
border-color: rgba(102, 126, 234, 0.3);
transform: translateY(-2px);
}
.stat-number {
font-size: 2rem;
font-weight: 700;
color: #667eea;
margin-bottom: 0.3rem;
}
.stat-label {
font-size: 1.1rem;
color: #a0aec0;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* Clickable Stats */
.clickable-stat {
cursor: pointer;
position: relative;
overflow: hidden;
}
.clickable-stat::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1));
opacity: 0;
transition: opacity 0.3s ease;
border-radius: 12px;
}
.clickable-stat:hover::before {
opacity: 1;
}
.clickable-stat:hover .stat-number {
color: #667eea;
transform: scale(1.05);
}
.clickable-stat:hover .stat-label {
color: #667eea;
}
/* Modal Infrastructure */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
backdrop-filter: blur(10px);
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
visibility: hidden;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
transform: scale(0.95);
}
.modal-overlay.active {
opacity: 1;
visibility: visible;
transform: scale(1);
}
.modal-container {
background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);
border-radius: 24px;
border: 2px solid rgba(102, 126, 234, 0.3);
max-width: 90vw;
max-height: 90vh;
width: 800px;
overflow: hidden;
transform: scale(0.9) translateY(20px);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.8);
}
.modal-overlay.active .modal-container {
transform: scale(1) translateY(0);
}
.modal-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 2rem 3rem;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.modal-title {
color: white;
font-size: 2.4rem;
font-weight: 700;
display: flex;
align-items: center;
gap: 1rem;
}
.modal-close {
background: none;
border: none;
color: white;
font-size: 2.4rem;
cursor: pointer;
padding: 0.5rem;
border-radius: 8px;
transition: all 0.3s ease;
}
.modal-close:hover {
background: rgba(255, 255, 255, 0.1);
transform: scale(1.1);
}
.modal-content {
padding: 3rem;
max-height: 70vh;
overflow-y: auto;
}
/* Modal Content Styles */
.modal-search {
margin-bottom: 2rem;
}
.modal-search input {
width: 100%;
padding: 1.2rem 2rem;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 12px;
color: white;
font-size: 1.6rem;
transition: all 0.3s ease;
}
.modal-search input:focus {
outline: none;
border-color: #667eea;
background: rgba(255, 255, 255, 0.15);
}
.modal-search input::placeholder {
color: #a0aec0;
}
.modal-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 2rem;
}
.modal-item {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
padding: 2rem;
transition: all 0.3s ease;
cursor: pointer;
}
.modal-item:hover {
background: rgba(255, 255, 255, 0.1);
border-color: rgba(102, 126, 234, 0.3);
transform: translateY(-4px);
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.2);
}
.modal-loading {
text-align: center;
padding: 4rem;
color: #a0aec0;
font-size: 1.8rem;
}
.modal-empty {
text-align: center;
padding: 4rem;
color: #a0aec0;
font-size: 1.8rem;
}
.modal-empty i {
font-size: 4rem;
margin-bottom: 1rem;
opacity: 0.5;
}
/* Track Item Styles */
.track-item {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.track-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.track-title {
font-size: 1.8rem;
font-weight: 600;
color: white;
flex: 1;
}
.track-duration {
font-size: 1.4rem;
color: #a0aec0;
background: rgba(255, 255, 255, 0.1);
padding: 0.5rem 1rem;
border-radius: 8px;
}
.track-info {
display: flex;
gap: 1rem;
font-size: 1.2rem;
color: #a0aec0;
}
.track-genre, .track-type {
background: rgba(102, 126, 234, 0.2);
color: #667eea;
padding: 0.3rem 0.8rem;
border-radius: 6px;
font-weight: 500;
text-transform: uppercase;
font-size: 1rem;
}
.track-preview {
width: 100%;
display: flex;
justify-content: center;
}
.preview-play-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 1.2rem 2.4rem;
border-radius: 12px;
font-size: 1.6rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 1rem;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
width: 100%;
justify-content: center;
}
.preview-play-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}
.preview-play-btn.playing {
background: linear-gradient(135deg, #48bb78, #38a169);
box-shadow: 0 4px 15px rgba(72, 187, 120, 0.3);
}
.preview-play-btn.playing:hover {
box-shadow: 0 8px 25px rgba(72, 187, 120, 0.4);
}
.track-actions {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.track-actions .btn {
flex: 1;
min-width: 120px;
padding: 1rem 1.5rem;
font-size: 1.4rem;
}
.purchase-btn {
background: linear-gradient(135deg, #48bb78, #38a169);
color: white;
border: none;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.purchase-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(72, 187, 120, 0.4);
}
.add-to-playlist-btn {
background: rgba(255, 255, 255, 0.1);
color: white;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.add-to-playlist-btn:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-2px);
}
/* Follower/Following Item Styles */
.follower-item, .following-item {
display: flex;
align-items: center;
gap: 1.5rem;
padding: 1.5rem;
}
.follower-avatar, .following-avatar {
width: 50px;
height: 50px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 1.8rem;
font-weight: 600;
flex-shrink: 0;
}
.follower-info, .following-info {
flex: 1;
min-width: 0;
}
.follower-name, .following-name {
font-size: 1.6rem;
font-weight: 600;
color: white;
margin-bottom: 0.3rem;
}
.follower-stats, .following-stats {
font-size: 1.2rem;
color: #a0aec0;
}
.follower-item .follow-btn, .following-item .follow-btn {
flex-shrink: 0;
min-width: 100px;
padding: 0.8rem 1.5rem;
font-size: 1.3rem;
}
/* Purchase Modal Styles */
.purchase-modal {
max-width: 500px;
}
.purchase-loading {
text-align: center;
padding: 3rem;
color: #a0aec0;
}
.purchase-details {
padding: 2rem;
}
.track-purchase-info {
background: rgba(102, 126, 234, 0.1);
border: 1px solid rgba(102, 126, 234, 0.3);
border-radius: 12px;
padding: 2rem;
margin-bottom: 2rem;
}
.track-purchase-title {
font-size: 2rem;
font-weight: 600;
color: white;
margin-bottom: 0.5rem;
}
.track-purchase-artist {
font-size: 1.4rem;
color: #667eea;
margin-bottom: 1rem;
}
.track-purchase-price {
font-size: 1.8rem;
font-weight: 600;
color: #48bb78;
margin-bottom: 1rem;
}
.track-purchase-preview {
margin: 1.5rem 0;
}
.purchase-actions {
display: flex;
gap: 1rem;
margin-top: 2rem;
}
.purchase-btn {
flex: 1;
padding: 1.2rem;
border: none;
border-radius: 8px;
font-size: 1.6rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.purchase-confirm {
background: linear-gradient(135deg, #48bb78, #38a169);
color: white;
}
.purchase-confirm:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(72, 187, 120, 0.4);
}
.purchase-cancel {
background: rgba(255, 255, 255, 0.1);
color: #a0aec0;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.purchase-cancel:hover {
background: rgba(255, 255, 255, 0.2);
color: white;
}
.purchase-success {
text-align: center;
padding: 3rem;
}
.purchase-success i {
font-size: 4rem;
color: #48bb78;
margin-bottom: 1rem;
}
.purchase-success h3 {
font-size: 2rem;
color: white;
margin-bottom: 1rem;
}
.purchase-success p {
font-size: 1.4rem;
color: #a0aec0;
margin-bottom: 2rem;
}
/* Stripe Payment Form Styles */
#payment-element {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 12px;
padding: 2rem;
margin: 2rem 0;
}
#payment-element .StripeElement {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
padding: 1rem;
color: white;
}
#payment-element .StripeElement--focus {
border-color: #667eea;
box-shadow: 0 0 0 1px #667eea;
}
#payment-element .StripeElement--invalid {
border-color: #f56565;
}
.payment-info {
background: rgba(102, 126, 234, 0.1);
border: 1px solid rgba(102, 126, 234, 0.3);
border-radius: 12px;
padding: 1.5rem;
margin: 1rem 0;
color: #a0aec0;
font-size: 1.3rem;
line-height: 1.5;
}
/* Login Prompt Styles */
.login-prompt {
text-align: center;
padding: 2rem;
}
.login-prompt-icon {
font-size: 4rem;
color: #667eea;
margin-bottom: 1rem;
}
.login-prompt h3 {
font-size: 2rem;
color: white;
margin-bottom: 1rem;
}
.login-prompt p {
font-size: 1.4rem;
color: #a0aec0;
margin-bottom: 2rem;
line-height: 1.5;
}
.login-prompt-actions {
display: flex;
gap: 1rem;
margin-bottom: 1.5rem;
}
.login-prompt-actions .purchase-btn {
flex: 1;
text-decoration: none;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.login-prompt-note {
color: #718096;
font-size: 1.2rem;
}
.artist-details {
margin-bottom: 2rem;
}
.detail-row {
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
font-size: 1.3rem;
color: #a0aec0;
}
.detail-row i {
color: #667eea;
margin-right: 0.5rem;
}
.track-status {
margin-bottom: 2rem;
}
.status-processing {
color: #ffc107;
font-size: 1.3rem;
margin-right: 1rem;
}
.status-failed {
color: #f56565;
font-size: 1.3rem;
}
.artist-actions {
display: flex;
gap: 1rem;
}
.view-profile-btn {
flex: 1;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 1.2rem 2rem;
border-radius: 12px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.8rem;
}
.view-profile-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.4);
}
.follow-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 1.2rem 2rem;
border-radius: 12px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 0.8rem;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}
.follow-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.4);
}
.follow-btn.following {
background: linear-gradient(135deg, #48bb78, #38a169);
border-color: #48bb78;
color: white;
}
.follow-btn.following:hover {
background: linear-gradient(135deg, #38a169, #2f855a);
transform: translateY(-2px);
box-shadow: 0 10px 30px rgba(72, 187, 120, 0.4);
}
/* Loading state for buttons */
.follow-btn:disabled {
opacity: 0.7;
cursor: not-allowed;
}
.follow-btn.login-required {
background: linear-gradient(135deg, #a0aec0, #718096);
opacity: 0.8;
}
.follow-btn.login-required:hover {
opacity: 1;
transform: translateY(-2px);
}
/* Animation for button state changes */
.follow-btn {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Pulse animation for following state */
@keyframes pulse-follow {
0% { box-shadow: 0 0 0 0 rgba(72, 187, 120, 0.7); }
70% { box-shadow: 0 0 0 10px rgba(72, 187, 120, 0); }
100% { box-shadow: 0 0 0 0 rgba(72, 187, 120, 0); }
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
opacity: 0.6;
}
50% {
transform: scale(1.1);
opacity: 0.8;
}
}
/* Floating hearts animation - BIGGER and more dramatic! */
@keyframes float-heart {
0% {
transform: translateY(0) rotate(0deg) scale(1);
opacity: 1;
}
50% {
transform: translateY(-50px) rotate(180deg) scale(1.2);
opacity: 0.8;
}
100% {
transform: translateY(-150px) rotate(360deg) scale(0.8);
opacity: 0;
}
}
/* Heart pulse animation */
@keyframes heart-pulse {
0% { transform: scale(1); }
50% { transform: scale(1.3); }
100% { transform: scale(1); }
}
/* Sparkle twinkle animation */
@keyframes sparkle-twinkle {
0%, 100% { opacity: 0; transform: scale(0.5) rotate(0deg); }
50% { opacity: 1; transform: scale(1.2) rotate(180deg); }
}
.floating-heart {
position: absolute;
pointer-events: none;
animation: float-heart 2s ease-out forwards;
filter: drop-shadow(0 0 8px rgba(255, 107, 107, 0.6));
}
.sparkle {
position: absolute;
pointer-events: none;
animation: sparkle-twinkle 1.5s ease-out forwards;
}
/* Enhanced button effects */
.follow-btn.following {
animation: pulse-follow 2s infinite, button-glow 1s ease-out;
}
@keyframes button-glow {
0% { box-shadow: 0 0 0 0 rgba(72, 187, 120, 0.7); }
50% { box-shadow: 0 0 20px 10px rgba(72, 187, 120, 0.4); }
100% { box-shadow: 0 0 0 0 rgba(72, 187, 120, 0); }
}
.last-activity {
font-size: 1.2rem;
color: #718096;
margin-top: 1rem;
text-align: center;
}
/* Empty State */
.empty-state {
text-align: center;
padding: 8rem 2rem;
grid-column: 1 / -1;
}
.empty-icon {
font-size: 8rem;
margin-bottom: 2rem;
opacity: 0.5;
}
.empty-title {
font-size: 3.2rem;
color: white;
margin-bottom: 1.5rem;
font-weight: 700;
}
.empty-description {
font-size: 1.8rem;
color: #a0aec0;
margin-bottom: 3rem;
max-width: 50rem;
margin-left: auto;
margin-right: auto;
}
/* Responsive Design */
@media (max-width: 768px) {
.hero-title {
font-size: 3.2rem;
line-height: 1.2;
}
.hero-subtitle {
font-size: 1.4rem;
line-height: 1.5;
}
.hero-badge {
padding: 1rem 2rem;
font-size: 1.2rem;
}
.hero {
padding: 4rem 0 3rem;
}
.artists-grid {
grid-template-columns: 1fr;
gap: 1.5rem;
padding: 0 1rem;
}
.artist-card {
padding: 2rem 1.5rem;
margin: 0;
}
.artist-header {
flex-direction: column;
text-align: center;
gap: 1rem;
}
.artist-avatar {
width: 6rem;
height: 6rem;
font-size: 2.4rem;
}
.default-avatar {
width: 6rem;
height: 6rem;
font-size: 2.4rem;
}
.artist-name {
font-size: 2rem;
text-align: center;
}
.artist-stats {
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
margin: 1.5rem 0;
}
.stat-item {
font-size: 1.3rem;
padding: 1rem;
text-align: center;
}
.artist-details {
margin: 1.5rem 0;
}
.detail-row {
flex-direction: column;
gap: 0.5rem;
text-align: center;
}
.artist-actions {
flex-direction: column;
gap: 1rem;
}
.view-profile-btn, .follow-btn {
padding: 1.2rem 2rem;
font-size: 1.4rem;
min-height: 48px;
width: 100%;
}
.last-activity {
font-size: 1.1rem;
text-align: center;
}
.container {
padding: 0 1rem;
}
.artists-content {
padding: 2rem 0;
}
.live-stats {
flex-direction: column;
gap: 1rem;
margin: 2rem 0;
}
.stat-item {
min-width: auto;
width: 100%;
padding: 1.5rem;
}
.search-container {
margin: 2rem 0;
}
.search-box {
margin-bottom: 1rem;
}
.search-filters {
flex-wrap: wrap;
gap: 0.5rem;
justify-content: center;
}
.filter-btn {
padding: 0.8rem 1.2rem;
font-size: 1.2rem;
min-height: 44px;
}
.search-input {
font-size: 1.6rem;
padding: 1.2rem 2rem 1.2rem 5rem;
min-height: 52px;
}
.search-icon {
font-size: 1.8rem;
left: 1.5rem;
}
.hero-actions {
flex-direction: column;
gap: 1rem;
align-items: center;
}
.cta-btn {
padding: 1.2rem 2rem;
font-size: 1.4rem;
min-height: 48px;
width: 100%;
max-width: 300px;
}
.floating-cards {
position: relative;
height: auto;
display: flex;
gap: 1rem;
padding: 2rem 1rem;
overflow-x: auto;
pointer-events: auto;
scroll-snap-type: x mandatory;
z-index: 5;
margin-bottom: 2rem;
}
.floating-card {
position: relative;
width: 160px;
height: 210px;
opacity: 1;
animation: none;
transform: none !important;
pointer-events: auto;
flex-shrink: 0;
scroll-snap-align: center;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
}
.animated-badge {
flex-direction: column;
gap: 0.5rem;
text-align: center;
}
.typing-title {
font-size: 2.8rem;
}
.animated-subtitle {
font-size: 1.3rem;
}
/* Epic Hero Mobile Adjustments */
.epic-hero {
min-height: 60vh;
padding: 1.5rem 0;
}
/* Artists Container Mobile */
.artists-container {
padding: 0 1rem;
}
/* Artists Grid Mobile */
.artists-grid {
gap: 1.5rem;
margin-top: 2rem;
}
/* Artist Card Community Mobile */
.artist-card-community {
border-radius: 16px;
}
.artist-card-community:hover {
transform: none;
}
/* Artist Info Section Mobile */
.artist-info-section {
padding: 1.5rem;
}
/* Artist Name Link Mobile */
.artist-name-link {
font-size: 1rem;
white-space: normal;
word-wrap: break-word;
overflow-wrap: break-word;
line-height: 1.4;
}
/* Artist Plan Link Mobile */
.artist-plan-link {
font-size: 0.85rem;
}
/* Artist Stats Section Mobile */
.artist-stats-section {
padding: 10px 1rem;
gap: 6px;
}
/* Artist Stat Button Mobile */
.artist-stat-btn {
padding: 8px;
font-size: 0.75rem;
gap: 4px;
}
/* View Profile Button Overlay Mobile */
.view-profile-btn-overlay {
width: 50px;
height: 50px;
font-size: 1.4rem;
}
/* Hero Content Mobile */
.hero-content {
padding: 0 1rem;
}
/* Empty State Mobile */
.empty-state {
padding: 4rem 1.5rem;
}
.empty-icon {
font-size: 5rem;
margin-bottom: 1.5rem;
}
.empty-title {
font-size: 2.4rem;
margin-bottom: 1rem;
}
.empty-description {
font-size: 1.4rem;
margin-bottom: 2rem;
}
/* Modal Mobile */
.modal-container {
width: 95vw;
max-width: 95vw;
max-height: 85vh;
border-radius: 16px;
}
.modal-header {
padding: 1.5rem 1.5rem;
}
.modal-title {
font-size: 1.8rem;
}
.modal-close {
font-size: 2rem;
min-width: 44px;
min-height: 44px;
}
.modal-content {
padding: 1.5rem;
max-height: 65vh;
}
}
/* Extra small devices */
@media (max-width: 480px) {
.hero-title {
font-size: 2.8rem;
}
.hero-subtitle {
font-size: 1.2rem;
}
.artist-card {
padding: 1.5rem 1rem;
}
.artist-stats {
grid-template-columns: 1fr;
gap: 0.8rem;
}
.stat-item {
font-size: 1.2rem;
padding: 0.8rem;
}
.view-profile-btn, .follow-btn {
padding: 1rem 1.5rem;
font-size: 1.3rem;
}
.search-filters {
flex-direction: column;
align-items: stretch;
}
.filter-btn {
width: 100%;
}
.search-input {
font-size: 1.4rem;
padding: 1rem 1.5rem 1rem 4.5rem;
min-height: 48px;
}
.search-icon {
font-size: 1.6rem;
left: 1.2rem;
}
/* Epic Hero Extra Small */
.epic-hero {
min-height: 50vh;
padding: 1rem 0;
}
/* Artists Container Extra Small */
.artists-container {
padding: 0 0.75rem;
}
/* Artists Grid Extra Small */
.artists-grid {
gap: 1rem;
margin-top: 1.5rem;
}
/* Artist Card Community Extra Small */
.artist-card-community {
border-radius: 12px;
}
/* Artist Info Section Extra Small */
.artist-info-section {
padding: 1rem;
}
/* Artist Name Link Extra Small */
.artist-name-link {
font-size: 0.95rem;
}
/* Artist Plan Link Extra Small */
.artist-plan-link {
font-size: 0.8rem;
}
/* Artist Stats Section Extra Small */
.artist-stats-section {
padding: 8px 0.75rem;
gap: 4px;
}
/* Artist Stat Button Extra Small */
.artist-stat-btn {
padding: 6px 4px;
font-size: 0.7rem;
gap: 3px;
}
.artist-stat-btn i {
font-size: 0.8rem;
}
/* View Profile Button Overlay Extra Small */
.view-profile-btn-overlay {
width: 45px;
height: 45px;
font-size: 1.2rem;
}
/* Artists Content Extra Small */
.artists-content {
padding: 1.5rem 0;
border-radius: 20px 20px 0 0;
}
/* Empty State Extra Small */
.empty-state {
padding: 3rem 1rem;
}
.empty-icon {
font-size: 4rem;
margin-bottom: 1rem;
}
.empty-title {
font-size: 2rem;
}
.empty-description {
font-size: 1.2rem;
}
/* Modal Extra Small */
.modal-container {
width: 100vw;
max-width: 100vw;
max-height: 90vh;
border-radius: 12px 12px 0 0;
margin: 0;
}
.modal-header {
padding: 1rem 1rem;
}
.modal-title {
font-size: 1.5rem;
}
.modal-close {
font-size: 1.8rem;
}
.modal-content {
padding: 1rem;
max-height: 70vh;
}
}
/* EPIC INTERACTIVE HERO STYLES */
.epic-hero {
position: relative;
min-height: 90vh;
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 25%, #2d1b69 50%, #1a1a1a 75%, #0a0a0a 100%);
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 0;
margin-top: 0;
padding: 2rem 0;
}
/* Particle Canvas */
.particle-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
opacity: 0.6;
}
/* Animated Background Orbs */
.animated-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
.gradient-orb {
position: absolute;
border-radius: 50%;
filter: blur(60px);
animation: float 12s ease-in-out infinite; /* SLOWER: 8s → 12s */
}
.orb-1 {
width: 300px;
height: 300px;
background: radial-gradient(circle, rgba(102, 126, 234, 0.3) 0%, transparent 70%);
top: 10%;
left: 10%;
animation-delay: 0s;
}
.orb-2 {
width: 400px;
height: 400px;
background: radial-gradient(circle, rgba(118, 75, 162, 0.3) 0%, transparent 70%);
top: 60%;
right: 10%;
animation-delay: 4s; /* SLOWER: 2s → 4s */
}
.orb-3 {
width: 250px;
height: 250px;
background: radial-gradient(circle, rgba(245, 101, 101, 0.3) 0%, transparent 70%);
bottom: 20%;
left: 20%;
animation-delay: 8s; /* SLOWER: 4s → 8s */
}
.orb-4 {
width: 350px;
height: 350px;
background: radial-gradient(circle, rgba(72, 187, 120, 0.3) 0%, transparent 70%);
top: 30%;
right: 30%;
animation-delay: 12s; /* SLOWER: 6s → 12s */
}
@keyframes float {
0%, 100% { transform: translateY(0px) rotate(0deg); }
50% { transform: translateY(-20px) rotate(180deg); } /* SIMPLER: removed complex rotation */
}
/* Floating 3D Cards */
.floating-cards {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 15; /* Higher than hero-content to ensure clicks work */
pointer-events: none; /* Allow clicks to pass through container */
}
.floating-card {
position: absolute;
width: 180px;
height: 220px;
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(20px);
border-radius: 24px;
border: 2px solid rgba(255, 255, 255, 0.3);
cursor: pointer;
pointer-events: auto; /* Enable clicks on cards */
transition: all 0.3s ease;
animation: floatCard 25s ease-in-out infinite;
overflow: hidden;
z-index: 15; /* Higher than hero-content */
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
user-select: none;
transform-origin: center center;
}
/* Hall of Fame Card Styling */
.hall-of-fame-card {
width: 200px;
height: 260px;
background: linear-gradient(135deg, rgba(255, 215, 0, 0.2) 0%, rgba(102, 126, 234, 0.25) 50%, rgba(118, 75, 162, 0.2) 100%);
border: 2px solid rgba(255, 215, 0, 0.4);
box-shadow: 0 15px 40px rgba(255, 215, 0, 0.2), 0 10px 30px rgba(102, 126, 234, 0.3);
pointer-events: auto; /* Enable clicks */
}
.hall-of-fame-card:hover {
transform: scale(1.2);
box-shadow: 0 25px 60px rgba(255, 215, 0, 0.4), 0 15px 40px rgba(102, 126, 234, 0.5);
border-color: rgba(255, 215, 0, 0.7);
}
.hall-of-fame-badge {
position: absolute;
top: 8px;
right: 8px;
width: 32px;
height: 32px;
background: linear-gradient(135deg, #ffd700, #ffed4e);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
z-index: 20;
box-shadow: 0 4px 15px rgba(255, 215, 0, 0.5);
animation: pulseGold 2s ease-in-out infinite;
}
@keyframes pulseGold {
0%, 100% {
transform: scale(1);
box-shadow: 0 4px 15px rgba(255, 215, 0, 0.5);
}
50% {
transform: scale(1.1);
box-shadow: 0 6px 20px rgba(255, 215, 0, 0.8);
}
}
.card-avatar-image {
width: 80px;
height: 80px;
border-radius: 50%;
object-fit: cover;
border: 3px solid rgba(255, 215, 0, 0.5);
box-shadow: 0 8px 20px rgba(255, 215, 0, 0.3);
display: block;
margin: 0 auto 20px;
flex-shrink: 0;
}
.floating-card:hover {
transform: scale(1.15);
box-shadow: 0 20px 50px rgba(102, 126, 234, 0.5);
border-color: rgba(102, 126, 234, 0.6);
}
.floating-card:active {
transform: scale(1.05);
transition: transform 0.1s ease;
}
.card-1 {
top: 10%;
left: 10%;
animation-delay: 0s;
animation: orbitCard1 20s linear infinite;
}
.card-2 {
top: 8%;
left: 25%;
animation-delay: 2s;
animation: orbitCard2 18s linear infinite;
}
.card-3 {
top: 12%;
left: 40%;
animation-delay: 4s;
animation: orbitCard3 22s linear infinite;
}
.card-4 {
top: 15%;
left: 55%;
animation-delay: 6s;
animation: orbitCard4 19s linear infinite;
}
.card-5 {
top: 18%;
left: 70%;
animation-delay: 8s;
animation: orbitCard5 21s linear infinite;
}
.card-6 {
top: 20%;
left: 85%;
animation-delay: 10s;
animation: orbitCard6 17s linear infinite;
}
.card-7 {
top: 75%;
left: 85%;
animation-delay: 12s;
animation: orbitCard7 23s linear infinite;
}
.card-8 {
top: 80%;
left: 70%;
animation-delay: 14s;
animation: orbitCard8 20s linear infinite;
}
.card-9 {
top: 85%;
left: 40%;
animation-delay: 16s;
animation: orbitCard9 18s linear infinite;
}
.card-10 {
top: 80%;
left: 10%;
animation-delay: 18s;
animation: orbitCard10 22s linear infinite;
}
@keyframes floatCard {
0% {
transform: translateY(0px) translateX(0px) rotate(0deg);
}
25% {
transform: translateY(-30px) translateX(30px) rotate(90deg);
}
50% {
transform: translateY(-20px) translateX(60px) rotate(180deg);
}
75% {
transform: translateY(10px) translateX(30px) rotate(270deg);
}
100% {
transform: translateY(0px) translateX(0px) rotate(360deg);
}
}
/* Orbital animations - cards orbit around the center with bouncing effects */
@keyframes orbitCard1 {
0% { transform: translateY(0px) translateX(0px) scale(1); }
25% { transform: translateY(-60px) translateX(60px) scale(1.1); }
50% { transform: translateY(-40px) translateX(120px) scale(0.9); }
75% { transform: translateY(20px) translateX(60px) scale(1.05); }
100% { transform: translateY(0px) translateX(0px) scale(1); }
}
@keyframes orbitCard2 {
0% { transform: translateY(0px) translateX(0px) scale(1); }
25% { transform: translateY(-50px) translateX(-50px) scale(0.95); }
50% { transform: translateY(-20px) translateX(-100px) scale(1.1); }
75% { transform: translateY(30px) translateX(-50px) scale(0.9); }
100% { transform: translateY(0px) translateX(0px) scale(1); }
}
@keyframes orbitCard3 {
0% { transform: translateY(0px) translateX(0px) scale(1); }
25% { transform: translateY(-70px) translateX(30px) scale(1.05); }
50% { transform: translateY(-50px) translateX(60px) scale(0.9); }
75% { transform: translateY(10px) translateX(30px) scale(1.1); }
100% { transform: translateY(0px) translateX(0px) scale(1); }
}
@keyframes orbitCard4 {
0% { transform: translateY(0px) translateX(0px) scale(1); }
25% { transform: translateY(-40px) translateX(-60px) scale(0.9); }
50% { transform: translateY(-10px) translateX(-120px) scale(1.1); }
75% { transform: translateY(40px) translateX(-60px) scale(0.95); }
100% { transform: translateY(0px) translateX(0px) scale(1); }
}
@keyframes orbitCard5 {
0% { transform: translateY(0px) translateX(0px) scale(1); }
25% { transform: translateY(-80px) translateX(20px) scale(1.1); }
50% { transform: translateY(-60px) translateX(40px) scale(0.9); }
75% { transform: translateY(5px) translateX(20px) scale(1.05); }
100% { transform: translateY(0px) translateX(0px) scale(1); }
}
@keyframes orbitCard6 {
0% { transform: translateY(0px) translateX(0px) scale(1); }
25% { transform: translateY(-30px) translateX(-70px) scale(0.95); }
50% { transform: translateY(0px) translateX(-140px) scale(1.1); }
75% { transform: translateY(50px) translateX(-70px) scale(0.9); }
100% { transform: translateY(0px) translateX(0px) scale(1); }
}
@keyframes orbitCard7 {
0% { transform: translateY(0px) translateX(0px) scale(1); }
25% { transform: translateY(60px) translateX(60px) scale(1.05); }
50% { transform: translateY(40px) translateX(120px) scale(0.9); }
75% { transform: translateY(-20px) translateX(60px) scale(1.1); }
100% { transform: translateY(0px) translateX(0px) scale(1); }
}
@keyframes orbitCard8 {
0% { transform: translateY(0px) translateX(0px) scale(1); }
25% { transform: translateY(50px) translateX(-50px) scale(0.9); }
50% { transform: translateY(20px) translateX(-100px) scale(1.1); }
75% { transform: translateY(-30px) translateX(-50px) scale(0.95); }
100% { transform: translateY(0px) translateX(0px) scale(1); }
}
@keyframes orbitCard9 {
0% { transform: translateY(0px) translateX(0px) scale(1); }
25% { transform: translateY(70px) translateX(30px) scale(1.1); }
50% { transform: translateY(50px) translateX(60px) scale(0.9); }
75% { transform: translateY(-10px) translateX(30px) scale(1.05); }
100% { transform: translateY(0px) translateX(0px) scale(1); }
}
@keyframes orbitCard10 {
0% { transform: translateY(0px) translateX(0px) scale(1); }
25% { transform: translateY(40px) translateX(-60px) scale(0.95); }
50% { transform: translateY(10px) translateX(-120px) scale(1.1); }
75% { transform: translateY(-40px) translateX(-60px) scale(0.9); }
100% { transform: translateY(0px) translateX(0px) scale(1); }
}
.card-glow {
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
background: linear-gradient(45deg, #667eea, #764ba2, #f56565, #48bb78);
border-radius: 26px;
z-index: -1;
opacity: 0;
transition: opacity 0.3s ease;
}
.floating-card:hover .card-glow {
opacity: 0.6;
animation: glowPulse 2s ease-in-out infinite;
}
@keyframes glowPulse {
0%, 100% { opacity: 0.6; }
50% { opacity: 0.3; }
}
.card-content {
padding: 25px;
text-align: center;
color: white;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
pointer-events: none; /* Allow clicks to pass through to card */
}
.card-avatar {
width: 80px;
height: 80px;
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 25%, #16213e 50%, #0f3460 75%, #0a0a0a 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
margin: 0 auto 20px;
font-size: 32px;
flex-shrink: 0;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
position: relative;
overflow: hidden;
}
.card-avatar-pattern {
position: absolute;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
}
.card-avatar-pattern i {
font-size: 2.5rem;
color: rgba(102, 126, 234, 0.6);
animation: pulse 2s ease-in-out infinite;
text-shadow: 0 0 20px rgba(102, 126, 234, 0.5);
}
.card-avatar-gradient-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.3) 0%, rgba(118, 75, 162, 0.3) 50%, rgba(240, 147, 251, 0.2) 100%);
border-radius: 50%;
z-index: 2;
}
.hall-of-fame-card .card-avatar {
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 25%, #16213e 50%, #0f3460 75%, #0a0a0a 100%);
border: 3px solid rgba(255, 215, 0, 0.5);
box-shadow: 0 8px 20px rgba(255, 215, 0, 0.3);
}
.hall-of-fame-card .card-avatar-pattern i {
color: rgba(255, 215, 0, 0.7);
text-shadow: 0 0 20px rgba(255, 215, 0, 0.6);
}
.hall-of-fame-card .card-avatar-gradient-overlay {
background: linear-gradient(135deg, rgba(255, 215, 0, 0.2) 0%, rgba(102, 126, 234, 0.3) 50%, rgba(118, 75, 162, 0.2) 100%);
}
.card-name {
font-size: 16px;
font-weight: 700;
margin-bottom: 12px;
line-height: 1.2;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: white;
}
.card-stats {
font-size: 13px;
opacity: 0.9;
line-height: 1.4;
}
.card-stats .stat {
display: block;
margin-bottom: 5px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: white;
font-weight: 500;
}
/* Hero Content */
.hero-content {
position: relative;
z-index: 10;
text-align: center;
max-width: 90rem;
margin: 0 auto;
padding: 0 2rem;
pointer-events: auto; /* Ensure interactive elements work */
}
/* Animated Badge */
.animated-badge {
display: inline-flex;
align-items: center;
gap: 1rem;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.2);
padding: 1.5rem 2.5rem;
border-radius: 50px;
margin-bottom: 3rem;
animation: badgeFloat 8s ease-in-out infinite; /* SLOWER: 4s → 8s */
}
@keyframes badgeFloat {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-8px); } /* SMALLER: -10px → -8px */
}
.badge-icon {
font-size: 2rem;
animation: iconBounce 4s ease-in-out infinite; /* SLOWER: 2s → 4s */
}
@keyframes iconBounce {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); } /* SMALLER: 1.1 → 1.05 */
}
.badge-text {
font-size: 1.4rem;
font-weight: 600;
color: white;
}
.badge-count {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
padding: 0.5rem 1rem;
border-radius: 20px;
font-weight: bold;
font-size: 1.2rem;
animation: countPulse 4s ease-in-out infinite; /* SLOWER: 2s → 4s */
}
@keyframes countPulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.02); } /* SMALLER: 1.05 → 1.02 */
}
/* Typing Title */
.typing-title {
font-size: 5rem;
font-weight: 900;
margin-bottom: 2rem;
line-height: 1.2;
opacity: 0;
animation: fadeInUp 1s ease forwards 1s;
text-align: center;
white-space: nowrap;
overflow: visible;
}
.title-line {
display: inline;
opacity: 0;
animation: fadeInUp 1s ease forwards;
}
.title-line:nth-child(1) {
animation-delay: 0.5s;
}
.title-line:nth-child(2) {
animation-delay: 1s;
}
.title-line.highlight {
background: linear-gradient(135deg, #667eea, #764ba2, #f56565, #48bb78);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: gradientShift 6s ease-in-out infinite; /* SLOWER: 3s → 6s */
}
@keyframes gradientShift {
0%, 100% { filter: hue-rotate(0deg); }
50% { filter: hue-rotate(90deg); } /* SMALLER: 180deg → 90deg */
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Animated Subtitle */
.animated-subtitle {
font-size: 1.8rem;
color: rgba(255, 255, 255, 0.8);
margin-bottom: 4rem;
line-height: 1.6;
opacity: 0;
animation: fadeInUp 1s ease forwards 1.5s;
}
.highlight-text {
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
font-weight: 600;
}
.pulse-text {
animation: textPulse 2s ease-in-out infinite;
}
@keyframes textPulse {
0%, 100% { opacity: 0.8; }
50% { opacity: 1; }
}
/* Live Stats */
.live-stats {
display: flex;
justify-content: center;
gap: 2rem;
margin-bottom: 4rem;
opacity: 0;
animation: fadeInUp 1s ease forwards 2s;
flex-wrap: wrap;
}
.stat-item {
text-align: center;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.2);
padding: 2rem 1.5rem;
border-radius: 20px;
min-width: 140px;
transition: all 0.3s ease;
}
.stat-item:hover {
transform: translateY(-10px);
box-shadow: 0 20px 40px rgba(102, 126, 234, 0.3);
}
.stat-icon {
font-size: 2.5rem;
margin-bottom: 1rem;
animation: iconFloat 3s ease-in-out infinite;
}
@keyframes iconFloat {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-5px); }
}
.stat-number {
font-size: 2.5rem;
font-weight: 900;
color: white;
margin-bottom: 0.5rem;
line-height: 1;
}
.stat-label {
font-size: 1rem;
color: rgba(255, 255, 255, 0.7);
font-weight: 500;
line-height: 1.2;
}
/* Search Container */
.search-container {
margin-bottom: 4rem;
opacity: 0;
animation: fadeInUp 1s ease forwards 2.5s;
}
.search-box {
position: relative;
max-width: 600px;
margin: 0 auto 2rem;
}
.search-icon {
position: absolute;
left: 2rem;
top: 50%;
transform: translateY(-50%);
font-size: 1.5rem;
color: rgba(255, 255, 255, 0.6);
z-index: 2;
}
.search-input {
width: 100%;
padding: 1.5rem 2rem 1.5rem 5rem;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
border: 2px solid rgba(255, 255, 255, 0.2);
border-radius: 50px;
color: white;
font-size: 1.4rem;
transition: all 0.3s ease;
min-height: 48px;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
.search-input:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 30px rgba(102, 126, 234, 0.3);
}
.search-input::placeholder {
color: rgba(255, 255, 255, 0.6);
}
.search-filters {
display: flex;
justify-content: center;
gap: 1rem;
flex-wrap: wrap;
}
.filter-btn {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: white;
padding: 1rem 2rem;
border-radius: 25px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 1rem;
}
.filter-btn:hover,
.filter-btn.active {
background: linear-gradient(135deg, #667eea, #764ba2);
border-color: transparent;
transform: translateY(-2px);
}
/* Hero Actions */
.hero-actions {
display: flex;
justify-content: center;
gap: 2rem;
opacity: 0;
animation: fadeInUp 1s ease forwards 3s;
}
.cta-btn {
display: flex;
align-items: center;
gap: 1rem;
padding: 1.5rem 3rem;
border-radius: 50px;
font-size: 1.4rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
border: none;
position: relative;
overflow: hidden;
}
.cta-btn.primary {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
}
.cta-btn.secondary {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
border: 2px solid rgba(255, 255, 255, 0.3);
color: white;
}
.cta-btn:hover {
transform: translateY(-5px);
box-shadow: 0 20px 40px rgba(102, 126, 234, 0.4);
}
.cta-btn.primary:hover {
background: linear-gradient(135deg, #5a67d8, #6b46c1);
}
.cta-btn.secondary:hover {
background: rgba(255, 255, 255, 0.2);
border-color: rgba(255, 255, 255, 0.5);
}
/* Scroll Indicator */
.scroll-indicator {
position: absolute;
bottom: 2rem;
left: 50%;
transform: translateX(-50%);
text-align: center;
color: rgba(255, 255, 255, 0.6);
z-index: 10;
opacity: 0;
animation: fadeInUp 1s ease forwards 3.5s;
padding: 1rem;
background: rgba(0, 0, 0, 0.3);
border-radius: 20px;
backdrop-filter: blur(10px);
}
.scroll-arrow {
width: 2px;
height: 30px;
background: linear-gradient(to bottom, transparent, rgba(255, 255, 255, 0.6));
margin: 0 auto 1rem;
animation: scrollBounce 2s ease-in-out infinite;
}
@keyframes scrollBounce {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(10px); }
}
.scroll-text {
font-size: 1rem;
font-weight: 500;
white-space: nowrap;
}
/* Additional mobile styles for hero elements */
@media (max-width: 768px) {
.typing-title {
font-size: 2.8rem;
}
.animated-subtitle {
font-size: 1.3rem;
}
.live-stats {
flex-direction: column;
gap: 1rem;
}
.stat-item {
min-width: auto;
}
.hero-actions {
flex-direction: column;
align-items: center;
}
.floating-card {
display: none;
}
.animated-badge {
flex-direction: column;
gap: 0.5rem;
}
}
/* Search Suggestions */
.search-suggestions {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.9);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 20px;
margin-top: 1rem;
max-height: 300px;
overflow-y: auto;
z-index: 1000;
display: none;
}
.suggestion-item {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem 1.5rem;
cursor: pointer;
transition: all 0.3s ease;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
min-height: 44px;
}
.suggestion-item:last-child {
border-bottom: none;
}
.suggestion-item:hover {
background: rgba(102, 126, 234, 0.2);
}
.suggestion-avatar {
width: 40px;
height: 40px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: white;
font-size: 16px;
overflow: hidden;
}
.suggestion-avatar.has-image {
background: none;
padding: 0;
}
.suggestion-avatar.has-image img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 50%;
display: block;
}
.suggestion-name {
color: white;
font-weight: 600;
font-size: 1.1rem;
}
@keyframes floatParticle {
0% {
transform: translateY(100vh) rotate(0deg);
opacity: 0;
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
transform: translateY(-100px) rotate(360deg);
opacity: 0;
}
}
.no-results {
padding: 2rem;
text-align: center;
color: rgba(255, 255, 255, 0.6);
font-style: italic;
}
/* Simple Particle Effect */
.simple-particle {
position: absolute;
width: 4px;
height: 4px;
background: rgba(102, 126, 234, 0.5);
border-radius: 50%;
pointer-events: none;
z-index: 1;
}
/* Simple Background Effect */
.simple-bg-effect {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
radial-gradient(circle at 20% 80%, rgba(102, 126, 234, 0.1) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(118, 75, 162, 0.1) 0%, transparent 50%),
radial-gradient(circle at 40% 40%, rgba(245, 101, 101, 0.1) 0%, transparent 50%);
z-index: 1;
}
/* Admin Floating Cards Control */
.admin-floating-control {
position: absolute;
top: 20px;
right: 20px;
z-index: 1000;
opacity: 0.9;
transition: opacity 0.3s ease;
}
.admin-floating-control:hover {
opacity: 1;
}
.admin-control-panel {
background: rgba(0, 0, 0, 0.9);
backdrop-filter: blur(20px);
border: 2px solid rgba(102, 126, 234, 0.5);
border-radius: 15px;
padding: 20px;
max-width: 300px;
color: white;
}
.admin-control-panel h4 {
margin: 0 0 10px 0;
font-size: 16px;
color: #667eea;
}
.admin-control-panel p {
margin: 0 0 15px 0;
font-size: 14px;
opacity: 0.8;
}
.artist-selector {
max-height: 200px;
overflow-y: auto;
margin-bottom: 15px;
}
.artist-option {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 0;
cursor: pointer;
transition: all 0.2s ease;
}
.artist-option:hover {
background: rgba(102, 126, 234, 0.2);
border-radius: 8px;
padding-left: 10px;
}
.artist-option input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: #667eea;
}
.artist-name {
font-size: 14px;
font-weight: 500;
}
.update-floating-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 10px 20px;
border-radius: 8px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
width: 100%;
}
.update-floating-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.4);
}
.admin-control-actions {
display: flex;
gap: 10px;
margin-top: 15px;
}
.update-floating-btn,
.reset-floating-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 10px 15px;
border-radius: 8px;
font-size: 12px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
flex: 1;
}
.reset-floating-btn {
background: linear-gradient(135deg, #f56565, #e53e3e);
}
.update-floating-btn:hover,
.reset-floating-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.4);
}
.reset-floating-btn:hover {
box-shadow: 0 10px 20px rgba(245, 101, 101, 0.4);
}
.admin-control-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.close-admin-btn {
background: rgba(255, 255, 255, 0.1);
color: #a0aec0;
border: none;
width: 30px;
height: 30px;
border-radius: 50%;
font-size: 16px;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
.close-admin-btn:hover {
background: rgba(245, 101, 101, 0.2);
color: #f56565;
transform: scale(1.1);
}
/* Artist Preview Tooltip */
.artist-preview-tooltip {
position: fixed;
z-index: 10000;
width: 360px;
background: white;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
opacity: 0;
pointer-events: none;
transform: translateY(10px) scale(0.95);
transition: opacity 0.2s ease, transform 0.2s ease;
display: block;
visibility: hidden;
}
.artist-preview-tooltip.visible {
opacity: 1;
pointer-events: auto;
transform: translateY(0) scale(1);
visibility: visible;
}
.artist-preview-cover {
width: 100%;
height: 120px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%);
background-size: cover;
background-position: center;
position: relative;
}
.artist-preview-content {
padding: 16px;
position: relative;
}
.artist-preview-avatar {
position: absolute;
top: -40px;
left: 16px;
width: 80px;
height: 80px;
border-radius: 50%;
border: 4px solid white;
background: linear-gradient(135deg, #667eea, #764ba2);
overflow: hidden;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
.artist-preview-avatar:has(.artist-preview-avatar-placeholder) {
background: transparent;
}
.artist-preview-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.artist-preview-avatar .default-avatar {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 2.5rem;
font-weight: 700;
color: white;
}
/* New placeholder styles matching artist_profile_clean.php */
.artist-preview-cover-placeholder {
width: 100%;
height: 100%;
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 25%, #16213e 50%, #0f3460 75%, #0a0a0a 100%);
position: relative;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
.artist-preview-cover-pattern {
position: absolute;
width: 100%;
height: 100%;
opacity: 0.3;
z-index: 1;
}
.artist-preview-waveform-pattern {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 40%;
background: repeating-linear-gradient(
90deg,
transparent,
transparent 2px,
rgba(102, 126, 234, 0.3) 2px,
rgba(102, 126, 234, 0.3) 4px
);
background-size: 20px 100%;
animation: waveform 3s ease-in-out infinite;
}
.artist-preview-music-notes {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
gap: 1.5rem;
font-size: 2rem;
color: rgba(102, 126, 234, 0.4);
}
.artist-preview-music-notes i {
animation: floatMusic 4s ease-in-out infinite;
animation-delay: calc(var(--i) * 0.3s);
}
.artist-preview-music-notes i:nth-child(1) { --i: 0; }
.artist-preview-music-notes i:nth-child(2) { --i: 1; }
.artist-preview-music-notes i:nth-child(3) { --i: 2; }
.artist-preview-cover-gradient-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.2) 0%, rgba(118, 75, 162, 0.2) 50%, rgba(240, 147, 251, 0.1) 100%);
z-index: 2;
}
.artist-preview-avatar-placeholder {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 50%;
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 25%, #16213e 50%, #0f3460 75%, #0a0a0a 100%);
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
}
.artist-preview-profile-pattern {
position: absolute;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
}
.artist-preview-profile-pattern i {
font-size: 2.5rem;
color: rgba(102, 126, 234, 0.6);
animation: pulse 2s ease-in-out infinite;
text-shadow: 0 0 20px rgba(102, 126, 234, 0.5);
}
.artist-preview-profile-gradient-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.3) 0%, rgba(118, 75, 162, 0.3) 50%, rgba(240, 147, 251, 0.2) 100%);
border-radius: 50%;
z-index: 2;
}
.artist-preview-info {
margin-top: 50px;
text-align: center;
}
.artist-preview-name {
font-size: 1.2rem;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 12px;
}
.artist-preview-stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
margin: 12px 0;
padding: 0 8px;
}
.preview-stat-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 8px 4px;
background: #f8f9fa;
border-radius: 8px;
transition: all 0.2s ease;
}
.preview-stat-item:hover {
background: #e9ecef;
transform: translateY(-2px);
}
.preview-stat-item i {
font-size: 0.9rem;
color: #667eea;
margin-bottom: 2px;
}
.preview-stat-item .stat-value {
font-size: 1rem;
font-weight: 700;
color: #1a1a1a;
line-height: 1.2;
}
.preview-stat-item .stat-label {
font-size: 0.7rem;
color: #666;
text-transform: uppercase;
letter-spacing: 0.5px;
font-weight: 500;
}
.artist-preview-link {
display: inline-block;
margin-top: 12px;
padding: 8px 16px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
text-decoration: none;
border-radius: 8px;
font-size: 0.85rem;
font-weight: 600;
transition: all 0.3s ease;
width: calc(100% - 32px);
text-align: center;
pointer-events: auto;
cursor: pointer;
position: relative;
z-index: 10001;
}
.artist-preview-link:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
background: linear-gradient(135deg, #5a67d8 0%, #6b46c1 100%);
}
.artist-preview-link:active {
transform: translateY(0);
}
@keyframes waveform {
0%, 100% { transform: translateY(0) scaleY(1); }
50% { transform: translateY(-10px) scaleY(1.2); }
}
@keyframes floatMusic {
0%, 100% { transform: translateY(0) rotate(0deg); }
50% { transform: translateY(-20px) rotate(5deg); }
}
</style>
<!-- EPIC INTERACTIVE HERO SECTION -->
<section class="epic-hero">
<!-- Simple Background Effect -->
<div class="simple-bg-effect"></div>
<!-- Animated Background -->
<div class="animated-bg">
<div class="gradient-orb orb-1"></div>
<div class="gradient-orb orb-2"></div>
<div class="gradient-orb orb-3"></div>
<div class="gradient-orb orb-4"></div>
</div>
<!-- Hall of Fame Floating Cards -->
<div class="floating-cards" id="floatingCardsContainer">
<?php
// Use Hall of Fame artists (top 6-8 most popular) as default
// JavaScript will update this if there's a saved selection in localStorage
foreach ($hallOfFameArtists as $index => $artist):
if ($index >= 8) break; // Max 8 cards
?>
<div class="floating-card hall-of-fame-card card-<?= $index + 1 ?>" data-artist-id="<?= $artist['id'] ?>">
<div class="card-glow"></div>
<div class="hall-of-fame-badge">⭐</div>
<div class="card-content">
<?php if (!empty($artist['profile_image'])): ?>
<img src="<?= htmlspecialchars($artist['profile_image']) ?>"
alt="<?= htmlspecialchars($artist['username']) ?>"
class="card-avatar-image"
style="object-position: <?= htmlspecialchars($artist['profile_position'] ?? 'center center') ?>;"
onerror="this.onerror=null; this.style.display='none'; const avatar = this.parentElement.querySelector('.card-avatar'); if(avatar) avatar.style.display='flex';">
<div class="card-avatar" style="display:none;">
<div class="card-avatar-pattern">
<i class="fas fa-music"></i>
</div>
<div class="card-avatar-gradient-overlay"></div>
</div>
<?php else: ?>
<div class="card-avatar">
<div class="card-avatar-pattern">
<i class="fas fa-music"></i>
</div>
<div class="card-avatar-gradient-overlay"></div>
</div>
<?php endif; ?>
<div class="card-name"><?= htmlspecialchars($artist['username']) ?></div>
<div class="card-stats">
<span class="stat">🎵 <?= $artist['completed_tracks'] ?? 0 ?> <?= t('artists.tracks') ?></span>
<span class="stat">🎧 <?= number_format($artist['total_plays'] ?? 0) ?> <?= t('artists.plays') ?></span>
<span class="stat">👥 <?= number_format($artist['followers_count'] ?? 0) ?> <?= t('artists.followers') ?></span>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<div class="container">
<div class="hero-content">
<!-- Animated Badge -->
<div class="hero-badge animated-badge">
<span class="badge-icon">⭐</span>
<span class="badge-text"><?= t('artists.hall_badge') ?></span>
<span class="badge-count" id="liveMemberCount"><?= count($hallOfFameArtists) ?> <?= t('artists.artists_label') ?></span>
</div>
<!-- Main Title with Typing Effect -->
<h1 class="hero-title typing-title">
<?= t('artists.hall_title') ?>
</h1>
<!-- Animated Subtitle -->
<p class="hero-subtitle animated-subtitle">
<?= t('artists.hall_subtitle') ?>
</p>
<!-- Live Stats Bar -->
<div class="live-stats">
<div class="stat-item">
<div class="stat-icon">🎵</div>
<div class="stat-number" id="totalTracks">0</div>
<div class="stat-label"><?= t('artists.public_tracks') ?></div>
</div>
<div class="stat-item">
<div class="stat-icon">👥</div>
<div class="stat-number" id="totalFollowers">0</div>
<div class="stat-label"><?= t('artists.followers') ?></div>
</div>
<div class="stat-item">
<div class="stat-icon">⭐</div>
<div class="stat-number" id="activeArtists">0</div>
<div class="stat-label"><?= t('artists.active_artists') ?></div>
</div>
<div class="stat-item">
<div class="stat-icon">🔥</div>
<div class="stat-number" id="totalDuration">0</div>
<div class="stat-label"><?= t('artists.hours_created') ?></div>
</div>
</div>
<!-- Interactive Search -->
<div class="search-container">
<div class="search-box">
<div class="search-icon">🔍</div>
<input type="text" id="artistSearch" placeholder="<?= t('artists.search_placeholder') ?>" class="search-input">
<div class="search-suggestions" id="searchSuggestions"></div>
</div>
<div class="search-filters">
<button class="filter-btn active" data-filter="all"><?= t('artists.all') ?></button>
<button class="filter-btn" data-filter="pro"><?= t('artists.pro_creators') ?></button>
<button class="filter-btn" data-filter="rising"><?= t('artists.rising_stars') ?></button>
<button class="filter-btn" data-filter="new"><?= t('artists.new_artists') ?></button>
</div>
</div>
<!-- Call to Action -->
<div class="hero-actions">
<button class="cta-btn primary" onclick="scrollToArtists()">
<span class="btn-text"><?= t('artists.explore_community') ?></span>
<span class="btn-icon">🚀</span>
</button>
<button class="cta-btn secondary" onclick="window.location.href='/auth/register.php'">
<span class="btn-text"><?= t('artists.join_community') ?></span>
<span class="btn-icon">✨</span>
</button>
</div>
</div>
</div>
</section>
<!-- Artists Content -->
<section class="artists-content">
<div class="container">
<div class="artists-container">
<?php if (empty($artists)): ?>
<div class="empty-state">
<div class="empty-icon">👥</div>
<h3 class="empty-title"><?= t('artists.no_members') ?></h3>
<p class="empty-description"><?= t('artists.no_members_desc') ?></p>
<a href="/auth/register.php" class="btn btn-primary">
<i class="fas fa-user-plus"></i> <?= t('artists.join_community') ?>
</a>
</div>
<?php else: ?>
<div class="artists-grid">
<?php
$defaultArtistName = t('artists.default_name');
foreach ($artists as $artist):
// Get profile image or use gradient background
$profileImage = !empty($artist['profile_image']) ? htmlspecialchars($artist['profile_image']) : null;
$displayName = trim($artist['username'] ?? $defaultArtistName);
if ($displayName === '') {
$displayName = $defaultArtistName;
}
$artistInitial = strtoupper(substr($displayName, 0, 1));
?>
<div class="artist-card-community" data-artist-id="<?= $artist['id'] ?>">
<!-- Artist Image -->
<div class="artist-image-wrapper">
<?php if ($profileImage): ?>
<img src="<?= $profileImage ?>" alt="<?= htmlspecialchars($artist['username']) ?>" class="artist-image" loading="lazy" style="object-position: <?= htmlspecialchars($artist['profile_position'] ?? 'center center') ?>;" onerror="this.onerror=null; this.style.display='none'; const placeholder = this.parentElement.querySelector('.artist-image-placeholder'); if(placeholder) placeholder.style.display='flex';">
<?php endif; ?>
<div class="artist-image-placeholder" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: <?= $profileImage ? 'none' : 'flex' ?>; align-items: center; justify-content: center;">
<div class="artist-image-placeholder-pattern">
<i class="fas fa-music"></i>
</div>
<div class="artist-image-placeholder-gradient-overlay"></div>
</div>
<div class="artist-overlay">
<a href="/artist_profile.php?id=<?= $artist['id'] ?>" class="view-profile-btn-overlay" onclick="event.stopPropagation(); loadArtistProfile(<?= $artist['id'] ?>); return false;">
<i class="fas fa-user"></i>
</a>
</div>
</div>
<!-- Artist Info -->
<div class="artist-info-section">
<a href="/artist_profile.php?id=<?= $artist['id'] ?>"
class="artist-name-link artist-preview-trigger"
onclick="event.stopPropagation(); loadArtistProfile(<?= $artist['id'] ?>); return false;"
title="<?= htmlspecialchars($displayName) ?>"
data-artist-id="<?= $artist['id'] ?>"
data-artist-name="<?= htmlspecialchars($displayName, ENT_QUOTES) ?>"
data-artist-image="<?= htmlspecialchars($artist['profile_image'] ?? '', ENT_QUOTES) ?>"
data-artist-cover="<?= htmlspecialchars($artist['cover_image'] ?? '', ENT_QUOTES) ?>"
data-artist-tracks="<?= $artist['completed_tracks'] ?? 0 ?>"
data-artist-plays="<?= $artist['total_plays'] ?? 0 ?>"
data-artist-followers="<?= $artist['followers_count'] ?? 0 ?>"
data-artist-following="<?= $artist['following_count'] ?? 0 ?>"
data-artist-friends="0"
data-artist-events="0">
<?= htmlspecialchars($displayName) ?>
</a>
<?php
$planSlug = strtolower($artist['plan'] ?? '');
$planKey = 'plans.label.' . $planSlug;
$planLabel = t($planKey);
if ($planLabel === $planKey || empty($artist['plan'])) {
$planLabel = ucfirst($artist['plan'] ?? t('plans.label.default'));
}
?>
<a href="/artist_profile.php?id=<?= $artist['id'] ?>" class="artist-plan-link" onclick="event.stopPropagation(); loadArtistProfile(<?= $artist['id'] ?>); return false;">
<?= htmlspecialchars($planLabel) ?>
</a>
</div>
<!-- Artist Stats -->
<div class="artist-stats-section">
<button class="artist-stat-btn" onclick="event.stopPropagation();" title="<?= t('artists.public_tracks') ?>">
<i class="fas fa-music"></i>
<span><?= number_format($artist['total_tracks'] ?? 0) ?></span>
</button>
<button class="artist-stat-btn" onclick="event.stopPropagation();" title="<?= t('artists.total_plays') ?>">
<i class="fas fa-headphones"></i>
<span><?= number_format($artist['total_plays'] ?? 0) ?></span>
</button>
<button class="artist-stat-btn follow-count"
onclick="event.stopPropagation();"
title="<?= t('artists.followers') ?>"
data-follow-count="<?= $artist['id'] ?>"
data-count="<?= (int)($artist['followers_count'] ?? 0) ?>">
<i class="fas fa-users"></i>
<span><?= number_format($artist['followers_count'] ?? 0) ?></span>
</button>
</div>
<div class="artist-follow-actions">
<button class="follow-btn artist-follow-btn <?= $artist['is_following'] ? 'following' : '' ?>"
data-follow-label="<?= t('artists.follow') ?>"
data-following-label="<?= t('artists.following') ?>"
onclick="event.stopPropagation(); <?= isset($_SESSION['user_id']) && $_SESSION['user_id'] != $artist['id'] ? 'toggleFollow(' . $artist['id'] . ', this);' : 'window.location.href=\'/auth/login.php\';' ?>">
<i class="fas fa-<?= $artist['is_following'] ? 'heart' : 'user-plus' ?>"></i>
<span class="label-text"><?= $artist['is_following'] ? t('artists.following') : t('artists.follow') ?></span>
</button>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</section>
<!-- Modal Infrastructure -->
<div class="modal-overlay" id="modalOverlay">
<div class="modal-container">
<div class="modal-header">
<div class="modal-title" id="modalTitle">
<i class="fas fa-users"></i>
<span id="modalTitleText"><?= t('artists.loading_generic') ?></span>
</div>
<button class="modal-close" id="modalClose">
<i class="fas fa-times"></i>
</button>
</div>
<div class="modal-content" id="modalContent">
<div class="modal-loading">
<i class="fas fa-spinner fa-spin"></i>
<div><?= t('artists.loading_generic') ?></div>
</div>
</div>
</div>
</div>
<!-- Purchase Confirmation Modal -->
<div class="modal-overlay" id="purchaseModal">
<div class="modal-container purchase-modal">
<div class="modal-header">
<div class="modal-title">
<i class="fas fa-shopping-cart"></i>
<span><?= t('artists.purchase_track') ?></span>
</div>
<button class="modal-close" id="purchaseModalClose">
<i class="fas fa-times"></i>
</button>
</div>
<div class="modal-content">
<div id="purchaseContent">
<div class="purchase-loading">
<i class="fas fa-spinner fa-spin"></i>
<div><?= t('artists.loading_track_details') ?></div>
</div>
</div>
</div>
</div>
</div>
<script>
const ARTISTS_I18N = {
loading: <?= json_encode(t('artists.loading_generic'), JSON_UNESCAPED_UNICODE) ?>,
loadingTrackDetails: <?= json_encode(t('artists.loading_track_details'), JSON_UNESCAPED_UNICODE) ?>,
purchaseTrack: <?= json_encode(t('artists.purchase_track'), JSON_UNESCAPED_UNICODE) ?>,
loadingProfile: <?= json_encode(t('artists.loading_profile'), JSON_UNESCAPED_UNICODE) ?>,
errorFollowUpdate: <?= json_encode(t('artists.error_follow_update'), JSON_UNESCAPED_UNICODE) ?>,
errorArtistNotFound: <?= json_encode(t('artists.error_artist_not_found'), JSON_UNESCAPED_UNICODE) ?>,
warningMaxFloating: <?= json_encode(t('artists.warning_max_floating'), JSON_UNESCAPED_UNICODE) ?>,
warningMinFloating: <?= json_encode(t('artists.warning_min_floating'), JSON_UNESCAPED_UNICODE) ?>,
floatingUpdated: <?= json_encode(t('artists.floating_updated'), JSON_UNESCAPED_UNICODE) ?>,
scrolling: <?= json_encode(t('artists.scrolling_notification'), JSON_UNESCAPED_UNICODE) ?>,
scrollingError: <?= json_encode(t('artists.scrolling_error'), JSON_UNESCAPED_UNICODE) ?>,
profileLoadError: <?= json_encode(t('artists.profile_load_error'), JSON_UNESCAPED_UNICODE) ?>,
playlistComing: <?= json_encode(t('artists.playlist_coming'), JSON_UNESCAPED_UNICODE) ?>,
previewPlayingNotice: <?= json_encode(t('artists.preview_playing'), JSON_UNESCAPED_UNICODE) ?>,
followSuccess: <?= json_encode(t('artists.follow_success'), JSON_UNESCAPED_UNICODE) ?>,
followError: <?= json_encode(t('artists.follow_error'), JSON_UNESCAPED_UNICODE) ?>,
networkError: <?= json_encode(t('artists.network_error'), JSON_UNESCAPED_UNICODE) ?>,
cartNotification: <?= json_encode(t('artists.cart_notification'), JSON_UNESCAPED_UNICODE) ?>,
cartAlert: <?= json_encode(t('artists.cart_alert'), JSON_UNESCAPED_UNICODE) ?>,
cartError: <?= json_encode(t('artists.cart_error'), JSON_UNESCAPED_UNICODE) ?>,
loginRequiredFollow: <?= json_encode(t('artists.login_required_follow'), JSON_UNESCAPED_UNICODE) ?>,
modalTitles: {
tracks: <?= json_encode(t('artists.modal_title_tracks'), JSON_UNESCAPED_UNICODE) ?>,
completed: <?= json_encode(t('artists.modal_title_completed'), JSON_UNESCAPED_UNICODE) ?>,
followers: <?= json_encode(t('artists.modal_title_followers'), JSON_UNESCAPED_UNICODE) ?>,
following: <?= json_encode(t('artists.modal_title_following'), JSON_UNESCAPED_UNICODE) ?>,
default: <?= json_encode(t('artists.modal_title_details'), JSON_UNESCAPED_UNICODE) ?>
},
modalLoading: {
tracks: <?= json_encode(t('artists.loading_tracks'), JSON_UNESCAPED_UNICODE) ?>,
completed: <?= json_encode(t('artists.loading_tracks'), JSON_UNESCAPED_UNICODE) ?>,
followers: <?= json_encode(t('artists.loading_followers'), JSON_UNESCAPED_UNICODE) ?>,
following: <?= json_encode(t('artists.loading_following'), JSON_UNESCAPED_UNICODE) ?>
},
modalSearch: {
tracks: <?= json_encode(t('artists.search_tracks_placeholder'), JSON_UNESCAPED_UNICODE) ?>,
followers: <?= json_encode(t('artists.search_followers_placeholder'), JSON_UNESCAPED_UNICODE) ?>,
following: <?= json_encode(t('artists.search_following_placeholder'), JSON_UNESCAPED_UNICODE) ?>
},
modalUnknownType: <?= json_encode(t('artists.modal_unknown_type'), JSON_UNESCAPED_UNICODE) ?>,
noTracks: <?= json_encode(t('artists.no_tracks'), JSON_UNESCAPED_UNICODE) ?>,
errorLoadingTracks: <?= json_encode(t('artists.error_loading_tracks'), JSON_UNESCAPED_UNICODE) ?>,
untitledTrack: <?= json_encode(t('artists.untitled_track'), JSON_UNESCAPED_UNICODE) ?>,
unknownGenre: <?= json_encode(t('artists.unknown_genre'), JSON_UNESCAPED_UNICODE) ?>,
unknownArtist: <?= json_encode(t('artists.unknown_artist'), JSON_UNESCAPED_UNICODE) ?>,
previewLabel: <?= json_encode(t('artists.preview_label'), JSON_UNESCAPED_UNICODE) ?>,
previewButton: <?= json_encode(t('artists.preview_button'), JSON_UNESCAPED_UNICODE) ?>,
previewPlayingButton: <?= json_encode(t('artists.preview_playing_button'), JSON_UNESCAPED_UNICODE) ?>,
addToCart: <?= json_encode(t('credits.add_to_cart'), JSON_UNESCAPED_UNICODE) ?>,
addToPlaylist: <?= json_encode(t('artists.add_to_playlist'), JSON_UNESCAPED_UNICODE) ?>,
followersPlaceholder: <?= json_encode(t('artists.followers_placeholder'), JSON_UNESCAPED_UNICODE) ?>,
followingPlaceholder: <?= json_encode(t('artists.following_placeholder'), JSON_UNESCAPED_UNICODE) ?>,
trackNotFound: <?= json_encode(t('artists.track_not_found'), JSON_UNESCAPED_UNICODE) ?>,
trackDetailsError: <?= json_encode(t('artists.track_details_error'), JSON_UNESCAPED_UNICODE) ?>,
byArtist: <?= json_encode(t('artists.by_artist'), JSON_UNESCAPED_UNICODE) ?>,
previewTrack: <?= json_encode(t('artists.preview_track'), JSON_UNESCAPED_UNICODE) ?>,
purchaseCancel: <?= json_encode(t('artists.purchase_cancel'), JSON_UNESCAPED_UNICODE) ?>,
purchaseClose: <?= json_encode(t('artists.purchase_close'), JSON_UNESCAPED_UNICODE) ?>,
signinTitle: <?= json_encode(t('artists.signin_purchase_title'), JSON_UNESCAPED_UNICODE) ?>,
signinDesc: <?= json_encode(t('artists.signin_purchase_desc'), JSON_UNESCAPED_UNICODE) ?>,
signinCta: <?= json_encode(t('artists.signin_purchase_cta'), JSON_UNESCAPED_UNICODE) ?>,
purchaseConfirm: <?= json_encode(t('artists.purchase_confirm'), JSON_UNESCAPED_UNICODE) ?>,
creditsLabel: <?= json_encode(t('user.credits'), JSON_UNESCAPED_UNICODE) ?>,
loginRedirectNote: <?= json_encode(t('artists.login_redirect_note'), JSON_UNESCAPED_UNICODE) ?>,
defaultArtistName: <?= json_encode(t('artists.default_name'), JSON_UNESCAPED_UNICODE) ?>,
processing: <?= json_encode(t('artists.processing'), JSON_UNESCAPED_UNICODE) ?>,
purchaseFailed: <?= json_encode(t('artists.purchase_failed'), JSON_UNESCAPED_UNICODE) ?>,
purchaseFailedRetry: <?= json_encode(t('artists.purchase_failed_retry'), JSON_UNESCAPED_UNICODE) ?>,
cartCheckoutPrompt: <?= json_encode(t('artists.cart_checkout_prompt'), JSON_UNESCAPED_UNICODE) ?>,
processingPayment: <?= json_encode(t('artists.processing_payment'), JSON_UNESCAPED_UNICODE) ?>,
paymentSuccessPending: <?= json_encode(t('artists.payment_success_pending'), JSON_UNESCAPED_UNICODE) ?>,
paymentFailedRetry: <?= json_encode(t('artists.payment_failed_retry'), JSON_UNESCAPED_UNICODE) ?>,
paymentSuccess: <?= json_encode(t('artists.payment_success'), JSON_UNESCAPED_UNICODE) ?>,
purchaseOwnership: <?= json_encode(t('artists.purchase_ownership'), JSON_UNESCAPED_UNICODE) ?>,
purchaseAvailableNote: <?= json_encode(t('artists.purchase_available_note'), JSON_UNESCAPED_UNICODE) ?>,
purchaseSuccessHeading: <?= json_encode(t('artists.purchase_success_heading'), JSON_UNESCAPED_UNICODE) ?>,
viewPurchases: <?= json_encode(t('artists.view_purchases'), JSON_UNESCAPED_UNICODE) ?>,
paymentFailedReason: <?= json_encode(t('artists.payment_failed_reason'), JSON_UNESCAPED_UNICODE) ?>,
paymentDataMissing: <?= json_encode(t('artists.payment_data_missing'), JSON_UNESCAPED_UNICODE) ?>,
followLabel: <?= json_encode(t('artists.follow'), JSON_UNESCAPED_UNICODE) ?>,
followingLabel: <?= json_encode(t('artists.following'), JSON_UNESCAPED_UNICODE) ?>,
cartErrorGeneric: <?= json_encode(t('artists.cart_error_generic'), JSON_UNESCAPED_UNICODE) ?>,
tracksLabel: <?= json_encode(t('artists.tracks'), JSON_UNESCAPED_UNICODE) ?>,
followersLabel: <?= json_encode(t('artists.followers'), JSON_UNESCAPED_UNICODE) ?>,
playsLabel: <?= json_encode(t('artists.plays'), JSON_UNESCAPED_UNICODE) ?>
};
// SIMPLE & STABLE HERO JAVASCRIPT
// Simple particle effect (no canvas, just CSS) - REDUCED FOR PERFORMANCE
function createSimpleParticles() {
const hero = document.querySelector('.epic-hero');
if (!hero) return;
// Create simple floating elements - REDUCED FROM 20 TO 8 PARTICLES
for (let i = 0; i < 8; i++) {
const particle = document.createElement('div');
particle.className = 'simple-particle';
particle.style.cssText = `
position: absolute;
width: ${Math.random() * 4 + 2}px;
height: ${Math.random() * 4 + 2}px;
background: rgba(102, 126, 234, ${Math.random() * 0.5 + 0.2});
border-radius: 50%;
left: ${Math.random() * 100}%;
top: ${Math.random() * 100}%;
animation: floatParticle ${Math.random() * 10 + 10}s linear infinite;
pointer-events: none;
z-index: 1;
`;
hero.appendChild(particle);
}
}
// Load artist profile function - MISSING FUNCTION ADDED
function loadArtistProfile(artistId) {
if (!artistId) {
console.error('No artist ID provided');
return;
}
console.log('🎯 Loading artist profile for ID:', artistId);
// Show loading notification
if (typeof window.showNotification === 'function') {
window.showNotification(ARTISTS_I18N.loadingProfile, 'info');
}
// Navigate to artist profile page
setTimeout(() => {
if(window.ajaxNavigation){window.ajaxNavigation.navigateToPage(`/artist_profile.php?id=${artistId}`)}else{window.location.href=`/artist_profile.php?id=${artistId}`;}
}, 300);
}
// Simple stats calculator
function updateStats() {
const artists = <?= json_encode($artists) ?>;
let totalTracks = 0;
let totalFollowers = 0;
let activeArtists = 0;
let totalDuration = 0;
artists.forEach(artist => {
totalTracks += parseInt(artist.total_tracks || 0);
totalFollowers += parseInt(artist.followers_count || 0);
totalDuration += parseInt(artist.total_duration || 0);
if (parseInt(artist.total_tracks || 0) > 0) {
activeArtists++;
}
});
// Update stats directly
const trackElement = document.getElementById('totalTracks');
const followerElement = document.getElementById('totalFollowers');
const artistElement = document.getElementById('activeArtists');
const durationElement = document.getElementById('totalDuration');
if (trackElement) trackElement.textContent = totalTracks.toLocaleString();
if (followerElement) followerElement.textContent = totalFollowers.toLocaleString();
if (artistElement) artistElement.textContent = activeArtists.toLocaleString();
if (durationElement) durationElement.textContent = Math.floor(totalDuration / 3600).toLocaleString();
// Update member count
const memberElement = document.getElementById('liveMemberCount');
if (memberElement) memberElement.textContent = artists.length;
}
// Simple search
function setupSearch() {
const searchInput = document.getElementById('artistSearch');
const suggestions = document.getElementById('searchSuggestions');
const artists = <?= json_encode($artists) ?>;
if (!searchInput) return;
const escapeHtml = (str = '') => str.replace(/[&<>"']/g, (char) => {
const escapeMap = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return escapeMap[char] || char;
});
const renderAvatar = (artist, displayName) => {
const initial = escapeHtml(displayName.charAt(0).toUpperCase() || '?');
if (artist.profile_image) {
const imageUrl = escapeHtml(artist.profile_image);
const altText = escapeHtml(displayName);
const profilePosition = escapeHtml(artist.profile_position || 'center center');
return `
<div class="suggestion-avatar has-image">
<img src="${imageUrl}" alt="${altText}" style="object-position: ${profilePosition};">
</div>
`;
}
return `<div class="suggestion-avatar">${initial}</div>`;
};
searchInput.addEventListener('input', (e) => {
const query = e.target.value.toLowerCase();
if (query.length < 2) {
if (suggestions) suggestions.style.display = 'none';
return;
}
const filtered = artists.filter(artist => {
const name = (artist.username || '').toLowerCase();
return name.includes(query);
}).slice(0, 3);
if (suggestions && filtered.length > 0) {
suggestions.innerHTML = filtered.map(artist => {
const displayName = artist.username && artist.username.trim()
? artist.username
: (ARTISTS_I18N.defaultArtistName || 'Artist');
const avatar = renderAvatar(artist, displayName);
const artistId = parseInt(artist.id) || 0; // Ensure artist.id is a valid number
return `
<div class="suggestion-item" onclick="loadArtistProfile(${artistId})">
${avatar}
<div class="suggestion-name">${escapeHtml(displayName)}</div>
</div>
`;
}).join('');
suggestions.style.display = 'block';
} else if (suggestions) {
suggestions.style.display = 'none';
}
});
}
// Simple filters
function setupFilters() {
const filterButtons = document.querySelectorAll('.filter-btn');
const artistCards = document.querySelectorAll('.artist-card-community');
filterButtons.forEach(button => {
button.addEventListener('click', () => {
const filter = button.getAttribute('data-filter');
filterButtons.forEach(btn => btn.classList.remove('active'));
button.classList.add('active');
// Get all artists data for filtering
const artists = <?= json_encode($artists) ?>;
const artistMap = {};
artists.forEach(artist => {
artistMap[artist.id] = artist;
});
artistCards.forEach(card => {
const artistId = parseInt(card.getAttribute('data-artist-id'));
const artist = artistMap[artistId];
if (!artist) {
card.style.display = 'none';
return;
}
let show = true;
const completedTracks = artist.completed_tracks || 0;
if (filter === 'pro' && completedTracks < 20) show = false;
if (filter === 'rising' && (completedTracks < 10 || completedTracks >= 20)) show = false;
if (filter === 'new' && completedTracks >= 5) show = false;
card.style.display = show ? 'block' : 'none';
});
});
});
}
// Toggle follow function
function toggleFollow(artistId, button) {
if (!<?= isset($_SESSION['user_id']) ? 'true' : 'false' ?>) {
window.location.href = '/auth/login.php';
return;
}
const isFollowing = button.classList.contains('following');
const action = isFollowing ? 'unfollow' : 'follow';
const countButton = document.querySelector(`.follow-count[data-follow-count="${artistId}"]`);
const countSpan = countButton ? countButton.querySelector('span') : null;
const followLabel = button.dataset.followLabel || ARTISTS_I18N.followLabel;
const followingLabel = button.dataset.followingLabel || ARTISTS_I18N.followingLabel;
const updateCountDisplay = (delta) => {
if (!countButton || !countSpan) return;
let value = parseInt(countButton.dataset.count || countSpan.textContent.replace(/,/g,''), 10) || 0;
value = Math.max(0, value + delta);
countButton.dataset.count = value;
countSpan.textContent = value.toLocaleString();
};
const setButtonVisualState = (following) => {
button.classList.toggle('following', following);
const icon = button.querySelector('i');
if (icon) icon.className = following ? 'fas fa-heart' : 'fas fa-user-plus';
const label = button.querySelector('.label-text');
if (label) label.textContent = following ? followingLabel : followLabel;
};
// Update UI immediately
if (isFollowing) {
updateCountDisplay(-1);
setButtonVisualState(false);
} else {
updateCountDisplay(1);
setButtonVisualState(true);
}
// Make API call
fetch('/api/follow.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
artist_id: artistId,
action: action
})
})
.then(response => response.json())
.then(data => {
if (!data.success) {
// Revert UI on error
if (isFollowing) {
updateCountDisplay(1);
setButtonVisualState(true);
} else {
updateCountDisplay(-1);
setButtonVisualState(false);
}
if (typeof window.showNotification === 'function') {
window.showNotification(data.message || ARTISTS_I18N.errorFollowUpdate, 'error');
}
}
})
.catch(error => {
console.error('Error:', error);
// Revert UI on error
if (isFollowing) {
updateCountDisplay(1);
setButtonVisualState(true);
} else {
updateCountDisplay(-1);
setButtonVisualState(false);
}
});
}
// Floating cards with click handlers
function setupFloatingCards() {
const cards = document.querySelectorAll('.floating-card');
console.log('🎯 Found', cards.length, 'floating cards to setup');
cards.forEach((card, index) => {
const artistId = card.getAttribute('data-artist-id');
if (!artistId) {
console.warn(`⚠️ Card ${index + 1} has no artist ID, skipping`);
return;
}
// Remove any existing click handlers by cloning the node
// This prevents duplicate event listeners after AJAX navigation
const newCard = card.cloneNode(true);
card.parentNode.replaceChild(newCard, card);
console.log(`🎯 Setting up card ${index + 1} for artist ID: ${artistId}`);
// Mark as having listener attached (on the new node)
newCard.dataset.listenerAttached = 'true';
// Make cards clickable - attach directly to newCard
newCard.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
const clickedArtistId = this.getAttribute('data-artist-id');
console.log('🎯 Floating card clicked for artist ID:', clickedArtistId);
if (clickedArtistId) {
// Show loading notification
if (typeof window.showNotification === 'function') {
window.showNotification(ARTISTS_I18N.loadingProfile, 'info');
}
// Navigate to artist profile
setTimeout(() => {
if (window.ajaxNavigation && typeof window.ajaxNavigation.navigateToPage === 'function') {
window.ajaxNavigation.navigateToPage(`/artist_profile.php?id=${clickedArtistId}`);
} else {
window.location.href = `/artist_profile.php?id=${clickedArtistId}`;
}
}, 300);
} else {
console.error('❌ No artist ID found for card');
if (typeof window.showNotification === 'function') {
window.showNotification(ARTISTS_I18N.errorArtistNotFound, 'error');
}
}
});
// Add hover effects
newCard.addEventListener('mouseenter', function() {
if (!this.classList.contains('hall-of-fame-card')) {
this.style.transform = 'scale(1.15)';
this.style.boxShadow = '0 20px 50px rgba(102, 126, 234, 0.6)';
}
this.style.cursor = 'pointer';
});
newCard.addEventListener('mouseleave', function() {
if (!this.classList.contains('hall-of-fame-card')) {
this.style.transform = '';
this.style.boxShadow = '';
}
});
});
}
// Admin control for floating cards
function setupAdminFloatingControl() {
<?php if (isset($_SESSION['is_admin']) && $_SESSION['is_admin']): ?>
// Only show admin controls if user is admin
const heroSection = document.querySelector('.epic-hero');
if (heroSection) {
const adminControl = document.createElement('div');
adminControl.className = 'admin-floating-control';
adminControl.innerHTML = `
<div class="admin-control-panel">
<div class="admin-control-header">
<h4>🎛️ Floating Cards Control</h4>
<button class="close-admin-btn" onclick="closeAdminPanel()">✕</button>
</div>
<p>Select which artists appear in floating cards (max 10):</p>
<div class="artist-selector">
<?php
// Get saved selection or use Hall of Fame artists as default
$savedSelection = [];
if (isset($_SESSION['is_admin']) && $_SESSION['is_admin']) {
// Check localStorage via JavaScript, but for initial render use Hall of Fame
$defaultSelection = array_map(function($a) { return $a['id']; }, $hallOfFameArtists);
} else {
$defaultSelection = [];
}
foreach ($artists as $artist):
$isChecked = in_array($artist['id'], $defaultSelection);
?>
<label class="artist-option">
<input type="checkbox"
name="floating_artist"
value="<?= $artist['id'] ?>"
<?= $isChecked ? 'checked' : '' ?>>
<span class="artist-name"><?= htmlspecialchars($artist['username']) ?> (<?= $artist['completed_tracks'] ?? 0 ?> tracks, <?= $artist['followers_count'] ?? 0 ?> followers)</span>
</label>
<?php endforeach; ?>
</div>
<div class="admin-control-actions">
<button class="update-floating-btn" onclick="updateFloatingCards()">
🚀 Update Floating Cards
</button>
<button class="reset-floating-btn" onclick="resetFloatingCards()">
🔄 Reset to Default
</button>
</div>
</div>
`;
heroSection.appendChild(adminControl);
// Don't load saved selection here - it will be loaded in initializeArtistsPage()
// This prevents conflicts when AJAX navigation loads the page
}
<?php endif; ?>
}
// Update floating cards based on admin selection
function updateFloatingCards() {
const selectedArtists = Array.from(document.querySelectorAll('input[name="floating_artist"]:checked'))
.map(input => parseInt(input.value));
console.log('🎯 Selected artists for floating cards:', selectedArtists);
if (selectedArtists.length > 8) {
if (typeof window.showNotification === 'function') {
window.showNotification(ARTISTS_I18N.warningMaxFloating, 'error');
} else {
alert(ARTISTS_I18N.warningMaxFloating);
}
return;
}
if (selectedArtists.length === 0) {
if (typeof window.showNotification === 'function') {
window.showNotification(ARTISTS_I18N.warningMinFloating, 'error');
} else {
alert(ARTISTS_I18N.warningMinFloating);
}
return;
}
// Save selection to localStorage for persistence
localStorage.setItem('floatingArtists', JSON.stringify(selectedArtists));
// Update display immediately without page reload
updateFloatingCardsDisplay(selectedArtists);
// Show success message
if (typeof window.showNotification === 'function') {
window.showNotification(ARTISTS_I18N.floatingUpdated, 'success');
} else {
console.log('✅ Floating cards updated successfully!');
}
}
// Reset floating cards to default (use Hall of Fame artists)
function resetFloatingCards() {
localStorage.removeItem('floatingArtists');
// Get default Hall of Fame artists from PHP
const hallOfFameArtists = <?= json_encode(array_map(function($a) { return $a['id']; }, $hallOfFameArtists)) ?>;
// Update checkboxes
document.querySelectorAll('input[name="floating_artist"]').forEach(checkbox => {
checkbox.checked = hallOfFameArtists.includes(parseInt(checkbox.value));
});
// Update display
updateFloatingCardsDisplay(hallOfFameArtists);
if (typeof window.showNotification === 'function') {
window.showNotification(<?= json_encode(t('artists.reset_cards_message')) ?>, 'info');
} else {
console.log('✅ Floating cards reset to default!');
}
}
// Load floating cards from admin selection
function loadFloatingCardsFromSelection() {
const savedSelection = localStorage.getItem('floatingArtists');
const allCheckboxes = document.querySelectorAll('input[name="floating_artist"]');
if (savedSelection) {
try {
const selectedIds = JSON.parse(savedSelection).map(id => parseInt(id));
console.log('🎯 Loading floating cards from saved selection:', selectedIds);
// CRITICAL: Sync ALL checkboxes - uncheck ones not in saved selection
allCheckboxes.forEach(checkbox => {
const checkboxId = parseInt(checkbox.value);
checkbox.checked = selectedIds.includes(checkboxId);
});
// Update floating cards display based on selection
if (selectedIds.length > 0) {
updateFloatingCardsDisplay(selectedIds);
} else {
// If no saved selection, use Hall of Fame default
const hallOfFameArtists = <?= json_encode(array_map(function($a) { return $a['id']; }, $hallOfFameArtists)) ?>;
updateFloatingCardsDisplay(hallOfFameArtists);
}
} catch (e) {
console.error('❌ Error loading saved selection:', e);
localStorage.removeItem('floatingArtists');
// Fall back to Hall of Fame
const hallOfFameArtists = <?= json_encode(array_map(function($a) { return $a['id']; }, $hallOfFameArtists)) ?>;
allCheckboxes.forEach(checkbox => {
checkbox.checked = hallOfFameArtists.includes(parseInt(checkbox.value));
});
updateFloatingCardsDisplay(hallOfFameArtists);
}
} else {
// No saved selection - use Hall of Fame default and sync checkboxes
const hallOfFameArtists = <?= json_encode(array_map(function($a) { return $a['id']; }, $hallOfFameArtists)) ?>;
allCheckboxes.forEach(checkbox => {
checkbox.checked = hallOfFameArtists.includes(parseInt(checkbox.value));
});
// Don't update display here - it's already set by PHP
}
}
// Update floating cards display based on selected artist IDs
function updateFloatingCardsDisplay(selectedIds) {
const floatingCardsContainer = document.querySelector('.floating-cards');
if (!floatingCardsContainer) {
console.error('❌ Floating cards container not found');
return;
}
// Get full artists data from PHP (passed to JavaScript)
const allArtists = <?= json_encode($artists) ?>;
// Clear existing cards
floatingCardsContainer.innerHTML = '';
// Create cards for selected artists using full data
selectedIds.forEach((artistId, index) => {
if (index >= 8) return; // Max 8 cards
const artist = allArtists.find(a => a.id == artistId);
if (!artist) {
console.warn('⚠️ Artist not found:', artistId);
return;
}
const card = document.createElement('div');
card.className = `floating-card hall-of-fame-card card-${index + 1}`;
card.setAttribute('data-artist-id', artist.id);
// Get profile image or use initial
const profileImage = artist.profile_image || '';
const profilePosition = artist.profile_position || 'center center';
const initial = (artist.username || '').charAt(0).toUpperCase();
card.innerHTML = `
<div class="card-glow"></div>
<div class="hall-of-fame-badge">⭐</div>
<div class="card-content">
${profileImage ? `
<img src="${profileImage.replace(/"/g, '"')}"
alt="${(artist.username || '').replace(/"/g, '"')}"
class="card-avatar-image"
style="object-position: ${profilePosition.replace(/"/g, '"')};"
onerror="this.onerror=null; this.style.display='none'; const avatar = this.parentElement.querySelector('.card-avatar'); if(avatar) avatar.style.display='flex';">
<div class="card-avatar" style="display:none;">
<div class="card-avatar-pattern">
<i class="fas fa-music"></i>
</div>
<div class="card-avatar-gradient-overlay"></div>
</div>
` : `
<div class="card-avatar">
<div class="card-avatar-pattern">
<i class="fas fa-music"></i>
</div>
<div class="card-avatar-gradient-overlay"></div>
</div>
`}
<div class="card-name">${(artist.username || '').replace(/</g, '<').replace(/>/g, '>')}</div>
<div class="card-stats">
<span class="stat">🎵 ${artist.completed_tracks || 0} ${ARTISTS_I18N.tracksLabel}</span>
<span class="stat">👥 ${artist.followers_count || 0} ${ARTISTS_I18N.followersLabel}</span>
</div>
</div>
`;
floatingCardsContainer.appendChild(card);
});
// Reinitialize floating card interactions after a short delay to ensure DOM is ready
setTimeout(() => {
setupFloatingCards();
}, 100);
}
// Close admin panel
function closeAdminPanel() {
const adminControl = document.querySelector('.admin-floating-control');
if (adminControl) {
adminControl.style.display = 'none';
console.log('🎯 Admin panel closed');
}
}
// Scroll function
function scrollToArtists() {
console.log('🎯 Scrolling to artists section...');
// Show feedback to user
if (typeof window.showNotification === 'function') {
window.showNotification(ARTISTS_I18N.scrolling, 'info');
}
const section = document.querySelector('.artists-content');
if (section) {
console.log('🎯 Found artists section, scrolling...');
section.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
// Add a small delay to ensure scroll completes
setTimeout(() => {
console.log('🎯 Scroll completed');
}, 1000);
} else {
console.error('❌ Artists section not found!');
if (typeof window.showNotification === 'function') {
window.showNotification(ARTISTS_I18N.scrollingError, 'error');
}
}
}
// Fallback notification function
if (typeof window.showNotification === 'undefined') {
window.showNotification = function(message, type = 'info') {
console.log(`🎯 ${type.toUpperCase()}: ${message}`);
// Create a simple notification
const notification = document.createElement('div');
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: ${type === 'error' ? '#f56565' : type === 'success' ? '#48bb78' : '#667eea'};
color: white;
padding: 15px 20px;
border-radius: 8px;
z-index: 10000;
font-family: Arial, sans-serif;
font-size: 14px;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, 3000);
};
}
// Reusable initialization function (called on both normal load and AJAX load)
function initializeArtistsPage() {
console.log('🚀 Initializing artists page...');
try {
createSimpleParticles();
updateStats();
setupSearch();
setupFilters();
// CRITICAL: Clear listener flags before setting up floating cards
// This allows re-initialization after AJAX navigation
document.querySelectorAll('.floating-card').forEach(card => {
card.dataset.listenerAttached = 'false';
});
setupFloatingCards();
setupAdminFloatingControl();
// Load saved selection AFTER admin control is set up (for both admin and non-admin)
// This ensures checkboxes and cards are properly synced
setTimeout(() => {
<?php if (isset($_SESSION['is_admin']) && $_SESSION['is_admin']): ?>
loadFloatingCardsFromSelection();
<?php else: ?>
// For non-admin, just ensure cards match Hall of Fame
const hallOfFameArtists = <?= json_encode(array_map(function($a) { return $a['id']; }, $hallOfFameArtists)) ?>;
updateFloatingCardsDisplay(hallOfFameArtists);
<?php endif; ?>
}, 200);
// Initialize artist card clicks (community style)
const artistCards = document.querySelectorAll('.artist-card-community');
console.log('🎯 Found', artistCards.length, 'artist cards');
artistCards.forEach((card) => {
const artistId = card.getAttribute('data-artist-id');
if (artistId) {
// Remove existing listeners to prevent duplicates
const newCard = card.cloneNode(true);
card.parentNode.replaceChild(newCard, card);
// Make entire card clickable (except buttons)
newCard.addEventListener('click', function(e) {
// Don't trigger if clicking on buttons or links
if (e.target.closest('button') || e.target.closest('a')) {
return;
}
loadArtistProfile(artistId);
});
}
});
// Initialize view profile buttons (if any still exist)
const viewProfileButtons = document.querySelectorAll('.view-profile-btn');
console.log('🎯 Found', viewProfileButtons.length, 'View Profile buttons');
viewProfileButtons.forEach((button, index) => {
const artistId = button.getAttribute('data-artist-id');
console.log(`🎯 Button ${index + 1}: artist ID = ${artistId}`);
button.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
const artistId = this.getAttribute('data-artist-id');
console.log('🎯 View Profile clicked for artist ID:', artistId);
if (artistId) {
loadArtistProfile(artistId);
} else {
console.error('🎯 No artist ID found on button!');
if (typeof window.showNotification === 'function') {
window.showNotification(ARTISTS_I18N.profileLoadError, 'error');
}
}
});
});
// Initialize follow buttons
const followButtons = document.querySelectorAll('.follow-btn');
console.log('🎯 Found', followButtons.length, 'Follow buttons');
followButtons.forEach(button => {
button.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
const artistId = this.getAttribute('data-artist-id');
console.log('🎯 Follow button clicked for artist ID:', artistId);
if (artistId) {
toggleFollow(artistId, this);
}
});
});
// Initialize clickable stats
setupClickableStats();
// Initialize modal functionality
setupModal();
console.log('✅ Artists page initialized successfully!');
} catch (error) {
console.error('❌ Artists page initialization error:', error);
}
}
// Initialize on DOM ready (normal page load)
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeArtistsPage);
} else {
// DOM already loaded
initializeArtistsPage();
}
// CRITICAL: Also initialize when AJAX navigation loads the page
document.addEventListener('ajaxPageLoaded', function(e) {
console.log('🔄 AJAX page loaded, reinitializing artists page...');
// Small delay to ensure DOM is fully updated
setTimeout(() => {
initializeArtistsPage();
// After initialization, sync with localStorage if admin
<?php if (isset($_SESSION['is_admin']) && $_SESSION['is_admin']): ?>
setTimeout(() => {
loadFloatingCardsFromSelection();
}, 300);
<?php endif; ?>
}, 100);
});
// Setup clickable stats
function setupClickableStats() {
const clickableStats = document.querySelectorAll('.clickable-stat');
console.log('🎯 Found', clickableStats.length, 'clickable stats');
clickableStats.forEach(stat => {
stat.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
const type = this.getAttribute('data-type');
const artistId = this.getAttribute('data-artist-id');
console.log('🎯 Stat clicked:', type, 'for artist:', artistId);
if (type && artistId) {
openModal(type, artistId);
}
});
});
}
// Modal functionality
function setupModal() {
const modalOverlay = document.getElementById('modalOverlay');
const modalClose = document.getElementById('modalClose');
console.log('🎯 Setting up modal controls');
console.log('🎯 Modal overlay:', modalOverlay ? 'found' : 'not found');
console.log('🎯 Modal close button:', modalClose ? 'found' : 'not found');
// Close modal on overlay click
if (modalOverlay) {
modalOverlay.addEventListener('click', function(e) {
if (e.target === modalOverlay) {
console.log('🎯 Modal overlay clicked - closing');
closeModal();
}
});
}
// Close modal on close button click
if (modalClose) {
modalClose.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
console.log('🎯 Modal close button clicked');
closeModal();
});
}
// Close modal on escape key
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && modalOverlay && modalOverlay.classList.contains('active')) {
console.log('🎯 Escape key pressed - closing modal');
closeModal();
}
});
}
// Open modal
function openModal(type, artistId) {
const modalOverlay = document.getElementById('modalOverlay');
const modalTitle = document.getElementById('modalTitle');
const modalContent = document.getElementById('modalContent');
// Prevent multiple rapid clicks
if (modalOverlay.classList.contains('active')) {
return;
}
// Set modal title based on type
const modalConfigs = {
tracks: { icon: 'fas fa-music', title: ARTISTS_I18N.modalTitles.tracks, loading: ARTISTS_I18N.modalLoading.tracks },
completed: { icon: 'fas fa-check-circle', title: ARTISTS_I18N.modalTitles.completed, loading: ARTISTS_I18N.modalLoading.completed },
followers: { icon: 'fas fa-users', title: ARTISTS_I18N.modalTitles.followers, loading: ARTISTS_I18N.modalLoading.followers },
following: { icon: 'fas fa-user-plus', title: ARTISTS_I18N.modalTitles.following, loading: ARTISTS_I18N.modalLoading.following }
};
const titleConfig = modalConfigs[type] || { icon: 'fas fa-info', title: ARTISTS_I18N.modalTitles.default, loading: ARTISTS_I18N.loading };
modalTitle.innerHTML = `<i class="${titleConfig.icon}"></i><span>${titleConfig.title}</span>`;
// Show loading state
modalContent.innerHTML = `
<div class="modal-loading">
<i class="fas fa-spinner fa-spin"></i>
<div>${titleConfig.loading}</div>
</div>
`;
// Show modal with a small delay for better UX
setTimeout(() => {
modalOverlay.classList.add('active');
// Load content after modal is visible
setTimeout(() => {
loadModalContent(type, artistId);
}, 100);
}, 50);
}
// Close modal
function closeModal() {
const modalOverlay = document.getElementById('modalOverlay');
const modalContent = document.getElementById('modalContent');
// Add fade out effect
modalOverlay.style.opacity = '0';
modalOverlay.style.transform = 'scale(0.95)';
setTimeout(() => {
modalOverlay.classList.remove('active');
modalOverlay.style.opacity = '';
modalOverlay.style.transform = '';
// Clear content
modalContent.innerHTML = '';
}, 200);
}
// Load modal content
function loadModalContent(type, artistId) {
const modalContent = document.getElementById('modalContent');
switch(type) {
case 'tracks':
case 'completed':
loadTracksModal(artistId, type);
break;
case 'followers':
loadFollowersModal(artistId);
break;
case 'following':
loadFollowingModal(artistId);
break;
default:
modalContent.innerHTML = `
<div class="modal-empty">
<i class="fas fa-exclamation-triangle"></i>
<div>${ARTISTS_I18N.modalUnknownType}</div>
</div>
`;
}
}
// Load tracks modal
function loadTracksModal(artistId, type) {
const modalContent = document.getElementById('modalContent');
// Show loading
modalContent.innerHTML = `
<div class="modal-loading">
<i class="fas fa-spinner fa-spin"></i>
<div>${ARTISTS_I18N.modalLoading.tracks}</div>
</div>
`;
// Fetch tracks from API
fetch(`/api/get_artist_tracks.php?artist_id=${artistId}&type=${type}`)
.then(response => response.json())
.then(data => {
if (data.success && data.tracks) {
renderTracksModal(data.tracks, type);
} else {
modalContent.innerHTML = `
<div class="modal-empty">
<i class="fas fa-music"></i>
<div>${ARTISTS_I18N.noTracks}</div>
</div>
`;
}
})
.catch(error => {
console.error('Error loading tracks:', error);
modalContent.innerHTML = `
<div class="modal-empty">
<i class="fas fa-exclamation-triangle"></i>
<div>${ARTISTS_I18N.errorLoadingTracks}</div>
</div>
`;
});
}
// Render tracks in modal
function renderTracksModal(tracks, type) {
const modalContent = document.getElementById('modalContent');
const tracksHtml = tracks.map(track => `
<div class="modal-item track-item" data-track-id="${track.id}">
<div class="track-header">
<div class="track-title">${track.title || ARTISTS_I18N.untitledTrack}</div>
<div class="track-duration">${formatDuration(Math.min(track.duration || 0, 30))} ${ARTISTS_I18N.previewLabel}</div>
</div>
<div class="track-info">
<div class="track-genre">${track.genre || ARTISTS_I18N.unknownGenre}</div>
<div class="track-type">${track.music_type || ARTISTS_I18N.unknownGenre}</div>
</div>
<div class="track-preview">
<button class="preview-play-btn" data-track-id="${track.id}" data-audio-url="/utils/play_audio.php?id=${track.id}" data-track-title="${track.title || ARTISTS_I18N.untitledTrack}">
<i class="fas fa-play"></i>
<span>${ARTISTS_I18N.previewButton}</span>
</button>
</div>
<div class="track-actions">
<button class="btn btn-primary add-to-cart-btn" data-track-id="${track.id}" data-track-title="${track.title || ARTISTS_I18N.untitledTrack}" data-track-artist="${track.artist_name || ARTISTS_I18N.unknownArtist}" data-track-price="${track.price || 9.99}">
<i class="fas fa-shopping-cart"></i>
${ARTISTS_I18N.addToCart}
</button>
<button class="btn btn-secondary add-to-playlist-btn" data-track-id="${track.id}">${ARTISTS_I18N.addToPlaylist}</button>
</div>
</div>
`).join('');
modalContent.innerHTML = `
<div class="modal-search">
<input type="text" placeholder="${ARTISTS_I18N.modalSearch.tracks}" id="trackSearch">
</div>
<div class="modal-grid">
${tracksHtml}
</div>
`;
// Setup search functionality
setupTrackSearch();
// Setup track action buttons with a small delay to ensure DOM is ready
setTimeout(() => {
setupTrackActions();
setupPreviewPlayButtons();
}, 100);
// Setup purchase modal
setupPurchaseModal();
console.log('🎯 Tracks modal loaded successfully');
}
// Load followers modal
function loadFollowersModal(artistId) {
const modalContent = document.getElementById('modalContent');
// Show loading
modalContent.innerHTML = `
<div class="modal-loading">
<i class="fas fa-spinner fa-spin"></i>
<div>${ARTISTS_I18N.modalLoading.followers}</div>
</div>
`;
setTimeout(() => {
modalContent.innerHTML = `
<div class="modal-empty">
<i class="fas fa-users"></i>
<div>${ARTISTS_I18N.followersPlaceholder}</div>
</div>
`;
}, 600);
}
// Load following modal
function loadFollowingModal(artistId) {
const modalContent = document.getElementById('modalContent');
// Show loading
modalContent.innerHTML = `
<div class="modal-loading">
<i class="fas fa-spinner fa-spin"></i>
<div>${ARTISTS_I18N.modalLoading.following}</div>
</div>
`;
setTimeout(() => {
modalContent.innerHTML = `
<div class="modal-empty">
<i class="fas fa-user-plus"></i>
<div>${ARTISTS_I18N.followingPlaceholder}</div>
</div>
`;
}, 600);
}
// Setup track search
function setupTrackSearch() {
const searchInput = document.getElementById('trackSearch');
if (searchInput) {
searchInput.addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();
const trackItems = document.querySelectorAll('.track-item');
trackItems.forEach(item => {
const title = item.querySelector('.track-title').textContent.toLowerCase();
if (title.includes(searchTerm)) {
item.style.display = 'block';
} else {
item.style.display = 'none';
}
});
});
}
}
// Format duration helper
function formatDuration(seconds) {
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
}
// Setup track action buttons
function setupTrackActions() {
// Add to cart buttons
const addToCartButtons = document.querySelectorAll('.add-to-cart-btn');
console.log('🎯 Found', addToCartButtons.length, 'add to cart buttons');
addToCartButtons.forEach(button => {
console.log('🎯 Setting up add to cart button with track ID:', button.getAttribute('data-track-id'));
button.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
const trackId = this.getAttribute('data-track-id');
const trackTitle = this.getAttribute('data-track-title');
const trackArtist = this.getAttribute('data-track-artist');
const trackPrice = parseFloat(this.getAttribute('data-track-price'));
console.log('🎯 Add to cart button clicked for track:', trackId, trackTitle, trackArtist, trackPrice);
// Add track to cart
addTrackToCart(trackId, trackTitle, trackArtist, trackPrice, this);
});
});
// Add to playlist buttons
const addToPlaylistButtons = document.querySelectorAll('.add-to-playlist-btn');
addToPlaylistButtons.forEach(button => {
button.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
const trackId = this.getAttribute('data-track-id');
console.log('🎯 Add to playlist button clicked for track:', trackId);
// For now, show a message about playlist functionality
if (typeof window.showNotification === 'function') {
window.showNotification(ARTISTS_I18N.playlistComing, 'info');
} else {
alert(ARTISTS_I18N.playlistComing);
}
});
});
}
// Setup preview play buttons
function setupPreviewPlayButtons() {
const previewButtons = document.querySelectorAll('.preview-play-btn');
console.log('🎯 Found', previewButtons.length, 'preview play buttons');
previewButtons.forEach(button => {
button.addEventListener('click', async function(e) {
e.preventDefault();
e.stopPropagation();
const trackId = this.getAttribute('data-track-id');
const trackTitle = this.getAttribute('data-track-title');
console.log('🎯 Preview play button clicked for track:', trackId, trackTitle);
// Check if this button is already playing
if (this.classList.contains('playing')) {
// Stop this preview
this.classList.remove('playing');
this.innerHTML = `<i class="fas fa-play"></i><span>${ARTISTS_I18N.previewButton}</span>`;
// Stop enhanced global player if it's playing this track
if (window.enhancedGlobalPlayer && window.enhancedGlobalPlayer.audio) {
window.enhancedGlobalPlayer.audio.pause();
}
return;
}
// Stop any currently playing preview
stopAllPreviews();
// Update button state
this.classList.add('playing');
this.innerHTML = `<i class="fas fa-pause"></i><span>${ARTISTS_I18N.previewPlayingButton}</span>`;
// Get signed audio URL from API
let audioUrl = '';
try {
const response = await fetch('/api/get_audio_token.php?track_id=' + trackId);
const data = await response.json();
if (data.success && data.url) {
audioUrl = data.url;
} else {
console.error('Failed to get audio token:', data.error);
this.classList.remove('playing');
this.innerHTML = `<i class="fas fa-play"></i><span>${ARTISTS_I18N.previewButton}</span>`;
return;
}
} catch (err) {
console.error('Error getting audio token:', err);
this.classList.remove('playing');
this.innerHTML = `<i class="fas fa-play"></i><span>${ARTISTS_I18N.previewButton}</span>`;
return;
}
// Play through enhanced global player if available
if (typeof window.enhancedGlobalPlayer !== 'undefined' && typeof window.enhancedGlobalPlayer.playTrack === 'function') {
console.log('🎵 Using enhanced global player for preview');
window.enhancedGlobalPlayer.playTrack(audioUrl, trackTitle, ARTISTS_I18N.previewTrack);
// Set up 30-second preview limit for enhanced global player
setTimeout(() => {
if (window.enhancedGlobalPlayer && window.enhancedGlobalPlayer.audio) {
window.enhancedGlobalPlayer.audio.currentTime = 0;
window.enhancedGlobalPlayer.audio.pause();
// Reset button state
button.classList.remove('playing');
button.innerHTML = `<i class="fas fa-play"></i><span>${ARTISTS_I18N.previewButton}</span>`;
}
}, 30000); // 30 seconds
} else {
console.log('🎵 Enhanced global player not available, using fallback');
// Fallback: create temporary audio element
playPreviewAudio(audioUrl, trackTitle, this);
}
// Show notification
if (typeof window.showNotification === 'function') {
window.showNotification(ARTISTS_I18N.previewPlayingNotice.replace(':title', trackTitle), 'info');
}
});
});
}
// Stop all preview buttons
function stopAllPreviews() {
const previewButtons = document.querySelectorAll('.preview-play-btn');
previewButtons.forEach(button => {
button.classList.remove('playing');
button.innerHTML = `<i class="fas fa-play"></i><span>${ARTISTS_I18N.previewButton}</span>`;
});
}
// Fallback preview audio function
function playPreviewAudio(audioUrl, trackTitle, button) {
console.log('🎵 Using fallback preview audio');
// Create temporary audio element
const audio = new Audio(audioUrl);
audio.currentTime = 0;
// Set up 30-second preview
const previewDuration = 30;
audio.addEventListener('timeupdate', function() {
if (audio.currentTime >= previewDuration) {
audio.pause();
button.classList.remove('playing');
button.innerHTML = `<i class="fas fa-play"></i><span>${ARTISTS_I18N.previewButton}</span>`;
}
});
audio.addEventListener('ended', function() {
button.classList.remove('playing');
button.innerHTML = `<i class="fas fa-play"></i><span>${ARTISTS_I18N.previewButton}</span>`;
});
audio.addEventListener('error', function() {
button.classList.remove('playing');
button.innerHTML = `<i class="fas fa-play"></i><span>${ARTISTS_I18N.previewButton}</span>`;
console.error('Error playing preview audio');
});
// Play the audio
audio.play().catch(error => {
console.error('Error playing audio:', error);
button.classList.remove('playing');
button.innerHTML = `<i class="fas fa-play"></i><span>${ARTISTS_I18N.previewButton}</span>`;
});
}
// Purchase functions
function showPurchaseConfirmation(trackId) {
console.log('🎯 showPurchaseConfirmation called with trackId:', trackId);
const purchaseModal = document.getElementById('purchaseModal');
const purchaseContent = document.getElementById('purchaseContent');
if (!purchaseModal) {
console.error('🎯 Purchase modal not found!');
return;
}
// Show modal
purchaseModal.classList.add('active');
// Load track details
loadTrackPurchaseDetails(trackId);
}
function loadTrackPurchaseDetails(trackId) {
const purchaseContent = document.getElementById('purchaseContent');
console.log('🎯 Loading track details for ID:', trackId);
// Show loading
purchaseContent.innerHTML = `
<div class="purchase-loading">
<i class="fas fa-spinner fa-spin"></i>
<div>${ARTISTS_I18N.loadingTrackDetails}</div>
</div>
`;
// Create a new API endpoint to get single track details
const apiUrl = `/api/get_track_details.php?track_id=${trackId}`;
console.log('🎯 Fetching from:', apiUrl);
fetch(apiUrl)
.then(response => {
console.log('🎯 Response status:', response.status);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('🎯 API response:', data);
if (data.success) {
showPurchaseDetails(data.track);
} else {
showPurchaseError(data.error || ARTISTS_I18N.trackNotFound);
}
})
.catch(error => {
console.error('🎯 Error loading track details:', error);
showPurchaseError(ARTISTS_I18N.trackDetailsError.replace(':message', error.message));
});
}
function showPurchaseDetails(track) {
if (!track) {
console.error('showPurchaseDetails: track is null or undefined');
showPurchaseError(ARTISTS_I18N.trackNotFound || 'Track not found');
return;
}
const purchaseContent = document.getElementById('purchaseContent');
const price = parseFloat(track.price) || 1.00; // Convert to number
const trackId = parseInt(track.id) || 0; // Ensure track.id is a valid number
const trackTitle = (track.title || ARTISTS_I18N.untitledTrack || 'Untitled Track');
const trackArtist = (track.artist_name || ARTISTS_I18N.unknownArtist || 'Unknown Artist');
const artistLine = ARTISTS_I18N.byArtist.replace(':artist', trackArtist);
const priceLine = `$${price.toFixed(2)} (${Math.ceil(price)} ${ARTISTS_I18N.creditsLabel})`;
// Escape strings for use in JavaScript within HTML attributes (JSON.stringify handles escaping, then use single quotes)
const escapeForJs = (str) => {
if (str === null || str === undefined) {
return "''";
}
const jsonStr = JSON.stringify(String(str));
// Replace outer double quotes with single quotes
if (jsonStr.startsWith('"') && jsonStr.endsWith('"')) {
return "'" + jsonStr.slice(1, -1) + "'";
}
return jsonStr;
};
const escapedTitle = escapeForJs(track.title || ARTISTS_I18N.untitledTrack);
const escapedTrackTitle = escapeForJs(trackTitle);
const escapedTrackArtist = escapeForJs(trackArtist);
// Validate all values before using in template
if (isNaN(trackId) || trackId < 0) {
console.error('Invalid trackId:', trackId);
trackId = 0;
}
if (isNaN(price) || price < 0) {
console.error('Invalid price:', price);
price = 1.00;
}
console.log('🎯 Track price:', track.price, 'Parsed price:', price, 'trackId:', trackId);
purchaseContent.innerHTML = `
<div class="purchase-details">
<div class="track-purchase-info">
<div class="track-purchase-title">${trackTitle}</div>
<div class="track-purchase-artist">${artistLine}</div>
<div class="track-purchase-price">${priceLine}</div>
<div class="track-purchase-preview">
<button class="preview-play-btn" onclick="playPurchasePreview(${trackId}, ${escapedTitle})">
<i class="fas fa-play"></i>
<span>${ARTISTS_I18N.previewTrack}</span>
</button>
</div>
</div>
<div class="purchase-actions">
<button class="purchase-btn purchase-cancel" onclick="closePurchaseModal()">${ARTISTS_I18N.purchaseCancel}</button>
<button class="purchase-btn purchase-confirm" onclick="addTrackToCartFromModal(${trackId}, ${escapedTrackTitle}, ${escapedTrackArtist}, ${price})">
<i class="fas fa-shopping-cart"></i>
${ARTISTS_I18N.purchaseConfirm}
</button>
</div>
</div>
`;
}
function showPurchaseError(message) {
const purchaseContent = document.getElementById('purchaseContent');
purchaseContent.innerHTML = `
<div class="purchase-loading">
<i class="fas fa-exclamation-triangle"></i>
<div>${message}</div>
<button class="purchase-btn purchase-cancel" onclick="closePurchaseModal()" style="margin-top: 1rem;">${ARTISTS_I18N.purchaseClose}</button>
</div>
`;
}
function showLoginPrompt(redirectUrl, trackId) {
const purchaseContent = document.getElementById('purchaseContent');
// Ensure redirectUrl is safe for use in href attribute
const safeRedirectUrl = redirectUrl ? String(redirectUrl).replace(/"/g, '"') : '/auth/login.php';
// Ensure i18n strings are defined
const signinTitle = ARTISTS_I18N.signinTitle || 'Sign In Required';
const signinDesc = ARTISTS_I18N.signinDesc || 'Please sign in to purchase this track.';
const signinCta = ARTISTS_I18N.signinCta || 'Sign In';
const purchaseCancel = ARTISTS_I18N.purchaseCancel || 'Cancel';
const loginRedirectNote = ARTISTS_I18N.loginRedirectNote || '';
purchaseContent.innerHTML = `
<div class="login-prompt">
<div class="login-prompt-icon">
<i class="fas fa-user-lock"></i>
</div>
<h3>${signinTitle}</h3>
<p>${signinDesc}</p>
<div class="login-prompt-actions">
<a href="${safeRedirectUrl}" class="purchase-btn purchase-confirm">
<i class="fas fa-sign-in-alt"></i>
${signinCta}
</a>
<button class="purchase-btn purchase-cancel" onclick="closePurchaseModal()">
${purchaseCancel}
</button>
</div>
<div class="login-prompt-note">
<small>${loginRedirectNote}</small>
</div>
</div>
`;
}
async function playPurchasePreview(trackId, trackTitle) {
// Get signed URL from API
try {
const response = await fetch('/api/get_audio_token.php?track_id=' + trackId);
const data = await response.json();
if (data.success && data.url) {
// Play preview in enhanced global player
if (typeof window.enhancedGlobalPlayer !== 'undefined' && typeof window.enhancedGlobalPlayer.playTrack === 'function') {
window.enhancedGlobalPlayer.playTrack(data.url, trackTitle, ARTISTS_I18N.previewTrack);
}
} else {
console.error('Failed to get audio token:', data.error);
}
} catch (err) {
console.error('Error getting audio token:', err);
}
}
function confirmPurchase(trackId, price) {
console.log('🎯 Confirming purchase for track:', trackId, 'price:', price);
const confirmBtn = document.querySelector('.purchase-confirm');
const originalText = confirmBtn.innerHTML;
// Show loading state
confirmBtn.innerHTML = `<i class="fas fa-spinner fa-spin"></i> ${ARTISTS_I18N.processing}`;
confirmBtn.disabled = true;
// Send purchase request
const formData = new FormData();
formData.append('track_id', trackId);
formData.append('payment_method', 'stripe'); // Always use Stripe for dollar amount
fetch('/api/purchase_track.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
console.log('🎯 Purchase response:', data);
if (data.success) {
if (data.requires_payment) {
// User needs to pay via Stripe
processStripePayment(data);
// Reset button since we're showing payment form
confirmBtn.innerHTML = originalText;
confirmBtn.disabled = false;
} else {
// Direct purchase successful
showPurchaseSuccess(data.purchase);
}
} else {
if (data.redirect) {
// User needs to login - show login prompt
showLoginPrompt(data.redirect, trackId);
} else {
showPurchaseError(data.error || ARTISTS_I18N.purchaseFailed);
}
// Reset button
confirmBtn.innerHTML = originalText;
confirmBtn.disabled = false;
}
})
.catch(error => {
console.error('🎯 Purchase error:', error);
showPurchaseError(ARTISTS_I18N.purchaseFailedRetry);
// Reset button
confirmBtn.innerHTML = originalText;
confirmBtn.disabled = false;
});
}
// Process Stripe payment for track purchase
function processStripePayment(paymentData) {
console.log('🎯 Processing Stripe payment for track:', paymentData.track.id);
// Load Stripe.js if not already loaded
if (typeof Stripe === 'undefined') {
const script = document.createElement('script');
script.src = 'https://js.stripe.com/v3/';
script.onload = () => initializeStripePayment(paymentData);
document.head.appendChild(script);
} else {
initializeStripePayment(paymentData);
}
}
function initializeStripePayment(paymentData) {
const stripe = Stripe('pk_live_51Rn8TtD0zXLMB4gHMCZ5OMunyo0YtN6hBR30BoXFEiQxPG9I6U2tko6Axxwl0yJS21DCCykhC9PxAMdZoEfwJI0p00KlrZUR3w');
// Show payment form
const purchaseContent = document.getElementById('purchaseContent');
purchaseContent.innerHTML = `
<div class="purchase-details">
<div class="track-purchase-info">
<div class="track-purchase-title">${paymentData.track.title}</div>
<div class="track-purchase-artist">by ${paymentData.track.artist_name}</div>
<div class="track-purchase-price">$${paymentData.amount.toFixed(2)}</div>
<p style="color: #a0aec0; margin: 1rem 0;">Complete your purchase to own this track.</p>
</div>
<div id="payment-element" style="margin: 2rem 0;"></div>
<div class="purchase-actions">
<button class="purchase-btn purchase-cancel" onclick="closePurchaseModal()">Cancel</button>
<button class="purchase-btn purchase-confirm" id="stripe-confirm-btn" onclick="confirmStripePayment()">
<i class="fas fa-credit-card"></i>
Pay $${paymentData.amount.toFixed(2)}
</button>
</div>
</div>
`;
// Create payment element
const elements = stripe.elements();
const paymentElement = elements.create('payment');
paymentElement.mount('#payment-element');
// Store payment data for confirmation
window.currentStripePayment = {
stripe: stripe,
clientSecret: paymentData.payment_intent,
trackData: paymentData.track
};
}
function confirmStripePayment() {
const paymentData = window.currentStripePayment;
if (!paymentData) {
showPurchaseError(ARTISTS_I18N.paymentDataMissing);
return;
}
const confirmBtn = document.getElementById('stripe-confirm-btn');
const originalText = confirmBtn.innerHTML;
// Show loading state
confirmBtn.innerHTML = `<i class="fas fa-spinner fa-spin"></i> ${ARTISTS_I18N.processingPayment}`;
confirmBtn.disabled = true;
// Confirm payment
paymentData.stripe.confirmPayment({
elements: paymentData.stripe.elements(),
confirmParams: {
return_url: window.location.origin + '/artists.php?purchase_success=1',
},
}).then((result) => {
if (result.error) {
console.error('🎯 Payment error:', result.error);
showPurchaseError(ARTISTS_I18N.paymentFailedReason.replace(':message', result.error.message));
confirmBtn.innerHTML = originalText;
confirmBtn.disabled = false;
} else {
// Payment successful - webhook will handle the rest
showPurchaseSuccess({
track_title: paymentData.trackData.title,
artist_name: paymentData.trackData.artist_name,
payment_method: 'stripe',
message: ARTISTS_I18N.paymentSuccessPending
});
}
}).catch((error) => {
console.error('🎯 Payment confirmation error:', error);
showPurchaseError(ARTISTS_I18N.paymentFailedRetry);
confirmBtn.innerHTML = originalText;
confirmBtn.disabled = false;
});
}
function showPurchaseSuccess(purchase) {
const purchaseContent = document.getElementById('purchaseContent');
const successMessage = `
<p>${purchase.message || ARTISTS_I18N.paymentSuccess}</p>
<p>${ARTISTS_I18N.purchaseOwnership.replace(':title', purchase.track_title).replace(':artist', purchase.artist_name)}</p>
<p style="color: #a0aec0; font-size: 1.2rem;">${ARTISTS_I18N.purchaseAvailableNote}</p>
`;
purchaseContent.innerHTML = `
<div class="purchase-success">
<i class="fas fa-check-circle"></i>
<h3>${ARTISTS_I18N.purchaseSuccessHeading}</h3>
${successMessage}
<div class="purchase-actions">
<button class="purchase-btn purchase-cancel" onclick="closePurchaseModal()">${ARTISTS_I18N.purchaseClose}</button>
<button class="purchase-btn purchase-confirm" onclick="viewMyPurchases()">
<i class="fas fa-music"></i>
${ARTISTS_I18N.viewPurchases}
</button>
</div>
</div>
`;
}
function closePurchaseModal() {
const purchaseModal = document.getElementById('purchaseModal');
purchaseModal.classList.remove('active');
}
function viewMyPurchases() {
// Navigate to user's purchases page
window.location.href = '/account_settings.php?tab=purchases';
}
// Setup purchase modal
function setupPurchaseModal() {
const purchaseModal = document.getElementById('purchaseModal');
const purchaseModalClose = document.getElementById('purchaseModalClose');
console.log('🎯 Setting up purchase modal:', purchaseModal ? 'found' : 'not found');
console.log('🎯 Purchase modal close button:', purchaseModalClose ? 'found' : 'not found');
// Close modal when clicking close button
if (purchaseModalClose) {
purchaseModalClose.addEventListener('click', closePurchaseModal);
}
// Close modal when clicking overlay
if (purchaseModal) {
purchaseModal.addEventListener('click', function(e) {
if (e.target === purchaseModal) {
closePurchaseModal();
}
});
}
// Check if user returned from login to complete a purchase
checkForPendingPurchase();
}
// Check for pending purchase after login
function checkForPendingPurchase() {
const urlParams = new URLSearchParams(window.location.search);
const pendingTrackId = urlParams.get('purchase_track');
if (pendingTrackId) {
console.log('🎯 Found pending purchase for track:', pendingTrackId);
// Clear the URL parameter
const newUrl = window.location.pathname;
window.history.replaceState({}, document.title, newUrl);
// Show purchase modal for the pending track with longer delay
setTimeout(() => {
console.log('🎯 Opening purchase modal for track:', pendingTrackId);
showPurchaseConfirmation(pendingTrackId);
}, 1000);
}
}
// Toggle follow function
function toggleFollow(userId, button) {
if (!<?= isset($_SESSION['user_id']) ? 'true' : 'false' ?>) {
alert(ARTISTS_I18N.loginRequiredFollow);
return;
}
fetch('/api_social.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'follow', user_id: userId })
})
.then(response => response.json())
.then(data => {
if (data.success) {
const wasFollowing = button.classList.contains('following');
button.classList.toggle('following');
const span = button.querySelector('span');
const icon = button.querySelector('i');
const followLabel = button.dataset.followLabel || ARTISTS_I18N.followLabel;
const followingLabel = button.dataset.followingLabel || ARTISTS_I18N.followingLabel;
if (span) {
span.textContent = button.classList.contains('following') ? followingLabel : followLabel;
}
if (icon) {
icon.className = button.classList.contains('following') ? 'fas fa-heart' : 'fas fa-user-plus';
}
// Only show notification when FOLLOWING (not unfollowing)
if (typeof window.showNotification === 'function' && !wasFollowing) {
window.showNotification(ARTISTS_I18N.followSuccess, 'success');
}
// Create heart burst effect when following
if (!wasFollowing) {
createHeartBurst(button);
}
} else {
console.error('Follow action failed:', data.error);
if (typeof window.showNotification === 'function') {
window.showNotification(ARTISTS_I18N.followError, 'error');
}
}
})
.catch(error => {
console.error('Follow request failed:', error);
if (typeof window.showNotification === 'function') {
window.showNotification(ARTISTS_I18N.networkError, 'error');
}
});
}
// Create heart burst effect
function createHeartBurst(button) {
const buttonRect = button.getBoundingClientRect();
const buttonCenterX = buttonRect.left + buttonRect.width / 2;
const buttonCenterY = buttonRect.top + buttonRect.height / 2;
// Create multiple hearts with different delays and positions
for (let i = 0; i < 8; i++) {
setTimeout(() => {
const heart = document.createElement('div');
heart.className = 'floating-heart';
heart.innerHTML = '💖';
heart.style.cssText = `
position: fixed;
left: ${buttonCenterX + (Math.random() - 0.5) * 40}px;
top: ${buttonCenterY + (Math.random() - 0.5) * 20}px;
font-size: ${20 + Math.random() * 15}px;
z-index: 9999;
pointer-events: none;
animation: float-heart 2s ease-out forwards;
`;
document.body.appendChild(heart);
// Remove heart after animation
setTimeout(() => {
if (heart.parentNode) {
heart.parentNode.removeChild(heart);
}
}, 2000);
}, i * 100);
}
// Add sparkles
for (let i = 0; i < 5; i++) {
setTimeout(() => {
const sparkle = document.createElement('div');
sparkle.className = 'sparkle';
sparkle.innerHTML = '✨';
sparkle.style.cssText = `
position: fixed;
left: ${buttonCenterX + (Math.random() - 0.5) * 60}px;
top: ${buttonCenterY + (Math.random() - 0.5) * 30}px;
font-size: ${15 + Math.random() * 10}px;
z-index: 9999;
pointer-events: none;
animation: sparkle-twinkle 1.5s ease-out forwards;
`;
document.body.appendChild(sparkle);
// Remove sparkle after animation
setTimeout(() => {
if (sparkle.parentNode) {
sparkle.parentNode.removeChild(sparkle);
}
}, 1500);
}, i * 150);
}
}
// Add track to cart (general function)
function addTrackToCart(trackId, title, artist, price, buttonElement) {
console.log('🎯 Adding track to cart:', { trackId, title, artist, price });
// Show loading state
const button = buttonElement || event.target;
const originalText = button.innerHTML;
button.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Adding...';
button.disabled = true;
// Use FormData to match cart.php expectations
const formData = new FormData();
formData.append('track_id', trackId);
formData.append('action', 'add');
formData.append('artist_plan', 'free'); // Default to free plan
fetch('/cart.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Show success notification
if (typeof window.showNotification === 'function') {
window.showNotification(ARTISTS_I18N.cartNotification, 'success');
} else {
alert(ARTISTS_I18N.cartAlert);
}
// Update cart count in header if available
if (typeof updateCartCount === 'function') {
updateCartCount();
} else {
// Fallback: reload page to update cart count
setTimeout(() => {
window.location.reload();
}, 1500);
}
} else {
throw new Error(data.error || ARTISTS_I18N.cartErrorGeneric);
}
})
.catch(error => {
console.error('🎯 Error adding track to cart:', error);
// Show error notification
if (typeof window.showNotification === 'function') {
window.showNotification(ARTISTS_I18N.cartError.replace(':message', error.message), 'error');
} else {
alert(ARTISTS_I18N.cartError.replace(':message', error.message));
}
})
.finally(() => {
// Reset button state
button.innerHTML = originalText;
button.disabled = false;
});
}
// Add track to cart from purchase modal
function addTrackToCartFromModal(trackId, title, artist, price) {
console.log('🎯 Adding track to cart:', { trackId, title, artist, price });
// Show loading state
const button = event.target;
const originalText = button.innerHTML;
button.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Adding...';
button.disabled = true;
// Use FormData to match cart.php expectations
const formData = new FormData();
formData.append('track_id', trackId);
formData.append('action', 'add');
formData.append('artist_plan', 'free'); // Default to free plan
fetch('/cart.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Close the purchase modal
closePurchaseModal();
// Show success notification
if (typeof window.showNotification === 'function') {
window.showNotification(ARTISTS_I18N.cartNotification, 'success');
} else {
alert(ARTISTS_I18N.cartAlert);
}
// Optionally redirect to checkout after a short delay
setTimeout(() => {
if (confirm(ARTISTS_I18N.cartCheckoutPrompt)) {
window.location.href = '/checkout.php';
}
}, 1000);
} else {
throw new Error(data.error || ARTISTS_I18N.cartErrorGeneric);
}
})
.catch(error => {
console.error('🎯 Error adding track to cart:', error);
// Reset button state
button.innerHTML = originalText;
button.disabled = false;
// Show error notification
if (typeof window.showNotification === 'function') {
window.showNotification(ARTISTS_I18N.cartError.replace(':message', error.message), 'error');
} else {
alert(ARTISTS_I18N.cartError.replace(':message', error.message));
}
});
}
// Force cache invalidation on page load
if (window.location.pathname === '/artists.php' && !window.location.search.includes('_v=')) {
// Add version parameter to force fresh load
const version = '<?= filemtime(__FILE__) ?>'; // File modification time as version
const url = new URL(window.location);
url.searchParams.set('_v', version);
// Only redirect if we don't already have the version param (prevent infinite loop)
if (!url.searchParams.has('_v') || url.searchParams.get('_v') !== version) {
window.history.replaceState({}, '', url.toString());
}
}
</script>
<script>
// Artist Preview Tooltip
(function() {
let tooltip = null;
let hideTimeout = null;
let showTimeout = null;
let currentTrigger = null;
let attachedTriggers = new WeakSet();
function initArtistPreview() {
tooltip = document.getElementById('artistPreviewTooltip');
if (!tooltip) {
setTimeout(initArtistPreview, 100);
return;
}
attachListeners();
// Keep tooltip visible when hovering over it
tooltip.addEventListener('mouseenter', function() {
clearAllTimeouts();
});
tooltip.addEventListener('mouseleave', function() {
clearAllTimeouts();
hideTimeout = setTimeout(hidePreview, 100);
});
// Handle click on view profile link - set up once
setupProfileLink();
}
function attachListeners() {
const triggers = document.querySelectorAll('.artist-preview-trigger');
triggers.forEach(trigger => {
if (attachedTriggers.has(trigger)) return;
trigger.addEventListener('mouseenter', function(e) {
e.stopPropagation();
clearAllTimeouts();
currentTrigger = this;
showTimeout = setTimeout(() => showPreview(this), 200);
});
trigger.addEventListener('mouseleave', function(e) {
e.stopPropagation();
if (this === currentTrigger) {
clearAllTimeouts();
hideTimeout = setTimeout(hidePreview, 150);
}
});
attachedTriggers.add(trigger);
});
}
function clearAllTimeouts() {
if (hideTimeout) {
clearTimeout(hideTimeout);
hideTimeout = null;
}
if (showTimeout) {
clearTimeout(showTimeout);
showTimeout = null;
}
}
function showPreview(trigger) {
if (!tooltip || !trigger) return;
clearAllTimeouts();
const artistId = trigger.getAttribute('data-artist-id');
const artistName = trigger.getAttribute('data-artist-name');
const artistImage = trigger.getAttribute('data-artist-image');
const artistCover = trigger.getAttribute('data-artist-cover');
const artistTracks = trigger.getAttribute('data-artist-tracks') || '0';
const artistPlays = trigger.getAttribute('data-artist-plays') || '0';
const artistFollowers = trigger.getAttribute('data-artist-followers') || '0';
const artistFollowing = trigger.getAttribute('data-artist-following') || '0';
const artistFriends = trigger.getAttribute('data-artist-friends') || '0';
const artistEvents = trigger.getAttribute('data-artist-events') || '0';
if (!artistId || !artistName) return;
// Set preview content
const nameEl = document.getElementById('artistPreviewName');
const linkEl = document.getElementById('artistPreviewLink');
if (nameEl) nameEl.textContent = artistName;
if (linkEl) {
const profileUrl = `/artist_profile.php?id=${artistId}`;
linkEl.href = profileUrl;
linkEl.setAttribute('href', profileUrl);
linkEl.style.pointerEvents = 'auto';
linkEl.style.cursor = 'pointer';
}
// Set stats
const statTracks = document.getElementById('statTracks');
const statPlays = document.getElementById('statPlays');
const statFollowers = document.getElementById('statFollowers');
const statFollowing = document.getElementById('statFollowing');
const statFriends = document.getElementById('statFriends');
const statEvents = document.getElementById('statEvents');
if (statTracks) statTracks.textContent = formatNumber(parseInt(artistTracks));
if (statPlays) statPlays.textContent = formatNumber(parseInt(artistPlays));
if (statFollowers) statFollowers.textContent = formatNumber(parseInt(artistFollowers));
if (statFollowing) statFollowing.textContent = formatNumber(parseInt(artistFollowing));
if (statFriends) statFriends.textContent = formatNumber(parseInt(artistFriends));
if (statEvents) statEvents.textContent = formatNumber(parseInt(artistEvents));
// Set cover image with cache-busting
const coverEl = document.getElementById('artistPreviewCover');
if (coverEl) {
const existingPlaceholder = coverEl.querySelector('.artist-preview-cover-placeholder');
if (existingPlaceholder) {
existingPlaceholder.remove();
}
if (artistCover && artistCover !== '' && artistCover !== 'null' && artistCover !== 'NULL') {
let coverUrl = artistCover.trim();
if (!coverUrl.startsWith('http') && !coverUrl.startsWith('//')) {
if (!coverUrl.startsWith('/')) {
coverUrl = '/' + coverUrl;
}
}
const separator = coverUrl.includes('?') ? '&' : '?';
coverUrl = coverUrl + separator + 'v=' + Date.now();
const testImg = new Image();
testImg.onload = function() {
if (coverEl) {
coverEl.style.backgroundImage = `url(${coverUrl})`;
coverEl.style.backgroundSize = 'cover';
coverEl.style.backgroundPosition = 'center';
}
};
testImg.onerror = function() {
if (coverEl) {
coverEl.style.backgroundImage = '';
createCoverPlaceholder(coverEl);
}
};
testImg.src = coverUrl;
} else {
coverEl.style.backgroundImage = '';
createCoverPlaceholder(coverEl);
}
}
function createCoverPlaceholder(container) {
const placeholder = document.createElement('div');
placeholder.className = 'artist-preview-cover-placeholder';
placeholder.innerHTML = `
<div class="artist-preview-cover-pattern">
<div class="artist-preview-waveform-pattern"></div>
<div class="artist-preview-music-notes">
<i class="fas fa-music"></i>
<i class="fas fa-headphones"></i>
<i class="fas fa-sliders-h"></i>
</div>
</div>
<div class="artist-preview-cover-gradient-overlay"></div>
`;
container.appendChild(placeholder);
}
// Set avatar image with cache-busting
const avatarEl = document.getElementById('artistPreviewAvatar');
const defaultAvatar = document.getElementById('artistPreviewAvatarDefault');
if (avatarEl) {
const currentArtistId = artistId;
const existingImg = avatarEl.querySelector('img');
if (existingImg) {
existingImg.remove();
}
const existingPlaceholder = avatarEl.querySelector('.artist-preview-avatar-placeholder');
if (existingPlaceholder) {
existingPlaceholder.remove();
}
if (defaultAvatar) {
defaultAvatar.style.display = 'none';
}
if (artistImage && artistImage !== '' && artistImage !== 'null' && artistImage !== 'NULL') {
let imageUrl = artistImage.trim();
if (!imageUrl.startsWith('http') && !imageUrl.startsWith('//')) {
if (!imageUrl.startsWith('/')) {
imageUrl = '/' + imageUrl;
}
}
const separator = imageUrl.includes('?') ? '&' : '?';
imageUrl = imageUrl + separator + 'v=' + Date.now() + '&artist=' + currentArtistId;
const img = document.createElement('img');
img.alt = artistName;
img.setAttribute('data-artist-id', currentArtistId);
img.style.width = '100%';
img.style.height = '100%';
img.style.objectFit = 'cover';
img.style.borderRadius = '50%';
img.onload = (function(capturedArtistId) {
return function() {
if (avatarEl && avatarEl.getAttribute('data-current-artist') === capturedArtistId) {
const existingImg = avatarEl.querySelector('img');
if (existingImg && existingImg !== this) {
existingImg.remove();
}
const existingPlaceholder = avatarEl.querySelector('.artist-preview-avatar-placeholder');
if (existingPlaceholder) {
existingPlaceholder.remove();
}
if (defaultAvatar) {
defaultAvatar.style.display = 'none';
}
avatarEl.style.background = 'linear-gradient(135deg, #667eea, #764ba2)';
if (!avatarEl.contains(this)) {
avatarEl.appendChild(this);
}
}
};
})(currentArtistId);
img.onerror = (function(capturedArtistId) {
return function() {
if (avatarEl && avatarEl.getAttribute('data-current-artist') === capturedArtistId) {
this.remove();
if (defaultAvatar) {
defaultAvatar.style.display = 'none';
}
createAvatarPlaceholder(avatarEl);
}
};
})(currentArtistId);
avatarEl.setAttribute('data-current-artist', currentArtistId);
avatarEl.appendChild(img);
img.src = imageUrl;
} else {
avatarEl.setAttribute('data-current-artist', currentArtistId);
if (defaultAvatar) {
defaultAvatar.style.display = 'none';
}
createAvatarPlaceholder(avatarEl);
}
}
function createAvatarPlaceholder(container) {
const existingPlaceholder = container.querySelector('.artist-preview-avatar-placeholder');
if (existingPlaceholder) {
return;
}
container.style.background = 'transparent';
const placeholder = document.createElement('div');
placeholder.className = 'artist-preview-avatar-placeholder';
placeholder.innerHTML = `
<div class="artist-preview-profile-pattern">
<i class="fas fa-music"></i>
</div>
<div class="artist-preview-profile-gradient-overlay"></div>
`;
container.appendChild(placeholder);
}
// Position tooltip
const rect = trigger.getBoundingClientRect();
const tooltipWidth = 360;
const spacing = 10;
tooltip.style.display = 'block';
tooltip.style.visibility = 'hidden';
const tooltipHeight = tooltip.offsetHeight || 200;
tooltip.style.visibility = '';
let left = rect.left + (rect.width / 2) - (tooltipWidth / 2);
let top = rect.bottom + spacing;
if (left < 10) left = 10;
if (left + tooltipWidth > window.innerWidth - 10) {
left = window.innerWidth - tooltipWidth - 10;
}
if (top + tooltipHeight > window.innerHeight - 10) {
top = rect.top - tooltipHeight - spacing;
if (top < 10) top = 10;
}
tooltip.style.left = left + 'px';
tooltip.style.top = top + 'px';
tooltip.classList.add('visible');
}
function hidePreview() {
if (tooltip) {
tooltip.classList.remove('visible');
}
currentTrigger = null;
}
function formatNumber(num) {
if (num >= 1000000) {
return (num / 1000000).toFixed(1) + 'M';
} else if (num >= 1000) {
return (num / 1000).toFixed(1) + 'K';
}
return num.toString();
}
function setupProfileLink() {
const profileLink = document.getElementById('artistPreviewLink');
if (profileLink) {
const newLink = profileLink.cloneNode(true);
profileLink.parentNode.replaceChild(newLink, profileLink);
newLink.addEventListener('click', function(e) {
e.preventDefault();
const href = this.getAttribute('href');
if (href && href !== '#') {
window.location.href = href;
}
});
}
}
// Initialize on DOM ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initArtistPreview);
} else {
initArtistPreview();
}
// Re-initialize when new content is loaded (for AJAX)
setTimeout(initArtistPreview, 100);
})();
</script>
<!-- Artist Preview Tooltip -->
<div id="artistPreviewTooltip" class="artist-preview-tooltip">
<div class="artist-preview-cover" id="artistPreviewCover"></div>
<div class="artist-preview-content">
<div class="artist-preview-avatar" id="artistPreviewAvatar">
<div class="default-avatar" id="artistPreviewAvatarDefault"></div>
</div>
<div class="artist-preview-info">
<div class="artist-preview-name" id="artistPreviewName"></div>
<div class="artist-preview-stats" id="artistPreviewStats">
<div class="preview-stat-item">
<i class="fas fa-music"></i>
<span class="stat-value" id="statTracks">0</span>
<span class="stat-label" data-translate="preview.tracks"><?= t('preview.tracks') ?? 'Tracks' ?></span>
</div>
<div class="preview-stat-item">
<i class="fas fa-headphones"></i>
<span class="stat-value" id="statPlays">0</span>
<span class="stat-label" data-translate="preview.plays"><?= t('preview.plays') ?? 'Plays' ?></span>
</div>
<div class="preview-stat-item">
<i class="fas fa-users"></i>
<span class="stat-value" id="statFollowers">0</span>
<span class="stat-label" data-translate="preview.followers"><?= t('preview.followers') ?? 'Followers' ?></span>
</div>
<div class="preview-stat-item">
<i class="fas fa-user-plus"></i>
<span class="stat-value" id="statFollowing">0</span>
<span class="stat-label" data-translate="preview.following"><?= t('preview.following') ?? 'Following' ?></span>
</div>
<div class="preview-stat-item">
<i class="fas fa-user-friends"></i>
<span class="stat-value" id="statFriends">0</span>
<span class="stat-label" data-translate="preview.friends"><?= t('preview.friends') ?? 'Friends' ?></span>
</div>
<div class="preview-stat-item">
<i class="fas fa-calendar-alt"></i>
<span class="stat-value" id="statEvents">0</span>
<span class="stat-label" data-translate="preview.events"><?= t('preview.events') ?? 'Events' ?></span>
</div>
</div>
<a href="#" class="artist-preview-link" id="artistPreviewLink">
<i class="fas fa-user"></i> <span data-translate="preview.view_profile"><?= t('preview.view_profile') ?? 'View Profile' ?></span>
</a>
</div>
</div>
</div>
<?php
if ($is_ajax) {
echo '</div> <!-- Close pageContainer -->';
}
// Include footer only for full page loads
// Include footer (AJAX navigation removed - always include footer)
include 'includes/footer.php';
?>