/* ============================================================
   Jonathan Berglund — portfolio
   1. Reset & tokens        5. Nav            9.  Hero
   2. Themes                6. Cursor         10. Buttons
   3. Base type             7. Page cover     11. LEGACY (being replaced)
   4. Layout primitives     8. Rail/progress  12. Reduced motion
   ============================================================ */

/* ═══ 1. RESET & TOKENS ═══ */
*, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

:root {
    --dur: 600ms;
    --ease: cubic-bezier(0.22, 1, 0.36, 1);
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

    /* Monospace is a system stack on purpose — the five theme families are
       already a heavy enough font payload to add a sixth over the network. */
    --font-mono: ui-monospace, 'SF Mono', 'Cascadia Mono', 'Roboto Mono', Menlo, Consolas, monospace;

    --wrap: 1060px;
    --wrap-wide: 1340px;
    --wrap-narrow: 660px;
    --gutter: clamp(1.25rem, 4vw, 3rem);

    --fs-micro: 0.68rem;
    --fs-label: 0.72rem;
    --fs-xs: 0.8rem;
    --fs-sm: 0.9rem;
    --fs-base: 1rem;
    --fs-lg: clamp(1.05rem, 1.6vw, 1.3rem);
    --fs-xl: clamp(1.4rem, 3vw, 2rem);
    --fs-2xl: clamp(2rem, 5vw, 3.4rem);
    --fs-3xl: clamp(2.6rem, 7vw, 5rem);
    --fs-hero: clamp(2.9rem, 10vw, 8.5rem);

    --sp-1: 0.25rem;
    --sp-2: 0.5rem;
    --sp-3: 0.75rem;
    --sp-4: 1rem;
    --sp-6: 1.5rem;
    --sp-8: 2rem;
    --sp-12: 3rem;
    --sp-16: 4rem;
    --sp-24: 6rem;
    --sp-32: 8rem;

    --nav-h: 64px;
}

@media (prefers-reduced-motion: no-preference) {
    html {
        scroll-behavior: smooth;
    }
}

html {
    scroll-padding-top: calc(var(--nav-h) + 1rem);
}

/* ═══ 2. THEMES ═══════════════════════════════════════════════
   One global theme paints the whole page. --deep / --on-deep are the
   dark end of each palette, used by the photography section so it reads
   as a darkroom in every theme. They are declared per theme rather than
   derived, because a mix toward --text inverts on the dark themes.
   --accent-deep is the same story for the accent: mono's accent is near
   black and would vanish against --deep, so each theme names its own. */

[data-theme="noir"] {
    --bg: #0d0d0d;
    --bg2: #141414;
    --line: rgba(255,255,255,0.07);
    --text: #f0ece4;
    --muted: #8a8378;
    --accent: #c8b5ff;
    --deep: #060606;
    --on-deep: #f0ece4;
    --accent-deep: #c8b5ff;
    --font: 'Space Grotesk',sans-serif;
    --font-head: 'Syne',sans-serif;
    --wh: 700;
    --cr: 16px;
    --ls: -0.03em;
}

[data-theme="cream"] {
    --bg: #f5f0e8;
    --bg2: #ede8df;
    --line: rgba(0,0,0,0.08);
    --text: #1c1814;
    --muted: #7d7266;
    --accent: #b5451b;
    --deep: #241f1a;
    --on-deep: #f5f0e8;
    --accent-deep: #e08a5c;
    --font: 'Playfair Display',serif;
    --font-head: 'Playfair Display',serif;
    --wh: 400;
    --cr: 4px;
    --ls: 0.01em;
}

[data-theme="slate"] {
    --bg: #0f1520;
    --bg2: #151e2e;
    --line: rgba(255,255,255,0.06);
    --text: #d8e4f0;
    --muted: #7d94b3;
    --accent: #5b9cf6;
    --deep: #080c13;
    --on-deep: #d8e4f0;
    --accent-deep: #7db4ff;
    --font: 'Inter',sans-serif;
    --font-head: 'Inter',sans-serif;
    --wh: 300;
    --cr: 8px;
    --ls: 0.04em;
}

[data-theme="blush"] {
    --bg: #fdf6f0;
    --bg2: #f9ede4;
    --line: rgba(0,0,0,0.06);
    --text: #2c1810;
    --muted: #9a6a58;
    --accent: #c04060;
    --deep: #2c1810;
    --on-deep: #fdf6f0;
    --accent-deep: #f08098;
    --font: 'DM Serif Display',serif;
    --font-head: 'DM Serif Display',serif;
    --wh: 400;
    --cr: 24px;
    --ls: 0.02em;
}

[data-theme="mono"] {
    --bg: #f8f8f6;
    --bg2: #f0f0ee;
    --line: rgba(0,0,0,0.1);
    --text: #111;
    --muted: #6e6e6e;
    --accent: #111;
    --deep: #141414;
    --on-deep: #f8f8f6;
    --accent-deep: #f8f8f6;
    --font: 'Inter',sans-serif;
    --font-head: 'Inter',sans-serif;
    --wh: 500;
    --cr: 0px;
    --ls: -0.02em;
}

/* ═══ 3. BASE TYPE ═══ */
body {
    font-family: var(--font);
    background: var(--bg);
    color: var(--text);
    line-height: 1.6;
    min-height: 100vh;
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
    transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}

body[data-theme="mono"] h1 {
    line-height: 1.0;
}

h1, h2, h3 {
    font-family: var(--font-head);
    font-weight: var(--wh);
    letter-spacing: var(--ls);
    transition: font-family var(--dur) var(--ease),letter-spacing var(--dur) var(--ease),font-weight var(--dur) var(--ease),color var(--dur) var(--ease);
}

p, span, button, a, li {
    font-family: var(--font);
    transition: font-family var(--dur) var(--ease),color var(--dur) var(--ease),background var(--dur) var(--ease),border-color var(--dur) var(--ease);
}

a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
    border-radius: 2px;
}

::selection {
    background: var(--accent);
    color: var(--bg);
}

.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 1000;
    padding: 0.7rem 1.2rem;
    background: var(--accent);
    color: var(--bg);
    font-size: var(--fs-sm);
    text-decoration: none;
}

    .skip-link:focus {
        left: 0.5rem;
        top: 0.5rem;
    }

/* ═══ 4. LAYOUT PRIMITIVES ═══ */
.wrap {
    max-width: var(--wrap);
    margin: 0 auto;
    padding: 0 var(--gutter);
}

.wrap-wide {
    max-width: var(--wrap-wide);
    margin: 0 auto;
    padding: 0 var(--gutter);
}

.wrap-narrow {
    max-width: var(--wrap-narrow);
    margin: 0 auto;
    padding: 0 var(--gutter);
}

section {
    padding: 7rem 0;
    border-top: 1px solid var(--line);
    transition: border-color var(--dur) var(--ease);
}

/* Shared section header: same component everywhere, so the sections read as
   one site even though their bodies are composed very differently. */
.sec-head {
    display: flex;
    align-items: baseline;
    gap: var(--sp-4);
    margin-bottom: var(--sp-12);
}

.sec-num {
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    letter-spacing: 0.14em;
    color: var(--accent);
    flex: none;
}

.sec-title {
    font-family: var(--font-mono);
    font-size: var(--fs-label);
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--muted);
}

.sec-rule {
    flex: 1;
    height: 1px;
    background: var(--line);
    transform-origin: left;
}

