/* ==========================================================================
   Portfolio - Muhammad Haidar Hakim (darXhi)
   --------------------------------------------------------------------------
   Table of contents:
    0. Self-hosted font
    1. Design tokens (CSS variables) - light & dark
    2. Reset & base
    3. Animated background
    4. Utilities (container, section, button, tag)
    5. Header / navbar
    6. Home
   6b. About
    7. Skills
    8. Projects & modal
    9. Contact
   10. Footer
   11. Scroll animations & reduced motion
   12. Responsive: tablet (768px-1024px) & mobile (<768px)
   13. Mobile / touch performance
   ========================================================================== */


/* ==========================================================================
   0. FONT (self-hosted Poppins, latin subset)
   Served from our own origin instead of fonts.googleapis.com: that removes a
   render-blocking stylesheet plus a second DNS/TLS handshake before any text
   can be painted. font-display: swap keeps text visible while it loads.
   ========================================================================== */
@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('./assets/fonts/poppins-400.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
        U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
        U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('./assets/fonts/poppins-500.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
        U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
        U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('./assets/fonts/poppins-600.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
        U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
        U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('./assets/fonts/poppins-700.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
        U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
        U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 800;
    font-display: swap;
    src: url('./assets/fonts/poppins-800.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
        U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
        U+FEFF, U+FFFD;
}

/* ==========================================================================
   1. DESIGN TOKENS
   The accent colors are the same in both modes; only backgrounds, surfaces,
   text, borders, and shadows change. Edit the palette here.
   ========================================================================== */
:root {
    /* Accents (same for light & dark) */
    --accent: #6d5dfc;
    --accent-2: #2bb3d9;
    --accent-3: #ec4899;
    --accent-gradient: linear-gradient(135deg, var(--accent), var(--accent-2));
    --on-accent: #ffffff;
    --nav-underline: var(--accent-gradient);
    --progress-gradient: linear-gradient(90deg, var(--accent), var(--accent-2), var(--accent-3));
    --accent-glow: rgba(109, 93, 252, 0.35);          /* colored shadow under the button & photo */
    --accent-glow-strong: rgba(109, 93, 252, 0.45);

    /* Light mode */
    --bg: #f6f7fb;
    --surface: rgba(255, 255, 255, 0.72);   /* translucent "glass" cards */
    --surface-solid: #ffffff;               /* modal & mobile menu (must stay opaque) */
    --surface-2: #f3f4fb;
    --text: #181a2b;
    --text-muted: #555a72;
    --border: rgba(24, 26, 43, 0.1);
    --accent-text: #5846ea;                 /* accent for text: safe contrast on light bg */
    --accent-text-2: #c026d3;
    --accent-soft: rgba(109, 93, 252, 0.12);
    --header-bg: rgba(246, 247, 251, 0.7);
    /* Panel behind the hero text: opaque enough to stay readable over the
       moving background, without a backdrop-filter (which would repaint every
       frame while the clouds drift). */
    --panel-bg: rgba(255, 255, 255, 0.82);
    --panel-border: rgba(24, 26, 43, 0.12);
    --shadow-sm: 0 2px 10px rgba(24, 26, 43, 0.06);
    --shadow-md: 0 14px 36px rgba(24, 26, 43, 0.1);
    --shadow-lg: 0 24px 60px rgba(24, 26, 43, 0.18);
    --blob-opacity: 0.38;
    --dot-color: rgba(24, 26, 43, 0.09);
    /* Light-mode clouds: soft white with a faint accent tint underneath.
       The shape is an SVG of blurred ellipses, tiled and used as a mask. */
    --cloud-top: rgba(255, 255, 255, 0.95);
    --cloud-bottom: rgba(214, 228, 255, 0.55);
    --cloud-shape: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 600 300' preserveAspectRatio='none'%3E%3Cdefs%3E%3Cfilter id='s' x='-20%25' y='-20%25' width='140%25' height='140%25'%3E%3CfeGaussianBlur stdDeviation='9'/%3E%3C/filter%3E%3C/defs%3E%3Cg fill='%23000' filter='url(%23s)'%3E%3Cellipse cx='110' cy='200' rx='88' ry='44'/%3E%3Cellipse cx='168' cy='163' rx='66' ry='52'/%3E%3Cellipse cx='238' cy='196' rx='78' ry='40'/%3E%3Cellipse cx='300' cy='210' rx='52' ry='28'/%3E%3Cellipse cx='425' cy='168' rx='58' ry='44'/%3E%3Cellipse cx='486' cy='196' rx='82' ry='38'/%3E%3Cellipse cx='536' cy='208' rx='46' ry='28'/%3E%3C/g%3E%3C/svg%3E");

    /* Shooting-star colors (dark mode uses its own, see below) */
    --neon-1: #6d5dfc;
    --neon-2: #2bb3d9;
    --neon-3: #ec4899;


    /* Typography, sizes, radii */
    --font: 'Poppins', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    --header-h: 72px;
    --container: 1140px;
    --radius-sm: 12px;
    --radius: 16px;
    --radius-lg: 24px;
    --glass: saturate(160%) blur(18px);

    /* Transitions */
    --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
    --t-fast: 0.2s;
    --t: 0.35s;
    --theme-transition: background-color var(--t) var(--ease), color var(--t) var(--ease),
        border-color var(--t) var(--ease), box-shadow var(--t) var(--ease);

    color-scheme: light;
}

/* Dark mode: enabled with <html data-theme="dark"> (see script.js) */
[data-theme="dark"] {
    /* Animated background palette (dark mode only; light mode uses the accents) */
    --bg-color-1: #092328;
    --bg-color-2: #12544f;
    --bg-color-3: #2a835f;
    --bg-color-4: #8bbb92;
    --nav-underline: linear-gradient(135deg, var(--bg-color-3), var(--bg-color-4));
    --progress-gradient: linear-gradient(90deg, var(--bg-color-2), var(--bg-color-3), var(--bg-color-4));

    --bg: #092328;
    --surface: rgba(14, 50, 54, 0.62);
    --surface-solid: #0f3035;
    --surface-2: #164045;
    --text: #eceefb;
    --text-muted: #a4a9c6;
    --border: rgba(255, 255, 255, 0.09);
    /* Accents: green palette in dark mode */
    --accent: #2a835f;                      /* white text on it stays readable (4.6:1) */
    --accent-2: #8bbb92;
    --accent-3: #5c9e76;
    --accent-gradient: linear-gradient(135deg, var(--accent), var(--accent-2));
    --accent-text: #8bbb92;                 /* lighter accent so it stays readable on dark bg */
    --accent-text-2: #c2e0c6;
    --accent-soft: rgba(42, 131, 95, 0.22);
    --accent-glow: rgba(42, 131, 95, 0.35);
    --accent-glow-strong: rgba(42, 131, 95, 0.5);
    --header-bg: rgba(9, 35, 40, 0.65);
    --panel-bg: rgba(11, 42, 47, 0.8);
    --panel-border: rgba(255, 255, 255, 0.12);
    --shadow-sm: 0 2px 10px rgba(0, 0, 0, 0.3);
    --shadow-md: 0 14px 36px rgba(0, 0, 0, 0.35);
    --shadow-lg: 0 24px 60px rgba(0, 0, 0, 0.55);
    --blob-opacity: 0.45;
    --dot-color: rgba(255, 255, 255, 0.06);
    /* Shooting stars: brighter teal/green so they read as light in the dark */
    --neon-1: #2ee6a8;
    --neon-2: #5ff2ff;
    --neon-3: #8bbb92;

    color-scheme: dark;
}


/* ==========================================================================
   2. RESET & BASE
   ========================================================================== */
*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html {
    scroll-behavior: smooth;                 /* smooth scroll when clicking the menu */
    scroll-padding-top: var(--header-h);     /* keep section titles clear of the sticky navbar */
    scrollbar-gutter: stable;                /* prevent layout jump when the modal locks scrolling */
    -webkit-text-size-adjust: 100%;
}

body {
    font-family: var(--font);
    font-size: 1rem;
    line-height: 1.7;
    color: var(--text);
    background-color: var(--bg);
    overflow-x: clip;
    transition: var(--theme-transition);
}

/* Sideways reveal animations (data-reveal="left/right") start up to 48px off-screen;
   clip them here so the page never gets wider than the screen on mobile */
.main {
    overflow-x: clip;
}

/* Lock page scrolling while the project modal is open (pure CSS, can't get stuck) */
body:has(.modal[open]) {
    overflow: hidden;
}

img {
    display: block;
    max-width: 100%;
    height: auto;
}

a {
    color: inherit;
    text-decoration: none;
}

ul {
    list-style: none;
}

button {
    font: inherit;
    color: inherit;
    background: none;
    border: none;
    cursor: pointer;
}

:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 3px;
    border-radius: 6px;
}

/* Visually hidden but still read by screen readers */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* "Skip to content" link for keyboard / screen reader users */
.skip-link {
    position: absolute;
    left: 1rem;
    top: -100px;
    z-index: 200;
    padding: 0.6rem 1rem;
    border-radius: var(--radius-sm);
    background: var(--accent);
    color: var(--on-accent);
    font-weight: 600;
}

.skip-link:focus {
    top: 1rem;
}


/* ==========================================================================
   3. ANIMATED BACKGROUND
   A fixed layer behind all content. Large blurred blobs drift around and
   cycle through the accent colors. script.js adds a slight parallax on scroll.
   ========================================================================== */
.bg-animated {
    position: fixed;
    inset: 0 0 -130px;          /* extra height: the scroll parallax shifts it up by up to 120px */
    z-index: -1;
    overflow: hidden;
    pointer-events: none;
    /* Own compositor layer: the scroll parallax below then only moves this
       layer instead of re-rasterizing the heavy blurred blobs every frame. */
    will-change: transform;
    transform: translateZ(0);
}

/* Subtle dot-grid texture on top of the blobs, fading out towards the edges */
.bg-animated::after {
    content: '';
    position: absolute;
    inset: 0;
    background-image: radial-gradient(var(--dot-color) 1px, transparent 1px);
    background-size: 28px 28px;
    -webkit-mask-image: radial-gradient(ellipse at center, #000 20%, transparent 75%);
    mask-image: radial-gradient(ellipse at center, #000 20%, transparent 75%);
}

.bg-blob {
    position: absolute;
    width: 46vmax;
    height: 46vmax;
    border-radius: 50%;
    filter: blur(90px);
    opacity: var(--blob-opacity);
    will-change: transform;
    transition: opacity var(--t) var(--ease);
}

.bg-blob--1 {
    top: -18%;
    left: -12%;
    background: var(--accent);
    animation: blob-drift-1 26s ease-in-out infinite alternate,
        blob-colors 18s linear infinite;
}

.bg-blob--2 {
    top: 10%;
    right: -18%;
    background: var(--accent-2);
    animation: blob-drift-2 30s ease-in-out infinite alternate,
        blob-colors 18s linear infinite -6s;
}

.bg-blob--3 {
    bottom: -22%;
    left: 20%;
    background: var(--accent-3);
    animation: blob-drift-3 24s ease-in-out infinite alternate,
        blob-colors 18s linear infinite -12s;
}

.bg-blob--4 {
    top: 45%;
    left: -20%;
    width: 32vmax;
    height: 32vmax;
    background: var(--accent-2);
    animation: blob-drift-2 34s ease-in-out infinite alternate-reverse,
        blob-colors 22s linear infinite -3s;
}

/* Dark mode: the blobs cycle through the green background palette instead */
[data-theme="dark"] .bg-blob--1 {
    background: var(--bg-color-3);
    animation: blob-drift-1 26s ease-in-out infinite alternate,
        blob-colors-dark 24s linear infinite -12s;
}

[data-theme="dark"] .bg-blob--2 {
    background: var(--bg-color-2);
    animation: blob-drift-2 30s ease-in-out infinite alternate,
        blob-colors-dark 24s linear infinite -6s;
}

[data-theme="dark"] .bg-blob--3 {
    background: var(--bg-color-4);
    animation: blob-drift-3 24s ease-in-out infinite alternate,
        blob-colors-dark 24s linear infinite -18s;
}

[data-theme="dark"] .bg-blob--4 {
    background: var(--bg-color-1);
    animation: blob-drift-2 34s ease-in-out infinite alternate-reverse,
        blob-colors-dark 24s linear infinite;
}

@keyframes blob-drift-1 {
    0%   { transform: translate(0, 0) scale(1); }
    50%  { transform: translate(28vw, 18vh) scale(1.15); }
    100% { transform: translate(12vw, 45vh) scale(0.9); }
}

@keyframes blob-drift-2 {
    0%   { transform: translate(0, 0) scale(1); }
    50%  { transform: translate(-25vw, 25vh) scale(0.85); }
    100% { transform: translate(-8vw, -10vh) scale(1.2); }
}

@keyframes blob-drift-3 {
    0%   { transform: translate(0, 0) scale(1.1); }
    50%  { transform: translate(20vw, -30vh) scale(0.9); }
    100% { transform: translate(-15vw, -15vh) scale(1); }
}

/* Light mode: each blob cycles violet -> cyan -> pink -> violet */
@keyframes blob-colors {
    0%, 100% { background-color: var(--accent); }
    33%      { background-color: var(--accent-2); }
    66%      { background-color: var(--accent-3); }
}

/* Dark mode: each blob cycles through the 4 green colors (offset per blob via negative delays) */
@keyframes blob-colors-dark {
    0%, 100% { background-color: var(--bg-color-1); }
    25%      { background-color: var(--bg-color-2); }
    50%      { background-color: var(--bg-color-3); }
    75%      { background-color: var(--bg-color-4); }
}


/* --------------------------------------------------------------------------
   3b. SHOOTING STARS (neon meteor field)
   script.js fills #bg-meteors with .meteor spans and gives each one its own
   position, length, speed, delay and color through the custom properties
   below, so no two streaks fall alike. Everything is fixed behind the content
   and freezes under `prefers-reduced-motion: reduce`.
   -------------------------------------------------------------------------- */
.bg-meteors {
    position: absolute;
    inset: 0;
    overflow: hidden;
}

.meteor {
    --angle: 135deg;      /* travel direction: down-left, tail pointing up-right */
    --len: 140px;         /* length of the tail */
    --travel: 90vmax;     /* distance covered before it fades out */
    --thickness: 2px;
    --color: var(--neon-2);
    position: absolute;
    top: var(--y, 0);
    left: var(--x, 0);
    width: var(--len);
    height: var(--thickness);
    border-radius: var(--thickness);
    transform-origin: 100% 50%;   /* the head (right end) leads the streak */
    background: linear-gradient(90deg, transparent, var(--color));
    filter: drop-shadow(0 0 6px var(--color));
    opacity: 0;
    will-change: transform, opacity;
    animation: meteor-fall var(--dur, 6s) linear var(--delay, 0s) infinite;
}

/* The bright head of the shooting star */
.meteor::after {
    content: '';
    position: absolute;
    right: 0;
    top: 50%;
    width: calc(var(--thickness) * 2.5);
    height: calc(var(--thickness) * 2.5);
    border-radius: 50%;
    transform: translate(50%, -50%);
    background: #fff;
    box-shadow: 0 0 10px 2px var(--color), 0 0 22px 6px var(--color);
}

@keyframes meteor-fall {
    0%   { transform: rotate(var(--angle)) translate3d(0, 0, 0); opacity: 0; }
    8%   { opacity: var(--meteor-opacity, 0.9); }
    75%  { opacity: var(--meteor-opacity, 0.9); }
    100% { transform: rotate(var(--angle)) translate3d(var(--travel), 0, 0); opacity: 0; }
}

/* The shooting stars belong to dark mode; light mode gets the water below */
:root:not([data-theme="dark"]) .bg-meteors {
    display: none;
}

/* --------------------------------------------------------------------------
   3c. DRIFTING CLOUDS (light mode)
   Three cloud bands, each masked with the SVG above and tiled three times
   across its own width, so translating by exactly one tile (-33.333%) loops
   forever without a seam. They all move left; the nearer, larger band moves
   fastest, which reads as depth.
   -------------------------------------------------------------------------- */
.bg-clouds {
    position: absolute;
    inset: 0;
    overflow: hidden;
}

[data-theme="dark"] .bg-clouds {
    display: none;
}

.cloud-band {
    position: absolute;
    left: -100%;
    width: 300%;
    background: linear-gradient(180deg, var(--cloud-top), var(--cloud-bottom));
    -webkit-mask-image: var(--cloud-shape);
    mask-image: var(--cloud-shape);
    -webkit-mask-size: 33.333% 100%;
    mask-size: 33.333% 100%;
    -webkit-mask-repeat: repeat-x;
    mask-repeat: repeat-x;
    will-change: transform;
    animation: cloud-drift var(--cloud-dur, 90s) linear infinite;
}

/* Far away: small, pale and slow */
.cloud-band--1 {
    --cloud-dur: 120s;
    top: 6%;
    height: 26vh;
    opacity: 0.5;
}

/* Middle distance */
.cloud-band--2 {
    --cloud-dur: 80s;
    top: 34%;
    height: 34vh;
    opacity: 0.62;
    animation-delay: -30s;      /* offset so the bands don't line up */
}

/* Closest: biggest and fastest */
.cloud-band--3 {
    --cloud-dur: 55s;
    bottom: -4%;
    height: 42vh;
    opacity: 0.72;
    animation-delay: -12s;
}

@keyframes cloud-drift {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(-33.333%, 0, 0); }   /* exactly one tile: seamless */
}

/* Nothing should be flying or drifting when the visitor asked for reduced motion */
@media (prefers-reduced-motion: reduce) {
    .bg-meteors {
        display: none;
    }

    .cloud-band {
        animation: none;
    }
}
/* ==========================================================================
   4. UTILITIES
   ========================================================================== */
.container {
    width: min(100% - 2.5rem, var(--container));
    margin-inline: auto;
}

.section {
    padding-block: clamp(4rem, 8vw, 6.5rem);
}

.section__header {
    text-align: center;
    margin-bottom: clamp(2rem, 5vw, 3.5rem);
}

.section__title {
    font-size: clamp(1.8rem, 4vw, 2.5rem);
    font-weight: 700;
    line-height: 1.2;
    letter-spacing: -0.01em;
}

/* Gradient bar under the title, "drawn" when the header scrolls into view */
.section__title::after {
    content: '';
    display: block;
    width: 64px;
    height: 4px;
    margin: 0.8rem auto 0;
    border-radius: 4px;
    background: linear-gradient(90deg, var(--accent), var(--accent-2), var(--accent-3));
    transform: scaleX(0);
    transition: transform 0.8s var(--ease) 0.3s;
}

.section__header.is-visible .section__title::after,
html:not(.js) .section__title::after {
    transform: scaleX(1);
}

.section__subtitle {
    max-width: 560px;
    margin: 1rem auto 0;
    color: var(--text-muted);
}

/* Buttons */
.btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.8rem 1.5rem;
    border: 2px solid transparent;
    border-radius: 999px;
    font-size: 0.95rem;
    font-weight: 600;
    transition: var(--theme-transition), transform var(--t-fast) var(--ease);
}

.btn--primary {
    background: var(--accent);
    color: var(--on-accent);
    box-shadow: 0 8px 22px var(--accent-glow);
}

.btn--primary:hover {
    transform: translateY(-2px);
    box-shadow: 0 12px 28px var(--accent-glow-strong);
}

.btn--ghost {
    border-color: var(--border);
    background: var(--surface);
    color: var(--text);
    -webkit-backdrop-filter: var(--glass);
    backdrop-filter: var(--glass);
}

.btn--ghost:hover {
    transform: translateY(-2px);
    border-color: var(--accent);
    color: var(--accent-text);
}

/* Small tags (project technologies) */
.tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

.tag {
    padding: 0.2rem 0.65rem;
    border-radius: 999px;
    background: var(--accent-soft);
    color: var(--accent-text);
    font-size: 0.75rem;
    font-weight: 500;
    line-height: 1.6;
}


/* ==========================================================================
   5. HEADER / NAVBAR
   3-column grid: logo (left) | menu (center) | theme button (right)
   ========================================================================== */
.header {
    position: sticky;
    top: 0;
    z-index: 100;
    background: var(--header-bg);
    -webkit-backdrop-filter: var(--glass);
    backdrop-filter: var(--glass);
    border-bottom: 1px solid transparent;
    transition: var(--theme-transition);
}

/* Added by script.js once the page has been scrolled */
.header.is-scrolled {
    border-bottom-color: var(--border);
    box-shadow: var(--shadow-sm);
}

/* Scroll progress bar; --progress (0-1) is set by script.js */
.scroll-progress {
    position: absolute;
    left: 0;
    bottom: -1px;
    width: 100%;
    height: 3px;
    background: var(--progress-gradient);
    transform: scaleX(var(--progress, 0));
    transform-origin: left;
}

.nav {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 1rem;
    height: var(--header-h);
}

.nav__logo {
    justify-self: start;
    font-size: 1.3rem;
    font-weight: 800;
    letter-spacing: -0.02em;
}

.nav__logo span {
    color: var(--accent-text);
}

.nav__menu {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: clamp(1.25rem, 3vw, 2.5rem);
    text-align: center;
}

.nav__link {
    position: relative;
    display: inline-block;
    padding: 0.35rem 0.15rem;
    color: var(--text-muted);
    font-size: 0.95rem;
    font-weight: 500;
    transition: color var(--t-fast) var(--ease);
}

/* "Home" stands out: larger & bold */
.nav__link--home {
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--text);
}

/* Underline marking the active section (scroll-spy) */
.nav__link::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -2px;
    height: 3px;
    border-radius: 3px;
    background: var(--nav-underline);
    transform: scaleX(0);
    transform-origin: center;
    transition: transform var(--t) var(--ease);
}

.nav__link:hover,
.nav__link.active {
    color: var(--text);
}

.nav__link.active::after {
    transform: scaleX(1);
}

.nav__actions {
    justify-self: end;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.theme-toggle,
.nav__burger {
    display: grid;
    place-items: center;
    width: 42px;
    height: 42px;
    border: 1px solid var(--border);
    border-radius: 50%;
    background: var(--surface);
    font-size: 1.2rem;
    transition: var(--theme-transition), transform var(--t) var(--ease);
}

.theme-toggle:hover,
.nav__burger:hover {
    border-color: var(--accent);
    color: var(--accent-text);
}

.theme-toggle:hover {
    transform: rotate(-20deg);
}

/* Pressed: the button squashes, then spins a full turn while a ring ripples
   out of it. script.js adds .is-switching for the length of the animation. */
.theme-toggle {
    position: relative;
}

.theme-toggle:active {
    transform: scale(0.86);
    transition-duration: var(--t-fast);
}

.theme-toggle.is-switching {
    animation: theme-spin 0.6s var(--ease);
}

.theme-toggle::after {
    content: '';
    position: absolute;
    inset: -2px;
    border-radius: 50%;
    border: 2px solid var(--accent);
    opacity: 0;
    pointer-events: none;
}

.theme-toggle.is-switching::after {
    animation: theme-ripple 0.6s var(--ease);
}

@keyframes theme-spin {
    0%   { transform: rotate(0) scale(1); }
    35%  { transform: rotate(150deg) scale(0.78); }
    100% { transform: rotate(360deg) scale(1); }
}

@keyframes theme-ripple {
    0%   { opacity: 0.85; transform: scale(0.9); }
    100% { opacity: 0; transform: scale(2.1); }
}

/* Which icon shows depends on the active theme */
.theme-toggle .icon-sun,
[data-theme="dark"] .theme-toggle .icon-moon {
    display: none;
}

[data-theme="dark"] .theme-toggle .icon-sun {
    display: block;
}

.theme-toggle i {
    animation: icon-pop 0.4s var(--ease);
}

@keyframes icon-pop {
    from { transform: rotate(-90deg) scale(0.4); opacity: 0; }
    to   { transform: none; opacity: 1; }
}

/* Hamburger hidden on desktop & tablet */
.nav__burger {
    display: none;
}


/* ==========================================================================
   6. HOME
   ========================================================================== */
.home {
    display: flex;
    align-items: center;
    min-height: calc(100svh - var(--header-h));
    padding-block: clamp(3rem, 6vw, 5rem);
}

.home__container {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: clamp(2.5rem, 6vw, 5rem);
}

/* Animatable angle for the rotating gradient ring around the photo */
@property --ring-angle {
    syntax: '<angle>';
    inherits: false;
    initial-value: 0deg;
}

/* Profile photo: round, animated gradient ring, gentle floating motion */
.home__photo-wrap {
    position: relative;
    width: clamp(220px, 28vw, 340px);
    aspect-ratio: 1;
    padding: 6px;
    border-radius: 50%;
    background: conic-gradient(from var(--ring-angle), var(--accent), var(--accent-2), var(--accent-3), var(--accent));
    box-shadow: 0 24px 60px var(--accent-glow);
    animation: ring-spin 6s linear infinite, float 6s ease-in-out infinite;
}

/* Dashed ring that slowly rotates */
.home__photo-wrap::after {
    content: '';
    position: absolute;
    inset: -16px;
    border: 2px dashed var(--accent);
    border-radius: 50%;
    opacity: 0.4;
    animation: spin 40s linear infinite;
}

@keyframes ring-spin {
    to { --ring-angle: 360deg; }
}

@keyframes spin {
    to { transform: rotate(360deg); }
}

@keyframes float {
    0%, 100% { translate: 0 0; }
    50%      { translate: 0 -12px; }
}

.home__photo {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border: 6px solid var(--bg);
    border-radius: 50%;
    transition: border-color var(--t) var(--ease);
}

/* The hero text sits on its own block so it stays readable over the animated
   background in both themes. */
.home__content {
    padding: clamp(1.5rem, 3vw, 2.25rem) clamp(1.5rem, 3vw, 2.5rem);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    background: var(--panel-bg);
    box-shadow: var(--shadow-md);
    transition: var(--theme-transition);
}

.home__name {
    font-size: clamp(2.1rem, 5vw, 3.6rem);
    font-weight: 800;
    line-height: 1.1;
    letter-spacing: -0.02em;
}

/* Typing name: ghost and typed text share one grid cell, so the ghost fixes the size */
.typing {
    display: inline-grid;
}

.typing__ghost,
.typing__text {
    grid-area: 1 / 1;
}

.js .typing__ghost {
    visibility: hidden;         /* without JS the ghost stays visible as the plain name */
}

/* Blinking caret; stays solid while letters are being typed/deleted */
.typing__text::after {
    content: '';
    display: inline-block;
    width: 0.08em;
    height: 0.9em;
    margin-left: 0.06em;
    border-radius: 2px;
    background: var(--accent-text);
    vertical-align: -0.08em;
    animation: caret-blink 1s steps(1) infinite;
}

html:not(.js) .typing__text::after {
    display: none;
}

.typing__text.is-typing::after {
    animation: none;
}

@keyframes caret-blink {
    50% { opacity: 0; }
}

.home__role {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin: 0.75rem 0 1.25rem;
    font-size: clamp(1.15rem, 2.2vw, 1.45rem);
    font-weight: 600;
}

.home__role::before {
    content: '';
    width: 36px;
    height: 3px;
    border-radius: 3px;
    background: var(--accent-gradient);
}

/* Shimmering gradient text */
.home__role span {
    background: linear-gradient(90deg, var(--accent-text), var(--accent-text-2), var(--accent-text));
    background-size: 200% auto;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    animation: shimmer 5s linear infinite;
}

@keyframes shimmer {
    to { background-position: -200% center; }
}

.home__intro {
    max-width: 540px;
    color: var(--text);
    opacity: 0.86;
    font-size: 1.05rem;
}

.home__cta {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    margin-top: 2rem;
}


/* ==========================================================================
   6b. ABOUT
   A single panel holding the intro paragraph. Same glass treatment as the
   Skills panel so the two sections read as a pair.
   ========================================================================== */
.about__panel {
    max-width: 760px;
    margin-inline: auto;
    padding: clamp(1.75rem, 4vw, 3rem);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface);
    -webkit-backdrop-filter: var(--glass);
    backdrop-filter: var(--glass);
    box-shadow: var(--shadow-md);
}

.about__text {
    font-size: 1.05rem;
    color: var(--text);
    opacity: 0.9;
}


/* ==========================================================================
   7. SKILLS
   One column that grows downwards. A single glass panel keeps the section
   visually separate; the skills themselves are plain icon + text rows.
   ========================================================================== */
.skills__panel {
    max-width: 760px;
    margin-inline: auto;
    padding: clamp(1.75rem, 4vw, 3rem);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface);
    -webkit-backdrop-filter: var(--glass);
    backdrop-filter: var(--glass);
    box-shadow: var(--shadow-md);
}

