/* ═══════════════════════════════════════════════════════════════════════════
   UrbanQuest Inc. — stylesheet
   Layout is fluid down to 320px. Breakpoints: 1200 / 1024 / 768 / 560.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── FONTS ── */
/* DM Sans ships as a variable font — one file covers weights 300–600. */
@font-face {
    font-family: 'DM Sans';
    font-style: normal;
    font-weight: 300 600;
    font-display: swap;
    src: url('assets/fonts/dm-sans-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'DM Sans';
    font-style: normal;
    font-weight: 300 600;
    font-display: swap;
    src: url('assets/fonts/dm-sans-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'DM Sans';
    font-style: italic;
    font-weight: 300 600;
    font-display: swap;
    src: url('assets/fonts/dm-sans-italic-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'DM Sans';
    font-style: italic;
    font-weight: 300 600;
    font-display: swap;
    src: url('assets/fonts/dm-sans-italic-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ── TOKENS ── */
:root {
    --black: #0d0d0d;
    --black-deep: #080808;
    --white: #f5f4f0;

    /* ── The slate accent is reserved for ICONIC ──
       #9ba4ad is a cool blue-grey; --white is a warm off-white. Used broadly,
       the two hues read as a third colour fighting the black-and-white scheme.
       So the slate now appears in exactly one place — the ICONIC line in the
       hero — where it earns its keep as a deliberate accent.

       Everywhere else, tonal steps of --white do the job the slate used to do.
       They carry the same hierarchy without introducing a competing hue.
       Do not reach for --accent when you want "a grey"; reach for these. */
    --accent: #9ba4ad;
    --accent-light: #bec6ce;

    --label: rgba(245, 244, 240, 0.68); /* micro-labels, tags, eyebrows */
    --label-dim: rgba(245, 244, 240, 0.42); /* tertiary text */
    --rule: rgba(245, 244, 240, 0.22); /* hairlines, dividers, borders */
    --rule-soft: rgba(245, 244, 240, 0.12); /* barely-there edges */

    --mid-grey: #4a4a4a;
    --light-grey: #aeaaa4;
    --body-grey: #9a9690;
    --faint: #3a3830;
    --border: rgba(245, 244, 240, 0.16);

    /* Display and body are the same family, separated by weight, size, and
       tracking rather than by typeface. Bebas Neue was condensed and heavy —
       the client read it as brutalist. DM Sans at 300 with open letter-spacing
       keeps the modern, clean register but sits lighter and more architectural.
       It is a variable font (300-600), already self-hosted, so the lighter
       display weight costs no extra request. */
    --font-display: 'DM Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
    --font-body: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif;

    /* Single knob for horizontal rhythm — every section inherits it. */
    --gutter: 56px;
    --maxw: 1400px;
}

/* ── RESET ── */
*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html {
    scroll-behavior: smooth;
    -webkit-text-size-adjust: 100%;
    /* Keeps anchor targets clear of the fixed nav instead of under it. */
    scroll-padding-top: 96px;
}

body {
    background: var(--black);
    color: var(--white);
    font-family: var(--font-body);
    font-weight: 300;
    overflow-x: hidden;
}

img {
    max-width: 100%;
}

::selection {
    background: var(--white);
    color: var(--black);
}

::-webkit-scrollbar {
    width: 3px;
}
::-webkit-scrollbar-track {
    background: var(--black);
}
::-webkit-scrollbar-thumb {
    background: var(--rule);
    border-radius: 2px;
}

:focus-visible {
    outline: 2px solid var(--white);
    outline-offset: 3px;
}

.skip-link {
    position: absolute;
    top: -60px;
    left: 16px;
    z-index: 300;
    background: var(--white);
    color: var(--black);
    padding: 10px 20px;
    font-size: 11px;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    text-decoration: none;
    transition: top 0.2s;
}
.skip-link:focus {
    top: 16px;
}

/* ── NAV ── */
nav {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 28px var(--gutter);
    transition: background 0.4s, padding 0.4s, border-color 0.4s;
    border-bottom: 1px solid transparent;
}
/* Every background treatment for the nav lives on this pseudo-element, never on
   <nav> itself. `backdrop-filter` on an element makes it a containing block for
   its position:fixed descendants — putting it on <nav> would trap the mobile
   drawer inside the 80px bar instead of letting it cover the viewport.
   The gradient also keeps the nav legible over bright hero skies before the
   scrolled state takes over. */
nav::before {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(to bottom, rgba(13, 13, 13, 0.55), transparent);
    pointer-events: none;
    z-index: -1;
    transition: background 0.4s, backdrop-filter 0.4s;
}
nav.scrolled::before {
    background: rgba(13, 13, 13, 0.94);
    backdrop-filter: blur(16px);
}
nav.scrolled {
    padding: 18px var(--gutter);
    border-bottom-color: var(--border);
}

.nav-logo {
    display: block;
    line-height: 0;
    position: relative;
}
/* Hover: the mark grows a touch while a white stroke draws itself around it —
   two L-shaped halves, each running its width first and its height second, so
   they travel in opposite directions and meet at the remaining corners.
   The 0.22s delay on `height` is what sequences it; drop it and the box just
   scales open. */
.nav-logo::before,
.nav-logo::after {
    content: '';
    position: absolute;
    width: 0;
    height: 0;
    pointer-events: none;
    /* At zero size the two 1px borders still paint — they showed as a pair of
       dots at opposite corners of the mark. Held at opacity 0 until hover so
       nothing is visible until the stroke actually draws. */
    opacity: 0;
    transition: width 0.22s ease, height 0.22s ease 0.22s, opacity 0.12s ease 0.1s;
}
.nav-logo::before {
    top: -9px;
    left: -9px;
    border-top: 1px solid var(--white);
    border-left: 1px solid var(--white);
}
.nav-logo::after {
    right: -9px;
    bottom: -9px;
    border-right: 1px solid var(--white);
    border-bottom: 1px solid var(--white);
}
.nav-logo:hover::before,
.nav-logo:hover::after {
    width: calc(100% + 18px);
    height: calc(100% + 18px);
    opacity: 1;
    /* No delay on the way in, so the stroke is visible as it draws. */
    transition: width 0.22s ease, height 0.22s ease 0.22s, opacity 0.1s ease;
}
/* The logo ships as black artwork on an opaque white background (no alpha).
   invert() flips it to white-on-black, then `screen` drops the black to
   transparent — giving a clean knockout mark without re-cutting the asset. */
.nav-logo img {
    height: 34px;
    filter: invert(1);
    mix-blend-mode: screen;
    /* Must stay `center`. The stroke rectangle is drawn from the anchor box,
       which `transform` does not resize — so scaling from `left center` grew
       the mark rightward only and left it sitting off-centre inside the box.
       Scaling about the centre keeps the two concentric. */
    transform-origin: center;
    transition: transform 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.nav-logo:hover img {
    transform: scale(1.07);
}

.nav-links {
    display: flex;
    gap: 40px;
    list-style: none;
}
.nav-links a {
    color: rgba(245, 244, 240, 0.75);
    text-decoration: none;
    font-size: clamp(10.5px, 0.78vw, 13px);
    letter-spacing: 0.22em;
    text-transform: uppercase;
    font-weight: 500;
    transition: color 0.2s;
    position: relative;
}
.nav-links a::after {
    content: '';
    position: absolute;
    bottom: -4px;
    left: 0;
    right: 0;
    height: 1px;
    background: var(--white);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.3s;
}
.nav-links a:hover {
    color: var(--white);
}
.nav-links a:hover::after {
    transform: scaleX(1);
}
/* Shown only inside the mobile drawer. */
.nav-links-cta {
    display: none;
}

.nav-cta {
    background: transparent;
    border: 1px solid rgba(245, 244, 240, 0.45);
    color: var(--white);
    padding: 9px 26px;
    font-family: var(--font-body);
    font-size: 10px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.25s, color 0.25s, border-color 0.25s;
}
.nav-cta:hover {
    background: var(--white);
    color: var(--black);
    border-color: var(--white);
}

/* Hamburger — hidden until the links collapse at 900px. */
.nav-toggle {
    display: none;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    width: 44px;
    height: 44px;
    background: none;
    border: none;
    cursor: pointer;
    padding: 0;
    z-index: 110;
}
.nav-toggle span {
    display: block;
    width: 24px;
    height: 1.5px;
    background: var(--white);
    margin-left: auto;
    margin-right: auto;
    transition: transform 0.3s, opacity 0.2s;
}
.nav-toggle[aria-expanded='true'] span:nth-child(1) {
    transform: translateY(6.5px) rotate(45deg);
}
.nav-toggle[aria-expanded='true'] span:nth-child(2) {
    opacity: 0;
}
.nav-toggle[aria-expanded='true'] span:nth-child(3) {
    transform: translateY(-6.5px) rotate(-45deg);
}

/* ── HERO ── */
#hero {
    height: 100svh;
    min-height: 620px;
    position: relative;
    display: flex;
    align-items: flex-end;
    overflow: hidden;
}

.hero-slides {
    position: absolute;
    inset: 0;
}
/* The slow push-in is a *transition* on transform, not a keyframe animation
   scoped to .active. As an animation it only existed while the class was set,
   so the moment a slide lost .active its transform snapped from wherever the
   zoom had reached back to none — a visible jump underneath the cross-fade.
   As a transition the scale simply eases back out while the slide fades, which
   is imperceptible.

   The fade is deliberately long relative to the 5.5s cycle: at this length the
   two frames genuinely dissolve rather than cutting. */
.hero-slide {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center 30%;
    opacity: 0;
    transform: scale(1.06);
    transition: opacity 2.2s cubic-bezier(0.4, 0, 0.2, 1), transform 9s ease-out;
}
.hero-slide.active {
    opacity: 1;
    transform: scale(1);
}

.hero-overlay {
    position: absolute;
    inset: 0;
    /* Tuned for photography, not renders. The real shots are bright daylight
       exteriors — pale stucco and blown-out sky — so the eyebrow, the accent
       line of the title, and the hero stats all need more scrim than the
       original AI renders did. Lighten these values and the top of the hero
       loses its text. */
    background:
        /* Edge scrim. All hero text is edge-aligned — copy left, stats right —
           and it sits high enough that the vertical gradient below is still
           thin there. This darkens both margins and leaves the centre of the
           photograph open. */
        linear-gradient(to right, rgba(13, 13, 13, 0.8) 0%, rgba(13, 13, 13, 0.3) 30%, rgba(13, 13, 13, 0) 52%, rgba(13, 13, 13, 0.2) 80%, rgba(13, 13, 13, 0.55) 100%),
        linear-gradient(to top, rgba(13, 13, 13, 0.82) 0%, rgba(13, 13, 13, 0.36) 38%, rgba(13, 13, 13, 0.12) 70%, rgba(13, 13, 13, 0.08) 100%);
}

.hero-content {
    position: relative;
    z-index: 2;
    padding: 0 var(--gutter) 88px;
    max-width: var(--maxw);
    width: 100%;
}
/* Sizes here are set for DM Sans, which is far wider than the condensed face
   this replaced — the same pixel value would run a third wider on the page. */
.hero-title {
    font-family: var(--font-display);
    font-size: clamp(40px, 6.4vw, 96px);
    font-weight: 300;
    line-height: 1.02;
    letter-spacing: 0.005em;
    color: #fff;
    margin-bottom: 32px;
}
.hero-title span {
    color: var(--accent-light);
}
.hero-subtitle {
    font-size: clamp(15px, 1.12vw, 18px);
    font-weight: 300;
    color: var(--light-grey);
    max-width: 440px;
    line-height: 1.75;
    margin-bottom: 44px;
}
.hero-actions {
    display: flex;
    gap: 16px;
    align-items: center;
    flex-wrap: wrap;
}

.btn-primary {
    background: var(--white);
    color: var(--black);
    border: none;
    padding: 15px 40px;
    font-family: var(--font-body);
    font-size: 11px;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.25s, transform 0.2s;
}
.btn-primary:hover {
    background: #ffffff;
    transform: translateY(-2px);
}
.btn-ghost {
    background: transparent;
    color: rgba(245, 244, 240, 0.7);
    border: 1px solid rgba(245, 244, 240, 0.22);
    padding: 15px 40px;
    font-family: var(--font-body);
    font-size: 11px;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    font-weight: 500;
    cursor: pointer;
    transition: border-color 0.25s, color 0.25s;
    text-decoration: none;
    display: inline-block;
}
.btn-ghost:hover {
    border-color: var(--white);
    color: var(--white);
}

/* Sits above the hero title. Deliberately quiet — it dates the practice
   without competing with the display type underneath. */
.hero-established {
    font-size: 10px;
    letter-spacing: 0.34em;
    text-transform: uppercase;
    color: var(--white);
    font-weight: 500;
    margin-bottom: 22px;
    display: flex;
    align-items: center;
    gap: 15px;
}
.hero-established::before {
    content: '';
    display: block;
    width: 34px;
    height: 1px;
    background: var(--label);
    flex: none;
}

/* Replaces the old KPI column in the same slot on the right rail. Three words
   rather than three figures, so nothing here needs verifying. */
.hero-discipline {
    position: absolute;
    top: 50%;
    right: var(--gutter);
    transform: translateY(-50%);
    z-index: 2;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 58px;
    transition: opacity 0.3s;
}
/* Each word sits on its own hairline. The rule is the full width of the word
   plus its padding, so the three stack into a small ruled column. */
.hero-discipline span {
    font-family: var(--font-display);
    font-size: 22px;
    font-weight: 300;
    line-height: 1;
    letter-spacing: 0.3em;
    /* Tracking adds a trailing gap after the last letter; pull it back so the
       text sits flush with the right edge of its rule. */
    margin-right: -0.3em;
    text-transform: uppercase;
    color: var(--white);
    display: block;
    text-align: right;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--rule);
}

.hero-dots {
    position: absolute;
    bottom: 44px;
    left: var(--gutter);
    z-index: 3;
    display: flex;
    gap: 10px;
    align-items: center;
}
.hero-dot {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: rgba(245, 244, 240, 0.28);
    border: none;
    padding: 0;
    cursor: pointer;
    position: relative;
    transition: background 0.3s, transform 0.3s;
}
/* A 5px target is too small to hit reliably, which made the dots read as
   decoration rather than a control. This grows the clickable area to roughly
   23x15 without changing the dot's size or the spacing between them. Applied
   to both slideshows so they behave identically.

   The horizontal inset is half the 10px gap on purpose: any wider and
   neighbouring targets overlap, so a click in the gap would land on whichever
   dot happens to come later in the DOM rather than the nearer one. */
.hero-dot::before,
.about-dot::before {
    content: '';
    position: absolute;
    inset: -9px -5px;
}
.hero-dot.active {
    background: var(--white);
    transform: scale(1.5);
}

/* ── SHARED ── */
.section-label {
    font-size: clamp(10.5px, 0.78vw, 13px);
    letter-spacing: 0.35em;
    text-transform: uppercase;
    color: var(--label);
    font-weight: 500;
    display: flex;
    align-items: center;
    gap: 14px;
    margin-bottom: 20px;
}
.section-label::before {
    content: '';
    display: block;
    width: 28px;
    height: 1px;
    background: var(--rule);
    flex: none;
}

/* ── MARQUEE ── */
.marquee-strip {
    background: var(--white);
    padding: 13px 0;
    overflow: hidden;
    white-space: nowrap;
}
.marquee-inner {
    display: inline-block;
    animation: marquee 24s linear infinite;
}
.marquee-item {
    font-family: var(--font-display);
    font-size: 12px;
    font-weight: 400;
    letter-spacing: 0.3em;
    color: var(--black);
    padding: 0 28px;
}
/* A drawn hairline, not a glyph. The diamond this replaced was the only
   ornamental mark on the site; a thin vertical rule matches the hairlines
   already used for the discipline rail, the about divider, and the nav edge.
   Sized in em so it tracks the marquee's own font-size across breakpoints. */
.marquee-dot {
    display: inline-block;
    width: 1px;
    height: 12px;
    /* Sits the rule on the cap-height of the words beside it rather than on
       the baseline. */
    vertical-align: -0.1em;
    background: rgba(13, 13, 13, 0.32);
}
@keyframes marquee {
    0% {
        transform: translateX(0);
    }
    100% {
        transform: translateX(-50%);
    }
}

/* ── ABOUT ── */
.about-outer {
    background: var(--black);
}
/* Both columns are centred on the same axis: `align-items: center` balances
   them vertically, and the text column is a centred block of its own so the
   copy sits against the image rather than drifting to the far left of a
   half-width column. `justify-items` keeps the pair centred in the grid. */
/* Text holds the left gutter, image holds the right, and the space between
   them is the gap plus whatever the copy does not use. The two are no longer
   centred against each other on purpose: the run of white space through the
   middle is what separates the reading column from the photography. */
#about {
    padding: 140px var(--gutter);
    display: grid;
    grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
    gap: 100px;
    align-items: center;
    max-width: var(--maxw);
    margin: 0 auto;
}
.about-left {
    width: 100%;
    max-width: 440px;
    justify-self: start;
}
.about-heading {
    font-family: var(--font-display);
    font-size: clamp(32px, 3.3vw, 54px);
    font-weight: 300;
    line-height: 1.14;
    letter-spacing: 0.03em;
    color: var(--white);
    margin-bottom: 30px;
}
.about-heading em {
    color: var(--accent-light);
    font-style: normal;
}
.about-body {
    font-size: clamp(14.5px, 1.08vw, 17.5px);
    line-height: 1.85;
    color: var(--body-grey);
    font-weight: 300;
    margin-bottom: 18px;
    max-width: 430px;
}
.about-divider {
    width: 56px;
    height: 1px;
    background: var(--rule);
    margin: 28px 0;
    opacity: 0.6;
}
.about-right {
    position: relative;
    width: 100%;
    justify-self: end;
}
/* Slides stack on top of each other, so the wrapper carries the box and the
   images fill it absolutely. 3:2 because every frame in APPROACH_IMAGES is a
   ~1.5 landscape: at 1:1 they were cropped hard at the sides, and the strip of
   bright pavement left along the bottom edge read as a pale line against the
   black page.

   7/5 rather than a true 3/2: the three elevations are ~1.5 and the Shaw entry
   detail is 1.33, so this sits between them and no frame loses more than about
   5% to the crop. */
.about-slides {
    position: relative;
    width: 100%;
    aspect-ratio: 7/5;
    overflow: hidden;
}
.about-slide {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    opacity: 0;
    /* Matched to the hero's dissolve so the two slideshows read as one system. */
    transition: opacity 2s cubic-bezier(0.4, 0, 0.2, 1);
}
.about-slide.active {
    opacity: 1;
}
/* Same dots as the hero, sat under the frame rather than over it — the
   photography here runs edge to edge and an overlaid control would land on
   the image itself. */
.about-dots {
    display: flex;
    gap: 10px;
    align-items: center;
    margin-top: 20px;
}
.about-dot {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: rgba(245, 244, 240, 0.28);
    border: none;
    padding: 0;
    cursor: pointer;
    position: relative;
    transition: background 0.3s, transform 0.3s;
}
.about-dot.active {
    background: var(--white);
    transform: scale(1.5);
}
.about-geo {
    position: absolute;
    top: -20px;
    right: -20px;
    width: 72px;
    height: 72px;
    border: 1px solid var(--rule-soft);
    opacity: 0.3;
}

/* ── PORTFOLIO ── */
#portfolio {
    padding: 120px var(--gutter);
    background: var(--black-deep);
}
.portfolio-header {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 32px;
    margin: 0 auto 60px;
    max-width: var(--maxw);
}
.portfolio-heading {
    font-family: var(--font-display);
    font-size: clamp(32px, 3.3vw, 54px);
    font-weight: 300;
    line-height: 1.14;
    letter-spacing: 0.03em;
}
.portfolio-grid {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    gap: 14px;
    max-width: var(--maxw);
    margin: 0 auto;
}

/* Three rows of two. Every card is span 6 on the 12-column grid, so the
   earlier nth-child(1)/(2) override is redundant but harmless. Aspect ratios
   rather than fixed pixel heights, so the composition survives every
   viewport width. */
.proj-card {
    position: relative;
    overflow: hidden;
    cursor: pointer;
    background: #111;
    grid-column: span 6;
    aspect-ratio: 7/5;
    border: none;
    padding: 0;
    text-align: left;
    font-family: inherit;
    color: inherit;
}
.proj-card:nth-child(1),
.proj-card:nth-child(2) {
    grid-column: span 6;
    aspect-ratio: 7/5;
}
.proj-card-img {
    width: 100%;
    height: 100%;
    display: block;
    object-fit: cover;
    transition: transform 0.75s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.proj-card:hover .proj-card-img,
.proj-card:focus-visible .proj-card-img {
    transform: scale(1.05);
}
.proj-card-overlay {
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, rgba(8, 8, 8, 0.94) 0%, rgba(8, 8, 8, 0.1) 55%, transparent 100%);
    transition: opacity 0.4s;
}
/* The card is a <button>, so its inner nodes are spans — restore block flow. */
.proj-card-overlay,
.proj-card-info,
.proj-tag,
.proj-name,
.proj-location {
    display: block;
}

.proj-card-info {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    padding: 26px;
    transform: translateY(5px);
    transition: transform 0.4s;
}
.proj-card:hover .proj-card-info,
.proj-card:focus-visible .proj-card-info {
    transform: translateY(0);
}
.proj-tag {
    font-size: clamp(10.5px, 0.78vw, 13px);
    letter-spacing: 0.28em;
    text-transform: uppercase;
    color: var(--label);
    font-weight: 500;
    margin-bottom: 7px;
}
/* One fluid size for every card, wide or narrow — 34px on a full desktop
   down to 26px on tablet/mobile. Deliberately not split per card: the grid
   already signals hierarchy through card size, so the names read as one
   consistent set instead of two tiers. */
.proj-name {
    font-family: var(--font-display);
    font-size: clamp(26px, 2.4vw, 34px);
    font-weight: 400;
    letter-spacing: 0.08em;
    color: var(--white);
    margin-bottom: 5px;
    line-height: 1.1;
}
.proj-location {
    font-size: clamp(13px, 0.95vw, 15.5px);
    color: var(--light-grey);
    font-weight: 400;
}
.proj-arrow {
    position: absolute;
    top: 24px;
    right: 24px;
    width: 38px;
    height: 38px;
    color: var(--white);
    border: 1px solid var(--rule);
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transform: translate(8px, -8px);
    transition: opacity 0.35s, transform 0.35s;
}
.proj-card:hover .proj-arrow,
.proj-card:focus-visible .proj-arrow {
    opacity: 1;
    transform: translate(0, 0);
}

/* ── CONTACT ── */
.contact-outer {
    background: var(--black);
}
/* Single column since the enquiry form was removed. */
#contact {
    padding: 140px var(--gutter);
    max-width: var(--maxw);
    margin: 0 auto;
}
/* The intro is capped for a readable measure, but the details below run the
   full width as a row — left in a narrow column they read as the leftovers of
   the two-column layout the form used to complete. */
.contact-intro {
    max-width: 620px;
}
.contact-heading {
    font-family: var(--font-display);
    font-size: clamp(32px, 3.3vw, 54px);
    font-weight: 300;
    line-height: 1.14;
    letter-spacing: 0.03em;
    margin-bottom: 22px;
}
.contact-heading em {
    color: var(--accent-light);
    font-style: normal;
}
.contact-body {
    font-size: clamp(14.5px, 1.08vw, 17.5px);
    line-height: 1.85;
    color: var(--body-grey);
    font-weight: 300;
    margin-bottom: 44px;
    max-width: 360px;
}
.contact-details {
    display: flex;
    flex-wrap: wrap;
    gap: 56px 96px;
    margin-top: 64px;
    padding-top: 48px;
    border-top: 1px solid var(--rule-soft);
}
.contact-item {
    display: flex;
    flex-direction: column;
    gap: 5px;
}
.contact-item-label {
    font-size: clamp(10.5px, 0.78vw, 13px);
    letter-spacing: 0.28em;
    text-transform: uppercase;
    color: var(--label);
    font-weight: 500;
}
.contact-item-value {
    font-size: clamp(14.5px, 1.05vw, 17px);
    color: var(--white);
    font-weight: 400;
    line-height: 1.6;
}
.contact-item-value a {
    color: inherit;
    text-decoration: none;
    transition: color 0.2s;
}
.contact-item-value a:hover {
    color: var(--white);
}

/* ── COPY EMAIL ── */
.contact-email {
    display: flex;
    align-items: center;
    gap: 10px;
    /* Anchors the toast, which is positioned against this row. */
    position: relative;
}
.copy-btn {
    background: none;
    border: 1px solid var(--rule);
    padding: 5px;
    line-height: 0;
    color: var(--label);
    cursor: pointer;
    flex: none;
    transition: color 0.2s, border-color 0.2s;
}
.copy-btn:hover,
.copy-btn:focus-visible {
    color: var(--white);
    border-color: rgba(245, 244, 240, 0.45);
}
/* Sits beside the button and fades out on its own; `visibility` rides the
   same transition so a hidden toast cannot be read out or clicked. */
.copy-toast {
    font-size: 10px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--label);
    white-space: nowrap;
    opacity: 0;
    visibility: hidden;
    transform: translateX(-4px);
    transition: opacity 0.45s ease, transform 0.45s ease, visibility 0s linear 0.45s;
}
.copy-toast.show {
    opacity: 1;
    visibility: visible;
    transform: translateX(0);
    transition: opacity 0.18s ease, transform 0.18s ease, visibility 0s;
}

