/* ============================================
   GRACE FU PORTFOLIO - STYLES
   Editorial · Cream & Blush Pink
   ============================================ */

/* CSS Variables */
:root {
    /* Colors — Cream / Blush / Espresso */
    --bg-primary: #EEEBE7;
    --bg-secondary: #E5E1DC;
    --bg-dark: #442F2A;
    --bg-dark-card: #5A3F39;
    --accent-pink: #F4C9D6;
    --accent-pink-dark: #E8A6B8;
    --text-primary: #3E2723;
    --text-secondary: #6D5D53;
    --text-muted: #9C8E84;
    --text-light: #EEEBE7;
    --text-light-muted: #C4B8AE;
    --white: #FFFFFF;

    /* Typography
       --font-script and --font-handwritten previously pointed at Dancing
       Script and Caveat. Both were replaced with DM Sans for readability —
       cursive scripts read fine at large display sizes but get squinty at
       the smaller sizes we use for subtitles, year tags, hints, etc. The
       prior italic feel is preserved by using `font-style: italic` in the
       relevant rules. */
    --font-display: 'Playfair Display', Georgia, serif;
    --font-script: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif;
    --font-body: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif;
    --font-handwritten: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif;

    /* Spacing — slightly tighter than before to compensate for the larger
       base font size (see html font-size below) so vertical rhythm stays
       balanced rather than ballooning. */
    --section-padding: clamp(3.25rem, 8vw, 6.5rem);
    --container-max: 1200px;
    --container-padding: clamp(1rem, 5vw, 3rem);

    /* Transitions */
    --transition-fast: 0.2s ease;
    --transition-medium: 0.4s ease;
    --transition-slow: 0.6s ease;

    /* Shadows */
    --shadow-sm: 0 2px 8px rgba(68, 47, 42, 0.08);
    --shadow-md: 0 4px 20px rgba(68, 47, 42, 0.12);
    --shadow-lg: 0 8px 40px rgba(68, 47, 42, 0.16);
    --shadow-hover: 0 12px 48px rgba(68, 47, 42, 0.22);
}

/* Reset & Base */
*, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html {
    scroll-behavior: smooth;
    /* Bumped from the 16px browser default — every rem-based size in the
       stylesheet (clamp lower bounds, paddings, etc.) scales by the same
       ~9% so the whole site reads slightly bigger without per-element edits. */
    font-size: 17.5px;
}

body {
    font-family: var(--font-body);
    background-color: var(--bg-primary);
    color: var(--text-primary);
    line-height: 1.6;
    overflow-x: hidden;
}

/* Subtle film grain overlay */
body::after {
    content: '';
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    z-index: 9998;
    opacity: 0.035;
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 512 512' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.7' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    background-repeat: repeat;
    background-size: 256px 256px;
}

/* Selection */
::selection {
    background-color: var(--accent-pink);
    color: var(--text-primary);
}

/* ============================================
   BACK TO TOP BUTTON
   ============================================ */
.back-to-top {
    position: fixed;
    bottom: 2rem;
    right: 2rem;
    width: 48px;
    height: 48px;
    background-color: var(--white);
    border: 1px solid rgba(42, 37, 32, 0.12);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    opacity: 0;
    visibility: hidden;
    transform: translateY(20px);
    transition: all var(--transition-medium);
    z-index: 100;
    box-shadow: var(--shadow-md);
    color: var(--text-primary);
}

.back-to-top.visible {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.back-to-top:hover {
    background-color: var(--accent-pink);
    transform: translateY(-4px);
    box-shadow: var(--shadow-lg);
}

/* ============================================
   NAVIGATION
   ============================================ */
.navbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 1000;
    padding: 1rem 0;
    transition: all var(--transition-medium);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-20px);
}

.navbar.visible {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    background-color: rgba(238, 235, 231, 0.88);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-bottom: 1px solid rgba(62, 39, 35, 0.08);
}

.nav-container {
    max-width: var(--container-max);
    margin: 0 auto;
    padding: 0 var(--container-padding);
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.nav-logo {
    font-family: var(--font-display);
    font-size: 1.25rem;
    font-weight: 600;
    color: var(--text-primary);
    text-decoration: none;
    letter-spacing: -0.02em;
}

.nav-links {
    display: flex;
    list-style: none;
    gap: 2rem;
}

.nav-links a {
    font-family: var(--font-body);
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--text-secondary);
    text-decoration: none;
    letter-spacing: 0.02em;
    transition: color var(--transition-fast);
    position: relative;
}

.nav-links a::after {
    content: '';
    position: absolute;
    bottom: -4px;
    left: 0;
    width: 0;
    height: 2px;
    background-color: var(--accent-pink);
    transition: width var(--transition-fast);
}

.nav-links a:hover {
    color: var(--text-primary);
}

.nav-links a:hover::after {
    width: 100%;
}

/* Mobile Nav Toggle */
.nav-toggle {
    display: none;
    flex-direction: column;
    gap: 5px;
    background: none;
    border: none;
    cursor: pointer;
    padding: 0.5rem;
}

.nav-toggle span {
    display: block;
    width: 24px;
    height: 2px;
    background-color: var(--text-primary);
    transition: all var(--transition-fast);
}

/* ============================================
   HERO SECTION
   ============================================ */
.hero {
    position: relative;
    width: 100%;
    overflow: hidden;
    background-color: #EEEBE7;
}

/* Positioning context for the banner's paint blobs + collage decorations,
   so they anchor to the image and never intrude into the cream band. */
.hero-inner {
    position: relative;
    width: 100%;
}

/* Hero banner image — main hero element, full width, cinematic entrance */
.hero-banner {
    display: block;
    width: 100%;
    height: auto;
    position: relative;
    z-index: 1;
    opacity: 0;
    transform: scale(0.97);
    transition: opacity 1s cubic-bezier(0.25, 0.46, 0.45, 0.94),
                transform 1s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.hero-banner.is-visible {
    opacity: 1;
    transform: scale(1);
}


/* Spotify Widget */
.spotify-widget {
    position: absolute;
    top: clamp(0.75rem, 2vh, 1.5rem);
    left: clamp(1rem, 3vw, 2rem);
    z-index: 30;
    opacity: 0;
    transition: opacity 0.4s ease;
}

.spotify-widget.is-visible {
    opacity: 1;
}

.spotify-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-family: var(--font-body);
    font-size: 0.75rem;
    font-weight: 500;
    letter-spacing: 0.04em;
    color: var(--text-primary);
    background-color: rgba(238, 235, 231, 0.88);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border: 1px solid rgba(62, 39, 35, 0.08);
    padding: 0.45rem 0.85rem;
    cursor: pointer;
    box-shadow: var(--shadow-sm);
    transition: all var(--transition-fast);
}

.spotify-btn:hover {
    box-shadow: var(--shadow-md);
    background-color: rgba(238, 235, 231, 0.95);
}

.spotify-icon {
    flex-shrink: 0;
}

.spotify-player {
    position: relative;
    width: 280px;
    max-height: 0;
    overflow: hidden;
    opacity: 0;
    margin-top: 0.5rem;
    transition: max-height 0.4s cubic-bezier(0.34, 1.56, 0.64, 1),
                opacity 0.3s ease;
}

.spotify-player.open {
    max-height: 120px;
    opacity: 1;
}

.spotify-close {
    position: absolute;
    top: 4px;
    right: 4px;
    width: 22px;
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(238, 235, 231, 0.9);
    border: none;
    cursor: pointer;
    color: var(--text-secondary);
    z-index: 5;
    transition: color var(--transition-fast);
}

.spotify-close:hover {
    color: var(--text-primary);
}

@media (max-width: 480px) {
    .spotify-widget {
        display: none;
    }
}

/* Running Ticker Bar — clamp lower bounds dropped so the bar actually
   shrinks on phones. Border thickness and letter-spacing also scale
   with viewport so the bar reads as "little" instead of chunky. */
.hero-ticker {
    position: relative;
    background-color: transparent;
    border-top: clamp(1px, 0.2vw, 2px) solid #6B504F;
    border-bottom: clamp(1px, 0.2vw, 2px) solid #6B504F;
    overflow: hidden;
    white-space: nowrap;
    padding: clamp(0.22rem, 0.9vw, 0.7rem) 0;
}

.ticker-track {
    display: inline-flex;
    animation: ticker 25s linear infinite;
}

.ticker-track span {
    font-family: var(--font-body);
    font-size: clamp(0.55rem, 1vw, 0.85rem);
    font-weight: 600;
    letter-spacing: clamp(0.04em, 0.5vw, 0.08em);
    text-transform: uppercase;
    color: #6B504F;
    padding-right: 0.25rem;
}

@keyframes ticker {
    0% {
        transform: translateX(0);
    }
    100% {
        transform: translateX(-33.333%);
    }
}

/* Scroll hint — top right of hero */
.scroll-hint {
    position: absolute;
    top: clamp(1rem, 2.5vw, 2rem);
    right: clamp(1rem, 2.5vw, 2rem);
    z-index: 20;
    margin: 0;
    padding: 0;
    font-family: var(--font-handwritten);
    font-size: clamp(0.95rem, 1.3vw, 1.15rem);
    font-weight: 500;
    letter-spacing: 0.05em;
    color: var(--text-muted);
    animation: gentle-bounce 1.5s ease-in-out infinite;
    opacity: 0;
    transition: opacity 0.4s ease;
}

.scroll-hint.is-visible {
    opacity: 1;
}

@keyframes gentle-bounce {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-4px); }
}

/* ============================================
   SECTION BASE STYLES
   ============================================ */
section {
    padding: var(--section-padding) 0;
}

.container {
    max-width: var(--container-max);
    margin: 0 auto;
    padding: 0 var(--container-padding);
}

.section-label {
    font-family: var(--font-body);
    font-size: 0.75rem;
    font-weight: 500;
    letter-spacing: 0.25em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-bottom: 2rem;
}

/* ============================================
   ABOUT SECTION — TIGHT, RECRUITER-FRIENDLY
   ============================================ */
.about {
    position: relative;
    background-color: var(--bg-primary);
    /* about-bg.jpg has subtle stripes — layered cream overlay washes most
       of it out so the stripes read as a faint texture, not a pattern. */
    background-image:
        linear-gradient(rgba(238, 235, 231, 0.65), rgba(238, 235, 231, 0.65)),
        url('about-bg.jpg');
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    padding: clamp(3rem, 6vw, 6.5rem) clamp(1.5rem, 5vw, 4rem);
    text-align: center;
    overflow: hidden;
    /* Section grows tall enough on big screens that 3 large flowers
       stacked vertically fit without clipping. Content stays vertically
       centered via the flex rules below. */
    min-height: clamp(700px, 60vw, 1200px);
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.about .about-label {
    font-family: var(--font-body);
    font-size: 0.75rem;
    font-weight: 500;
    letter-spacing: 0.25em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin: 0 0 1.4rem;
}

.about-greeting {
    font-family: var(--font-script);
    font-style: italic;
    font-weight: 500;
    font-size: clamp(1.7rem, 4vw, 3.6rem);
    color: var(--text-primary);
    margin: 0;
    line-height: 1;
}

.about-id {
    margin: clamp(0.5rem, 1vw, 1rem) auto clamp(1.1rem, 1.8vw, 1.8rem);
    max-width: 820px;
}

.about-id-line {
    font-family: var(--font-body);
    font-size: clamp(0.88rem, 1.2vw, 1.2rem);
    font-weight: 500;
    color: var(--text-secondary);
    margin: 0;
    line-height: 1.4;
}

.about-id-seek {
    font-family: var(--font-body);
    font-size: clamp(0.8rem, 1.05vw, 1.08rem);
    color: var(--text-muted);
    margin: 0.4rem 0 0;
    letter-spacing: 0.04em;
}

.about-id-seek strong {
    color: var(--text-primary);
    font-weight: 600;
    background: linear-gradient(transparent 60%, rgba(244, 201, 214, 0.55) 60%);
    padding: 0 0.2rem;
}

.about-tagline {
    font-family: 'Playfair Display', serif;
    font-size: clamp(1.25rem, 2.5vw, 2.5rem);
    font-weight: 500;
    color: var(--text-primary);
    line-height: 1.3;
    margin: 0 auto clamp(2rem, 3.5vw, 3.5rem);
    max-width: 820px;
}

.about-tagline em {
    font-style: italic;
    color: var(--accent-pink-dark);
    display: block;
    margin-top: 0.2rem;
}

/* Decorative flowers flanking the section. The wrapper handles entrance
   (translate + opacity via .about-fade), the inner <img> handles the
   continuous scroll-driven rotation via --spin set by JS. Splitting the
   transforms across two elements avoids fighting between entrance and spin. */
.about-flower {
    position: absolute;
    /* Big on large screens — caps around 360px on the biggest viewports */
    width: clamp(80px, 19vw, 360px);
    height: clamp(80px, 19vw, 360px);
    pointer-events: none;
    z-index: 0;
}

.about-flower-img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    /* Per-flower scale lets us bump up the light-pink cherry blossom
       (f2.png), whose subject fills less of its canvas than the others. */
    transform: rotate(var(--spin, 0deg)) scale(var(--flower-scale, 1));
    transition: transform 0.06s linear;
    filter: drop-shadow(2px 3px 4px rgba(80, 50, 30, 0.10));
}