/* Divider between Soft Skills and Hard Skills */
.skill-group + .skill-group {
    margin-top: 2rem;
    padding-top: 2rem;
    border-top: 1px solid var(--border);
}

.skill-group__title {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 1rem;
    font-size: 1.25rem;
    font-weight: 700;
}

.skill-group__icon {
    display: grid;
    place-items: center;
    width: 42px;
    height: 42px;
    border-radius: 12px;
    background: var(--accent-gradient);
    color: var(--on-accent);
    font-size: 1.25rem;
    font-weight: 400;
}

.skill-group__subtitle {
    margin: 1.5rem 0 0.5rem;
    color: var(--text-muted);
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.skill-group__title + .skill-group__subtitle {
    margin-top: 0;
}

/* Vertical list, no boxes around the items */
.skill-list {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.skill-item {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    padding: 0.45rem 0.25rem;
    font-weight: 500;
}

/* Icon column: Remixicon <i> (soft skills) or inline brand-logo <svg> (hard skills) */
.skill-item i,
.skill-item svg {
    flex-shrink: 0;
    width: 1.75rem;
    color: var(--accent-text);
    font-size: 1.4rem;
    text-align: center;
    transition: transform var(--t) var(--ease), color var(--t) var(--ease);
}

.skill-item svg {
    height: 1.3rem;
    fill: currentColor;
}

.skill-item span {
    transition: transform var(--t) var(--ease), color var(--t) var(--ease);
}

.skill-item:hover i,
.skill-item:hover svg {
    transform: scale(1.2) rotate(-8deg);
    color: var(--accent-text-2);
}

.skill-item:hover span {
    transform: translateX(6px);
    color: var(--accent-text);
}


/* ==========================================================================
   8. PROJECTS & MODAL
   ========================================================================== */
.projects__grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);   /* desktop: 3 columns */
    gap: clamp(1.25rem, 2.5vw, 2rem);
}