/* ── FOOTER ── */
footer {
    background: #060606;
    border-top: 1px solid var(--border);
    padding: 44px var(--gutter);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 24px;
    flex-wrap: wrap;
}
.footer-logo {
    line-height: 0;
}
.footer-logo img {
    height: 26px;
    filter: invert(1);
    mix-blend-mode: screen;
    opacity: 0.45;
}
.footer-copy {
    font-size: clamp(10.5px, 0.78vw, 13px);
    color: var(--faint);
    letter-spacing: 0.04em;
}
.footer-links {
    display: flex;
    gap: 28px;
    list-style: none;
    flex-wrap: wrap;
}
.footer-links a {
    font-size: 9.5px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--faint);
    text-decoration: none;
    transition: color 0.2s;
}
.footer-links a:hover {
    color: var(--white);
}

/* ── PROJECT OVERLAY ── */
.overlay-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(6, 6, 6, 0.97);
    z-index: 200;
    display: flex;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.45s ease;
}
.overlay-backdrop[hidden] {
    display: none;
}
.overlay-backdrop.open {
    opacity: 1;
    pointer-events: all;
}
.overlay-inner {
    width: 100%;
    height: 100%;
    display: grid;
    grid-template-columns: 55% 45%;
    overflow: hidden;
}
.overlay-img-pane {
    position: relative;
    overflow: hidden;
    background: #050505;
}
.overlay-carousel-img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0;
    transition: opacity 0.6s ease;
}
.overlay-carousel-img.active {
    opacity: 1;
}

