/* Reset and Base Styles */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html {
    scroll-behavior: smooth;
    overflow-y: scroll;
}

body {
    font-family: 'Quicksand', sans-serif;
    color: #2D2D2D;
    line-height: 1.6;
    overflow-x: hidden;
}

h1, h2 {
    font-family: 'Lora', serif;
}

h3 {
    font-family: 'Quicksand', sans-serif;
    color: #2D2D2d;
    font-weight: 500;
}

/* Side Navigation */
.side-nav {
    position: fixed;
    top: 2rem;
    left: 2.5rem; /* 40px page margin */
    z-index: 1000;
    display: flex;
    flex-direction: column;
    background: transparent;
    padding: 24px 12px 12px 12px; /* 24px top, 12px right, 12px bottom, 12px left */
    border-radius: 8px;
    transition: all 0.3s ease;
}

.side-nav:hover {
    background: transparent;
}

/* Nav color variants */
.side-nav.light {
    color: white;
}

.side-nav.dark {
    color: #056AE0;
}

.side-nav.dark .nav-text {
    color: #2D2D2D;
}

.side-nav.dark .brand-text {
    color: #2D2D2D;
}

.nav-brand {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 32px;
    font-weight: 500;
    font-size: 32px;
    white-space: nowrap;
    overflow: hidden;
    color: inherit;
    text-decoration: none;
    padding: 4px 8px; /* 4px vertical, 8px horizontal */
    border-radius: 6px;
    transition: all 0.3s ease;
}

.nav-brand:hover {
    background: transparent; /* No hover state for brand */
}

.side-nav.dark .nav-brand:hover {
    background: transparent; /* No hover state for brand */
}

.brand-icon {
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
}

.brand-text {
    opacity: 0;
    transition: opacity 0.3s ease;
}

.side-nav:hover .brand-text {
    opacity: 1;
}

.nav-links {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 24px;
}

.nav-link {
    display: flex;
    align-items: center;
    gap: 0; /* No gap in collapsed state */
    color: inherit;
    text-decoration: none;
    font-size: 24px;
    font-weight: 400;
    padding: 4px 8px; /* 4px vertical, 8px horizontal */
    border-radius: 8px;
    transition: all 0.3s ease;
    white-space: nowrap;
    overflow: visible;
    position: relative;
    width: fit-content; /* Hug content in collapsed state */
}

/* When nav is expanded (hovered), add gap; hug icon + label (8px left / 16px right)
   instead of filling the nav container */
.side-nav:hover .nav-link {
    gap: 12px;
    width: fit-content;
    padding: 4px 16px 4px 8px;
}

.nav-link:hover {
    background: #0B60E0;
}

.side-nav.dark .nav-link:hover {
    background: #F4F4F4;
}

/* Base nav is pinned .dark, but each link's hover pill still needs to read as the "white
   nav on blue" color whenever the clip-path overlay's white layer is the one currently
   visible *at that link's own position* — checked per link (see the nav color-switching
   IIFE in script.js, which toggles this class on each .nav-link every frame) rather than
   once for the whole nav, since the blue/white boundary can cross through the middle of
   the nav itself (e.g. landing between two links) instead of sitting cleanly above or
   below all of them. #sideNav (ID) beats .side-nav.dark .nav-link:hover's class-only
   specificity. */
#sideNav .nav-link.link-over-blue:hover {
    background: #0B60E0;
}

.side-nav:hover .nav-link.active {
    width: fit-content;
}

/* Selection reads via the underline only — no pill fill or inset shadow */
.nav-link.active {
    background: transparent;
}

.side-nav.dark .nav-link.active {
    background: transparent;
}

/* Underline: 2px thick, flush against the nav element, rounded caps */
.nav-link.active::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    margin-top: 0px;
    height: 2px;
    border-radius: 999px;
    background: white; /* light nav (white text) */
}