.project-card {
    position: relative;
    display: flex;
    flex-direction: column;
    height: 100%;
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    -webkit-backdrop-filter: var(--glass);
    backdrop-filter: var(--glass);
    box-shadow: var(--shadow-sm);
    cursor: pointer;
    transition: var(--theme-transition), transform var(--t) var(--ease);
}

/* Hover/focus: card lifts + accent border = clickable */
.project-card:hover,
.project-card:focus-within {
    transform: translateY(-6px);
    border-color: var(--accent);
    box-shadow: var(--shadow-lg);
}

.project-card__media {
    aspect-ratio: 16 / 10;
    overflow: hidden;
    background: var(--surface-2);
}

.project-card__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.5s var(--ease);
}

.project-card:hover .project-card__img {
    transform: scale(1.06);
}

.project-card__body {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding: 1.25rem 1.35rem 1.4rem;
}

.project-card__title {
    font-size: 1.1rem;
    font-weight: 600;
    line-height: 1.35;
}

.project-card__btn {
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    margin-top: auto;
    color: var(--accent-text);
    font-size: 0.9rem;
    font-weight: 600;
}

/* "Stretched button": the click area covers the whole card */
.project-card__btn::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
    border-radius: inherit;
}

.project-card__btn:focus-visible {
    outline: none;
}

