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

html {
    scroll-behavior: smooth;
}

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

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

/* ============================================================
   SIDE NAVIGATION
   Shared component with the home page (styles.css/script.js) — same
   frosted-glass collapsed pill, hover-expand, and clip-path color-matching.
   The only difference: a single "Back" link stands in for the home page's
   in-page nav-links / mobile menu.
   ============================================================ */
.side-nav {
    position: fixed;
    /* env() falls back to 0px on non-notched devices/browsers, so this is a no-op there —
       only matters once viewport-fit=cover (see project-template.html etc.) lets the page
       bleed under the status bar/notch, which would otherwise pull this same fixed offset
       in from the true screen edge instead of the safe content area. */
    top: calc(2rem + env(safe-area-inset-top, 0px));
    left: calc(2.5rem + env(safe-area-inset-left, 0px)); /* 40px page margin */
    z-index: 1000;
    display: flex;
    flex-direction: column;
    background: transparent;
    padding: 24px 12px 12px 12px; /* 24px top, 12px right, 12px bottom, 12px left */
    border-radius: 8px;
    transition: all 0.3s ease;
}

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

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

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

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

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

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

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

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

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

/* Brand label joins the same stagger sequence as the nav-links labels below (see the
   .nav-links li:nth-child --i rules) as item 0 — first to slide in on entrance, last to
   slide out on exit. Matches the home page (styles.css) exactly. */
.nav-brand .brand-text { --i: 0; }

/* max-width (not width) for the same reason as .nav-text below: Chromium doesn't honor
   transition-delay on a width transition that involves the `auto` keyword at either end —
   it just snaps instantly regardless of the delay specified. That's exactly what was
   happening here: unlike the home page's .brand-text (styles.css), which never resizes at
   all — .nav-brand there keeps a constant gap and just fades the text in place — this
   page's brand pill actually shrinks to icon-only when collapsed, so it needs its own
   width mechanism. With plain `width: 0 → auto`, "Tanner" was clipped to zero width the
   INSTANT the mouse left, well before its own opacity delay below even started, while
   "Back" (already on max-width) correctly waited out its delay — making Back visually
   linger after Tanner despite the opacity timing underneath being correct. 150px is a
   generous cap; the label still renders at its own natural (smaller) width once expanded. */
.brand-text {
    opacity: 0;
    transform: translateX(-8px);
    max-width: 0;
    overflow: hidden;
    white-space: nowrap;
    transition-property: opacity, transform, max-width;
    transition-duration: 0.2s, 0.2s, 0s;
    transition-timing-function: ease, ease, ease;
    /* The "+ 0.1s" matches .nav-text's own collapse-grace delay (see .nav-text below) —
       without it, Tanner's exit delay (0.15s at --i:0) sits BELOW Back's (0.1s + 0.1s =
       0.2s at --i:1), so Tanner would start fading out before Back despite being meant to
       exit last. The extra "+ 0.2s" on max-width's delay is the fade's own duration —
       keeps the box open until the fade actually finishes, same as .nav-text. */
    transition-delay:
        calc((3 - var(--i, 0)) * 0.05s + 0.1s),
        calc((3 - var(--i, 0)) * 0.05s + 0.1s),
        calc((3 - var(--i, 0)) * 0.05s + 0.1s + 0.2s);
}

.side-nav:hover .brand-text {
    opacity: 1;
    transform: translateX(0);
    max-width: 150px;
    transition-delay:
        calc(var(--i, 0) * 0.05s),
        calc(var(--i, 0) * 0.05s),
        0s;
}

/* Resize guard (class toggled in project-script.js) — a native OS-level window-edge drag
   freezes the page's JS for the whole drag, then does one layout+paint with the new
   geometry the instant you release, before the resize-driven clip-path recompute has run.
   That single frame can briefly show the base (permanently .dark) nav's real label
   unmasked at the stale clip boundary. Rather than race to recompute the correct boundary
   before that frame paints, just hide every label outright — real and overlay copies
   alike, and collapse Tanner's pill back to icon-only — for the duration of any resize, so
   there's nothing wrong to see regardless of which frame wins. !important because this
   needs to beat both the resting and hover opacity/max-width rules above; transition: none
   so the hide/reveal itself is an instant cut, not a fade that could look like its own
   flash. */
html.is-resizing .nav-text,
html.is-resizing .brand-text {
    opacity: 0 !important;
    max-width: 0 !important;
    transition: none !important;
}

/* Mobile/tablet-only clone of .nav-brand's own icon+text, used for its section-matched
   color switching there (see the ≤1024px override, and project-script.js) — desktop
   doesn't need it, since it already gets the same smooth switching for free via the
   whole-nav .nav-overlay clone, which still contains its own copy of .nav-brand. */
.brand-overlay {
    display: none;
}

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

/* Per-link index, used below to stagger the label's slide/fade — entrance cascades
   top-down (brand label first — see its own --i:0 rule above — then Back), exit reverses
   it. Only one nav-link exists here (Back), but this stays index-based (nth-child) rather
   than hardcoded to keep it a drop-in match for the home page's version (styles.css) if
   more links are ever added. */
