/* ============================================
   TOSIN ADE-AJAYI — PORTFOLIO
   Premium Editorial Design System
   ============================================ */

/* --- Breakpoints (documented set) ---
   1024px  tablet — page margin steps down, layouts stack, scroll-snap off
    768px  mobile — nav swaps to hamburger, single-column patterns
    480px  small phones — tightest overrides only
   Interaction gates (not widths):
    (hover: hover) and (pointer: fine)  custom cursors / cursor pills
    (pointer: coarse)                   touch scroll-snap + board touch-action
   Desktop (≥1025px) is signed off — changes live inside these queries only. */

/* --- CSS Variables --- */
:root {
    --bg-primary: #E4E2DE;
    --bg-dark: #0c0b0b;
--text-primary: #0c0b0b;
    --text-secondary: #7e7e7e;
    --text-muted: #b0b0b0;
    --text-light: #f7f7f7;
    --accent: #3a3a3a;
    --font-primary: 'Inter Tight', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
    --nav-height: 76px;
    --pm: 32px; /* page margin */
    --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
    --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
    --transition-smooth: 0.6s var(--ease-out-expo);
}

@media (max-width: 1024px) {
    :root { --pm: 20px; }
}

@media (max-width: 768px) {
    :root { --pm: 16px; }
}

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

html {
    scroll-behavior: smooth;
    font-size: 16px;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

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

/* Custom Figma cursor is a mouse-only affordance — on touch/hybrid devices the
   default (no) cursor applies and `cursor: none` never hides a real pointer. */
@media (hover: hover) and (pointer: fine) {
    body {
        cursor: url('../icons/figma-cursor.svg') 2 2, auto;
    }

    a, button, [role="button"], label[for] {
        cursor: url('../icons/figma-cursor.svg') 2 2, auto;
    }
}

img {
    display: block;
    max-width: 100%;
    height: auto;
}

a {
    text-decoration: none;
    color: inherit;
    transition: color 0.3s ease, opacity 0.3s ease;
}

/* --- Grain Overlay --- */
.grain-overlay {
    position: fixed;
    top: -50%;
    left: -50%;
    right: -50%;
    bottom: -50%;
    width: 200%;
    height: 200%;
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.03'/%3E%3C/svg%3E");
    pointer-events: none;
    z-index: 9999;
    opacity: 0.5;
}

/* ============================================
   NAVIGATION
   ============================================ */
.navbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: var(--nav-height);
    z-index: 1000;
    background: var(--bg-primary);
}

.nav-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 100%;
    padding: 0 var(--pm);
}

.nav-logo {
    font-size: 20px;
    font-weight: 500;
    letter-spacing: 1.2px;
    line-height: 1;
    color: var(--text-primary);
    white-space: nowrap;
}

.nav-name {
    font-weight: 700;
}

.nav-role {
    font-weight: 500;
    margin-left: 16px;
}

.nav-logo:hover {
    opacity: 0.6;
}

.nav-links {
    display: flex;
    gap: 40px;
}

.nav-link {
    font-size: 16px;
    font-weight: 500;
    letter-spacing: 1px;
    color: var(--text-primary);
    position: relative;
}

.nav-link::after {
    content: '';
    position: absolute;
    bottom: -2px;
    left: 0;
    width: 0;
    height: 1px;
    background: var(--text-primary);
    transition: width 0.4s var(--ease-out-expo);
}

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

.nav-link:hover {
    opacity: 0.6;
}

/* Nav right group: links + copy pill */
.nav-right {
    display: flex;
    align-items: center;
    gap: 32px;
}

/* Email copy pill */
.nav-copy-btn {
    display: flex;
    align-items: center;
    gap: 10px;
    background: var(--text-primary);
    border-radius: 100px;
    border: none;
    color: var(--text-light);
    padding: 8px 14px 8px 18px;
    cursor: pointer;
    transition: background 0.3s ease;
}