.project-card__btn:focus-visible::after {
    outline: 3px solid var(--accent);
    outline-offset: -3px;
    border-radius: var(--radius);
}

.project-card__btn i {
    transition: transform var(--t-fast) var(--ease);
}

.project-card:hover .project-card__btn i {
    transform: translateX(4px);
}

/* --- Project detail modal --- */
/* Wide 16:9 "slide": large image on the left, text on the right.
   FIXED size: long descriptions scroll inside, the modal doesn't grow.
   The width is also capped by the screen height so the whole slide always fits. */
.modal {
    width: min(1120px, calc(100% - 3rem), calc((100svh - 3rem) * 16 / 9));
    aspect-ratio: 16 / 9;
    max-height: calc(100svh - 3rem);
    margin: auto;
    padding: 0;
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface-solid);
    color: var(--text);
    box-shadow: var(--shadow-lg);
}

.modal[open] {
    display: flex;
    animation: modal-in 0.35s var(--ease);
}

.modal::backdrop {
    background: rgba(8, 10, 20, 0.6);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
}

@keyframes modal-in {
    from { opacity: 0; transform: translateY(24px) scale(0.97); }
    to   { opacity: 1; transform: none; }
}

.modal__media {
    position: relative;
    flex: 0 0 56%;              /* large image: 56% of the slide width */
    min-height: 0;              /* don't let the image size push the modal size */
    overflow: hidden;
    background: var(--surface-2);
}