.label {
    font-size: var(--fs-label);
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--muted);
    margin-bottom: 2.5rem;
    display: block;
    font-family: var(--font-mono);
    transition: color var(--dur) var(--ease);
}

/* ═══ 5. NAV ═══ */
nav.site-nav {
    position: sticky;
    top: 0;
    z-index: 100;
    background: color-mix(in srgb, var(--bg) 82%, transparent);
    backdrop-filter: blur(14px) saturate(1.3);
    -webkit-backdrop-filter: blur(14px) saturate(1.3);
    border-bottom: 1px solid transparent;
    transition: background var(--dur) var(--ease),border-color 300ms linear;
}

    nav.site-nav.stuck {
        border-bottom-color: var(--line);
    }

.nav-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: var(--nav-h);
    gap: var(--sp-8);
}

.nav-logo {
    font-family: var(--font-head);
    font-weight: var(--wh);
    font-size: var(--fs-base);
    letter-spacing: var(--ls);
    color: var(--text);
    text-decoration: none;
    display: inline-flex;
    align-items: baseline;
    gap: 0.55rem;
    transition: font-family var(--dur) var(--ease),letter-spacing var(--dur) var(--ease);
}

.nav-logo-mark {
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    letter-spacing: 0.2em;
    color: var(--accent);
}

.nav-links {
    display: flex;
    gap: var(--sp-8);
    list-style: none;
}

    .nav-links a {
        color: var(--muted);
        text-decoration: none;
        font-size: var(--fs-xs);
        font-family: var(--font-mono);
        letter-spacing: 0.08em;
        text-transform: uppercase;
        position: relative;
        padding: 0.35rem 0;
        transition: color 220ms;
    }

        /* Underline grows from the left on hover, retracts to the right on
           leave — cheap, but it is the difference between "a link" and "a
           link that feels made". */
        .nav-links a::after {
            content: '';
            position: absolute;
            left: 0;
            bottom: 0;
            height: 1px;
            width: 100%;
            background: var(--accent);
            transform: scaleX(0);
            transform-origin: right;
            transition: transform 320ms var(--ease);
        }

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

            .nav-links a:hover::after,
            .nav-links a.current::after {
                transform: scaleX(1);
                transform-origin: left;
            }

.nav-actions {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
}

.nav-toggle {
    display: none;
    width: 40px;
    height: 34px;
    border: 1px solid var(--line);
    background: none;
    border-radius: var(--cr);
    cursor: pointer;
    padding: 0;
    place-items: center;
    gap: 4px;
    align-content: center;
    justify-items: center;
}

    .nav-toggle span {
        display: block;
        width: 16px;
        height: 1.5px;
        background: var(--text);
        transition: transform 300ms var(--ease), opacity 200ms;
    }

    .nav-toggle[aria-expanded="true"] span:first-child {
        transform: translateY(2.75px) rotate(45deg);
    }

    .nav-toggle[aria-expanded="true"] span:last-child {
        transform: translateY(-2.75px) rotate(-45deg);
    }

/* ═══ 6. CURSOR ═══ */
.cursor {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 950;
    pointer-events: none;
    display: none;
    place-items: center;
    width: 26px;
    height: 26px;
    margin: -13px 0 0 -13px;
    border: 1px solid var(--accent);
    border-radius: 999px;
    will-change: transform;
    transition: width 300ms var(--ease), height 300ms var(--ease), margin 300ms var(--ease), background 300ms var(--ease), opacity 200ms;
}

    .cursor-on .cursor {
        display: grid;
    }

    .cursor-label {
        font-family: var(--font-mono);
        font-size: 0.58rem;
        letter-spacing: 0.14em;
        text-transform: uppercase;
        color: var(--bg);
        opacity: 0;
        transform: scale(0.7);
        transition: opacity 200ms, transform 300ms var(--ease);
        white-space: nowrap;
    }

    .cursor.is-active {
        width: 74px;
        height: 74px;
        margin: -37px 0 0 -37px;
        background: var(--accent);
    }

        .cursor.is-active .cursor-label {
            opacity: 1;
            transform: scale(1);
        }

    .cursor.is-down {
        transform: scale(0.85);
    }

/* Hide the native cursor only where we actually draw a replacement. */
.cursor-on, .cursor-on a, .cursor-on button, .cursor-on [role="button"] {
    cursor: none;
}

/* ═══ 7. PAGE COVER ═══ */
.page-cover {
    display: none;
}

.js-ready .page-cover {
    position: fixed;
    inset: 0;
    z-index: 500;
    display: grid;
    place-items: center;
    background: var(--bg);
    transition: transform 900ms var(--ease), opacity 600ms var(--ease);
}

.cover-mark {
    font-family: var(--font-head);
    font-weight: var(--wh);
    font-size: clamp(3rem, 12vw, 7rem);
    letter-spacing: 0.08em;
    color: var(--text);
    opacity: 0;
    animation: coverMark 900ms var(--ease) 120ms forwards;
}

.cover-bar {
    position: absolute;
    bottom: 0;
    left: 0;
    height: 2px;
    width: 0;
    background: var(--accent);
    animation: coverBar 900ms var(--ease) forwards;
}

.page-cover.hide {
    transform: translateY(-100%);
    opacity: 0.4;
    pointer-events: none;
}

@keyframes coverMark {
    from { opacity: 0; transform: translateY(0.35em) scale(0.94); letter-spacing: 0.3em; }
    to   { opacity: 1; transform: none;                          letter-spacing: 0.08em; }
}

@keyframes coverBar {
    from { width: 0; }
    to   { width: 100%; }
}

/* ═══ 8. SCROLL PROGRESS + SECTION RAIL ═══ */
.scroll-progress {
    position: fixed;
    top: 0;
    left: 0;
    height: 2px;
    width: 100%;
    transform: scaleX(0);
    transform-origin: left;
    background: var(--accent);
    z-index: 900;
    will-change: transform;
    transition: background var(--dur) var(--ease);
}

.rail {
    position: fixed;
    right: clamp(0.8rem, 2vw, 1.6rem);
    top: 50%;
    transform: translateY(-50%);
    z-index: 90;
    display: grid;
    gap: 0.9rem;
    opacity: 0;
    pointer-events: none;
    transition: opacity 400ms var(--ease);
}

    .rail.show {
        opacity: 1;
        pointer-events: auto;
    }

    .rail a {
        display: flex;
        align-items: center;
        justify-content: flex-end;
        gap: 0.6rem;
        text-decoration: none;
        font-family: var(--font-mono);
        font-size: 0.6rem;
        letter-spacing: 0.12em;
        color: var(--muted);
    }

.rail-name {
    text-transform: uppercase;
    opacity: 0;
    transform: translateX(6px);
    transition: opacity 300ms var(--ease), transform 300ms var(--ease);
}

.rail-dot {
    width: 22px;
    height: 1px;
    background: currentColor;
    transition: width 300ms var(--ease), background 300ms var(--ease);
    flex: none;
}

.rail a:hover .rail-name,
.rail a.current .rail-name {
    opacity: 1;
    transform: none;
}

.rail a:hover,
.rail a.current {
    color: var(--accent);
}

    .rail a.current .rail-dot {
        width: 38px;
    }

/* ═══ 9. HERO ═══ */
.hero {
    position: relative;
    min-height: calc(100svh - var(--nav-h));
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: clamp(4rem, 10vh, 8rem) 0 clamp(3rem, 8vh, 6rem);
    border-top: none;
    overflow: hidden;
}