.nav-copy-btn:hover {
    background: #3a3a3a;
}

/* Secondary state — applied while the bottom contact panel is in view, so the
   nav button steps back from the panel's own solid buttons. An inset shadow
   draws the stroke without the layout shift a real border would cause.
   The icons use stroke="currentColor", so they pick up the colour too. */
.nav-copy-btn.is-secondary {
    background: transparent;
    color: var(--text-primary);
    box-shadow: inset 0 0 0 1px var(--text-primary);
}

.nav-copy-btn.is-secondary:hover {
    background: var(--text-primary);
    color: var(--text-light);
    box-shadow: inset 0 0 0 1px var(--text-primary);
}

.nav-copy-btn.copied {
    color: #6ee7b7;
}

.nav-email {
    font-size: 12px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.8px;
    color: inherit;
    white-space: nowrap;
}

/* Hamburger */
.nav-hamburger {
    display: none;
    position: relative;
    width: 32px;
    height: 32px;
    background: none;
    border: none;
    cursor: pointer;
    padding: 0;
    z-index: 1001;
    flex-shrink: 0;
}

/* Always show the close button if the menu is open, regardless of viewport */
.nav-hamburger.active {
    display: flex;
}

.nav-hamburger span {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 20px;
    height: 1.5px;
    background: var(--text-primary);
    transition: transform 0.3s ease;
}

/* Default (closed): two lines spaced symmetrically around centre */
.nav-hamburger span:first-child {
    transform: translate(-50%, calc(-50% - 4px));
}

.nav-hamburger span:last-child {
    transform: translate(-50%, calc(-50% + 4px));
}

/* Active (open): both lines share the same centre point → perfect X */
.nav-hamburger.active span:first-child {
    transform: translate(-50%, -50%) rotate(45deg);
}

.nav-hamburger.active span:last-child {
    transform: translate(-50%, -50%) rotate(-45deg);
}

/* Mobile Menu */
.mobile-menu {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: var(--bg-primary);
    z-index: 999;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.4s ease;
    /* Short/landscape viewports: the menu itself can scroll */
    overflow-y: auto;
    padding: 24px var(--pm);
}

.mobile-menu.active {
    opacity: 1;
    pointer-events: auto;
}

.mobile-menu-inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 32px;
    /* margin:auto centres when there's room, but — unlike align-items:center —
       never clips the top of the list when the menu is taller than a short
       (landscape) viewport and has to scroll */
    margin: auto;
}

.mobile-link {
    font-size: 32px;
    font-weight: 600;
    letter-spacing: -0.5px;
    color: var(--text-primary);
    transition: opacity 0.3s ease;
}

.mobile-link:hover {
    opacity: 0.5;
}

/* The menu's email button reuses .contact-btn — the same large pill as the
   homepage contact panel — so the small .mobile-copy-btn variant is gone.
   Only the top margin separating it from the links is menu-specific. */
.mobile-menu-inner .contact-btn {
    margin-top: 8px;
    /* On the homepage panel this button sits in a flex row and stretches to
       the taller LinkedIn button's 44px. Standing alone in the menu it would
       size to its own 38px, so pin the height to match — which also keeps the
       tap target at 44px at every width. */
    min-height: 44px;
}


/* ============================================
   HERO SECTION — Edge-to-edge massive type
   ============================================ */
/* The caption sits centred in the space between the bottom of the fixed nav
   and the bottom of the viewport. The nav height is reserved as top padding
   and there is no bottom padding, so centring within the remaining box gives
   an equal gap above and below — whatever the caption's content or length. */
.hero {
    min-height: 100vh;
    /* svh tracks the visible viewport on mobile, where 100vh overshoots */
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: stretch;
    padding: var(--nav-height) 0 0;
    position: relative;
    overflow: hidden;
}

.hero-content {
    width: 100%;
    padding: 0 var(--pm);
}