/* The whole image is shown (not cropped), placed inside the media area like a
   picture on a slide. Absolute, so it never affects the modal's size. */
.modal__img {
    --pad: clamp(1.25rem, 3vw, 2.25rem);
    position: absolute;
    inset: var(--pad);
    width: calc(100% - 2 * var(--pad));
    height: calc(100% - 2 * var(--pad));
    object-fit: contain;
    filter: drop-shadow(0 14px 28px rgba(0, 0, 0, 0.22));   /* hugs the visible image */
}

.modal__close {
    position: absolute;
    top: 0.9rem;
    right: 0.9rem;
    z-index: 1;
    display: grid;
    place-items: center;
    width: 42px;
    height: 42px;
    border-radius: 50%;
    background: rgba(13, 15, 28, 0.6);
    color: #ffffff;
    font-size: 1.35rem;
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    transition: background-color var(--t-fast) var(--ease), transform var(--t) var(--ease);
}

.modal__close:hover {
    background: var(--accent);
    transform: rotate(90deg);
}

.modal__body {
    flex: 1;
    min-width: 0;
    min-height: 0;              /* important: lets the description scroll inside the flex column */
    display: flex;
    flex-direction: column;
    gap: 1rem;
    padding: clamp(3.25rem, 5vw, 4rem) clamp(1.5rem, 3vw, 2.75rem) clamp(1.5rem, 3vw, 2.5rem);
}