.nav-links li:nth-child(1) .nav-text { --i: 1; }

.nav-link {
    display: flex;
    align-items: center;
    gap: 0; /* No gap in collapsed state */
    color: inherit;
    text-decoration: none;
    font-size: 24px;
    font-weight: 400;
    padding: 4px 8px; /* 4px vertical, 8px horizontal */
    border-radius: 8px;
    /* Matches the home page's .nav-link exactly (styles.css) — the 0.1s delay only ever
       applies on the way back to THIS (collapsed) state, since a transition's
       delay/duration/timing come from whichever rule matches the state being transitioned
       TO; the hover rule below re-zeroes it for the opposite direction. Gives the pill a
       "hair" of grace after the mouse actually leaves before it visibly starts collapsing,
       so a brief overshoot past the nav's edge doesn't read as a flicker. Matches the same
       +0.1s added to .nav-text's exit transition-delay below, so label and pill collapse
       in sync. */
    transition: all 0.3s 0.1s ease;
    white-space: nowrap;
    overflow: visible;
    position: relative;
    width: fit-content; /* Hug content in collapsed state */
}

/* When nav is expanded (hovered), add gap; hug icon + label (8px left / 16px right)
   instead of filling the nav container */
.side-nav:hover .nav-link {
    gap: 12px;
    width: fit-content;
    padding: 4px 16px 4px 8px;
    /* Re-zeroes the resting rule's 0.1s collapse delay above so expanding stays instant —
       the grace period is only for collapsing back out. */
    transition-delay: 0s;
}

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

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

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

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

/* Selection reads via the underline only — no pill fill or inset shadow. (Currently
   unused on project pages: the single "Back" link never gets .active, since there's no
   in-page section for it to track — kept identical to the home page for consistency,
   in case that ever changes.) */
.nav-link.active {
    background: transparent;
}

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

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

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

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

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

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

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

/* .brand-icon has no padding, so its content box and padding box are the same size —
   percentage sizing (against a positioned ancestor's padding box, per spec, for an
   absolutely positioned child) safely scales the icon 1:1 with the container, matching it
   across breakpoints (40x40 desktop, 30x30 tablet/mobile) without hardcoding either value
   here. */
.brand-icon svg {
    width: 100%;
    height: 100%;
}

/* .nav-icon, unlike .brand-icon, keeps an 8px padding at desktop — sized explicitly to its
   24x24 content box instead of width:100%, which would resolve against the padding box
   (40x40) and render the Back icon visibly oversized. .nav-icon's own content box stays
   24x24 at every breakpoint here (only its padding changes, see the ≤1024px override),
   so this one declaration covers desktop, tablet, and mobile alike. */
.nav-icon svg {
    width: 24px;
    height: 24px;
}

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

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

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

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

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

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

/* Matches the home page's .nav-text exactly (styles.css). max-width (not width) so both
   endpoints are concrete lengths, not the `auto` keyword — Chromium doesn't honor
   transition-delay on a width transition that involves `auto` at either end, it just snaps
   instantly regardless of the delay specified. 200px is just a generous cap; the label
   still renders at its own natural (smaller) intrinsic width once expanded, max-width only
   clips it while collapsed. Zero duration since this is a step, not a fade, but it still
   needs its own delay: without one it snaps shut instantly on exit, and since
   overflow:hidden clips at that width, the label would vanish before its own opacity/
   transform fade has actually finished. That delay is offset by the fade's own duration so
   the box only collapses once the fade is done, and by the same +0.1s collapse grace as
   .nav-link's own transition-delay above, so the label doesn't start fading out before the
   pill itself has even begun collapsing. Order matches transition-property: opacity,
   transform, max-width. */
.nav-text {
    opacity: 0;
    transform: translateX(-8px);
    color: inherit;
    max-width: 0;
    overflow: hidden;
    transition-property: opacity, transform, max-width;
    transition-duration: 0.2s, 0.2s, 0s;
    transition-timing-function: ease, ease, ease;
    transition-delay:
        calc((3 - var(--i, 0)) * 0.05s + 0.1s),
        calc((3 - var(--i, 0)) * 0.05s + 0.1s),
        calc((3 - var(--i, 0)) * 0.05s + 0.1s + 0.2s);
}

.side-nav:hover .nav-text {
    opacity: 1;
    transform: translateX(0);
    max-width: 200px;
    transition-delay:
        calc(var(--i, 0) * 0.05s),
        calc(var(--i, 0) * 0.05s),
        0s;
}