.hero-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: 0;
    pointer-events: none;
    opacity: 0.9;
}

.hero-inner {
    position: relative;
    z-index: 1;
    max-width: var(--wrap-wide);
    margin: 0 auto;
    padding: 0 var(--gutter);
    width: 100%;
}

.hero-eyebrow {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: var(--muted);
    margin-bottom: clamp(1.2rem, 3vh, 2.2rem);
}

.hero-eyebrow-line {
    width: clamp(28px, 6vw, 64px);
    height: 1px;
    background: var(--accent);
    transform-origin: left;
}

.hero-title {
    font-size: var(--fs-hero);
    line-height: 0.9;
    margin: 0 0 clamp(1.5rem, 4vh, 2.6rem);
    max-width: 14ch;
}

/* Each line is its own mask so the reveal wipes upward from a hard edge
   rather than fading in — closer to type being set than to a page loading. */
.hero-title .line {
    display: block;
    overflow: hidden;
    padding-bottom: 0.18em;
}

.hero-title .line-in {
    display: block;
    will-change: transform;
}

/* Lines sit below their mask until the cover hands off, then wipe up.
   Per-line delay is set in JS so the stagger survives copy edits. */
.js-ready .hero .line-in {
    transform: translateY(106%);
    transition: transform 950ms var(--ease);
}

.js-ready .hero.in .line-in {
    transform: none;
}

.js-ready .hero .hero-eyebrow-line {
    transform: scaleX(0);
    transition: transform 700ms var(--ease) 150ms;
}

.js-ready .hero.in .hero-eyebrow-line {
    transform: none;
}

.hero-accent {
    color: var(--accent);
    font-style: normal;
}

.hero-sub {
    color: var(--muted);
    font-size: var(--fs-lg);
    max-width: 32ch;
    margin-bottom: clamp(1.8rem, 4vh, 2.8rem);
}

.hero-actions {
    display: flex;
    align-items: center;
    gap: var(--sp-6);
    flex-wrap: wrap;
}

.hero-meta {
    position: relative;
    z-index: 1;
    max-width: var(--wrap-wide);
    margin: clamp(3rem, 9vh, 6rem) auto 0;
    padding: 0 var(--gutter);
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    gap: var(--sp-8);
    flex-wrap: wrap;
}

.hero-facts {
    display: flex;
    gap: clamp(1.5rem, 5vw, 4rem);
    flex-wrap: wrap;
}

.hero-fact dt {
    font-family: var(--font-mono);
    font-size: 0.6rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--muted);
    margin-bottom: 0.3rem;
}

.hero-fact dd {
    font-size: var(--fs-sm);
    color: var(--text);
}

.hero-fact .dot {
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: 999px;
    background: var(--accent);
    margin-right: 0.45rem;
    vertical-align: baseline;
}

.scroll-cue {
    display: inline-flex;
    align-items: center;
    gap: 0.7rem;
    text-decoration: none;
    color: var(--muted);
    font-family: var(--font-mono);
    font-size: 0.6rem;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    flex: none;
}

    .scroll-cue:hover {
        color: var(--text);
    }

.scroll-cue-track {
    position: relative;
    display: block;
    width: 1px;
    height: 46px;
    background: var(--line);
    overflow: hidden;
}

    .scroll-cue-track::after {
        content: '';
        position: absolute;
        inset: 0;
        background: var(--accent);
        transform-origin: top;
        animation: cueDraw 2.2s var(--ease) infinite;
    }

@keyframes cueDraw {
    0%   { transform: translateY(-100%); }
    55%  { transform: translateY(0); }
    100% { transform: translateY(100%); }
}

/* ═══ 10. BUTTONS ═══ */
.cta {
    position: relative;
    display: inline-block;
    padding: 0.85rem 1.9rem;
    background: var(--accent);
    color: var(--bg);
    text-decoration: none;
    font-size: var(--fs-xs);
    font-family: var(--font-mono);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    border: 1px solid var(--accent);
    border-radius: var(--cr);
    overflow: hidden;
    isolation: isolate;
    transition: color 320ms var(--ease), background var(--dur) var(--ease), border-color var(--dur) var(--ease), border-radius var(--dur) var(--ease);
}

    /* Fill wipes up from the bottom and the label inverts — the Flash-era
       button move, done with one pseudo-element and a transform. */
    .cta::before {
        content: '';
        position: absolute;
        inset: 0;
        z-index: -1;
        background: var(--bg);
        transform: translateY(101%);
        transition: transform 420ms var(--ease);
    }

    .cta:hover::before,
    .cta:focus-visible::before {
        transform: translateY(0);
    }

    .cta:hover,
    .cta:focus-visible {
        color: var(--accent);
    }

    .cta:active {
        transform: translateY(1px);
    }

.cta-ghost {
    background: none;
    color: var(--text);
    border-color: var(--line);
}

    .cta-ghost::before {
        background: var(--accent);
    }

    .cta-ghost:hover,
    .cta-ghost:focus-visible {
        color: var(--bg);
        border-color: var(--accent);
    }

.style-btn {
    height: 34px;
    padding: 0 1.1rem;
    border: 1px solid var(--line);
    background: var(--bg2);
    color: var(--muted);
    font-size: var(--fs-micro);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    cursor: pointer;
    border-radius: var(--cr);
    font-family: var(--font-mono);
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    transition: background var(--dur) var(--ease),border-color var(--dur) var(--ease),color var(--dur) var(--ease),border-radius var(--dur) var(--ease);
}

    .style-btn:hover {
        color: var(--text);
        border-color: var(--muted);
    }

.style-swatch {
    width: 9px;
    height: 9px;
    border-radius: 999px;
    background: var(--accent);
    flex: none;
    transition: background var(--dur) var(--ease);
}

.magnetic {
    will-change: transform;
}

#themeOverlay {
    position: fixed;
    inset: 0;
    z-index: 400;
    pointer-events: none;
    opacity: 0;
    clip-path: circle(0% at var(--tx, 50%) var(--ty, 0%));
}

    #themeOverlay.wipe {
        opacity: 1;
        clip-path: circle(150% at var(--tx, 50%) var(--ty, 0%));
        transition: clip-path 720ms var(--ease);
    }

    /* Must hold the circle open while fading. If the clip snapped back to 0%
       the overlay would vanish instantly instead of dissolving. */
    #themeOverlay.fade {
        opacity: 0;
        clip-path: circle(150% at var(--tx, 50%) var(--ty, 0%));
        transition: opacity 300ms linear;
    }

/* ═══ REVEAL ═══ */
.js-ready .reveal {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 500ms var(--ease),transform 500ms var(--ease);
}

    .js-ready .reveal.visible {
        opacity: 1;
        transform: none;
    }

.js-ready .stagger-item {
    opacity: 0;
    transition: opacity 500ms var(--ease);
}

    .js-ready .stagger.visible .stagger-item {
        opacity: 1;
    }

/* `.freeze *` alone misses <body> itself — and body owns the background and
   colour transitions, which are exactly the ones that must not animate while
   the theme is swapped behind the overlay. */
.freeze,
.freeze *,
.freeze *::before,
.freeze *::after {
    transition: none !important;
}

/* ══════════════════════════════════════════════════════════════
   11. LEGACY — sections not yet redesigned. Replaced round by round.
   ══════════════════════════════════════════════════════════════ */