/* Per-project colour correction, opt-in via `tone` in projects.js.
   The Chiara shoot came back cool and overcast and read as gloomy next to the
   other projects. Correcting in CSS rather than re-encoding keeps the files on
   disk untouched, so a properly graded set can drop in later and this comes
   straight back out. Keep it subtle — this is a lift, not a filter. */
.tone-warm {
    /* The sepia + hue-rotate this used to carry pulled everything toward
       yellow, which cancelled the blue now baked into the Chiara skies. Left
       as a plain brightness/saturation lift so the two corrections agree. */
    filter: brightness(1.05) saturate(1.12);
}
/* No box, no fill, no blur — just the mark itself.
   The arrow is near-black and fully opaque from the start: at lower opacity it
   vanished entirely on the darker frames. What keeps a black glyph readable on
   a dark photograph is the black edge below, not a panel behind it.

   The four drop-shadows are offsets at zero blur, one per direction, which
   traces a hard outline around the glyph rather than the soft halo this used
   to have. They are declared at low alpha rather than omitted so the hover
   state interpolates — a filter list can only animate into another list with
   the same number of entries; going from `none` would snap. */
.carousel-arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 4;
    width: 52px;
    height: 52px;
    background: none;
    border: none;
    padding: 0;
    color: var(--white);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    filter:
        drop-shadow(1px 0 0 rgba(8, 8, 8, 0.45))
        drop-shadow(-1px 0 0 rgba(8, 8, 8, 0.45))
        drop-shadow(0 1px 0 rgba(8, 8, 8, 0.45))
        drop-shadow(0 -1px 0 rgba(8, 8, 8, 0.45));
    transition: filter 0.28s ease, transform 0.25s ease;
}
.carousel-arrow svg {
    width: 26px;
    height: 26px;
    stroke-width: 2;
}
/* Hover thickens the outline to a full black stroke. */
.carousel-arrow:hover,
.carousel-arrow:focus-visible {
    filter:
        drop-shadow(1.5px 0 0 rgba(8, 8, 8, 0.95))
        drop-shadow(-1.5px 0 0 rgba(8, 8, 8, 0.95))
        drop-shadow(0 1.5px 0 rgba(8, 8, 8, 0.95))
        drop-shadow(0 -1.5px 0 rgba(8, 8, 8, 0.95));
}
.carousel-prev {
    left: 28px;
}
.carousel-next {
    right: 28px;
}
.carousel-prev:hover {
    transform: translateY(-50%) translateX(-2px);
}
.carousel-next:hover {
    transform: translateY(-50%) translateX(2px);
}
.carousel-counter {
    position: absolute;
    top: 32px;
    left: 32px;
    z-index: 4;
    display: flex;
    align-items: center;
    gap: 12px;
    font-family: var(--font-display);
    font-size: 13px;
    font-weight: 400;
    letter-spacing: 0.16em;
    color: var(--white);
    background: rgba(13, 13, 13, 0.4);
    backdrop-filter: blur(10px);
    padding: 7px 16px;
}
.carousel-counter-divider {
    width: 22px;
    height: 1px;
    background: rgba(245, 244, 240, 0.45);
}
.carousel-dots {
    position: absolute;
    bottom: 28px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 4;
    display: flex;
    gap: 9px;
}
.carousel-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: rgba(245, 244, 240, 0.35);
    border: none;
    padding: 0;
    cursor: pointer;
    transition: background 0.3s, transform 0.3s;
}
.carousel-dot.active {
    background: var(--white);
    transform: scale(1.4);
}
/* A single-image project shows no chrome. */
.overlay-inner.single-image .carousel-arrow,
.overlay-inner.single-image .carousel-counter,
.overlay-inner.single-image .carousel-dots {
    display: none;
}