/* --- Nav color switching via clip-path ---
   Duplicated verbatim from the home page (styles.css/script.js), which is confirmed
   working — an earlier project-page-specific variant of this (different hover-expand
   width mechanics, JS-driven hover mirroring) diverged just enough to hit an unresolved
   bug, so this reuses the exact same CSS/JS pattern rather than parallel-maintaining a
   variant. Two full copies of the nav sit stacked at the identical fixed position: the
   real, interactive "base" copy (#sideNav, permanently .dark/blue) always renders; a
   decorative "overlay" clone (permanently .light/white, inserted by project-script.js)
   sits on top, clipped with a live clip-path to only show over the page's blue region.
   Where the overlay is visible it fully repaints over the base copy beneath it, giving a
   hard, pixel-accurate split instead of a class-based swap. Both copies get all their
   color (text, icons, hover pill) from the ordinary .dark/.light rules above — each
   copy's class is just pinned rather than scroll-driven.
   Unlike the home page, project pages have no separate mobile hamburger-menu nav state
   (just minor position tweaks at narrow widths, see the @media blocks below), so this
   runs at all viewport widths — no breakpoint gating needed. */
.nav-overlay {
    pointer-events: none;
    /* Fully visible by default, matching the top of the hero (blue, white nav needed) —
       safe if the geometry script can't run. JS clips this from the bottom once it
       measures the real split. */
    clip-path: inset(0px 0px 0px 0px);
    /* .side-nav sets `transition: all 0.3s ease` for its hover expand/collapse
       animation. The overlay is a clone, so it inherits that — which would silently
       ease every clip-path update over 300ms. This needs to be a hard, instant split. */
    transition: none;
    /* .side-nav never sets align-items, so its flex children (.nav-brand, .nav-links)
       default to align-items: stretch — trying to fill .side-nav's own width, while
       .side-nav's width is itself computed via shrink-to-fit based on those same
       children. That circular dependency is what was clipping "Back"/"Tanner" mid-word
       on hover: browsers resolve it correctly for the original, parse-time DOM (base)
       but not reliably for a JS-cloned subtree inserted after the fact (overlay).
       Forcing flex-start here breaks the circularity for the overlay specifically,
       without touching base's (already-working) layout. */
    align-items: flex-start;
}

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

/* Base nav's real :hover expands it; mirror that onto the overlay (which can never
   receive a genuine :hover of its own, since pointer-events is off) via :has() rather
   than duplicating the hover state in JS. */
body:has(#sideNav:hover) .nav-overlay .nav-link {
    gap: 12px;
    width: fit-content;
    padding: 4px 16px 4px 8px;
    /* Same re-zero as the base nav's own .side-nav:hover .nav-link rule — without it this
       mirror falls back to the resting .nav-link rule's 0.1s collapse delay in BOTH
       directions, so the overlay would visibly lag 0.1s behind the real nav on every
       expand. */
    transition-delay: 0s;
}

body:has(#sideNav:hover) .nav-overlay .nav-text {
    opacity: 1;
    transform: translateX(0);
    width: auto;
    max-width: 200px;
    transition-delay:
        calc(var(--i, 0) * 0.05s),
        calc(var(--i, 0) * 0.05s),
        0s;
}

body:has(#sideNav:hover) .nav-overlay .brand-text {
    opacity: 1;
    transform: translateX(0);
    max-width: 150px;
    transition-delay:
        calc(var(--i, 0) * 0.05s),
        calc(var(--i, 0) * 0.05s),
        0s;
}

/* ============================================================
   Hero Section
   ============================================================ */

.hero-section {
    background: white;
    min-height: 50vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 4rem 0 2rem;
    position: relative;
    overflow: hidden;
}

.hero-bg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
}

.hero-content {
    position: relative;
    z-index: 1;
    max-width: 1200px;
    width: 100%;
    margin: 0 auto;
    padding-left: 12.5rem;
}

.hero-image {
    width: 100%;
}

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

/* ============================================================
   Title Section
   ============================================================ */

.title-section {
    background: white;
    padding: 3rem 2rem 2rem;
}

.title-section .section-container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0;
}

.project-title {
    font-size: 3rem;
    color: #2d2d2d;
    font-weight: 400;
    margin: 0;
}

.project-subtitle {
    font-size: 1.5rem; /* 24px */
    color: #74767E;
    font-weight: 400;
    margin-top: 0.5rem;
}

/* Gold underline accent — sits below subtitle (or title if no subtitle present) */
.project-subtitle::after,
.project-title:not(:has(~ .project-subtitle))::after {
    content: '';
    display: block;
    width: 7.5rem; /* 120px */
    height: 2px;
    background: #F5A623;
    margin-top: 1rem;
}

/* Phase heading — optional, for multi-phase project pages */
.phase-title {
    font-family: 'Lora', serif;
    font-size: 2rem;
    font-weight: 400;
    color: #2D2D2D;
    margin-bottom: 0.25rem;
}

.phase-date {
    font-family: 'Quicksand', sans-serif;
    font-size: 1.25rem;
    font-weight: 400;
    color: #74767E;
    display: block;
    margin-bottom: 1.5rem;
}

/* ============================================================
   Project Meta (Role / Timeline / Impact)
   Sits inside the title section's own .section-container, right after
   the subtitle, so it inherits the title's alignment automatically.
   ============================================================ */

.project-meta {
    margin-top: 5rem; /* 80px below the gold underline */
    display: flex;
    flex-direction: column;
    gap: 2.5rem; /* 40px between Role/Timeline row and Impact row */
}

