update mobile

This commit is contained in:
karyamanswasta
2025-08-17 23:53:18 +07:00
parent cd5fb36279
commit 8d677eda1d
10 changed files with 420 additions and 42 deletions

View File

@@ -3,11 +3,11 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta <meta
name="description" name="description"
content="Web site created using create-react-app" content="Kediri Technopark - Katalis Karir dan Bisnis Digital"
/> />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!-- Font Awesome --> <!-- Font Awesome -->
@@ -26,7 +26,7 @@
work correctly both with client-side routing and a non-root public URL. work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`. Learn how to configure a non-root public URL by running `npm run build`.
--> -->
<title>React App</title> <title>Kediri Technopark</title>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>

View File

@@ -9,10 +9,27 @@ const AnimatedBackground = () => {
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
let animationFrameId; let animationFrameId;
let particles = []; let particles = [];
const particleCount = 70;
// Determine particle count based on screen size
const getParticleCount = () => {
const width = window.innerWidth;
if (width <= 400) return 30; // Very small screens
if (width <= 576) return 40; // Small screens
if (width <= 768) return 50; // Medium screens
return 70; // Large screens
};
function Particle(x, y, vx, vy) { function Particle(x, y, vx, vy) {
this.x = x; this.y = y; this.vx = vx; this.vy = vy; this.radius = 1.5; this.x = x; this.y = y; this.vx = vx; this.vy = vy;
// Adjust particle radius based on screen size
if (window.innerWidth <= 400) {
this.radius = 1.0; // Smaller radius for very small screens
} else if (window.innerWidth <= 576) {
this.radius = 1.2; // Medium radius for small screens
} else {
this.radius = 1.5; // Default radius for larger screens
}
} }
const setupCanvas = () => { const setupCanvas = () => {
@@ -31,6 +48,9 @@ const AnimatedBackground = () => {
ctx.scale(dpr, dpr); ctx.scale(dpr, dpr);
// Get particle count based on current screen size
const particleCount = getParticleCount();
particles = []; particles = [];
for (let i = 0; i < particleCount; i++) { for (let i = 0; i < particleCount; i++) {
particles.push(new Particle(Math.random() * cssWidth, Math.random() * cssHeight, (Math.random() - 0.5) * 0.5, (Math.random() - 0.5) * 0.5)); particles.push(new Particle(Math.random() * cssWidth, Math.random() * cssHeight, (Math.random() - 0.5) * 0.5, (Math.random() - 0.5) * 0.5));
@@ -40,7 +60,14 @@ const AnimatedBackground = () => {
function connectParticles() { function connectParticles() {
const cssWidth = canvas.clientWidth; const cssWidth = canvas.clientWidth;
const cssHeight = canvas.clientHeight; const cssHeight = canvas.clientHeight;
const connectionDistance = 90;
// Adjust connection distance based on screen size
let connectionDistance = 90;
if (window.innerWidth <= 400) {
connectionDistance = 60; // Smaller connection distance for very small screens
} else if (window.innerWidth <= 576) {
connectionDistance = 70; // Medium connection distance for small screens
}
for (let i = 0; i < particles.length; i++) { for (let i = 0; i < particles.length; i++) {
for (let j = i + 1; j < particles.length; j++) { for (let j = i + 1; j < particles.length; j++) {
@@ -83,6 +110,14 @@ const AnimatedBackground = () => {
const cssHeight = canvas.clientHeight; const cssHeight = canvas.clientHeight;
ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.clearRect(0, 0, canvas.width, canvas.height);
// Adjust animation speed based on screen size
let speedFactor = 1.0;
if (window.innerWidth <= 400) {
speedFactor = 0.7; // Slower animation for very small screens
} else if (window.innerWidth <= 576) {
speedFactor = 0.8; // Slightly slower animation for small screens
}
for (const p of particles) { for (const p of particles) {
// LOGIKA BARU: Partikel tembus (wrapping) bukan memantul (bounce) // LOGIKA BARU: Partikel tembus (wrapping) bukan memantul (bounce)
if (p.x > cssWidth + p.radius) p.x = -p.radius; if (p.x > cssWidth + p.radius) p.x = -p.radius;
@@ -91,8 +126,8 @@ const AnimatedBackground = () => {
if (p.y > cssHeight + p.radius) p.y = -p.radius; if (p.y > cssHeight + p.radius) p.y = -p.radius;
else if (p.y < -p.radius) p.y = cssHeight + p.radius; else if (p.y < -p.radius) p.y = cssHeight + p.radius;
p.x += p.vx; p.x += p.vx * speedFactor;
p.y += p.vy; p.y += p.vy * speedFactor;
ctx.beginPath(); ctx.beginPath();
ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2); ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
@@ -106,11 +141,17 @@ const AnimatedBackground = () => {
setupCanvas(); setupCanvas();
animate(); animate();
window.addEventListener('resize', setupCanvas);
// Handle resize events to adjust particle count
const handleResize = () => {
setupCanvas();
};
window.addEventListener('resize', handleResize);
return () => { return () => {
cancelAnimationFrame(animationFrameId); cancelAnimationFrame(animationFrameId);
window.removeEventListener('resize', setupCanvas); window.removeEventListener('resize', handleResize);
}; };
}, []); }, []);

View File

@@ -71,6 +71,15 @@ const CoverflowCarousel = ({ products, onCardClick }) => {
goToProduct(index, dir); goToProduct(index, dir);
}; };
// Collapse overlay for center card
const collapseOverlay = () => {
// Reset animation state to force collapse
setAnimationState('spread');
setTimeout(() => {
setAnimationState('ready');
}, 50);
};
// Initialize carousel with spread effect when products are available // Initialize carousel with spread effect when products are available
useEffect(() => { useEffect(() => {
if (!products || products.length === 0) return; if (!products || products.length === 0) return;
@@ -159,7 +168,15 @@ const CoverflowCarousel = ({ products, onCardClick }) => {
animationState === 'initial' ? styles.initial : animationState === 'initial' ? styles.initial :
animationState === 'spread' ? styles.spread : '' animationState === 'spread' ? styles.spread : ''
}`} }`}
onClick={() => { goToProduct(productIndex, position > 0 ? 'right' : (position < 0 ? 'left' : null)); }} onClick={() => {
// Only trigger navigation if this is not the center card
// or if it's the center card but not in hover state (overlay not visible)
const isCenter = position === 0;
const canHover = isCenter && animationState === 'ready' && !shiftDirection && !isDragging;
if (position !== 0 || (position === 0 && (!canHover || animationState !== 'ready' || shiftDirection || isDragging))) {
goToProduct(productIndex, position > 0 ? 'right' : (position < 0 ? 'left' : null));
}
}}
> >
<div className={styles.cardShadow} aria-hidden="true"></div> <div className={styles.cardShadow} aria-hidden="true"></div>
<div className={styles.cardWrapper}> <div className={styles.cardWrapper}>
@@ -168,6 +185,7 @@ const CoverflowCarousel = ({ products, onCardClick }) => {
onCardClick={(p) => { onCardClick && onCardClick(p); }} onCardClick={(p) => { onCardClick && onCardClick(p); }}
isCenter={position === 0} isCenter={position === 0}
canHover={position === 0 && animationState === 'ready' && !shiftDirection && !isDragging} canHover={position === 0 && animationState === 'ready' && !shiftDirection && !isDragging}
onCollapse={position === 0 ? collapseOverlay : undefined}
/> />
</div> </div>
</div> </div>

View File

@@ -10,6 +10,27 @@
user-select: none; user-select: none;
} }
/* Left and right fade out masks */
.leftMask,
.rightMask {
position: absolute;
top: 0;
bottom: 0;
width: 100px;
z-index: 20;
pointer-events: none;
}
.leftMask {
left: 0;
background: linear-gradient(to right, #0b1220, transparent);
}
.rightMask {
right: 0;
background: linear-gradient(to left, #0b1220, transparent);
}
.container.dragging { .container.dragging {
cursor: grabbing; cursor: grabbing;
} }
@@ -415,6 +436,11 @@
.dotsContainer { .dotsContainer {
bottom: -35px; bottom: -35px;
} }
.leftMask,
.rightMask {
width: 80px;
}
} }
@media (max-width: 992px) { @media (max-width: 992px) {
@@ -453,6 +479,11 @@
width: 10px; width: 10px;
height: 10px; height: 10px;
} }
.leftMask,
.rightMask {
width: 70px;
}
} }
@media (max-width: 768px) { @media (max-width: 768px) {
@@ -497,21 +528,26 @@
width: 9px; width: 9px;
height: 9px; height: 9px;
} }
.leftMask,
.rightMask {
width: 60px;
}
} }
@media (max-width: 576px) { @media (max-width: 576px) {
.container { .container {
height: 350px; height: 320px;
margin: 20px 0 40px 0; margin: 20px 0 40px 0;
} }
.cardContainer { .cardContainer {
width: 220px; width: 200px;
height: 270px; height: 250px;
} }
.cardWrapper { .cardWrapper {
padding: 0 8px; padding: 0 6px;
} }
.navButton { .navButton {
@@ -541,17 +577,39 @@
width: 8px; width: 8px;
height: 8px; height: 8px;
} }
/* Adjust positions for smaller screens */
.cardContainer.positionNeg2 {
transform: translateX(-280px) rotateY(55deg) scale(0.6);
}
.cardContainer.positionNeg1 {
transform: translateX(-140px) rotateY(35deg) scale(0.75);
}
.cardContainer.position1 {
transform: translateX(140px) rotateY(-35deg) scale(0.75);
}
.cardContainer.position2 {
transform: translateX(280px) rotateY(-55deg) scale(0.6);
}
.leftMask,
.rightMask {
width: 50px;
}
} }
@media (max-width: 400px) { @media (max-width: 400px) {
.container { .container {
height: 320px; height: 280px;
margin: 15px 0 35px 0; margin: 15px 0 35px 0;
} }
.cardContainer { .cardContainer {
width: 200px; width: 180px;
height: 250px; height: 230px;
} }
.navButton { .navButton {
@@ -572,4 +630,26 @@
width: 7px; width: 7px;
height: 7px; height: 7px;
} }
/* Further adjust positions for very small screens */
.cardContainer.positionNeg2 {
transform: translateX(-240px) rotateY(55deg) scale(0.5);
}
.cardContainer.positionNeg1 {
transform: translateX(-120px) rotateY(35deg) scale(0.7);
}
.cardContainer.position1 {
transform: translateX(120px) rotateY(-35deg) scale(0.7);
}
.cardContainer.position2 {
transform: translateX(240px) rotateY(-55deg) scale(0.5);
}
.leftMask,
.rightMask {
width: 40px;
}
} }

View File

@@ -27,6 +27,18 @@ const Header = ({ username, scrollToProduct, scrollToCourse, setShowedModal, han
}, 0); }, 0);
}; };
// Close mobile menu when window is resized to desktop size
useEffect(() => {
const handleResize = () => {
if (window.innerWidth > 600) {
setMenuOpen(false);
}
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return ( return (
<header className={`${styles.header} ${isScrolled ? styles.headerScrolled : ''}`}> <header className={`${styles.header} ${isScrolled ? styles.headerScrolled : ''}`}>
<img src="./kediri-technopark-logo.png" className={styles.logo} alt="Logo" /> <img src="./kediri-technopark-logo.png" className={styles.logo} alt="Logo" />
@@ -39,7 +51,7 @@ const Header = ({ username, scrollToProduct, scrollToCourse, setShowedModal, han
className={`${styles.navLink} ${hoveredNav === 2 ? styles.navLinkHover : ''}`} className={`${styles.navLink} ${hoveredNav === 2 ? styles.navLinkHover : ''}`}
onMouseEnter={() => setHoveredNav(2)} onMouseEnter={() => setHoveredNav(2)}
onMouseLeave={() => setHoveredNav(null)} onMouseLeave={() => setHoveredNav(null)}
onClick={() => navigate('/')} onClick={() => { navigate('/'); setMenuOpen(false); }}
> >
Home Home
</a> </a>
@@ -49,6 +61,7 @@ const Header = ({ username, scrollToProduct, scrollToCourse, setShowedModal, han
onMouseLeave={() => setHoveredNav(null)} onMouseLeave={() => setHoveredNav(null)}
onClick={() => { onClick={() => {
navigate('/dashboard'); navigate('/dashboard');
setMenuOpen(false);
}}> }}>
Dashboard Dashboard
</a> </a>
@@ -60,7 +73,7 @@ const Header = ({ username, scrollToProduct, scrollToCourse, setShowedModal, han
className={`${styles.navLink} ${hoveredNav === 21 ? styles.navLinkHover : ''}`} className={`${styles.navLink} ${hoveredNav === 21 ? styles.navLinkHover : ''}`}
onMouseEnter={() => setHoveredNav(21)} onMouseEnter={() => setHoveredNav(21)}
onMouseLeave={() => setHoveredNav(null)} onMouseLeave={() => setHoveredNav(null)}
onClick={() => scrollToId('about')} onClick={() => { scrollToId('about'); setMenuOpen(false); }}
> >
About About
</a> </a>
@@ -68,7 +81,7 @@ const Header = ({ username, scrollToProduct, scrollToCourse, setShowedModal, han
className={`${styles.navLink} ${hoveredNav === 22 ? styles.navLinkHover : ''}`} className={`${styles.navLink} ${hoveredNav === 22 ? styles.navLinkHover : ''}`}
onMouseEnter={() => setHoveredNav(22)} onMouseEnter={() => setHoveredNav(22)}
onMouseLeave={() => setHoveredNav(null)} onMouseLeave={() => setHoveredNav(null)}
onClick={() => scrollToId('services')} onClick={() => { scrollToId('services'); setMenuOpen(false); }}
> >
Services Services
</a> </a>
@@ -76,7 +89,7 @@ const Header = ({ username, scrollToProduct, scrollToCourse, setShowedModal, han
className={`${styles.navLink} ${hoveredNav === 3 ? styles.navLinkHover : ''}`} className={`${styles.navLink} ${hoveredNav === 3 ? styles.navLinkHover : ''}`}
onMouseEnter={() => setHoveredNav(3)} onMouseEnter={() => setHoveredNav(3)}
onMouseLeave={() => setHoveredNav(null)} onMouseLeave={() => setHoveredNav(null)}
onClick={() => scrollToId('products')} onClick={() => { scrollToId('products'); setMenuOpen(false); }}
> >
Products Products
</a> </a>
@@ -84,7 +97,7 @@ const Header = ({ username, scrollToProduct, scrollToCourse, setShowedModal, han
className={`${styles.navLink} ${hoveredNav === 4 ? styles.navLinkHover : ''}`} className={`${styles.navLink} ${hoveredNav === 4 ? styles.navLinkHover : ''}`}
onMouseEnter={() => setHoveredNav(4)} onMouseEnter={() => setHoveredNav(4)}
onMouseLeave={() => setHoveredNav(null)} onMouseLeave={() => setHoveredNav(null)}
onClick={() => scrollToId('academy')} onClick={() => { scrollToId('academy'); setMenuOpen(false); }}
> >
Academy Academy
</a> </a>
@@ -92,7 +105,7 @@ const Header = ({ username, scrollToProduct, scrollToCourse, setShowedModal, han
className={`${styles.navLink} ${hoveredNav === 5 ? styles.navLinkHover : ''}`} className={`${styles.navLink} ${hoveredNav === 5 ? styles.navLinkHover : ''}`}
onMouseEnter={() => setHoveredNav(5)} onMouseEnter={() => setHoveredNav(5)}
onMouseLeave={() => setHoveredNav(null)} onMouseLeave={() => setHoveredNav(null)}
onClick={() => scrollToId('faq')} onClick={() => { scrollToId('faq'); setMenuOpen(false); }}
> >
FAQ FAQ
</a> </a>
@@ -111,7 +124,7 @@ const Header = ({ username, scrollToProduct, scrollToCourse, setShowedModal, han
{username ? ( {username ? (
<> <>
<div className={styles.username}>{username}</div> <div className={styles.username}>{username}</div>
{/* <button onClick={() => { setMenuOpen(false); navigate('/'); }}>Home</button> */} <button onClick={() => { setMenuOpen(false); navigate('/'); }}>Home</button>
<button onClick={() => { setMenuOpen(false); scrollToId('about'); }}>About</button> <button onClick={() => { setMenuOpen(false); scrollToId('about'); }}>About</button>
<button onClick={() => { setMenuOpen(false); scrollToId('services'); }}>Services</button> <button onClick={() => { setMenuOpen(false); scrollToId('services'); }}>Services</button>
<button onClick={() => { setMenuOpen(false); scrollToId('products'); }}>Products</button> <button onClick={() => { setMenuOpen(false); scrollToId('products'); }}>Products</button>
@@ -119,6 +132,7 @@ const Header = ({ username, scrollToProduct, scrollToCourse, setShowedModal, han
<button onClick={() => { setMenuOpen(false); scrollToId('faq'); }}>FAQ</button> <button onClick={() => { setMenuOpen(false); scrollToId('faq'); }}>FAQ</button>
<button className={styles.logoutButton} onClick={() => { <button className={styles.logoutButton} onClick={() => {
navigate('/dashboard'); navigate('/dashboard');
setMenuOpen(false);
}}> }}>
Dashboard Dashboard
</button> </button>

View File

@@ -198,22 +198,41 @@
} }
@media (max-width: 575.98px) { @media (max-width: 575.98px) {
.hero { padding-top: 1.25rem; } .hero { padding-top: 1rem; }
.ctaGroup { display: grid; gap: 8px; } .ctaGroup { display: grid; gap: 8px; }
.ctaPrimary, .ctaSecondary { width: 100% !important; text-align: center; } .ctaPrimary, .ctaSecondary { width: 100% !important; text-align: center; }
.copyWrap { max-width: 100%; padding: 0 10px; }
.title { font-size: clamp(1.3rem, 2.5vw + 1rem, 1.8rem); }
.lead { font-size: clamp(0.9rem, 0.5vw + 0.8rem, 1rem); }
} }
@media (max-width: 767.98px) { @media (max-width: 767.98px) {
.imageWrap::before, .imageWrap::before,
.imageWrap::after { display: none; } .imageWrap::after { display: none; }
.title { font-size: clamp(1.4rem, 1.8vw + 1rem, 2.1rem); line-height: 1.12; } .title { font-size: clamp(1.4rem, 2vw + 1rem, 2.1rem); line-height: 1.12; }
.lead { font-size: clamp(0.93rem, 0.4vw + 0.84rem, 1.03rem); } .lead { font-size: clamp(0.93rem, 0.4vw + 0.84rem, 1.03rem); }
.bulletItem { font-size: 0.92rem; } .bulletItem { font-size: 0.92rem; }
.mesh, .grid { display: none; } .mesh, .grid { display: none; }
.copyWrap { max-width: 100%; padding: 0 15px; }
.imageWrap { max-width: 100%; }
.imageFrame { border-radius: calc(var(--radius-2xl) + 2px); }
.heroImage { border-radius: calc(var(--radius-2xl) - 6px); }
} }
.imageFrame:hover { box-shadow: none; transform: none; } .imageFrame:hover { box-shadow: none; transform: none; }
@media (min-width: 1400px) { @media (min-width: 1400px) {
.imageWrap { max-width: 720px; } .imageWrap { max-width: 720px; }
}
@media (max-width: 400px) {
.hero { padding-top: 0.8rem; }
.title { font-size: clamp(1.2rem, 3vw + 0.9rem, 1.7rem); }
.lead { font-size: clamp(0.85rem, 0.5vw + 0.75rem, 0.95rem); }
.ctaGroup { gap: 6px; }
.ctaPrimary, .ctaSecondary {
padding: 0.4rem 0.7rem !important;
font-size: 0.9rem;
}
.copyWrap { padding: 0 5px; }
} }

View File

@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import styles from './ProductCard.module.css'; import styles from './ProductCard.module.css';
const ProductCard = ({ product, onCardClick, isCenter, canHover }) => { const ProductCard = ({ product, onCardClick, isCenter, canHover, onCollapse }) => {
return ( return (
<div <div
className={`${styles.card} ${isCenter ? styles.isCenter : ''} ${canHover ? styles.canHover : ''}`} className={`${styles.card} ${isCenter ? styles.isCenter : ''} ${canHover ? styles.canHover : ''}`}
@@ -16,10 +16,13 @@ const ProductCard = ({ product, onCardClick, isCenter, canHover }) => {
<div <div
className={styles.overlay} className={styles.overlay}
onClick={(e) => { onClick={(e) => {
if (isCenter) { // Collapse overlay when clicking on the overlay background (not buttons)
// Clicks on overlay open detail; prevent parent selection if (isCenter && canHover && onCollapse) {
e.stopPropagation(); // Check if the click target is the overlay itself, not a button
onCardClick && onCardClick(product); if (e.target === e.currentTarget) {
e.stopPropagation();
onCollapse();
}
} }
}} }}
> >

View File

@@ -132,7 +132,40 @@
.description { -webkit-line-clamp: 2; } .description { -webkit-line-clamp: 2; }
} }
@media (max-width: 576px) { @media (max-width: 768px) {
.title { font-size: 0.9rem; } .title { font-size: 0.9rem; }
.overlay { --overlay-collapsed: 56px; } .overlay { --overlay-collapsed: 60px; }
.description { font-size: 0.8rem; }
.buttonGroup { gap: 6px; }
.detailButton,
.buyButton {
padding: 5px 10px;
font-size: 0.75rem;
}
}
@media (max-width: 576px) {
.title { font-size: 0.85rem; }
.overlay { --overlay-collapsed: 50px; }
.overlayInner { padding: 10px 12px 12px; }
.description { font-size: 0.75rem; margin: 6px 0 8px; }
.buttonGroup { gap: 5px; }
.detailButton,
.buyButton {
padding: 4px 8px;
font-size: 0.7rem;
}
}
@media (max-width: 400px) {
.title { font-size: 0.8rem; }
.overlay { --overlay-collapsed: 45px; }
.overlayInner { padding: 8px 10px 10px; }
.description { font-size: 0.7rem; margin: 4px 0 6px; }
.buttonGroup { gap: 4px; }
.detailButton,
.buyButton {
padding: 3px 6px;
font-size: 0.65rem;
}
} }

View File

@@ -152,6 +152,11 @@
margin-bottom: 25px; margin-bottom: 25px;
} }
.sectionHeader p {
max-width: 100%;
padding: 0 15px;
}
.filterWrapper { .filterWrapper {
gap: 8px; gap: 8px;
} }
@@ -179,6 +184,11 @@
.sectionHeader { .sectionHeader {
margin-bottom: 25px; margin-bottom: 25px;
padding: 0 10px;
}
.sectionTitle {
font-size: clamp(1.4rem, 4vw, 1.8rem);
} }
.sectionHeader p { .sectionHeader p {
@@ -187,6 +197,7 @@
.filterContainer { .filterContainer {
margin-bottom: 20px; margin-bottom: 20px;
padding: 0 10px;
} }
.filterWrapper { .filterWrapper {
@@ -216,14 +227,33 @@
.sectionHeader { .sectionHeader {
margin-bottom: 20px; margin-bottom: 20px;
padding: 0 15px;
}
.sectionTitle {
font-size: clamp(1.3rem, 5vw, 1.7rem);
} }
.sectionHeader p { .sectionHeader p {
font-size: 0.9rem; font-size: 0.9rem;
} }
.filterContainer {
margin-bottom: 15px;
padding: 0 15px;
}
.filterWrapper {
gap: 5px;
}
.filterBtn {
padding: 4px 8px;
font-size: 0.7rem;
}
.carouselContainer { .carouselContainer {
padding: 0 35px; padding: 0 30px;
min-height: 320px; min-height: 320px;
} }
@@ -234,8 +264,39 @@
} }
@media (max-width: 400px) { @media (max-width: 400px) {
.productSection {
padding: 25px 0;
}
.sectionHeader {
margin-bottom: 15px;
padding: 0 10px;
}
.sectionTitle {
font-size: clamp(1.2rem, 6vw, 1.6rem);
}
.sectionHeader p {
font-size: 0.85rem;
}
.filterContainer {
margin-bottom: 12px;
padding: 0 10px;
}
.filterWrapper {
gap: 4px;
}
.filterBtn {
padding: 3px 6px;
font-size: 0.65rem;
}
.carouselContainer { .carouselContainer {
padding: 0 30px; padding: 0 25px;
min-height: 300px; min-height: 300px;
} }

View File

@@ -661,16 +661,19 @@
/* Responsive Styles */ /* Responsive Styles */
@media (max-width: 800px) { @media (max-width: 800px) {
.modalBody { .modalBody {
width: 80%; width: 85%;
max-width: 90%;
margin: 0 15px;
} }
.header { .header {
padding: 14px 2rem; padding: 14px 1.5rem;
} }
.heroContainer { .heroContainer {
grid-template-columns: 1fr; grid-template-columns: 1fr;
text-align: center; text-align: center;
gap: 2rem;
} }
.ctaTitle, .ctaTitle,
@@ -685,7 +688,7 @@
.ctaCard, .ctaCard,
.Section { .Section {
padding: 2rem 0.8rem; padding: 2rem 1rem;
background-color: #f8fafc; background-color: #f8fafc;
} }
@@ -695,7 +698,7 @@
.ctaContainer, .ctaContainer,
.coursesGrid { .coursesGrid {
grid-template-columns: repeat(auto-fit, minmax(173px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 0.8rem; gap: 0.8rem;
} }
@@ -729,6 +732,7 @@
display: none; display: none;
font-size: 28px; font-size: 28px;
cursor: pointer; cursor: pointer;
user-select: none;
} }
.mobileMenu { .mobileMenu {
@@ -736,7 +740,7 @@
} }
/* Tampilkan burger dan menu di mobile */ /* Tampilkan burger dan menu di mobile */
@media (max-width: 600px) { @media (max-width: 768px) {
.nav { .nav {
display: none; display: none;
} }
@@ -757,11 +761,12 @@
border: 1px solid #ddd; border: 1px solid #ddd;
border-radius: 8px; border-radius: 8px;
padding: 12px; padding: 12px;
z-index: 10; z-index: 1000;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10px; gap: 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
min-width: 150px;
} }
.mobileMenu button { .mobileMenu button {
@@ -771,6 +776,8 @@
color: white; color: white;
border-radius: 6px; border-radius: 6px;
cursor: pointer; cursor: pointer;
font-size: 0.9rem;
text-align: left;
} }
.mobileMenu button:hover { .mobileMenu button:hover {
@@ -782,6 +789,108 @@
color: #2563eb; color: #2563eb;
margin-bottom: 4px; margin-bottom: 4px;
} }
.logoutButton {
background-color: transparent;
border: 1px solid #2563eb;
color: #2563eb;
padding: 6px 12px;
border-radius: 6px;
cursor: pointer;
transition: background-color 0.2s ease, color 0.2s ease;
font-size: 0.9rem;
text-align: left;
}
.logoutButton:hover {
background-color: #2563eb;
color: white;
}
}
@media (max-width: 576px) {
.header {
padding: 12px 1rem;
}
.Section {
padding: 1.5rem 0.8rem;
}
.coursesGrid {
grid-template-columns: 1fr;
gap: 1rem;
}
.ctaContainer {
grid-template-columns: 1fr;
gap: 1rem;
}
.featuresList {
gap: 1.5rem;
}
.featureItem {
gap: 1rem;
}
.featureIcon {
width: 50px;
height: 50px;
font-size: 1.2rem;
}
.featureTitle {
font-size: 1.1rem;
}
.mobileMenu {
right: 15px;
top: 55px;
min-width: 140px;
}
.mobileMenu button {
padding: 6px 12px;
font-size: 0.85rem;
}
}
@media (max-width: 400px) {
.Section {
padding: 1.2rem 0.6rem;
}
.featureItem {
gap: 0.8rem;
}
.featureIcon {
width: 45px;
height: 45px;
font-size: 1rem;
}
.featureTitle {
font-size: 1rem;
}
.featureDescription {
font-size: 0.9rem;
}
.mobileMenu {
right: 10px;
top: 50px;
min-width: 130px;
padding: 10px;
}
.mobileMenu button {
padding: 5px 10px;
font-size: 0.8rem;
}
} }
.loggedInContainer { .loggedInContainer {