.hero-caption {
    font-size: clamp(28px, 4.2vw, 64px);
    font-weight: 500;
    line-height: 1.1;
    letter-spacing: -0.025em;
    max-width: 65vw;
    margin-left: auto;
    margin-right: auto;
}

.hero-caption-primary {
    display: block;
    color: var(--text-primary);
}

.hero-caption-secondary {
    display: block;
    color: var(--text-secondary);
}


/* ============================================
   FEATURED WORK — Full-bleed with overlay text
   ============================================ */
.work-sections {
    padding: 0;
    display: flex;
    flex-direction: column;
}

.project-section {
    position: relative;
    overflow: hidden;
    min-height: 80vh;
    min-height: 80svh; /* mobile: track the visible viewport */
    display: flex;
    align-items: stretch;
    margin: 0;
}

@media (hover: hover) and (pointer: fine) {
    .project-section {
        cursor: none;
    }
}

/* "View Case Study" cursor for the work cards — identical to the pill used on
   the case-study next panel (.cs-next-cursor), white on black. */
.project-cursor {
    position: fixed;
    top: 0;
    left: 0;
    display: flex;
    align-items: center;
    gap: 8px;
    background: var(--text-primary);
    color: var(--text-light);
    padding: 8px 16px 8px 12px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 500;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    white-space: nowrap;
    pointer-events: none;
    z-index: 9998;
    opacity: 0;
    /* scales up as it fades in; position is driven by left/top so movement
       is never transitioned */
    transform: translate(-50%, -50%) scale(0.85);
    transition: opacity 0.25s ease, transform 0.42s var(--ease-out-expo);
}

/* Full-bleed image fills the entire section — extends beyond bounds so parallax shift never exposes the background */
.project-section > .project-image-wrapper {
    position: absolute;
    top: -6%;
    bottom: -6%;
    left: 0;
    right: 0;
    overflow: hidden;
    border-radius: 0;
    background: #1a1a1a;
}

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

.project-placeholder {
    background-color: #2a2a2a;
    transition: transform 8s var(--ease-out-expo);
    will-change: transform;
}

.project-cursor.visible {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1);
}

/* Dark gradient overlay for text legibility */
.project-overlay {
    position: relative;
    z-index: 2;
    width: 100%;
    display: flex;
    align-items: center;
    background: linear-gradient(
        to bottom,
        rgba(0, 0, 0, 0) 0%,
        rgba(0, 0, 0, 0.02) 30%,
        rgba(0, 0, 0, 0.4) 70%,
        rgba(0, 0, 0, 0.72) 100%
    );
    padding: clamp(40px, 6vw, 80px) var(--pm);
    min-height: 80vh;
}

.project-overlay-inner {
    width: 100%;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    align-items: flex-end;
    gap: 40px;
}

/* Number + title grouped tight on the left */
.project-left {
    display: flex;
    flex-direction: column;
    gap: 6px;
    flex-shrink: 0;
}

.project-index {
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 1.5px;
    color: rgba(255, 255, 255, 0.5);
}

.project-title {
    font-size: clamp(32px, 5vw, 64px);
    font-weight: 600;
    letter-spacing: -0.03em;
    line-height: 1.05;
    color: #fff;
}

.project-description {
    font-size: clamp(16px, 1.6vw, 20px);
    font-weight: 400;
    line-height: 1.65;
    color: rgba(255, 255, 255, 0.6);
    max-width: 400px;
    letter-spacing: -0.01em;
    flex-shrink: 0;
}


/* ============================================
   PHILOSOPHY SECTION
   ============================================ */
.philosophy {
    padding: clamp(80px, 12vw, 140px) var(--pm);
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    background: var(--bg-primary);
    position: relative;
}

.philosophy-text {
    font-size: clamp(28px, 5vw, 64px);
    font-weight: 500;
    line-height: 1.1;
    letter-spacing: -0.03em;
    color: var(--text-primary);
}