.meta-row {
    display: flex;
    flex-wrap: wrap;
    gap: 2.5rem; /* 40px between Role and Timeline */
}

.meta-item {
    display: grid;
    grid-template-columns: 40px 1fr;
    gap: 1.5rem; /* 24px between the icon column and its text */
    align-items: start;
}

.meta-icon {
    width: 32px;
    height: 32px;
    flex-shrink: 0;
    margin-top: 6px;
    display: block;
    justify-self: center;
}

.meta-content {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    max-width: 34rem;
}

.meta-row--impact .meta-content {
    max-width: none;
}

.meta-label {
    font-family: 'Quicksand', sans-serif;
    font-size: 1.25rem;
    font-weight: 500;
    color: #2D2D2D;
}

.meta-value {
    font-family: 'Quicksand', sans-serif;
    font-size: 1rem;
    line-height: 1.8;
    color: #74767E;
}

/* ============================================================
   Content Sections
   ============================================================ */

.content-section {
    background: white;
    padding: 1rem;
}

.section-container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 2rem 0;
    padding-left: 12.5rem;
}

.section-title {
    font-size: 2rem;
    margin-bottom: 2rem;
    color: #2d2d2d;
    font-weight: 400;
}

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

.section-text:last-child {
    margin-bottom: 0;
}

/* ============================================================
   Pull Quote
   ============================================================ */

.pull-quote {
    border-left: 2px solid #F5A623;
    margin: 2.5rem 0 2.5rem 2.5rem; /* 40px left margin indents the border */
    padding: 0 2.5rem 0 0.75rem; /* 40px right, 12px between border and text */
}

.pull-quote p {
    font-family: 'Quicksand', sans-serif;
    font-size: 1.25rem; /* 20px */
    font-style: italic;
    color: #2D2D2D;
    line-height: 1.7;
    margin: 0;
}

.pull-quote cite {
    display: block;
    font-family: 'Quicksand', sans-serif;
    font-size: 1rem; /* 16px */
    font-style: normal;
    color: #74767E;
    margin-top: 0.75rem;
}

/* ============================================================
   Two-column text + image layout
   ============================================================ */

.two-col {
    display: grid;
    grid-template-columns: 1fr 40%;
    gap: 3rem;
    align-items: start;
    margin: 2.5rem 0;
}

/* Reverse variant: image left, text right */
.two-col.two-col--reverse {
    grid-template-columns: 40% 1fr;
}

.two-col--reverse .two-col-image {
    order: -1;
}

.two-col-image img {
    width: 100%;
    height: auto;
    border-radius: 8px;
    display: block;
}

/* Remove max-width constraint on text inside two-col */
.two-col .section-text {
    max-width: none;
}

@media (max-width: 768px) {
    .two-col,
    .two-col.two-col--reverse {
        grid-template-columns: 1fr;
    }

    .two-col--reverse .two-col-image {
        order: 0;
    }

    .two-col-image {
        order: 1;
    }
}

/* ============================================================
   Case Study Card
   ============================================================ */

.case-study-card {
    display: grid;
    grid-template-columns: 60% 1fr;
    gap: 3rem;
    align-items: start;
    margin: 2.5rem 0;
}

.case-card-image img {
    width: 100%;
    height: auto;
    border-radius: 8px;
    display: block;
    background: #D9D9D9;
}

.case-card-content {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1rem;
}

.case-card-title {
    font-family: 'Lora', serif;
    font-size: 1.5rem;
    font-weight: 400;
    color: #2D2D2D;
    margin: 0;
}

.case-card-text {
    font-size: 1rem;
    line-height: 1.8;
    color: #555;
    margin: 0;
}

.case-card-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.meta-tag {
    background: #F4F4F4;
    border-radius: 99px;
    padding: 4px 12px;
    font-size: 0.75rem;
    color: #74767E;
    font-family: 'Quicksand', sans-serif;
    font-weight: 500;
}

.case-card-cta {
    display: inline-block;
    border: 1.5px solid #056AE0;
    color: #056AE0;
    border-radius: 8px;
    padding: 8px 20px;
    font-size: 0.875rem;
    font-family: 'Quicksand', sans-serif;
    font-weight: 500;
    text-decoration: none;
    transition: background 0.2s ease, color 0.2s ease;
}

.case-card-cta:hover {
    background: #056AE0;
    color: white;
}

@media (max-width: 768px) {
    .case-study-card {
        grid-template-columns: 1fr;
    }
}

/* ============================================================
   Section List
   ============================================================ */

.section-list {
    list-style: disc;
    padding-left: 1.5rem;
    margin-bottom: 1.5rem;
    color: #555;
}

.section-list li {
    font-size: 1rem;
    line-height: 1.8;
    margin-bottom: 0.5rem;
}

.section-list li:last-child {
    margin-bottom: 0;
}

/* ============================================================
   Full-width Image
   ============================================================ */

.full-width-image {
    margin: 2.5rem 0;
}

.full-width-image img {
    width: 100%;
    height: auto;
    display: block;
    border-radius: 8px;
}