.overlay-content-pane {
    padding: 96px 60px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    justify-content: center;
}
.overlay-close {
    position: fixed;
    top: 32px;
    right: 44px;
    z-index: 201;
    background: rgba(6, 6, 6, 0.6);
    border: 1px solid var(--rule);
    color: #8a8680;
    width: 46px;
    height: 46px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    font-size: 17px;
    transition: border-color 0.2s, color 0.2s;
}
.overlay-close:hover {
    border-color: var(--white);
    color: var(--white);
}
.overlay-tag {
    font-size: clamp(10.5px, 0.78vw, 13px);
    letter-spacing: 0.32em;
    text-transform: uppercase;
    color: var(--label);
    margin-bottom: 14px;
}
.overlay-title {
    font-family: var(--font-display);
    font-size: clamp(26px, 2.7vw, 44px);
    font-weight: 300;
    line-height: 1.14;
    letter-spacing: 0.03em;
    color: var(--white);
    margin-bottom: 10px;
}
.overlay-location {
    font-size: 12.5px;
    color: var(--light-grey);
    margin-bottom: 36px;
    display: flex;
    align-items: center;
    gap: 10px;
}
.overlay-location::before {
    content: '';
    display: block;
    width: 18px;
    height: 1px;
    background: var(--rule);
    flex: none;
}
.overlay-desc {
    font-size: clamp(15px, 1.12vw, 18px);
    line-height: 1.85;
    color: #8a8680;
    font-weight: 300;
    margin-bottom: 44px;
}
.overlay-specs {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 22px;
    border-top: 1px solid var(--border);
    padding-top: 36px;
}
.spec-label {
    font-size: clamp(10.5px, 0.78vw, 13px);
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--label);
    margin-bottom: 5px;
}
.spec-value {
    font-size: 17px;
    font-weight: 400;
    color: var(--white);
}
.spec-value small {
    font-size: 11px;
    color: var(--light-grey);
}

