diff --git a/src/components/certificate/Certificate.tsx b/src/components/certificate/Certificate.tsx
index 45bdac2..3b4d0c6 100644
--- a/src/components/certificate/Certificate.tsx
+++ b/src/components/certificate/Certificate.tsx
@@ -82,19 +82,40 @@ const Certificate: React.FC = () => {
const maxScroll = totalWidth - (typeof window !== "undefined" ? window.innerWidth * 0.7 : 800);
const handleNext = useCallback(() => {
+ if (isMobile && scrollRef.current) {
+ const el = scrollRef.current;
+ const step = cardWidth + GAP;
+ const atEnd = el.scrollLeft + el.clientWidth >= el.scrollWidth - 10;
+ if (atEnd) {
+ el.scrollTo({ left: 0, behavior: "smooth" });
+ } else {
+ el.scrollBy({ left: step, behavior: "smooth" });
+ }
+ return;
+ }
setScrollX((prev) => {
const next = prev + cardWidth + GAP;
if (next > maxScroll) return 0;
return next;
});
- }, [maxScroll, cardWidth]);
+ }, [isMobile, maxScroll, cardWidth]);
const handlePrev = useCallback(() => {
+ if (isMobile && scrollRef.current) {
+ const el = scrollRef.current;
+ const step = cardWidth + GAP;
+ if (el.scrollLeft <= 10) {
+ el.scrollTo({ left: el.scrollWidth, behavior: "smooth" });
+ } else {
+ el.scrollBy({ left: -step, behavior: "smooth" });
+ }
+ return;
+ }
setScrollX((prev) => {
if (prev <= 0) return maxScroll > 0 ? maxScroll : 0;
return prev - cardWidth - GAP;
});
- }, [maxScroll, cardWidth]);
+ }, [isMobile, maxScroll, cardWidth]);
const openModal = useCallback((cert: CertificateItem) => {
setSelectedCertificate(cert);
@@ -107,10 +128,10 @@ const Certificate: React.FC = () => {
}, []);
useEffect(() => {
- if (modalOpen) return;
+ if (modalOpen || isMobile) return;
const interval = setInterval(handleNext, 4000);
return () => clearInterval(interval);
- }, [modalOpen, handleNext]);
+ }, [modalOpen, handleNext, isMobile]);
// Build SVG connector paths
const connectorPaths = certificates.slice(0, -1).map((_, i) => {
@@ -160,8 +181,11 @@ const Certificate: React.FC = () => {
{/* Navigation */}
{
{
- {/* Scrollable zigzag */}
+ {/* Scrollable container — translateX on desktop, native horizontal scroll on mobile */}
{/* SVG connectors overlay */}
@@ -242,8 +281,8 @@ const Certificate: React.FC = () => {
left: { xs: "auto", md: left },
top: { xs: "auto", md: isDown ? OFFSET_Y : 0 },
width: cardWidth,
- mb: { xs: 3, md: 0 },
- display: { xs: index < 4 ? "block" : "none", md: "block" },
+ flexShrink: 0,
+ scrollSnapAlign: { xs: "center", md: "none" },
zIndex: 2
}}
>
@@ -340,8 +379,9 @@ const Certificate: React.FC = () => {
- {/* Progress bar */}
+ {/* Progress bar - desktop only */}
diff --git a/src/components/contact/Contact.tsx b/src/components/contact/Contact.tsx
index de54888..55f3461 100644
--- a/src/components/contact/Contact.tsx
+++ b/src/components/contact/Contact.tsx
@@ -150,7 +150,7 @@ const Contact: React.FC = () => {
{
backdropFilter: "blur(20px)",
width: "100%"
}}
- styles={{ body: { padding: 32 } }}
+ className="contact-form-card"
>
Send Me a Message
diff --git a/src/components/contact/contact.css b/src/components/contact/contact.css
index 0d42198..0b7983b 100644
--- a/src/components/contact/contact.css
+++ b/src/components/contact/contact.css
@@ -560,3 +560,13 @@
font-size: var(--fs-xs);
}
}
+
+
+.contact-form-card .ant-card-body {
+ padding: 32px;
+}
+@media (max-width: 600px) {
+ .contact-form-card .ant-card-body {
+ padding: 16px;
+ }
+}
diff --git a/src/components/footer/Footer.tsx b/src/components/footer/Footer.tsx
index fc869fc..7e89f87 100644
--- a/src/components/footer/Footer.tsx
+++ b/src/components/footer/Footer.tsx
@@ -47,7 +47,7 @@ const Footer: React.FC = () => {
window.scrollTo({ top: 0, behavior: "smooth" });
};
- const currentYear: number = new Date().getFullYear();
+ const currentYear: number = 2025;
const quickLinks: QuickLink[] = [
{ name: "About", href: "#about" },
@@ -140,7 +140,7 @@ const Footer: React.FC = () => {
{/* Main Footer Content */}
{/* Brand Section */}
-
+
{
{/* Social Links */}
-
+
{socialLinks.map((social: SocialLink) => (
{
{/* Quick Links */}
-
+
{
mb: 3,
color: "white",
position: "relative",
+ display: "inline-block",
"&::after": {
content: '""',
position: "absolute",
bottom: -8,
- left: 0,
+ left: { xs: "50%", sm: 0 },
+ transform: { xs: "translateX(-50%)", sm: "none" },
width: 50,
height: 3,
background: "#0eaddf"
@@ -221,7 +223,7 @@ const Footer: React.FC = () => {
>
Quick Links
-
+
{quickLinks.map((link: QuickLink) => (
{
{/* Contact Info */}
-
+
{
mb: 3,
color: "white",
position: "relative",
+ display: "inline-block",
"&::after": {
content: '""',
position: "absolute",
bottom: -8,
- left: 0,
+ left: { xs: "50%", sm: 0 },
+ transform: { xs: "translateX(-50%)", sm: "none" },
width: 50,
height: 3,
background: "#0eaddf"
@@ -279,7 +283,7 @@ const Footer: React.FC = () => {
>
Contact Info
-
+
{contactInfo.map((info: ContactInfo, index: number) => (
{
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
+
return (
{
-
+
-
+
+
+ {/* Right-edge fade hint for horizontal scroll on mobile */}
+
-
+
);
diff --git a/src/components/qualification/Qualification.tsx b/src/components/qualification/Qualification.tsx
index 6f46941..9ca7f69 100644
--- a/src/components/qualification/Qualification.tsx
+++ b/src/components/qualification/Qualification.tsx
@@ -10,7 +10,9 @@ import {
Chip,
Paper,
Grid,
- Avatar
+ Avatar,
+ useTheme,
+ useMediaQuery
} from "@mui/material";
import {
Timeline,
@@ -141,6 +143,8 @@ const getTypeColor = (type: string): string => {
const Qualification: React.FC = () => {
const [value, setValue] = useState(0);
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
const handleChange = useCallback((_event: React.SyntheticEvent, newValue: number): void => {
setValue(newValue);
@@ -223,10 +227,10 @@ const Qualification: React.FC = () => {
sx={{
textTransform: "none",
fontWeight: value === 0 ? 600 : 500,
- fontSize: "1rem",
+ fontSize: { xs: "0.9rem", md: "1rem" },
color: value === 0 ? "#0eaddf" : "#8b949e",
minHeight: 64,
- px: 4
+ px: { xs: 2, md: 4 }
}}
/>
{
sx={{
textTransform: "none",
fontWeight: value === 1 ? 600 : 500,
- fontSize: "1rem",
+ fontSize: { xs: "0.9rem", md: "1rem" },
color: value === 1 ? "#0eaddf" : "#8b949e",
minHeight: 64,
- px: 4
+ px: { xs: 2, md: 4 }
}}
/>
-
+
{education.map((item: EducationItem, index: number) => (