/* ============================================================
   Two-column Card pair (equal-width, stacked layout per card)
   ============================================================ */

.two-col-card-pair {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 3rem;
    margin: 2.5rem 0;
}

.two-col-card {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.two-col-card-image img {
    width: 100%;
    height: auto;
    border-radius: 8px;
    display: block;
}

.two-col-card-title {
    font-family: 'Lora', serif;
    font-size: 1.5rem;
    font-weight: 400;
    color: #2D2D2D;
    margin: 0;
}

/* Override .two-col to support equal-width card pair */
.two-col.two-col--equal {
    grid-template-columns: 1fr 1fr;
}

/* ============================================================
   Research Stats Bar
   ============================================================ */

.research-stats {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 0;
    background: #056AE0;
    border-radius: 8px;
    margin: 2.5rem 0;
    overflow: hidden;
}

.research-stat {
    padding: 1.75rem 2rem;
    border-right: 1px solid rgba(255, 255, 255, 0.2);
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.research-stat:last-child {
    border-right: none;
}

.research-stat-number {
    font-family: 'Lora', serif;
    font-size: 2.5rem;
    font-weight: 400;
    color: white;
    line-height: 1;
}

.research-stat-label {
    font-family: 'Quicksand', sans-serif;
    font-size: 0.875rem;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.8);
    line-height: 1.4;
}

/* ============================================================
   Stat Metrics Row (large number + orange underline + label)
   Hardcoded text in place of an exported metrics graphic — auto-fit lets it
   reflow naturally at any width instead of needing a fixed breakpoint, so it
   works the same whether a row has 3 or 4 stats.
   ============================================================ */

.stat-metrics {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 2.5rem 2rem;
    margin: 2.5rem 0;
}

.stat-metric {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
}

.stat-metric-number {
    font-family: 'Quicksand', sans-serif;
    font-size: 2.75rem;
    font-weight: 500;
    color: #2D2D2D;
    line-height: 1;
    display: inline-block;
    position: relative;
    width: fit-content;
    padding-bottom: 0.5rem;
    margin-bottom: 0.75rem;
}

.stat-metric-number::after {
    content: '';
    position: absolute;
    left: -4px;
    right: -4px;
    bottom: 0;
    height: 3px;
    border-radius: 2px;
    background: #F5A623;
}

.stat-metric-label {
    font-family: 'Quicksand', sans-serif;
    font-size: 1rem;
    color: #74767E;
}

@media (max-width: 768px) {
    .research-stats {
        grid-template-columns: 1fr 1fr;
    }

    .research-stat:nth-child(2) {
        border-right: none;
    }

    .research-stat:nth-child(1),
    .research-stat:nth-child(2) {
        border-bottom: 1px solid rgba(255, 255, 255, 0.2);
    }
}

/* ============================================================
   Numbered Findings
   ============================================================ */

.numbered-findings {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    margin: 2.5rem 0;
}

.finding-item {
    display: grid;
    grid-template-columns: 40px 1fr;
    gap: 1.5rem; /* 24px between the number/icon column and its text */
    align-items: start;
}

.finding-icon {
    width: 60px;
    height: 60px;
    flex-shrink: 0;
    display: block;
}

.finding-content {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding-top: 0.25rem;
}

.finding-title {
    font-family: 'Quicksand', sans-serif;
    font-size: 1rem;
    font-weight: 600;
    color: #2D2D2D;
    line-height: 1.5;
}

.finding-text {
    font-family: 'Quicksand', sans-serif;
    font-size: 1rem;
    line-height: 1.8;
    color: #555;
}

/* Numeral variant of .finding-icon — a hardcoded number + orange underline in
   place of an exported image, so the digit is real text (reflows, no image
   request, matches surrounding type). Drop into the same grid slot the
   <img class="finding-icon"> used to occupy. */
.finding-number {
    font-family: 'Quicksand', sans-serif;
    font-size: 1.75rem;
    font-weight: 600;
    color: #2D2D2D;
    line-height: 1;
    display: inline-block;
    position: relative;
    width: fit-content;
    padding-bottom: 0.35rem;
    margin-top: 8px;
    justify-self: center;
}

.finding-number::after {
    content: '';
    position: absolute;
    left: -4px;
    right: -4px;
    bottom: 0;
    height: 3px;
    border-radius: 2px;
    background: #F5A623;
}

/* ============================================================
   Rotating Features List
   ============================================================ */

.rotating-features {
    display: flex;
    flex-direction: column;
    gap: 2rem;
    margin: 2rem 0;
}

.feature-item {
    display: grid;
    grid-template-columns: 40px 1fr;
    gap: 1.5rem; /* 24px between the icon column and its text */
    align-items: start;
}

.feature-icon {
    width: 60px;
    height: 60px;
    flex-shrink: 0;
    display: block;
    justify-self: center;
}

.feature-content {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding-top: 0.25rem;
}

.feature-title {
    font-family: 'Quicksand', sans-serif;
    font-size: 1rem;
    font-weight: 600;
    color: #2D2D2D;
}

.feature-text {
    font-family: 'Quicksand', sans-serif;
    font-size: 1rem;
    line-height: 1.8;
    color: #555;
}


/* ============================================================
   Project Partners / Logos
   ============================================================ */

.project-partners {
    display: flex;
    align-items: center;
    gap: 2rem;
    margin-top: 2rem;
    flex-wrap: wrap;
}

.project-partners img {
    height: 32px;
    width: auto;
    object-fit: contain;
    opacity: 0.7;
}

/* ============================================================
   Three-Column Image Grid (process iterations)
   ============================================================ */

.three-col-images {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1.5rem;
    margin: 2.5rem 0;
}

@media (max-width: 768px) {
    .three-col-images {
        grid-template-columns: 1fr;
    }
}

/* ============================================================
   Image Placeholders
   ============================================================ */

.img-placeholder {
    background: #D9D9D9;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: 'Quicksand', sans-serif;
    font-size: 0.8rem;
    color: #888;
    text-align: center;
    padding: 1rem;
    width: 100%;
}

.img-placeholder--hero {
    min-height: 380px;
}

.img-placeholder--full {
    min-height: 320px;
}

.img-placeholder--process {
    min-height: 180px;
}

.img-placeholder--card {
    width: 100%;
    aspect-ratio: 12 / 5;
}

/* ============================================================
   Behavior Carousel
   (reusable — pair with the [data-carousel] JS in project-script.js)
   ============================================================ */

.carousel {
    border: 2px solid #1A1D24;
    border-radius: 24px;
    overflow: hidden;
    margin: 2.5rem 0;
    background: #EBF1FF;
}

.carousel-viewport {
    position: relative;
    height: 480px; /* fixed — keeps carousel height consistent regardless of each slide's image aspect ratio */
    padding: 2.5rem;
    display: flex;
    align-items: center;
    justify-content: center;
}

.carousel-slide {
    display: none;
    width: 100%;
    height: 100%;
    align-items: center;
    justify-content: center;
}

.carousel-slide.active {
    display: flex;
}

.carousel-slide .img-placeholder {
    background: white;
    border: 1px solid #D9D9D9;
    border-radius: 12px;
    width: 100%;
    height: 100%;
}

.carousel-slide img {
    max-width: 100%;
    max-height: 100%;
    width: auto;
    height: auto;
    object-fit: contain;
    display: block;
    border-radius: 12px;
}

.carousel-caption {
    background: white;
    border-top: 2px solid #1A1D24;
    padding: 2rem 1.5rem 1.75rem;
    display: flex;
    flex-direction: column;
}

.carousel-title {
    font-family: 'Quicksand', sans-serif;
    font-size: 1.5rem;
    font-weight: 500;
    color: #2D2D2D;
    margin: 0 0 0.5rem;
}

.carousel-text {
    font-family: 'Quicksand', sans-serif;
    font-size: 1rem;
    line-height: 1.6;
    color: #555;
    margin: 0 0 1.75rem;
    max-width: 900px;
    min-height: 3.2rem; /* reserves space for 2 lines so the caption bar doesn't resize between slides */
}

.carousel-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1.5rem;
    margin-top: auto; /* anchors the controls to the bottom of the fixed-height caption, regardless of how many lines the title/text above take */
}