/* Contact buttons — same pill pattern as the nav email button (.nav-copy-btn) */
.contact-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 12px;
    margin-top: clamp(52px, 6.5vw, 78px);
}

.contact-btn {
    display: inline-flex;
    align-items: center;
    gap: 12px;
    background: var(--text-primary);
    border-radius: 100px;
    border: none;
    color: var(--text-light);
    /* padding scaled for the 16px label */
    padding: 10px 18px 10px 22px;
    cursor: pointer;
    transition: background 0.3s ease;
}

.contact-btn:hover {
    background: #3a3a3a;
}

/* Matches the nav copy button's confirmation colour */
.contact-btn.copied {
    color: #6ee7b7;
}

.contact-btn-label {
    font-size: 16px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.8px;
    color: inherit;
    white-space: nowrap;
}

/* ============================================
   FOOTER
   ============================================ */
.footer {
    padding: clamp(60px, 8vw, 100px) var(--pm) clamp(24px, 3vw, 40px);
    background: var(--bg-primary);
    border-top: 1px solid rgba(12, 11, 11, 0.08);
}

.footer-inner {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 32px;
}

.footer-links {
    display: flex;
    gap: clamp(24px, 4vw, 56px);
    flex-wrap: wrap;
    justify-content: flex-start;
}

.footer-link {
    font-size: 13px;
    font-weight: 500;
    letter-spacing: 0.5px;
    color: var(--text-primary);
    position: relative;
    transition: opacity 0.3s ease;
}

.footer-link::after {
    content: '';
    position: absolute;
    bottom: -2px;
    left: 0;
    width: 0;
    height: 1px;
    background: var(--text-primary);
    transition: width 0.4s var(--ease-out-expo);
}

.footer-link:hover::after {
    width: 100%;
}

.footer-link:hover {
    opacity: 0.6;
}

.footer-copyright {
    font-size: 11px;
    font-weight: 400;
    color: var(--text-muted);
    letter-spacing: 0.3px;
}

/* ============================================
   SCROLL ANIMATIONS
   ============================================ */
[data-animate="fade-up"] {
    opacity: 0;
    transform: translateY(40px);
    transition: opacity 0.8s ease, transform 0.8s var(--ease-out-expo);
}

[data-animate="fade-up"].visible {
    opacity: 1;
    transform: translateY(0);
}

[data-animate="scale-in"] {
    opacity: 0;
    transform: scale(0.96);
    transition: opacity 0.8s ease, transform 1s var(--ease-out-expo);
}

[data-animate="scale-in"].visible {
    opacity: 1;
    transform: scale(1);
}


/* ============================================
   RESPONSIVE
   ============================================ */
@media (max-width: 1024px) {
    .project-overlay-inner {
        flex-direction: column;
        align-items: flex-start;
        gap: 16px;
    }

    .project-description {
        max-width: 100%;
    }
}

/* Nav swaps to the hamburger at 1024px, not 768px.
   Measured: the gap between the logo and the nav-right group closes to 0px at
   950px and the two groups sit flush from there down — they were touching
   across the whole 769–1024 band. Switching at 1024 keeps a comfortable
   margin at every width where the full nav is shown. */
@media (max-width: 1024px) {
    .nav-right {
        display: none;
    }

    .nav-hamburger {
        display: flex;
    }
}

@media (max-width: 768px) {
    /* Padding stays symmetric (nav height top, none bottom) so the caption
       remains centred between the nav and the viewport bottom on mobile too. */

    .hero-caption {
        max-width: 90vw;
    }


    .project-section {
        min-height: 60vh;
    }

    .project-overlay {
        min-height: 60vh;
        padding: clamp(24px, 5vw, 40px) var(--pm);
    }

    .project-title {
        font-size: clamp(28px, 8vw, 48px);
    }

    .philosophy-text {
        font-size: clamp(24px, 7vw, 40px);
    }

    .philosophy-text br {
        display: none;
    }

    .footer-links {
        flex-direction: column;
        align-items: flex-start;
        gap: 16px;
    }
}