/* ═══ §01 WORK — the ledger ═══════════════════════════════════
   Full-bleed ruled rows, oversized numerals, monospace metadata.
   The thumbnail leaves the row and trails the cursor instead of
   sitting in the layout. */

.work, .skills {
    padding: clamp(4.5rem, 11vh, 8rem) 0;
}

.sec-count {
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--muted);
    flex: none;
}

.work-list {
    list-style: none;
    margin-top: var(--sp-8);
    border-top: 1px solid var(--line);
}

.work-row {
    position: relative;
    border-bottom: 1px solid var(--line);
    cursor: pointer;
}

    /* Fill grows down on enter and retracts upward on leave — the origin
       flip is what makes it read as a wipe rather than a fade. */
    .work-row::before {
        content: '';
        position: absolute;
        inset: 0;
        background: var(--bg2);
        transform: scaleY(0);
        transform-origin: bottom;
        transition: transform 480ms var(--ease);
        pointer-events: none;
    }

    .work-row:hover::before,
    .work-row:focus-within::before {
        transform: scaleY(1);
        transform-origin: top;
    }

.work-inner {
    position: relative;
    z-index: 1;
    display: grid;
    grid-template-columns: clamp(2.6rem, 5vw, 6rem) minmax(0, 1fr) auto auto;
    align-items: center;
    gap: clamp(0.9rem, 2.5vw, 2.4rem);
    padding-top: clamp(1.4rem, 3vh, 2.4rem);
    padding-bottom: clamp(1.4rem, 3vh, 2.4rem);
}

.work-num {
    font-family: var(--font-mono);
    font-size: clamp(1.4rem, 3.4vw, 2.8rem);
    line-height: 1;
    color: var(--muted);
    opacity: 0.4;
    transition: color 420ms var(--ease), opacity 420ms var(--ease), transform 520ms var(--ease);
}

.work-row:hover .work-num,
.work-row:focus-within .work-num {
    color: var(--accent);
    opacity: 1;
    transform: translateX(5px);
}

.work-main {
    min-width: 0;
}

.work-title {
    font-size: clamp(1.15rem, 2.6vw, 2.1rem);
    line-height: 1.06;
    margin: 0 0 0.3rem;
}

.work-open {
    position: relative;
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    text-align: left;
    cursor: pointer;
    color: var(--text);
    font: inherit;
    letter-spacing: inherit;
    transition: transform 480ms var(--ease), color var(--dur) var(--ease);
}

    .work-open::after {
        content: '';
        position: absolute;
        left: 0;
        bottom: -3px;
        width: 100%;
        height: 1px;
        background: var(--accent);
        transform: scaleX(0);
        transform-origin: right;
        transition: transform 460ms var(--ease);
    }

.work-row:hover .work-open,
.work-row:focus-within .work-open {
    transform: translateX(8px);
}

    .work-row:hover .work-open::after,
    .work-row:focus-within .work-open::after {
        transform: scaleX(1);
        transform-origin: left;
    }

.work-sub {
    font-size: var(--fs-xs);
    color: var(--muted);
    transition: transform 520ms var(--ease), color var(--dur) var(--ease);
}

.work-row:hover .work-sub {
    transform: translateX(8px);
}

.work-tags {
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.2rem 0.75rem;
    max-width: 24ch;
}

    .work-tags li {
        font-family: var(--font-mono);
        font-size: var(--fs-micro);
        letter-spacing: 0.07em;
        color: var(--muted);
        white-space: nowrap;
    }

        .work-tags li::before {
            content: '/';
            color: var(--accent);
            opacity: 0.55;
            margin-right: 0.3rem;
        }

.work-actions {
    position: relative;
    z-index: 3;
    display: flex;
    gap: 0.45rem;
    flex-wrap: wrap;
    justify-content: flex-end;
}

.work-act {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.5rem 0.85rem;
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--muted);
    text-decoration: none;
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: var(--cr);
    cursor: pointer;
    white-space: nowrap;
    transition: color 240ms, background 240ms, border-color 240ms, transform 260ms var(--ease);
}

    .work-act:hover,
    .work-act:focus-visible {
        color: var(--bg);
        background: var(--accent);
        border-color: var(--accent);
        transform: translateY(-2px);
    }

/* Inline thumbnail is the touch fallback for the cursor-following peek.
   Sources range from a 3.4:1 site screenshot to a near-square game render,
   so the frame is fixed at 2:1 and the image is cropped into it — otherwise
   each row sets its own height and the ledger stops reading as a list.
   It sits in the text column, not under the numeral, so the row lines up. */
.work-thumb {
    display: none;
    width: 100%;
    max-width: 460px;
    /* height:auto is what lets aspect-ratio win over the img height attribute. */
    height: auto;
    aspect-ratio: 2 / 1;
    object-fit: cover;
    border-radius: var(--cr);
    grid-column: 2 / -1;
}

.work-peek {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 80;
    width: clamp(190px, 19vw, 300px);
    aspect-ratio: 16 / 10;
    border-radius: var(--cr);
    overflow: hidden;
    pointer-events: none;
    opacity: 0;
    will-change: transform;
    box-shadow: 0 30px 60px -26px rgba(0,0,0,0.55);
    transition: opacity 280ms var(--ease);
}

    .work-peek.show {
        opacity: 1;
    }

    .work-peek img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
    }

/* Rows wipe in from alternating sides.
   The clip lives on .work-inner, never on the observed <li>: Chrome folds a
   target's own clip-path into its intersection rect, so clipping the <li> to
   zero width would stop the IntersectionObserver that reveals it from ever
   firing. The rules stay drawn and the content wipes in over them. */
.js-ready .work-inner {
    opacity: 0;
    clip-path: inset(0 100% 0 0);
    transition: opacity 700ms var(--ease), clip-path 900ms var(--ease);
}

    .js-ready .work-row[data-side="right"] .work-inner {
        clip-path: inset(0 0 0 100%);
    }

    .js-ready .work-row.in .work-inner {
        opacity: 1;
        clip-path: inset(0 0 0 0);
    }

/* ── Paging ──────────────────────────────────────────────────
   The ledger only ever shows one page of rows; the rest are taken out of
   flow entirely. A page change is a slide, not a cut: the whole list leaves
   toward the direction of travel, the rows are swapped while it is
   invisible, then it comes back in from the opposite edge. The section
   clips horizontally so that travel never widens the document. */
.work {
    overflow-x: clip;
}

.work-row.is-off {
    display: none;
}

.work-list.pg-anim {
    transition: transform 300ms var(--ease), opacity 240ms var(--ease);
}

.work-list.pg-hide {
    opacity: 0;
}

.work-list.pg-left {
    transform: translateX(-4%);
}

.work-list.pg-right {
    transform: translateX(4%);
}

.work-pager {
    display: flex;
    align-items: center;
    gap: clamp(0.5rem, 1.6vw, 1rem);
    margin-top: var(--sp-8);
}

.wpg-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    flex: none;
    background: none;
    border: 1px solid color-mix(in srgb, var(--muted) 30%, transparent);
    border-radius: 999px;
    color: var(--muted);
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
    transition: color 240ms var(--ease), border-color 240ms var(--ease), transform 240ms var(--ease);
}

    .wpg-btn:hover:not(:disabled) {
        color: var(--accent);
        border-color: var(--accent);
    }

    .wpg-btn:active:not(:disabled) {
        transform: scale(0.94);
    }

    .wpg-btn:disabled {
        opacity: 0.3;
        cursor: default;
    }