.carousel-arrow {
    background: none;
    border: none;
    padding: 0.5rem;
    margin: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #056AE0;
    cursor: pointer;
    transition: opacity 0.2s ease;
}

.carousel-arrow:hover {
    opacity: 0.65;
}

.carousel-dots {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.carousel-dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #313A44;
    border: 1.5px solid #313A44;
    padding: 0;
    margin: 0;
    cursor: pointer;
    opacity: 1;
}

.carousel-dot.active {
    background: #F5A623;
    border: 2px solid #313A44;
}

@media (max-width: 768px) {
    .carousel-viewport {
        height: 260px;
        padding: 1.5rem;
    }

    .carousel-caption {
        padding: 1.5rem;
    }

    .carousel-title {
        font-size: 1.4rem;
    }
}

/* ============================================================
   Footer
   ============================================================ */

.project-footer {
    background: white;
    padding: 3rem 2rem 24px;
    text-align: center;
    color: #74767E;
    font-size: 14px;
}

.footer-links {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    margin-top: 8px;
}

.footer-links a {
    color: #056AE0;
    text-decoration: none;
    font-size: 14px;
}

.footer-links a:hover {
    text-decoration: underline;
}

.footer-link-divider {
    width: 1px;
    height: 16px;
    background: #F5A623;
}

/* ============================================================
   MOBILE & TABLET NAV (≤1024px)
   Ported from the home page (styles.css/script.js) for exact visual parity:
   same frosted-glass collapsed pill + separately-floating brand pill, same
   shadow-isolation and clip-path-color-matching techniques. The one
   deliberate difference: there's no hamburger/full-screen-menu state here.
   The home page's collapsed pill opens a menu on tap; this nav holds a
   single "Back" link instead of an in-page menu, so the collapsed pill IS
   that link — tap navigates immediately, there's nothing to open.
   ============================================================ */
