Initialer Laravel Commit für BetiX
This commit is contained in:
520
resources/js/pages/VipLevels.vue
Normal file
520
resources/js/pages/VipLevels.vue
Normal file
@@ -0,0 +1,520 @@
|
||||
<script setup lang="ts">
|
||||
import { Head, usePage, router } from '@inertiajs/vue3';
|
||||
import { computed, onMounted, nextTick, ref, watch } from 'vue';
|
||||
import { ChevronLeft, ChevronRight, Lock, Check, Star, Gift } from 'lucide-vue-next';
|
||||
import UserLayout from '../layouts/user/userlayout.vue';
|
||||
import { useNotifications } from '@/composables/useNotifications';
|
||||
|
||||
const page = usePage();
|
||||
const { notify } = useNotifications();
|
||||
const user = computed(() => page.props.auth.user);
|
||||
const stats = computed(() => user.value?.stats || { vip_points: 0, vip_level: 0 });
|
||||
|
||||
// Props from Controller
|
||||
const props = defineProps<{
|
||||
claimedLevels: number[];
|
||||
cashRewards: Record<number, number>;
|
||||
}>();
|
||||
|
||||
// Level Definitions
|
||||
const levels = [
|
||||
{
|
||||
level: 0,
|
||||
name: 'Newbie',
|
||||
xp: 0,
|
||||
color: '#888888',
|
||||
gradient: 'linear-gradient(135deg, #2a2a2a, #444)',
|
||||
shadow: 'rgba(100,100,100,0.5)',
|
||||
icon: 'egg',
|
||||
rewards: ['Access to Global Chat', 'Daily Free Spin']
|
||||
},
|
||||
{
|
||||
level: 1,
|
||||
name: 'Bronze',
|
||||
xp: 1000,
|
||||
color: '#cd7f32',
|
||||
gradient: 'linear-gradient(135deg, #5c3618, #cd7f32)',
|
||||
shadow: 'rgba(205,127,50,0.6)',
|
||||
icon: 'shield',
|
||||
rewards: ['10 Free Spins', '5% Rakeback', 'Bronze Badge', 'Weekly Reload']
|
||||
},
|
||||
{
|
||||
level: 2,
|
||||
name: 'Silver',
|
||||
xp: 5000,
|
||||
color: '#c0c0c0',
|
||||
gradient: 'linear-gradient(135deg, #555, #c0c0c0)',
|
||||
shadow: 'rgba(192,192,192,0.6)',
|
||||
icon: 'sword',
|
||||
rewards: ['25 Free Spins', '7% Rakeback', 'Silver Badge', 'Priority Support', 'Monthly Bonus']
|
||||
},
|
||||
{
|
||||
level: 3,
|
||||
name: 'Gold',
|
||||
xp: 20000,
|
||||
color: '#ffd700',
|
||||
gradient: 'linear-gradient(135deg, #8a6e06, #ffd700)',
|
||||
shadow: 'rgba(255,215,0,0.6)',
|
||||
icon: 'crown',
|
||||
rewards: ['50€ Cash Bonus', '10% Rakeback', 'Gold Badge', 'Instant Withdrawals', 'Birthday Gift']
|
||||
},
|
||||
{
|
||||
level: 4,
|
||||
name: 'Platinum',
|
||||
xp: 50000,
|
||||
color: '#00f2ff',
|
||||
gradient: 'linear-gradient(135deg, #005f6b, #00f2ff)',
|
||||
shadow: 'rgba(0,242,255,0.6)',
|
||||
icon: 'gem',
|
||||
rewards: ['200€ Cash Bonus', '12% Rakeback', 'Personal VIP Host', 'Exclusive Events', 'Higher Limits']
|
||||
},
|
||||
{
|
||||
level: 5,
|
||||
name: 'Diamond',
|
||||
xp: 150000,
|
||||
color: '#ff007a',
|
||||
gradient: 'linear-gradient(135deg, #75003a, #ff007a)',
|
||||
shadow: 'rgba(255,0,122,0.6)',
|
||||
icon: 'diamond',
|
||||
rewards: ['1000€ Cash Bonus', '15% Rakeback', 'Luxury Gifts', 'Concierge Service', 'Offline Events']
|
||||
},
|
||||
{
|
||||
level: 6,
|
||||
name: 'Obsidian',
|
||||
xp: 500000,
|
||||
color: '#ff3e3e',
|
||||
gradient: 'linear-gradient(135deg, #520000, #ff3e3e)',
|
||||
shadow: 'rgba(255,62,62,0.6)',
|
||||
icon: 'flame',
|
||||
rewards: ['Custom Supercar', '20% Rakeback', 'Share of Casino Profits', 'The "God" Badge', 'Private Jet Transfer']
|
||||
}
|
||||
];
|
||||
|
||||
// User Progress Logic
|
||||
const currentXP = computed(() => parseFloat(stats.value.vip_points || 0));
|
||||
|
||||
// Explicitly check user.vip_level first, then stats.vip_level
|
||||
const currentLevelIdx = computed(() => {
|
||||
// 1. Check direct user property (from users table)
|
||||
if (user.value?.vip_level !== undefined && user.value?.vip_level !== null) {
|
||||
const lvl = parseInt(user.value.vip_level);
|
||||
if (!isNaN(lvl) && lvl >= 0) {
|
||||
return Math.min(lvl, levels.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Check stats relation
|
||||
if (stats.value.vip_level !== undefined && stats.value.vip_level !== null) {
|
||||
const lvl = parseInt(stats.value.vip_level);
|
||||
if (!isNaN(lvl) && lvl >= 0) {
|
||||
return Math.min(lvl, levels.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Fallback: Calculate from XP
|
||||
let idx = 0;
|
||||
for (let i = 0; i < levels.length; i++) {
|
||||
if (currentXP.value >= levels[i].xp) idx = i;
|
||||
else break;
|
||||
}
|
||||
return idx;
|
||||
});
|
||||
|
||||
const nextLevel = computed(() => levels[currentLevelIdx.value + 1] || null);
|
||||
const progressPercent = computed(() => {
|
||||
if (!nextLevel.value) return 100;
|
||||
const currentLvlXP = levels[currentLevelIdx.value].xp;
|
||||
const nextLvlXP = nextLevel.value.xp;
|
||||
|
||||
// If user has level but not enough XP (manual set), show 0% or calculate relative
|
||||
if (currentXP.value < currentLvlXP) return 0;
|
||||
|
||||
const p = ((currentXP.value - currentLvlXP) / (nextLvlXP - currentLvlXP)) * 100;
|
||||
return Math.min(Math.max(p, 0), 100);
|
||||
});
|
||||
|
||||
// Slider Logic
|
||||
const activeIndex = ref(0);
|
||||
|
||||
// Watch for level changes to update slider initially
|
||||
watch(currentLevelIdx, (newVal) => {
|
||||
activeIndex.value = newVal;
|
||||
}, { immediate: true });
|
||||
|
||||
const nextSlide = () => {
|
||||
if (activeIndex.value < levels.length - 1) activeIndex.value++;
|
||||
};
|
||||
const prevSlide = () => {
|
||||
if (activeIndex.value > 0) activeIndex.value--;
|
||||
};
|
||||
const setSlide = (index: number) => {
|
||||
activeIndex.value = index;
|
||||
};
|
||||
|
||||
// Claim Logic
|
||||
const claiming = ref(false);
|
||||
async function claimReward(level: number) {
|
||||
if (claiming.value) return;
|
||||
claiming.value = true;
|
||||
try {
|
||||
await router.post('/vip-levels/claim', { level }, {
|
||||
onSuccess: () => {
|
||||
notify({ type: 'green', title: 'REWARD CLAIMED', desc: `You received your level ${level} bonus!`, icon: 'gift' });
|
||||
},
|
||||
onError: (err) => {
|
||||
notify({ type: 'magenta', title: 'ERROR', desc: err.message || 'Failed to claim.', icon: 'alert-triangle' });
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
claiming.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to check if reward is claimable
|
||||
const isClaimable = (levelIdx: number) => {
|
||||
const lvl = levels[levelIdx];
|
||||
if (levelIdx === 0) return false; // No cash for newbie
|
||||
if (levelIdx > currentLevelIdx.value) return false; // Not reached yet
|
||||
if (props.claimedLevels.includes(lvl.level)) return false; // Already claimed
|
||||
return true;
|
||||
};
|
||||
|
||||
const isClaimed = (levelIdx: number) => {
|
||||
return props.claimedLevels.includes(levels[levelIdx].level);
|
||||
};
|
||||
|
||||
// Calculate styles for 3D effect
|
||||
const getCardStyle = (index: number) => {
|
||||
const offset = index - activeIndex.value;
|
||||
const absOffset = Math.abs(offset);
|
||||
const isActive = offset === 0;
|
||||
|
||||
// Config
|
||||
const xDist = 60; // % overlap
|
||||
const scale = 1 - (absOffset * 0.15);
|
||||
const opacity = 1 - (absOffset * 0.3);
|
||||
const zIndex = 100 - absOffset;
|
||||
const rotateY = offset * -25; // Rotation angle
|
||||
|
||||
return {
|
||||
transform: `translateX(${offset * xDist}%) scale(${scale}) perspective(1000px) rotateY(${rotateY}deg)`,
|
||||
opacity: Math.max(opacity, 0),
|
||||
zIndex: zIndex,
|
||||
pointerEvents: isActive ? 'auto' : 'none', // Only active card is interactive
|
||||
filter: isActive ? 'none' : 'brightness(0.5) blur(2px)'
|
||||
};
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => { if ((window as any).lucide) (window as any).lucide.createIcons(); });
|
||||
});
|
||||
|
||||
// Re-init icons when slide changes (for the details section)
|
||||
watch(activeIndex, () => {
|
||||
nextTick(() => { if ((window as any).lucide) (window as any).lucide.createIcons(); });
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UserLayout>
|
||||
<Head title="VIP Club" />
|
||||
<div class="vip-container">
|
||||
|
||||
<!-- Header / Progress -->
|
||||
<div class="vip-header">
|
||||
<div class="header-content">
|
||||
<div class="user-rank">
|
||||
<span class="label">{{ $t('vip.your_rank') }}</span>
|
||||
<h1 class="rank-title" :style="{ color: levels[currentLevelIdx].color }">
|
||||
{{ levels[currentLevelIdx].name }}
|
||||
</h1>
|
||||
</div>
|
||||
<div class="xp-bar-container">
|
||||
<div class="xp-info">
|
||||
<span>{{ currentXP.toLocaleString() }} XP</span>
|
||||
<span v-if="nextLevel">{{ nextLevel.xp.toLocaleString() }} XP</span>
|
||||
<span v-else>{{ $t('vip.max') }}</span>
|
||||
</div>
|
||||
<div class="xp-track">
|
||||
<div class="xp-fill" :style="{ width: `${progressPercent}%`, background: levels[currentLevelIdx].color, boxShadow: `0 0 15px ${levels[currentLevelIdx].color}` }"></div>
|
||||
</div>
|
||||
<div class="xp-next" v-if="nextLevel">
|
||||
{{ (nextLevel.xp - currentXP).toLocaleString() }} XP to {{ nextLevel.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3D Slider -->
|
||||
<div class="slider-section">
|
||||
<button class="nav-btn prev" @click="prevSlide" :disabled="activeIndex === 0">
|
||||
<ChevronLeft class="w-8 h-8" />
|
||||
</button>
|
||||
|
||||
<div class="cards-wrapper">
|
||||
<div
|
||||
v-for="(lvl, i) in levels"
|
||||
:key="lvl.level"
|
||||
class="vip-card"
|
||||
:class="{ 'is-active': i === activeIndex }"
|
||||
:style="getCardStyle(i)"
|
||||
@click="setSlide(i)"
|
||||
>
|
||||
<div class="card-inner" :style="{ background: lvl.gradient }">
|
||||
<div class="card-glow" :style="{ background: lvl.color }"></div>
|
||||
|
||||
<div class="card-top">
|
||||
<div class="level-badge">LVL {{ lvl.level }}</div>
|
||||
<i :data-lucide="lvl.icon" class="level-icon"></i>
|
||||
</div>
|
||||
|
||||
<div class="card-mid">
|
||||
<div class="card-name">{{ lvl.name }}</div>
|
||||
<div class="card-xp">{{ lvl.xp.toLocaleString() }} XP</div>
|
||||
</div>
|
||||
|
||||
<div class="card-status">
|
||||
<div v-if="i < currentLevelIdx" class="status passed">
|
||||
<Check class="w-4 h-4" /> {{ $t('vip.unlocked') }}
|
||||
</div>
|
||||
<div v-else-if="i === currentLevelIdx" class="status current">
|
||||
<div class="pulse"></div> {{ $t('vip.current') }}
|
||||
</div>
|
||||
<div v-else class="status locked">
|
||||
<Lock class="w-4 h-4" /> {{ $t('vip.locked') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="nav-btn next" @click="nextSlide" :disabled="activeIndex === levels.length - 1">
|
||||
<ChevronRight class="w-8 h-8" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Details Panel (Dynamic based on active slide) -->
|
||||
<transition name="fade-up" mode="out-in">
|
||||
<div :key="activeIndex" class="details-panel">
|
||||
<div class="details-head" :style="{ borderColor: levels[activeIndex].color }">
|
||||
<div class="head-left">
|
||||
<h2 :style="{ color: levels[activeIndex].color }">
|
||||
<i :data-lucide="levels[activeIndex].icon"></i>
|
||||
{{ levels[activeIndex].name }} {{ $t('vip.benefits') }}
|
||||
</h2>
|
||||
<div class="details-xp">{{ $t('vip.requires') }} {{ levels[activeIndex].xp.toLocaleString() }} XP</div>
|
||||
</div>
|
||||
|
||||
<!-- Claim Button -->
|
||||
<div class="head-right">
|
||||
<button
|
||||
v-if="isClaimable(activeIndex)"
|
||||
class="btn-claim"
|
||||
:style="{ background: levels[activeIndex].color, boxShadow: `0 0 20px ${levels[activeIndex].color}60` }"
|
||||
@click="claimReward(levels[activeIndex].level)"
|
||||
:disabled="claiming"
|
||||
>
|
||||
<Gift class="w-4 h-4 mr-2" />
|
||||
Claim {{ props.cashRewards[levels[activeIndex].level] }}€
|
||||
</button>
|
||||
<div v-else-if="isClaimed(activeIndex)" class="claimed-badge">
|
||||
<Check class="w-4 h-4 mr-1" /> {{ $t('vip.reward_claimed') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rewards-grid">
|
||||
<div v-for="(reward, r) in levels[activeIndex].rewards" :key="r" class="reward-item">
|
||||
<div class="reward-icon" :style="{ background: `${levels[activeIndex].color}20`, color: levels[activeIndex].color }">
|
||||
<Star class="w-5 h-5" />
|
||||
</div>
|
||||
<span class="reward-text">{{ reward }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
</div>
|
||||
</UserLayout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.vip-container {
|
||||
min-height: 100vh;
|
||||
padding: 40px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
overflow-x: hidden;
|
||||
background: radial-gradient(circle at top, #1a1a1a 0%, #020202 100%);
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.vip-header {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
margin-bottom: 60px;
|
||||
text-align: center;
|
||||
}
|
||||
.label { font-size: 12px; font-weight: 900; color: #666; letter-spacing: 3px; }
|
||||
.rank-title { font-size: 48px; font-weight: 900; text-transform: uppercase; margin: 5px 0 20px; text-shadow: 0 0 30px currentColor; }
|
||||
|
||||
.xp-bar-container { background: #0a0a0a; border: 1px solid #222; padding: 20px; border-radius: 20px; position: relative; }
|
||||
.xp-info { display: flex; justify-content: space-between; font-size: 12px; font-weight: 700; color: #ccc; margin-bottom: 8px; }
|
||||
.xp-track { height: 8px; background: #1a1a1a; border-radius: 10px; overflow: hidden; }
|
||||
.xp-fill { height: 100%; transition: width 1s cubic-bezier(0.2, 0, 0, 1); }
|
||||
.xp-next { margin-top: 8px; font-size: 11px; color: #666; text-align: right; }
|
||||
|
||||
/* Slider Section */
|
||||
.slider-section {
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
height: 450px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.cards-wrapper {
|
||||
position: relative;
|
||||
width: 320px; /* Card Width */
|
||||
height: 420px; /* Card Height */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
perspective: 1000px;
|
||||
}
|
||||
|
||||
.vip-card {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition: all 0.5s cubic-bezier(0.2, 0, 0, 1);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.card-inner {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 24px;
|
||||
padding: 30px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
box-shadow: 0 20px 50px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.card-glow {
|
||||
position: absolute;
|
||||
top: -50%; left: -50%; width: 200%; height: 200%;
|
||||
opacity: 0.15;
|
||||
filter: blur(60px);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.card-top { display: flex; justify-content: space-between; align-items: flex-start; position: relative; z-index: 2; }
|
||||
.level-badge { font-size: 12px; font-weight: 900; background: rgba(0,0,0,0.3); padding: 5px 12px; border-radius: 50px; border: 1px solid rgba(255,255,255,0.2); color: #fff; }
|
||||
.level-icon { width: 40px; height: 40px; color: #fff; filter: drop-shadow(0 0 10px rgba(255,255,255,0.5)); }
|
||||
|
||||
.card-mid { text-align: center; position: relative; z-index: 2; }
|
||||
.card-name { font-size: 36px; font-weight: 900; color: #fff; text-transform: uppercase; letter-spacing: 2px; text-shadow: 0 5px 15px rgba(0,0,0,0.3); }
|
||||
.card-xp { font-size: 14px; font-weight: 700; color: rgba(255,255,255,0.8); margin-top: 5px; }
|
||||
|
||||
.card-status { display: flex; justify-content: center; position: relative; z-index: 2; }
|
||||
.status { display: flex; align-items: center; gap: 8px; font-size: 12px; font-weight: 800; padding: 8px 16px; border-radius: 12px; text-transform: uppercase; }
|
||||
.status.passed { background: rgba(0,255,157,0.2); color: #00ff9d; border: 1px solid rgba(0,255,157,0.3); }
|
||||
.status.locked { background: rgba(0,0,0,0.3); color: #888; border: 1px solid rgba(255,255,255,0.1); }
|
||||
.status.current { background: rgba(255,255,255,0.2); color: #fff; border: 1px solid #fff; }
|
||||
|
||||
.pulse { width: 8px; height: 8px; background: #fff; border-radius: 50%; animation: pulse 1.5s infinite; }
|
||||
|
||||
/* Navigation Buttons */
|
||||
.nav-btn {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: rgba(255,255,255,0.05);
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
color: #fff;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
z-index: 20;
|
||||
}
|
||||
.nav-btn:hover:not(:disabled) { background: #fff; color: #000; box-shadow: 0 0 20px rgba(255,255,255,0.3); }
|
||||
.nav-btn:disabled { opacity: 0.3; cursor: not-allowed; }
|
||||
.nav-btn.prev { left: 0; }
|
||||
.nav-btn.next { right: 0; }
|
||||
|
||||
/* Details Panel */
|
||||
.details-panel {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
background: #0a0a0a;
|
||||
border: 1px solid #222;
|
||||
border-radius: 24px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.5);
|
||||
}
|
||||
.details-head { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #222; padding-bottom: 20px; margin-bottom: 20px; border-left: 4px solid transparent; padding-left: 20px; transition: 0.3s; }
|
||||
.details-head h2 { font-size: 24px; font-weight: 900; margin: 0; display: flex; align-items: center; gap: 12px; text-transform: uppercase; }
|
||||
.details-xp { font-size: 12px; font-weight: 700; color: #666; text-transform: uppercase; letter-spacing: 1px; }
|
||||
|
||||
.btn-claim {
|
||||
display: flex; align-items: center;
|
||||
padding: 10px 20px;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
color: #000;
|
||||
font-weight: 900;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
transition: 0.3s;
|
||||
font-size: 12px;
|
||||
}
|
||||
.btn-claim:hover:not(:disabled) { transform: translateY(-2px); filter: brightness(1.1); }
|
||||
.btn-claim:disabled { opacity: 0.6; cursor: not-allowed; }
|
||||
|
||||
.claimed-badge {
|
||||
display: flex; align-items: center;
|
||||
color: #00ff9d;
|
||||
font-weight: 800;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
background: rgba(0,255,157,0.1);
|
||||
padding: 8px 16px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(0,255,157,0.2);
|
||||
}
|
||||
|
||||
.rewards-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 15px; }
|
||||
.reward-item { background: #111; padding: 15px; border-radius: 12px; display: flex; align-items: center; gap: 15px; border: 1px solid #1a1a1a; transition: 0.2s; }
|
||||
.reward-item:hover { border-color: #333; transform: translateY(-2px); }
|
||||
.reward-icon { width: 36px; height: 36px; border-radius: 10px; display: flex; align-items: center; justify-content: center; }
|
||||
.reward-text { font-size: 13px; font-weight: 600; color: #ddd; }
|
||||
|
||||
/* Animations */
|
||||
@keyframes pulse { 0% { opacity: 1; transform: scale(1); } 50% { opacity: 0.5; transform: scale(1.2); } 100% { opacity: 1; transform: scale(1); } }
|
||||
|
||||
.fade-up-enter-active, .fade-up-leave-active { transition: all 0.3s ease; }
|
||||
.fade-up-enter-from { opacity: 0; transform: translateY(20px); }
|
||||
.fade-up-leave-to { opacity: 0; transform: translateY(-20px); }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.slider-section { height: 400px; }
|
||||
.cards-wrapper { width: 260px; height: 360px; }
|
||||
.nav-btn { width: 40px; height: 40px; }
|
||||
.rank-title { font-size: 32px; }
|
||||
.details-head { flex-direction: column; align-items: flex-start; gap: 10px; }
|
||||
.head-right { width: 100%; display: flex; justify-content: flex-start; }
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user