.wpg-dots {
    display: flex;
    align-items: center;
    gap: 0.45rem;
}

/* Bars rather than circles — the section is ruled lines all the way down.
   The bar itself is the 2px content box; the padding around it is dead
   space that keeps the hit target finger-sized. */
.wpg-dot {
    width: 26px;
    height: 20px;
    padding: 9px 0;
    background: none;
    border: none;
    cursor: pointer;
    background-clip: content-box;
    background-color: color-mix(in srgb, var(--muted) 40%, transparent);
    transition: background-color 300ms var(--ease), width 300ms var(--ease);
}

    .wpg-dot:hover {
        background-color: var(--muted);
    }

    .wpg-dot.on {
        width: 42px;
        background-color: var(--accent);
    }

.wpg-readout {
    margin-left: auto;
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--muted);
}

/* ═══ §02 SKILLS — the console ═══════════════════════════════ */

.skills-stage {
    position: relative;
    border: 1px solid var(--line);
    border-radius: var(--cr);
    padding: clamp(1.4rem, 3.5vw, 2.8rem);
    background:
        linear-gradient(var(--line) 1px, transparent 1px) 0 0 / 100% 2.5rem,
        linear-gradient(90deg, var(--line) 1px, transparent 1px) 0 0 / 2.5rem 100%,
        var(--bg2);
    transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), border-radius var(--dur) var(--ease);
}

.skills-tabs {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0.15rem;
    padding: 0.25rem;
    margin-bottom: var(--sp-6);
    background: var(--bg2);
    border: 1px solid var(--line);
    border-radius: var(--cr);
}

.stab {
    height: 34px;
    padding: 0 1.05rem;
    border: none;
    background: none;
    color: var(--muted);
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    cursor: pointer;
    border-radius: calc(var(--cr) - 2px);
    transition: color 240ms, background 240ms;
}

    .stab:hover {
        color: var(--text);
    }

    .stab.on {
        background: var(--text);
        color: var(--bg);
    }

.skills-panel {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: clamp(1.4rem, 3.5vw, 3.2rem);
}

    .skills-panel.hidden {
        display: none;
    }

    .skills-panel:focus-visible {
        outline: 2px solid var(--accent);
        outline-offset: 6px;
    }

.skill-heading {
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--muted);
    display: block;
    margin-bottom: 1.1rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid var(--line);
    transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.skill-list {
    display: grid;
    gap: 0.1rem;
}

.skill-item {
    display: grid;
    grid-template-columns: minmax(6rem, 10rem) minmax(0, 1fr) 2.9rem;
    align-items: center;
    gap: 0.9rem;
    padding: 0.42rem 0;
}

.skill-name {
    font-size: var(--fs-xs);
    color: var(--text);
    transition: color var(--dur) var(--ease);
}

.skill-bar {
    position: relative;
    height: 6px;
    background: var(--line);
    border-radius: 999px;
    overflow: hidden;
    transition: background var(--dur) var(--ease);
}

    /* Quarter ticks sit above the fill so the bar reads as a measured
       scale rather than a decorative blob. */
    .skill-bar::after {
        content: '';
        position: absolute;
        inset: 0;
        z-index: 2;
        pointer-events: none;
        background: repeating-linear-gradient(90deg,
            transparent 0,
            transparent calc(25% - 1px),
            var(--bg) calc(25% - 1px),
            var(--bg) 25%);
        opacity: 0.55;
    }

.skill-fill {
    display: block;
    height: 100%;
    width: var(--pct);
    background: var(--accent);
    border-radius: 999px;
    transition: width 1100ms var(--ease), background var(--dur) var(--ease);
}

.skill-val {
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    color: var(--muted);
    text-align: right;
    font-variant-numeric: tabular-nums;
    transition: color var(--dur) var(--ease);
}

/* ═══ §03 ABOUT — the letter and the paperwork ═══════════════
   Prose on the left, credentials on the right: the two things a reader
   is actually here for, side by side rather than stacked. */
.about-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
    gap: clamp(2.5rem, 6vw, 5.5rem);
    align-items: start;
}

.about-bio p {
    color: var(--muted);
    font-size: var(--fs-sm);
    line-height: 1.85;
    max-width: 60ch;
    margin-bottom: var(--sp-4);
    transition: color var(--dur) var(--ease);
}

.about-bio strong {
    color: var(--text);
    font-weight: inherit;
    transition: color var(--dur) var(--ease);
}

.about-lead {
    font-family: var(--font-head);
    font-size: var(--fs-xl) !important;
    line-height: 1.3 !important;
    color: var(--text) !important;
    letter-spacing: var(--ls);
    margin-bottom: var(--sp-6) !important;
    max-width: 22ch !important;
}

.about-facts {
    margin-top: var(--sp-8);
    border-top: 1px solid var(--line);
    transition: border-color var(--dur) var(--ease);
}

.about-fact {
    display: grid;
    grid-template-columns: minmax(6rem, 9rem) minmax(0, 1fr);
    gap: var(--sp-4);
    padding: 0.85rem 0;
    border-bottom: 1px solid var(--line);
    transition: border-color var(--dur) var(--ease);
}

    .about-fact dt {
        font-family: var(--font-mono);
        font-size: var(--fs-micro);
        letter-spacing: 0.14em;
        text-transform: uppercase;
        color: var(--muted);
        padding-top: 0.15rem;
    }

    .about-fact dd {
        font-size: var(--fs-xs);
        color: var(--text);
        line-height: 1.6;
        transition: color var(--dur) var(--ease);
    }

/* ═══ Documents ═══ */
.docs-head {
    display: flex;
    align-items: center;
    gap: var(--sp-4);
    margin-bottom: var(--sp-4);
}

.docs-label {
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--muted);
    flex: none;
}

.doc-list {
    list-style: none;
    display: grid;
    gap: var(--sp-3);
}

.doc-card {
    position: relative;
    display: grid;
    gap: 0.45rem;
    padding: 1.4rem 1.5rem;
    background: var(--bg2);
    border: 1px solid var(--line);
    border-radius: var(--cr);
    transition: background var(--dur) var(--ease), border-color 260ms, transform 400ms var(--ease);
}

    .doc-card:hover,
    .doc-card:focus-within {
        border-color: var(--accent);
        transform: translateY(-2px);
    }

/* A stamp in the corner rather than an icon in the flow — it reads as
   paperwork without pushing the title off its own line. */
.doc-kind {
    position: absolute;
    top: 1.2rem;
    right: 1.3rem;
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    letter-spacing: 0.14em;
    color: var(--accent);
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 0.2rem 0.6rem;
    transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.doc-name {
    font-size: var(--fs-sm);
    line-height: 1.35;
    padding-right: 4rem;
}

.doc-desc {
    font-size: var(--fs-xs);
    color: var(--muted);
    line-height: 1.65;
    transition: color var(--dur) var(--ease);
}

.doc-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    letter-spacing: 0.08em;
    color: var(--muted);
    margin-top: 0.15rem;
}

    .doc-meta span + span::before {
        content: '·';
        margin-right: 0.5rem;
    }

.doc-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
    margin-top: 0.55rem;
}

.doc-act {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text);
    text-decoration: none;
    padding-bottom: 0.2rem;
    border-bottom: 1px solid var(--line);
    transition: color 200ms, border-color 200ms;
}

    .doc-act span {
        color: var(--accent);
        transition: transform 300ms var(--ease);
        display: inline-block;
    }

    .doc-act:hover,
    .doc-act:focus-visible {
        color: var(--accent);
        border-bottom-color: var(--accent);
    }

    .doc-act:hover span {
        transform: translate(2px, -2px);
    }

    .doc-act[download]:hover span {
        transform: translateY(2px);
    }