@media (max-width: 1024px) {
    :root {
        --nav-edge-offset: 24px;
    }

    /* --- Collapsed pill: just the Back link's icon now — sized to match the
       home page's collapsed hamburger pill exactly (12px padding around a
       24x24 icon = a 48x48 circle). The logo lives in its own separate
       .nav-brand pill (below), no longer stacked inside this one. */
    .side-nav {
        top: calc(var(--nav-edge-offset) + env(safe-area-inset-top, 0px));
        left: calc(var(--nav-edge-offset) + env(safe-area-inset-left, 0px));
        width: auto;
        padding: 12px;
        background: rgba(255, 255, 255, 0.15) !important;
        backdrop-filter: blur(12px);
        -webkit-backdrop-filter: blur(12px);
        border-radius: 999px;
        flex-direction: column;
        align-items: flex-start;
        transition: none;
    }

    /* Shadow lives on its own filter-free layer — see the home page's
       identical comment on this rule for why (backdrop-filter + box-shadow
       on the same rounded element is a known Safari rasterization bug). */
    .side-nav::before {
        content: '';
        position: absolute;
        inset: 0;
        border-radius: inherit;
        box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
        z-index: -1;
        pointer-events: none;
    }

    .nav-overlay::before {
        box-shadow: none;
    }

    .side-nav:hover {
        width: auto;
        background: rgba(255, 255, 255, 0.15) !important;
    }

    /* Frosted glass tint follows the same light/dark split the clip-path
       overlay uses for text/icon color, so the pill reads consistently
       instead of nearly disappearing against a background close to its own
       tint color. */
    .side-nav.light {
        background: rgba(5, 106, 224, 0.15) !important;
    }

    .side-nav.dark {
        background: rgba(255, 255, 255, 0.15) !important;
    }

    /* --- Logo pill (separate from the Back-link pill above) ---
       Always visible, always centered. project-script.js (placeNavBrand(),
       called from positionNavBrand()) moves the real element out to a direct
       child of <body> at this breakpoint so it's clear of #sideNav's own
       backdrop-filter — a `position: fixed` descendant nested inside another
       element's backdrop-filter hits a real WebKit bug where its own blur
       samples the wrong backdrop. Moved out to <body>, its top/left resolve
       directly against the real viewport — script.js sets both directly. */
    .nav-brand {
        position: fixed;
        margin-bottom: 0;
        height: 48px;
        padding: 0 12px 0 12px;
        background: rgba(255, 255, 255, 0.15) !important;
        backdrop-filter: blur(12px);
        -webkit-backdrop-filter: blur(12px);
        border-radius: 999px;
        z-index: 1000;
        overflow: visible;
        transition: none;
    }

    .nav-brand::before {
        content: '';
        position: absolute;
        inset: 0;
        border-radius: inherit;
        box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
        z-index: -1;
        pointer-events: none;
    }

    #navBrand .brand-icon {
        width: 30px;
        height: 30px;
    }

    /* #navBrand stays permanently .dark at this breakpoint — same permanent
       pinning as #sideNav itself — with .brand-overlay below doing the
       section-matched color switching instead of toggling this class. */
    #navBrand.dark {
        color: #056AE0;
    }

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

    #navBrand.dark .brand-icon .icon-blue {
        display: block;
    }

    #navBrand.dark .brand-icon .icon-white {
        display: none;
    }

    /* .brand-overlay is the same clip-path technique as the whole-nav
       .nav-overlay clone, scoped down to just .nav-brand's own icon+text
       (see project-script.js) — a permanently light (white) clone, clipped
       every scroll frame with the same inset-from-boundary math, for a
       continuous pixel-accurate wipe through the blue/white boundary. */
    .brand-overlay {
        display: flex;
        position: absolute;
        inset: 0;
        align-items: center;
        gap: 12px;
        padding: 0 12px 0 12px;
        pointer-events: none;
    }

    #navBrand .brand-overlay .brand-text {
        color: white;
    }

    #navBrand .brand-overlay .brand-icon .icon-white {
        display: block;
    }

    #navBrand .brand-overlay .brand-icon .icon-blue {
        display: none;
    }

    /* Wordmark stays visible at all times now — no more reveal-on-hover. Also overrides
       the desktop rule's max-width: 0 (see .brand-text above) — without resetting it here
       too, the label would stay clipped to zero width even at opacity: 1. */
    .brand-text {
        opacity: 1;
        width: auto;
        max-width: 150px;
    }

    /* The resize guard (html.is-resizing, near the top of this file) still forces
       .brand-text to opacity:0/max-width:0 !important underneath this — needed on desktop
       to hide the label during a native window-edge drag, but on iOS Safari plain page
       scrolling fires 'resize' events too (the dynamic toolbar hiding/showing), which
       triggered that same guard and made the always-visible wordmark here flicker/collapse
       mid-scroll. Same selector + !important, placed after, so it wins the tie and keeps
       the wordmark visible at this breakpoint regardless of the guard. */
    html.is-resizing .brand-text {
        opacity: 1 !important;
        max-width: 150px !important;
    }

    /* The overlay clone's own copy of .nav-brand would otherwise sit
       permanently visible on top of the real one, now that the real one is
       `position: fixed` and escapes this element's clip-path. The real
       .nav-brand gets its own independent clip (.brand-overlay) instead. */
    .nav-overlay .nav-brand {
        display: none;
    }

    /* --- Collapsed Back link: sized to exactly the icon itself (24x24) —
       #sideNav's own 12px padding is what creates the 48px circle around it,
       not this link's own padding on top of that. No hover-expand text
       reveal here — unlike the home page's in-page links, tapping this
       navigates immediately, so there's nothing gained by revealing the
       label first, and hover isn't a reliable gesture on touch anyway. */
    .nav-links {
        gap: 0;
    }

    .nav-link,
    .side-nav:hover .nav-link {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 0;
        width: 24px;
        /* Explicit, not left to content: .nav-text is width:0/opacity:0 here but still an
           in-flow flex item, and its own line-height (inherited from the 24px .nav-link
           font-size, body's line-height:1.6) would otherwise stretch this flex item to
           ~38px tall — leaving #sideNav's pill 48px wide but taller than it is wide,
           instead of the perfect circle the home page's hamburger pill has. */
        height: 24px;
        padding: 0;
        border-radius: 6px;
    }

    .nav-icon {
        padding: 0;
    }

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

    /* Subtle hover overlay, matching the home page's hamburger button rather
       than the desktop Back-link's solid fill pill (which would otherwise
       still apply here). */
    .nav-link:hover {
        background: rgba(255, 255, 255, 0.1);
    }

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

    #sideNav .nav-link.link-over-blue:hover {
        background: rgba(255, 255, 255, 0.1);
    }
}