@media (max-width: 480px) {
    /* Name (20px, tracked) + role no longer fit beside the hamburger below
       ~480px — the nowrap logo would push past the viewport edge. */
    .nav-role {
        display: none;
    }

    /* Contact pill: the full email at 16px caps overflows a 320px viewport */
    .contact-btn-label {
        font-size: 13px;
    }

    .contact-btn {
        padding: 10px 16px 10px 18px;
        min-height: 44px; /* keep the tap target */
    }
}

/* ============================================
   SELECTION & FOCUS
   ============================================ */
::selection {
    background: rgba(12, 11, 11, 0.15);
    color: var(--text-primary);
}

:focus-visible {
    outline: 2px solid var(--text-primary);
    outline-offset: 4px;
}

/* ============================================
   SMOOTH SCROLL OFFSET
   ============================================ */
#work, #about {
    scroll-margin-top: var(--nav-height);
}

/* ============================================
   WORK CARDS — full-viewport snap sections (Garri-style)
   ============================================ */
html {
    scroll-snap-type: y mandatory;
    scroll-padding-top: 0;
}

/* Touch devices wider than 1024px (e.g. iPad Pro landscape): mandatory
   snapping fights momentum scrolling and can trap sections taller than the
   viewport, so relax it. Desktop mouse/trackpad (pointer: fine) is unaffected. */
@media (pointer: coarse) {
    html {
        scroll-snap-type: y proximity;
    }
}

.hero {
    scroll-snap-align: start;
    scroll-snap-stop: always;
}

/* Philosophy + footer — full-viewport scroll section.
   No progress dot: dots only track .work-card elements. */
.page-end {
    scroll-snap-align: start;
    scroll-snap-stop: always;
    min-height: 100svh;
    /* containing block for the absolutely-positioned footer */
    position: relative;
}

/* The text + buttons centre in the space between the bottom of the fixed nav
   and the bottom of the viewport. Nav height is reserved as top padding with
   no bottom padding, so centring splits the remainder equally — an identical
   gap above and below, whatever the block contains. */
.page-end .philosophy {
    min-height: 100svh;
    padding-top: var(--nav-height);
    padding-bottom: 0;
}

/* Pinned to the bottom and out of flow, so it doesn't pull the block off
   centre. No keyline between the centred text and the footer. */
.page-end .footer {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    border-top: none;
}

.work-list {
    padding: 0 var(--pm);
    display: flex;
    flex-direction: column;
}

.work-card {
    min-height: 100svh;
    scroll-snap-align: start;
    scroll-snap-stop: always;
    display: grid;
    grid-template-columns: minmax(240px, 1fr) 2.8fr;
    grid-template-rows: auto;
    align-content: center;
    align-items: start;
    column-gap: clamp(32px, 5vw, 96px);
    padding: var(--nav-height) 0 0;
}

/* Only the title, description and screen group swap the native cursor for the
   "View Case Study" pill — the rest of the card keeps the default cursor.
   Mouse-only: on touch these areas stay plain tap targets (click handlers in
   main.js still navigate). */
@media (hover: hover) and (pointer: fine) {
    .work-card-title,
    .work-card-desc,
    .work-card-screens,
    .work-card-screens * {
        cursor: none;
    }
}

/* Number floats just above the title, out of flow so it doesn't shift the group */
/* Matches the case-study section label (.cs-section-num — e.g. "Overview") */
.work-card-index {
    position: absolute;
    left: 0;
    bottom: calc(100% + 8px);
    font-size: 16px;
    font-weight: 500;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    /* matches the hero caption's secondary text */
    color: var(--text-secondary);
}

.work-card-text {
    grid-column: 1;
    grid-row: 1;
    align-self: start;
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 20px;
}