.about-flower-img[src="f2.png"] {
    --flower-scale: 1.55;
}

/* Vertical line down each side, evenly spaced top → middle → bottom.
   Same horizontal anchor for all 3 on a side so they read as a column. */
.about-flower-l1 { top: 4%;  left: 1.5%; }
.about-flower-l2 { top: 38%; left: 1.5%; }
.about-flower-l3 { top: 72%; left: 1.5%; }
.about-flower-r1 { top: 4%;  right: 1.5%; }
.about-flower-r2 { top: 38%; right: 1.5%; }
.about-flower-r3 { top: 72%; right: 1.5%; }

/* Phones — too cramped on the sides; hide flowers entirely */
@media (max-width: 480px) {
    .about-flower { display: none; }
}

/* Three-card row */
.about-cards {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: stretch;
    gap: clamp(1rem, 2.5vw, 2.4rem);
    max-width: 1300px;
    margin: 0 auto;
}

.about-card {
    position: relative;
    flex: 1 1 260px;
    max-width: 380px;
    min-height: clamp(220px, 22vw, 280px);
    padding: clamp(1.4rem, 2vw, 2rem) clamp(1.2rem, 1.8vw, 1.8rem) clamp(1.2rem, 1.6vw, 1.6rem);
    background-color: var(--card-bg, #FAEDE8);
    box-shadow: 3px 5px 16px rgba(80, 50, 30, 0.16);
    text-align: left;
    transform: rotate(var(--rot, 0deg));
}

.about-card-pink  { --card-bg: #FAD2DC; --card-accent: #C4687A; }
.about-card-sage  { --card-bg: #D8E5C8; --card-accent: #6E8460; }
.about-card-cream { --card-bg: #F8E8D0; --card-accent: #B07A48; }

/* Washi tape strip across the top of each card */
.about-card-tape {
    position: absolute;
    top: -10px;
    left: 50%;
    width: clamp(72px, 7vw, 110px);
    height: clamp(16px, 1.5vw, 24px);
    transform: translateX(-50%) rotate(var(--tape-rot, -3deg));
    background-color: rgba(255, 255, 255, 0.55);
    background-image: repeating-linear-gradient(
        90deg,
        rgba(255, 255, 255, 0.6) 0 5px,
        transparent 5px 11px);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
    pointer-events: none;
}

.about-card-pink  .about-card-tape { background-color: rgba(244, 201, 214, 0.85); --tape-rot: -4deg; }
.about-card-sage  .about-card-tape { background-color: rgba(168, 184, 154, 0.7); --tape-rot: 3deg; }
.about-card-cream .about-card-tape { background-color: rgba(243, 201, 181, 0.85); --tape-rot: -2deg; }

.about-card-title {
    font-family: var(--font-handwritten);
    font-style: italic;
    font-size: clamp(1.15rem, 1.9vw, 1.7rem);
    font-weight: 600;
    color: var(--card-accent);
    margin: 0 0 clamp(0.6rem, 1vw, 1rem);
    line-height: 1.05;
    letter-spacing: 0.01em;
}

.about-card-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: clamp(0.5rem, 0.8vw, 0.8rem);
}

.about-card-list li {
    font-family: var(--font-body);
    font-size: clamp(0.85rem, 1.05vw, 1.1rem);
    font-weight: 500;
    color: var(--text-primary);
    line-height: 1.4;
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
}

.about-card-icon {
    flex-shrink: 0;
    font-size: clamp(0.95rem, 1.15vw, 1.2rem);
    line-height: 1.2;
}

/* Skills card uses inline pill chips instead of a list column */
.about-card-chips {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 0.45rem;
}

.about-card-chips li {
    font-family: var(--font-body);
    font-size: clamp(0.7rem, 0.88vw, 0.95rem);
    font-weight: 600;
    letter-spacing: 0.04em;
    color: var(--text-primary);
    background-color: rgba(255, 255, 255, 0.6);
    padding: clamp(0.28rem, 0.45vw, 0.5rem) clamp(0.5rem, 0.8vw, 0.85rem);
}

.about-card-links a {
    color: var(--text-primary);
    text-decoration: none;
    background: linear-gradient(transparent 70%, rgba(255, 255, 255, 0.65) 70%);
    padding: 0 0.18rem;
    transition: color 0.2s ease;
}
.about-card-links a:hover {
    color: var(--card-accent);
}

/* Scroll-in fade — same .about-fade hook as the existing observer */
.about-fade {
    opacity: 0;
    transform: translateY(14px) rotate(var(--rot, 0deg));
    transition: opacity 0.6s ease,
                transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.about-fade.visible {
    opacity: 1;
    transform: translateY(0) rotate(var(--rot, 0deg));
}

/* Stagger entrance — greeting first, then identity, tagline, then cards */
.about-greeting.visible { transition-delay: 0s; }
.about-id.visible       { transition-delay: 0.15s; }
.about-tagline.visible  { transition-delay: 0.3s; }
.about-card-pink.visible  { transition-delay: 0.5s; }
.about-card-sage.visible  { transition-delay: 0.6s; }
.about-card-cream.visible { transition-delay: 0.7s; }

/* Continuous gentle bob — kicks in after the entrance stagger has finished
   so the cards feel alive without competing with the scroll-in animation.
   Different durations + directions per card so they never sync. */
.about-card.visible {
    animation: about-card-bob 4.4s ease-in-out 1.6s infinite alternate;
}
.about-card-pink.visible  { animation-duration: 4.2s; }
.about-card-sage.visible  { animation-duration: 5.1s; animation-direction: alternate-reverse; }
.about-card-cream.visible { animation-duration: 4.7s; }

@keyframes about-card-bob {
    from { transform: translateY(0) rotate(var(--rot, 0deg)); }
    to   { transform: translateY(-6px) rotate(var(--rot, 0deg)); }
}

/* Hover — lift up, scale slightly, deeper shadow. Animation pauses so the
   bob doesn't fight the lift. */
.about-card:hover {
    animation-play-state: paused;
    transform: translateY(-10px) rotate(var(--rot, 0deg)) scale(1.03);
    box-shadow: 5px 10px 26px rgba(80, 50, 30, 0.28);
    z-index: 5;
    transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1),
                box-shadow 0.3s ease;
}

/* Tiny wiggle on the greeting when its entrance lands */
.about-greeting.visible {
    animation: about-greeting-wiggle 0.7s cubic-bezier(0.34, 1.56, 0.64, 1) 0.2s 1 forwards;
}
@keyframes about-greeting-wiggle {
    0%   { transform: translateY(0) rotate(0deg); }
    35%  { transform: translateY(0) rotate(-3deg); }
    70%  { transform: translateY(0) rotate(2deg); }
    100% { transform: translateY(0) rotate(0deg); }
}

/* Mobile — stack cards, drop heavy rotations */
@media (max-width: 768px) {
    .about-cards {
        flex-direction: column;
        align-items: center;
        gap: 1.25rem;
    }
    .about-card {
        max-width: 380px;
        width: 100%;
        min-height: auto;
        --rot: 0deg !important;
    }
    .about-tagline br { display: none; }
}

/* Tiny screens — tighter padding + slightly smaller hero text */
@media (max-width: 480px) {
    .about {
        padding: 2.5rem 1rem;
    }
    .about-greeting {
        font-size: 1.85rem;
    }
    .about-tagline {
        font-size: 1.15rem;
        margin-bottom: 1.75rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .about-fade,
    .about-fade.visible {
        opacity: 1 !important;
        transform: rotate(var(--rot, 0deg)) !important;
        transition: none !important;
    }
}

/* ============================================
   EXPERIENCE — BOTANICAL VINE TIMELINE
   ============================================ */
.vine-section {
    position: relative;
    background-color: var(--bg-dark);
    color: var(--text-light);
    /* Tighter than 200vh — staggered cards pack into roughly half the
       previous vertical footprint, so the section can be much shorter.
       Bumped for the 7th card (idx 6) so the last flower/card has room
       to bloom above the section's bottom edge. */
    height: 170vh;
    overflow: hidden;
    padding: 0;
}

/* Subtle paper-grain noise texture overlay */
.vine-paper {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    opacity: 0.04;
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.7' numOctaves='3' stitchTiles='stitch' seed='7'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    background-size: 256px 256px;
}

.vine-header {
    position: absolute;
    top: clamp(3rem, 6vw, 5rem);
    left: clamp(8%, 12vw, 14%);
    z-index: 5;
}

.vine-label {
    color: var(--text-light-muted);
    margin-bottom: 0.4rem;
}

.vine-subtitle {
    font-family: var(--font-script);
    font-style: italic;
    font-size: clamp(1rem, 1.5vw, 1.4rem);
    color: var(--text-light-muted);
    margin: 0;
    opacity: 0.85;
}

/* The vine SVG fills the section. Strokes use non-scaling-stroke so
   they render at their declared px width regardless of stretch.
   Top offset clears the .vine-header label so the vine doesn't start
   directly behind it. Height shrinks to match so the vine still ends
   at the section's bottom edge. */
.vine-trunk {
    position: absolute;
    top: clamp(4rem, 7vw, 7rem);
    left: 0;
    width: 100%;
    height: calc(100% - clamp(4rem, 7vw, 7rem));
    pointer-events: none;
    z-index: 1;
    overflow: visible;
}

.vine-main {
    /* Stroke length is set via JS once the path is in DOM. Default
       hides the stroke until scroll progress kicks in. */
    stroke-dasharray: 1000;
    stroke-dashoffset: 1000;
}

.vine-branch {
    stroke-dasharray: 60;
    stroke-dashoffset: 60;
    transition: stroke-dashoffset 0.6s cubic-bezier(0.65, 0, 0.35, 1);
}

.vine-branch.is-drawn {
    stroke-dashoffset: 0;
}

.vine-sprout {
    opacity: 0;
    transition: opacity 0.5s ease;
}

.vine-sprout.is-shown {
    opacity: 0.9;
}

/* Flowers — each its own SVG, undistorted, centered on its anchor */
.vine-flower {
    position: absolute;
    width: clamp(52px, 5vw, 86px);
    height: clamp(52px, 5vw, 86px);
    pointer-events: none;
    z-index: 2;
    /* Position each flower so the SVG center aligns with the branch tip. */
    transform: translate(-50%, -50%);
}

/* Flower positions are now set in JS (positionFlowers) by reading the
   actual rendered branch endpoints — preserveAspectRatio="meet" can shift
   things based on viewport aspect, so hardcoded percentages would drift. */

/* The flower-content group is the container the sway animation rotates.
   transform-box: fill-box makes the rotation pivot around the flower's
   visual center instead of the SVG viewport origin. */
.flower-content {
    transform-box: fill-box;
    transform-origin: center;
}

/* Petal bloom — opacity-only so it works regardless of how the browser
   handles transform-box on nested SVG groups. Stagger comes from the
   transition-delay rules below. */
.petal {
    opacity: 0;
    transition: opacity 0.45s ease;
}

.vine-flower.is-bloomed .petal {
    opacity: 1;
}

/* Per-petal stagger — petals open one at a time, 60ms apart */
.vine-flower.is-bloomed .petal[data-p="0"]  { transition-delay: 0ms; }
.vine-flower.is-bloomed .petal[data-p="1"]  { transition-delay: 60ms; }
.vine-flower.is-bloomed .petal[data-p="2"]  { transition-delay: 120ms; }
.vine-flower.is-bloomed .petal[data-p="3"]  { transition-delay: 180ms; }
.vine-flower.is-bloomed .petal[data-p="4"]  { transition-delay: 240ms; }
.vine-flower.is-bloomed .petal[data-p="5"]  { transition-delay: 300ms; }
.vine-flower.is-bloomed .petal[data-p="6"]  { transition-delay: 360ms; }
.vine-flower.is-bloomed .petal[data-p="7"]  { transition-delay: 420ms; }
.vine-flower.is-bloomed .petal[data-p="8"]  { transition-delay: 480ms; }
.vine-flower.is-bloomed .petal[data-p="9"]  { transition-delay: 540ms; }
.vine-flower.is-bloomed .petal[data-p="10"] { transition-delay: 600ms; }
.vine-flower.is-bloomed .petal[data-p="11"] { transition-delay: 660ms; }
.vine-flower.is-bloomed .petal[data-p="12"] { transition-delay: 720ms; }

/* Continuous gentle sway after blooming. Different durations per flower
   so they never sway in sync. transform only — no layout reflow. */
.vine-flower.is-bloomed .flower-content {
    animation: flower-sway 3.4s ease-in-out infinite alternate;
    transform-origin: center;
}
.vine-flower[data-idx="0"].is-bloomed .flower-content { animation-duration: 3.2s; }
.vine-flower[data-idx="1"].is-bloomed .flower-content { animation-duration: 4.1s; animation-direction: alternate-reverse; }
.vine-flower[data-idx="2"].is-bloomed .flower-content { animation-duration: 3.8s; }
.vine-flower[data-idx="3"].is-bloomed .flower-content { animation-duration: 3.5s; animation-direction: alternate-reverse; }
.vine-flower[data-idx="4"].is-bloomed .flower-content { animation-duration: 4.3s; }
.vine-flower[data-idx="5"].is-bloomed .flower-content { animation-duration: 3.6s; animation-direction: alternate-reverse; }
.vine-flower[data-idx="6"].is-bloomed .flower-content { animation-duration: 3.9s; }

/* Per SVG2 spec, a CSS transform on an SVG element REPLACES that element's
   `transform=` attribute (it doesn't compose). Each flower-content has
   `transform="translate(35 35)"` to center its petals at viewBox (35, 35).
   The keyframes therefore have to re-include that translate, otherwise the
   sway wipes the centering and petals render around viewBox (0, 0) — outside
   the visible viewBox, leaving only the lower-right quadrant visible. */
@keyframes flower-sway {
    from { transform: translate(35px, 35px) rotate(-1deg); }
    to   { transform: translate(35px, 35px) rotate(1deg); }
}

/* Hover — brighten + sway slightly more actively */
.vine-flower {
    pointer-events: auto;
    transition: filter 0.25s ease;
}
.vine-flower:hover {
    filter: brightness(1.15);
}

/* Cards — pinned at irregular vertical positions and slight rotations */
.vine-card {
    position: absolute;
    z-index: 4;
    /* Narrower than before so two staggered columns fit on 1024–1280
       screens without overlapping each other. */
    width: clamp(330px, 32vw, 540px);
    padding: clamp(1.1rem, 1.5vw, 1.6rem) clamp(1.3rem, 1.7vw, 1.9rem);
    /* Slightly tinted + backdrop blur — branches that pass behind a card
       (the long ones for cards 1/3/5 cross over the near-column cards on
       big screens) get softly diffused instead of cutting through the card. */
    background-color: rgba(255, 255, 255, 0.10);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border: 1px solid rgba(255, 255, 255, 0.10);
    border-left: 3px solid var(--card-color, var(--text-light-muted));
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.20);
    color: var(--text-light);
    opacity: 0;
    transform: translateX(20px) rotate(var(--rot, 0deg));
    transition: opacity 0.5s ease,
                transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.vine-card.is-bloomed {
    opacity: 1;
    transform: translateX(0) rotate(var(--rot, 0deg));
}

/* Per-card position + flower color tie-back. Vertical positions match
   the flower triggers above; horizontal nudges keep things asymmetric. */
/* Card positions are set in JS (positionCards) relative to each flower.
   These rules just assign each card its accent color. */
.vine-card[data-idx="0"] { --card-color: #F4C9D6; }
.vine-card[data-idx="1"] { --card-color: #C9A84C; }
.vine-card[data-idx="2"] { --card-color: #C4B0D4; }
.vine-card[data-idx="3"] { --card-color: #E8A6B8; }
.vine-card[data-idx="4"] { --card-color: #F0C4A0; }
.vine-card[data-idx="5"] { --card-color: #A8C4D4; }
.vine-card[data-idx="6"] { --card-color: #E89A8A; }

.card-annotation {
    display: block;
    font-family: var(--font-handwritten);
    font-size: clamp(0.85rem, 1vw, 1.05rem);
    color: var(--text-light-muted);
    letter-spacing: 0.04em;
    margin-bottom: 0.35rem;
    opacity: 0.85;
}

.card-role {
    font-family: 'Playfair Display', serif;
    font-style: italic;
    font-size: clamp(1.1rem, 1.7vw, 1.55rem);
    font-weight: 500;
    color: var(--text-light);
    margin: 0 0 0.35rem;
    line-height: 1.25;
}

.card-company {
    font-family: var(--font-script);
    font-style: italic;
    font-weight: 500;
    font-size: clamp(0.95rem, 1.3vw, 1.2rem);
    color: var(--card-color);
    margin: 0 0 0.4rem;
    line-height: 1.2;
}

.card-date {
    font-family: var(--font-body);
    font-size: clamp(0.62rem, 0.78vw, 0.8rem);
    font-weight: 500;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--text-light-muted);
    margin: 0 0 0.6rem;
}

.card-rule {
    display: block;
    width: clamp(40px, 4vw, 60px);
    height: 2px;
    background-color: var(--card-color);
    opacity: 0.5;
    margin: 0 0 0.6rem;
}

.card-desc {
    font-family: var(--font-body);
    font-size: clamp(0.85rem, 1.05vw, 1.05rem);
    line-height: 1.5;
    color: var(--text-light-muted);
    margin: 0;
}

.card-doodle {
    position: absolute;
    top: 1.25rem;
    right: 1.25rem;
    width: 26px;
    height: 14px;
    color: var(--card-color);
    opacity: 0.85;
}

/* Right-side vine — hidden by default. Only appears on really big
   screens, where far-column cards (1, 3, 5) attach to it instead of
   the left vine, so the long left-vine branches don't have to cross
   over the near-column cards above them. */
.vine-trunk-right {
    display: none;
}

@media (min-width: 1400px) {
    .vine-trunk-right {
        display: block;
    }

    /* Hide the long left-vine branches that would otherwise reach
       across to the far cards. The right vine handles those now. */
    .vine-trunk:not(.vine-trunk-right) .vine-branch[data-idx="1"],
    .vine-trunk:not(.vine-trunk-right) .vine-branch[data-idx="3"],
    .vine-trunk:not(.vine-trunk-right) .vine-branch[data-idx="5"] {
        display: none;
    }

    /* Narrower cards in dual-vine mode. In single-vine staggering both
       cards extend rightward from their flowers, but here near and far
       cards extend toward each other (near rightward from x≈22%, far
       leftward from x≈78%). With the original 32vw card width that
       leaves no room — the columns overlap by 100–200px at typical
       1400–1900px viewports. Slimming to ~24vw keeps a comfortable
       gap between columns; the 540px cap matches the original max so
       cards grow back to full width once the viewport (>1750px or so)
       has room for it again. */
    .vine-card {
        width: clamp(300px, 24vw, 540px);
    }

    /* Far-column cards now slide in from the left (since their flower
       is on the right side of the card on big screens). */
    .vine-card[data-idx="1"],
    .vine-card[data-idx="3"],
    .vine-card[data-idx="5"] {
        transform: translateX(-20px) rotate(var(--rot, 0deg));
    }

    .vine-card[data-idx="1"].is-bloomed,
    .vine-card[data-idx="3"].is-bloomed,
    .vine-card[data-idx="5"].is-bloomed {
        transform: translateX(0) rotate(var(--rot, 0deg));
    }
}

/* Narrow — hide the vine, stack cards in a single column.
   Breakpoint pushed up to 1000px because below that the staggered
   layout's two columns overlap horizontally (left card's right edge
   reaches into the right column's flower position when the card's
   330px min-width can no longer fit alongside the right column). */
@media (max-width: 1000px) {
    .vine-section {
        height: auto;
        padding: 4rem 0 5rem;
    }

    .vine-trunk,
    .vine-flower {
        display: none;
    }

    .vine-header {
        position: relative;
        top: auto;
        left: auto;
        text-align: center;
        margin-bottom: 2.5rem;
        padding: 0 1.5rem;
    }

    .vine-card {
        position: relative;
        top: auto !important;
        left: auto !important;
        width: min(92vw, 480px);
        margin: 1.25rem auto;
        opacity: 0;
        transform: translateY(20px);
        transition: opacity 0.5s ease, transform 0.5s ease;
    }

    .vine-card.is-bloomed {
        opacity: 1;
        transform: translateY(0);
    }

    .card-doodle {
        top: 1rem;
        right: 1rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .vine-main {
        stroke-dashoffset: 0 !important;
    }
    .vine-branch {
        stroke-dashoffset: 0 !important;
        transition: none !important;
    }
    .vine-sprout {
        opacity: 0.9 !important;
    }
    .petal {
        transform: none !important;
        opacity: 1 !important;
        transition: none !important;
    }
    .vine-flower.is-bloomed .flower-content {
        animation: none !important;
    }
    .vine-card {
        opacity: 1 !important;
        transform: rotate(var(--rot, 0deg)) !important;
        transition: none !important;
    }
}

/* ============================================
   PROJECTS SECTION
   ============================================ */
.projects {
    background-color: var(--bg-primary);
}

/* Screen-reader-only text — used to give the repeated "code ↗" links
   distinct accessible names without repeating the project name visually. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.nowrap {
    white-space: nowrap;
}

/* The botanical sprite is geometry only — never rendered directly. */
.hb-defs {
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
}

.filter-bar {
    display: flex;
    gap: 0.6rem;
    margin-bottom: 0.5rem;
    flex-wrap: wrap;
}

.filter-btn {
    font-family: var(--font-body);
    font-size: 0.8125rem;
    font-weight: 500;
    padding: 0.55rem 1.1rem;
    border: 1px solid rgba(42, 37, 32, 0.15);
    border-radius: 0;
    background-color: transparent;
    color: var(--text-secondary);
    cursor: pointer;
    transition: all var(--transition-fast);
}

.filter-btn:hover {
    background-color: var(--accent-pink);
    border-color: var(--accent-pink);
    color: var(--text-primary);
}

.filter-btn.active {
    background-color: var(--bg-dark);
    border-color: var(--bg-dark);
    color: var(--text-light);
}

/* ============================================
   PROJECTS — THE SPECIMEN DRAWER

   A horizontal rail of pressed-botanical "plates" instead of a vertical
   card grid. Eight projects cost one row of height rather than four, and
   the sideways motion is the one place on the site that doesn't scroll
   down. Collapses to a plain vertical stack under 768px.

   Grid areas are used so the arrows can sit in the bottom control strip
   while staying in a sensible DOM order (prev → rail → next).
   ============================================ */
.drawer {
    display: grid;
    grid-template-columns: 1fr auto auto;
    grid-template-areas:
        "rail   rail rail"
        "hint   prev next"
        "runner runner runner";
    align-items: center;
    column-gap: 0.5rem;
    row-gap: 0.9rem;
    margin-bottom: 4.5rem;
}

.projects-rail {
    grid-area: rail;
    display: flex;
    align-items: stretch;
    gap: 1.5rem;
    overflow-x: auto;
    overflow-y: hidden;
    /* proximity, not mandatory: mandatory forces the scroller to rest on a
       snap point immediately, which on load scrolled the rail past its own
       left padding (misaligning the first plate) and pulled the scroller
       back from the true end so the last plate was unreachable. */
    scroll-snap-type: x proximity;
    scroll-behavior: smooth;
    /* Bleed past the container so the rail reads as continuing offscreen,
       while the first card still lines up with the section label. */
    margin-inline: calc(var(--container-padding) * -1);
    padding-inline: var(--container-padding);
    /* Inset the snapport to match the padding. Without this, `snap-align:
       start` aligns the first plate to the scrollport edge — i.e. scrollLeft
       72 — so the rail scrolls its own left padding away on load and the
       first plate stops lining up with the section label. */
    scroll-padding-inline: var(--container-padding);
    padding-block: 2.25rem 1.6rem;
    /* Hidden scrollbar — the runner line below is the position indicator. */
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.projects-rail::-webkit-scrollbar {
    display: none;
}

.projects-rail:focus-visible {
    outline: 2px solid var(--accent-pink-dark);
    outline-offset: 4px;
}

@media (hover: hover) and (pointer: fine) {
    .projects-rail {
        cursor: grab;
    }
    .projects-rail.is-dragging {
        cursor: grabbing;
        scroll-behavior: auto;
        scroll-snap-type: none;
        user-select: none;
    }
}

/* ============================================
   SPECIMEN PLATE
   ============================================ */
.project-card {
    --rot: 0deg;
    --sprig: #A8B89A;
    position: relative;
    flex: 0 0 auto;
    width: clamp(17.5rem, 78vw, 20.5rem);
    min-height: 21rem;
    scroll-snap-align: start;
    display: flex;
    flex-direction: column;
    padding: 2.1rem 1.7rem 1.5rem;
    background-image: linear-gradient(158deg, #FCFAF7 0%, #F6F2EA 100%);
    border: 1px solid color-mix(in srgb, var(--sprig) 58%, transparent);
    border-radius: 0;
    box-shadow: 5px 6px 0 rgba(62, 39, 35, 0.06), var(--shadow-md);
    opacity: 0;
    transform: translateY(26px) rotate(var(--rot));
    transition: opacity 0.55s ease,
                transform 0.4s cubic-bezier(0.34, 1.4, 0.64, 1),
                box-shadow 0.4s cubic-bezier(0.34, 1.4, 0.64, 1),
                border-color var(--transition-fast);
}

/* Staggered reveal — the rail reveals all plates at once (they can sit
   outside the viewport horizontally), so the stagger lives in CSS. */
.project-card.visible {
    opacity: 1;
    transform: translateY(0) rotate(var(--rot));
    transition-delay: calc(var(--i, 0) * 70ms);
}

/* Pressed-specimen hues, keyed to category so the color carries information:
   filter to "Mobile" and the surviving plates read as one set. All four sit in
   the same low-chroma, mid-value range so the drawer stays editorial rather
   than novelty. Ordered least- to most-specific: a "web tools" plate takes the
   web hue, a bare "tools" plate takes slate. */
.project-card[data-tags~="tools"]   { --sprig: #94A7B0; }
.project-card[data-tags~="desktop"] { --sprig: #B8A88A; }
.project-card[data-tags~="mobile"]  { --sprig: #C9A9BC; }
.project-card[data-tags~="web"]     { --sprig: #A8B89A; }

/* Hand-placed tilt — small enough to stay tidy in a snapping rail. */
.project-card:nth-child(3n + 1) { --rot: -0.7deg; }
.project-card:nth-child(3n + 2) { --rot: 0.5deg; }
.project-card:nth-child(3n + 3) { --rot: -0.35deg; }

/* Inner mounting rule, inset like a herbarium plate's pencil frame. */
.project-card::before {
    content: '';
    position: absolute;
    inset: 7px;
    border: 1px dashed color-mix(in srgb, var(--sprig) 55%, transparent);
    pointer-events: none;
}

.project-card:hover,
.project-card:focus-within {
    transform: translateY(-9px) rotate(0deg);
    box-shadow: 9px 12px 0 rgba(62, 39, 35, 0.1), var(--shadow-hover);
    border-color: color-mix(in srgb, var(--sprig) 90%, transparent);
    z-index: 5;
}

.project-card.hidden {
    display: none;
}

.project-card.fade-out {
    opacity: 0;
    transform: scale(0.96) rotate(var(--rot));
}

/* Washi tape pinning the specimen to the plate. */
.plate-tape {
    position: absolute;
    top: -0.55rem;
    left: 50%;
    width: 4.5rem;
    height: 1.1rem;
    transform: translateX(-50%) rotate(-1.6deg);
    background-color: color-mix(in srgb, var(--sprig) 62%, var(--white));
    border-left: 1px dashed rgba(255, 255, 255, 0.6);
    border-right: 1px dashed rgba(255, 255, 255, 0.6);
    pointer-events: none;
}

/* Corner sprigs — the floral frame. One sprig symbol arcs along its own
   ↘ diagonal, so rotating it 0/90/180/270° walks the same geometry around
   all four corners. Sizes are deliberately unequal so the frame reads
   hand-arranged rather than stencilled, and each sprig is allowed to break
   the plate's dashed rule for depth. */
.plate-sprig {
    --sr: 0deg;
    color: var(--sprig);
    position: absolute;
    width: 3.3rem;
    height: 3.3rem;
    overflow: visible;
    pointer-events: none;
    transform: rotate(var(--sr));
    transition: transform 0.45s cubic-bezier(0.34, 1.4, 0.64, 1);
}

.plate-sprig-tl {
    top: -0.4rem;
    left: -0.4rem;
}

.plate-sprig-tr {
    --sr: 90deg;
    width: 2.3rem;
    height: 2.3rem;
    top: -0.25rem;
    right: -0.25rem;
}

.plate-sprig-br {
    --sr: 180deg;
    width: 2.7rem;
    height: 2.7rem;
    right: -0.3rem;
    bottom: -0.3rem;
}

.plate-sprig-bl {
    --sr: 270deg;
    width: 1.5rem;
    height: 1.5rem;
    bottom: -0.35rem;
    left: -0.35rem;
}

.project-card:hover .plate-sprig,
.project-card:focus-within .plate-sprig {
    transform: rotate(var(--sr)) scale(1.12);
}

/* ---- Plate typography ---- */
/* Inset on both sides so the catalogue number and the category stamp clear
   the two top corner sprigs. */
.plate-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 1rem;
    padding-inline: 1.5rem 1.35rem;
}

.plate-no {
    font-family: var(--font-body);
    font-size: 0.6rem;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--text-muted);
}

/* Category stamp — dashed like the old postmark, now in the header flow. */
.project-stamp {
    font-family: var(--font-body);
    font-size: 0.58rem;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--text-primary);
    background-color: var(--white);
    padding: 0.3rem 0.6rem;
    border: 1px dashed rgba(62, 39, 35, 0.45);
    white-space: nowrap;
}

.project-name {
    font-family: var(--font-display);
    font-style: italic;
    font-weight: 700;
    font-size: clamp(1.4rem, 2vw, 1.7rem);
    line-height: 1.12;
    color: var(--text-primary);
    margin: 0 0 0.5rem;
}

.plate-kind {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    font-family: var(--font-body);
    font-size: 0.625rem;
    font-weight: 600;
    letter-spacing: 0.13em;
    text-transform: uppercase;
    color: #7E8C71;
    margin: 0 0 0.9rem;
}

/* Hairline between the classification and the year, drawn to fill. */
.plate-kind::after {
    content: '';
    flex: 1;
    height: 1px;
    background-image: linear-gradient(to right, rgba(168, 184, 154, 0.75) 55%, transparent 55%);
    background-size: 5px 1px;
    order: 1;
}

.plate-year {
    order: 2;
    color: var(--text-muted);
    letter-spacing: 0.1em;
}

.project-description {
    font-family: var(--font-body);
    font-size: 0.8125rem;
    line-height: 1.62;
    color: var(--text-secondary);
    margin: 0 0 1.1rem;
    flex-grow: 1;
}

.plate-foot {
    margin-top: auto;
    padding-top: 0.85rem;
    border-top: 1px dashed rgba(62, 39, 35, 0.18);
}

.project-stack {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    margin-bottom: 0.7rem;
}

.stack-item {
    font-family: var(--font-body);
    font-size: 0.66rem;
    font-weight: 500;
    padding: 0.22rem 0.5rem;
    background-color: rgba(229, 225, 220, 0.9);
    color: var(--text-secondary);
    border-radius: 0;
}

.plate-links {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem 0.9rem;
}

.plate-link {
    font-family: var(--font-body);
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-primary);
    text-decoration: none;
    padding-bottom: 2px;
    border-bottom: 1px solid rgba(62, 39, 35, 0.3);
    transition: color var(--transition-fast), border-color var(--transition-fast);
}

.plate-link:hover,
.plate-link:focus-visible {
    color: var(--accent-pink-dark);
    border-bottom-color: var(--accent-pink-dark);
}

/* Collaboration credit / award line — small, italic, never a link. */
.plate-credit,
.plate-award {
    flex: 1 0 100%;
    font-family: var(--font-body);
    font-style: italic;
    font-size: 0.66rem;
    line-height: 1.35;
    color: var(--text-muted);
    margin: 0 0 0.1rem;
}

.plate-award {
    color: var(--accent-pink-dark);
    font-weight: 600;
    font-style: normal;
    letter-spacing: 0.03em;
}

.rail-empty {
    align-self: center;
    margin: 0 auto;
    padding: 3rem 1rem;
    font-family: var(--font-body);
    font-style: italic;
    font-size: 0.9rem;
    color: var(--text-muted);
}

.rail-empty[hidden] {
    display: none;
}

/* ============================================
   DRAWER CONTROLS — arrows, runner, hint
   ============================================ */
.rail-nav {
    width: 2.5rem;
    height: 2.5rem;
    display: grid;
    place-items: center;
    padding: 0;
    background-color: transparent;
    border: 1px solid rgba(62, 39, 35, 0.2);
    border-radius: 0;
    color: var(--text-primary);
    cursor: pointer;
    transition: background-color var(--transition-fast),
                border-color var(--transition-fast),
                color var(--transition-fast),
                opacity var(--transition-fast);
}

.rail-nav-prev { grid-area: prev; }
.rail-nav-next { grid-area: next; }

.rail-nav svg {
    width: 1.1rem;
    height: 1.1rem;
}

.rail-nav:hover:not(:disabled) {
    background-color: var(--bg-dark);
    border-color: var(--bg-dark);
    color: var(--text-light);
}

.rail-nav:disabled {
    opacity: 0.3;
    cursor: default;
}

/* The runner the drawer slides on — also the scroll position readout. */
.rail-runner {
    grid-area: runner;
    position: relative;
    height: 2px;
    background-color: rgba(62, 39, 35, 0.1);
}

.rail-runner-fill {
    position: absolute;
    inset-block: 0;
    left: 0;
    display: block;
    width: 12%;
    min-width: 2rem;
    background-color: #A8B89A;
    transition: width 0.15s linear, left 0.15s linear;
}

.rail-hint {
    grid-area: hint;
    font-family: var(--font-body);
    font-style: italic;
    font-size: 0.7rem;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    margin: 0;
}

.rail-hint kbd {
    font-family: var(--font-body);
    font-style: normal;
    font-size: 0.66rem;
    padding: 0.05rem 0.3rem;
    border: 1px solid rgba(62, 39, 35, 0.22);
    color: var(--text-secondary);
}

/* GitHub Contributions Heatmap */
.heatmap-section {
    margin-top: 4rem;
}

.heatmap-stats {
    font-family: var(--font-body);
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--text-muted);
    margin-top: -1rem;
    margin-bottom: 1.5rem;
    letter-spacing: 0.02em;
}

.heatmap-wrap {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.heatmap-grid {
    display: grid;
    grid-auto-flow: column;
    /* Row tracks clamp the same way as the column tracks set in JS so cells
       stay square at any viewport. */
    grid-template-rows: repeat(7, clamp(11px, 1.5vw, 20px));
    gap: clamp(3px, 0.4vw, 6px);
    width: 100%;
    /* Cap how wide the whole strip can spread on big screens — without this,
       space-between would scatter squares across the full container. */
    max-width: 920px;
    margin: 0 auto;
    /* Distribute leftover space as extra gap between weeks so wider screens
       fill horizontally instead of inflating cell size. */
    justify-content: space-between;
    /* visible so the cell hover tooltip can escape past the top edge of the
       grid for top-row cells without being clipped */
    overflow: visible;
    position: relative;
}

.heatmap-cell {
    aspect-ratio: 1;
    width: 100%;
    background-color: var(--bg-secondary);
    border-radius: 0;
    position: relative;
    cursor: default;
    opacity: 0;
    transform: scale(0.3);
    transition: background-color var(--transition-fast),
                opacity 0.4s cubic-bezier(0.34, 1.56, 0.64, 1),
                transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.heatmap-loading {
    grid-column: 1 / -1;
    grid-row: 1 / -1;
    text-align: center;
    padding: 2rem;
    font-family: var(--font-body);
    font-size: 0.9375rem;
    color: var(--text-muted);
}

.heatmap-loading a {
    color: var(--text-primary);
    text-decoration: underline;
}

/* Heatmap color scale — tight pink gradient + warm espresso peak */
.heatmap-level-0 { background-color: #E8CCD5; }
.heatmap-level-1 { background-color: #DEAEBC; }
.heatmap-level-2 { background-color: #D39CAE; }
.heatmap-level-3 { background-color: #C68A9F; }
.heatmap-level-4 { background-color: #7A5149; }

/* Legend */
.heatmap-legend {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-family: var(--font-body);
    font-size: 0.75rem;
    color: var(--text-muted);
    justify-content: flex-end;
}

.heatmap-legend-cell {
    width: 12px;
    height: 12px;
    display: inline-block;
}

/* Cell hover tooltip — high z-index + cell lifted to its own stacking
   context so the tooltip floats above neighboring cells, the legend,
   the GitHub CTA, and anything else nearby. */
.heatmap-cell:hover {
    outline: 1px solid var(--text-primary);
    outline-offset: 1px;
    z-index: 9999;
}

.heatmap-cell:hover::after {
    content: attr(data-tooltip);
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%);
    background-color: var(--bg-dark);
    color: var(--text-light);
    font-family: var(--font-body);
    font-size: 0.7rem;
    font-weight: 400;
    padding: 0.4rem 0.6rem;
    white-space: nowrap;
    z-index: 9999;
    pointer-events: none;
    box-shadow: 0 4px 12px rgba(46, 30, 26, 0.25);
}

/* Tiny pointer triangle below the tooltip */
.heatmap-cell:hover::before {
    content: '';
    position: absolute;
    bottom: calc(100% + 2px);
    left: 50%;
    transform: translateX(-50%);
    border: 6px solid transparent;
    border-top-color: var(--bg-dark);
    z-index: 9999;
    pointer-events: none;
}

/* Responsive: smaller gap on tablet, hide older weeks on phone */
@media (max-width: 1024px) {
    .heatmap-grid {
        gap: 2px;
    }
}

@media (max-width: 640px) {
    .heatmap-grid {
        gap: 2px;
    }

    .heatmap-cell.heatmap-cell-hidden {
        display: none;
    }

    .heatmap-legend {
        justify-content: flex-start;
    }
}

/* ============================================
   GITHUB SECTION — BIG CUTE LINK
   ============================================ */
.github-section {
    margin-top: clamp(3rem, 5vw, 4.5rem);
    text-align: center;
    display: flex;
    justify-content: center;
}

/* Big pinned card linking to GitHub — washi tape, slight rotation, lift on
   hover, arrow nudges right. Reads as a charming single CTA instead of a
   wall of repo cards. */
.github-cta {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: clamp(0.9rem, 1.5vw, 1.4rem);
    padding: clamp(1.1rem, 1.8vw, 1.6rem) clamp(1.6rem, 2.6vw, 2.4rem);
    text-decoration: none;
    color: var(--text-primary);
    background-color: #FAD2DC;
    background-image: linear-gradient(135deg,
        transparent 90%,
        rgba(120, 90, 50, 0.18) 90%,
        rgba(120, 90, 50, 0.32) 97%);
    box-shadow: 3px 6px 16px rgba(80, 50, 30, 0.20);
    transform: rotate(-1.5deg);
    transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1),
                box-shadow 0.3s ease;
    cursor: pointer;
}

.github-cta:hover {
    transform: rotate(0deg) translateY(-5px) scale(1.03);
    box-shadow: 5px 10px 24px rgba(80, 50, 30, 0.28);
}

.github-cta-tape {
    position: absolute;
    top: -10px;
    left: 50%;
    width: clamp(80px, 8vw, 120px);
    height: clamp(16px, 1.6vw, 22px);
    transform: translateX(-50%) rotate(-4deg);
    background-color: rgba(232, 166, 184, 0.85);
    background-image: repeating-linear-gradient(
        90deg,
        rgba(255, 255, 255, 0.45) 0 5px,
        transparent 5px 11px);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
    pointer-events: none;
}

.github-cta-icon {
    flex-shrink: 0;
    width: clamp(36px, 3.5vw, 56px);
    height: clamp(36px, 3.5vw, 56px);
    color: var(--text-primary);
}

.github-cta-label {
    font-family: var(--font-handwritten);
    font-style: italic;
    font-size: clamp(1.1rem, 1.8vw, 1.6rem);
    font-weight: 600;
    color: var(--text-primary);
    line-height: 1.05;
    letter-spacing: 0.01em;
}

.github-cta-handle {
    font-family: var(--font-script);
    font-style: italic;
    font-size: clamp(0.92rem, 1.3vw, 1.15rem);
    color: #C4687A;
    margin-left: 0.25rem;
}

.github-cta-arrow {
    flex-shrink: 0;
    width: clamp(28px, 2.6vw, 40px);
    height: auto;
    color: var(--text-primary);
    transition: transform 0.3s ease;
}

.github-cta:hover .github-cta-arrow {
    transform: translateX(6px);
}

@media (max-width: 600px) {
    .github-cta {
        flex-direction: column;
        gap: 0.5rem;
        padding: 1.2rem 1.4rem 1.1rem;
    }
    .github-cta-handle { margin-left: 0; }
}

/* ============================================
   SKILLS SECTION — WHAT'S IN MY BAG
   ============================================ */
.skills {
    position: relative;
    background-color: var(--bg-primary);
    /* Tighter padding so the section fits in one viewport on big screens */
    padding: clamp(2.5rem, 5vw, 4rem) 0;
    overflow: hidden;
}

/* Squiggly hand-drawn espresso borders at the top + bottom edges so the
   section reads as its own little chapter, distinct from neighbors. */
.skills::before,
.skills::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    height: 22px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 22' preserveAspectRatio='none'%3E%3Cpath d='M 0 11 Q 12 2 25 11 Q 37 20 50 11 Q 62 2 75 11 Q 87 20 100 11' stroke='%23442F2A' stroke-width='2.4' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
    background-repeat: repeat-x;
    background-size: 90px 22px;
    z-index: 5;
    pointer-events: none;
}
.skills::before { top: 0; }
.skills::after  { bottom: 0; }

/* Soft watercolor blobs drifting in the background */
.skills-paint {
    position: absolute;
    pointer-events: none;
    z-index: 0;
    filter: blur(50px);
    opacity: 0.55;
    animation: skills-paint-drift 13s ease-in-out infinite alternate;
}

/* Blobs are corner-anchored and pulled away from the center where the
   bag image sits — the bag uses mix-blend-mode: multiply, so any
   non-uniform color behind it bleeds through its white background. */
.skills-paint-1 {
    top: -4%;
    left: -10%;
    width: 38%;
    height: 45%;
    background-color: rgba(244, 201, 214, 0.45);
    border-radius: 62% 38% 55% 45% / 48% 56% 44% 52%;
    animation-duration: 14s;
}

.skills-paint-2 {
    top: -2%;
    right: -10%;
    width: 36%;
    height: 50%;
    background-color: rgba(238, 220, 200, 0.4);
    border-radius: 48% 52% 43% 57% / 58% 44% 56% 42%;
    animation-duration: 12s;
    animation-direction: alternate-reverse;
}

.skills-paint-3 {
    bottom: -4%;
    right: -8%;
    width: 32%;
    height: 38%;
    background-color: rgba(232, 166, 184, 0.3);
    border-radius: 55% 45% 60% 40% / 50% 60% 40% 50%;
    animation-duration: 15s;
}

.skills-paint-4 {
    bottom: -2%;
    left: -6%;
    width: 28%;
    height: 32%;
    background-color: rgba(250, 242, 235, 0.55);
    border-radius: 58% 42% 45% 55% / 44% 58% 42% 56%;
    animation-duration: 11s;
    animation-direction: alternate-reverse;
}

@keyframes skills-paint-drift {
    0%   { transform: translate(0, 0) scale(1); }
    50%  { transform: translate(12px, -10px) scale(1.03); }
    100% { transform: translate(-14px, 12px) scale(0.97); }
}

/* Title */
.skills-title {
    position: relative;
    z-index: 1;
    text-align: center;
    margin-bottom: 1.25rem;
}

.skills-heading-top {
    font-family: var(--font-display);
    font-size: clamp(2.5rem, 5vw, 4.5rem);
    font-weight: 700;
    color: var(--text-primary);
    line-height: 1;
}

.skills-heading-bottom {
    font-family: var(--font-display);
    font-size: clamp(3rem, 6vw, 5.5rem);
    font-weight: 700;
    color: var(--text-primary);
    line-height: 1;
    margin-bottom: 0.5rem;
}

.skills-heading-bottom em {
    font-style: italic;
    color: var(--bg-dark);
}

.skills-subtitle {
    font-family: var(--font-body);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.3em;
    text-transform: uppercase;
    color: var(--bg-dark);
}

/* Bag Canvas — sized to comfortably fit the blob explosion. Blobs use
   vh-based offsets, so the canvas just needs to be tall enough to hold
   the spread without bleeding into the title or the bottom squiggle. */
.bag-canvas {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: none;
    height: clamp(500px, 65vh, 640px);
    margin: 0 auto;
}

.bag-img {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: clamp(160px, 22vw, 260px);
    height: auto;
    cursor: pointer;
    z-index: 2;
    transform-origin: center bottom;
    /* Smooth scale transition for the opened/closed state. The wobble
       and shake animations override transform during their own cycles,
       so this transition only kicks in when no animation is running. */
    transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Resting state — bag wobbles side to side like something wants out */
.bag-img.is-wobbling {
    animation: bag-wobble 2.5s ease-in-out infinite alternate;
}

@keyframes bag-wobble {
    0%   { transform: translate(calc(-50% - 3px), -50%) rotate(-2deg); }
    100% { transform: translate(calc(-50% + 3px), -50%) rotate(2deg); }
}

/* Hover state — rapid urgent shake, overrides wobble */
.bag-img.is-shaking {
    animation: bag-shake 0.08s ease-in-out infinite alternate;
}

@keyframes bag-shake {
    0%   { transform: translate(calc(-50% - 5px), -50%); }
    100% { transform: translate(calc(-50% + 5px), -50%); }
}

/* Opened — bag shrinks smoothly first (via the .bag-img transition above),
   then resumes wobbling at the smaller scale so the user can see it's still
   tappable. The 0.55s animation-delay matches the shrink transition so the
   wobble starts only after the bag has finished shrinking. */
.bag-canvas.opened .bag-img {
    transform: translate(-50%, -50%) scale(0.6);
    animation: bag-wobble-shrunk 2.5s ease-in-out 0.55s infinite alternate;
}

@keyframes bag-wobble-shrunk {
    0%   { transform: translate(calc(-50% - 3px), -50%) scale(0.6) rotate(-2deg); }
    100% { transform: translate(calc(-50% + 3px), -50%) scale(0.6) rotate(2deg); }
}

/* Handwritten hint sitting up + to the right of the bag, with a curling
   arrow pointing back toward the zipper. CSS vars feed the keyframes so
   the bob animation and any media-query reposition share one source of
   truth. */
/* Centered hint — text on top, curling arrow below pointing down at the
   zipper. CSS vars feed the keyframes so the bob and any media-query
   reposition share one source of truth. */
.bag-hint {
    position: absolute;
    left: 50%;
    top: 50%;
    --hint-y: calc(-1 * clamp(140px, 17vw, 220px));
    --hint-y-bounce: calc(-1 * clamp(144px, 17.5vw, 226px));
    transform: translate(-50%, var(--hint-y)) rotate(-2deg);
    z-index: 3;
    pointer-events: none;
    color: var(--bg-dark);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.15rem;
    transition: opacity 0.3s ease;
    animation: bag-hint-bob 2.4s ease-in-out infinite;
}

.bag-hint-text {
    font-family: var(--font-body);
    font-style: italic;
    font-size: clamp(1.05rem, 1.6vw, 1.4rem);
    font-weight: 600;
    line-height: 1;
    white-space: nowrap;
    /* Tiny shadow so the text reads against any background paint blob */
    text-shadow: 1px 1px 0 rgba(238, 235, 231, 0.9);
}

.bag-hint-arrow {
    width: clamp(28px, 3vw, 40px);
    height: auto;
    flex-shrink: 0;
}

@keyframes bag-hint-bob {
    0%, 100% { transform: translate(-50%, var(--hint-y)) rotate(-2deg); }
    50%      { transform: translate(-50%, var(--hint-y-bounce)) rotate(-1deg); }
}

.bag-canvas.opened .bag-hint {
    opacity: 0;
    pointer-events: none;
    /* Wait for the bag to finish shrinking, then fade out */
    transition: opacity 0.35s ease 0.5s;
}

/* Skill Blobs */
.skill-blob {
    position: absolute;
    left: 50%;
    top: 50%;
    font-family: var(--font-body);
    font-size: clamp(0.78rem, 1.05vw, 1rem);
    font-weight: 600;
    letter-spacing: 0.01em;
    padding: clamp(0.4rem, 0.8vw, 0.6rem) clamp(0.7rem, 1.2vw, 1.2rem);
    border-radius: 50% 40% 55% 45% / 45% 55% 40% 50%;
    white-space: nowrap;
    opacity: 0;
    transform: translate(-50%, -50%) scale(0);
    z-index: 1;
    cursor: default;
    transition: box-shadow var(--transition-fast), transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Fly out when opened */
.bag-canvas.opened .skill-blob {
    animation: blob-fly-out 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
    animation-delay: calc(var(--delay) * 60ms + 0.1s);
}

/* Fly back in when closed */
.bag-canvas:not(.opened) .skill-blob.was-open {
    animation: blob-fly-in 0.4s ease forwards;
    animation-delay: calc(var(--delay) * 30ms);
}

@keyframes blob-fly-in {
    0% {
        opacity: 1;
        transform: translate(calc(-50% + var(--x)), calc(-50% + var(--y))) scale(1) rotate(var(--r));
    }
    100% {
        opacity: 0;
        transform: translate(-50%, -50%) scale(0) rotate(0deg);
    }
}

@keyframes blob-fly-out {
    0% {
        opacity: 0;
        transform: translate(-50%, -50%) scale(0) rotate(0deg);
    }
    60% {
        opacity: 1;
        /* Past target by ~10% — overshoot */
        transform: translate(calc(-50% + var(--x) * 1.1), calc(-50% + var(--y) * 1.1)) scale(1.12) rotate(var(--r));
    }
    80% {
        opacity: 1;
        /* Back past target the other way — slight undershoot */
        transform: translate(calc(-50% + var(--x) * 0.95), calc(-50% + var(--y) * 0.95)) scale(0.95) rotate(var(--r));
    }
    100% {
        opacity: 1;
        transform: translate(calc(-50% + var(--x)), calc(-50% + var(--y))) scale(1) rotate(var(--r));
    }
}

/* Category colors — four shades of brown, one per quadrant.
   top-left: mocha · top-right: espresso · bottom-left: tan · bottom-right: caramel */
.blob-lang {
    background-color: #6D5D53;
    color: var(--bg-primary);
    border: 1.5px solid var(--bg-dark);
}

.blob-frame {
    background-color: var(--bg-dark);
    color: var(--bg-primary);
    border: 1.5px solid var(--bg-dark);
}

.blob-tools {
    background-color: #9C8E84;
    color: var(--bg-dark);
    border: 1.5px solid var(--bg-dark);
}

.blob-prof {
    background-color: #856B5A;
    color: var(--bg-primary);
    border: 1.5px solid var(--bg-dark);
}

.skill-blob {
    box-shadow: 2px 2px 0 rgba(68, 47, 42, 0.25);
}

.skill-blob:hover {
    box-shadow: 3px 3px 0 var(--bg-dark);
}

/* Inner wrapper carries the wiggle transform — isolated from the
   positioning transform on .skill-blob so they don't conflict. */
.blob-inner {
    display: inline-block;
    transform-origin: center;
}

.skill-blob:hover .blob-inner {
    animation: blob-wiggle 0.5s ease-in-out 1 forwards;
}

@keyframes blob-wiggle {
    0%   { transform: rotate(0deg) scale(1); }
    15%  { transform: rotate(-4deg) scale(1.05); }
    30%  { transform: rotate(4deg) scale(1.08); }
    45%  { transform: rotate(-3deg) scale(1.05); }
    60%  { transform: rotate(3deg) scale(1.03); }
    75%  { transform: rotate(-1deg) scale(1.01); }
    100% { transform: rotate(0deg) scale(1); }
}

/* Tooltip */
.skill-tooltip {
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%) translateY(5px);
    background-color: var(--bg-dark);
    color: var(--text-light);
    font-family: var(--font-body);
    font-size: 0.75rem;
    font-weight: 400;
    padding: 0.5rem 0.75rem;
    white-space: nowrap;
    opacity: 0;
    transition: all 0.2s ease;
    z-index: 10;
    pointer-events: none;
}

.skill-tooltip.visible {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}

/* Category Labels */
.cat-label {
    position: absolute;
    left: 50%;
    top: 50%;
    font-family: var(--font-body);
    font-size: clamp(0.7rem, 0.95vw, 0.92rem);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.18em;
    white-space: nowrap;
    opacity: 0;
    transform: translate(calc(-50% + var(--x)), calc(-50% + var(--y)));
    z-index: 1;
    background-color: rgba(238, 235, 231, 0.85);
    padding: 0.2rem 0.6rem;
    border: 1.5px dashed var(--bg-dark);
}

.cat-label-lang  { color: var(--bg-dark); }
.cat-label-frame { color: var(--bg-dark); }
.cat-label-tools { color: var(--bg-dark); }
.cat-label-prof  { color: var(--bg-dark); }

.bag-canvas.opened .cat-label {
    animation: label-fade 0.4s ease forwards;
    animation-delay: 1.5s;
}

@keyframes label-fade {
    to { opacity: 1; }
}

/* Side-pocket aside — a quiet note that the bag isn't the exhaustive list.
   Kept as flowing text (not flex) so it wraps to two lines gracefully on
   narrow screens with the bloom staying on the first line. */
.skills-more {
    position: relative;
    z-index: 1;
    max-width: 30rem;
    margin: 0.7rem auto 0;
    padding: 0 1.25rem;
    text-align: center;
    font-family: var(--font-body);
    font-style: italic;
    font-size: clamp(0.74rem, 0.9vw, 0.84rem);
    line-height: 1.5;
    letter-spacing: 0.02em;
    color: var(--text-secondary);
}

.skills-more-bloom {
    width: 0.8rem;
    height: 0.8rem;
    margin-right: 0.3rem;
    vertical-align: -0.1em;
    overflow: visible;
}

/* Languages spoken */
.skills-languages {
    position: relative;
    z-index: 1;
    text-align: center;
    font-family: var(--font-body);
    font-size: clamp(0.8rem, 1vw, 0.9rem);
    color: var(--bg-dark);
    font-weight: 500;
    margin-top: 0.5rem;
    letter-spacing: 0.05em;
}

/* Mobile */
@media (max-width: 768px) {
    .bag-canvas {
        height: clamp(500px, 90vh, 700px);
    }

    .bag-img {
        width: clamp(110px, 28vw, 160px);
    }

    .bag-hint {
        --hint-y: -120px;
        --hint-y-bounce: -126px;
    }
    .bag-hint-text {
        font-size: 1.15rem;
    }
    .bag-hint-arrow {
        width: 26px;
    }

    .cat-label {
        font-size: 0.7rem;
    }
}

/* Hide the smaller paint blobs on tiny screens to keep things clean —
   matches the same pattern used for hero paint blobs. */
@media (max-width: 599px) {
    .skills-paint-3,
    .skills-paint-4 {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .skills-paint {
        animation: none;
    }
}


/* ============================================
   RECOGNITION — EDITORIAL REEL
   ============================================
   Magazine-spread treatment: numbered list, italic Playfair titles,
   Caveat numbers, dashed espresso dividers, blush hover bar. Replaces
   the corkboard polaroids — quieter, more readable, sits cleanly with
   the typographic hierarchy used in About/Experience. */
.recognition {
    position: relative;
    background-color: var(--bg-primary);
    /* Equal breathing room above and below the flower bouquet — the
     * bouquet's margin-top + this padding-bottom should match. */
    padding: clamp(4rem, 9vw, 7rem) 0 clamp(1.5rem, 3vw, 3rem);
    overflow: hidden;
    isolation: isolate;
}

/* Subtle paper grain — same noise pattern used elsewhere on cream
   sections so the texture reads as continuous across the page. */
.recog-paper {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    opacity: 0.05;
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.7' numOctaves='3' stitchTiles='stitch' seed='9'/%3E%3CfeColorMatrix values='0 0 0 0 0.27 0 0 0 0 0.18 0 0 0 0 0.14 0 0 0 1 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    background-size: 256px 256px;
}

/* Three flowers clustered along the bottom — visual separator between
   recognition and contact, in the same hand-picked bouquet language
   used elsewhere on the site. The wrapper is a centered flex row so
   flower sizing scales fluidly with the viewport without manual
   left/right percentage tweaks. */
/* In-flow at the bottom of the section so the cluster sits BELOW the
   reel and never overlaps award rows. (Absolute + bottom positioning
   would let the big flowers reach back up over the reel on tall layouts.) */
.recog-flower-row {
    position: relative;
    z-index: 0;
    /* Top margin matches the section's padding-bottom so the gap above
     * and below the bouquet reads as symmetric. */
    margin: clamp(1.5rem, 3vw, 3rem) auto 0;
    pointer-events: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: max-content;
    max-width: 100%;
}

.recog-flower {
    /* Way bigger upper bound so the cluster scales up dramatically on
       large monitors instead of looking lost in white space. */
    width: clamp(80px, 20vw, 320px);
    height: clamp(80px, 20vw, 320px);
    flex-shrink: 0;
    opacity: 0.9;
}

/* Aggressive negative margin → flowers overlap by ~30–40% so the row
   reads as one tight bouquet rather than spaced-out icons. (CSS `gap`
   doesn't accept negative values, so we use margin.) */
.recog-flower + .recog-flower {
    margin-left: clamp(-120px, -7.5vw, -28px);
}

.recog-flower-img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    transform: rotate(var(--spin, 0deg)) scale(var(--flower-scale, 1));
    transition: transform 0.06s linear;
    filter: drop-shadow(2px 3px 4px rgba(80, 50, 30, 0.10));
}

/* f2.png (cherry blossom) renders smaller relative to its canvas — same
   trick as the about section keeps it visually balanced. */
.recog-flower-img[src="f2.png"] {
    --flower-scale: 1.45;
}

/* ---- Header ---- */
.recog-header {
    position: relative;
    z-index: 1;
    text-align: center;
    margin: 0 auto clamp(2.5rem, 5vw, 4rem);
    max-width: 880px;
    padding: 0 1.5rem;
}

.recog-header .section-label {
    color: var(--text-muted);
    margin-bottom: 0.65rem;
}

.recog-title {
    font-family: var(--font-display);
    font-size: clamp(2.4rem, 5vw, 4.4rem);
    font-weight: 500;
    color: var(--text-primary);
    line-height: 1.1;
    margin: 0;
    letter-spacing: -0.01em;
}

.recog-title em {
    font-style: italic;
    font-family: var(--font-script);
    font-weight: 700;
    color: var(--accent-pink-dark);
    margin-right: 0.15em;
}

.recog-flourish {
    width: clamp(60px, 7vw, 90px);
    height: auto;
    margin-top: 0.9rem;
    color: var(--text-muted);
    opacity: 0.7;
}

/* ---- Reel of award rows ---- */
.recog-reel {
    position: relative;
    z-index: 1;
    list-style: none;
    margin: 0 auto;
    max-width: 880px;
    padding: 0 clamp(1rem, 4vw, 3rem);
}

.award-row {
    display: grid;
    grid-template-columns: clamp(48px, 6vw, 78px) 1fr;
    column-gap: clamp(1rem, 2.4vw, 2.4rem);
    align-items: baseline;
    padding: clamp(1.4rem, 2.6vw, 2.2rem) 0;
    border-bottom: 1px dashed rgba(62, 39, 35, 0.22);
    position: relative;
    opacity: 0;
    transform: translateY(14px);
    transition:
        opacity 0.55s ease,
        transform 0.55s cubic-bezier(0.34, 1.56, 0.64, 1),
        padding-left 0.3s ease;
}

.award-row:first-child {
    border-top: 1px dashed rgba(62, 39, 35, 0.22);
}

.award-row.is-revealed {
    opacity: 1;
    transform: translateY(0);
}

/* Blush left bar appears on hover — subtle accent that nods to the rest
   of the site's pink without making the row feel like a tile. */
.award-row::before {
    content: '';
    position: absolute;
    left: -3px;
    top: 18%;
    bottom: 18%;
    width: 3px;
    background-color: var(--accent-pink-dark);
    transform: scaleY(0);
    transform-origin: center;
    transition: transform 0.3s ease;
}

.award-row:hover::before {
    transform: scaleY(1);
}

.award-row:hover {
    padding-left: clamp(0.5rem, 1vw, 1rem);
}

.award-num {
    font-family: var(--font-display);
    font-style: italic;
    font-size: clamp(2rem, 3.4vw, 2.8rem);
    font-weight: 500;
    color: var(--text-secondary);
    line-height: 1;
    opacity: 0.5;
    transition: opacity 0.3s ease, color 0.3s ease;
    align-self: start;
    padding-top: 0.4rem;
    letter-spacing: -0.02em;
}

.award-row:hover .award-num {
    opacity: 1;
    color: var(--accent-pink-dark);
}

.award-body {
    min-width: 0;
}

.award-title {
    font-family: var(--font-display);
    font-style: italic;
    font-size: clamp(1.2rem, 2.05vw, 1.7rem);
    font-weight: 500;
    color: var(--text-primary);
    margin: 0 0 0.45rem;
    line-height: 1.25;
}

.award-meta {
    font-family: var(--font-body);
    font-size: clamp(0.78rem, 1vw, 0.92rem);
    font-weight: 500;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    margin: 0 0 0.65rem;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.55rem;
}

.award-org {
    color: var(--text-secondary);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    font-size: 0.95em;
}

.award-sep {
    opacity: 0.4;
}

.award-year {
    font-family: var(--font-script);
    font-size: 1.4em;
    color: var(--text-primary);
    font-weight: 600;
    letter-spacing: 0;
}

.award-detail {
    font-family: var(--font-display);
    font-style: italic;
    font-size: clamp(0.92rem, 1.1vw, 1.05rem);
    color: var(--text-secondary);
    margin: 0;
    line-height: 1.5;
    max-width: 56ch;
    opacity: 0.85;
}

/* ---- Big screens — two columns to save vertical space ---- */
@media (min-width: 1100px) {
    .recog-reel {
        display: grid;
        grid-template-columns: 1fr 1fr;
        column-gap: clamp(2rem, 4vw, 4rem);
        max-width: 1180px;
        /* Reset list-item margins so the grid controls spacing */
        padding: 0 clamp(1rem, 4vw, 3rem);
    }
    /* The second item sits in the first row's right column, so it needs
       its own top divider — :first-child only catches item 1. */
    .award-row:nth-child(2) {
        border-top: 1px dashed rgba(62, 39, 35, 0.22);
    }
}

/* ---- Mobile ---- */
@media (max-width: 600px) {
    .award-row {
        grid-template-columns: clamp(38px, 11vw, 54px) 1fr;
        column-gap: 0.8rem;
        padding: 1.3rem 0;
    }
    .award-num {
        font-size: 2.1rem;
        padding-top: 0.25rem;
    }
    .recog-flower {
        width: 70px;
        height: 70px;
        opacity: 0.8;
    }
    .recog-flower + .recog-flower {
        margin-left: -22px;
    }
    .recog-flower-row {
        margin-top: 2rem;
    }
    .award-row:hover {
        padding-left: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .award-row {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }
    .award-row::before {
        transition: none !important;
    }
}

/* ============================================
   CONTACT SECTION — "and that's a wrap."
   ============================================ */
.contact {
    position: relative;
    background-image: url('GRACEenvelope.png');
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    min-height: 80vh;
    padding: 0;
    overflow: hidden;
}

.contact-envelope {
    display: none;
}

.contact-corner-top {
    position: absolute;
    top: clamp(1rem, 3vw, 2.5rem);
    right: clamp(1.5rem, 4vw, 4rem);
    font-family: var(--font-display);
    font-size: clamp(3rem, 8vw, 9rem);
    font-weight: 900;
    color: var(--text-primary);
    line-height: 0.85;
    text-align: right;
    z-index: 2;
    transform: translateX(120%);
    transition: transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);
}

.contact-corner-top.visible {
    transform: translateX(0);
}

.contact-corner-bottom {
    position: absolute;
    bottom: clamp(1rem, 3vw, 2.5rem);
    left: clamp(1.5rem, 4vw, 4rem);
    font-family: var(--font-display);
    font-size: clamp(4rem, 12vw, 14rem);
    font-weight: 900;
    color: var(--text-primary);
    line-height: 0.8;
    z-index: 2;
    transform: translateX(-120%);
    transition: transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);
    transition-delay: 0.15s;
}

.contact-corner-bottom.visible {
    transform: translateX(0);
}

.contact-info {
    position: absolute;
    left: clamp(1.5rem, 4vw, 4rem);
    top: clamp(1.5rem, 5vw, 4rem);
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    z-index: 2;
}

.contact-info-line {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-family: var(--font-body);
    font-size: clamp(0.8rem, 1.2vw, 0.95rem);
    font-weight: 500;
    color: var(--text-primary);
    text-decoration: none;
    background-color: var(--accent-pink);
    padding: 0.5rem 1rem;
    box-shadow: 2px 3px 0px rgba(68, 47, 42, 0.15);
    transition: transform var(--transition-fast), box-shadow var(--transition-fast);
    width: fit-content;
}

.contact-info-line:nth-child(1) {
    transform: rotate(-2deg);
}

.contact-info-line:nth-child(2) {
    transform: rotate(1.5deg);
    margin-left: 0.75rem;
}

.contact-info-line:nth-child(3) {
    transform: rotate(-1deg);
}

.contact-info-line:hover {
    transform: rotate(0deg) scale(1.05);
    box-shadow: 3px 5px 0px rgba(68, 47, 42, 0.2);
}

.contact-info-line svg {
    flex-shrink: 0;
    opacity: 0.7;
}

/* ============================================
   FOOTER
   ============================================ */
.footer {
    background-color: var(--bg-dark);
    padding: 1.4rem clamp(1rem, 4vw, 2rem) 1.2rem;
    text-align: center;
    border-top: none;
}

.footer-line {
    font-family: var(--font-body);
    font-size: clamp(0.78rem, 0.95vw, 0.92rem);
    color: var(--text-light-muted);
    line-height: 1.6;
    margin: 0 auto 0.4rem;
    max-width: 1200px;
    letter-spacing: 0.01em;
}

.footer-lead {
    color: var(--accent-pink);
    font-style: italic;
    margin-right: 0.35rem;
}

.footer-tag {
    padding: 0 0.4rem;
    transition: color 0.2s ease;
}

.footer-tag:hover {
    color: var(--accent-pink);
}

.footer-credit {
    font-family: var(--font-body);
    font-size: 0.7rem;
    color: var(--text-light-muted);
    letter-spacing: 0.15em;
    text-transform: uppercase;
    margin: 0;
    opacity: 0.55;
}

/* ============================================
   RESPONSIVE DESIGN
   ============================================ */
@media (max-width: 1024px) {
    .projects-rail {
        gap: 1.25rem;
    }
}

/* Projects — under 768px the drawer stops being a drawer. A horizontal rail
   on a phone fights the page's own vertical scroll and hides most of the set
   behind a swipe, so the plates collapse into a plain single-column stack:
   arrows, runner, and hint all drop away and the tilt is flattened. */
@media (max-width: 768px) {
    .drawer {
        display: block;
        margin-bottom: 3rem;
    }

    .projects-rail {
        flex-direction: column;
        gap: 1rem;
        overflow: visible;
        scroll-snap-type: none;
        margin-inline: 0;
        padding-inline: 0;
        padding-block: 1.5rem 0;
        cursor: auto;
    }

    .project-card,
    .project-card:nth-child(3n + 1),
    .project-card:nth-child(3n + 2),
    .project-card:nth-child(3n + 3) {
        --rot: 0deg;
    }

    .project-card {
        width: 100%;
        min-height: 0;
        padding: 1.75rem 1.25rem 1.15rem;
    }

    .project-card:hover,
    .project-card:focus-within {
        transform: translateY(-5px);
    }

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

    .project-description {
        font-size: 0.82rem;
        line-height: 1.55;
        margin-bottom: 0.9rem;
    }

    /* Drawer chrome is meaningless once the rail is a stack. */
    .rail-nav,
    .rail-runner,
    .rail-hint {
        display: none;
    }

    /* Scale the floral frame down with the tighter plate padding. */
    .plate-sprig-tl {
        width: 2.5rem;
        height: 2.5rem;
    }

    .plate-sprig-tr,
    .plate-sprig-br {
        width: 1.9rem;
        height: 1.9rem;
    }

    .plate-sprig-bl {
        width: 1.6rem;
        height: 1.6rem;
    }

    .plate-head {
        padding-inline: 1.15rem 1rem;
    }
}

@media (max-width: 768px) {
    .nav-links {
        display: none;
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        background-color: rgba(238, 235, 231, 0.95);
        backdrop-filter: blur(12px);
        flex-direction: column;
        padding: 1.5rem;
        gap: 1rem;
        border-bottom: 1px solid rgba(26, 26, 46, 0.05);
    }

    .nav-links.active {
        display: flex;
    }

    .nav-toggle {
        display: flex;
    }


    /* On small mobile, drop the rotations so notes sit flat */
    .github-card {
        --note-rot: 0deg;
    }
    .github-card,
    .github-card.is-visible,
    .github-card:hover {
        transform: rotate(0deg);
    }
    .github-card:hover {
        transform: translateY(-4px);
    }

    .filter-bar {
        justify-content: center;
    }

    /* Contact — phone layout. The desktop design uses the envelope as
       a `cover` background with absolutely-positioned dramatic text in
       opposite corners and the info chips floating top-left. On phones
       all three pieces collide. Restack as a clean vertical flow:
       envelope as a contained backdrop sized to the viewport, dramatic
       text shrunk and pinned at top/bottom, info chips centered in the
       middle on solid ground. */
    .contact {
        min-height: auto;
        padding: clamp(2rem, 7vw, 3.5rem) 1.25rem clamp(2.5rem, 8vw, 4rem);
        background-size: contain;
        background-position: center;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: clamp(0.5rem, 2vw, 1rem);
    }

    .contact-corner-top,
    .contact-corner-bottom {
        position: relative;
        top: auto;
        right: auto;
        bottom: auto;
        left: auto;
        text-align: center;
        font-size: clamp(2rem, 9vw, 3.5rem);
        line-height: 0.95;
    }

    .contact-corner-bottom {
        font-size: clamp(2.6rem, 12vw, 4.5rem);
    }

    .contact-info {
        position: relative;
        top: auto;
        left: auto;
        align-items: center;
        gap: 0.55rem;
        margin: clamp(0.5rem, 3vw, 1.25rem) 0;
    }

    .contact-info-line {
        font-size: 0.85rem;
        padding: 0.45rem 0.85rem;
    }

    .contact-info-line:nth-child(2) {
        margin-left: 0;
    }

    .contact-envelope {
        width: clamp(220px, 65vw, 350px);
    }
}

@media (max-width: 480px) {
    .experience-card {
        padding: 1rem;
    }

    .project-card {
        padding: 1.25rem;
    }
}

/* ============================================
   LARGE SCREENS
   ============================================ */
@media (min-width: 1440px) {
    html {
        font-size: 18px;
    }

    :root {
        --container-max: 1400px;
        --container-padding: 4rem;
    }

    .nav-links {
        gap: 2.5rem;
    }


    .experience-card {
        padding: 2rem 2.5rem;
    }

    .project-card {
        width: clamp(19rem, 26vw, 23rem);
    }

    .contact-btn {
        padding: 1rem 2rem;
    }

    .contact-links {
        gap: 1.5rem;
    }
}

@media (min-width: 1920px) {
    html {
        font-size: 20px;
    }

    :root {
        --container-max: 1600px;
        --container-padding: 5rem;
    }

    .experience-list {
        gap: 2rem;
    }


}

/* ============================================
   REDUCED MOTION
   ============================================ */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }

    html {
        scroll-behavior: auto;
    }

    .scroll-indicator {
        animation: none;
    }

    /* Smooth programmatic scrolling and snap both read as motion — the
       arrows still work, they just jump. Plates land flat and un-tilted. */
    .projects-rail {
        scroll-behavior: auto;
        scroll-snap-type: none;
    }

    .project-card,
    .project-card.visible {
        --rot: 0deg;
        opacity: 1 !important;
        transform: none !important;
        transition-delay: 0ms !important;
    }

    .plate-sprig {
        transform: rotate(var(--sr)) !important;
    }
}

/* ============================================
   HERO — PASTEL AESTHETIC
   ============================================ */

/* Pastel paint stroke blobs — sit behind the banner image */
.hero-paint {
    position: absolute;
    z-index: 0;
    pointer-events: none;
    filter: blur(40px);
    animation: paint-drift 10s ease-in-out infinite alternate;
    opacity: 0;
    transition: opacity 1.2s ease;
}

.hero-paint.is-visible {
    opacity: 1;
}

.paint-1 {
    background-color: rgba(244, 201, 214, 0.35);
    border-radius: 62% 38% 55% 45% / 48% 56% 44% 52%;
    animation-duration: 11s;
}

.paint-2 {
    background-color: rgba(238, 220, 200, 0.3);
    border-radius: 48% 52% 43% 57% / 58% 44% 56% 42%;
    animation-duration: 9s;
    animation-direction: alternate-reverse;
}

.paint-3 {
    background-color: rgba(232, 166, 184, 0.25);
    border-radius: 55% 45% 60% 40% / 50% 60% 40% 50%;
    animation-duration: 12s;
}

.paint-4 {
    background-color: rgba(250, 242, 235, 0.5);
    border-radius: 58% 42% 45% 55% / 44% 58% 42% 56%;
    animation-duration: 8s;
    animation-direction: alternate-reverse;
}

@keyframes paint-drift {
    0%   { transform: translate(0, 0) scale(1); }
    50%  { transform: translate(12px, -8px) scale(1.03); }
    100% { transform: translate(-10px, 10px) scale(0.98); }
}

/* Collage corner decorations */
.collage {
    position: absolute;
    pointer-events: none;
    z-index: 5;
    opacity: 0;
    transform: translateY(-8px);
    transition: opacity 0.6s ease,
                transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.collage.is-visible {
    opacity: 1;
    transform: translateY(0);
}

.collage-tl { top: -12px; left: -12px; }
.collage-tr { top: -8px; right: -8px; }
.collage-bl { bottom: 80px; left: 20px; }
.collage-br { bottom: 90px; right: 24px; }

.washi {
    position: absolute;
    width: 130px;
    height: 26px;
    background-image: repeating-linear-gradient(90deg,
        rgba(255,255,255,0.2) 0 6px,
        transparent 6px 14px);
}

.washi-tl {
    background-color: rgba(244, 201, 214, 0.75);
    transform: rotate(-35deg);
    top: 26px;
    left: -22px;
}

.washi-tr {
    background-color: rgba(238, 220, 200, 0.8);
    transform: rotate(30deg);
    top: 26px;
    right: -22px;
}

.washi-br {
    background-color: rgba(232, 166, 184, 0.7);
    transform: rotate(-25deg);
    bottom: -8px;
    right: -10px;
}

.collage .doodle-star {
    position: absolute;
    top: 56px;
    left: 44px;
    transform: rotate(15deg);
}

.collage .doodle-heart {
    position: absolute;
    top: 58px;
    right: 44px;
    transform: rotate(-10deg);
}

.collage .doodle-flower {
    transform: rotate(-8deg);
}

.collage .doodle-sparkle {
    position: absolute;
    bottom: 40px;
    right: 48px;
}

.doodle-dots {
    position: absolute;
    top: 48px;
    left: 52px;
    width: 14px;
    height: 4px;
    background-image: radial-gradient(circle, var(--text-muted) 1.5px, transparent 1.6px);
    background-size: 6px 4px;
    background-repeat: repeat-x;
    opacity: 0.7;
}

/* Ink trail cursor */
.ink-dot {
    position: fixed;
    pointer-events: none;
    border-radius: 50%;
    background-color: var(--accent-pink-dark);
    z-index: 9998;
    opacity: 0.55;
    transition: opacity 0.6s ease;
}

.ink-dot.fade {
    opacity: 0;
}

/* ============================================
   HERO — RESPONSIVE BREAKPOINTS
   ============================================ */

/* Small desktop / large tablet: 768px–1023px */
@media (max-width: 1023px) and (min-width: 768px) {
    .collage-tl,
    .collage-tr,
    .collage-bl,
    .collage-br {
        transform: scale(0.8);
    }

    .scroll-hint {
        font-size: 1rem;
    }
}

/* Tablet portrait: 600px–767px */
@media (max-width: 767px) and (min-width: 600px) {
    .collage-bl,
    .collage-br { display: none; }

    .collage-tl,
    .collage-tr {
        transform: scale(0.65);
        transform-origin: top;
    }

    .scroll-hint {
        font-size: 0.95rem;
    }
}

/* Mobile: 599px and below */
@media (max-width: 599px) {
    /* Hide all collage corners on small mobile */
    .collage-tl,
    .collage-tr,
    .collage-bl,
    .collage-br { display: none; }

    /* Simplify to 2 blobs on mobile */
    .paint-3,
    .paint-4 { display: none; }

    .scroll-hint {
        font-size: 0.85rem;
        top: 0.75rem;
        right: 0.75rem;
    }

    /* Hide Spotify widget on tiny screens to avoid overlap */
    .spotify-widget {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .hero-paint {
        animation: none;
    }
    .hero-banner {
        transition: opacity 0.2s ease !important;
        transform: none !important;
    }
}

/* ============================================
   SKILLS — UNZIP INTERACTION
   ============================================
   Replaces the previous instant-fly-out blob animation with an
   orchestrated sequence: zipper draws across bag → bag flap folds
   open → blobs rise out of the opening one at a time. JS in
   script.js drives the timing; this stylesheet sets the static
   look + the resting state blobs revert to once they've landed. */

/* Zipper SVG overlay — positioned absolutely over the bag's top
   opening. JS sets left/top/width/height after the bag image renders
   (positionZipper). overflow:visible so the pull tab can sit a few
   pixels left of the track start before it slides right. */
#zipperSvg {
    position: absolute;
    pointer-events: none;
    opacity: 0;
    z-index: 3;
    overflow: visible;
    transition: opacity 0.2s ease;
}

#zipperSvg .zipper-track {
    stroke: #B89B85;
    stroke-width: 1.4;
    fill: none;
    stroke-linecap: round;
    /* JS overrides both with the curved path's actual length (in viewBox
       units) at init. Generous fallback here just hides the strokes
       until that runs. */
    stroke-dasharray: 400;
    stroke-dashoffset: 400;
}

#zipperSvg .zipper-tooth {
    fill: #B89B85;
    opacity: 0;
    transition: opacity 0.12s ease;
}

/* No CSS transform on #zipperPull — JS uses setAttribute('transform', ...)
   to translate it along the curved top track via getPointAtLength, and a
   CSS transform rule would override that per the SVG2 cascade. */

/* Blobs need to sit ABOVE the zipper SVG so they appear to rise out
   from inside the bag rather than behind the zipper. */
.skill-blob { z-index: 4; }

/* Override the original auto-fly-out keyframe — JS now drives the
   stage-A rise + stage-B drift so timing can sync with the unzip.
   This rule is the resting/static state for blobs once they've
   landed; magnetic-hover clears its inline transform back to '' and
   this rule provides the final position. */
.bag-canvas.opened .skill-blob {
    animation: none;
    opacity: 1;
    transform: translate(calc(-50% + var(--x)), calc(-50% + var(--y))) scale(1) rotate(var(--r));
}

/* Perspective container for the bag's 3D fold-open transform.
   Set on the canvas so child .bag-img rotateX has depth. */
.bag-canvas.is-folding {
    perspective: 800px;
}

@media (prefers-reduced-motion: reduce) {
    #zipperSvg,
    #zipperSvg .zipper-track,
    #zipperSvg .zipper-tooth {
        transition: none !important;
    }
}