/* ── SCROLL REVEALS ── */
.reveal {
    opacity: 0;
    transform: translateY(28px);
    transition: opacity 0.85s cubic-bezier(0.25, 0.46, 0.45, 0.94), transform 0.85s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.reveal.visible {
    opacity: 1;
    transform: translateY(0);
}
.reveal-left {
    opacity: 0;
    transform: translateX(-36px);
    transition: opacity 0.9s ease, transform 0.9s ease;
}
.reveal-left.visible {
    opacity: 1;
    transform: translateX(0);
}
.reveal-right {
    opacity: 0;
    transform: translateX(36px);
    transition: opacity 0.9s ease, transform 0.9s ease;
}
.reveal-right.visible {
    opacity: 1;
    transform: translateX(0);
}
.d1 {
    transition-delay: 0.12s;
}
.d2 {
    transition-delay: 0.25s;
}
.d3 {
    transition-delay: 0.4s;
}

/* ═══════════════════════════════════════════════════════════════════════════
   RESPONSIVE
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── ≤1200px — tighten gutters, drop the hero stats panel ── */
@media (max-width: 1200px) {
    :root {
        --gutter: 40px;
    }
    #about {
        gap: 56px;
        padding: 110px var(--gutter);
    }
    #contact {
        padding: 110px var(--gutter);
    }
    /* The right rail would collide with the hero title once it narrows. */
    .hero-discipline {
        display: none !important;
    }
}

