/* The homepage stylesheet, extracted from index.html's inline <style> block.
   Depends on css/tokens.css, which must be linked first.

   url() paths are root-relative: inside a stylesheet they would otherwise
   resolve against /css/. */

/* --container is homepage-scoped and deliberately NOT in tokens.css: case
   studies use 1140px (see css/case-study.css), so this value must stay
   per-context rather than centralised. --gutter is here for the same reason:
   it is the page gutter that goes with THIS container. */
:root {
    /* Matches the showcase build's hero column. At 1745 the old 1280 cap
       left 226px of gutter a side and squeezed every section 260px narrower
       than the design; the showcase runs a 1540 column on a 4.5vw gutter and
       that is what the layout was drawn against. */
    --container: 1540px;
    /* The page gutter, as one number. .container's padding and the hero
       dial's offset both have to resolve to it or the dial stops landing on
       the copy column's right edge. 4.5vw is the showcase's ramp; the 24px
       floor is this site's existing phone padding, so nothing below ~533px
       moves at all. */
    --gutter: clamp(24px, 4.5vw, 84px);
}

    /* ============ Base ============ */
    *, *::before, *::after { box-sizing: border-box; }
    html { scroll-behavior: smooth; }
    body {
        margin: 0;
        font-family: var(--font-body);
        font-size: var(--fs-body);
        line-height: var(--lh-body);
        color: var(--text-on-dark);
        background: var(--surface-deep);
        -webkit-font-smoothing: antialiased;
    }
    img { max-width: 100%; display: block; }
    a { color: inherit; }
    :focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 3px; border-radius: 4px; }

    .skip-link{position:absolute;left:-9999px;top:0;z-index:100000;background:#381c55;color:#fff;padding:10px 16px;border-radius:0 0 8px 0;font-weight:700;text-decoration:none}
    .skip-link:focus{left:0}
    .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}

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

    h1, h2, h3 {
        font-family: var(--font-display);
        line-height: var(--lh-display);
        letter-spacing: var(--ls-display);
        margin: 0 0 var(--space-5);
    }
    h1 { font-size: var(--fs-h1); font-weight: 800; }
    h2 { font-size: var(--fs-h2); font-weight: 800; }
    h3 { font-size: var(--fs-h3); font-weight: 700; }
    p  { margin: 0 0 var(--space-4); max-width: 62ch; }

    /* The operative word treatment: the signature of the GU type voice */
    .gu-word {
        font-family: var(--font-logo);
        font-weight: 900;
        color: var(--text-on-dark-accent);
        letter-spacing: 0.005em;
    }
    .on-light .gu-word { color: var(--gu-purple-600); }

    .eyebrow {
        display: inline-block;
        font-size: var(--fs-label);
        font-weight: 700;
        letter-spacing: var(--ls-label);
        text-transform: uppercase;
        margin-bottom: var(--space-4);
        color: var(--text-on-dark-accent);
    }
    .on-light .eyebrow { color: var(--gu-purple-500); }

    /* ============ Buttons (pill, flat, 2px stroke) ============ */
    .btn {
        position: relative;
        display: inline-flex;
        align-items: center;
        gap: 10px;
        padding: 14px 28px;
        font-family: var(--font-body);
        font-size: var(--fs-body);
        font-weight: 600;
        text-decoration: none;
        border-radius: var(--radius-pill);
        border: var(--border-width) solid transparent;
        cursor: pointer;
        overflow: hidden;
        isolation: isolate;
        transition: color var(--dur) var(--ease-standard),
                    border-color var(--dur) var(--ease-standard),
                    transform var(--dur-fast) var(--ease-standard);
    }
    .btn:active { transform: scale(0.98); }
    /* fill-sweep micro-interaction */
    .btn::before {
        content: "";
        position: absolute;
        inset: 0;
        z-index: -1;
        border-radius: inherit;
        transform: translateY(101%);
        transition: transform var(--dur-slow) var(--ease-out);
    }
    .btn:hover::before, .btn:focus-visible::before { transform: translateY(0); }
    .btn .arrow { transition: transform var(--dur) var(--ease-standard); }
    .btn:hover .arrow { transform: translateX(4px); }

    .btn-primary { background: var(--gu-lilac-200); color: var(--gu-purple-900); }
    .btn-primary::before { background: var(--gu-paper); }


    .on-light .btn-primary { background: var(--gu-purple-900); color: var(--gu-paper); }
    .on-light .btn-primary::before { background: var(--gu-purple-600); }
    .on-light .btn-primary:hover { color: var(--gu-paper); }

    /* ============ Sticky header ============ */
    .site-header {
        position: fixed;
        top: 0; left: 0; right: 0;
        z-index: 100;
        padding: 22px 0;
        background: transparent;
        border-bottom: 1px solid transparent;
        transition: background var(--dur-slow) var(--ease-out),
                    border-color var(--dur-slow) var(--ease-out),
                    box-shadow var(--dur-slow) var(--ease-out),
                    transform var(--dur-slow) var(--ease-standard);
    }
    /* The solid bar. It is never shown ON the hero: the first scroll down takes
       the nav straight out of view while it is still transparent, and it only
       comes back — already solid — on a scroll up. See the state machine in the
       page script. */
    .site-header.is-scrolled {
        background: #1C0447;
        border-bottom-color: rgba(208,193,255,0.18);
        box-shadow: 0 8px 30px rgba(12, 2, 33, 0.35);
    }
    /* Hidden while scrolling down, revealed on any upward scroll (JS toggles it).
       -105% clears the drop shadow; focus-within keeps keyboard nav reachable. */
    .site-header.is-hidden { transform: translateY(-105%); }
    .site-header.is-hidden:focus-within { transform: none; }
    /* Two states where the colour must change with no animation:
       .is-hidden — the bar is armed solid while it is off-screen, so it slides
       back in already dark instead of visibly turning dark on the way in;
       .is-instant — a reload at a scrolled position, which must not fade the
       bar in from transparent. Both keep the transform transition. */
    .site-header.is-hidden,
    .site-header.is-instant {
        transition: transform var(--dur-slow) var(--ease-standard);
    }
    .site-header .container {
        display: flex;
        align-items: center;
        justify-content: space-between;
    }

    /* ---- Logo via CSS mask: one SVG file, any colour ----
       Place the provided file at images/GoodUnicorn_logo.svg
       (its internal fill is irrelevant; only the alpha channel is used). */
    .logo-mask {
        display: inline-block;
        background-color: var(--gu-lilac-200); /* #D0C1FF */
        -webkit-mask-image: url("/images/GoodUnicorn_logo.svg");
        -webkit-mask-repeat: no-repeat;
        -webkit-mask-position: center;
        -webkit-mask-size: contain;
        mask-image: url("/images/GoodUnicorn_logo.svg");
        mask-repeat: no-repeat;
        mask-position: center;
        mask-size: contain;
        aspect-ratio: 560 / 185;
    }
    .site-header .logo-mask { height: 46px; }
    /* The pill sits on the bright top-right of the hero, so it is dark there; once the
       solid bar kicks in on scroll it flips to lilac. (The logo sits over the scrim, so
       it stays light in both states.) */
    .site-header .btn-primary { background: var(--gu-purple-900); color: var(--gu-paper); }
    .site-header .btn-primary::before { background: var(--gu-purple-600); }
    .site-header.is-scrolled .btn-primary { background: var(--gu-lilac-200); color: var(--gu-purple-900); }
    .site-header.is-scrolled .btn-primary::before { background: var(--gu-paper); }
    .site-header .btn { padding: 10px 22px; font-size: var(--fs-body-sm); white-space: nowrap; }
    /* Keep the nav CTA on one line on narrow phones: since the bar is now always
       visible over the hero, a wrapped two-line pill would inflate it and overlap
       the "A new chapter" eyebrow. */
    @media (max-width: 560px) {
        .site-header .btn { padding: 9px 15px; font-size: 13px; }
    }

    /* ============================================================
       COVER-STACK
       The hero pins and the section after it slides up over its rounded
       top — the way the acts stack on the showcase build.

       Sticky, so the hero stays IN FLOW: nothing below it moves, the
       document height is unchanged and every anchor still lands where it
       did. Only the hero's painted position changes.

       All of it is switched on by html.act-stack, which the script adds
       only where motion is welcome — with it off this is an ordinary
       stack of sections.
       ============================================================ */
    html.act-stack #story {
        position: relative;
        z-index: 2;
        border-radius: 28px 28px 0 0;
        margin-top: -28px;
    }
    @supports (animation-timeline: view()) and (timeline-scope: --t) {
        html.act-stack body { timeline-scope: --act2; }
        html.act-stack #story { view-timeline-name: --act2; }
        /* The hero dims as it is buried. ::after is already the film grain
           on every .s-deep, so this is ::before; z-index 3 puts it over
           `.s-deep > .container`'s 2, which is the content it has to dim. */
        html.act-stack .hero::before {
            content: "";
            position: absolute;
            inset: 0;
            z-index: 3;
            background: #0E0124;
            opacity: 0;
            pointer-events: none;
            animation: act-covered linear both;
            animation-timeline: --act2;
            /* cover 0% is the story's top edge at the bottom of the screen,
               exit 0% is that same edge at the top of it. That span is
               exactly the burial — and it is read off the INCOMING section,
               because a pinned element's own timeline never advances. */
            animation-range: cover 0% exit 0%;
        }
        @keyframes act-covered { to { opacity: 0.45; } }
    }

    /* ============================================================
       CURSOR
       The showcase build's cursor: an 8px dot that tracks the pointer
       exactly, and a ring that lags behind it and swells into a labelled
       pill over anything carrying data-cursor.

       Fine pointers only, and only once the script has confirmed it — the
       `cursor: none` below is gated on html.cur-on, so a browser that never
       runs it, or a visitor on a touch screen or with reduced motion, keeps
       the native pointer and never sees a dead cursorless page.
       ============================================================ */
    .cur-dot, .cur-ring {
        position: fixed;
        top: 0; left: 0;
        pointer-events: none;
        z-index: 9999;   /* over the showreel overlay (990) and its trigger (991) */
        opacity: 0;
        will-change: transform;
    }
    html.cur-on .cur-dot, html.cur-on .cur-ring { opacity: 1; }
    .cur-dot {
        width: 8px; height: 8px;
        margin: -4px 0 0 -4px;
        border-radius: 50%;
        background: var(--gu-lilac-200);
    }
    /* The showcase dot used to invert itself with mix-blend-mode: difference.
       That reads well and is charged for expensively — a blended element
       forces its whole stacking context to re-composite, and this one moves
       with every pointer event over a page carrying a live WebGL canvas. The
       same read comes from the section tone the script publishes on the root,
       for the cost of a class. */
    html[data-act="light"] .cur-dot { background: var(--gu-purple-900); }
    .cur-ring {
        width: 32px; height: 32px;
        margin: -16px 0 0 -16px;
        /* quiet by default — the ring is an accent, not a reticle */
        border: 1px solid rgba(196, 170, 243, 0.45);
        border-radius: 50%;
        display: grid;
        place-items: center;
        font-family: var(--font-display);
        font-size: 0;
        font-weight: 800;
        white-space: nowrap;
        color: var(--gu-purple-900);
        transition: width var(--dur) var(--ease-out), height var(--dur) var(--ease-out),
                    margin var(--dur) var(--ease-out), background var(--dur) var(--ease-out),
                    font-size var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
    }
    /* A 45%-alpha lilac ring is invisible on paper. The showcase never had to
       solve this — its light acts are covered by the stack most of the time —
       but half this page is #FAFAFA. */
    html[data-act="light"] .cur-ring { border-color: rgba(28, 4, 71, 0.35); }
    html.cur-label .cur-ring {
        width: 82px; height: 82px;
        margin: -41px 0 0 -41px;
        background: var(--gu-lilac-200);
        border-color: transparent;
        font-size: 14px;
    }
    html.cur-label .cur-dot { opacity: 0; }
    /* #video-trigger sets `cursor: pointer` from an id selector, which outranks
       an attribute one — it has to be named here or it keeps the arrow. */
    @media (hover: hover) and (pointer: fine) {
        html.cur-on, html.cur-on a, html.cur-on button,
        html.cur-on [data-cursor], html.cur-on #video-trigger { cursor: none; }
    }

    /* ============ Sections ============ */
    section { padding: var(--space-9) 0; }
    @media (min-width: 900px) { section { padding: var(--space-10) 0; } }

    .s-paper { background: var(--surface-paper); color: var(--text-on-light); }
    .s-eggplant { background: var(--surface-eggplant); color: var(--text-on-dark); }
    .s-lilac { background: var(--surface-lilac); color: var(--text-on-light); }
    .s-deep { background: var(--surface-deep); color: var(--text-on-dark); }

    /* ---- Subtle film-grain noise on purple surfaces ----
       Pure SVG feTurbulence, no image request. Tune opacity to taste (0.03 to 0.07). */
    .s-deep, .s-eggplant { position: relative; }
    .s-deep::after, .s-eggplant::after {
        content: "";
        position: absolute;
        inset: 0;
        z-index: 1;
        pointer-events: none;
        opacity: 0.05;
        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
        background-size: 160px 160px;
        mix-blend-mode: overlay;
    }
    .s-deep > .container, .s-eggplant > .container { position: relative; z-index: 2; }

    /* ============ Hero ============ */
    /* Dark hero (Figma "dark-gradient" export): near-black field #0a0020 lifted by
       large, soft, blurred purple glows (#381c55 / #28143d). Light text throughout. */
    .hero {
        position: relative;
        min-height: 92vh;
        display: flex;
        flex-direction: column;
        justify-content: center;
        overflow: hidden;
        padding: var(--space-8) 0 var(--space-9);
        /* Exact Figma background export (glow shapes + swirl), with a left-side scrim
           that darkens the copy column so the light text stays legible while the
           luminous glow + swirl remain on the right. */
        background:
            linear-gradient(90deg, rgba(10, 0, 32, 0.95) 0%, rgba(10, 0, 32, 0.85) 24%, rgba(10, 0, 32, 0.45) 42%, rgba(10, 0, 32, 0) 58%),
            url("/images/dark-gradient.svg") center center / cover no-repeat,
            #0A0020;
    }

    /* width:100% stops the flex column's auto-margins shrinking the container
       to its content, so the hero aligns to the same --container width/margins
       as every other section instead of sitting narrower. */
    .hero .container { position: relative; z-index: 2; width: 100%; }

    .hero-eyebrow { margin-bottom: var(--space-5); color: var(--gu-lilac-300); }

    .hero h1 { max-width: 19ch; margin-bottom: var(--space-6); }
    .hero .hero-sub {
        font-size: var(--fs-body-lg);
        color: var(--text-on-dark-muted);
        max-width: 52ch;
        margin-bottom: var(--space-7);
    }
    .hero-ctas { display: flex; flex-wrap: wrap; gap: var(--space-4); }

    /* ============ Showreel CTA (adapted from the Creamos home-hero reel) ============
       Circular rotating "Watch our reel" trigger in the hero; clicking opens a
       fixed fullscreen overlay that lazy-loads the responsive MP4 and plays with
       sound. The trigger sits ABOVE the overlay and becomes the close button. */
    #video-trigger {
        position: absolute;
        z-index: 10; /* under the fixed site header (100) while scrolling */
        /* keep it within the content container on wide screens (matches the swirl) */
        right: max(16px, calc((100% - var(--container)) / 2 + 20px));
        bottom: 7svh;
        width: 6.5rem;
        aspect-ratio: 1;
        display: flex;
        align-items: center;
        justify-content: center;
        cursor: pointer;
        -webkit-tap-highlight-color: transparent;
    }
    /* while the showreel is open the trigger doubles as the close control,
       so only then does it rise above the overlay (990) */
    #video-trigger.active { z-index: 991; }
    #video-trigger #circle-svg { width: 100%; height: 100%; overflow: visible; }
    #video-trigger #circle-background { transition: fill var(--dur) var(--ease-standard); }
    #video-trigger.active #circle-background { fill: rgba(0, 0, 0, 0.7); }
    #video-trigger #text-group {
        transform-origin: 150px 150px;
        animation: reel-rotate 22s linear infinite;
        will-change: transform;
    }
    #video-trigger #circle-text {
        fill: var(--gu-lilac-200);
        font-family: var(--font-body);
        font-weight: 500;
        /* Fill the ring exactly. Chrome ignores SVG textLength, so letter-spacing
           is tuned per font: 5.5px safely under-fills the system fallback on any
           OS (never overflows); JS adds .reel-font-ready to switch to DM Sans's
           exact 8.1px once that font is loaded. */
        letter-spacing: 5.5px;
        font-size: 34px;
        transition: opacity var(--dur) var(--ease-standard), letter-spacing var(--dur-slow) var(--ease-standard);
    }
    #video-trigger.reel-font-ready #circle-text { letter-spacing: 8.1px; }
    #video-trigger.active #circle-text { opacity: 0; }
    #video-trigger .reel-icon {
        position: absolute;
        top: 50%; left: 50%;
        transform: translate(-50%, -50%);
        color: var(--gu-paper);
        transition: opacity var(--dur) var(--ease-standard), transform var(--dur) var(--ease-standard);
        pointer-events: none;
    }
    /* idle center = mouse-scroll; idle-hover = speaker (sound); playing = close (Creamos-match) */
    #video-trigger #btn-icon-sound,
    #video-trigger #btn-icon-close { opacity: 0; }
    #video-trigger:hover:not(.active) #btn-icon-mouse { opacity: 0; }
    #video-trigger:hover:not(.active) #btn-icon-sound { opacity: 1; }
    #video-trigger.active #btn-icon-mouse { opacity: 0; }
    #video-trigger.active #btn-icon-close { opacity: 1; }
    #video-trigger:hover .reel-icon { transform: translate(-50%, -50%) scale(1.08); }
    /* subtle scroll cue on the mouse wheel — the hero's single scroll indicator */
    #video-trigger #mouse-wheel { animation: reel-scroll 1.8s var(--ease-standard) infinite; }
    @keyframes reel-scroll { 0% { opacity: .2; transform: translateY(-2px); } 45% { opacity: 1; } 100% { opacity: .2; transform: translateY(3px); } }
    @media (prefers-reduced-motion: reduce) { #video-trigger #mouse-wheel { animation: none; } }
    #video-trigger:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 4px; border-radius: 50%; }
    @keyframes reel-rotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }

    .video-overlay {
        position: fixed;
        inset: 0;
        background: #000;
        display: flex;
        align-items: center;
        justify-content: center;
        z-index: 990; /* above the fixed site header (100) */
        opacity: 0;
        visibility: hidden;
        transition: opacity var(--dur-slow) var(--ease-standard), visibility var(--dur-slow) var(--ease-standard);
    }
    .video-overlay.active { opacity: 1; visibility: visible; }
    #fullscreen-video { width: 100%; height: 100%; object-fit: cover; }

    /* Sound control for the open reel. Top-left, on the page gutter, so it
       sits opposite the trigger — which is doubling as the close button in
       the other corner — rather than crowding it. Inside the overlay, so it
       is only reachable (and only tabbable) while the reel is open. */
    .reel-sound {
        position: absolute;
        top: var(--gutter);
        left: var(--gutter);
        z-index: 2;
        display: grid;
        place-items: center;
        width: 52px;
        height: 52px;
        padding: 0;
        border: 1px solid rgba(250, 250, 250, 0.28);
        border-radius: 50%;
        background: rgba(10, 1, 30, 0.42);
        backdrop-filter: blur(10px);
        -webkit-backdrop-filter: blur(10px);
        color: var(--gu-paper);
        cursor: pointer;
        -webkit-tap-highlight-color: transparent;
        transition: background var(--dur) var(--ease-standard),
                    border-color var(--dur) var(--ease-standard),
                    transform var(--dur) var(--ease-standard);
    }
    .reel-sound:hover {
        background: rgba(10, 1, 30, 0.62);
        border-color: rgba(250, 250, 250, 0.5);
        transform: scale(1.06);
    }
    .reel-sound:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 4px; }
    /* Both icons share one grid cell; the class on the button picks which
       shows, so there is no layout change and nothing to load on the swap. */
    .reel-sound svg { grid-area: 1 / 1; }
    .reel-sound .reel-sound-off { display: none; }
    .reel-sound.is-muted .reel-sound-on { display: none; }
    .reel-sound.is-muted .reel-sound-off { display: block; }
    @media (max-width: 600px) { .reel-sound { width: 44px; height: 44px; } }
    @media (prefers-reduced-motion: reduce) { .reel-sound { transition: none; } }

    @media (max-width: 600px) {
        #video-trigger { width: 5rem; right: 14px; bottom: 16px; }
    }
    @media (prefers-reduced-motion: reduce) {
        #video-trigger #text-group { animation: none; }
    }

    /* ============ Story ============ */
    .story-eyebrow {
        display: block;
        font-size: 18px;
        font-weight: 500;
        color: var(--text-on-light-muted);
        margin-bottom: var(--space-4);
    }
    .story-headline-wrap { position: relative; }
    .story-title {
        font-size: clamp(40px, 6.4vw, 80px);
        font-weight: 500;
        line-height: 0.95;
        letter-spacing: -0.015em;
        color: var(--gu-purple-600);
        /* em-based cap forces the Figma line break: "Building in the" / "big leagues" */
        max-width: 6.6em;
        margin-bottom: 0;
    }
    /* "+ 25 years of experience" sticker; overlaps the headline on desktop */
    .story-badge {
        display: inline-block;
        background: #A4FCF8;
        color: var(--gu-eggplant-800);
        font-weight: 700;
        font-size: 14px;
        letter-spacing: 0.01em;
        white-space: nowrap;
        padding: 12px 20px;
        border-radius: 14px;
        transform: rotate(7deg);
        margin-top: var(--space-4);
    }
    @media (min-width: 900px) {
        .story-badge { position: absolute; top: clamp(46px, 5vw, 70px); right: -10px; margin-top: 0; }
    }

    .story-copy-col { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-6); }

    .story-copy p { color: var(--text-on-light-muted); font-size: 18px; max-width: 27em; }
    .inline-link {
        color: var(--gu-purple-600);
        font-weight: 600;
        text-decoration: none;
        background-image: linear-gradient(var(--gu-lilac-300), var(--gu-lilac-300));
        background-repeat: no-repeat;
        background-size: 100% 2px;
        background-position: 0 100%;
        transition: background-size var(--dur) var(--ease-standard);
        padding-bottom: 2px;
    }
    .inline-link:hover { background-size: 100% 38%; }

    /* ---- Brand marquee ---- */
    .marquee-wrap { 
        margin-top: var(--space-8);

    }
    .marquee-label {
        font-size: var(--fs-label);
        letter-spacing: var(--ls-label);
        text-transform: uppercase;
        font-weight: 700;
        color: var(--text-on-light-muted);
        margin-bottom: var(--space-5);
        max-width: 100% !important;
    }
    .marquee {
        overflow: hidden;
        mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
        -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
    }
    .marquee-track {
        display: flex;
        align-items: center;
        width: max-content;
    }
    .marquee-wrap.is-in .marquee-track {
        animation: marquee 60s linear infinite;
    }
    .marquee:hover .marquee-track { animation-play-state: paused; }
    @keyframes marquee { to { transform: translateX(-50%); } }
    .marquee-strip {
        height: 56px;
        width: auto;
        max-width: none;
        /* Spacing lives on the strip (not the track gap) so translateX(-50%) loops seamlessly */
        margin-right: var(--space-8);
        filter: grayscale(1);
        opacity: 0.6;
    }

    /* ============ Our Story — light section, single carousel + copy (Figma redesign) ============
       One card track (stills + muted looping videos) scrolls vertically beside the copy
       on desktop and horizontally under it on mobile. Track content is duplicated in the
       HTML so the translate(-50%) loop repeats seamlessly (~4.2s per card). The card
       nearest the column centre is zoomed to full size/opacity by a JS position pass. */
    .story { overflow: hidden; }
    .story .story-inner {
        position: relative; z-index: 2;
        max-width: var(--container); margin: 0 auto;
        padding: 0 var(--gutter);
    }
    /* logo strip: centred under the copy, ink-dark like the Figma */
    .story .marquee-wrap { text-align: center; margin-top: var(--space-7); }
    .story .marquee-label { margin-bottom: var(--space-4); color: var(--gu-eggplant-800); opacity: 0.9; }
    .story .marquee-strip { height: 30px; margin-right: var(--space-7); filter: grayscale(1) brightness(0.4); opacity: 1; }
    /* CTA pill: Figma uses the mid purple with a deepening hover fill */
    .story .btn-primary { background: var(--gu-purple-600); font-size: var(--fs-body-sm); padding: 16px 28px; }
    .story .btn-primary::before { background: var(--gu-purple-900); }

    .story-gallery { position: relative; }
    .story-colwrap { position: relative; overflow: hidden; min-width: 0; }
    .story-track {
        display: flex;
        width: max-content;
        will-change: transform;
        backface-visibility: hidden;
        transform: translateZ(0);
        animation: 50s linear infinite paused;
        animation-name: story-x;
    }
    .story-card {
        display: block;
        width: clamp(240px, 68vw, 330px);
        /* height:auto beats the width/height attribute hints so aspect-ratio wins */
        height: auto;
        aspect-ratio: 509 / 468;
        /* own compositor layer: the JS focus pass tweens transform/opacity, and without
           this every write re-rasterises the (huge) moving track layer */
        will-change: transform, opacity;
        object-fit: cover;
        border-radius: clamp(24px, 3.2vw, 40px);
        background: var(--gu-lilac-100);
        margin: 0 var(--space-4) 0 0;
        flex-shrink: 0;
    }
    .story-gallery.is-in .story-track { animation-play-state: running; }
    /* translate3d keeps the animation on its own GPU layer -> no per-frame rasterisation */
    @keyframes story-x { from { transform: translate3d(0, 0, 0); } to { transform: translate3d(-50%, 0, 0); } }
    @keyframes story-y { from { transform: translate3d(0, 0, 0); } to { transform: translate3d(0, -50%, 0); } }
    /* fade the strip into the page at both ends via STATIC overlays (not a mask on the
       moving layer) so the scroll stays GPU-composited and glitch-free */
    .story-gallery::before, .story-gallery::after { content: ""; position: absolute; z-index: 3; pointer-events: none; }
    .story-gallery::before { top: 0; bottom: 0; left: 0; width: 10%; background: linear-gradient(to right, var(--surface-paper), transparent); }
    .story-gallery::after  { top: 0; bottom: 0; right: 0; width: 10%; background: linear-gradient(to left, var(--surface-paper), transparent); }

    @media (max-width: 899px) {
        /* full-bleed horizontal marquee between the CTA and the brand logos */
        .story-gallery { width: 100vw; margin-left: calc(50% - 50vw); margin-top: var(--space-7); }
        /* cards drift right, playing against the leftward logo marquee below */
        .story-track { animation-direction: reverse; }
        .story-title { font-size: clamp(42px, 13.5vw, 62px); }
        .story-badge { font-size: 11px; padding: 7px 12px; border-radius: 10px; margin-top: 12px; }
    }
    @media (min-width: 900px) {
        /* gallery bleeds top-to-bottom of the section on the LEFT; copy on the right
           with the brand marquee pinned under it */
        .story { padding-block: 0; }
        .story .story-inner {
            display: grid;
            /* The copy column is CAPPED, not flexible. It used to be the 1fr
               track, so when --container went from 1280 to 1540 it swallowed
               all 260px of the new width — and its contents don't grow with
               it (the headline caps at 528px, the paragraphs at 27em), so all
               that arrived was trailing whitespace. 700px is the width it had
               at the old container, which is the width it was drawn at.

               The gallery takes the surplus instead, but keeps its old track
               as a FLOOR: below ~1300 there is no surplus, and a plain
               `1fr` would let the cards collapse under the copy's 700. */
            grid-template-columns: minmax(clamp(340px, 39vw, 500px), 1fr) minmax(0, 700px);
            grid-template-rows: auto auto;
            grid-template-areas: "gallery copy" "gallery brands";
            column-gap: clamp(48px, 6vw, 120px);
            align-items: center;
            min-height: clamp(680px, 94vh, 900px);
        }
        /* symmetric 128px breathing around the whole copy+logos group; the brand
           marquee flows right under the copy instead of pinning to the section end */
        .story-copy-col { grid-area: copy; padding-block: var(--space-10) 0; }
        .story .marquee-wrap { grid-area: brands; align-self: start; margin-top: var(--space-7); padding-bottom: var(--space-10); }
        .story-gallery { grid-area: gallery; align-self: stretch; }
        .story-colwrap { position: absolute; inset: 0; }
        /* track is absolute so its (tall) content never inflates the column height —
           lets the gallery stretch to the section height without a circular dependency */
        .story-track {
            position: absolute; top: 0; left: 0; width: 100%;
            flex-direction: column;
            animation-name: story-y;
        }
        .story-card { width: 100%; margin: 0 0 20px 0; border-radius: 40px; }
        .story-gallery::before, .story-gallery::after { left: 0; right: 0; width: auto; height: 12%; }
        .story-gallery::before { top: 0; bottom: auto; background: linear-gradient(to bottom, var(--surface-paper), transparent); }
        .story-gallery::after  { bottom: 0; top: auto; background: linear-gradient(to top, var(--surface-paper), transparent); }
    }
    @media (prefers-reduced-motion: reduce) { .story-track { animation: none; } }

    /* ============ The turn (Good add Ventures) ============ */
    /* The 2026 design (style guide 1360:32253): the section speaks in the
       hero's voice — Inter Tight text in grey, a SemiBold headline — and
       the card-less lockup becomes the showcase's white GaV card link.
       The comp sets the pull-quote in plain Inter; Inter Tight stands in
       deliberately, so the page loads no extra family for one paragraph. */
    #why, #work, #section-contact { --turn-text: "Inter Tight", system-ui, -apple-system, "Segoe UI", sans-serif; }
    .turn-grid { display: grid; gap: var(--space-8); align-items: center; }
    /* 1024 rather than the page's usual 900: the card cannot shrink below
       ~306px (mascot + wordmark floors), and under 1024 a 0.78fr column
       is narrower than that, so the card bled into the gutter. */
    @media (min-width: 1024px) {
        /* copy on the left, the card on the right. 1.22/0.78 with the
           smaller gap gives the copy the design's 723px at 1440, which is
           what holds the 67px headline to its three-line break. */
        .turn-grid { grid-template-columns: minmax(0, 1.22fr) minmax(0, 0.78fr); gap: var(--space-8); }
        .gav { justify-self: center; }
    }
    .turn-copy .eyebrow {
        font-family: var(--turn-text);
        font-size: clamp(17px, 1.5vw, 21px);
        font-weight: 400;
        letter-spacing: 0;
        text-transform: none;
        color: var(--gu-gray-300);
        margin-bottom: var(--space-5);
    }
    /* 67px Red Hat Display SemiBold in the 1440 frame; the -2px tracking
       and 72.9px leading are kept as ratios so the clamp preserves them. */
    .turn-copy h2 {
        font-weight: 600;
        font-size: clamp(34px, 4.65vw, 67px);
        line-height: 1.09;
        letter-spacing: -0.03em;
        max-width: 10.9em;
        margin-bottom: clamp(24px, 2.8vw, 40px);
    }
    .turn-copy p {
        font-family: var(--turn-text);
        line-height: 1.6;
        color: var(--gu-gray-300);
        max-width: 60ch;
    }
    /* two classes, so it outranks .turn-copy p on color and max-width */
    .turn-copy .turn-focus {
        margin-top: clamp(24px, 2.8vw, 40px);
        padding-left: clamp(20px, 1.9vw, 28px);
        border-left: 3px solid var(--gu-lilac-200);
        font-weight: 500;
        font-size: clamp(20px, 1.9vw, 27px);
        line-height: 1.3;
        color: var(--gu-paper);
        max-width: 21em;
    }
    /* The GaV card, ported from the showcase build: the whole card is the
       link, tilted 5deg on a hard lilac shadow, settling level on hover. */
    .gav-card {
        position: relative;
        display: flex;
        align-items: center;
        gap: clamp(14px, 1.4vw, 20px);
        /* fit-content so the card hugs its lockup below the two-column
           break; 416px is the comp's card width */
        width: fit-content;
        max-width: min(416px, 100%);
        background: var(--gu-paper);
        color: var(--gv-ink);
        border-radius: 24px;
        /* the tall vertical padding is what gives the comp's 290px card
           its air — the lockup itself is only ~160px tall */
        padding: clamp(32px, 4.5vw, 64px) clamp(20px, 2.2vw, 32px);
        box-shadow: 16px 16px 0 var(--gu-lilac-400);
        rotate: 5deg;
        text-decoration: none;
        transition: rotate var(--dur-slow) var(--ease-out);
    }
    .gav-card:hover, .gav-card:focus-visible { rotate: 0.5deg; }
    /* past the 16px hard shadow, so the whole ring sits on the eggplant
       ground instead of vanishing lilac-on-lilac along two edges */
    .gav-card:focus-visible { outline-offset: 20px; }
    /* border and radius per the comp's chip export (2px #1C0447, r13);
       the arrow is the comp's drawn vector, not a text glyph — the DM Sans
       stack has no U+2192, and a fallback glyph's ink sits off the box
       centre, which is why a → here rendered small and adrift */
    .gav-chip {
        position: absolute;
        top: clamp(16px, 1.8vw, 26px);
        right: clamp(16px, 1.8vw, 26px);
        display: grid;
        place-items: center;
        width: clamp(44px, 4vw, 57px);
        height: clamp(41px, 3.75vw, 54px);
        border: 2px solid var(--gu-purple-900);
        border-radius: 13px;
    }
    /* the comp's arrow is 13.8px of a 57.5px chip — 24%, held at every size */
    .gav-chip .arrow { width: 24%; height: auto; }
    .gav-chip .arrow { transition: transform var(--dur) var(--ease-standard); }
    .gav-card:hover .arrow, .gav-card:focus-visible .arrow { transform: translateX(4px); }
    .gav-char { position: relative; flex: none; }
    .gav-character { width: clamp(96px, 9.3vw, 134px); height: auto; }
    /* the soft ground ellipse the design floats the mascot over */
    .gav-char::after {
        content: "";
        display: block;
        width: 46%;
        height: 6px;
        margin: 8px auto 0;
        border-radius: 50%;
        background: rgba(34, 29, 56, 0.14);
    }
    .gav-brand { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
    .gav-builtby {
        font-family: var(--font-display);
        font-weight: 600;
        font-size: clamp(18px, 1.6vw, 23px);
        line-height: 1;
    }
    .gav-wordmark { width: clamp(148px, 14vw, 200px); height: auto; }
    @media (max-width: 420px) {
        .gav-card { padding: 20px 18px; gap: 12px; }
        .gav-character { width: 84px; }
        .gav-wordmark { width: 132px; }
        .gav-chip { width: 36px; height: 34px; border-radius: 10px; top: 12px; right: 12px; }
    }

    /* ============ Who we work with ============ */
    /* The 2026 design (1357:28761 closed / 1357:28734 open): the section
       rests closed — three coloured dots with labels under the milestone
       line — and a hovered or focused card opens: its 447px circle grows
       out of the dot (same colour, so the dot melts into it) and the body
       copy fades in. The paragraph keeps its layout box when invisible, so
       nothing below the section ever shifts. */
    .milestone-line {
        font-family: var(--font-display);
        font-weight: 500;
        font-size: clamp(30px, 4vw, 58px);
        line-height: 1.15;
        color: var(--gu-purple-600);
        max-width: 19.6em;
        margin-bottom: clamp(28px, 3.3vw, 48px);
    }
    .milestone-line strong { font-weight: 800; }
    .who-grid { display: grid; gap: 10px; }
    @media (min-width: 900px) { .who-grid { grid-template-columns: repeat(3, 1fr); } }
    .who-card {
        position: relative;
        display: flex;
        gap: 24px;
        align-items: flex-start;
        /* sideways the circle stays inside its card (it would wash the
           neighbour's copy), but downward it bleeds through the section's
           bottom padding — the next section's own ground is what cuts it */
        overflow: clip visible;
        border-radius: 16px;
        /* the top padding is the air the circle rises into — the comp
           draws its top arc just above the title */
        --who-pad: clamp(40px, 4.4vw, 60px);
        padding: var(--who-pad) 20px 30px;
        min-height: clamp(250px, 22vw, 320px);
    }
    /* ONE element plays dot and circle: at rest it sits as the 52px dot
       beside the label; on hover it grows and travels into the 447px
       circle behind the copy. Until the first open it idles a "peek" —
       swelling 40% of the way and settling back — as a live preview of
       the hover. Retired via #fit.who-explored once a card is opened. */
    .who-card::before {
        content: "";
        position: absolute;
        z-index: 0;
        left: 20px;
        top: var(--who-pad, 48px);
        width: 52px;
        height: 52px;
        border-radius: 50%;
        background: var(--who-c, var(--gu-lilac-200));
        transition: left 0.6s var(--ease-out), top 0.6s var(--ease-out),
                    width 0.6s var(--ease-out), height 0.6s var(--ease-out);
    }
    /* The idle peek is DRIVEN BY TRANSITIONS, not keyframes: a script cycles
       .is-peeking across the cards, so a hover that lands mid-peek simply
       continues the same tween instead of snapping out of an animation.
       The journey is anchored in px (not the card's %) so the copy's reveal
       mask below can mirror the circle exactly. */
    .who-card.is-peeking::before {
        left: 12px;
        top: calc(var(--who-pad, 48px) * 0.6);
        width: 192px;
        height: 192px;
    }
    .who-card:hover::before,
    .who-card:focus-visible::before,
    .who-card:focus-within::before {
        left: 0;
        top: 0;
        width: 400px;
        height: 400px;
    }
    /* the flex dot is now only the spacer that holds the label's indent
       (the visible dot is the ::before above, at the same spot) */
    .who-dot {
        flex: none;
        width: 52px;
        height: 52px;
    }
    .who-copy { position: relative; z-index: 1; }
    .who-copy h3 {
        font-size: 22px;
        font-weight: 700;
        line-height: 1.3;
        color: var(--gu-eggplant-800);
        /* a single-line label sits optically centred against the 52px dot */
        padding-top: 11px;
        margin: 0 0 10px;
    }
    .who-copy p {
        font-family: var(--font-display);
        font-size: 17px;
        line-height: 1.5;
        color: #38333D;
        max-width: 270px;
        margin: 0;
    }
    /* Only the words the circle covers exist: the wrapper is masked by a
       circle that mirrors the ::before's journey exactly (its geometry
       re-based to this box, which sits 96px right and ~50px below the
       circle's card-space origin). closest-side keeps the mask a true
       circle at every size step. */
    .who-reveal {
        -webkit-mask-image: radial-gradient(circle closest-side, #000 99.5%, transparent 100%);
        mask-image: radial-gradient(circle closest-side, #000 99.5%, transparent 100%);
        -webkit-mask-repeat: no-repeat;
        mask-repeat: no-repeat;
        -webkit-mask-size: 52px 52px;
        mask-size: 52px 52px;
        -webkit-mask-position: -76px -50px;
        mask-position: -76px -50px;
        transition: -webkit-mask-size 0.6s var(--ease-out), -webkit-mask-position 0.6s var(--ease-out),
                    mask-size 0.6s var(--ease-out), mask-position 0.6s var(--ease-out);
    }
    .who-card.is-peeking .who-reveal {
        -webkit-mask-size: 192px 192px; mask-size: 192px 192px;
        -webkit-mask-position: -84px calc(-50px - 0.4 * var(--who-pad, 48px));
        mask-position: -84px calc(-50px - 0.4 * var(--who-pad, 48px));
    }
    .who-card:hover .who-reveal,
    .who-card:focus-visible .who-reveal,
    .who-card:focus-within .who-reveal {
        -webkit-mask-size: 400px 400px; mask-size: 400px 400px;
        -webkit-mask-position: -96px calc(-50px - var(--who-pad, 48px));
        mask-position: -96px calc(-50px - var(--who-pad, 48px));
    }
    /* no hover to speak of: touch stays open */
    @media (hover: none) {
        .who-card::before { left: 0; top: 0; width: 400px; height: 400px; }
        .who-reveal { -webkit-mask-image: none; mask-image: none; }
    }

    /* ============ Work (grid) ============ */
    /* The 2026 design (style guide 1538:22492 / 1692:16332): the carousel
       becomes a grid — one full-width card, then pairs — and each card
       raises its brand tile centred over the image on hover. Sizes are
       1:1 with the comp's 1540px column. */
    .work-header {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: clamp(20px, 2.8vw, 40px);
        /* the comp puts 128px between the headline block and the grid: 30px
           to the "See all work" link (58px tall, omitted until a work index
           page exists) plus the header's own 40px gap */
        margin-bottom: clamp(48px, 8.9vw, 128px);
    }
    .work-header .eyebrow {
        font-family: var(--turn-text);
        font-size: clamp(17px, 1.5vw, 21px);
        font-weight: 500;
        letter-spacing: 0;
        text-transform: none;
        color: var(--gu-gray-300);
        margin: 0;
    }
    /* 144px Red Hat Display SemiBold in the comp — tracking (-4px) and
       leading (132.5px) held as ratios so the clamp preserves them */
    .work-header h1,
    .work-header h2 {
        font-weight: 600;
        font-size: clamp(56px, 10vw, 144px);
        line-height: 0.92;
        letter-spacing: -0.028em;
        text-align: center;
        margin: 0;
    }
    /* the comp's "See all work" chip, right-aligned under the headline */
    .work-seeall {
        align-self: flex-end;
        display: inline-flex;
        align-items: center;
        gap: clamp(16px, 2vw, 30px);
        padding: 17px 24px;
        border: 1px solid var(--gu-paper);
        border-radius: 14px;
        font-family: var(--turn-text);
        font-weight: 500;
        font-size: 15.75px;
        color: var(--gu-paper);
        text-decoration: none;
        transition: background var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
    }
    .work-seeall:hover { background: var(--gu-paper); color: var(--gu-purple-900); }
    .work-seeall .arrow { transition: transform var(--dur) var(--ease-standard); }
    .work-seeall:hover .arrow { transform: translateX(4px); }
    .work-grid {
        display: grid;
        gap: clamp(56px, 11.5vw, 176px) 21px;
    }
    @media (min-width: 900px) {
        .work-grid { grid-template-columns: 1fr 1fr; }
        .work-grid > .work-card:first-of-type { grid-column: 1 / -1; }
    }
    .work-card {
        display: block;
        text-decoration: none;
        color: var(--gu-paper);
    }
    /* display:block above would defeat the hidden attribute's UA rule,
       and /work/'s filter relies on it */
    .work-card[hidden] { display: none; }
    .work-card figure {
        position: relative;
        margin: 0;
        overflow: clip;
        aspect-ratio: 760 / 560;
        /* The two comps are the two STATES of the card: at rest it sits on
           the default frame's 24px rounding (which is also what the exported
           artwork bakes into its corners), and on hover it swells to the
           hover frame's 140px as the brand tile fades in. */
        border-radius: 24px;
        background: var(--gu-purple-700);
        transition: border-radius var(--dur-slow) var(--ease-out);
    }
    .work-card:hover figure,
    .work-card:focus-visible figure {
        /* 140px at the comp's 760px pair card, scaling with the column */
        border-radius: clamp(28px, 9.2vw, 140px);
    }
    /* the lead card keeps its wide crop, and the hover comp draws it with a
       shallower rounding (~64px) than the pairs' 140px */
    .work-grid > .work-card:first-of-type figure {
        aspect-ratio: 1540 / 630;
    }
    .work-grid > .work-card:first-of-type:hover figure,
    .work-grid > .work-card:first-of-type:focus-visible figure {
        border-radius: clamp(24px, 4.2vw, 64px);
    }
    .work-card__img {
        width: 100%;
        height: 100%;
        object-fit: cover;
    }
    /* The brand tile: an app-icon square that fades in centred over the
       image on hover and keyboard focus. 356px in the comp — 47% of a pair
       card, the same cap the lead card's 23% resolves to at full width. */
    .work-card__tile {
        position: absolute;
        inset: 0;
        margin: auto;
        width: min(47%, 356px);
        height: auto;
        opacity: 0;
        filter: drop-shadow(0 22px 44px rgba(0, 0, 0, 0.25));
        transition: opacity var(--dur-slow) var(--ease-out);
        pointer-events: none;
    }
    .work-card:hover .work-card__tile,
    .work-card:focus-visible .work-card__tile {
        opacity: 1;
    }
    /* the lead card's figure is only 41% as tall as it is wide, so the
       pairs' 47% tile would clip against it on narrower screens */
    .work-grid > .work-card:first-of-type .work-card__tile {
        width: min(32%, 356px);
    }
    .work-card__body {
        max-width: 760px;
        padding-top: 28px;
    }
    .work-card__name {
        font-family: var(--turn-text);
        font-size: 17.5px;
        font-weight: 400;
        line-height: 1;
        color: var(--gu-gray-300);
        margin: 0;
    }
    .work-card__tagline {
        font-family: var(--turn-text);
        font-size: 22.75px;
        font-weight: 500;
        line-height: 1.3;
        color: var(--gu-paper);
        margin: 2px 0 0;
    }
    .work-card__desc {
        font-family: var(--turn-text);
        font-size: 17.5px;
        line-height: 1.4;
        color: var(--gu-gray-300);
        margin: 17.5px 0 0;
        max-width: none;
    }

    /* ── SDG Badge pills ────────────────────────────── */
    .sdg-badges {
        display: flex;
        flex-wrap: wrap;
        gap: 10.5px;
        margin-top: 17.5px;
    }
    .sdg-badge {
        display: inline-flex;
        align-items: center;
        font-family: var(--turn-text);
        font-size: 12.25px;
        font-weight: 400;
        line-height: 1.3;
        text-transform: uppercase;
        padding: 4.5px 13.25px;
        border: 1px solid var(--gu-paper);
        border-radius: 8.75px;
        color: var(--gu-gray-300);
        cursor: default;
        user-select: none;
    }
    .sdg-tooltip {
        position: absolute;
        z-index: 9999;
        background: var(--gu-purple-950);
        color: var(--gu-paper);
        padding: 14px 18px;
        border-radius: 6px;
        pointer-events: none;
        white-space: nowrap;
        box-shadow: 0 4px 16px rgba(0,0,0,0.4);
        letter-spacing: 0;
        text-transform: none;
        display: flex;
        flex-direction: column;
        gap: 2px;
    }
    .sdg-tip-l1 { font-size: 10px; font-weight: 500; opacity: 0.6; }
    .sdg-tip-l2 { font-size: 11px; font-weight: 600; opacity: 0.85; }
    .sdg-tip-l3 { font-size: 13px; font-weight: 700; }

    /* ============ Let's talk (2026 comp, sans form) ============
       The comp pairs a form card with the big "Let's talk." column on a
       violet gradient; the form waits for the qualifying flow, so for now
       the column speaks alone and a mailto CTA stands in. */
    #section-contact {
        color: var(--gu-paper);
        text-align: center;
        /* the comp's violet wash, mirrored: its copy column sits on the dark
           side and the lilac blooms on the formless one — here the copy is on
           the left, so the bloom moves right and the text keeps its contrast */
        background:
            radial-gradient(90% 90% at 20% -10%, rgba(14, 1, 36, 0.92), transparent 62%),
            radial-gradient(110% 90% at 100% 115%, var(--gu-lilac-200), transparent 58%),
            radial-gradient(70% 70% at 0% 105%, rgba(208, 193, 255, 0.28), transparent 60%),
            linear-gradient(210deg, #4A2A6B 10%, #2A1747 55%, #4A2A6B 100%);
    }
    .contact-title {
        font-weight: 600;
        font-size: clamp(64px, 10vw, 144px);
        line-height: 0.92;
        letter-spacing: -0.014em;
        color: var(--gu-paper);
        margin-bottom: clamp(32px, 4.2vw, 60px);
    }
    .contact-lede {
        margin-inline: auto;
        font-family: var(--turn-text);
        font-weight: 500;
        font-size: clamp(22px, 2.1vw, 30px);
        line-height: 1.2;
        color: var(--gu-paper);
        max-width: 17em;
        margin-bottom: var(--space-6);
    }
    /* the hero's white-pill CTA voice, standing where the comp's form sat */
    .contact-cta {
        position: relative;
        overflow: hidden;
        isolation: isolate;
        display: inline-flex;
        align-items: center;
        gap: 16px;
        padding: 18px 34px;
        border-radius: var(--radius-pill);
        background: var(--gu-paper);
        color: var(--gu-purple-600);
        font-family: var(--font-body);
        font-size: var(--fs-body);
        font-weight: 600;
        text-decoration: none;
        transition: color var(--dur) var(--ease-out), transform var(--dur) var(--ease-standard);
    }
    /* the .btn fill-sweep, in this pill's colours */
    .contact-cta::before {
        content: "";
        position: absolute;
        inset: 0;
        z-index: -1;
        border-radius: inherit;
        background: var(--gu-purple-600);
        transform: translateY(101%);
        transition: transform var(--dur-slow) var(--ease-out);
    }
    .contact-cta:hover, .contact-cta:focus-visible { color: var(--gu-paper); transform: translateY(-1px); }
    .contact-cta:hover::before, .contact-cta:focus-visible::before { transform: translateY(0); }
    .contact-cta .arrow { transition: transform var(--dur) var(--ease-standard); }
    .contact-cta:hover .arrow { transform: translateX(4px); }
    .contact-rule {
        border: 0;
        height: 1px;
        background: var(--gu-paper);
        opacity: 0.1;
        max-width: 758px;
        margin: clamp(40px, 4.2vw, 60px) auto;
    }
    .contact-points {
        display: grid;
        gap: var(--space-6);
        max-width: 758px;
        margin-inline: auto;
        justify-items: center;
    }
    @media (min-width: 700px) { .contact-points { grid-template-columns: 1fr 1fr; gap: var(--space-5); } }
    .contact-point h3 {
        display: flex;
        justify-content: center;
        align-items: center;
        gap: 16px;
        font-family: var(--turn-text);
        font-weight: 600;
        font-size: 18px;
        letter-spacing: 0;
        color: var(--gu-paper);
        margin-bottom: 14px;
    }
    .contact-point h3 img { flex: none; }
    .contact-point p {
        font-family: var(--turn-text);
        font-weight: 500;
        font-size: 15px;
        line-height: 1.4;
        color: rgba(250, 250, 250, 0.78);
        max-width: 280px;
        margin: 0 auto;
    }

    /* ============ Footer ============ */
    footer { padding: var(--space-7) 0 0; font-size: var(--fs-body-sm); color: var(--text-on-dark-muted); }
    footer .container { display: flex; flex-direction: column; align-items: center; gap: var(--space-4); text-align: center; }
    footer a { color: var(--gu-lilac-200); text-decoration: none; }
    footer a:hover { text-decoration: underline; }
    /* The showcase's wordmark band, closing the page. The track holds FOUR
       identical copies, so travelling exactly one quarter of its width lands
       the second copy where the first began and the loop is seamless. */
    .ft-band { overflow: clip; padding: var(--space-6) 0 var(--space-5); }
    .ft-band[data-idle] .ft-track { animation-play-state: paused; }
    .ft-track {
        display: flex;
        width: max-content;
        animation: band-x 30s linear infinite;
        font-family: var(--font-display);
        font-size: clamp(72px, 11vw, 180px);
        font-weight: 900;
        line-height: 1;
        letter-spacing: -0.02em;
        color: transparent;
        -webkit-text-stroke: 2px var(--gu-purple-500);
        white-space: nowrap;
    }
    @keyframes band-x { to { transform: translate3d(-25%, 0, 0); } }

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

    /* ============ Scroll reveal ============ */
    /* ============ Curtain (loading screen) ============
       Ported from the showcase build: brand lockup + outlined count over the
       darkest brand ground, then the pixel cells sweep away bottom-up. */
    /* a head script stamps html.curtain-seen from sessionStorage before
       first paint, so repeat in-session visits never flash the dark slab */
    .curtain-seen .curtain { display: none; }
    /* the page must not scroll invisibly beneath the curtain */
    .curtain-lock { overflow: hidden; }
    .curtain {
        position: fixed;
        inset: 0;
        /* above the consent banner (2147483000) for its three seconds — the
           banner is fully interactive the moment the cells sweep away */
        z-index: 2147483200;
        background: var(--gu-purple-950);
        display: grid;
        place-items: end start;
    }
    /* Once the pixel exit starts, the slab is the cells, not the container. */
    .curtain.done { background: transparent; pointer-events: none; }
    .curtain-cells { position: absolute; inset: 0; }
    .curtain-brand {
        position: absolute;
        top: 50%;
        left: 50%;
        translate: -50% -50%;
        display: flex;
        align-items: center;
        gap: clamp(22px, 3.4vw, 48px);
        z-index: 2;
        white-space: nowrap;
    }
    /* the swirl, wearing the showcase's iridescent wash (static angle — the
       conic spin needs a registered property and three seconds don't earn it) */
    .curtain-mark {
        flex: none;
        width: clamp(120px, 15vw, 210px);
        aspect-ratio: 193 / 185;
        -webkit-mask: url("/images/hero-swirl.svg") no-repeat center / contain;
        mask: url("/images/hero-swirl.svg") no-repeat center / contain;
        background:
            radial-gradient(52% 44% at 24% 26%, rgba(214, 107, 213, 0.85), transparent 66%),
            radial-gradient(46% 42% at 78% 22%, rgba(124, 200, 255, 0.75), transparent 64%),
            radial-gradient(56% 50% at 72% 78%, rgba(116, 224, 184, 0.7), transparent 66%),
            radial-gradient(60% 56% at 26% 76%, rgba(180, 155, 240, 0.9), transparent 68%),
            conic-gradient(from 40deg at 50% 50%,
                #B49BF0, #D66BD5, #7CC8FF, #74E0B8, #D0C1FF, #B49BF0);
        background-size: 130% 130%, 130% 130%, 130% 130%, 130% 130%, 100% 100%;
        animation: curtain-drift 11s ease-in-out infinite alternate;
    }
    @keyframes curtain-drift {
        from { background-position: 0% 0%, 100% 0%, 100% 100%, 0% 100%, 50% 50%; }
        to   { background-position: 30% 20%, 70% 30%, 70% 70%, 30% 80%, 50% 50%; }
    }
    .curtain-inner {
        position: relative;
        z-index: 2;
        display: flex;
        width: 100%;
        align-items: flex-end;
        justify-content: flex-end;
        padding: var(--gutter);
    }
    .curtain-inner, .curtain-brand { transition: opacity 160ms linear; }
    .curtain.done .curtain-inner, .curtain.done .curtain-brand { opacity: 0; }
    .curtain-left { display: grid; gap: 12px; }
    .curtain-word {
        font-family: var(--font-display);
        font-size: clamp(38px, 6.2vw, 92px);
        font-weight: 900;
        letter-spacing: -0.02em;
        color: var(--gu-lilac-200);
    }
    .curtain-reg { font-size: 0.4em; vertical-align: super; }
    .curtain-tag {
        font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
        font-size: 12px;
        letter-spacing: 0.28em;
        text-transform: uppercase;
        color: var(--gu-lilac-400);
    }
    .curtain-count {
        font-family: var(--font-display);
        font-size: clamp(64px, 12vw, 190px);
        font-weight: 900;
        line-height: 0.8;
        color: transparent;
        -webkit-text-stroke: 2px var(--gu-lilac-400);
        font-variant-numeric: tabular-nums;
    }
    @media (max-width: 640px) {
        .curtain-brand { flex-direction: column; text-align: center; white-space: normal; }
        .curtain-left { justify-items: center; }
    }
    /* The pixel grid: chunky ink cells stamped with --pd, a bottom-weighted
       random delay. steps(1) keeps every pop abrupt — that IS the pixel. */
    /* The listing runs a tighter vertical rhythm than the homepage
       section: its header is a page masthead, not a scroll destination,
       so on a MacBook-class viewport the cards arrive without a fold of
       empty air. */
    #design-work #work { padding: clamp(104px, 9vw, 130px) 0 var(--space-10); }
    #design-work .work-header { gap: clamp(12px, 1.6vw, 24px); margin-bottom: clamp(28px, 3.5vw, 52px); }
    #design-work .work-toolbar { margin-bottom: clamp(28px, 3.5vw, 52px); }
    #design-work .work-grid { row-gap: clamp(48px, 8vw, 112px); }

    /* the hero's aurora orbs resting behind the listing — the bubbles,
       not the particles: the hero ground's colours, dialled down so the
       cards keep their contrast on the eggplant */
    #design-work #work {
        background:
            radial-gradient(circle 55vmin at 84% 3%, rgba(180, 155, 240, 0.18), transparent 70%),
            radial-gradient(circle 42vmin at 8% 10%, rgba(214, 107, 213, 0.10), transparent 70%),
            radial-gradient(circle 38vmin at 94% 34%, rgba(124, 200, 255, 0.08), transparent 70%),
            radial-gradient(circle 34vmin at 4% 52%, rgba(116, 224, 184, 0.07), transparent 70%),
            radial-gradient(circle 48vmin at 88% 78%, rgba(180, 155, 240, 0.10), transparent 70%),
            var(--surface-eggplant);
    }

    /* the listing page runs a uniform grid: its order changes with sort and
       filter, and the wide lead treatment belongs to the homepage's curated
       first card, not to whichever card a sort puts first */
    #design-work .work-grid > .work-card:first-of-type { grid-column: auto; }
    #design-work .work-grid > .work-card:first-of-type figure {
        aspect-ratio: 760 / 560;
        border-radius: 24px;
    }
    #design-work .work-grid > .work-card:first-of-type:hover figure,
    #design-work .work-grid > .work-card:first-of-type:focus-visible figure {
        border-radius: clamp(28px, 9.2vw, 140px);
    }
    #design-work .work-grid > .work-card:first-of-type .work-card__tile { width: min(47%, 356px); }

    .px-cell {
        position: absolute;
        background: var(--gu-purple-950);
        opacity: 0;
    }
    .px-show .px-cell { opacity: 1; }
    .px-fall .px-cell {
        animation: px-off 1ms steps(1, end) both;
        animation-delay: var(--pd, 0ms);
    }
    @keyframes px-off { from { opacity: 1; } to { opacity: 0; } }

    /* ============ Work toolbar (/work/ listing) ============
       The comp's filter pills and sort chip (style guide 1695:19774). */
    .work-toolbar {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: var(--space-5) var(--space-6);
        flex-wrap: wrap;
        margin-bottom: clamp(40px, 5vw, 70px);
    }
    .work-filter { display: flex; align-items: center; gap: 24px; flex-wrap: wrap; }
    .work-toolbar__label {
        font-family: var(--turn-text);
        font-weight: 500;
        font-size: 18px;
        color: var(--gu-gray-300);
        opacity: 0.7;
        white-space: nowrap;
    }
    .work-filter__pills { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
    .filter-pill {
        position: relative;
        overflow: hidden;
        isolation: isolate;
        appearance: none;
        background: transparent;
        border: 1.6px solid var(--gu-paper);
        border-radius: 24px;
        padding: 6.4px 18.4px;
        font-family: var(--turn-text);
        font-weight: 400;
        font-size: 16px;
        line-height: 20px;
        text-transform: uppercase;
        color: var(--gu-gray-300);
        cursor: pointer;
        transition: color var(--dur) var(--ease-out);
    }
    .filter-pill::before {
        content: "";
        position: absolute;
        inset: 0;
        z-index: -1;
        border-radius: inherit;
        background: var(--gu-paper);
        transform: translateY(101%);
        transition: transform var(--dur-slow) var(--ease-out);
    }
    .filter-pill:hover, .filter-pill.is-active { color: var(--gu-eggplant-800); }
    .filter-pill:hover::before, .filter-pill.is-active::before { transform: translateY(0); }
    .filter-pill.is-active { font-weight: 600; }
    .work-sort { display: flex; align-items: center; gap: 16px; }
    .sort-menu { position: relative; }
    .sort-pill {
        appearance: none;
        display: inline-flex;
        align-items: center;
        gap: 12px;
        background: transparent;
        border: 1.6px solid var(--gu-paper);
        border-radius: 24px;
        padding: 6.4px 20px;
        font-family: var(--turn-text);
        font-weight: 500;
        font-size: 16px;
        line-height: 20px;
        text-transform: uppercase;
        color: var(--gu-paper);
        cursor: pointer;
        transition: background var(--dur) var(--ease-out);
    }
    .sort-pill:hover { background: rgba(250, 250, 250, 0.1); }
    .sort-chevron {
        width: 14px;
        height: 14px;
        background: currentColor;
        -webkit-mask: url("/images/chevron-down.svg") no-repeat center / contain;
        mask: url("/images/chevron-down.svg") no-repeat center / contain;
        transition: transform var(--dur) var(--ease-out);
    }
    .sort-pill[aria-expanded="true"] .sort-chevron { transform: rotate(180deg); }
    .sort-list {
        position: absolute;
        z-index: 20;
        top: calc(100% + 8px);
        right: 0;
        min-width: 100%;
        margin: 0;
        padding: 8px;
        list-style: none;
        background: var(--gu-purple-900);
        border: 1px solid rgba(208, 193, 255, 0.25);
        border-radius: 16px;
        box-shadow: 0 16px 40px rgba(12, 2, 33, 0.45);
    }
    .sort-option {
        padding: 9px 14px;
        border-radius: 10px;
        font-family: var(--turn-text);
        font-weight: 500;
        font-size: 15px;
        text-transform: uppercase;
        color: var(--gu-gray-300);
        white-space: nowrap;
        cursor: pointer;
    }
    .sort-option:hover, .sort-option:focus-visible {
        background: var(--gu-lilac-200);
        color: var(--gu-purple-900);
        outline: none;
    }
    .sort-option[aria-selected="true"] { color: var(--gu-paper); font-weight: 600; }
    .sort-option[aria-selected="true"]:hover, .sort-option[aria-selected="true"]:focus-visible { color: var(--gu-purple-900); }

    /* ---- filter change: a breath out, then the new listing pops in ---- */
    .work-grid.is-filtering .work-card {
        opacity: 0;
        transform: translateY(14px);
        transition-duration: 0.22s;
        transition-delay: 0ms;
    }

    .reveal {
        opacity: 0;
        transform: translateY(28px);
        transition: opacity 0.7s var(--ease-out), transform 0.7s var(--ease-out);
        transition-delay: var(--reveal-delay, 0ms);
    }
    .reveal.is-in { opacity: 1; transform: none; }
    /* A keyboard user can tab to a link before its reveal fires; focus on an
       invisible element is a trap, so focus short-circuits the reveal. */
    .reveal:focus-within { opacity: 1; transform: none; transition: none; }

    /* ============ Reduced motion ============ */
    @media (prefers-reduced-motion: reduce) {
        html { scroll-behavior: auto; }
        .gav-card, .gav-chip .arrow { transition: none; }
        .work-card__tile, .work-card figure { transition: none; }
        .curtain { display: none; }
        .contact-cta, .contact-cta::before, .filter-pill::before, .sort-chevron { transition: none; }
        .who-card::before { transition: none; left: 0; top: 0; width: 400px; height: 400px; }
        .who-reveal { transition: none; -webkit-mask-image: none; mask-image: none; }
        .who-hint { display: none; }
        .curtain-mark { animation: none; }
        .ft-track { animation: none; }
        /* Fully stop the marquee under reduced motion. Uses the same specificity
           as the .marquee-wrap.is-in base rule so it actually wins. */
        .marquee-wrap.is-in .marquee-track { animation: none; }
        .reveal { opacity: 1; transform: none; transition: none; }
        .site-header { transition: none; }
        .btn::before { transition: none; }
        /* The script already bails on reduced motion, so cur-on is never set
           and the native pointer is never hidden. This is the belt to that
           brace, in case the class is ever set some other way. */
        html.act-stack .hero { position: relative !important; top: auto !important; }
        .cur-dot, .cur-ring { display: none; }
        html.cur-on, html.cur-on a, html.cur-on button,
        html.cur-on [data-cursor], html.cur-on #video-trigger { cursor: auto; }
    }