.docs-empty {
    font-size: var(--fs-xs);
    color: var(--muted);
    line-height: 1.6;
}

/* ═══ Education timeline ═══ */
.timeline {
    list-style: none;
    position: relative;
    padding-left: 1.5rem;
}

    .timeline::before {
        content: '';
        position: absolute;
        left: 4px;
        top: 0.55rem;
        bottom: 0.55rem;
        width: 1px;
        background: var(--line);
    }

.tl-item {
    position: relative;
    padding-bottom: 1.4rem;
}

    .tl-item:last-child {
        padding-bottom: 0;
    }

    .tl-item::before {
        content: '';
        position: absolute;
        left: -1.5rem;
        top: 0.5rem;
        width: 9px;
        height: 9px;
        border-radius: 999px;
        background: var(--bg);
        border: 1px solid var(--accent);
        transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
    }

.tl-date {
    display: block;
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    letter-spacing: 0.12em;
    color: var(--accent);
    margin-bottom: 0.2rem;
    transition: color var(--dur) var(--ease);
}

.tl-title {
    font-size: var(--fs-sm);
    line-height: 1.35;
    margin-bottom: 0.2rem;
}

.tl-desc {
    font-size: var(--fs-xs);
    color: var(--muted);
    line-height: 1.6;
}

@media (max-width: 820px) {
    .work-inner {
        grid-template-columns: clamp(2.2rem, 8vw, 3rem) minmax(0, 1fr);
        row-gap: 0.9rem;
    }

    .work-tags,
    .work-actions {
        grid-column: 2;
        justify-content: flex-start;
        max-width: none;
    }

    .skills-panel {
        grid-template-columns: minmax(0, 1fr);
    }
}

@media (max-width: 560px) {
    .skill-item {
        grid-template-columns: minmax(5rem, 8rem) minmax(0, 1fr) 2.6rem;
        gap: 0.6rem;
    }
}

/* No cursor to follow on touch, so the thumbnail returns to the layout. */
@media (hover: none), (pointer: coarse) {
    .work-thumb {
        display: block;
    }

    .work-peek {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .js-ready .work-inner {
        opacity: 1 !important;
        clip-path: none !important;
    }

    .work-peek {
        display: none;
    }
}

/* ═══ §04 PHOTOGRAPHY — the darkroom ═════════════════════════
   The section rebinds the palette tokens locally, so every shared
   component inside it (sec-head, rules, labels) inverts for free. */
.photography {
    --text: var(--on-deep);
    --muted: color-mix(in srgb, var(--on-deep) 55%, transparent);
    --line: color-mix(in srgb, var(--on-deep) 15%, transparent);
    --accent: var(--accent-deep);
    background: var(--deep);
    color: var(--on-deep);
    border-top-color: transparent;
    transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}

.photo-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    margin-bottom: var(--sp-8);
}

.photo-filter {
    display: flex;
    gap: 0.6rem;
    flex-wrap: wrap;
}

.fbtn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    height: 34px;
    padding: 0 0.95rem;
    background: none;
    border: 1px solid var(--line);
    color: var(--muted);
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    cursor: pointer;
    border-radius: 999px;
    transition: background 220ms, color 220ms, border-color 220ms;
}

.fbtn-n {
    font-size: 0.6rem;
    opacity: 0.55;
    letter-spacing: 0;
}

    .fbtn:hover {
        color: var(--on-deep);
        border-color: color-mix(in srgb, var(--on-deep) 40%, transparent);
    }

    .fbtn.on {
        background: var(--on-deep);
        border-color: var(--on-deep);
        color: var(--deep);
    }

.photo-gear {
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    letter-spacing: 0.1em;
    color: var(--muted);
}

/* Justified rows: flex-basis and flex-grow both track the frame's own
   aspect ratio, so a row fills the width at a single shared height.
   max-width caps how far a short last row is allowed to stretch. */
.photo-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 0.9rem;
    --row-h: clamp(15rem, 24vw, 23rem);
}

.photo-item {
    position: relative;
    height: var(--row-h);
    flex: var(--ar) 1 calc(var(--ar) * var(--row-h));
    max-width: calc(var(--ar) * var(--row-h) * 1.7);
    min-width: 0;
    overflow: hidden;
    border-radius: var(--cr);
    cursor: pointer;
    background: color-mix(in srgb, var(--on-deep) 6%, transparent);
    transition: border-radius var(--dur) var(--ease), transform 500ms var(--ease), box-shadow 500ms var(--ease), opacity 260ms var(--ease);
}

    .photo-item:focus-visible {
        outline: 2px solid var(--accent);
        outline-offset: 3px;
    }

    .photo-item img {
        width: 100%;
        height: 100%;
        display: block;
        object-fit: cover;
        filter: saturate(0.92) contrast(1.04);
        transform: scale(1.001);
        transition: transform 900ms var(--ease), filter 500ms var(--ease);
    }

    .photo-item:hover,
    .photo-item:focus-visible {
        box-shadow: 0 26px 50px -24px rgba(0,0,0,0.75);
    }

        .photo-item:hover img,
        .photo-item:focus-visible img {
            transform: scale(1.055);
            filter: saturate(1) contrast(1.02);
        }

/* Filtered-out frames leave the flow entirely — with a justified row the
   layout has to reflow, so fading in place is not an option. */
.photo-item.is-hidden {
    display: none;
}

.photo-item.is-in {
    animation: photoIn 520ms var(--ease) backwards;
}

@keyframes photoIn {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
}

.photo-cap {
    position: absolute;
    inset: auto 0 0 0;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: baseline;
    gap: 0.1rem 0.7rem;
    padding: 3.5rem 1.1rem 1rem;
    background: linear-gradient(to top, rgba(0,0,0,0.78), rgba(0,0,0,0.34) 45%, transparent);
    color: #fff;
    pointer-events: none;
}

.photo-idx {
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    color: var(--accent);
    letter-spacing: 0.1em;
}