/* ── ≤1024px — portfolio to 2-up, overlay stacks ── */
@media (max-width: 1024px) {
    .proj-card,
    .proj-card:nth-child(1),
    .proj-card:nth-child(2) {
        grid-column: span 6;
        aspect-ratio: 3/2;
    }
    /* An odd final card runs full width rather than leaving a hole. */
    .proj-card:last-child:nth-child(odd) {
        grid-column: span 12;
        aspect-ratio: 2/1;
    }

    .overlay-inner {
        grid-template-columns: 1fr;
        grid-template-rows: 45vh 1fr;
        overflow-y: auto;
    }
    .overlay-content-pane {
        padding: 48px var(--gutter) 64px;
        justify-content: flex-start;
    }
    .overlay-close {
        top: 20px;
        right: 20px;
    }
    .carousel-counter {
        top: 20px;
        left: 20px;
    }
}

/* ── ≤900px — nav collapses to a drawer ── */
@media (max-width: 900px) {
    .nav-toggle {
        display: flex;
    }
    nav .nav-cta {
        display: none;
    }
    /* Keep the mark above the open drawer so branding never drops out. */
    .nav-logo {
        position: relative;
        z-index: 110;
    }

    .nav-links {
        position: fixed;
        inset: 0;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 36px;
        /* Fully opaque — at 0.98 the page text behind stayed legible enough
           to compete with the menu items. */
        background: var(--black);
        opacity: 0;
        pointer-events: none;
        transition: opacity 0.35s ease;
    }
    .nav-links.open {
        opacity: 1;
        pointer-events: all;
    }
    .nav-links a {
        font-size: 15px;
        letter-spacing: 0.28em;
        color: var(--white);
    }
    .nav-links-cta {
        display: block;
        margin-top: 12px;
    }
    .nav-links-cta a {
        border: 1px solid rgba(245, 244, 240, 0.45);
        color: var(--white);
        padding: 14px 34px;
        font-size: 11px;
    }
    .nav-links-cta a::after {
        display: none;
    }
}