.side-nav.dark .nav-link.active::after {
    background: #056AE0; /* dark nav (primary blue text) */
}

/* An active link is already communicated by its underline — a hover pill on top of it
   is redundant/conflicting. !important because several context-specific hover rules
   (.dark, .link-over-blue, mobile .is-open variants) all need to be overridden uniformly. */
.nav-link.active:hover {
    background: transparent !important;
}

.nav-link.active .nav-text {
    font-weight: 500;
}

.nav-icon {
    flex-shrink: 0;
    width: 24px;
    height: 24px;
    /* Icon sits in a 40x40 container */
    padding: 8px;
    box-sizing: content-box;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
}

/* Hide all icon variations by default */
.nav-icon svg,
.brand-icon svg {
    position: absolute;
    display: none;
}

/* Show blue icons on dark nav (light backgrounds) by default */
.side-nav.dark .nav-icon .icon-blue,
.side-nav.dark .brand-icon .icon-blue {
    display: block;
}

/* Show white icons on light nav (dark backgrounds) by default */
.side-nav.light .nav-icon .icon-white,
.side-nav.light .brand-icon .icon-white {
    display: block;
}

/* Show blue selected icons when active on dark nav */
.side-nav.dark .nav-link.active .nav-icon .icon-blue {
    display: none;
}

.side-nav.dark .nav-link.active .nav-icon .icon-blue-selected {
    display: block;
}

/* Show white selected icons when active on light nav */
.side-nav.light .nav-link.active .nav-icon .icon-white {
    display: none;
}

.side-nav.light .nav-link.active .nav-icon .icon-white-selected {
    display: block;
}

.nav-text {
    opacity: 0;
    transition: opacity 0.3s ease;
    color: inherit;
    width: 0;
    overflow: hidden;
}

.side-nav:hover .nav-text {
    opacity: 1;
    width: auto;
}

/* --- Nav color switching via clip-path ---
   Two full copies of the nav sit stacked at the identical fixed position: the real,
   interactive "base" copy (#sideNav, permanently .dark/blue) always renders; a decorative
   "overlay" clone (permanently .light/white, inserted by script.js) sits on top, clipped
   with a live clip-path to only show over the page's blue region. Where the overlay is
   visible it fully repaints over the base copy beneath it, giving a hard, pixel-accurate
   split instead of a class-based swap or an eased blend. Both copies get all their color
   (text, icons, hover pill, active underline) from the ordinary .dark/.light rules above —
   nothing here overrides those; each copy's class is just pinned rather than scroll-driven. */
.nav-overlay {
    pointer-events: none;
    /* Fully visible by default, matching the page's actual top-of-load state (hero
       section, blue background, white nav needed) — safe if the geometry script can't
       run. JS clips this from the bottom once it measures the real split. */
    clip-path: inset(0px 0px 0px 0px);
    /* .side-nav sets `transition: all 0.3s ease` for its hover expand/collapse animation.
       The overlay is a clone, so it inherits that — which was silently easing every
       clip-path update over 300ms, the "delay" noticeable when scrolling quickly. This
       needs to be a hard, instant split. */
    transition: none;
}

.nav-overlay[aria-hidden] * {
    pointer-events: none;
}

/* Base nav's real :hover expands it; mirror that onto the overlay (which can never
   receive a genuine :hover of its own, since pointer-events is off) via :has() rather
   than duplicating the hover state in JS. */
body:has(#sideNav:hover) .nav-overlay .nav-link {
    gap: 12px;
    width: fit-content;
    padding: 4px 16px 4px 8px;
}

body:has(#sideNav:hover) .nav-overlay .nav-text {
    opacity: 1;
    width: auto;
}

body:has(#sideNav:hover) .nav-overlay .brand-text {
    opacity: 1;
}

/* Hidden on desktop — media query overrides these */
.nav-hamburger,
.nav-close,
.nav-divider {
    display: none;
}