/* Slide-style title with an accent bar underneath */
.modal__title {
    font-size: clamp(1.4rem, 2.4vw, 2rem);
    font-weight: 700;
    line-height: 1.25;
}

.modal__title::after {
    content: '';
    display: block;
    width: 56px;
    height: 4px;
    margin-top: 0.75rem;
    border-radius: 4px;
    background: var(--accent-gradient);
}

/* Scrollable description area */
.modal__desc {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-right: 0.5rem;
    color: var(--text-muted);
    scrollbar-width: thin;
    scrollbar-color: var(--accent) transparent;
}

.modal__desc p + p {
    margin-top: 0.85rem;
}


/* ==========================================================================
   9. CONTACT
   A single card holding every contact as a row separated by thin lines.
   ========================================================================== */
.contact__card {
    max-width: 580px;
    margin-inline: auto;
    padding: clamp(0.75rem, 2.5vw, 1.25rem);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface);
    -webkit-backdrop-filter: var(--glass);
    backdrop-filter: var(--glass);
    box-shadow: var(--shadow-md);
}

.contact__list > li + li {
    border-top: 1px solid var(--border);
}

/* One contact row: [icon] Platform name / value */
.contact__item {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0.95rem 0.85rem;
    border-radius: var(--radius);
    transition: background-color var(--t) var(--ease), transform var(--t-fast) var(--ease);
}