/* ============================================================
   Responsive Design
   ============================================================ */

/* Matches the nav's own ≤1024px breakpoint (see MOBILE & TABLET NAV above) — content
   needs to drop its desktop nav-offset padding at the exact width the nav itself collapses
   to the small floating pill, or the 968–1024px range strands the old desktop gutter with
   no expanded nav there to justify it. */
@media (max-width: 1024px) {
    .hero-section {
        min-height: 40vh;
        padding: 3rem 0 1.5rem;
    }

    .hero-content {
        /* Full-width: content now scrolls behind the fixed nav icon instead of being
           pushed clear of it (see the frosted-glass .side-nav rules below, which add a
           blur + shadow so the nav still reads as elevated above content passing
           underneath it), so this just needs a small, even gutter on both sides. */
        padding-left: 1.5rem;
        padding-right: 1.5rem; /* 24px — matches content-section right edge */
    }

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

    .title-section {
        padding: 2rem 1.5rem 1.5rem;
    }

    .content-section {
        padding: 1.5rem; /* 24px on all sides — handles right edge spacing */
    }

    .section-container {
        padding-left: 0;
    }

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

    /* Drop the desktop-only 40px left margin/right padding so the quote's
       border and text line up with the surrounding paragraphs instead of
       sitting narrower than the rest of the content column. */
    .pull-quote {
        margin-left: 0;
        padding-right: 0;
    }
}

@media (max-width: 768px) {
    /* Both nav pills sit 12px from the edge on mobile, vs. 24px on tablet — read by
       the ≤1024px nav rules above, and by project-script.js (positionNavBrand()). */
    :root {
        --nav-edge-offset: 12px;
    }

    /* Wordmark reads large at 32px (inherited from the desktop .nav-brand rule)
       against a 30px icon on a small phone screen — sized down specifically for
       mobile, not tablet, where the larger size still reads fine. */
    .nav-brand {
        font-size: 24px;
    }

    .hero-content {
        padding-left: 1.5rem;  /* 24px — same gutter as the 1024px breakpoint above */
        padding-right: 1.5rem;
    }

    .section-container {
        padding-left: 0;
    }

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

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

    .two-col,
    .two-col.two-col--reverse,
    .two-col.two-col--equal {
        grid-template-columns: 1fr;
    }

    .case-study-card {
        grid-template-columns: 1fr;
    }

    .finding-item,
    .feature-item {
        grid-template-columns: 32px 1fr;
        gap: 1rem;
    }

    .meta-row {
        flex-direction: column;
        gap: 1.25rem;
    }

    .meta-item {
        grid-template-columns: 32px 1fr;
        gap: 1rem;
    }

    .meta-content {
        max-width: none;
    }

    /* Corner radius scaled to 50% of its desktop value, matching images shrinking down
       to the mobile breakpoint. */
    .hero-image img,
    .two-col-image img,
    .case-card-image img,
    .full-width-image img,
    .two-col-card-image img,
    .img-placeholder {
        border-radius: 4px; /* 8px desktop */
    }

    .carousel {
        border-radius: 12px; /* 24px desktop */
    }

    .carousel-slide .img-placeholder,
    .carousel-slide img {
        border-radius: 6px; /* 12px desktop */
    }
}