/* Scroll Wrapper */
.scroll-wrapper {
    height: 100vh;
    overflow-y: scroll;
}

/* Sections */
.section {
    min-height: 100vh;
    position: relative;
    /* Page margins: 40px left, 80px right */
    padding-left: 2.5rem;  /* 40px */
    padding-right: 5rem;   /* 80px */
}

/* Hero Section */
.hero-section {
    background: #056AE0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem;
    position: relative;
    overflow: hidden;
}

/* Caps the hero's content (image, lines, text) at the 1440px design width so their
   percentage/edge-anchored positions stay in sync with each other on wider screens —
   otherwise .hero-lines (anchored to the right edge) and .hero-headshot (anchored via a
   left percentage) drift apart as the viewport grows past the design width. Beyond
   1440px this just centers the same 1440px layout in extra blue background, matching
   how the section already renders at exactly 1440px.
   Positioned absolute (inset 0), not as a stretched flex item, so it ignores
   .hero-section's own 2rem padding the same way .hero-lines/.hero-headshot (also
   absolute) always have — a flex item is laid out inside the flex container's content
   box (inset by that padding), which was pulling the lines/headshot in from the true
   viewport edges instead of sitting flush against them. */
.hero-inner {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    max-width: 1440px;
    margin: 0 auto;
    display: flex;
    align-items: center;
}

.hero-lines {
    position: absolute;
    right: 0;
    top: 50%;
    transform: translateY(-50%);
    height: 100%;
    width: auto;
    z-index: 1;
    pointer-events: none;
}

/* Hero headshot positioning */
.hero-headshot {
    position: absolute;
    left: 5%; /* 158/1440 = 10.97% */
    bottom: 0;
    width: 50%; /* 870/1440 = 60.42% */
    height: auto;
    z-index: 2; /* Sits above gradient lines (background is z-index 0) */
}

.hero-content {
    position: relative;
    z-index: 3; /* Above the headshot */
    text-align: right; /* Align text to the right */
    margin-left: auto; /* Push to the right side */
    margin-right: 20%; /* Some spacing from the right edge */
    max-width: 600px;
    /* .hero-content is the sole flex item in .hero-inner, with ample free space around it —
       it should never need to shrink. Some browsers (seen in Arc, not Chrome) compute the
       flex auto-minimum-size differently and were shrinking it below its one-line natural
       width anyway, wrapping "Tanner" onto its own line. flex-shrink:0 makes it render at
       its natural size everywhere, regardless of that engine difference. Reset back to
       shrinkable in the mobile media query below, where wrapping onto multiple lines is
       intentional. */
    flex-shrink: 0;
}

.hero-title {
    font-size: 3rem;
    color: white;
    font-weight: 400;
    white-space: nowrap; /* single-line headline by design; reset in the mobile media query */
}

/* Hero title underline: matches the .section-title::after pattern (#F5A623), scoped to
   just "Tanner" and drawn in left-to-right after the hero-lines SVG animation finishes
   (last line: 0.4s delay + 1.4s duration = 1.8s, plus a short 0.05s buffer). Duration and
   easing match the lines' actual draw speed rather than reusing their duration: line-5's
   path is ~1773 units drawn over 1.4s (~1266 units/sec); "Tanner" is only ~160px wide, so
   at that same rate it draws in ~0.13s (tuned up slightly to 0.18s to feel less instant).
   Same cubic-bezier as the lines for a consistent motion feel. */
.hero-title-highlight {
    position: relative;
    display: inline-block;
}

.hero-title-highlight::after {
    content: '';
    position: absolute;
    left: 0;
    bottom: 0px;
    width: 100%;
    height: 2px;
    background: #F5A623;
    border-radius: 999px;
    transform: scaleX(0);
    transform-origin: left;
    animation: heroTitleUnderline 0.18s cubic-bezier(0.4, 0, 0.2, 1) 1.85s forwards;
}

@keyframes heroTitleUnderline {
    to {
        transform: scaleX(1);
    }
}