.contact__item:hover {
    background: var(--accent-soft);
}

.contact__icon {
    flex-shrink: 0;
    display: grid;
    place-items: center;
    width: 46px;
    height: 46px;
    border-radius: 14px;
    background: var(--accent-soft);
    color: var(--accent-text);
    font-size: 1.4rem;
    transition: var(--theme-transition), transform var(--t) var(--ease);
}

.contact__item:hover .contact__icon {
    background: var(--accent-gradient);
    color: var(--on-accent);
    transform: rotate(-8deg) scale(1.05);
}

.contact__text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.contact__label {
    font-weight: 600;
    line-height: 1.35;
}

.contact__value {
    color: var(--text-muted);
    font-size: 0.85rem;
    overflow-wrap: anywhere;
}

.contact__arrow {
    margin-left: auto;
    color: var(--text-muted);
    font-size: 1.1rem;
    transition: transform var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}

.contact__item:hover .contact__arrow {
    transform: translate(3px, -3px);
    color: var(--accent-text);
}


/* ==========================================================================
   10. FOOTER
   ========================================================================== */
.footer {
    padding-block: 1.75rem;
    border-top: 1px solid var(--border);
    background: var(--header-bg);
    -webkit-backdrop-filter: var(--glass);
    backdrop-filter: var(--glass);
    color: var(--text-muted);
    font-size: 0.9rem;
    text-align: center;
    transition: var(--theme-transition);
}


/* ==========================================================================
   11. SCROLL ANIMATIONS & REDUCED MOTION
   Elements with data-reveal="<type>" start hidden and animate in when they
   enter the viewport (script.js toggles .is-visible). They replay every time
   you scroll down to them again. Only hidden when JS runs (.js on <html>).
   Types: up | left | right | zoom | blur | flip
   ========================================================================== */
.js [data-reveal] {
    opacity: 0;
    transition: opacity 0.7s var(--ease), transform 0.7s var(--ease), filter 0.7s var(--ease),
        var(--theme-transition);
    transition-delay: var(--delay, 0s);
}

.js [data-reveal="up"]    { transform: translateY(40px); }
.js [data-reveal="left"]  { transform: translateX(-48px); }
.js [data-reveal="right"] { transform: translateX(48px); }
.js [data-reveal="zoom"]  { transform: scale(0.85); }
.js [data-reveal="blur"]  { transform: translateY(20px); filter: blur(12px); }
.js [data-reveal="flip"]  { transform: perspective(900px) rotateX(22deg) translateY(50px); transform-origin: center top; }