.photo-name {
    font-size: var(--fs-xs);
    letter-spacing: var(--ls);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.photo-tag {
    font-family: var(--font-mono);
    font-size: 0.6rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: rgba(255,255,255,0.6);
}

/* The capture data is the reward for looking closer, so it stays folded
   away until the frame is hovered or focused. */
.photo-exif {
    grid-column: 2 / -1;
    font-family: var(--font-mono);
    font-size: 0.6rem;
    letter-spacing: 0.08em;
    color: rgba(255,255,255,0.62);
    max-height: 0;
    opacity: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    transition: max-height 400ms var(--ease), opacity 300ms var(--ease), margin-top 400ms var(--ease);
}

.photo-item:hover .photo-exif,
.photo-item:focus-visible .photo-exif {
    max-height: 1.4rem;
    margin-top: 0.35rem;
    opacity: 1;
}

.photo-empty {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    color: var(--muted);
    padding: var(--sp-8) 0;
}

.contact-split {
    display: grid;
    grid-template-columns: 0.9fr 1.1fr;
    gap: 4.5rem;
    align-items: start;
}

.contact-info h2 {
    font-size: clamp(2.6rem,5vw,4.2rem);
    line-height: 1;
    margin-bottom: 1.2rem;
}

.contact-lead {
    color: var(--muted);
    font-size: 0.95rem;
    line-height: 1.7;
    max-width: 34ch;
    margin-bottom: 1.8rem;
}

.contact-info .cta {
    margin-bottom: 2.2rem;
}

.link-list {
    list-style: none;
    display: grid;
    gap: 0.9rem;
}

    .link-list a {
        color: var(--muted);
        text-decoration: none;
        font-size: 0.92rem;
        border-bottom: 1px solid var(--line);
        padding-bottom: 0.9rem;
        display: flex;
        align-items: center;
        gap: 0.75rem;
        transition: color 200ms,border-color var(--dur) var(--ease);
    }

        .link-list a:hover {
            color: var(--text);
        }

    .link-list .link-ico {
        width: 18px;
        height: 18px;
        flex: 0 0 auto;
        opacity: 0.75;
        transition: opacity 200ms,transform var(--dur) var(--ease);
    }

    .link-list a:hover .link-ico {
        opacity: 1;
        transform: translateY(-1px);
    }

.contact-form {
    display: grid;
    gap: 1.3rem;
}

.form-row {
    display: grid;
    gap: 0.5rem;
}

    .form-row label {
        font-size: 0.72rem;
        letter-spacing: 0.1em;
        text-transform: uppercase;
        color: var(--muted);
        font-family: var(--font-mono);
    }

    .form-row input,
    .form-row textarea {
        width: 100%;
        padding: 0.85rem 1rem;
        background: var(--bg2);
        border: 1px solid var(--line);
        border-radius: var(--cr);
        color: var(--text);
        font-family: var(--font);
        font-size: 0.92rem;
        resize: vertical;
        transition: background var(--dur) var(--ease),border-color 150ms,border-radius var(--dur) var(--ease);
    }

        .form-row input:focus,
        .form-row textarea:focus {
            outline: none;
            border-color: var(--accent);
        }

.form-hp {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    opacity: 0;
    pointer-events: none;
}

.form-submit {
    justify-self: start;
    cursor: pointer;
}

    .form-submit:disabled {
        opacity: 0.6;
        cursor: default;
    }

.form-status {
    font-size: 0.85rem;
    min-height: 1.2em;
    color: var(--muted);
}

    .form-status.success {
        color: var(--accent);
    }

    .form-status.error {
        color: #c0405a;
    }

footer {
    padding: 2.2rem 0;
    border-top: 1px solid var(--line);
    color: var(--muted);
    font-size: 0.78rem;
    transition: border-color var(--dur) var(--ease);
}

/* ═══ MODALS ═══ */
.modal-bg, .fullscreen-bg {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.82);
    backdrop-filter: blur(16px);
    z-index: 200;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}

    .modal-bg.open, .fullscreen-bg.open {
        display: flex;
    }

.modal-x {
    position: fixed;
    top: 1.4rem;
    right: 1.4rem;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: rgba(255,255,255,0.1);
    border: 1px solid rgba(255,255,255,0.16);
    color: #fff;
    font-size: 1.1rem;
    cursor: pointer;
    display: grid;
    place-items: center;
    transition: background 150ms;
    z-index: 10;
}

    .modal-x:hover {
        background: rgba(255,255,255,0.22);
    }

/* --pa is the real aspect ratio of whichever frame is open, set by JS.
   Sizing the card from it means portraits and landscapes both fit the
   viewport uncropped instead of being squared off. */
.polaroid {
    --pa: 1;
    background: #f4ecdb;
    padding: 1.1rem 1.1rem 4.6rem;
    width: min(520px, 90vw, calc((84vh - 8rem) * var(--pa)));
    box-shadow: 0 40px 90px rgba(0,0,0,0.55),inset 0 0 0 1px rgba(0,0,0,0.04);
    transform: rotate(-1.8deg);
    animation: pop 380ms cubic-bezier(0.22,1,0.36,1) forwards;
    position: relative;
}

@keyframes pop {
    from { transform: rotate(-1.8deg) scale(0.82); opacity: 0; }
    to   { transform: rotate(-1.8deg) scale(1);    opacity: 1; }
}

.polaroid::before {
    content: '';
    position: absolute;
    top: -11px;
    left: 50%;
    transform: translateX(-50%) rotate(-1.5deg);
    width: 68px;
    height: 20px;
    background: rgba(255,255,255,0.52);
    border: 1px solid rgba(0,0,0,0.06);
}

.polaroid img {
    width: 100%;
    aspect-ratio: var(--pa);
    object-fit: cover;
    display: block;
    border-radius: 1px;
    filter: sepia(0.1) contrast(1.04) saturate(0.9);
    cursor: zoom-in;
    overflow: hidden;
    transform-origin: center center;
}

.polaroid img.swap {
    animation: polSwap 340ms var(--ease);
}

@keyframes polSwap {
    from { opacity: 0.25; }
    to   { opacity: 1; }
}

/* Stepping through the set from inside the modal, so a visitor never has
   to close and re-aim at a thumbnail to see the next frame. */
.photo-nav {
    position: fixed;
    top: 50%;
    transform: translateY(-50%);
    width: 46px;
    height: 46px;
    border-radius: 50%;
    background: rgba(255,255,255,0.1);
    border: 1px solid rgba(255,255,255,0.16);
    color: #fff;
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
    display: none;
    place-items: center;
    transition: background 150ms, transform 260ms var(--ease);
    z-index: 10;
}

    .modal-bg.open .photo-nav {
        display: grid;
    }

    .photo-nav.prev {
        left: clamp(0.6rem, 3vw, 2.2rem);
    }

    .photo-nav.next {
        right: clamp(0.6rem, 3vw, 2.2rem);
    }

    .photo-nav:hover {
        background: rgba(255,255,255,0.22);
    }

    .photo-nav.prev:hover {
        transform: translateY(-50%) translateX(-3px);
    }

    .photo-nav.next:hover {
        transform: translateY(-50%) translateX(3px);
    }

    .photo-nav[hidden] {
        display: none !important;
    }

/* Fixed to the viewport rather than the card: a portrait frame makes the
   polaroid tall enough that anything hung above it lands off-screen. */
.pol-tip {
    display: none;
    position: fixed;
    bottom: 1.6rem;
    left: 50%;
    transform: translateX(-50%);
    background: rgba(0, 0, 0, 0.65);
    color: #fff;
    font-size: 0.72rem;
    padding: 0.35rem 0.85rem;
    border-radius: 999px;
    white-space: nowrap;
    opacity: 0;
    transition: opacity 300ms ease;
    pointer-events: none;
    font-family: var(--font-mono);
    letter-spacing: 0.05em;
}

    .pol-tip.visible {
        opacity: 1;
    }

.pol-meta {
    padding-top: 1.1rem;
}

    .pol-meta h3 {
        font-family: 'DM Serif Display',serif;
        font-size: 1.4rem;
        color: #1c160f;
        margin-bottom: 0.15rem;
        font-weight: 400;
    }

    .pol-meta p {
        font-family: 'DM Serif Display',serif;
        font-size: 0.85rem;
        color: #7a6a56;
        font-style: italic;
    }

    .pol-meta .pol-exif {
        display: flex;
        justify-content: space-between;
        align-items: baseline;
        gap: 1rem;
        font-family: var(--font-mono);
        font-size: 0.62rem;
        letter-spacing: 0.07em;
        font-style: normal;
        color: #9a8a74;
        margin-top: 0.5rem;
        padding-top: 0.5rem;
        border-top: 1px solid rgba(0,0,0,0.08);
    }