@media (max-width: 968px) {
    .hero-headshot {
        display: none;
    }

    /* Parked: none of the sizing approaches tried here (scaling, capping, fixed-size
       cropping) behaved as expected on tablet/mobile — hiding the lines for now rather
       than shipping a half-right version. Revisit later. */
    .hero-lines {
        display: none;
    }
}

/* Section Containers */
.section-container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 6rem 2rem;
    /* Offset: 40px (page margin) + 197px (expanded nav) + 24px (gap) = 261px = 16.3125rem */
    padding-left: 12.5rem;
}

.section-title {
    font-size: 2rem;
    margin-bottom: 2rem;
    color: #333;
    position: relative;
    padding-bottom: .25rem;
    font-weight: 400;
}

.section-title::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 0;
    width: 7.5rem; /* 120px */
    height: 2px;
    background: #F5A623;
}

/* About Section */
.about-section {
    background: white;
    color: white;
    overflow: hidden;
}

.about-bg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    transform: translateY(-80px);
}

.about-section .section-container {
    position: relative;
    z-index: 1;
}

.about-section .section-title {
    color: white;
}

.about-section .section-title::after {
    background: #F5A623;
}

.about-text {
    font-size: 1rem;
    line-height: 1.8;
    margin-bottom: 3rem;
}

.about-image {
    width: 100%;
    height: auto;
    border-radius: 20px;
    box-shadow: 0 8px 32px rgba(0, 10, 20, 0.25);
    display: block;
}

/* Work Section */
.work-section {
    background: #ffffff;
    min-height: auto; /* Allow it to be taller than viewport */
}

.project-card {
    display: grid;
    grid-template-columns: 40% 60%;
    gap: 3rem;
    margin-bottom: 4rem;
    align-items: center;
}

.project-card:last-child {
    /* .work-section's own section-container padding-bottom already provides
       trailing space — this margin was stacking an extra 64px on top of it,
       making the Work→Contact gap inconsistent with the rest of the page's
       192px section-to-section rhythm. */
    margin-bottom: 0;
}

.project-content {
    display: flex;
    flex-direction: column;
}

.project-title {
    font-family: 'Quicksand', sans-serif;
    font-size: 1.5rem;
    font-weight: 400;
    line-height: 1.3;
    margin-bottom: 0.25rem;
    color: #2D2D2D;
}

.project-client {
    color: #74767E;
    font-size: 16px;
    margin-bottom: 1rem;
}

a {
    text-decoration: none;
}

.project-description {
    color: #555;
    line-height: 1.7;
    font-size: 0.95rem;
    margin-bottom: 1.5rem;
}

.project-image {
    display: block;
    text-decoration: none;
    border-radius: 12px;
    overflow: hidden;
    transition: transform 0.3s ease, opacity 0.3s ease;
}

.project-image:hover {
    transform: translateY(-4px);
    opacity: 0.9;
}

.project-image img {
    width: 100%;
    height: auto;
    aspect-ratio: 12 / 5;
    object-fit: cover;
    display: block;
}

/* Play Section
.play-section {
    background: white;
} */

.section-text {
    max-width: 800px;
    font-size: 1rem;
    line-height: 1.8;
    color: #555;
}

/* Contact Section */
.contact-section {
    background: white;
    display: flex;
    flex-direction: column;
}

/* Same artwork as .about-bg, mirrored — rotate(180deg) is a point reflection, i.e.
   flipped both horizontally and vertically in one transform. preserveAspectRatio
   keeps the crop-not-warp behavior at any width. Sized to match .site-footer's
   min-height and anchored to the section's own bottom edge (which .site-footer sits
   flush against) so it lines up with the footer without collide with the cards
   above, no matter how many rows they wrap into at narrower widths. Positioned
   full-bleed via the 100vw/-50vw breakout so .section's own left/right padding
   (which .site-footer is inset by) doesn't crop it — it needs to span the true
   viewport width, not just the section's content box. */