.js [data-reveal].is-visible {
    opacity: 1;
    transform: none;
    filter: none;
}

@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }

    .js [data-reveal] {
        opacity: 1;
        transform: none;
        filter: none;
    }

    .section__title::after {
        transform: scaleX(1);
    }

    .typing__text::after {
        display: none;
    }
}


/* ==========================================================================
   12. RESPONSIVE
   Desktop : > 1024px   (defaults above)
   Tablet  : 768px - 1024px
   Mobile  : < 768px
   ========================================================================== */

/* ---------- Tablet ---------- */
@media (max-width: 1024px) {
    .home__container {
        gap: 3rem;
    }

    .home__photo-wrap {
        width: clamp(220px, 30vw, 280px);
    }

    .projects__grid {
        grid-template-columns: repeat(2, 1fr);   /* tablet: 2 columns */
    }
}

/* ---------- Mobile ---------- */
@media (max-width: 767.98px) {
    :root {
        --header-h: 64px;
    }

    /* Navbar: logo left, theme toggle + hamburger right */
    .nav {
        grid-template-columns: 1fr auto;
    }

    .nav__burger {
        display: grid;
    }

    /* Menu turns into a dropdown below the header */
    .nav__menu {
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        flex-direction: column;
        gap: 0.25rem;
        padding: 0.75rem 1.25rem 1.25rem;
        border-bottom: 1px solid var(--border);
        background: var(--surface-solid);
        box-shadow: var(--shadow-md);
        opacity: 0;
        visibility: hidden;
        transform: translateY(-10px);
        transition: opacity var(--t) var(--ease), transform var(--t) var(--ease),
            visibility var(--t), var(--theme-transition);
    }

    .nav__menu.is-open {
        opacity: 1;
        visibility: visible;
        transform: none;
    }

    .nav__menu li {
        width: 100%;
    }

    .nav__link {
        padding: 0.6rem 0.25rem;
    }

    /* Home: 1 column, photo on top, text below, centered */
    .home {
        min-height: auto;
    }

    .home__container {
        grid-template-columns: 1fr;
        justify-items: center;
        text-align: center;
    }

    .home__photo-wrap {
        width: clamp(200px, 60vw, 260px);
    }

    .home__content {
        width: 100%;       /* the block spans the column instead of hugging the text */
    }

    .home__role {
        justify-content: center;
    }

    .home__intro {
        margin-inline: auto;
    }

    .home__cta {
        justify-content: center;
    }

    /* Projects: 1 column */
    .projects__grid {
        grid-template-columns: 1fr;
    }

    .contact__item {
        padding: 0.85rem 0.5rem;
    }

    /* Modal: too narrow for a side-by-side slide, so stack image on top of the text */
    .modal {
        width: calc(100% - 1.5rem);
        height: min(620px, calc(100svh - 1.5rem));
        aspect-ratio: auto;
    }

    .modal[open] {
        flex-direction: column;
    }

    .modal__media {
        flex-basis: 38%;
    }

    /* Here the media area is already close to the image's shape, so fill it edge to edge */
    .modal__img {
        --pad: 0px;
        object-fit: cover;
        filter: none;
    }

    .modal__body {
        padding: 1.5rem 1.25rem 1.75rem;
    }

    /* Lighter background on small screens (fewer, smaller blobs) */
    .bg-blob {
        filter: blur(70px);
    }

    .bg-blob--4 {
        display: none;
    }
}


/* ==========================================================================
   13. MOBILE / TOUCH PERFORMANCE
   Phones have far less GPU fill rate than a laptop, and three things in this
   design are expensive there: backdrop-filter (the blurred glass has to be
   recomputed every frame while the background moves underneath it), animating
   background-color on a 46vmax layer that is blurred by 90px (each color step
   repaints and re-blurs the whole layer), and scaling that same blurred layer
   (a scale cannot be done by the compositor alone, so it re-rasterizes).
   Below, all three are dropped on touch devices and small screens; the design
   still reads the same, it just stops repainting.
   ========================================================================== */
@media (max-width: 900px), (hover: none) {
    /* 1. No live glass: use the opaque panel colors instead */
    .header,
    .skills__panel,
    .about__panel,
    .project-card,
    .contact__card,
    .nav__menu,
    .modal__dialog {
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
    }

    :root {
        --surface: rgba(255, 255, 255, 0.9);
        --header-bg: rgba(246, 247, 251, 0.94);
    }

    [data-theme="dark"] {
        --surface: rgba(14, 50, 54, 0.9);
        --header-bg: rgba(9, 35, 40, 0.94);
    }

    /* 2. No color cycling on the blobs - drift only, and a cheaper blur */
    .bg-blob,
    [data-theme="dark"] .bg-blob {
        filter: blur(60px);
        animation-name: blob-drift-flat;
        animation-duration: 30s;
    }

    .bg-blob--2,
    [data-theme="dark"] .bg-blob--2 {
        animation-name: blob-drift-flat-2;
    }

    /* 3. Translate only, never scale: keeps it on the compositor */
    @keyframes blob-drift-flat {
        0%   { transform: translate3d(0, 0, 0); }
        100% { transform: translate3d(18vw, 30vh, 0); }
    }

    @keyframes blob-drift-flat-2 {
        0%   { transform: translate3d(0, 0, 0); }
        100% { transform: translate3d(-16vw, 18vh, 0); }
    }

    /* Fewer, simpler moving parts in the background layer */
    .bg-blob--3 {
        display: none;
    }

    .cloud-band--2 {
        display: none;
    }

    /* drop-shadow on every streak is the expensive part; the head keeps its glow */
    .meteor {
        filter: none;
    }
}