.work-card-title {
    font-size: clamp(32px, 4.5vw, 56px);
    font-weight: 600;
    letter-spacing: -0.03em;
    line-height: 0.95;
    color: var(--text-primary);
}

.work-card-desc {
    /* 16px floor on small screens, reaching 24px from ~1410px up */
    font-size: clamp(16px, 1.7vw, 24px);
    font-weight: 400;
    line-height: 1.65;
    /* matches the hero caption's secondary text */
    color: var(--text-secondary);
    max-width: 440px;
    letter-spacing: -0.01em;
}

.work-card-link {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    margin-top: 8px;
    padding: 10px 20px;
    border: 1px solid rgba(12, 11, 11, 0.3);
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--text-primary);
    transition: background 0.3s ease, color 0.3s ease, border-color 0.3s ease;
}

.work-card-link svg {
    transition: transform 0.4s var(--ease-out-expo);
}

.work-card:hover .work-card-link {
    background: var(--text-primary);
    border-color: var(--text-primary);
    color: var(--text-light);
}

.work-card:hover .work-card-link svg {
    transform: translate(2px, -2px);
}

/* Screens define the row height; the title top-aligns to their top */
.work-card-screens {
    grid-column: 2;
    grid-row: 1;
    align-self: start;
    display: flex;
    gap: clamp(12px, 1.6vw, 24px);
    justify-content: flex-end;
    padding-right: clamp(28px, 3vw, 48px);
}

.wc-phone {
    flex: 1 1 0;
    max-width: min(360px, calc((100svh - 215px) * 0.4618));
    aspect-ratio: 375 / 812;
    background: transparent;
    border-radius: clamp(12px, 1.6vw, 32px);
    overflow: hidden;
}

.wc-phone:empty {
    background: #2a2a2a;
}

/* Always fit the frame's width and take height from the image's own ratio, so
   no export is ever magnified or side-cropped. The frame's overflow:hidden
   clips taller exports from the bottom. */
.wc-phone img {
    width: 100%;
    height: auto;
    display: block;
}

/* ============================================
   WORK PROGRESS DOTS
   ============================================ */
.work-progress {
    position: fixed;
    right: var(--pm);
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    flex-direction: column;
    gap: 14px;
    z-index: 900;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.35s ease;
}

.work-progress.visible {
    opacity: 1;
    pointer-events: auto;
}

.wp-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: rgba(12, 11, 11, 0.14);
    border: none;
    padding: 0;
    cursor: pointer;
    transition: background 0.3s ease, transform 0.3s ease;
}

.wp-dot:hover {
    transform: scale(1.3);
}

.wp-dot.active {
    background: var(--text-primary);
}

/* Migrated from a one-off 900px query onto the standard 1024px breakpoint —
   the 901–1024px band (tablet landscape) now gets the stacked/tablet layout;
   desktop ≥1025px renders exactly as before. */