.contact-bg {
    position: absolute;
    left: 50%;
    bottom: 0;
    width: 100vw;
    height: 320px;
    margin-left: -50vw;
    pointer-events: none;
    transform: rotate(180deg);
}

/* Desktop only: grow the box 40px taller while keeping bottom:0 fixed — since the
   artwork is cropped (not stretched), this reveals more of it at the top edge only,
   nudging the wave crest up 40px with the solid-fill portion seamlessly extending
   into the new space. No gap, no separate fallback color needed. */
@media (min-width: 1025px) {
    .contact-bg {
        height: 360px;
    }
}

.contact-section .section-container {
    position: relative;
    z-index: 1;
}

.contact-text {
    font-size: 1rem;
    line-height: 1.8;
    color: #555;
    margin-bottom: 3rem;
}

/* Contact Cards */
.contact-cards {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 2rem;
    max-width: 1000px;
}

.contact-card {
    display: flex;
    flex-direction: column;
    text-decoration: none;
    background: white;
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.contact-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}

.contact-card-image {
    width: 100%;
    aspect-ratio: 1536 / 488;
    overflow: hidden;
}

.contact-card-image img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.contact-card-label {
    padding: 1.25rem;
    text-align: center;
    font-family: 'Quicksand', sans-serif;
    font-size: 1rem;
    font-weight: 500;
    color: #2D2D2D;
    border-top: 1px solid #EBEBEB;
}

/* Footer */
.site-footer {
    position: relative;
    z-index: 1;
    margin-top: auto;
    text-align: center;
    overflow: hidden;
}

.contact-section .site-footer {
    min-height: 320px;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    padding-bottom: 24px;
}

.contact-section .footer-text {
    position: relative;
    z-index: 1;
    color: white;
}

.footer-text {
    font-size: 0.85rem;
    color: #ABABAB;
}

/* Responsive Design */
@media (max-width: 968px) {
    /* Reduce page margins on smaller screens */
    .section {
        padding-left: 1.5rem;  /* 24px */
        padding-right: 3rem;   /* 48px */
    }

    /* Below the 1024px hamburger-nav breakpoint, the nav no longer expands on hover — it's
       a small fixed icon (collapsed footprint ~32px–100px from the left edge) that opens
       as a full-screen overlay instead, so there's no need to reserve the old desktop
       hover-expanded sidebar's space (40px margin + 197px nav + 24px gap). But since the
       nav is position:fixed, *every* section's content still scrolls past underneath it,
       so some clearance is still needed — just enough to clear the collapsed icon's own
       ~100px right edge, not the old expanded-sidebar reservation. */
    .section-container {
        padding-left: 7rem;
    }

    .hero-content {
        flex-direction: column;
        text-align: center;
        margin-left: auto;
        margin-right: auto;
        flex-shrink: 1; /* allow shrinking again so the title can wrap onto multiple lines */
    }

    .hero-title {
        font-size: 2.5rem;
        white-space: normal; /* wrapping is intentional at this size */
    }

    /* .project-card is `display: grid` (two side-by-side columns) at desktop width —
       flex-direction only affects flex containers, so this was silently doing nothing and
       the grid columns stayed in effect on mobile, squeezing the image and text into two
       narrow side-by-side slivers instead of stacking. */
    .project-card {
        display: flex;
        flex-direction: column;
    }

    .project-image {
        width: 100%;
        order: -1; /* image above the title/description, matching the mobile design */
    }

    .section-container {
        padding-top: 4rem;
        padding-bottom: 4rem;
        padding-right: 1.5rem;
    }

    .side-nav {
        top: 1rem;
        left: 1.5rem;
    }
}