/* ── ≤768px — single column throughout ── */
@media (max-width: 768px) {
    :root {
        --gutter: 24px;
    }

    #hero {
        min-height: 560px;
    }
    .hero-content {
        padding: 0 var(--gutter) 96px;
    }
    .hero-subtitle {
        font-size: 14px;
        margin-bottom: 32px;
        /* On a phone the hero photo sits right behind this copy with no side
           column to fall back on, so the muted --light-grey blends into bright
           sky. Lift it toward white and add a soft scrim-shadow — contrast over
           any frame without touching the desktop treatment. */
        color: rgba(245, 244, 240, 0.92);
        text-shadow: 0 1px 12px rgba(13, 13, 13, 0.55);
    }
    .hero-dots {
        bottom: 32px;
    }

    #about {
        grid-template-columns: 1fr;
        gap: 64px;
        padding: 84px var(--gutter);
    }
    .about-body {
        max-width: none;
    }
    .about-right {
        margin-bottom: 28px;
    }
    .about-image-main {
        aspect-ratio: 3/4;
    }
    .about-geo {
        top: -12px;
        right: -8px;
        width: 52px;
        height: 52px;
    }

    #portfolio {
        padding: 84px var(--gutter);
    }
    .portfolio-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 28px;
        margin-bottom: 40px;
    }
    .proj-card,
    .proj-card:nth-child(1),
    .proj-card:nth-child(2),
    .proj-card:last-child:nth-child(odd) {
        grid-column: span 12;
        aspect-ratio: 4/3;
    }
    .proj-card-info {
        padding: 20px;
    }
    /* Touch has no hover, so reveal the affordances by default. */
    .proj-card-info {
        transform: translateY(0);
    }

    #contact {
        padding: 84px var(--gutter);
    }
    .contact-body {
        max-width: none;
        margin-bottom: 32px;
    }

    .overlay-inner {
        grid-template-rows: 38vh 1fr;
    }
    .carousel-arrow {
        width: 44px;
        height: 44px;
    }
    .carousel-prev {
        left: 10px;
    }
    .carousel-next {
        right: 10px;
    }
    .overlay-specs {
        gap: 18px;
        padding-top: 28px;
    }

    footer {
        flex-direction: column;
        align-items: flex-start;
        gap: 20px;
        padding: 36px var(--gutter);
    }
}

/* ── ≤560px — smallest phones ── */
@media (max-width: 560px) {
    .hero-title {
        font-size: clamp(48px, 15vw, 76px);
    }
    .hero-actions {
        gap: 12px;
    }
    .hero-actions .btn-primary,
    .hero-actions .btn-ghost {
        flex: 1 1 100%;
        text-align: center;
        padding: 15px 24px;
    }


    .overlay-specs {
        grid-template-columns: 1fr;
    }
    .marquee-item {
        font-size: 12px;
        padding: 0 18px;
    }
}

/* ── MOTION / TOUCH ── */
@media (hover: none) {
    .proj-card-info {
        transform: translateY(0);
    }
    .proj-arrow {
        opacity: 0.7;
        transform: translate(0, 0);
    }
}

@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
    .reveal,
    .reveal-left,
    .reveal-right {
        opacity: 1;
        transform: none;
    }
    /* The push-in is a transform transition now, not an animation, so it needs
       neutralising here rather than `animation: none`. */
    .hero-slide,
    .hero-slide.active {
        transform: none;
    }
}