@media (max-width: 1024px) {
    /* Dots stay visible below desktop — the cards are still full-viewport
       snap sections here, so the progress indicator is still meaningful.
       Spacing keeps the desktop 14px gap; only the inset changes, to 24px, so
       the touch target below can't reach past the viewport edge.
       Vertical position is set by main.js to centre on the card's screens
       rather than on the viewport. */
    .work-progress {
        right: 24px;
        /* Easing matches the dot's own state change and the site's motion
           curve, so moving between cards reads as the same subtle shift the
           desktop dots have rather than a jump. */
        transition: opacity 0.35s ease, top 0.6s var(--ease-out-expo);
    }

    /* The dot stays 10px visually; a transparent pseudo-element supplies the
       touch target, since a 10px control is unusable on a finger. 24px tall
       matches the 10px dot + 14px gap pitch exactly, so neighbouring targets
       tile without overlapping; the extra width is free space. */
    .wp-dot {
        position: relative;
    }

    .wp-dot::after {
        content: '';
        position: absolute;
        top: 50%;
        left: 50%;
        width: 40px;
        height: 24px;
        transform: translate(-50%, -50%);
    }

    /* Set by main.js when the column would sit on top of the screen images
       (narrow viewports, where the phones run to the right edge). visibility
       rather than display so the element can still be measured. */
    .work-progress.is-clashing {
        visibility: hidden;
        pointer-events: none;
    }

    @media (prefers-reduced-motion: reduce) {
        .work-progress {
            transition: opacity 0.35s ease;
        }
    }

    /* Full-viewport sectioning is kept below desktop — each work card and the
       contact panel still fill the window and snap, as they do on desktop.
       The end section puts its footer back in flow so the centred contact
       block can't collide with it on short viewports; the philosophy block
       centres in whatever space the footer leaves. */
    .page-end {
        min-height: 100svh;
        display: flex;
        flex-direction: column;
    }

    .page-end .philosophy {
        flex: 1 1 auto;
        min-height: 0;
    }

    .page-end .footer {
        position: static;
    }

    .work-card {
        min-height: 100svh;
        grid-template-columns: 1fr;
        grid-template-rows: auto auto;
        align-content: center;
        padding: var(--nav-height) 0 0;
        row-gap: clamp(20px, 3.5vh, 40px);
    }

    .work-card-index { position: static; margin-bottom: 0; }

    /* Text block leads, screens follow underneath */
    .work-card-text {
        grid-column: 1;
        grid-row: 1;
    }

    .work-card-screens {
        grid-column: 1;
        grid-row: 2;
        justify-content: flex-start;
        padding-right: 0;
    }

    /* Screens take their height from what's left after the text, so a card
       always fits one viewport and never gets stranded mid-snap. */
    .wc-phone {
        max-width: min(280px, calc((100svh - 340px) * 0.4618));
    }

    /* ── Headline scale below desktop ──
       The base rules bottom out on floors tuned for a compact layout (28px
       hero, 32px card title), which lost the weight these full-viewport
       statements carry on desktop. Each override raises the small-screen floor
       and adds a fixed offset to the vw rate, capped at exactly the value the
       base rule produces at 1025px — so the scale climbs smoothly and meets
       desktop with no step at the breakpoint. Desktop itself is untouched. */
    .hero-caption {
        font-size: clamp(34px, 4.2vw + 12px, 43px);
    }

    .philosophy-text {
        font-size: clamp(32px, 5vw + 6px, 51px);
    }

    .work-card-title {
        font-size: clamp(38px, 4.5vw + 8px, 46px);
    }

    /* Floor only — 16px was thin beside the larger title */
    .work-card-desc {
        font-size: clamp(17px, 1.7vw, 24px);
    }
}

/* Hero statement on phones. Placed after the 1024 block above so it wins the
   cascade — both are single-class selectors, so source order decides.
   The copy is allowed to run to more lines here so the type can be far
   larger, keeping the bold-headline weight the desktop version has instead of
   shrinking to hold a line count: ~45px at 375px (was 34px), 9 lines not 7.
   The second declaration also caps against viewport height — .hero is 100svh
   with overflow:hidden, so on very short viewports (phone landscape, ~320px
   tall) the taller block would clip. It only engages below ~400px of height;
   everywhere else the width-based value wins. Browsers without svh support
   fall back to the first declaration. */
@media (max-width: 768px) {
    .hero-caption {
        font-size: clamp(42px, 12vw, 48px);
        font-size: min(clamp(42px, 12vw, 48px), 11svh);
    }
}

/* Landscape phones / very short windows can't fit a full card, and snapping
   there would strand the user mid-section — fall back to natural flow. */
@media (max-width: 1024px) and (max-height: 620px) {
    html { scroll-snap-type: none; }

    .work-card,
    .page-end {
        min-height: 0;
    }

    .work-card {
        padding: 48px 0;
    }

    .wc-phone { max-width: 200px; }
}