.pol-count {
    flex: none;
    color: #b3a48d;
}

.fullscreen-bg {
    background: rgba(0,0,0,0.95);
    cursor: zoom-out;
    z-index: 300;
}

    .fullscreen-bg img {
        max-width: min(1000px, 94vw);
        max-height: 92vh;
        object-fit: contain;
        transform-origin: center center;
        cursor: grab;
    }

.full-tips {
    position: fixed;
    right: 1.5rem;
    top: 50%;
    transform: translateY(-50%);
    display: none;
    flex-direction: column;
    gap: 0.75rem;
    opacity: 0;
    transition: opacity 400ms ease;
    pointer-events: none;
}

    .full-tips.visible {
        opacity: 1;
    }

.full-tip {
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.15);
    color: rgba(255, 255, 255, 0.85);
    font-size: 0.82rem;
    padding: 0.5rem 1.1rem;
    border-radius: 999px;
    white-space: nowrap;
    font-family: var(--font-mono);
    letter-spacing: 0.06em;
}

.proj-modal-card {
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: var(--cr);
    width: min(780px,94vw);
    animation: slideUp 380ms cubic-bezier(0.22,1,0.36,1) forwards;
    max-height: 90vh;
    overflow-y: auto;
    transition: background var(--dur) var(--ease),border-color var(--dur) var(--ease),border-radius var(--dur) var(--ease);
}

@keyframes slideUp {
    from { transform: translateY(24px); opacity: 0; }
    to   { transform: translateY(0);    opacity: 1; }
}

.proj-gallery {
    position: relative;
    width: 100%;
    height: 260px;
    overflow: hidden;
    background: var(--bg2);
    touch-action: pan-y;
}

.proj-gallery-track {
    display: flex;
    height: 100%;
    transition: transform 420ms var(--ease);
}

    .proj-gallery-track img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
        flex: 0 0 100%;
        filter: saturate(0.85) contrast(1.05);
        -webkit-user-drag: none;
        user-select: none;
    }

.proj-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 38px;
    height: 38px;
    border-radius: 50%;
    border: 1px solid rgba(255,255,255,0.25);
    background: rgba(0,0,0,0.4);
    color: #fff;
    font-size: 1.3rem;
    line-height: 1;
    cursor: pointer;
    display: grid;
    place-items: center;
    transition: background 150ms;
}

    .proj-nav:hover {
        background: rgba(0,0,0,0.6);
    }

    .proj-nav.prev {
        left: 0.8rem;
    }

    .proj-nav.next {
        right: 0.8rem;
    }

    .proj-nav.hidden {
        display: none;
    }

.proj-dots {
    position: absolute;
    bottom: 0.8rem;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 0.4rem;
}

.proj-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: rgba(255,255,255,0.4);
    border: none;
    cursor: pointer;
    padding: 0;
    transition: background 150ms, transform 150ms;
}

    .proj-dot.on {
        background: #fff;
        transform: scale(1.25);
    }

.proj-modal-body {
    padding: 2rem 2.5rem 2.5rem;
}

    .proj-modal-body h3 {
        font-size: clamp(1.6rem,3vw,2.2rem);
        margin-bottom: 1rem;
        line-height: 1.1;
    }

    .proj-modal-body p {
        color: var(--muted);
        font-size: 0.95rem;
        line-height: 1.7;
        margin-bottom: 1.5rem;
    }

.proj-modal-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-bottom: 2rem;
}

    .proj-modal-tags .tag {
        font-size: 0.75rem;
        padding: 0.3rem 0.8rem;
    }

.proj-modal-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
}

    .proj-modal-actions:empty {
        display: none;
    }

/* ═══ RESPONSIVE ═══ */
@media (max-width:900px) {
    .rail {
        display: none;
    }
}

@media (max-width:760px) {
    .skills-panel {
        grid-template-columns: 1fr;
        gap: 2rem;
    }

    .skill-item {
        grid-template-columns: 8rem 1fr;
    }
}

@media (max-width:900px) {
    .about-grid {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--sp-12);
    }
}

/* Below this the justified rows stop paying for themselves — a single
   column at each frame's own ratio shows more of every photograph. */
@media (max-width:760px) {
    .photo-grid {
        gap: 1.4rem;
    }

    .photo-item {
        height: auto;
        flex: 1 1 100%;
        max-width: none;
        aspect-ratio: var(--ar);
    }

    .photo-bar {
        align-items: flex-start;
    }

    .photo-exif {
        max-height: 1.4rem;
        margin-top: 0.35rem;
        opacity: 1;
    }
}

@media (max-width:520px) {
    .about-fact {
        grid-template-columns: minmax(0, 1fr);
        gap: 0.3rem;
    }

    .doc-card {
        padding: 1.2rem 1.15rem;
    }

    .doc-name {
        padding-right: 3.4rem;
    }
}

@media (max-width:620px) {
    .contact-split {
        grid-template-columns: 1fr;
        gap: 2.5rem;
    }
}

@media (max-width:700px) {
    .nav-toggle {
        display: grid;
    }

    .nav-links {
        position: fixed;
        inset: var(--nav-h) 0 auto 0;
        flex-direction: column;
        gap: 0;
        background: var(--bg);
        border-bottom: 1px solid var(--line);
        padding: var(--sp-2) var(--gutter) var(--sp-6);
        transform: translateY(-8px);
        opacity: 0;
        visibility: hidden;
        transition: opacity 260ms var(--ease), transform 320ms var(--ease), visibility 0s linear 320ms;
    }

        .nav-links.open {
            opacity: 1;
            transform: none;
            visibility: visible;
            transition-delay: 0s;
        }

        .nav-links a {
            display: block;
            padding: 0.9rem 0;
            font-size: var(--fs-sm);
            border-bottom: 1px solid var(--line);
        }

            .nav-links a::after {
                display: none;
            }

    .hero-meta {
        flex-direction: column;
        align-items: flex-start;
        gap: var(--sp-6);
    }
}

@media (max-width:620px) {
    /* No room beside the card at this width, so the gallery controls drop
       to the corners below it rather than sitting on the photograph. */
    .photo-nav {
        top: auto;
        bottom: 1.1rem;
        transform: none;
        width: 40px;
        height: 40px;
        font-size: 1.3rem;
    }

        .photo-nav.prev:hover,
        .photo-nav.next:hover {
            transform: none;
        }

    .pol-tip {
        bottom: 4.6rem;
        max-width: calc(100vw - 9rem);
        white-space: normal;
        text-align: center;
    }
}

@media (max-width:600px) {
    .proj-modal-body {
        padding: 1.5rem;
    }
}

@media (hover: hover) and (pointer: fine) {
    .full-tips {
        display: flex;
    }

    .pol-tip {
        display: flex;
    }
}

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

    /* The reveal system hides content until JS marks it visible; with
       transitions neutralised it must start visible instead. */
    .js-ready .reveal,
    .js-ready .stagger-item {
        opacity: 1 !important;
        transform: none !important;
    }

    .js-ready .hero .line-in,
    .js-ready .hero .hero-eyebrow-line {
        transform: none !important;
    }

    .cursor {
        display: none !important;
    }

    .cursor-on, .cursor-on a, .cursor-on button, .cursor-on [role="button"] {
        cursor: auto;
    }

    .scroll-cue-track::after {
        animation: none;
        transform: none;
    }
}