@media (max-width: 768px) {
    /* Further reduce margins on mobile */
    .section {
        padding-left: 1rem;   /* 16px */
        padding-right: 1.5rem; /* 24px */
    }

    /* Same reasoning as the 968px block above — clear the collapsed nav icon's own
       footprint, not the old expanded-sidebar reservation. */
    .section-container {
        padding-left: 7rem;
    }

    .side-nav {
        padding: 0.75rem;
        left: 1rem;
    }

    .nav-links {
        gap: 0.5rem;
    }

    .hero-title {
        font-size: 2rem;
    }

    .section-title {
        font-size: 1.5rem;
    }

    /* Scale body copy down proportionally on mobile — these otherwise stayed at
       full desktop size and read too large on a phone screen. */
    .about-text {
        font-size: 0.9rem;
    }

    .project-title {
        font-size: 1.25rem;
    }

    .project-client {
        font-size: 14px;
    }

    .project-description {
        font-size: 0.85rem;
    }

    .contact-text {
        font-size: 0.9rem;
    }

    .contact-card-label {
        font-size: 0.9rem;
    }

    .footer-text {
        font-size: 0.8rem;
    }
}
/* ============================================================
   MOBILE & TABLET NAV (≤1024px)
   ============================================================ */

@media (max-width: 1024px) {

    /* The overlay stays enabled below this breakpoint too — it shares every collapsed-state
       rule below with the base nav (both are just `.side-nav`), so it collapses to the same
       logo+hamburger icon, in the same spot, and the clip-path split still gives that icon
       correct white-on-blue / blue-on-white contrast while scrolling. It's hidden separately,
       once the menu is actually open (see `#sideNav.is-open ~ .nav-overlay` below) since at
       that point the base nav becomes its own full-screen panel and the overlay has nothing
       useful to paint. */

    /* --- Collapsed state --- */

    .side-nav {
        top: 2rem;
        left: 2rem;
        width: auto;
        padding: 0;
        background: transparent !important;
        flex-direction: column;
        align-items: flex-start;
        gap: 12px;
        transition: none;
    }

    .side-nav:hover {
        width: auto;
        background: transparent !important;
    }

    .nav-brand {
        margin-bottom: 0;
        padding: 4px 8px;
        overflow: visible;
    }

    /* Hide wordmark in collapsed state */
    .brand-text {
        opacity: 0;
        width: 0;
        overflow: hidden;
        transition: opacity 0.2s ease, width 0.2s ease;
    }

    /* Show hamburger — override base display:none */
    .nav-hamburger {
        display: flex;
        align-items: center;
        justify-content: center;
        background: none;
        border: none;
        cursor: pointer;
        color: inherit;
        padding: 4px 8px;
        margin-left: 8px;
        width: 40px;
        height: 40px;
        border-radius: 6px;
        transition: background 0.2s ease;
    }

    .nav-hamburger:hover {
        background: rgba(255, 255, 255, 0.1);
    }

    .side-nav.dark .nav-hamburger:hover {
        background: rgba(30, 111, 217, 0.1);
    }

    /* Close button stays hidden until menu opens */
    .nav-close {
        display: none;
    }

    /* Divider stays hidden until menu opens */
    .nav-divider {
        display: none;
    }

    /* Hide nav links in collapsed state */
    .nav-links {
        display: none;
    }

    /* Prevent desktop hover-expand text reveal */
    .side-nav:hover .nav-text {
        opacity: 0;
        width: 0;
    }

    .side-nav:hover .brand-text {
        opacity: 0;
    }

    /* --- Expanded state (is-open toggled by JS) --- */

    /* JS only ever toggles `.is-open` on the real base nav (#sideNav), never on the overlay
       clone — so once the base expands into its full-screen panel, the overlay would
       otherwise keep sitting on top of it as a small leftover icon in the corner, still
       showing whatever collapsed-state clip it last had. Hide it for the duration. */
    #sideNav.is-open ~ .nav-overlay {
        display: none;
    }

    .side-nav.is-open {
        position: fixed;
        top: 0;
        left: 0;
        width: 100vw;
        height: 100vh;
        padding: 2rem;
        border-radius: 0;
        flex-direction: column;
        align-items: flex-start;
        gap: 0;
        z-index: 1000;
    }

    /* Blue overlay on blue sections */
    .side-nav.is-open.light {
        background: #056AE0 !important;
        color: white;
    }

    /* White overlay on light sections */
    .side-nav.is-open.dark {
        background: white !important;
        color: #056AE0;
    }

    /* Show wordmark when open */
    .side-nav.is-open .brand-text {
        opacity: 1;
        width: auto;
    }

    .side-nav.is-open .nav-brand {
        display: flex;
        flex-direction: row;
        align-items: center;
        gap: 12px;
        margin-bottom: 12px;
        font-size: 2rem;
        color: inherit;
    }

    /* Swap hamburger for close button */
    .side-nav.is-open .nav-hamburger {
        display: none;
    }

    .side-nav.is-open .nav-close {
        display: flex;
        align-items: center;
        justify-content: center;
        background: none;
        border: none;
        cursor: pointer;
        color: inherit;
        padding: 4px 8px;
        margin-left: 8px;
        width: 40px;
        height: 40px;
        border-radius: 6px;
        transition: background 0.2s ease;
        margin-bottom: 8px;
    }

    .side-nav.is-open .nav-close:hover {
        background: rgba(255, 255, 255, 0.1);
    }

    .side-nav.is-open.dark .nav-close:hover {
        background: rgba(30, 111, 217, 0.1);
    }

    /* Divider below close button */
    .side-nav.is-open .nav-divider {
        display: block;
        width: 40px;
        height: 1.5px;
        margin-left: 8px;
        margin-bottom: 2rem;
    }

    .side-nav.is-open.light .nav-divider {
        background: rgba(255, 255, 255, 0.6);
    }

    .side-nav.is-open.dark .nav-divider {
        background: rgba(30, 111, 217, 0.4);
    }

    /* Nav links: always visible with icon + text */
    .side-nav.is-open .nav-links {
        display: flex;
        flex-direction: column;
        gap: 8px;
        width: 100%;
    }

    .side-nav.is-open .nav-link {
        display: flex;
        flex-direction: row;
        align-items: center;
        gap: 12px;
        width: auto;
        font-size: 1.5rem;
        font-weight: 400;
        padding: 8px;
        border-radius: 8px;
        color: inherit;
    }

    .side-nav.is-open .nav-text {
        opacity: 1;
        width: auto;
        color: inherit;
    }

    /* Hover states in overlay */
    .side-nav.is-open.light .nav-link:hover {
        background: #0B60E0;
    }

    .side-nav.is-open.dark .nav-link:hover {
        background: rgba(30, 111, 217, 0.08);
    }

    /* Active state in overlay */
    .side-nav.is-open.light .nav-link.active {
        background: #0B60E0;
    }

    .side-nav.is-open.dark .nav-link.active {
        background: #F4F4F4;
    }

    /* Text color overrides in white overlay */
    .side-nav.is-open.dark .nav-text {
        color: #2D2D2D;
    }

    .side-nav.is-open.dark .brand-text {
        color: #2D2D2D;
    }

    /* Contact cards: 2 columns on tablet */
    .contact-cards {
        grid-template-columns: repeat(2, 1fr);
    }
}

/* ============================================================
   MOBILE ONLY (≤768px)
   ============================================================ */

@media (max-width: 768px) {
    /* Corner radius scaled to 50% of its desktop value, matching images shrinking down
       to the mobile breakpoint. */
    .about-image {
        border-radius: 10px; /* 20px desktop */
    }

    .project-image {
        border-radius: 6px; /* 12px desktop */
    }

    .contact-card {
        border-radius: 6px; /* 12px desktop */
    }

    /* Contact cards: single column on mobile */
    .contact-cards {
        grid-template-columns: 1fr;
    }
}