This commit is contained in:
karyamanswasta
2025-08-17 14:41:48 +07:00
parent 0dc6d64e07
commit 9e23588ec6
32 changed files with 5933 additions and 299 deletions

View File

@@ -0,0 +1,575 @@
.container {
position: relative;
width: 100%;
height: 450px;
overflow: hidden;
touch-action: none;
margin: 40px 0 60px 0;
perspective: 1000px;
cursor: grab;
user-select: none;
}
.container.dragging {
cursor: grabbing;
}
.carouselWrapper {
position: relative;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
transform-style: preserve-3d;
perspective: 1500px;
}
/* Entering edge helpers to ensure ease-out even with <5 products */
/* entering helpers removed (reverted) */
/* Edge fade-out on shift for better intuition */
.carouselWrapper.shiftingLeft .cardContainer.positionNeg2 {
opacity: 0;
transform: translateX(-420px) rotateY(62deg) scale(0.66);
}
.carouselWrapper.shiftingRight .cardContainer.position2 {
opacity: 0;
transform: translateX(420px) rotateY(-62deg) scale(0.66);
}
.cardContainer {
position: absolute;
width: 320px;
height: 370px;
cursor: pointer;
transform-style: preserve-3d;
will-change: transform;
backface-visibility: hidden;
transform-origin: center center;
transition: transform 0.8s cubic-bezier(0.22, 1, 0.36, 1),
opacity 0.8s cubic-bezier(0.22, 1, 0.36, 1);
transform: translateX(0) rotateY(0) scale(1);
opacity: 1;
z-index: 1;
box-sizing: border-box;
aspect-ratio: 320/370;
}
.cardWrapper {
width: 100%;
height: 100%;
padding: 0 15px;
transform-style: preserve-3d;
backface-visibility: hidden;
box-sizing: border-box;
position: relative;
z-index: 2; /* ensure content stacks above shadow */
}
/* Ground shadow to enhance 3D standing effect */
.cardShadow {
position: absolute;
left: 50%;
bottom: 6px;
width: 92%;
height: 32px;
transform: translateX(-50%) scale(1);
background: radial-gradient(ellipse at center, rgba(0,0,0,0.55) 0%, rgba(0,0,0,0.32) 45%, rgba(0,0,0,0) 78%);
filter: blur(8px);
opacity: 0.45;
pointer-events: none;
z-index: 1;
}
.cardContainer.position0 .cardShadow {
opacity: 0.8;
transform: translateX(-50%) scale(1.15);
}
.cardContainer.positionNeg1 .cardShadow,
.cardContainer.position1 .cardShadow {
opacity: 0.55;
transform: translateX(-50%) scale(1.02);
}
.cardContainer.positionNeg2 .cardShadow,
.cardContainer.position2 .cardShadow {
opacity: 0.42;
transform: translateX(-50%) scale(0.96);
}
/* Initial state: subtle pop-in */
.cardContainer.initial {
transform: translateX(0) translateY(20px) rotateY(0) scale(0.92);
opacity: 0;
z-index: 1;
}
/* New load spread: compact fan-out from center */
.cardContainer.spread.positionNeg2 {
transform: translateX(-320px) translateZ(-120px) rotateY(10deg) scale(0.9);
opacity: 0.8;
}
.cardContainer.spread.positionNeg1 {
transform: translateX(-160px) translateZ(-60px) rotateY(6deg) scale(0.97);
opacity: 0.9;
}
.cardContainer.spread.position0 {
transform: translateX(0) translateZ(0) rotateY(0) scale(1.06);
opacity: 1;
}
.cardContainer.spread.position1 {
transform: translateX(160px) translateZ(-60px) rotateY(-6deg) scale(0.97);
opacity: 0.9;
}
.cardContainer.spread.position2 {
transform: translateX(320px) translateZ(-120px) rotateY(-10deg) scale(0.9);
opacity: 0.8;
}
/* Position classes for coverflow effect (closer to center) */
.cardContainer.positionNeg2 {
transform: translateX(-380px) rotateY(55deg) scale(0.7);
opacity: 1;
z-index: 5;
}
.cardContainer.positionNeg1 {
transform: translateX(-190px) rotateY(35deg) scale(0.8);
opacity: 1;
z-index: 10;
}
.cardContainer.position0 {
transform: translateX(0) rotateY(0deg) scale(1);
opacity: 1;
z-index: 15;
}
.cardContainer.position1 {
transform: translateX(190px) rotateY(-35deg) scale(0.8);
opacity: 1;
z-index: 10;
}
.cardContainer.position2 {
transform: translateX(380px) rotateY(-55deg) scale(0.7);
opacity: 1;
z-index: 5;
}
/* Edge-enter overrides (placed after position rules to win cascade) */
.cardContainer.enterFromRight.position1,
.cardContainer.enterFromRight.position2 {
opacity: 0;
transform: translateX(520px) rotateY(-62deg) scale(0.66);
}
.cardContainer.enterFromLeft.positionNeg1,
.cardContainer.enterFromLeft.positionNeg2 {
opacity: 0;
transform: translateX(-520px) rotateY(62deg) scale(0.66);
}
.cardImage {
width: 100%;
height: 120px;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
transition: transform 0.4s ease;
position: relative;
background-color: #f1f5f9;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
object-fit: cover;
aspect-ratio: 4/3;
overflow: hidden;
}
.placeholderImage {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background-color: #f1f5f9;
object-fit: cover;
aspect-ratio: 4/3;
overflow: hidden;
}
.placeholderImage span {
font-size: 2rem;
opacity: 0.3;
}
.cardContent {
padding: 1rem;
flex: 1;
display: flex;
flex-direction: column;
}
.cardTitle {
font-size: 1.1rem;
font-weight: 700;
color: #1e293b;
margin-bottom: 0.5rem;
line-height: 1.3;
flex: 1;
}
.cardDescription {
color: #64748b;
line-height: 1.5;
margin-bottom: 1rem;
font-size: 0.85rem;
flex: 1;
}
.cardFooter {
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1px solid #e2e8f0;
padding-top: 1rem;
margin-top: auto;
}
.priceContainer {
flex: 1;
}
.price {
font-size: 1rem;
font-weight: 700;
}
.freePrice {
color: #059669;
}
.paidPrice {
color: #2563eb;
}
.enrollButton {
background: linear-gradient(135deg, #6a59ff 0%, #8261ee 100%);
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 8px;
font-size: 0.8rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(106, 89, 255, 0.2);
flex-shrink: 0;
}
.enrollButton:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(106, 89, 255, 0.3);
}
.enrollButton:active {
transform: translateY(0);
}
.noCourses {
text-align: center;
grid-column: 1 / -1;
padding: 3rem;
color: #64748b;
}
.loadingCourses {
text-align: center;
grid-column: 1 / -1;
padding: 3rem;
color: #64748b;
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
}
.loadingSpinner {
width: 24px;
height: 24px;
border: 3px solid #e2e8f0;
border-top: 3px solid #6a59ff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Navigation buttons */
.navButton {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 50px;
height: 50px;
border-radius: 50%;
background-color: white;
border: 2px solid #e2e8f0;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
color: #475569;
z-index: 100;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}
.navButton:hover:not(:disabled) {
border-color: #6a59ff;
color: #6a59ff;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
}
.navButton:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.navButton:first-of-type {
left: 30px;
}
.navButton:last-of-type {
right: 30px;
}
/* Dots indicator */
.dotsContainer {
position: absolute;
bottom: -40px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 12px;
z-index: 100;
}
.dot {
width: 12px;
height: 12px;
border-radius: 50%;
background-color: #cbd5e1;
border: none;
cursor: pointer;
transition: all 0.3s ease;
transform: scale(1);
}
.dot.active {
background-color: #6a59ff;
transform: scale(1.2);
}
.dot:hover {
background-color: #94a3b8;
}
/* Responsive design */
@media (max-width: 1200px) {
.container {
height: 430px;
margin: 35px 0 55px 0;
}
.cardContainer {
width: 300px;
height: 350px;
}
.navButton {
width: 45px;
height: 45px;
}
.navButton:first-of-type {
left: 25px;
}
.navButton:last-of-type {
right: 25px;
}
.dotsContainer {
bottom: -35px;
}
}
@media (max-width: 992px) {
.container {
height: 410px;
margin: 30px 0 50px 0;
}
.cardContainer {
width: 280px;
height: 330px;
}
.cardWrapper {
padding: 0 12px;
}
.navButton {
width: 40px;
height: 40px;
}
.navButton:first-of-type {
left: 20px;
}
.navButton:last-of-type {
right: 20px;
}
.dotsContainer {
bottom: -30px;
}
.dot {
width: 10px;
height: 10px;
}
}
@media (max-width: 768px) {
.container {
height: 380px;
margin: 25px 0 45px 0;
}
.cardContainer {
width: 250px;
height: 300px;
}
.cardWrapper {
padding: 0 10px;
}
.navButton {
width: 35px;
height: 35px;
}
.navButton:first-of-type {
left: 15px;
}
.navButton:last-of-type {
right: 15px;
}
.navButton svg {
width: 20px;
height: 20px;
}
.dotsContainer {
bottom: -25px;
gap: 10px;
}
.dot {
width: 9px;
height: 9px;
}
}
@media (max-width: 576px) {
.container {
height: 350px;
margin: 20px 0 40px 0;
}
.cardContainer {
width: 220px;
height: 270px;
}
.cardWrapper {
padding: 0 8px;
}
.navButton {
width: 30px;
height: 30px;
}
.navButton:first-of-type {
left: 10px;
}
.navButton:last-of-type {
right: 10px;
}
.navButton svg {
width: 16px;
height: 16px;
}
.dotsContainer {
bottom: -20px;
gap: 8px;
}
.dot {
width: 8px;
height: 8px;
}
}
@media (max-width: 400px) {
.container {
height: 320px;
margin: 15px 0 35px 0;
}
.cardContainer {
width: 200px;
height: 250px;
}
.navButton {
width: 28px;
height: 28px;
}
.navButton svg {
width: 14px;
height: 14px;
}
.dotsContainer {
bottom: -18px;
}
.dot {
width: 7px;
height: 7px;
}
}