

/* ----------------------------------------------------------------*/
:root {
    /* palette — exactly as specified */
    --cream: #F5EADF;
    --ink: #062D35;
    --accent: #FF4938;
    --btn-primary: #012529;
    --btn-secondary: #A8C202;
    --paper: #FFFFFF;

    /* type — 160 / 100 / 30 px at the 1920 reference */
    --fs-display: clamp(3.25rem, 8.3333vw, 160px);
    --fs-accent: clamp(2.05rem, 5.2083vw, 100px);
    --fs-lede: clamp(1rem, 1.5625vw, 30px);
    --fs-ui: clamp(0.6875rem, 0.6771vw, 13px);
    --fs-panel: clamp(0.6875rem, 0.7292vw, 14px);
    --fs-panel-name: clamp(0.95rem, 1.0417vw, 20px);
    --fs-index: clamp(1.5rem, 1.8229vw, 35px);

    /* controls — 225.85 × 64.28 px */
    --btn-w: clamp(170px, 11.7630vw, 225.85px);
    --btn-h: clamp(50px, 3.3479vw, 64.28px);

    /* rhythm */
    --edge: clamp(1.25rem, 4.1667vw, 80px);
    --lh-display: 0.9;

    /* motion */
    --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
    --dur-ui: 0.28s;
    --dur-fast: 0.2s;
}

/* ------------------------------------------------------------------ BASE */
*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    background: var(--cream);
    color: var(--ink);
    font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
    font-size: 16px;
    line-height: 1.5;
    overflow-x: clip;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

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

a {
    color: inherit;
    text-decoration: none;
}

button {
    font: inherit;
    color: inherit;
    background: none;
    border: 0;
    cursor: pointer;
}

h1,
p {
    margin: 0;
}

ul {
    margin: 0;
    padding: 0;
    list-style: none;
}

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

:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 4px;
}

.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

.skip-link {
    position: absolute;
    top: -100px;
    left: var(--edge);
    z-index: 60;
    padding: 12px 20px;
    background: var(--ink);
    color: var(--cream);
    font-size: 13px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    transition: top var(--dur-ui) var(--ease-out);
}

.skip-link:focus-visible {
    top: 16px;
}

/* ------------------------------------------------------------------ HERO */
.hero {
    position: relative;
    min-height: 100svh;
    padding: clamp(1.5rem, 2.6042vw, 50px) var(--edge) clamp(2rem, 3.5vw, 68px);
    overflow: hidden;
    background: var(--cream);
    isolation: isolate;
}

/* ------------------------------------------------------------- ARTWORK */
.hero__art {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
}

/* Transform carriers. Each nests inside the last so scroll parallax, pointer
   parallax, the intro tween and the fabric keyframes never share a matrix. */
.layer {
    position: absolute;
    inset: 0;
    will-change: transform;
}

.flow-layer {
    position: relative;
    z-index: 2;
    will-change: transform;
}

/* The geometry plate is square (887×888) with transparent margins baked in —
   8.12% right, 5.74% bottom, 4.62% top. Oversizing to 110% and nudging right
   by exactly that margin puts the orange plane hard against the viewport's
   right and bottom edges, with the teal arc's top still visible. */
.hero__geometry {
    position: absolute;
    top: 0;
    right: 0;
    width: auto;
    /* The 0.7 parallax lifts this plate by |0.7 - 1| x 0.55 x 100vh = 16.5vh
       over the hero's scroll range, which used to expose a strip of the cream
       background along the bottom edge. The extra height is the travel plus a
       margin, so the plate always outruns its own movement. */
    height: calc(110% + 22vh);
    max-width: none;
    /* The Y is the file's own 4.617% transparent TOP margin, measured off the
       alpha channel of hero-bg.webp (opaque box 140,41 - 815,837 in 887 x 888).
       Without it the plate's artwork began 4.617% of its rendered height below
       the viewport, which is the cream band that used to sit above the whole
       composition — the hero looked like it started 60px down the page. The X
       is the same correction for the 8.117% right margin, unchanged.
       The bottom still has room: the height carries 22vh of slack and the 0.7
       parallax only lifts this plate by 16.5vh over the hero's scroll range. */
    transform: translate3d(8.12%, -4.617%, 0);
}

[data-geometry] {
    transform-origin: 100% 50%;
    will-change: transform, opacity;
}

.hero__model-wrap {
    position: absolute;
    right: 16%;
    bottom: 0;
    height: 91%;
    aspect-ratio: 765 / 760;
    will-change: transform, opacity;
}

.hero__model {
    width: 100%;
    height: 100%;
    object-fit: contain;
    object-position: bottom center;
        /* animation: fabricBreathe 8s cubic-bezier(0.37, 0.5, 1.0, 1) infinite; */
}

/* Fabric layer: the same PNG, clipped to the sculptural bow (measured at
   x 3.4–37.6%, y 62.5–99.7% of the source) and feathered so the duplicate
   dissolves into the model underneath while it breathes. */
.hero__fabric {
    position: absolute;
    inset: 0;
    background-image: url("../image/heroimg.webp");
    background-repeat: no-repeat;
    background-position: bottom center;
    background-size: 100% 100%;
    /* No clip-path. The mask below already limits this duplicate to the bow,
       and it does so with a soft falloff — a clip-path cut at 40% while the
       mask was still ~50% opaque there is what drew a hard vertical seam
       across the model's chest. */
    -webkit-mask-image: radial-gradient(30% 26% at 20% 82%, #000 40%, transparent 85%);
    mask-image: radial-gradient(30% 26% at 20% 82%, #000 40%, transparent 85%);
    transform-origin: 26% 88%;
    will-change: transform;
}

/* --------------------------------------------------------------- TOP BAR */
.hero__bar {
    position: relative;
    z-index: 3;
    display: flex;
    align-items: baseline;
    gap: clamp(1rem, 2.0833vw, 40px);
}

.hero__index {
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-size: var(--fs-index);
    font-weight: 400;
    line-height: 1;
    letter-spacing: 0.01em;
    color: var(--ink);
    opacity: 0.85;
}

.hero__brand {
    font-size: 32px;
    font-weight: 900;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink);
        max-width: 130px;
}

.topbar__brand{
  max-width:80px;  
}

/* ------------------------------------------------------------------- NAV */
.hero__nav {
    position: absolute;
    top: clamp(3.5rem, 5.7292vw, 110px);
    right: clamp(3.5rem, 6.7708vw, 130px);
    z-index: 4;
}

.hero__nav ul {
    display: grid;
    gap: clamp(0.45rem, 0.5729vw, 11px);
    justify-items: end;
}

.hero__nav-link {
    position: relative;
    display: inline-block;
    font-size: 15px;
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--paper);
    transition: opacity var(--dur-ui) var(--ease-out);
    opacity: 0.82;
}

.hero__nav-link::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: -3px;
    width: 100%;
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: 100% 50%;
    transition: transform 0.45s var(--ease-out);
}

.hero__nav-link:hover,
.hero__nav-link:focus-visible,
.hero__nav-link.is-active {
    opacity: 1;
}

.hero__nav-link:hover::after,
.hero__nav-link:focus-visible::after,
.hero__nav-link.is-active::after {
    transform: scaleX(1);
    transform-origin: 0 50%;
}

/* ---------------------------------------------------------- MODEL PANEL */
.hero__panel {
    position: absolute;
    top: clamp(11rem, 20.8333vw, 400px);
    right: clamp(3.5rem, 6.7708vw, 130px);
    z-index: 4;
    display: grid;
    gap: clamp(0.35rem, 0.4167vw, 8px);
    justify-items: end;
    text-align: right;
    color: var(--paper);
}

.hero__panel-label {
    font-size: var(--fs-panel);
    font-weight: 400;
    letter-spacing: 0.08em;
    opacity: 0.85;
}

.hero__panel-name {
    font-size: var(--fs-panel-name);
    font-weight: 500;
    letter-spacing: 0.06em;
}

.hero__panel-meta {
    position: relative;
    margin-top: clamp(0.4rem, 0.5208vw, 10px);
    padding-top: clamp(0.4rem, 0.5208vw, 10px);
    font-size: 15px;
    font-weight: 400;
    letter-spacing: 0.16em;
}

.hero__panel-meta::before {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    width: clamp(90px, 7.8125vw, 150px);
    height: 1px;
    background: rgba(255, 255, 255, 0.4);
}

/* The hairline running down the right edge of the composition. */
.hero__panel-rule {
    position: absolute;
    top: clamp(-17rem, -23vw, -100px);
    right: clamp(-1.75rem, -3.3854vw, -30px);
    width: 1px;
    height: clamp(16rem, 36vw, 480px);
    background: rgba(255, 255, 255, 0.35);
    transform: scaleY(0);
    transform-origin: 50% 0;
}

/* ------------------------------------------------------------------ COPY */
.hero__copy {
    max-width: clamp(20rem, 46vw, 900px);
    margin-top: clamp(2rem, 4.6875vw, 45px);
}

.hero__title {
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-display);
    line-height: var(--lh-display);
    letter-spacing: 0.005em;
    color: var(--ink);
    text-transform: uppercase;
}
.ac_we{
   font-size: var(--fs-accent); 
   vertical-align: top;
}
/* Each visual line is its own mask so characters rise out of nothing. */
.hero__line {
    display: block;
    overflow: hidden;
    padding-bottom: 0.04em;
}

.hero__line:first-child {
    font-size: var(--fs-accent);
}

.hero__accent {
    color: var(--accent);
}

.hero__rule {
    display: block;
    width: clamp(120px, 13.0208vw, 250px);
    height: 1px;
    margin: clamp(1.5rem, 2.6042vw, 20px) 0 clamp(1.25rem, 2.0833vw, 40px);
    background: rgba(6, 45, 53, 0.45);
    transform: scaleX(0);
    transform-origin: 0 50%;
    margin-top: 90px;
}

.hero__lede {
    /* max-width: clamp(18rem, 27vw, 520px); */
    max-width: 422px;
    font-size: var(--fs-panel-name);
    font-weight: 400;
    line-height: 1.5;
    color: var(--ink);
}

.hero__actions {
    display: flex;
    flex-wrap: wrap;
    gap: clamp(0.75rem, 1.7188vw, 33px);
    margin-top: clamp(1.75rem, 3.3854vw, 65px);
}

/* --------------------------------------------------------------- BUTTONS */
.btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--btn-w);
    height: var(--btn-h);
    font-family: "Inter", sans-serif;
    font-size: var(--fs-ui);
    font-weight: 400;
    letter-spacing: 1px;
    text-transform: uppercase;
    text-align: center;
    border-radius: 0;
    will-change: transform;
    transition:
        transform var(--dur-ui) var(--ease-out),
        box-shadow var(--dur-ui) var(--ease-out),
        background-color var(--dur-ui) var(--ease-out);
}

.btn--primary {
    background: var(--btn-primary);
    color: var(--cream);
}

.btn--primary:hover,
.btn--primary:focus-visible {
    background: #063137;
    transform: translate3d(0, -2px, 0) scale(1.02);
    box-shadow: 0 18px 34px -18px rgba(1, 37, 41, 0.65);
}

.btn--secondary {
    background: var(--btn-secondary);
    color: var(--ink);
}

.btn--secondary:hover,
.btn--secondary:focus-visible {
    background: #B8D40A;
    transform: translate3d(0, -3px, 0);
    box-shadow: 0 16px 32px -14px rgba(168, 194, 2, 0.8), 0 0 22px -4px rgba(168, 194, 2, 0.5);
}

.btn:active {
    transform: scale(0.98);
    transition-duration: var(--dur-fast);
}

/* -------------------------------------------------------------- SHOWREEL */
/* .reel {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: grid;
    place-items: center;
    padding: clamp(1rem, 4vw, 4rem);
}

.reel[hidden] {
    display: none;
}

.reel__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(3, 18, 21, 0.82);
    backdrop-filter: blur(24px) saturate(1.1);
    -webkit-backdrop-filter: blur(24px) saturate(1.1);
    opacity: 0;
    will-change: opacity;
}

.reel__frame {
    position: relative;
    display: grid;
    place-items: center;
    width: min(1280px, 92vw);
    aspect-ratio: 16 / 9;
    overflow: hidden;
    background: #000000;
    border-radius: 2px;
    opacity: 0;
    transform: scale(0.94);
    will-change: transform, opacity;
}

.reel__video {
    width: 100%;
    height: 100%;
    object-fit: contain;
    background: #000000;
}

.reel__video[data-empty] {
    display: none;
}

.reel__empty {
    position: absolute;
    max-width: 34ch;
    padding: 0 1.5rem;
    color: rgba(255, 255, 255, 0.62);
    font-size: 13px;
    letter-spacing: 0.08em;
    text-align: center;
    line-height: 1.8;
}

.reel__empty code {
    color: var(--btn-secondary);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

.reel__empty[hidden] {
    display: none;
}

.reel__close {
    position: absolute;
    top: clamp(1rem, 2.5vw, 2.5rem);
    right: clamp(1rem, 2.5vw, 2.5rem);
    z-index: 2;
    display: grid;
    place-items: center;
    width: 52px;
    height: 52px;
    color: var(--cream);
    border: 1px solid rgba(255, 255, 255, 0.3);
    border-radius: 50%;
    opacity: 0;
    transition: background-color var(--dur-ui) var(--ease-out), color var(--dur-ui) var(--ease-out), transform var(--dur-ui) var(--ease-out);
}

.reel__close:hover,
.reel__close:focus-visible {
    background: var(--cream);
    color: var(--ink);
    transform: scale(1.06);
}

.is-reel-open {
    overflow: hidden;
}
 */



















/* =========================================================
   SHOWREEL POPUP
========================================================= */

.showreel-modal {
    position: fixed;
    inset: 0;
    z-index: 99999;

    display: none;
    align-items: center;
    justify-content: center;

    width: 100%;
    height: 100%;

    padding: 40px;

    overflow: hidden;
}

.showreel-modal.is-open {
    display: flex;
}


/* =========================================================
   OVERLAY
========================================================= */

.showreel-overlay {
    position: absolute;
    inset: 0;

    width: 100%;
    height: 100%;

    background: rgba(3, 18, 21, 0.82);

    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
}


/* =========================================================
   VIDEO CONTAINER
========================================================= */

.showreel-container {
    position: relative;
    z-index: 2;

    /* The desktop reel (video/showreel.mp4) is 1920 x 1080, read from the
       file's own tkhd box, so its ratio is exactly 16 / 9. The frame now
       takes the full width the modal offers, and its height follows from the
       video's own proportions because the <video> below is width 100% with
       height auto.

       The max-width is that same available height expressed as a width, so a
       short window narrows the frame instead of cropping it or padding it
       out with black bars. It also leaves this container exactly as wide as
       the video, which keeps the close button on the frame's corner rather
       than out at the viewport edge.

       150px is the modal's 40px of padding top and bottom plus the 55px the
       close button occupies above this container, with a little slack. */
    width: 100%;
    max-width: calc((100vh - 150px) * 16 / 9);
    max-height: none;

    display: flex;
    align-items: center;
    justify-content: center;

    transform: scale(0.96);
    opacity: 0;

    transition:
        transform 0.45s cubic-bezier(.16, 1, .3, 1),
        opacity 0.35s ease;
}

.showreel-modal.is-open .showreel-container {
    transform: scale(1);
    opacity: 1;
}


/* =========================================================
   VIDEO
========================================================= */

.showreel-video {
    display: block;

    width: 100%;
    height: auto;

    /* The container above is already limited to the largest 16 / 9 frame
       that fits, so the video simply fills it. height stays auto, so the
       browser uses the file's own 1920 x 1080 proportions and the frame is
       never stretched, squashed or cut. */
    max-width: 100%;
    max-height: none;

    /*
     * Keep original video proportions.
     * Do not stretch the video.
     */
    object-fit: contain;
    object-position: center;

    background: #000;

    /*
     * Remove inline spacing around video.
     */
    vertical-align: middle;
}


/* =========================================================
   CLOSE BUTTON
========================================================= */

.showreel-close {
    position: absolute;

    top: -55px;
    right: 0;

    z-index: 5;

    width: 44px;
    height: 44px;

    display: flex;
    align-items: center;
    justify-content: center;

    padding: 0;

    border: 1px solid rgba(255, 255, 255, 0.5);
    border-radius: 50%;

    background: transparent;
    color: #fff;
    font-size: 28px;
    line-height: 1;
    cursor: pointer;

    transition:
        background 0.3s ease,
        transform 0.3s ease,
        border-color 0.3s ease;
        padding: 0 0 6px;
}

.showreel-close:hover,
.showreel-close:focus-visible {
    background: rgba(255, 255, 255, 0.15);
    border-color: rgba(255, 255, 255, 0.8);
    transform: rotate(90deg);
    outline: none;
}


/* =========================================================
   BODY LOCK
========================================================= */

body.showreel-open {
    overflow: hidden;
}


.hero__line {
    line-height: 1.1 !important;
        overflow: inherit !important;
}


/* =========================================================
   TABLET
   1199px and below
========================================================= */

@media (max-width: 1199px) {

    .showreel-modal {
        padding: 25px;
    }

    .showreel-container {
        width: 94vw;
        max-width: 1100px;

        max-height: 85vh;
    }

    .showreel-video {
        width: 100%;

        max-width: 1100px;
        max-height: 78vh;

        object-fit: contain;
    }

    .showreel-close {
        top: -50px;
        right: 0;

        width: 44px;
        height: 44px;
    }

    .hero__panel{
        display: none !important;
    }

    .mobilehero-panel{
        opacity: 1 !important;
    }

    .mobilehero-panel {
    align-self: flex-start !important;
    justify-items: flex-start !important;
    text-align: left !important;
    }

    .mobherobg{
        display: none;
    }

    .unreall{
       color: #012529 !important;
    }

    .hero__rule {
    margin-top: 64px;
    }
}


.vid-mobile{
        display: none;
    }

/* =========================================================
   TABLET SMALL
   991px and below
========================================================= */

@media (max-width: 991px) {

    .showreel-modal {
        padding: 20px;
    }

    .showreel-container {
        width: 94vw;
        max-height: 82vh;
    }

    .showreel-video {
        max-height: 75vh;
    }

    .showreel-close {
        top: -48px;

        width: 42px;
        height: 42px;

        font-size: 25px;
    }

    .vid-desktop{
        display: none;
    }
    .vid-mobile{
        display: block;
    }
}


/* =========================================================
   MOBILE
   767px and below
========================================================= */

@media (max-width: 767px) {

    .showreel-modal {
        padding: 15px;
    }

    .showreel-container {
        width: 100%;
        max-width: 100%;

        max-height: 80vh;
    }

    .showreel-video {
        width: 100%;
        height: auto;

        max-width: 100%;
        max-height: 72vh;

        object-fit: contain;
    }

    .showreel-close {
        top: -52px;
        right: 0;

        width: 40px;
        height: 40px;

        font-size: 24px;
    }
}


/* =========================================================
   SMALL MOBILE
   480px and below
========================================================= */

@media (max-width: 480px) {

    .showreel-modal {
        padding: 12px;
    }

    .showreel-container {
        width: 100%;

        max-height: 76vh;
    }

    .showreel-video {
        width: 100%;
        max-height: 68vh;
    }

    .showreel-close {
        top: -48px;
        right: 0;

        width: 38px;
        height: 38px;

        font-size: 22px;
    }
}


/* =========================================================
   REDUCED MOTION
========================================================= */

@media (prefers-reduced-motion: reduce) {

    .showreel-container {
        transition: none;
    }

    .showreel-close {
        transition: none;
    }

}


/* ############################################################################
   ANIMATIONS.CSS
   Pre-animation states. Everything here is gated on html.is-armed.
   ######################################################################## */

/* ============================================================================
   ANIMATIONS.CSS — pre-animation states and the reduced-motion contract.
   GSAP owns every timeline. This file only declares the "before" state, so a
   slow script can never flash the finished layout, and guarantees nothing is
   left invisible if a timeline never runs.
   ========================================================================== */

/* ------------------------------------------------- PRE-ANIMATION STATES */
/* .is-armed is set by main.js the instant GSAP is confirmed present. */
.is-armed [data-geometry],
.is-armed .hero__model-wrap,
.is-armed .hero__lede,
.is-armed .hero__actions .btn,
.is-armed .hero__panel,
.is-armed .hero__bar > *,
.is-armed .hero__nav {
    opacity: 0;
}

/* Characters are masked by .hero__line's overflow, so they only need the
   transform primed — no opacity flash on the headline itself. */
.is-armed .hero__title .char {
    display: inline-block;
    transform: translate3d(0, 40px, 0);
    opacity: 0;
    will-change: transform, opacity;
}

/* ------------------------------------------------------------- KEYFRAMES */
/* Fabric breathing: ±10px, ±2°, 12s, sine in-out — driven in CSS so it keeps
   running independently of any scroll-linked timeline on the same element's
   parent. GSAP animates .hero__model-wrap; this animates the child. */
@keyframes fabricBreathe {
    0% {
        transform: translate3d(0, 10px, 0) rotate(-2deg);
    }
    50% {
        transform: translate3d(0, -10px, 0) rotate(2deg);
    }
    100% {
        transform: translate3d(0, 10px, 0) rotate(-2deg);
    }
}

.is-armed .hero__fabric {
    animation: fabricBreathe 12s cubic-bezier(0.37, 0, 0.63, 1) infinite;
}

/* --------------------------------------------------------- REDUCED MOTION */
@media (prefers-reduced-motion: reduce) {

    *,
    *::before,
    *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }

    /* Nothing may stay hidden waiting for a timeline that will not run. */
    .is-armed [data-geometry],
    .is-armed .hero__model-wrap,
    .is-armed .hero__lede,
    .is-armed .hero__actions .btn,
    .is-armed .hero__panel,
    .is-armed .hero__bar > *,
    .is-armed .hero__nav {
        opacity: 1;
    }

    .is-armed .hero__title .char {
        transform: none;
        opacity: 1;
    }

    .is-armed .hero__fabric {
        animation: none;
    }

    .hero__rule,
    .hero__panel-rule {
        transform: none;
    }

    .btn:hover,
    .btn:focus-visible {
        transform: none;
        box-shadow: none;
    }
}

/* ============================================================================
   LOADER — added with the premium intro. Nothing above this line changed.
   The element itself is built by js/loader.js at runtime; index.html is
   untouched. These rules only describe it and guarantee the hero can never
   flash before it exists.
   ========================================================================== */

/* --------------------------------------------------- PRE-COVER (no-JS-yet) */
/* Painted from the stylesheet, so it is on screen before a single line of JS
   runs — this is what prevents a frame of un-animated hero on first paint.
   js/loader.js adds .intro-ready the moment the real loader is in the DOM.
   The keyframe is a failsafe: if scripts never boot at all, the cover clears
   itself after 4s rather than leaving a black page. */
body::after {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 99998;
    background: #000;
    pointer-events: none;
    animation: introFailsafe 0.4s linear 4s forwards;
}

.intro-ready body::after,
html.intro-ready body::after {
    display: none;
}

@keyframes introFailsafe {
    to {
        opacity: 0;
        visibility: hidden;
    }
}

/* ------------------------------------------------------------ THE LOADER */
.loader {
    position: fixed;
    inset: 0;
    z-index: 99999;
    display: grid;
    place-items: center;
    overflow: hidden;
    pointer-events: none;
}

/* Two stacked panels lift with a stagger, so the exit has depth rather than
   reading as one flat rectangle sliding away. */
.loader__panels {
    position: absolute;
    inset: 0;
}

.loader__panel {
    position: absolute;
    inset: 0;
    display: block;
    background: #000;
    will-change: transform;
}

.loader__panel:last-child {
    background: #050505;
}

.loader__inner {
    position: relative;
    z-index: 2;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 18px;
}

.loader__brand {
    text-decoration: none;
    pointer-events: auto;
}

.loader__word {
    display: block;
    font-family: 'Bebas Neue', sans-serif;
    font-size: clamp(28px, 4.2vw, 62px);
    line-height: 1;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: #F5EADF;
    white-space: nowrap;
    /* Optical correction: tracking adds trailing space on the right only. */
    text-indent: 0.24em;
    will-change: transform, opacity, letter-spacing, filter;
}

.loader__rule {
    display: block;
    width: clamp(120px, 18vw, 260px);
    height: 1px;
    background: rgba(245, 234, 223, 0.55);
    transform: scaleX(0);
    transform-origin: left center;
    will-change: transform;
}

/* ------------------------------------------------- LOADER, REDUCED MOTION */
/* The loader is never built in this path (hero.js takes the reduced branch),
   but the pre-cover must still clear instantly if the class lands late. */
@media (prefers-reduced-motion: reduce) {

    body::after {
        animation: none;
        display: none;
    }

    .loader {
        display: none;
    }
}

/* ============================================================================
   HERO ENTRANCE MASKS — added with the editorial reveal pass.
   Two structural wrappers are injected at runtime by js/animations.js
   (wrapLines / maskNavItems). Both are layout-neutral by construction: the
   line wrapper is a block box inside an already-block line, and the nav mask
   cancels its own padding with an equal negative margin. No measured position
   changes anywhere.
   ========================================================================== */

/* Character boxes for the line-masked reveal. The line owns the clip, the
   inner wrapper stands in for the reference's text-word, and each character
   is its own inline-block so yPercent resolves against the glyph's own box.
   Purely a transform context — no metrics change, so the set type is
   identical to before. */
/* HEADING LINE BOX — clipping fix + tighter vertical rhythm, one rule.
   The glyphs are inline-block with `vertical-align: top`, pinned to the top of
   their line box. At the design's `line-height: 0.9` that box is far shorter
   than Bebas' ~1.2em glyph box, so the surplus hangs out the bottom and
   .hero__line's `overflow: hidden` (the reveal mask) cuts it off.

   Padding could not solve that — it grows the clip box but the glyph still
   overflows the LINE box, so the two never met. Giving the line box real
   height does solve it: 1.3em comfortably contains the glyph, then a negative
   margin pulls the surplus back out to set the advance.

   That margin is also the tightening dial. 1.3 - 0.48 = 0.82em of line
   advance, against the design's original 0.9em — so the three lines sit
   noticeably closer without touching font-size, letter-spacing or the block's
   left edge. Raise -0.48em toward -0.40em to open them back out; -0.40em is
   the original spacing exactly. */
.hero__line {
    line-height: 1.3;
    /* padding-top opens clearance above the caps so Bebas' ascender ink is
       never sheared by the reveal mask; the matching negative margin-top keeps
       the glyph in its original position, so only the clip box grows upward. */
    padding-top: 0.15em;
    padding-bottom: 0;
    margin-top: -0.18em;
    /* -0.48em pulled the lines into each other (CREATE IT. rode up into FIND
       TALENT.). -0.30em restores a clear gap while staying tight. */
    margin-bottom: -0.30em;
}

/* VERTICAL HEIGHT FIX.
   inline-block boxes sit on the text baseline, so the line box underneath them
   still reserves room for descenders that no longer exist — that phantom strip
   under every line is what made the headline grow taller than the approved
   design once the glyphs were split. `vertical-align: top` takes the boxes off
   the baseline and the reserved space goes with it, returning the block to its
   original measured height. */
.hero__line-inner {
    line-height: inherit;
}

.hero__line-inner .hero__word {
    display: inline-block;
    vertical-align: top;
    white-space: nowrap;
}

.hero__line-inner .char {
    display: inline-block;
    vertical-align: top;
    backface-visibility: hidden;
    will-change: transform, opacity;
}

/* The headline line wrapper — clipped by .hero__line's existing overflow. */
.hero__line-inner {
    display: block;
    will-change: transform;
}

/* Per-item nav mask. Padding gives uppercase glyphs breathing room inside the
   clip; the matching negative margin means the box the grid measures is
   identical to before. The underline stays on the <a> (::after), outside this
   mask, so the hover treatment is unaffected. */
.hero__nav-mask {
    display: block;
    overflow: hidden;
    padding: 0.14em 0.06em 0.14em 0;
    margin: -0.14em -0.06em -0.14em 0;
}

.hero__nav-mask > span {
    display: inline-block;
    will-change: transform, opacity;
}

/* Nothing may stay hidden if a timeline never runs. */
@media (prefers-reduced-motion: reduce) {

    .hero__line-inner,
    .hero__line-inner .char,
    .hero__line-inner .hero__word,
    .hero__nav-mask > span {
        transform: none !important;
        opacity: 1 !important;
    }
}


/* ############################################################################
   RESPONSIVE.CSS
   The original desktop-down breakpoints for the base layer above.
   ######################################################################## */

/* ============================================================================
   RESPONSIVE.CSS — the composition is re-staged per breakpoint, not shrunk.
   Above 1024 the art sits behind the copy exactly as in the reference. Below,
   the model moves out from under the type so nothing ever overlaps text.
   ========================================================================== */

/* ------------------------------------------------------------ ≤ 1440 */
@media (max-width: 1440px) {

    .hero__copy {
        max-width: 52vw;
    }

    .hero__model-wrap {
        right: 12%;
        height: 86%;
    }
}

/* ------------------------------------------------------------ ≤ 1200 */
@media (max-width: 1200px) {

    .hero__nav {
        right: clamp(2rem, 5vw, 4rem);
    }

    .hero__panel {
        right: clamp(2rem, 5vw, 4rem);
        top: 34vh;
    }

    .hero__model-wrap {
        right: 6%;
        height: 78%;
    }

    .hero__copy {
        max-width: 56vw;
    }
}

/* ------------------------------------------------- ≤ 1024 TABLET / STACK */
@media (max-width: 1199px) {

    /* Stacked order: bar → nav → copy → model panel. The panel is metadata
       about the render, so it reads after the pitch, not before it. */
    .hero {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        min-height: auto;
        padding-bottom: clamp(20rem, 52vw, 30rem);
    }

    .hero__bar {
        order: 1;
    }

    .hero__nav {
        order: 2;
    }

    .hero > .flow-layer {
        order: 3;
        width: 100%;
    }

    .hero__panel {
        order: 4;
    }

    /* Geometry becomes a bottom-right corner plate behind the model. */
    .hero__geometry {
        top: auto;
        bottom: 0;
        height: auto;
        width: 92%;
        max-width: 620px;
        opacity: 0.95;
    }

    .hero__model-wrap {
        right: 2%;
        bottom: 0;
        height: auto;
        width: 62%;
        max-width: 460px;
    }

    /* The nav leaves the artwork, so it needs the ink colour to stay legible. */
    .hero__nav {
        position: relative;
        top: auto;
        right: auto;
        margin-top: clamp(1.25rem, 3vw, 2rem);
    }

    .hero__nav ul {
        grid-auto-flow: column;
        justify-content: start;
        justify-items: start;
        gap: clamp(0.9rem, 2.4vw, 1.75rem);
    }

    .hero__nav-link {
        color: var(--ink);
        opacity: 0.7;
    }

    .hero__panel {
        position: relative;
        top: auto;
        right: auto;
        justify-items: start;
        margin-top: clamp(1.5rem, 4vw, 2.5rem);
        text-align: left;
        /* color: var(--ink); */
        color: #fff;
    }

    .hero__panel-meta::before {
        right: auto;
        left: 0;
        background: rgba(6, 45, 53, 0.3);
    }

    .hero__panel-rule {
        display: none;
    }

    .hero__copy {
        max-width: 100%;
        margin-top: 0px;
    }

    .hero__lede {
        max-width: 46ch;
    }

    .burger {
       top: -13px !important;
    }

    .service__rule{
        display: none !important;
    }

    .hero__actions .btn--secondary{
        margin-bottom: 20px;
    }
}

/* ------------------------------------------------------------- ≤ 768 */
@media (max-width: 767px) {

    :root {
        --fs-display: clamp(2.75rem, 11.5vw, 5rem);
        --fs-accent: clamp(1.75rem, 7.2vw, 3.125rem);
        --fs-lede: clamp(0.95rem, 3.6vw, 1.15rem);
    }

    /* Clear the artwork so the buttons never sit on the teal arc. */
    .hero {
        padding-bottom: clamp(19rem, 88vw, 27rem);
    }

    .hero__geometry {
        width: 108%;
        right: -8%;
    }

    .hero__model-wrap {
        right: -4%;
        width: 82%;
    }

    .hero__nav ul {
        flex-wrap: wrap;
        gap: 0.75rem 1.15rem;
    }
}

/* ------------------------------------------------------------- ≤ 560 */
@media (max-width: 560px) {

    .hero__bar {
        gap: 0.9rem;
    }

    .hero__nav ul {
        grid-auto-flow: row;
        grid-template-columns: repeat(3, auto);
        gap: 0.6rem 1rem;
    }

    .hero__actions {
        gap: 0.75rem;
    }

    /* Spec minimum touch target while keeping the specified proportions. */
    .btn {
        width: 100%;
        max-width: 320px;
        height: 56px;
    }

    .reel__frame {
        width: 94vw;
        aspect-ratio: 9 / 16;
    }

    .reel__close {
        width: 48px;
        height: 48px;
    }
}

/* ----------------------------------------------------- TOUCH / NO HOVER */
@media (hover: none) {

    .btn:hover {
        transform: none;
        box-shadow: none;
    }

    .hero__nav-link::after {
        transform: scaleX(0);
    }

    .hero__nav-link.is-active::after {
        transform: scaleX(1);
    }
}

/* --------------------------------------------------------- SHORT SCREENS */
@media (max-height: 700px) and (min-width: 1200px) {

    .hero {
        min-height: 700px;
    }

    .hero__panel {
        top: 42vh;
    }
}


/* ############################################################################
   SECTION-02.CSS
   Section 02.
   ######################################################################## */

/* ============================================================================
   SECTION-02.CSS — MEET THE UNREAL
   Additive only. Nothing in here touches a Section 01 selector.

   Reference canvas: the supplied design is 2.933:1 (1030 × 351 as delivered,
   1920 × 655 at full size). The stage keeps that aspect and every element is
   positioned in percentages of it, so the composition scales exactly rather
   than being re-laid-out at each width.
   ========================================================================== */

/* ---------------------------------------------------------------- TOKENS */
.roster {
    /* 128 / 56 / 20 px at the 1920 reference */
    --fs-unreal: clamp(3rem, 6.6667vw, 128px);
    --fs-meet: clamp(1.35rem, 2.9167vw, 56px);
    --fs-label: clamp(0.7rem, 1.0417vw, 20px);
    --fs-roster-index: clamp(1.5rem, 1.8229vw, 35px);

    --card-ease: cubic-bezier(0.22, 1, 0.36, 1);
    --card-dur: 0.45s;
}

/* ----------------------------------------------------------------- SHELL */
.roster {
    position: relative;
    width: 100%;
    overflow: hidden;
    background: #2C2740;
    isolation: isolate;
}


/* Background plate — its own carrier so the 0.7 parallax never collides
   with anything else's transform. */
.roster__bg {
    position: absolute;
    /* The 0.7 parallax moves this plate +/-15vh (|0.7 - 1| x 0.5 x 100vh), and
       a 6% inset on a 654px section is only ~39px of cover — so the section's
       own #2C2740 showed as a band along the bottom edge. Overscan in vh so it
       tracks the travel, which is measured in viewport height too. */
    inset: -18vh 0;
    z-index: 0;
    will-change: transform, opacity;
}

.roster__bg img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
}

.roster__index {
    position: absolute;
    top: clamp(1.1rem, 2.2vw, 2.6rem);
    left: clamp(1.1rem, 2.6vw, 3rem);
    z-index: 6;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-roster-index);
    line-height: 1;
    color: #FFFFFF;
    opacity: 0.32;
}

/* The composition itself. Fixed aspect = the design's proportions. */
.roster__stage {
    position: relative;
    z-index: 2;
    width: 100%;
    aspect-ratio: 1030 / 351;
    min-height: 420px;
}

/* Transform carriers, same discipline as Section 01: scroll parallax writes
   to [data-scroll-speed], pointer parallax to [data-pointer], the entrance to
   the element itself, the float loop to its own inner wrapper. */
.roster .layer {
    position: absolute;
    inset: 0;
    will-change: transform;
}

/* ------------------------------------------------------- FEATURED MODEL */
.roster__feature {
    position: absolute;
    left: 0;
    bottom: 0;
    height: 100%;
    margin: 0;
    will-change: transform, opacity;
}

.roster__feature-float {
    height: 100%;
    will-change: transform;
}

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

.roster__feature-label {
    position: absolute;
    top: 82%;
    left: 26%;
    z-index: 5;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-label);
    line-height: 1;
    letter-spacing: 0;
    color: #FFFFFF;
    opacity: 0.85;
}

/* --------------------------------------------------------------- HEADING */
.roster__title {
    position: absolute;
    top: 22%;
    left: 33.5%;
    z-index: 4;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    line-height: 1;
    letter-spacing: 0;
    color: #FFFFFF;
    text-transform: uppercase;
}

/* Masks so the characters rise out of nothing rather than fading in. */
.roster__line {
    display: block;
    /* overflow: hidden; */
    padding-bottom: 0.06em;
    font-size: var(--fs-unreal);
}

.roster__line--sm {
    font-size: var(--fs-meet);
}

/* ------------------------------------------------------------ MODEL CARDS */
.card {
    position: absolute;
    z-index: 3;
    margin: 0;
    will-change: transform, opacity;
}

.card__float {
    will-change: transform;
}

/* Hover lives on its own wrapper: .card carries the GSAP entrance transform
   and .card__float the loop, so a CSS transform here can never be clobbered
   by an inline one. */
.card__hover {
    display: block;
    transition:
        transform var(--card-dur) var(--card-ease),
        filter var(--card-dur) var(--card-ease);
}

.card img {
    display: block;
    width: 100%;
    height: auto;
    filter: drop-shadow(0 10px 22px rgba(10, 6, 24, 0.35));
    transition: filter var(--card-dur) var(--card-ease);
}

.card__label {
    position: absolute;
    z-index: 2;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-label);
    line-height: 1;
    letter-spacing: 0;
    white-space: nowrap;
    color: #FFFFFF;
    opacity: 0.8;
    transform-origin: 0 50%;
    transition:
        opacity var(--card-dur) var(--card-ease),
        letter-spacing var(--card-dur) var(--card-ease),
        transform var(--card-dur) var(--card-ease);
}

/* Hovered card lifts, scales and comes forward. */
.card:hover,
.card:focus-within {
    z-index: 8;
}

.card:hover .card__hover,
.card:focus-within .card__hover {
    transform: translate3d(0, -8px, 0) scale(1.06);
}

.card:hover img,
.card:focus-within img {
    filter: drop-shadow(0 26px 46px rgba(10, 6, 24, 0.55));
}

.card:hover .card__label,
.card:focus-within .card__label {
    opacity: 1;
    letter-spacing: 0.06em;
    transform: scale(1.04);
}

/* -------------------------------------------- CARD PLACEMENT (design map) */
/* Every value is measured off the supplied reference and expressed as a
   percentage of the stage, so the arrangement holds at any width. */
.card--zara {
    left: 55%;
    top: 7%;
    width: 12.8%;
}

.card--zara .card__label {
    left: 8%;
    bottom: 12%;
}

.card--elliot {
    left: 70.6%;
    top: 8%;
    width: 12%;
}

.card--elliot .card__label {
    left: 9%;
    bottom: 9%;
}

.card--sara {
    left: 85.2%;
    top: 16%;
    width: 11%;
}

.card--sara .card__label {
    left: 12%;
    bottom: -14%;
}

.card--lucas {
    left: 59.9%;
    top: 40.5%;
    width: 13.4%;
}

.card--lucas .card__label {
    right: 74%;
    bottom: 18%;
}

.card--milo {
    left: 82%;
    top: 56%;
    width: 11%;
}

.card--milo .card__label {
    left: 14%;
    bottom: 6%;
}

/* ============================================================== RESPONSIVE */
/* The composition holds its exact proportions down to 1024. Below that the
   stage stops being a fixed-aspect canvas and re-forms as a stacked layout,
   because the reference arrangement would put 20px labels at 6px. */

@media (max-width: 1440px) {

    .roster__stage {
        min-height: 460px;
    }
}

@media (max-width: 1199px) {

    .roster {
        padding: clamp(3rem, 8vw, 5rem) clamp(1.25rem, 4vw, 3rem) clamp(3rem, 8vw, 5rem);
    }

    /* Neutralise the transform carriers inside the stage so the real elements
       become grid items. Scroll parallax is deliberately off below this width
       (models.js gates it with matchMedia) — a display:contents box has no
       transform to animate, and layered depth reads as noise on a stacked
       layout anyway. The background carrier outside the stage is untouched. */
    .roster__stage .layer {
        position: static;
        inset: auto;
        display: contents;
    }

    .roster__stage {
        aspect-ratio: auto;
        min-height: 0;
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        gap: clamp(1rem, 3vw, 2rem);
        align-items: end;
    }

    /* Featured model returns to the flow as the left column. */
    .roster__feature {
        position: relative;
        left: auto;
        bottom: auto;
        height: auto;
        grid-column: 1;
        grid-row: 1;
    }

    /* Cap the featured render so the stack keeps a readable rhythm instead of
       one 700px portrait followed by dead space. */
    .roster__feature {
        justify-self: start;
    }

    .roster__feature img {
        width: auto;
        height: clamp(260px, 38vw, 420px);
        max-width: 100%;
    }

    .roster__feature-label {
        position: static;
        grid-column: 1;
        grid-row: 2;
        margin-top: 0.6rem;
    }

    .roster__title {
        position: static;
        grid-column: 2;
        grid-row: 1;
        align-self: center;
    }

    /* Cards leave the absolute map and become an even roster grid. */
    .roster__cards {
        display: grid;
        grid-column: 1 / -1;
        grid-row: 3;
        grid-template-columns: repeat(5, minmax(0, 1fr));
        gap: clamp(0.6rem, 2vw, 1.25rem);
        align-items: end;
        margin-top: clamp(0.75rem, 2vw, 1.25rem);
    }

    .card {
        position: relative;
        left: auto !important;
        top: auto !important;
        width: auto !important;
    }

    .card img {
        width: 100%;
    }

    .card__label {
        position: static;
        display: block;
        margin-top: 0.5rem;
    }

    /* Every per-card label offset from the design map is reset in one place. */
    .card .card__label {
        left: auto;
        right: auto;
        top: auto;
        bottom: auto;
    }
}

@media (max-width: 767px) {

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

    .roster__feature,
    .roster__feature-label,
    .roster__title {
        grid-column: 1;
    }

    .roster__title {
        grid-row: 2;
        margin-top: clamp(1rem, 3vw, 1.5rem);
    }

    .roster__feature-label {
        grid-row: 3;
    }

    .roster__feature img {
        height: clamp(220px, 52vw, 340px);
    }

    .roster__cards {
        grid-row: 4;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        row-gap: clamp(1rem, 4vw, 1.5rem);
    }

    :where(.roster) {
        --fs-unreal: clamp(2.75rem, 13vw, 5.5rem);
        --fs-meet: clamp(1.15rem, 5.6vw, 2.4rem);
        --fs-label: clamp(0.75rem, 3.2vw, 1rem);
    }
}

@media (max-width: 480px) {

    .roster__cards {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* --------------------------------------------------- TOUCH / NO HOVER */
@media (hover: none) {

    .card:hover .card__hover {
        transform: none;
    }

    .card:hover img {
        filter: drop-shadow(0 10px 22px rgba(10, 6, 24, 0.35));
    }

    .card__label {
        opacity: 1;
    }
}

/* ------------------------------------------------------- REDUCED MOTION */
/* Pre-animation states, mirrored by the reduced-motion reset so nothing is
   ever left invisible waiting for a timeline that will not run. */
.is-armed .roster__bg,
.is-armed .roster__feature,
.is-armed .roster__feature-label,
.is-armed .card {
    opacity: 0;
}

.is-armed .roster__title .char {
    display: inline-block;
    transform: translate3d(0, 40px, 0);
    opacity: 0;
    will-change: transform, opacity;
}

@media (prefers-reduced-motion: reduce) {

    .is-armed .roster__bg,
    .is-armed .roster__feature,
    .is-armed .roster__feature-label,
    .is-armed .card {
        opacity: 1;
    }

    .is-armed .roster__title .char {
        transform: none;
        opacity: 1;
    }

    .card:hover .card__hover,
    .card:focus-within .card__hover {
        transform: none;
    }

    .card:hover .card__label,
    .card:focus-within .card__label {
        letter-spacing: 0;
        transform: none;
    }
}

/* ============================================================================
   SECTION 02 — SCROLL-ENTRANCE PASS
   Appended only. Nothing above this line was altered, and no Section 01 file
   is touched by any of it.
   ========================================================================== */

/* ------------------------------------------------------------- HEIGHT */
/* REVERTED. The previous pass added min-height:112vh + padding-block with
   centred flex; that is exactly what produced the empty band above and below
   the composition. The section now takes its height from .roster__stage alone,
   so there is no slack at either edge and the featured model — already
   bottom:0 inside the stage — sits flush on the section's bottom edge.
   No min-height, no padding, no centring. */
.roster {
    padding-block: 0;
}

/* --------------------------------------------------- 3D + TEXT SCAFFOLD */
/* Perspective context for the entrance. Set on the section so every depth
   tween shares one vanishing point; it is a static property, so it neither
   animates nor affects layout. */
.roster {
    perspective: 1400px;
    perspective-origin: 50% 45%;
}

/* Depth carriers need their own 3D context or translateZ collapses flat. */
.roster__feature,
.roster .card {
    transform-style: preserve-3d;
}

/* The roster cards begin stacked at the section's centre, so during the first
   frames they overlap heavily. Backface hidden keeps the depth tilt from
   flashing a mirrored face, and the section's existing `overflow: hidden`
   already contains anything that reaches past an edge mid-flight. */
.roster__feature img,
.roster .card img {
    backface-visibility: hidden;
}

/* HOVER 3D CONTEXT.
   .card__float carries the cursor-tracked rotation, so it needs its own 3D
   context — without preserve-3d the rotateX/rotateY collapse into a flat skew
   and the tilt loses all its depth. Perspective itself is set from JS
   (transformPerspective: 1000) to match the reference exactly. */
.card__float {
    position: relative;
    transform-style: preserve-3d;
    will-change: transform;
    /* The tilt is assigned as a plain style.transform, so the easing lives
       here rather than in a tween. Short enough to still feel cursor-locked,
       long enough not to read as twitchy. */
    transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}

.roster .card {
    cursor: pointer;
}

/* HEADING LINE BOX — the real fix for the clipped glyphs.
   Padding was the wrong lever. The glyphs are inline-block with
   `vertical-align: top`, so each one is pinned to the TOP of its line box; at
   `line-height: 1` that box is shorter than Bebas' ~1.2em glyph box, so the
   overflow always lands at the bottom and .roster__line's `overflow: hidden`
   shears it off — which is why the E's were reading as F's.

   So make the line box tall enough to hold the glyph (1.3em), then pull the
   surplus back out with a negative margin so the visual line advance is
   unchanged at 1em. Nothing clips, and the heading occupies exactly the space
   it did before. */
.roster__line {
    line-height: 1.3;
    /* padding-top opens clearance above the caps so the reveal mask never
       shears Bebas' ascender ink (the top of MEET THE was clipping); the
       matching negative margin-top holds the glyph in place, growing only the
       clip box upward. */
    padding-top: 0.15em;
    padding-bottom: 0;
    margin-top: -0.18em;
    margin-bottom: -0.30em;
}

.comeale{
    font-size: 100px !important;
    color: #062D35 !important;
    line-height: 100px !important;
        overflow: initial !important;
}

.watchthe{
   font-size: 56px !important;
    color: #062D35 !important;
    line-height: 56px !important;
        overflow: initial !important;
}

.meetthe{
    font-size: 56px !important;
    line-height: 56px !important;
        overflow: initial !important; 
}

.unreall{
   overflow: initial !important;  
}

/* ------------------------------------------------------------ RESPONSIVE */
/* Below the desktop breakpoint the stage re-forms as a stacked grid; the tall
   min-height would only add dead space there. Depth is dialled back too — the
   JS already scales the values through motionScale(), this shortens the
   perspective to match. */
@media (max-width: 1199px) {

    .roster {
        perspective: 1000px;
    }
}

@media (max-width: 760px) {

    .roster {
        perspective: 700px;
    }
}

/* Nothing may stay hidden if a timeline never runs. */
@media (prefers-reduced-motion: reduce) {

    .roster__title .char,
    .roster__title .hero__word,
    .roster__title .hero__line-inner {
        transform: none !important;
        opacity: 1 !important;
        visibility: visible !important;
    }
}


/* ############################################################################
   SECTION-03.CSS
   Section 03.
   ######################################################################## */

/* ============================================================================
   SECTION-03.CSS — WATCH THE UNREAL COME ALIVE
   Additive only. No selector here touches Section 01 or Section 02.

   Reference canvas: the supplied design is 1040 × 490 (2.122:1). The stage
   holds that aspect and every element is placed in percentages of it, so the
   composition is the design's proportions at any width.

   Placement was measured off the reference: each character's visible height
   and horizontal centre were read from the comp, then converted to image box
   coordinates using each PNG's own opaque bounding box — so the people land
   where the design puts them regardless of the transparent padding around
   them.
   ========================================================================== */

/* ---------------------------------------------------------------- TOKENS */
.alive {
    /* 128 / 100 / 56 px at the 1920 reference — same scale as Sections 01/02 */
    --fs-unreal: clamp(3rem, 6.6667vw, 128px);
    --fs-come: clamp(2.35rem, 5.2083vw, 100px);
    --fs-watch: clamp(1.35rem, 2.9167vw, 56px);
    --fs-alive-index: clamp(1.5rem, 1.8229vw, 35px);

    --ink: #062D35;
    --yellow: #E7FF0C;

    --char-ease: cubic-bezier(0.22, 1, 0.36, 1);
    --char-dur: 0.5s;
}

/* ----------------------------------------------------------------- SHELL */
.alive {
    position: relative;
    width: 100%;
    overflow: hidden;
    background: #EFE7DE;
    isolation: isolate;
}

.alive__stage {
    position: relative;
    z-index: 1;
    width: 100%;
    aspect-ratio: 1040 / 490;
    min-height: 430px;
}

/* The heading's carrier is a sibling of the stage that comes BEFORE it in the
   DOM, and .layer sets will-change: transform — which opens a stacking context
   at z-index auto. Without an explicit z-index here the stage simply paints
   over the heading: it renders, at full opacity, in the right place, and is
   invisible. The play button never showed this because it carries its own
   z-index. Declare the paint order rather than inheriting it from DOM order. */
.alive > .layer {
    z-index: 7;
    /* This carrier is inset:0, so at z-index 7 it would sit over the whole
       section and swallow every hover and click meant for the cast below.
       The heading holds no interactive content, so it opts out of hit-testing
       entirely. */
    pointer-events: none;
}

/* Transform carriers — identical contract to Sections 01 and 02: scroll
   parallax writes to [data-scroll-speed], pointer parallax to [data-pointer],
   the entrance to the element itself, loops to their own inner wrapper. */
.alive .layer {
    position: absolute;
    inset: 0;
    will-change: transform;
}

/* ------------------------------------------------------------ ENVIRONMENT */
/* The supplied plate carries the architecture, the water runway and the
   fabrics in one photographic image, so it moves as one layer. */
.alive__bg {
    position: absolute;
    /* 0.6 parallax = +/-20vh of travel; overscan has to exceed it or the
       section's own cream shows as a band at the top edge. */
    inset: -24vh 0;
    z-index: 0;
    /* will-change: transform, opacity; */
}

.alive__bg img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center bottom;
}

/* Fabric shimmer — the same plate, clipped to the tulle and ribbon and
   feathered to nothing at its edges. Because the tulle is translucent and
   low-contrast, a soft duplicate drifting a few pixels reads as the fabric
   breathing rather than as a doubled image. It sits below the characters, so
   the couple still occludes it exactly as the design shows. */
.alive__fabric {
    position: absolute;
    inset: -5% 0;
    z-index: 1;
    /* background-image: url("../image/come-alive-bg.webp"); */
    background-size: cover;
    background-position: center bottom;
    opacity: 0.28;
    mix-blend-mode: screen;
    /* No clip-path — the radial mask already scopes this duplicate to the
       tulle with a soft falloff. The clip was cutting a hard vertical seam
       down the middle of the set at its 54% boundary. */
    -webkit-mask-image: radial-gradient(42% 44% at 80% 22%, #000 30%, transparent 82%);
    mask-image: radial-gradient(42% 44% at 80% 22%, #000 30%, transparent 82%);
    transform-origin: 76% 26%;
    pointer-events: none;
    will-change: transform;
}

/* Camera flash — one soft white wash, fired at random intervals. */
.alive__flash {
    position: absolute;
    inset: 0;
    z-index: 6;
    background: radial-gradient(38% 42% at 62% 30%, rgba(255, 255, 255, 0.95), transparent 68%);
    opacity: 0;
    pointer-events: none;
    will-change: opacity;
}

/* Light sweep — a slow, low-opacity band travelling across the whole set. */
.alive__sweep {
    position: absolute;
    top: -20%;
    left: 0;
    z-index: 5;
    width: 26%;
    height: 140%;
    background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.30), transparent);
    transform: translate3d(-140%, 0, 0) skewX(-14deg);
    pointer-events: none;
    will-change: transform;
}

/* ------------------------------------------------------------- SECTION 03 */
.alive__index {
    position: absolute;
    top: 6.5%;
    left: 2.7%;
    z-index: 8;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-alive-index);
    line-height: 1;
    color: var(--ink);
    opacity: 0.45;
}

/* --------------------------------------------------------------- HEADING */
.alive__title {
    position: absolute;
    top: 8%;
    left: 7.7%;
    z-index: 7;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    line-height: 1;
    letter-spacing: 0;
    text-transform: uppercase;
    color: var(--ink);
}

.alive__line {
    display: block;
    overflow: hidden;
    padding-bottom: 0.06em;
}

.alive__line--watch {
    font-size: var(--fs-watch);
}

.alive__line--unreal {
    font-size: var(--fs-unreal);
    color: var(--yellow);
}

.alive__line--come {
    font-size: var(--fs-come);
}

/* ------------------------------------------------------------ CHARACTERS */
.character {
    position: absolute;
    margin: 0;
    will-change: transform;
}

/* Each character owns four nested transform sources so nothing collides:
     .character        entrance tween
     .character__life  idle loop — dress sway, posture shift
     .character__hover CSS hover
   plus the two .layer wrappers above for parallax. */
.character__life {
    display: block;
    will-change: transform;
}

.character__hover {
    display: block;
    transition:
        transform var(--char-dur) var(--char-ease),
        filter var(--char-dur) var(--char-ease);
}

.character img {
    display: block;
    width: 100%;
    height: auto;
    filter: drop-shadow(0 18px 30px rgba(24, 16, 10, 0.28));
    transition: filter var(--char-dur) var(--char-ease);
}

.character:hover,
.character:focus-within {
    z-index: 9;
}

.character:hover .character__hover,
.character:focus-within .character__hover {
    transform: scale(1.04);
}

.character:hover img,
.character:focus-within img {
    filter: drop-shadow(0 34px 54px rgba(24, 16, 10, 0.42)) brightness(1.06);
}

/* ------------------------------------------- CHARACTER MAP (from the comp) */
/* left → image box left, top → image box top, width → image box width, all as
   a percentage of the stage. Derived from each PNG's opaque bbox so the person
   lands on the design's mark, not the file's edge. */
.character--guests {
    left: 30.0%;
    top: 28.4%;
    width: 18.0%;
    z-index: 2;
}

.character--orange {
    left: 53.2%;
    top: 11.3%;
    width: 21.4%;
    z-index: 3;
}

.character--pour {
    left: 67.4%;
    top: 2.0%;
    width: 27.5%;
    z-index: 4;
}

.character--lead {
    left: 39.4%;
    top: 3%;
    width: 28.0%;
    z-index: 5;
}

/* ----------------------------------------------------------- CHAMPAGNE */
/* Sits over the bottle and glass in alive-3. Everything here is a transform
   or an opacity on a handful of tiny nodes — no layout is ever touched. */
.pour {
    position: absolute;
    left: 78.6%;
    top: 36%;
    z-index: 6;
    width: 6%;
    height: 14%;
    pointer-events: none;
}

/* The falling stream: a thin bright thread with a travelling highlight. */
.pour__stream {
    position: absolute;
    top: 8%;
    left: 42%;
    width: 2.2px;
    height: 54%;
    border-radius: 2px;
    background: linear-gradient(180deg, rgba(255, 244, 190, 0.05), rgba(255, 238, 165, 0.85), rgba(255, 249, 214, 0.25));
    transform-origin: 50% 0;
    opacity: 0.9;
    will-change: transform, opacity;
}

.pour__glint {
    position: absolute;
    top: 8%;
    left: 40%;
    width: 4px;
    height: 16%;
    border-radius: 4px;
    background: linear-gradient(180deg, transparent, rgba(255, 255, 255, 0.9), transparent);
    opacity: 0;
    will-change: transform, opacity;
}

.pour__bubble {
    position: absolute;
    bottom: 12%;
    left: 44%;
    width: 3px;
    height: 3px;
    border-radius: 50%;
    background: rgba(255, 252, 226, 0.9);
    box-shadow: 0 0 4px rgba(255, 250, 205, 0.8);
    opacity: 0;
    will-change: transform, opacity;
}

/* Glass reflection — a soft vertical highlight that drifts across the flute. */
.pour__glass {
    position: absolute;
    bottom: 2%;
    left: 20%;
    width: 46%;
    height: 42%;
    overflow: hidden;
    border-radius: 40% 40% 20% 20%;
}

.pour__glass::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(112deg, transparent 30%, rgba(255, 255, 255, 0.55) 48%, transparent 66%);
    transform: translate3d(-120%, 0, 0);
    will-change: transform;
}

/* GSAP cannot target a pseudo-element, so this one loop lives in CSS. The
   class is added by scene.js once the entrance finishes. */
.pour__glass.is-sweeping::after {
    animation: glassSweep 5.5s cubic-bezier(0.37, 0, 0.63, 1) infinite;
}

@keyframes glassSweep {
    0%,
    62% {
        transform: translate3d(-120%, 0, 0);
    }
    100% {
        transform: translate3d(160%, 0, 0);
    }
}

/* ---------------------------------------------------------- PLAY BUTTON */
.alive__play {
    position: absolute;
    left: 13.4%;
    top: 69%;
    z-index: 8;
    display: grid;
    place-items: center;
    width: 8.2%;
    aspect-ratio: 1;
    color: var(--ink);
    background: transparent;
    border: 1px solid rgba(6, 45, 53, 0.55);
    border-radius: 50%;
    transition:
        background-color var(--char-dur) var(--char-ease),
        color var(--char-dur) var(--char-ease),
        transform var(--char-dur) var(--char-ease);
    will-change: transform, opacity;
}

.alive__play svg {
    width: 26%;
    height: auto;
    margin-left: 8%;
    fill: currentColor;
}

.alive__play:hover,
.alive__play:focus-visible {
    background: var(--ink);
    color: #F5EADF;
    transform: scale(1.06);
}

.alive__play-ring {
    position: absolute;
    inset: -8%;
    border: 1px solid rgba(6, 45, 53, 0.28);
    border-radius: 50%;
    pointer-events: none;
    will-change: transform, opacity;
}

/* ============================================================== RESPONSIVE */
/* The cinematic composition holds to 1024. Below that the stage stops being a
   fixed-aspect set and re-forms: the environment becomes a framed plate with
   the cast composited on it at a workable size, then the heading and play
   control move into normal flow beneath. */

@media (max-width: 1440px) {

    .alive__stage {
        min-height: 470px;
    }
}

@media (max-width: 1199px) {

    /* Heading, stage and play control become a simple column. The heading and
       play button are siblings of the stage in the markup precisely so they
       can do this — nothing inside the stage has to move. */
    .alive {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        padding: clamp(3.25rem, 9vw, 5rem) clamp(1.25rem, 4vw, 3rem) clamp(2.5rem, 7vw, 4.5rem);
    }

    /* Neutralise BOTH of the heading's transform carriers. Only doing the
       direct child leaves the inner pointer carrier absolute, and the h2 then
       lays out inside a full-bleed box that ignores the section padding —
       which reads as a heading glued to the left edge and clipped.
       Parallax is gated off below 1025 in scene.js, so nothing is lost. */
    .alive > .layer,
    .alive > .layer > .layer {
        position: static;
        inset: auto;
        display: contents;
    }

    /* The section numeral joins the column instead of sitting on the heading. */
    .alive__index {
        position: static;
        order: 0;
        margin-bottom: clamp(0.4rem, 1.5vw, 0.75rem);
    }

    .alive__title {
        position: static;
        order: 1;
        margin-bottom: clamp(1.25rem, 4vw, 2rem);
    }

    .alive__stage {
        order: 2;
        width: 100%;
    }

    /* The set keeps its own aspect so the cast stays in proportion — only the
       text is re-flowed around it. */
    .alive__stage {
        min-height: 0;
        border-radius: 2px;
        overflow: hidden;
    }

    .alive__play {
        position: static;
        order: 3;
        width: clamp(56px, 13vw, 76px);
        margin-top: clamp(1.25rem, 4vw, 2rem);
        border-color: rgba(6, 45, 53, 0.7);
    }

    /* Parallax is gated off below this width in scene.js — the pointer rig is
       hover-gated anyway and the scroll rig would fight the reflow. */
}

@media (max-width: 767px) {

    :where(.alive) {
        --fs-unreal: clamp(2.75rem, 13vw, 5.5rem);
        --fs-come: clamp(2.15rem, 10.2vw, 4.3rem);
        --fs-watch: clamp(1.15rem, 5.6vw, 2.4rem);
    }

    /* A 2.12:1 set is unreadably short on a phone. Crop to a taller frame and
       hold the cast's centre of interest — the lead model — in view. */
    .alive__stage {
        aspect-ratio: 4 / 3;
    }

    .alive__bg img {
        object-position: 62% bottom;
    }

    /* Re-centre the map on the lead and the pouring couple; the outer guests
       fall outside a 4:3 crop, so they step in and scale up to stay legible. */
    .character--lead {
        left: 22%;
        width: 46%;
    }

    .character--pour {
        left: 58%;
        width: 46%;
        top: -4%;
    }

    .character--orange {
        left: 44%;
        width: 34%;
        top: 12%;
    }

    .character--guests {
        left: 0%;
        top: 34%;
        width: 25%;
    }

    .pour {
        left: 72%;
        top: 30%;
        width: 9%;
    }
}

@media (max-width: 560px) {

    .alive__stage {
        aspect-ratio: 3 / 4;
    }

    .alive__bg img {
        object-position: 66% bottom;
    }

    .character--lead {
        left: 6%;
        width: 62%;
        top: 8%;
    }

    .character--pour {
        left: 52%;
        width: 62%;
        top: 2%;
    }

    .character--orange {
        left: 34%;
        width: 46%;
        top: 20%;
    }

    /* Two people in one plate at this width read as clutter behind the lead. */
    .character--guests {
        display: none;
    }

    .pour {
        left: 68%;
        top: 26%;
        width: 12%;
    }
}

/* ----------------------------------------------------- TOUCH / NO HOVER */
@media (hover: none) {

    .character:hover .character__hover {
        transform: none;
    }

    .character:hover img {
        filter: drop-shadow(0 18px 30px rgba(24, 16, 10, 0.28));
    }

    .alive__play:hover {
        transform: none;
    }
}

/* ------------------------------------------------------- REDUCED MOTION */
/* Pre-animation states, mirrored by the reduced-motion reset so nothing can
   be stranded invisible waiting for a timeline that will not run. */
.is-armed .alive__bg,
.is-armed .character,
.is-armed .alive__play,
.is-armed .pour {
    opacity: 0;
}

.is-armed .alive__title .char {
    display: inline-block;
    transform: translate3d(0, 40px, 0);
    opacity: 0;
    will-change: transform, opacity;
}

@media (prefers-reduced-motion: reduce) {

    .is-armed .alive__bg,
    .is-armed .character,
    .is-armed .alive__play {
        opacity: 1;
    }

    .is-armed .alive__title .char {
        transform: none;
        opacity: 1;
    }

    /* Continuous loops and their carriers are removed outright, not just
       paused — flashes, sweeps, bubbles and the pour are all decorative. */
    .is-armed .pour,
    .alive__flash,
    .alive__sweep,
    .alive__fabric,
    .alive__play-ring {
        display: none;
    }

    .character:hover .character__hover,
    .character:focus-within .character__hover {
        transform: none;
    }

    .character:hover img,
    .character:focus-within img {
        filter: drop-shadow(0 18px 30px rgba(24, 16, 10, 0.28));
    }
}

/* ============================================================================
   SECTION 03 — HEADING REVEAL SCAFFOLD
   Appended only; nothing above this line changed. These rules exist solely so
   the shared Section 01/02 text reveal renders correctly here. No position,
   size, colour or spacing is altered — the negative margins cancel the added
   line-height exactly, so the heading occupies the same box as before.
   ========================================================================== */

/* The glyphs are inline-block with `vertical-align: top`, pinned to the TOP of
   their line box. .alive__line owns the `overflow: hidden` that masks the
   reveal, so a line box shorter than Bebas' ~1.2em glyph box shears the
   bottoms off the letters. Give the box real height, then pull the surplus
   back out so the visual line advance is unchanged. */
.alive__line {
    line-height: 1.3;
    /* padding-top opens clearance above the caps so the reveal mask never
       shears Bebas' ascender ink (the top of WATCH THE was clipping); the
       matching negative margin-top holds the glyph in place, growing only the
       clip box upward. */
    padding-top: 0.15em;
    padding-bottom: 0;
    margin-top: -0.18em;
    margin-bottom: -0.30em;
}

.alive__title .hero__line-inner {
    display: block;
    line-height: inherit;
}

.alive__title .hero__word {
    display: inline-block;
    vertical-align: top;
    white-space: nowrap;
}

/* vertical-align: top also kills the phantom descender strip that would
   otherwise make the heading taller than the design. */
.alive__title .char {
    display: inline-block;
    vertical-align: top;
    backface-visibility: hidden;
    will-change: transform, opacity;
}

/* Nothing may stay hidden if the timeline never runs. */
@media (prefers-reduced-motion: reduce) {

    .alive__title .char,
    .alive__title .hero__word,
    .alive__title .hero__line-inner {
        transform: none !important;
        opacity: 1 !important;
        visibility: visible !important;
    }
}


/* ############################################################################
   SECTION-04.CSS
   Section 04.
   ######################################################################## */

/* ============================================================================
   SECTION-04.CSS — MANIFESTO
   Additive only. No selector here touches Sections 01, 02 or 03.

   Reference canvas: the comp is 1035 x 382 (2.709:1). Every value below was
   measured off it in pixels and converted, rather than eyeballed:

     04 index      x 29.5,  cap top 46.0,  cap height 28.5
     line 1 cap    x 81.0,  cap top 37.0,  cap height 51.0
     line pitch    71.2 (cap tops at 37.0, 108.2, 179.4, 250.5)
     rule          x 78.5 -> 157.0, y 315.0, 1px
     statement     x 78.5 -> 357.5, cap top 333.0, cap height 10.0
     portrait ink  left 604.0, top 23.5, bottom flush at 382

   Those cap heights settle the scale. Bebas sits at ~0.745em, so a 51px cap is
   a 68.45px font in comp space; Inter at ~0.727em makes the 10px cap a 13.76px
   font. Scaled up, both land on exactly 100px and 20px at a 1512-wide canvas —
   so 1512 is the width these values were authored at, and every size here is
   expressed against it (100/1512 = 6.6138vw, 20/1512 = 1.3228vw).

   The clamp ceilings are set at 1920, not at 1512, so the composition keeps
   its exact proportions across every common desktop width instead of the type
   freezing while the section — and the portrait, which is sized off section
   height — carries on growing.
   ========================================================================== */

/* ---------------------------------------------------------------- TOKENS */
.manifesto {
    /* 100 / 20 / 56 px at the 1512 reference, held in proportion to 1920. */
    --fs-manifesto: clamp(2.4rem, 6.6138vw, 127px);
    --fs-statement: clamp(0.85rem, 1.3228vw, 25px);
    --fs-manifesto-index: clamp(1.5rem, 3.7037vw, 71px);

    --lime: #B8CC01;
    --ink-04: #001E24;

    --m-ease: cubic-bezier(0.22, 1, 0.36, 1);
    --m-dur: 0.5s;
}

/* ----------------------------------------------------------------- SHELL */
.manifesto {
    position: relative;
    width: 100%;
    /* The comp's proportions. min-height keeps the composition from collapsing
       on short laptop viewports. */
    aspect-ratio: 1035 / 382;
    min-height: 420px;
    overflow: hidden;
    background: var(--lime);
    isolation: isolate;
}

/* --------------------------------------------------------- TRANSFORM RIGS */
/* Four carriers, two per moving group: the outer one takes scroll parallax,
   the inner one the pointer budget. One transform source per element, the
   same discipline as Sections 01-03.

   They stretch across the whole section so the percentages inside them
   resolve against the section box — which is exactly why they must be
   pointer-transparent. A full-bleed carrier at z-index 3 swallowing the
   portrait's hover is the bug Section 03 shipped and had to be fixed for. */
.manifesto__copy-scroll,
.manifesto__copy-pointer,
.manifesto__portrait-scroll,
.manifesto__portrait-pointer,
.manifesto__index-pointer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    will-change: transform;
}

/* Paint order declared, never inherited from DOM order. */
.manifesto__copy-scroll,
.manifesto__copy-pointer {
    z-index: 3;
}

.manifesto__portrait-scroll,
.manifesto__portrait-pointer {
    z-index: 2;
}

.manifesto__index-pointer {
    z-index: 1;
}

/* ------------------------------------------------------------ LEFT COLUMN */
.manifesto__copy {
    position: absolute;
    /* 8.25% puts the first cap on the comp's 37px (verified: 9.86% vs 9.69%).
       The copy is 58% wide and the longest line measures 46.2% of the section,
       so it clears the portrait's ink at 58.4% and never needs nowrap. */
    top: 8.25%;
    left: 7.6%;
    width: 58%;
}

/* A section index, not a decorative graphic — it carries the same 1px pointer
   depth the brief gives the background plate. Measured at 13% of the ink
   against the lime. */
.manifesto__index {
    position: absolute;
    top: 11.3%;
    left: 2.85%;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-manifesto-index);
    line-height: 1;
    color: var(--ink-04);
    opacity: 0.13;
}

/* --------------------------------------------------------------- HEADING */
.manifesto__title {
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-manifesto);
    /* Spec: 100px leading on a 100px font. The comp's pitch is 4% looser than
       that, so line-height 1 plus the 0.04em of mask padding below lands on
       the comp exactly (verified: 18.64% vs 18.64%) without contradicting it. */
    line-height: 1;
    /* Measured, not styled by eye: at 100px the four lines came out 2.6% short
       of the comp's ink widths, spread evenly across 13 and 18 letter gaps —
       0.01em of tracking, not a bigger font. */
    letter-spacing: 0.01em;
    text-transform: uppercase;
    color: var(--ink-04);
}

/* Each line is its own mask, so it rises out of nothing rather than fading
   in — and so the four lines can never resolve as one block. */
.manifesto__line {
    display: block;
    overflow: hidden;
    padding-bottom: 0.04em;
}

.manifesto__line-in {
    display: block;
    will-change: transform, opacity;
}

/* Rule and statement are spaced in em against the heading's own size, so the
   whole column scales as one instead of drifting apart as the type grows. */
.manifesto__rule {
    display: block;
    font-size: var(--fs-manifesto);
    width: 1.147em;
    height: 2px;
    margin: -0.03em 0 0.22em;
    background: var(--ink-04);
    transform: scaleX(0);
    transform-origin: 0 50%;
}

.manifesto__statement {
    margin: 0;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: var(--fs-statement);
    line-height: 1;
    text-transform: uppercase;
    /* The comp's statement runs 279px wide on a 10px cap. A 10px cap fixes the
       font at 20px, so the extra width is tracking, not size: 0.0135em over its
       34 letter gaps, measured back until the ink width matched. */
    letter-spacing: 0.0135em;
    color: var(--ink-04);
    will-change: transform, opacity;
    margin-top: 40px;
}

/* ----------------------------------------------------------- THE PORTRAIT */
/* casting.webp is 642 x 538 with its opaque box at x 48-581, y 29-537 — the
   subject is cut flush at the bottom edge. Solving the comp's ink position
   (left 604, top 23.5, bottom 382) through that box gives the frame below.

   The extra 2% of height, pushed below the section's foot, is deliberate: she
   is cut flush, so the float's 1° tilt and the 4px pointer budget would each
   open a sliver of lime beneath her. Bleeding the crop past the edge costs
   nothing visually — the image is already cropped there — and removes the
   whole class of gap. Scroll parallax is a much larger excursion and is
   handled separately, by the downward-only anchor in manifesto.js. */
.manifesto__portrait {
    position: absolute;
    left: 55%;
    bottom: -2.1%;
    height: 101.4%;
    aspect-ratio: 642 / 538;
    margin: 0;
    /* Re-enabled here only — the carriers above are pointer-transparent. */
    pointer-events: auto;
    will-change: transform, opacity;
}

.manifesto__portrait-float {
    display: block;
    height: 100%;
    will-change: transform;
}

/* Hover lives on its own wrapper so a CSS transform can never be clobbered by
   the inline one GSAP writes for the entrance or the float. */
.manifesto__portrait-hover {
    display: block;
    height: 100%;
    transition:
        transform var(--m-dur) var(--m-ease),
        filter var(--m-dur) var(--m-ease);
}

.manifesto__portrait img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
    object-position: bottom center;
    filter: drop-shadow(0 20px 34px rgba(0, 30, 36, 0.22));
    transition: filter var(--m-dur) var(--m-ease);
}

.manifesto__portrait:hover .manifesto__portrait-hover,
.manifesto__portrait:focus-visible .manifesto__portrait-hover {
    transform: scale(1.02);
}

.manifesto__portrait:hover img,
.manifesto__portrait:focus-visible img {
    filter: drop-shadow(0 34px 52px rgba(0, 30, 36, 0.34)) brightness(1.04);
}

.manifesto__portrait:focus-visible {
    outline: 2px solid var(--ink-04);
    outline-offset: 6px;
}

/* ============================================================== RESPONSIVE */
/* The composition holds its exact proportions down to 1024. Below that the
   section stops being a fixed-aspect canvas and re-forms as a stacked layout,
   because the reference arrangement would put the statement at 7px. */

@media (max-width: 1440px) {

    .manifesto {
        min-height: 440px;
    }
}

@media (max-width: 1199px) {

    .manifesto {
        aspect-ratio: auto;
        min-height: 0;
        padding: clamp(3.5rem, 9vw, 6rem) clamp(1.25rem, 4vw, 3rem) 0;
    }

    /* Neutralise the transform carriers so the real elements rejoin the flow.
       Scroll and pointer parallax are both gated off below this width in
       manifesto.js — a display:contents box has no transform to animate. */
    .manifesto__copy-scroll,
    .manifesto__copy-pointer,
    .manifesto__portrait-scroll,
    .manifesto__portrait-pointer,
    .manifesto__index-pointer {
        position: static;
        inset: auto;
        display: contents;
    }

    .manifesto__copy {
        position: static;
        width: 100%;
        max-width: 42rem;
    }

    .manifesto__index {
        position: static;
        display: block;
        margin-bottom: 0.4em;
    }

    /* The -0.03em top margin is a calibration against the comp, where the rule
       sits hard under a 100px cap. At a third of that size the same figure
       reads as touching the baseline, so the stacked layout gets its own air. */
    .manifesto__rule {
        margin: 0.16em 0 0.34em;
    }

    /* The figure returns to normal flow beneath the copy, still cut flush at
       the section's foot exactly as the comp intends. */
    .manifesto__portrait {
        position: relative;
        left: auto;
        bottom: auto;
        height: auto;
        /* The figure must take its height from the image, not from a ratio
           that rounds 3px away from it — 3px of her feet clipped by the
           section's overflow is exactly the kind of seam Section 03 taught. */
        aspect-ratio: auto;
        width: min(78%, 30rem);
        margin: clamp(2rem, 5vw, 3.5rem) 0 0 auto;
    }

    .manifesto__portrait img {
        height: auto;
    }

    :where(.manifesto) {
        --fs-manifesto: clamp(2.4rem, 8.4vw, 5.5rem);
        --fs-statement: clamp(0.8rem, 1.9vw, 1.15rem);
        --fs-manifesto-index: clamp(1.4rem, 4.2vw, 2.6rem);
    }
}

@media (max-width: 767px) {

    .manifesto__portrait {
        width: min(100%, 26rem);
        margin-left: auto;
        margin-right: auto;
    }

    :where(.manifesto) {
        --fs-manifesto: clamp(2.1rem, 11.4vw, 4.5rem);
        --fs-statement: clamp(0.78rem, 3.2vw, 1.05rem);
        --fs-manifesto-index: clamp(1.25rem, 5.6vw, 2.1rem);
    }
}

@media (max-width: 560px) {

    .manifesto {
        padding-top: clamp(2.75rem, 11vw, 4rem);
    }

    /* The statement is one long line of caps; below this width it has to be
       allowed to break rather than force a horizontal scrollbar. */
    .manifesto__statement {
        line-height: 1.35;
    }

    :where(.manifesto) {
        --fs-manifesto: clamp(1.9rem, 12.6vw, 3.4rem);
    }
}

/* ----------------------------------------------------------- TOUCH / NO HOVER */
@media (hover: none) {

    .manifesto__portrait:hover .manifesto__portrait-hover {
        transform: none;
    }

    .manifesto__portrait:hover img {
        filter: drop-shadow(0 20px 34px rgba(0, 30, 36, 0.22));
    }
}

/* ------------------------------------------------------------ ARMED STATES */
/* Pre-animation states, applied only once GSAP is confirmed (`is-armed` goes
   on the <html> in main.js). Without JS the section renders complete and
   readable; with JS it starts hidden and the timeline brings it in. */
.is-armed .manifesto__line-in {
    transform: translate3d(0, 40px, 0);
    opacity: 0;
}

.is-armed .manifesto__statement,
.is-armed .manifesto__index {
    opacity: 0;
}

.is-armed .manifesto__portrait {
    opacity: 0;
}

/* --------------------------------------------------------- REDUCED MOTION */
/* Every armed state above is mirrored here, so nothing is ever left invisible
   waiting for a timeline that will not run. */
@media (prefers-reduced-motion: reduce) {

    .is-armed .manifesto__line-in {
        transform: none;
        opacity: 1;
    }

    .is-armed .manifesto__statement {
        opacity: 1;
    }

    .is-armed .manifesto__index {
        opacity: 0.13;
    }

    .is-armed .manifesto__portrait {
        opacity: 1;
    }

    .manifesto__rule {
        transform: scaleX(1);
    }

    .manifesto__portrait:hover .manifesto__portrait-hover,
    .manifesto__portrait:focus-visible .manifesto__portrait-hover {
        transform: none;
    }
}

/* ============================================================================
   SECTION 04 — SCRUBBED HEADING REVEAL
   Appended only; nothing above this line changed. No position, size, colour
   or spacing is altered — these rules exist so the char-level reveal renders
   correctly inside the existing line masks.
   ========================================================================== */

/* The chars shift x:-7 -> 0. .manifesto__line owns `overflow: hidden` for the
   old line-mask entrance, which would clip that horizontal travel at the left
   edge of the first character. The mask is no longer needed — the reveal is
   opacity and a 7px nudge, nothing that needs hiding — so it is released here
   while the element's box, and therefore the layout, stays identical. */
.manifesto__line {
    overflow: visible;
}

/* SplitText wraps each visual line in .split-line. Block display keeps the
   line structure the design already has; overflow stays visible for the same
   reason as above. */
.manifesto__title .split-line {
    display: block;
    overflow: visible;
}

/* Char boxes. inline-block so x/opacity apply per glyph; vertical-align: top
   prevents the phantom descender strip that would otherwise make the heading
   taller than the design. */
.manifesto__title .split-line div,
.manifesto__title .split-line span {
    will-change: transform, opacity;
}

/* Nothing may stay dimmed if the timeline never runs. */
@media (prefers-reduced-motion: reduce) {

    .manifesto__title .split-line div,
    .manifesto__title .split-line span {
        transform: none !important;
        opacity: 1 !important;
    }
}


/* ############################################################################
   SECTION-05.CSS
   Section 05.
   ######################################################################## */

/* ============================================================================
   SECTION-05.CSS — INFINITE REALITIES
   Additive only. No selector here touches Sections 01, 02, 03 or 04.

   Reference canvas: the comp's section band is 1023 x 347 (2.948:1). Measured
   off it in pixels rather than eyeballed:

     05 index      x 24,  cap top 55,  cap height 30
     heading       x 73,  cap top 55,  cap height 51, pitch 71.5
     card frames   left 333 / 513 / 681 / 853, top 112, height 126
     card widths   161 / 162 / 159 / 156
     labels        ascender top 250.5, span 13 (ascender to descender)

   Two things fell out of measuring it:

   1. The cards are all 0.712x their natural size — 175px tall in the source,
      126 in the comp. So the strip is a UNIFORM HEIGHT with NATURAL WIDTHS,
      not a grid of identical boxes. That is why each frame below carries its
      own aspect-ratio: it is the supplied file's own shape, at one shared
      height, which is what "consistent sizing" means for a filmstrip.
   2. The heading's cap height (51) and line pitch (71.5) are identical to
      Section 04's, and the label's 13px ascender-to-descender span works back
      to exactly 20px — the same figure the brief specifies. So this section
      and Section 04 are one type system, and the tokens below are Section 04's
      arithmetic re-derived, not a second scale that happens to look similar.

   Everything vertical is a percentage of the section's height and everything
   horizontal a percentage of its width; `aspect-ratio` locks the two together
   so the composition scales as one piece.
   ========================================================================== */

/* ---------------------------------------------------------------- TOKENS */
.reels {
    /* 100 / 58.8 / 20 px at the 1512 reference, held in proportion to 1920. */
    --fs-reels: clamp(2.4rem, 6.6138vw, 127px);
    --fs-reels-index: clamp(1.5rem, 3.8903vw, 74px);
    --fs-campaign-label: clamp(0.8rem, 1.3228vw, 25px);

    --teal-05: #073F4C;
    --lime: #B8CC01;
    --paper-05: #FFFFFF;
    /* Sampled straight off the comp — a neutral warm grey, which no opacity of
       white over this teal can produce, so it is a colour and not a fade. */
    --index-05: #817E7E;

    --campaign-gap: clamp(0.5rem, 1.271vw, 25px);
    --campaign-ease: cubic-bezier(0.22, 1, 0.36, 1);
    --campaign-dur: 0.45s;
    /* The brief asks for the image zoom on its own, slower curve: 0.8s
       power2.out. This is that curve as a bezier. */
    --zoom-dur: 0.8s;
    --zoom-ease: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

/* ----------------------------------------------------------------- SHELL */
.reels {
    position: relative;
    width: 100%;
    aspect-ratio: 1023 / 347;
    min-height: 340px;
    overflow: hidden;
    background: var(--teal-05);
    isolation: isolate;
}

/* --------------------------------------------------------- TRANSFORM RIGS */
/* Pointer budgets from the brief: filmstrip 3px, heading 2px, background 1px.
   Each carrier spans the section so the percentages inside it resolve against
   the section box — which is why they are pointer-transparent, with `auto`
   restored on the frames alone. A full-bleed carrier swallowing a hover is the
   bug Section 03 shipped; Sections 04 and 05 were built already knowing it. */
.reels__index-pointer,
.reels__copy-pointer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    will-change: transform;
}

.reels__copy-pointer {
    z-index: 3;
}

.reels__index-pointer {
    z-index: 2;
}

/* ------------------------------------------------------------ LEFT COLUMN */
.reels__index {
    position: absolute;
    top: 15%;
    left: 2.346%;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-reels-index);
    line-height: 1;
    color: var(--index-05);
}

.reels__title {
    position: absolute;
    top: 14.2%;
    left: 7.136%;
    /* Wide enough that "ONE IDENTITY." — the longest line, 28.3% of the
       section — never wraps. It runs past the strip's left edge at 32.55%, as
       it does in the comp; they clear each other vertically, not horizontally. */
    width: 36%;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-reels);
    /* Spec: 100px leading on a 100px font. The comp's pitch is 4% looser, so
       line-height 1 plus the 0.04em of mask padding below lands on the comp
       exactly — the identical calibration Section 04 was verified against. */
    line-height: 1;
    letter-spacing: 0.01em;
    text-transform: uppercase;
    color: var(--paper-05);
}

/* Each line is its own mask, so the characters rise out of nothing. */
.reels__line {
    display: block;
    overflow: hidden;
    padding-bottom: 0.04em;
}

.reels__line-in {
    display: block;
}

.reels__line-in--accent {
    color: var(--lime);
}

/* ------------------------------------------------------------- FILMSTRIP */
/* The strip has to end somewhere on the LEFT — a running marquee slides its
   frames straight out over the heading otherwise. It must not end on the
   right, though: that overflow is the point, because a filmstrip that stops
   inside the frame reads as a carousel.

   So the boundary is a mask, not a clip, and it lives in the whitespace the
   comp already leaves between the heading (ending at 27.54%) and the first
   card (starting at 32.551%). Frames dissolve into the teal as they exit
   instead of being guillotined, and because the plate is a flat colour the
   fade itself is invisible — the Section 03 seam cannot happen here.

   The stops are the comp's own coordinates: opaque by 32.551%, which is
   exactly where the first card sits, so the composition still matches the
   design pixel for pixel. Masking a wrapper rather than the strip keeps the
   labels and the 1.05 hover lift inside the mask box. */
.reels__viewport {
    position: absolute;
    inset: 0;
    z-index: 1;
    -webkit-mask-image: linear-gradient(to right, transparent 28.2%, #000 32.551%);
    mask-image: linear-gradient(to right, transparent 28.2%, #000 32.551%);
}

.reels__strip {
    position: absolute;
    top: 32.277%;
    left: 32.551%;
    right: 0;
    height: 36.311%;
    z-index: 1;
}

/* Two carriers, two jobs, so no element ever has two transform sources:
   the outer one takes the scroll-linked drift, the inner one the 3px pointer
   budget, and the track itself carries only the loop. */
.reels__strip-scroll,
.reels__strip-pointer {
    height: 100%;
    will-change: transform;
}

.reels__track {
    display: flex;
    align-items: stretch;
    gap: var(--campaign-gap);
    height: 100%;
    width: max-content;
    will-change: transform;
}

.campaign {
    position: relative;
    flex: 0 0 auto;
    margin: 0;
}

/* Each frame keeps its own file's shape at one shared height. The comp's four
   widths (161/162/159/156) are exactly these ratios at a common 126px. */
.campaign__frame {
    display: block;
    height: 100%;
    overflow: hidden;
    background: rgba(255, 255, 255, 0.04);
    /* Pointer events live here, not on the carriers above. */
    pointer-events: auto;
    transition:
        transform var(--campaign-dur) var(--campaign-ease),
        filter var(--campaign-dur) var(--campaign-ease);
}

.campaign--model .campaign__frame {
    aspect-ratio: 226 / 175;
}

.campaign--beauty .campaign__frame {
    aspect-ratio: 226 / 175;
}

.campaign--lifestyle .campaign__frame {
    aspect-ratio: 223 / 175;
}

.campaign--flim .campaign__frame {
    aspect-ratio: 219 / 175;
}

/* The zoom is a separate element from the frame precisely so the frame's 0.45s
   lift and the image's 0.8s push can run on different curves without one
   overwriting the other's transform. */
.campaign__zoom {
    display: block;
    width: 100%;
    height: 100%;
    transition: transform var(--zoom-dur) var(--zoom-ease);
    will-change: transform;
}

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

.campaign__label {
    display: block;
    margin: 0.81em 0 0;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: var(--fs-campaign-label);
    line-height: 1;
    letter-spacing: 0;
    color: var(--paper-05);
}

/* ------------------------------------------------------------------ HOVER */
.campaign:hover,
.campaign:focus-within {
    z-index: 5;
}

.campaign:hover .campaign__frame,
.campaign:focus-within .campaign__frame {
    transform: scale(1.05);
    filter:
        drop-shadow(0 26px 48px rgba(0, 12, 16, 0.55))
        brightness(1.06);
}

/* 1 -> 1.08, the brief's figure. Sub-pixel, so it reads as the image settling
   into the frame rather than as a zoom. */
.campaign:hover .campaign__zoom,
.campaign:focus-within .campaign__zoom {
    transform: scale(1.08);
}

.campaign__frame:focus-visible {
    outline: 2px solid var(--lime);
    outline-offset: 4px;
}

/* ============================================================== RESPONSIVE */
/* The composition holds its exact proportions to 1024. Below that the section
   stops being a fixed-aspect canvas: the heading moves above the strip, and
   the strip becomes a real swipeable gallery — native overflow scrolling with
   snap points, not a marquee, because an auto-moving strip you cannot catch is
   hostile on a touch screen. reels.js gates the loop off at the same width. */

@media (max-width: 1440px) {

    .reels {
        min-height: 380px;
    }
}

@media (max-width: 1199px) {

    .reels {
        aspect-ratio: auto;
        min-height: 0;
        padding: clamp(3.5rem, 9vw, 6rem) 0 clamp(3.5rem, 9vw, 6rem);
    }

    .reels__index-pointer,
    .reels__copy-pointer,
    .reels__viewport {
        position: static;
        inset: auto;
        display: contents;
    }

    /* No marquee below this width, so nothing to dissolve. */
    .reels__viewport {
        -webkit-mask-image: none;
        mask-image: none;
    }

    .reels__index,
    .reels__title {
        position: static;
        width: auto;
        margin: 0 clamp(1.25rem, 4vw, 3rem);
    }

    .reels__index {
        display: block;
        margin-bottom: 0.35em;
    }

    .reels__strip {
        position: static;
        height: auto;
        margin-top: clamp(2rem, 5vw, 3rem);
        /* The swipeable gallery. Padding either side keeps the first and last
           frame off the screen edge; scroll-padding keeps the snap honest. */
        overflow-x: auto;
        overflow-y: hidden;
        scroll-snap-type: x mandatory;
        scroll-padding-left: clamp(1.25rem, 4vw, 3rem);
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
    }

    .reels__strip::-webkit-scrollbar {
        display: none;
    }

    .reels__strip-scroll,
    .reels__strip-pointer {
        height: auto;
    }

    .reels__track {
        height: auto;
        padding: 0 clamp(1.25rem, 4vw, 3rem);
        gap: clamp(0.75rem, 2.5vw, 1.5rem);
    }

    .campaign {
        scroll-snap-align: start;
    }

    .campaign__frame {
        height: clamp(180px, 26vw, 260px);
    }

    :where(.reels) {
        --fs-reels: clamp(2.4rem, 8.4vw, 5.5rem);
        --fs-reels-index: clamp(1.4rem, 4.9vw, 3.2rem);
        --fs-campaign-label: clamp(0.8rem, 1.9vw, 1.15rem);
    }
}

@media (max-width: 767px) {

    .campaign__frame {
        height: clamp(170px, 38vw, 240px);
    }

    :where(.reels) {
        --fs-reels: clamp(2.1rem, 11.4vw, 4.5rem);
        --fs-reels-index: clamp(1.25rem, 6.7vw, 2.6rem);
        --fs-campaign-label: clamp(0.78rem, 3.2vw, 1.05rem);
    }
}

@media (max-width: 560px) {

    .campaign__frame {
        height: clamp(160px, 46vw, 220px);
    }

    :where(.reels) {
        --fs-reels: clamp(1.9rem, 12.6vw, 3.4rem);
    }
}

/* ----------------------------------------------------- TOUCH / NO HOVER */
/* On a touch screen the hover state fires on tap and sticks. The frame keeps
   its resting size; swiping is the interaction. */
@media (hover: none) {

    .campaign:hover .campaign__frame {
        transform: none;
        filter: none;
    }

    .campaign:hover .campaign__zoom {
        transform: none;
    }
}

/* ------------------------------------------------------------ ARMED STATES */
/* Applied only once GSAP is confirmed (`is-armed` goes on the <html> in
   main.js). Without JS the section renders complete and readable. */
.is-armed .reels__title .char {
    display: inline-block;
    transform: translate3d(0, 40px, 0);
    opacity: 0;
    will-change: transform, opacity;
}

.is-armed .reels__index,
.is-armed .campaign {
    opacity: 0;
}

/* --------------------------------------------------------- REDUCED MOTION */
/* Every armed state above is mirrored here, so nothing is left invisible
   waiting for a timeline that will not run. The loop, the parallax, the hover
   zoom and the frame lift all go with it — reels.js skips the rest. */
@media (prefers-reduced-motion: reduce) {

    .is-armed .reels__title .char {
        transform: none;
        opacity: 1;
    }

    .is-armed .reels__index,
    .is-armed .campaign {
        opacity: 1;
    }

    .campaign:hover .campaign__frame,
    .campaign:focus-within .campaign__frame {
        transform: none;
        filter: brightness(1.06);
    }

    .campaign:hover .campaign__zoom,
    .campaign:focus-within .campaign__zoom {
        transform: none;
    }
}

/* ============================================================================
   SECTION 05 — SCRUBBED HEADING REVEAL
   Appended only; nothing above this line changed. Same treatment Section 04
   received. No position, size, colour or spacing is altered.
   ========================================================================== */

/* The armed pre-state above translates .char 40px down at opacity 0, which
   belonged to the old line-rise entrance. The scrubbed reveal sets its own
   start state (opacity 0.4, x -7) the moment it builds, so that pre-state
   would only fight it — and would leave the type invisible if SplitText ever
   failed to load. Neutralised here. */
.is-armed .reels__title .char {
    transform: none;
    opacity: 1;
}

/* The chars shift x:-7 -> 0. .reels__line owns `overflow: hidden` for the old
   entrance, which would clip that travel at the left edge of the first
   character of every line. Released — the reveal is opacity plus a 7px nudge,
   nothing that needs masking. The element's box, and so the layout, is
   unchanged. */
.reels__line {
    overflow: visible;
}

/* SplitText wraps each visual line in .split-line. */
.reels__title .split-line {
    display: block;
    overflow: visible;
}

.reels__title .split-line div,
.reels__title .split-line span {
    will-change: transform, opacity;
}

/* Nothing may stay dimmed if the timeline never runs. */
@media (prefers-reduced-motion: reduce) {

    .reels__title .split-line div,
    .reels__title .split-line span {
        transform: none !important;
        opacity: 1 !important;
    }
}


/* ############################################################################
   SECTION-06.CSS
   Section 06.
   ######################################################################## */

/* ============================================================================
   SECTION-06.CSS — SERVICES
   ========================================================================== */

/* ---------------------------------------------------------------- TOKENS */
.services {
    /* 100 / 62.6 / 20 / 18 / 16 px at the 1512 reference, in proportion to 1920. */
    --fs-services: clamp(2.4rem, 6.6138vw, 127px);
    --fs-services-index: clamp(1.6rem, 4.1402vw, 79px);
    --fs-service-num: clamp(0.85rem, 1.3228vw, 25px);
    --fs-service-title: clamp(0.8rem, 1.1905vw, 23px);
    --fs-service-desc: clamp(0.75rem, 1.0582vw, 20px);

    --cream: #F5EADF;
    --ink-06: #062D35;
    /* Sampled off the comp. Like Section 05's index it is a flat warm grey, not
       an opacity of the ink — the channels do not fit any single alpha. */
    --index-06: #9D8C8D;

    --service-ease: cubic-bezier(0.22, 1, 0.36, 1);
    --service-dur: 0.45s;
}

/* ----------------------------------------------------------------- SHELL */
.services {
    position: relative;
    width: 100%;
    aspect-ratio: 1020 / 341;
    min-height: 380px;
    overflow: hidden;
    background: var(--cream);
    isolation: isolate;
}

/* --------------------------------------------------------- TRANSFORM RIGS */
/* Pointer budgets from the brief: images 4px, heading 2px, columns 2px. Each
   carrier spans the section so the percentages inside resolve against the
   section box, which is why they are pointer-transparent. */
.services__copy-scroll,
.services__copy-pointer,
.services__index-pointer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    will-change: transform;
}

.services__copy-scroll,
.services__copy-pointer {
    z-index: 3;
}

.services__index-pointer {
    z-index: 3;
}

/* ------------------------------------------------------- THE COLUMN RULES */
/* The brief asks for a 0.7 background layer. On a flat #F5EADF plate there is
   nothing to displace — except these, which are the only thing the background
   actually contains. They are overscanned in vh so the 0.7 travel (±15vh)
   cannot drag either end into view, the lesson from the Section 02/03 colour
   bands. */
.services__rules {
    position: absolute;
    top: -20vh;
    bottom: -20vh;
    left: 0;
    right: 0;
    z-index: 0;
    pointer-events: none;
    will-change: transform;
}

.services__rule {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 1px;
    background: rgba(6, 45, 53, 0.09);
}

/* One at each column's left edge, as the comp has them — and none at the
   section's right edge, which the comp also does not have. */
.services__rule:nth-child(1) { left: 32.157%; }
.services__rule:nth-child(2) { left: 49.118%; }
.services__rule:nth-child(3) { left: 66.078%; }
.services__rule:nth-child(4) { left: 83.039%; }

/* ------------------------------------------------------------ LEFT COLUMN */
.services__index {
    position: absolute;
    top: 12.70%;
    left: 2.549%;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-services-index);
    line-height: 1;
    color: var(--index-06);
}

.services__title {
    position: absolute;
    top: 18.16%;
    left: 7.206%;
    /* "ATTENTION." is the longer line at 23.6% of the section; 26% clears it
       without reaching the first column rule at 32.157%. */
    width: 26%;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-services);
    line-height: 1;
    letter-spacing: 0.01em;
    text-transform: uppercase;
    color: var(--ink-06);
}

.services__line {
    display: block;
    overflow: hidden;
    padding-bottom: 0.04em;
}

.services__line-in {
    display: block;
}

/* ========================================================== SERVICE COLUMNS */
/* The comp's four rules sit at 328 / 497.5 / 676.5 / 847 — pitches of 169.5,
   179 and 170.5, which is not the "perfect alignment" the brief asks for. Four
   equal columns spanning the same total (328 to the section's right edge, 173
   each) reproduce the outer bounds exactly and remove the drift; the first and
   last rule land on the comp to the pixel. */
.services__grid {
    position: absolute;
    inset: 0;
    z-index: 1;
}

.service {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 16.961%;
    /* The card is a link. Nothing inside it may steal the pointer, or the
       hovered column would change as the cursor crossed a child. */
    text-decoration: none;
    color: inherit;
    -webkit-tap-highlight-color: transparent;
}

.service--virtual   { left: 32.157%; }
.service--campaigns { left: 49.118%; }
.service--ai        { left: 66.078%; }
.service--flim      { left: 83.039%; }

/* One transform source each: __enter takes the GSAP entrance, __lift the CSS
   hover. They are separate elements precisely so neither can clobber the
   other — the discipline every section since 01 has followed. */
.service__enter,
.service__lift {
    position: absolute;
    inset: 0;
}

.service__lift {
    transition:
        transform var(--service-dur) var(--service-ease),
        background-color var(--service-dur) var(--service-ease),
        box-shadow var(--service-dur) var(--service-ease);
}

/* ------------------------------------------------------------------- HEAD */
.service__head {
    position: absolute;
    top: 8.14%;
    left: 7.593%;
    right: 1.593%;
    z-index: 2;
}

.service__num {
    display: block;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-service-num);
    line-height: 1;
    color: var(--ink-06);
    transition: color var(--service-dur) var(--service-ease);
}

/* The comp shows the number and the title at the SAME cap height — 10px each.
   The brief's type block asks for a 28px number against an 18px title, which
   would stand the number half again as tall as the title and does not match
   the design it also asks to match exactly. Taken as ratios the brief's own
   figures settle it: 18/100 is within 5% of the comp's title-to-heading ratio,
   while 28/100 is 40% off the comp's number-to-heading ratio. So the title
   keeps the brief's 18px and the number is sized from the comp instead. */
.service__title {
    display: block;
    margin: 1.661em 0 0;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 500;
    font-size: var(--fs-service-title);
    /* The comp's 23px pitch on this font size. */
    line-height: 1.89;
    /* Measured back from all eight rendered lines against the comp's ink
       widths, then refined once from the render: the comp sets this label
       wider than Inter 500 does at this size, by 0.097em per gap. */
    letter-spacing: 0.097em;
    text-transform: uppercase;
    color: var(--ink-06);
    transition: color var(--service-dur) var(--service-ease);
}

/* The comp breaks every title after its first word — VIRTUAL / IDENTITIES,
   AI / INFLUENCER — which is a semantic break, not where the column happens to
   run out. Authored as two blocks so it breaks there at every width instead of
   wherever the measure lands. */
.service__title-line {
    display: block;
}

/* Ink at 29%, measured: the channels fit that one alpha to within a unit.
   40px wide at the 1512 reference, the brief's figure. */
.service__rule {
    display: block;
    width: clamp(1.5rem, 2.6455vw, 50px);
    height: 2px;
    margin: 0.877em 0 0;
    background: var(--ink-06);
    opacity: 0.29;
    transform-origin: 0 50%;
    /* The brief allows animating this one width. scaleX(2) is the same 40->80
       and stays on the compositor, which is what its performance rules ask for
       everywhere else. */
    transition:
        transform var(--service-dur) var(--service-ease),
        opacity var(--service-dur) var(--service-ease);
}

/* ------------------------------------------------------------ DESCRIPTION */
/* The comp leaves no room between the divider and the image, so the copy
   reveals over the foot of the frame instead, behind a scrim of the section's
   own colour. It is a hover state, so the resting composition is untouched. */
.service__desc {
    position: absolute;
    left: 1.593%;
    right: 1.593%;
    bottom: 0%;
    z-index: 3;
    margin: 0;
    padding: 10px;
    padding-top: 10px;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: 13px;
    line-height: 1.45;
    color: var(--ink-06);
    /* background: linear-gradient(to bottom, rgba(245, 234, 223, 0), rgba(245, 234, 223, 0.94) 42%); */
    background: rgba(245, 234, 223, 0.94);
    opacity: 0;
    transform: translate3d(0, 12px, 0);
    transition:
        opacity var(--service-dur) var(--service-ease),
        transform var(--service-dur) var(--service-ease);
}

/* -------------------------------------------------------------- THE IMAGE */
/* Uniform 0.712x natural, bottom-flush, centred on the column and free to
   overflow it — which is exactly what the comp shows: the AI frame is 141% of
   its column wide and only its transparent margins keep it clear of the rules.
   Heights are the two source heights (288 and 305) at that scale, as a
   percentage of the section. */
.service__art {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1;
    /* Transparent PNG margins still hit-test. The column box is the hit area,
       so the art must not be one, or hovering the empty corner of one image
       would highlight its neighbour's column. */
    pointer-events: none;
}

.service--virtual .service__art,
.service--flim .service__art {
    height: 73%;
}

.service--campaigns .service__art,
.service--ai .service__art {
    height: 73%;
}

.service__art-scroll,
.service__art-pointer,
.service__art-enter {
    display: block;
    height: 100%;
    will-change: transform;
}

.service__art-zoom {
    display: block;
    height: 100%;
    transition:
        transform 0.8s var(--service-ease),
        filter var(--service-dur) var(--service-ease);
}

.service__art img {
    display: block;
    height: 100%;
    width: auto;
    max-width: none;
    position: relative;
    left: 50%;
    transform: translateX(-50%);
}

/* ------------------------------------------------------------------ HOVER */
.service:hover,
.service:focus-visible {
    z-index: 6;
}

.service:hover .service__lift,
.service:focus-visible .service__lift {
    transform: translate3d(0, -8px, 0) scale(1.03);
    /* background-color: rgba(255, 253, 250, 0.55);
    box-shadow: 0 26px 60px rgba(6, 45, 53, 0.16); */
}

.service:hover .service__rule,
.service:focus-visible .service__rule {
    transform: scaleX(2);
    opacity: 0.75;
}

.service:hover .service__desc,
.service:focus-visible .service__desc {
    opacity: 1;
    transform: translate3d(0, 0, 0);
}

.service:hover .service__art-zoom,
.service:focus-visible .service__art-zoom {
    transform: scale(1.06);
    filter: brightness(1.06) contrast(1.03);
}

.service:focus-visible {
    outline: 2px solid var(--ink-06);
    outline-offset: -2px;
}

/* ============================================================== RESPONSIVE */
/* The composition holds its exact proportions to 1024. Below that the section
   stops being a fixed-aspect canvas and the columns stack, each keeping its
   number, title, divider and frame — the editorial rhythm survives, the
   four-across grid does not. */

@media (max-width: 1440px) {

    .services {
        min-height: 420px;
    }
}

@media (max-width: 1199px) {

    .services {
        aspect-ratio: auto;
        min-height: 0;
        padding: clamp(3.5rem, 9vw, 6rem) clamp(1.25rem, 4vw, 3rem);
        padding-bottom: 0px;
    }

    /* Vertical rules belong to a four-across grid; stacked, they are noise. */
    .services__rules {
        display: none;
    }

    .services__copy-scroll,
    .services__copy-pointer,
    .services__index-pointer {
        position: static;
        inset: auto;
        display: contents;
    }

    .services__index,
    .services__title {
        position: static;
        width: auto;
        margin: 0;
    }

    .services__index {
        display: block;
        margin-bottom: 0.3em;
    }

    /* The grid stops being an absolute canvas and becomes a real one. */
    .services__grid {
        position: static;
        inset: auto;
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: clamp(1.5rem, 4vw, 3rem);
        margin-top: clamp(2.5rem, 6vw, 4rem);
    }

    .service {
        position: relative;
        top: auto;
        bottom: auto;
        left: auto !important;
        width: auto;
        /* Enough room for the frame to sit under the head without overlapping,
           now that there is no section height to anchor to. */
        padding-bottom: clamp(15rem, 34vw, 22rem);
        border-top: 1px solid rgba(6, 45, 53, 0.14);
        padding-top: clamp(1.25rem, 3vw, 2rem);
        padding-bottom: 0px !important;
    }

    .service__enter,
    .service__lift {
        position: static;
        inset: auto;
    }

    .service__head {
        position: static;
    }

    .service__art {
        height: clamp(14rem, 32vw, 21rem);
    }

    /* Touch has no hover, so the description is simply always there. */
    .service__desc {
        position: static;
        padding-top: 0;
        margin-top: 1em;
        background: none;
        opacity: 1;
        transform: none;
    }
}

@media (max-width: 767px) {

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

    .service {
        padding-bottom: clamp(13rem, 52vw, 20rem);
    }

    .service__art {
        height: clamp(12rem, 50vw, 19rem);
    }

    :where(.services) {
        --fs-services: clamp(2.1rem, 11.4vw, 4.5rem);
        --fs-services-index: clamp(1.3rem, 7vw, 2.8rem);
        --fs-service-num: clamp(0.85rem, 3.4vw, 1.3rem);
        --fs-service-title: clamp(0.8rem, 3.1vw, 1.2rem);
        --fs-service-desc: clamp(0.8rem, 3vw, 1.05rem);
    }
}

@media (max-width: 560px) {

    :where(.services) {
        --fs-services: clamp(1.9rem, 12.6vw, 3.4rem);
    }
}

/* ----------------------------------------------------- TOUCH / NO HOVER */
@media (hover: none) {

    .service:hover .service__lift {
        transform: none;
        background-color: transparent;
        box-shadow: none;
    }

    .service:hover .service__art-zoom {
        transform: none;
        filter: none;
    }

    .service:hover .service__rule {
        transform: none;
    }
}

/* ------------------------------------------------------------ ARMED STATES */
/* Applied only once GSAP is confirmed (`is-armed` goes on the <html> in
   main.js). Without JS the section renders complete and readable. */
.is-armed .services__title .char {
    display: inline-block;
    transform: translate3d(0, 40px, 0);
    opacity: 0;
    will-change: transform, opacity;
}

.is-armed .services__index,
.is-armed .service {
    opacity: 0;
}

/* --------------------------------------------------------- REDUCED MOTION */
/* Every armed state above is mirrored here, so nothing is left invisible
   waiting for a timeline that will not run. Parallax, the hover lift and the
   image zoom all go with it — services.js skips the rest. */
@media (prefers-reduced-motion: reduce) {

    .is-armed .services__title .char {
        transform: none;
        opacity: 1;
    }

    .is-armed .services__index,
    .is-armed .service {
        opacity: 1;
    }

    .service:hover .service__lift,
    .service:focus-visible .service__lift {
        transform: none;
        box-shadow: none;
    }

    .service:hover .service__art-zoom,
    .service:focus-visible .service__art-zoom {
        transform: none;
    }

    /* The description still needs to appear — it is information, not motion.
       It arrives as a plain fade with no travel. */
    .service__desc {
        transform: none;
        transition: opacity var(--service-dur) linear;
    }
}

/* ============================================================================
   SECTION 06 — HEADING REVEAL SCAFFOLD
   Appended only; nothing above this line changed. These rules exist solely so
   the shared Section 01 reveal renders correctly. No position, size, colour or
   spacing is altered — the negative margins cancel the added line-height
   exactly, so the heading occupies the same box as before.
   ========================================================================== */

/* The armed pre-state above translates .char 40px down at opacity 0, which
   belonged to the old line-rise entrance. The rotateY reveal sets its own
   start state when it builds, so that pre-state would only fight it — and
   would leave the type invisible if the split ever failed. Neutralised. */
.is-armed .services__title .char {
    transform: none;
    opacity: 1;
}

/* The glyphs are inline-block with `vertical-align: top`, pinned to the TOP of
   their line box. .services__line owns the `overflow: hidden` that masks the
   reveal, so a line box shorter than Bebas' ~1.2em glyph box shears the
   bottoms off the letters. Give the box real height, then pull the surplus
   back out so the visual line advance is unchanged. */
.services__line {
    line-height: 1.3;
    padding-bottom: 0;
    margin-top: -0.03em;
    margin-bottom: -0.3em;
}

.services__title .hero__line-inner {
    display: block;
    line-height: inherit;
}

.services__title .hero__word {
    display: inline-block;
    vertical-align: top;
    white-space: nowrap;
}

/* vertical-align: top also kills the phantom descender strip that would
   otherwise make the heading taller than the design. */
.services__title .char {
    display: inline-block;
    vertical-align: top;
    backface-visibility: hidden;
    will-change: transform, opacity;
}

/* Nothing may stay hidden if the timeline never runs. */
@media (prefers-reduced-motion: reduce) {

    .services__title .char,
    .services__title .hero__word,
    .services__title .hero__line-inner {
        transform: none !important;
        opacity: 1 !important;
        visibility: visible !important;
    }
}


/* ############################################################################
   SECTION-07.CSS
   Section 07.
   ######################################################################## */

/* ============================================================================
   SECTION-07.CSS — FROM AN IDEA TO AN ICON
   ========================================================================== */

/* ---------------------------------------------------------------- TOKENS */
.journey {
    /* 100 / 62.3 / 24 px at the 1512 reference, in proportion to 1920. */
    --fs-journey: clamp(2.4rem, 6.6138vw, 127px);
    --fs-journey-index: clamp(1.6rem, 4.1204vw, 79px);
    --fs-stage: clamp(0.9rem, 1.5873vw, 30px);

    --violet: #BCA8FF;
    --ink-07: #062D35;
    /* The same flat warm grey Section 05's index samples to. */
    --index-07: #817E7E;

    --stage-ease: cubic-bezier(0.22, 1, 0.36, 1);
    --stage-dur: 0.45s;
}

/* ----------------------------------------------------------------- SHELL */
.journey {
    position: relative;
    width: 100%;
    aspect-ratio: 1025 / 330;
    min-height: 340px;
    overflow: hidden;
    background: var(--violet);
    isolation: isolate;
}

/* --------------------------------------------------------- TRANSFORM RIGS */
/* Pointer budgets from the brief: images 3px, heading 2px, background 1px.
   Each carrier spans the section so the percentages inside resolve against the
   section box, which is why they are pointer-transparent — a full-bleed
   carrier swallowing a hover is the bug Section 03 shipped. */
.journey__copy-scroll,
.journey__copy-pointer,
.journey__index-pointer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    will-change: transform;
}

.journey__copy-scroll,
.journey__copy-pointer,
.journey__index-pointer {
    z-index: 3;
}

/* ------------------------------------------------------------ LEFT COLUMN */
.journey__index {
    position: absolute;
    top: 12.31%;
    left: 2.732%;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-journey-index);
    line-height: 1;
    color: var(--index-07);
}

.journey__title {
    position: absolute;
    top: 20.88%;
    left: 7.66%;
    /* "FROM AN IDEA" is the longer line at 28.7% of the section. 32% clears it
       and still stops short of the Define stage at 40.98%. */
    width: 32%;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-journey);
    line-height: 1;
    letter-spacing: 0.01em;
    text-transform: uppercase;
    color: var(--ink-07);
}

.journey__line {
    display: block;
    overflow: hidden;
    padding-bottom: 0.04em;
}

.journey__line-in {
    display: block;
}

/* ================================================================= STAGES */
.journey__stages {
    position: absolute;
    inset: 0;
    z-index: 2;
}

.stage {
    position: absolute;
    /* Height is content — label, gap, frames box — so the hover target is the
       stage and not a slab of empty section. */
    color: inherit;
}

/* Left and top come straight off the comp; width is the frames box's width,
   which the label then sits inside. */
.stage--define {
    left: 40.976%;
    top: 22.17%;
    width: 14.537%;
}

.stage--explore {
    left: 58.439%;
    top: 34.29%;
    width: 15.610%;
}

.stage--launch {
    left: 77.268%;
    top: 34.29%;
    width: 19.610%;
}

/* One transform source each: __enter takes the scrubbed reveal, __active the
   scrubbed active-stage highlight, __lift the CSS hover. Three elements
   because all three can be in flight at once. */
.stage__enter,
.stage__active,
.stage__lift {
    display: block;
    will-change: transform, opacity;
}

.stage__active {
    transition: opacity var(--stage-dur) var(--stage-ease);
        opacity: 1 !important;
}

.stage__lift {
    position: relative;
    transition:
        transform var(--stage-dur) var(--stage-ease),
        filter var(--stage-dur) var(--stage-ease);
}

/* The label sits ABOVE its own frames in paint order. The brief gives the
   stages 1.0 and the images 1.15, so the art necessarily drifts against its
   label — over a section this short that is ±71px, enough to swallow the word
   entirely. Declaring the order turns that into depth: the sketch passes
   behind "Define" instead of over it. */
.stage__label {
    position: relative;
    z-index: 3;
    display: block;
    margin: 0;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 500;
    font-size: var(--fs-stage);
    line-height: 1;
    letter-spacing: 0;
    color: var(--ink-07);
}

/* ---------------------------------------------------------- THE FRAME MAP */
.stage__frames {
    position: relative;
    z-index: 1;
    width: 100%;
}

.stage--define .stage__frames {
    aspect-ratio: 149 / 173;
    margin-top: calc(var(--fs-stage) * 0.955);
}

.stage--explore .stage__frames {
    aspect-ratio: 160 / 116;
    margin-top: calc(var(--fs-stage) * 2.004);
}

.stage--launch .stage__frames {
    aspect-ratio: 201 / 129;
    margin-top: calc(var(--fs-stage) * 1.083);
}

/* Parallax and pointer live on their own inset carriers, so the frames'
   percentages still resolve against the aspect box above. */
.stage__frames-scroll,
.stage__frames-pointer {
    position: absolute;
    inset: 0;
    will-change: transform;
}

.shot {
    position: absolute;
    margin: 0;
    overflow: hidden;
    background: rgba(6, 45, 53, 0.06);
    box-shadow: 0 6px 18px rgba(6, 45, 53, 0.14);
    transition: box-shadow var(--stage-dur) var(--stage-ease);
}

.shot__float {
    display: block;
    width: 100%;
    height: 100%;
    will-change: transform;
}

.shot img {
    display: block;
    width: 100%;
    height: 100%;
    /* Every frame is a different crop of the same supplied file: `cover` plus
       a per-frame object-position is what makes thirteen frames out of three
       photographs without inventing an asset. */
    object-fit: cover;
    /* The project's base reset caps images at 100% of their container; these
       are meant to fill a box that is often a different shape. */
    max-width: none;
    transition: filter var(--stage-dur) var(--stage-ease);
}

/* -------------------------------------------------- DEFINE — one sketch */
/* define.png carries its own 1px dark border, so the frame needs none. */
.shot--d1 { left: 0; top: 0; width: 100%; height: 100%; }
.shot--d1 img { object-position: 50% 50%; }

/* --------------------------- EXPLORE — six thumbnails and one selection */
.shot--e1 { left: 0;       top: 0;       width: 21.25%; height: 46.552%; }
.shot--e2 { left: 29.063%; top: 0;       width: 21.25%; height: 46.552%; }
.shot--e3 { left: 0;       top: 51.724%; width: 21.25%; height: 22.414%; }
.shot--e4 { left: 29.063%; top: 51.724%; width: 21.25%; height: 22.414%; }
.shot--e5 { left: 0;       top: 77.586%; width: 21.25%; height: 22.414%; }
.shot--e6 { left: 29.063%; top: 77.586%; width: 21.25%; height: 22.414%; }
.shot--e7 { left: 56.25%;  top: 14.655%; width: 43.75%; height: 84.483%; }

.shot--e1 img { object-position: 30% 40%; }
.shot--e2 img { object-position: 72% 35%; }
.shot--e3 img { object-position: 50% 22%; }
.shot--e4 img { object-position: 50% 58%; }
.shot--e5 img { object-position: 32% 72%; }
.shot--e6 img { object-position: 68% 46%; }
.shot--e7 img { object-position: 50% 45%; }

/* ------------------------- LAUNCH — the hero and four campaign variations */
.shot--l1 { left: 0;       top: 0;       width: 52.736%; height: 100%; }
.shot--l2 { left: 55.721%; top: 4.651%;  width: 16.418%; height: 37.209%; }
.shot--l3 { left: 74.627%; top: 19.380%; width: 25.373%; height: 39.535%; }
.shot--l4 { left: 54.229%; top: 50.388%; width: 20.398%; height: 49.612%; }
.shot--l5 { left: 79.104%; top: 62.791%; width: 20.896%; height: 37.209%; }

.shot--l1 img { object-position: 50% 45%; }
.shot--l2 img { object-position: 56% 28%; }
.shot--l3 img { object-position: 50% 30%; }
.shot--l4 img { object-position: 38% 62%; }
.shot--l5 img { object-position: 62% 48%; }

/* The hero is the finished identity: it sits above its own variations. */
.shot--l1,
.shot--e7 {
    z-index: 2;
}

/* ------------------------------------------------------------- CONNECTORS */
/* ------------------------------------------------------------------ HOVER */
.stage {
    cursor: pointer;
}

.stage:hover,
.stage:focus-within {
    z-index: 6;
}

.stage:hover .stage__lift,
.stage:focus-within .stage__lift {
    transform: scale(1.04);
}

.stage:hover .shot,
.stage:focus-within .shot {
    box-shadow: 0 20px 44px rgba(6, 45, 53, 0.3);
}

.stage:hover .shot img,
.stage:focus-within .shot img {
    filter: brightness(1.06) contrast(1.02);
}

/* "Animate border or outline" — a hairline that eases outward rather than a
   border-width change, so it stays on transform and opacity. */
.stage__outline {
    position: absolute;
    inset: -0.6em;
    /* border: 1px solid rgba(6, 45, 53, 0.38); */
    opacity: 0;
    transform: scale(0.985);
    pointer-events: none;
    transition:
        opacity var(--stage-dur) var(--stage-ease),
        transform var(--stage-dur) var(--stage-ease);
}

.stage:hover .stage__outline,
.stage:focus-within .stage__outline {
    opacity: 1;
    transform: scale(1);
}

.stage:focus-visible {
    outline: 2px solid var(--ink-07);
    outline-offset: 8px;
}

/* ============================================================== RESPONSIVE */
/* The composition holds its exact proportions to 1024. Below that the section
   stops being a fixed-aspect canvas and the three stages stack in order —
   Define, Explore, Launch — each keeping its own internal frame map, because
   those percentages are of the stage's own box and not of the section. */

@media (max-width: 1440px) {

    .journey {
        min-height: 400px;
    }
}

@media (max-width: 1199px) {

    .journey {
        aspect-ratio: auto;
        min-height: 0;
        padding: clamp(3.5rem, 9vw, 6rem) clamp(1.25rem, 4vw, 3rem);
    }

    .journey__copy-scroll,
    .journey__copy-pointer,
    .journey__index-pointer,
    .journey__stages {
        position: static;
        inset: auto;
        display: contents;
    }

    .journey__index,
    .journey__title {
        position: static;
        width: auto;
        margin: 0;
    }

    .journey__index {
        display: block;
        margin-bottom: 0.3em;
    }

    .stage {
        position: relative;
        left: auto !important;
        top: auto !important;
        width: auto;
        max-width: 34rem;
        margin-top: clamp(2.5rem, 7vw, 4.5rem);
    }

    /* The stage keeps its own frame map, just at the width it is given. */
    .stage--define .stage__frames,
    .stage--explore .stage__frames,
    .stage--launch .stage__frames {
        margin-top: clamp(0.9rem, 2.4vw, 1.5rem);
    }

    .stage__label {
        margin-bottom: 0;
    }

    :where(.journey) {
        --fs-journey: clamp(2.4rem, 8.4vw, 5.5rem);
        --fs-journey-index: clamp(1.4rem, 4.9vw, 3.2rem);
        --fs-stage: clamp(0.95rem, 2.2vw, 1.4rem);
    }
}

@media (max-width: 767px) {

    .stage {
        max-width: none;
    }

    :where(.journey) {
        --fs-journey: clamp(2.1rem, 11.4vw, 4.5rem);
        --fs-journey-index: clamp(1.25rem, 6.7vw, 2.6rem);
        --fs-stage: clamp(0.9rem, 4vw, 1.3rem);
    }
}

@media (max-width: 560px) {

    :where(.journey) {
        --fs-journey: clamp(1.9rem, 12.6vw, 3.4rem);
    }
}

/* ----------------------------------------------------- TOUCH / NO HOVER */
@media (hover: none) {

    .stage:hover .stage__lift {
        transform: none;
    }

    .stage:hover .stage__outline {
        opacity: 0;
    }

    .stage:hover .shot img {
        filter: none;
    }
}

/* ------------------------------------------------------------ ARMED STATES */
/* Applied only once GSAP is confirmed (`is-armed` goes on the <html> in
   main.js). Without JS the section renders complete and readable — the whole
   journey is visible, it simply does not unfold. */
.is-armed .journey__title .char {
    display: inline-block;
    transform: translate3d(0, 40px, 0);
    opacity: 0;
    will-change: transform, opacity;
}

.is-armed .journey__index,
.is-armed .stage__enter,
.is-armed .stage--explore .shot,
.is-armed .stage--launch .shot {
    opacity: 0;
}

/* --------------------------------------------------------- REDUCED MOTION */
/* Every armed state above is mirrored here. Parallax, floating and the
   scroll-linked progression all go with it — journey.js replaces the scrubbed
   master with three plain fades. */
@media (prefers-reduced-motion: reduce) {

    .is-armed .journey__title .char {
        transform: none;
        opacity: 1;
    }

    .is-armed .journey__index,
    .is-armed .stage__enter,
    .is-armed .stage--explore .shot,
    .is-armed .stage--launch .shot {
        opacity: 1;
    }

    .stage:hover .stage__lift,
    .stage:focus-within .stage__lift {
        transform: none;
    }

    .stage__outline {
        transform: none;
    }
}

/* ============================================================================
   SECTION 07 — HEADING REVEAL + PER-COLUMN BLOOM
   Appended only; nothing above this line changed. No position, size, colour or
   spacing is altered — the negative margins cancel the added line-height
   exactly, so the heading occupies the same box as before.
   ========================================================================== */

/* The armed pre-state translates .char 40px down at opacity 0, which belonged
   to the old line-rise entrance. The rotateY reveal sets its own start state
   when it builds, so that would only fight it — and would leave the type
   invisible if the split ever failed. Neutralised. */
.is-armed .journey__title .char {
    transform: none;
    opacity: 1;
}

/* Glyphs are inline-block with `vertical-align: top`, pinned to the TOP of
   their line box. .journey__line owns the `overflow: hidden` that masks the
   reveal, so a line box shorter than Bebas' ~1.2em glyph box shears the
   bottoms off the letters. Give the box real height, then pull the surplus
   back out so the visual line advance is unchanged. */
.journey__line {
    line-height: 1.3;
    padding-bottom: 0;
    margin-top: -0.03em;
    margin-bottom: -0.3em;
}

.journey__title .hero__line-inner {
    display: block;
    line-height: inherit;
}

.journey__title .hero__word {
    display: inline-block;
    vertical-align: top;
    white-space: nowrap;
}

.journey__title .char {
    display: inline-block;
    vertical-align: top;
    backface-visibility: hidden;
    will-change: transform, opacity;
}

/* The shots now travel in 3D out of their column's centre, so each needs a
   preserved 3D context. Perspective itself is set per column from JS. */
.shot {
    backface-visibility: hidden;
    will-change: transform, opacity;
}

/* Nothing may stay hidden if a timeline never runs. */
@media (prefers-reduced-motion: reduce) {

    .journey__title .char,
    .journey__title .hero__word,
    .journey__title .hero__line-inner {
        transform: none !important;
        opacity: 1 !important;
        visibility: visible !important;
    }

    .shot {
        transform: none !important;
        opacity: 1 !important;
    }
}

/* ============================================================================
   SECTION 07 — THE SCROLL-LINKED IMAGE RIG REMOVED
   Appended last. Nothing above this line is edited, and no other section is
   touched.

   journey.js no longer builds the scrubbed master that flew each shot in from
   its column's centre, so the armed pre-state it animated FROM has nothing
   left to unwind it. Cleared here for every width, desktop included, which is
   what makes the three stages simply present rather than assembling on scroll.

   The heading's own entrance is a separate rig and keeps its pre-state.
   ========================================================================== */
.is-armed .journey__index,
.is-armed .stage__enter,
.is-armed .stage--explore .shot,
.is-armed .stage--launch .shot,
.is-armed .stage--define .shot {
    opacity: 1;
}

/* The float loop and the parallax carriers are already inert in this section;
   this only stops either one from being promised a layer it never uses. */
.stage__frames-scroll,
.stage__frames-pointer,
.shot,
.shot__float {
    will-change: auto;
}


/* ############################################################################
   SECTION-08.CSS
   Section 08.
   ######################################################################## */

/* ============================================================================
  
   ========================================================================== */

/* ---------------------------------------------------------------- TOKENS */
.package {
    /* 100 / 62.6 / 20 / 21 px at the 1512 reference, in proportion to 1920. */
    --fs-package: clamp(2.4rem, 6.6138vw, 127px);
    --fs-package-index: clamp(1.6rem, 4.1402vw, 79px);
    --fs-package-lede: clamp(0.85rem, 1.3228vw, 25px);
    --fs-item-label: clamp(0.8rem, 1.3889vw, 26px);

    /* The brief specifies #FFF9F3; the comp is filled with #F5EADF, which is
       Section 06's cream — the artboard it was almost certainly built from.
       Four of the five colours the brief lists for this section match the comp
       exactly, so the brief is the reliable document and this is its value.
       One line to flip it back. */
    --paper-08: #FFF9F3;
    --teal-08: #073F4C;
    --ink-08: #092427;
    --label-08: #062D35;
    --btn-08: #001E1D;
    /* Sampled off the comp — a flat warm grey, as in every section's index. */
    --index-08: #BCABA5;

    --pkg-ease: cubic-bezier(0.22, 1, 0.36, 1);
    --pkg-dur: 0.45s;
}

/* ----------------------------------------------------------------- SHELL */
.package {
    position: relative;
    width: 100%;
    aspect-ratio: 1021 / 600;
    min-height: 520px;
    overflow: hidden;
    background: var(--paper-08);
    isolation: isolate;
}

/* --------------------------------------------------------- TRANSFORM RIGS */
/* Pointer budgets from the brief: assets 4px, heading 2px, background 1px.
   Each carrier spans the section so the percentages inside resolve against the
   section box, which is why they are pointer-transparent — a full-bleed
   carrier swallowing a hover is the bug Section 03 shipped. */
.package__copy-scroll,
.package__copy-pointer,
.package__index-pointer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 4;
    will-change: transform;
}

/* The copy column's own controls have to be clickable again. */
.package__copy-pointer .package__cta {
    pointer-events: auto;
}

/* ------------------------------------------------------------ LEFT COLUMN */
.package__index {
    position: absolute;
    top: 4.38%;
    left: 2.351%;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-package-index);
    line-height: 1;
    color: var(--index-08);
}

.package__title {
    position: absolute;
    top: 9.15%;
    left: 7.591%;
    /* "BRAND WORLD" is the longest line at 29.5% of the section; 38% clears it
       and stops well short of the Identity Bible at 42.3%. */
    width: 38%;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-package);
    line-height: 1;
    letter-spacing: 0.01em;
    text-transform: uppercase;
    color: var(--teal-08);
}

.package__line {
    display: block;
    overflow: hidden;
    padding-bottom: 0.04em;
}

.package__line-in {
    display: block;
}

.package__rule {
    position: absolute;
    top: 44.667%;
    left: 7.640%;
    width: 8.129%;
    height: 1px;
    background: var(--ink-08);
    transform-origin: 0 50%;
}

.package__lede {
    position: absolute;
    top: 48.71%;
    left: 7.640%;
    /* The comp's two lines run 187px and 182px — an 18.3% column. 22% holds
       them without letting line one reach the Campaign Assets label at
       35.65%, which a measure-width break did. */
    width: 22%;
    margin: 0;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: var(--fs-package-lede);
    /* The brief says line-height 100%, which on a two-line paragraph would set
       the lines solid. The comp's pitch is 20px on a 20px font's cap — 1.45 —
       and that is what a paragraph needs to be readable. */
    line-height: 1.45;
    color: var(--ink-08);
}

/* The break after "needs" is the comp's, not the measure's — authored so it
   falls in the same place at every width. */
.package__lede-line {
    display: block;
}

/* --------------------------------------------------------------- THE CTA */
/* Same shape and behaviour as the hero's primary button, in this section's
   palette: lift, a hair of scale, a shadow that grows, and a background that
   deepens. */
.package__cta {
    position: absolute;
    top: 65.833%;
    left: 6.758%;
    width: 21.254%;
    height: 8.167%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    padding: 0 1em;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: var(--fs-package-lede);
    letter-spacing: 0.055em;
    text-transform: uppercase;
    text-decoration: none;
    white-space: nowrap;
    color: #FFFFFF;
    background: var(--btn-08);
    border: 0;
    cursor: pointer;
    transition:
        transform var(--pkg-dur) var(--pkg-ease),
        background-color var(--pkg-dur) var(--pkg-ease),
        box-shadow var(--pkg-dur) var(--pkg-ease);
}

.package__cta:hover,
.package__cta:focus-visible {
    transform: translate3d(0, -3px, 0) scale(1.02);
    background: #001413;
    box-shadow: 0 18px 36px rgba(0, 30, 29, 0.32);
}

.package__cta:focus-visible {
    outline: 2px solid var(--teal-08);
    outline-offset: 4px;
}

/* ================================================================== ITEMS */
/* The group carrier takes the brief's 1.05; the three front-row items take a
   further 1.10 on their own carriers. Travel is (speed - 1), so nesting them
   adds 0.05 + 0.10 = 0.15 — exactly the 1.15 the brief asks of the foreground,
   without either element carrying two transform sources. */
.package__assets {
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;
    will-change: transform;
}

.item {
    position: absolute;
    /* The box is the artwork's own rect. Pointer events are re-enabled only on
       the ink below — these files carry wide transparent margins that overlap
       their neighbours, and a transparent corner must never steal a hover. */
    pointer-events: none;
}

.item__enter,
.item__lift,
.item__depth {
    display: block;
    width: 100%;
    height: 100%;
    will-change: transform, opacity;
}

.item__lift {
    transition:
        transform var(--pkg-dur) var(--pkg-ease),
        filter var(--pkg-dur) var(--pkg-ease);
}

/* Hover is applied by a class rather than :hover, because the element that
   receives the pointer is a descendant of the element that has to scale — and
   because pausing the float needs JS in any case. */
.item.is-hovered {
    z-index: 9;
}

.item.is-hovered .item__lift {
    transform: scale(1.04);
    filter:
        brightness(1.05)
        drop-shadow(0 26px 44px rgba(9, 36, 39, 0.28))
        drop-shadow(0 0 22px rgba(7, 63, 76, 0.16));
}

/* --------------------------------------------------------------- THE HIT */
.item__hit {
    position: absolute;
    z-index: 3;
    margin: 0;
    padding: 0;
    background: none;
    border: 0;
    pointer-events: auto;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.item__hit:focus-visible {
    outline: 2px solid var(--teal-08);
    outline-offset: 6px;
}

/* -------------------------------------------------------------- THE LABEL */
/* Bebas, measured at 21px against the 1512 reference. The comp gives the
   Identity Bible no label — its title is printed on the artwork — so it has
   none here either. */
.item__label {
    position: absolute;
    z-index: 2;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-item-label);
    line-height: 1;
    letter-spacing: 0.04em;
    white-space: nowrap;
    text-transform: uppercase;
    color: var(--label-08);
    pointer-events: auto;
}

/* -------------------------------------------------------------- THE ART */
.asset {
    position: absolute;
    margin: 0;
}

/* An item with a single asset — most of them — is filled by it. */
.item > .item__enter > .item__lift > .item__depth > .asset--solo {
    inset: 0;
}

.asset__float {
    display: block;
    width: 100%;
    will-change: transform;
}

.asset img {
    display: block;
    width: 100%;
    height: auto;
    /* The project's base reset caps images at 100% of their container; these
       are positioned by percentage and must be free to be whatever their box
       says — the same cap that squashed three of Section 06's frames. */
    max-width: none;
}

/* ---------------------------------------------------------- THE ITEM MAP */
/* Every rect below was solved from the comp's ink and each file's own alpha
   box, so the artwork lands where the design has it rather than where the
   PNG's padding would put it. The ratio is also published as --item-ar,
   because below 1024 it moves from the item to the art inside it: stacked, the
   label joins the flow above the artwork and the item's box has to grow by the
   label rather than overlap it. */
.item--bible    { left: 42.272%; top:  0.183%; width: 36.308%; --item-ar: 3707 / 3104; aspect-ratio: var(--item-ar); }
.item--usage    { left: 78.32%; top: 12.9%; width: 21.46%; --item-ar: 2350 / 1970; aspect-ratio: var(--item-ar); }
.item--comp     { left: 47.95%; top: 39.14%; width: 22.11%; --item-ar: 2350 / 1970; aspect-ratio: var(--item-ar); }
.item--campaign { left: 25.73%; top: 49.82%; width: 27.19%; --item-ar: 2350 / 1970; aspect-ratio: var(--item-ar); }
.item--sheet    { left: 72.9%; top: 41.6%; width: 22.6%; --item-ar: 2350 / 1970; aspect-ratio: var(--item-ar); }
.item--social   { left: 52.01%; top: 71.03%; width: 17.27%; --item-ar: 2125 / 2125; aspect-ratio: var(--item-ar); }
.item--film     { left: 72.99%; top: 63.65%; width: 21.02%; --item-ar: 2350 / 1970; aspect-ratio: var(--item-ar); }

/* Hit areas — the ink, not the file. */
.item--bible    .item__hit { left:  5.23%; top: 16.40%; width: 84.97%; height: 65.08%; }
.item--usage    .item__hit { left: 18.20%; top: 11.46%; width: 65.28%; height: 81.95%; }
.item--comp     .item__hit { left:  5.72%; top:  5.85%; width: 86.90%; height: 81.83%; }
.item--campaign .item__hit { left: 18.48%; top:  7.38%; width: 60.52%; height: 87.61%; }
.item--sheet    .item__hit { left:  6.05%; top:  7.09%; width: 88.74%; height: 84.97%; }
.item--social   .item__hit { left:  1.70%; top:  6.38%; width: 93.02%; height: 81.16%; }
.item--film     .item__hit { left:  4.74%; top: 29.03%; width: 90.09%; height: 57.76%; }

/* Labels, positioned against their own item. */
.item--usage    .item__label { left: 25.13%; top:  -4.75%; }
.item--comp     .item__label { left: 27.56%; top: -12.43%; }
.item--campaign .item__label { left: 36.49%; top:  -1.39%; }
.item--sheet    .item__label { left: 29.45%; top: 7%; }
.item--social   .item__label { left: 27.79%; top: -11.97%; }
.item--film     .item__label { left: 32.24%; top:  22.02%; }

/* The two magazines and the three phones, inside their own item. */
.asset--back  { left:  0.00%; top:  0.00%; width: 77.56%; z-index: 1; }
.asset--front { left: 18.01%; top: 17.70%; width: 81.99%; z-index: 2; }
.asset--p1    { left:  0.00%; top: 12.25%; width: 37.38%; }
.asset--p2    { left: 32.39%; top:  0.00%; width: 36.81%; z-index: 2; }
.asset--p3    { left: 63.02%; top: 12.32%; width: 36.98%; }

/* Paint order across the workspace, front row last. */
.item--bible { z-index: 1; }
.item--usage { z-index: 2; }
.item--sheet { z-index: 3; }
.item--comp { z-index: 4; }
.item--film { z-index: 5; }
.item--campaign { z-index: 6; }
.item--social { z-index: 7; }

/* ============================================================== LIGHTBOX */
.lightbox {
    position: fixed;
    inset: 0;
    z-index: 120;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: clamp(1rem, 4vw, 3rem);
    opacity: 0;
    visibility: hidden;
    transition:
        opacity 0.4s var(--pkg-ease),
        visibility 0s linear 0.4s;
}

.lightbox.is-open {
    opacity: 1;
    visibility: visible;
    transition:
        opacity 0.4s var(--pkg-ease),
        visibility 0s;
}

.lightbox__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(6, 26, 30, 0.72);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}

.lightbox__panel {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    align-items: center;
    gap: clamp(1.5rem, 3vw, 3rem);
    width: min(64rem, 100%);
    max-height: 100%;
    padding: clamp(1.5rem, 3vw, 2.75rem);
    background: #FFF9F3;
    box-shadow: 0 40px 90px rgba(6, 26, 30, 0.45);
    transform: translate3d(0, 18px, 0) scale(0.98);
    transition: transform 0.45s var(--pkg-ease);
}

.lightbox.is-open .lightbox__panel {
    transform: translate3d(0, 0, 0) scale(1);
}

.lightbox__figure {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 0;
    margin: 0;
}

.lightbox__figure img {
    display: block;
    width: auto;
    max-width: 100%;
    max-height: min(62vh, 30rem);
    height: auto;
    object-fit: contain;
}

.lightbox__title {
    margin: 0 0 0.5em;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: clamp(1.9rem, 3.4vw, 3.25rem);
    line-height: 1;
    letter-spacing: 0.01em;
    text-transform: uppercase;
    color: var(--teal-08);
}

.lightbox__text {
    margin: 0;
    max-width: 30ch;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: clamp(0.9rem, 1.15vw, 1.2rem);
    line-height: 1.55;
    color: var(--ink-08);
}

.lightbox__close {
    position: absolute;
    top: 0;
    right: 0;
    width: 3rem;
    height: 3rem;
    display: grid;
    place-items: center;
    font-size: 1.1rem;
    color: var(--teal-08);
    background: none;
    border: 0;
    cursor: pointer;
    transition: transform var(--pkg-dur) var(--pkg-ease);
}

.lightbox__close:hover,
.lightbox__close:focus-visible {
    transform: rotate(90deg);
}

.lightbox__close:focus-visible {
    outline: 2px solid var(--teal-08);
    outline-offset: -6px;
}

/* ============================================================== RESPONSIVE */
/* The composition holds its exact proportions to 1024. Below that the section
   stops being a fixed-aspect canvas: the copy column runs full width and the
   seven items stack in the order the brief reveals them, each keeping its own
   internal arrangement because those percentages are of the item and not of
   the section. */

@media (max-width: 1440px) {

    .package {
        min-height: 560px;
    }
}

@media (max-width: 1199px) {

    .package {
        aspect-ratio: auto;
        min-height: 0;
        padding: clamp(3.5rem, 9vw, 6rem) clamp(1.25rem, 4vw, 3rem);
    }

    .package__copy-scroll,
    .package__copy-pointer,
    .package__index-pointer,
    .package__assets {
        position: static;
        inset: auto;
        display: contents;
    }

    .package__index,
    .package__title,
    .package__rule,
    .package__lede,
    .package__cta {
        position: static;
        width: auto;
        height: auto;
    }

    .package__index {
        display: block;
        margin-bottom: 0.3em;
    }

    .package__rule {
        display: block;
        width: clamp(3rem, 8vw, 5rem);
        margin: clamp(1.5rem, 4vw, 2.5rem) 0 clamp(1.25rem, 3vw, 2rem);
    }

    .package__lede {
        max-width: 32rem;
    }

    .package__cta {
        margin-top: clamp(1.75rem, 4vw, 2.5rem);
        padding: 1.1em 2.2em;
        align-self: start;
        justify-self: start;
        width: max-content;
    }

    .item {
        position: relative;
        left: auto !important;
        top: auto !important;
        width: min(100%, 34rem);
        margin: clamp(2.75rem, 7vw, 4.5rem) auto 0;
        pointer-events: auto;
    }

    /* The Identity Bible's file is mostly transparent margin; stacked, that
       reads as a hole rather than as breathing room. */
    .item--bible {
        margin-top: clamp(1rem, 3vw, 2rem);
    }

    .item__label {
        position: static;
        display: block;
        margin-bottom: 0.6em;
    }

    /* Stacked, the label is above the art in the flow, so the item's box has
       to grow by the label rather than overlap it. */
    .item__enter,
    .item__lift,
    .item__depth {
        height: auto;
    }

    .item {
        aspect-ratio: auto !important;
    }

    .item__depth {
        position: relative;
        aspect-ratio: var(--item-ar);
    }

    :where(.package) {
        --fs-package: clamp(2.4rem, 8.4vw, 5.5rem);
        --fs-package-index: clamp(1.4rem, 4.9vw, 3.2rem);
        --fs-package-lede: clamp(0.9rem, 2vw, 1.2rem);
        --fs-item-label: clamp(0.95rem, 2.4vw, 1.5rem);
    }
}

@media (max-width: 767px) {

    .lightbox__panel {
        grid-template-columns: minmax(0, 1fr);
        align-content: start;
        overflow-y: auto;
    }

    .lightbox__figure img {
        max-height: 42vh;
    }

    :where(.package) {
        --fs-package: clamp(2.1rem, 11.4vw, 4.5rem);
        --fs-package-index: clamp(1.25rem, 6.7vw, 2.6rem);
        --fs-package-lede: clamp(0.9rem, 3.6vw, 1.15rem);
        --fs-item-label: clamp(0.9rem, 4vw, 1.3rem);
    }
}

@media (max-width: 560px) {

    :where(.package) {
        --fs-package: clamp(1.9rem, 12.6vw, 3.4rem);
    }
}

/* ----------------------------------------------------- TOUCH / NO HOVER */
@media (hover: none) {

    .item.is-hovered .item__lift {
        transform: none;
        filter: none;
    }

    .package__cta:hover {
        transform: none;
        box-shadow: none;
    }
}

/* ------------------------------------------------------------ ARMED STATES */
/* Applied only once GSAP is confirmed (`is-armed` goes on the <html> in
   main.js). Without JS the section renders complete and readable — the whole
   package is there, it simply does not assemble. */
.is-armed .package__title .char {
    display: inline-block;
    transform: translate3d(0, 40px, 0);
    opacity: 0;
    will-change: transform, opacity;
}

.is-armed .package__index,
.is-armed .package__lede,
.is-armed .package__cta,
.is-armed .item__enter {
    opacity: 0;
}

.is-armed .package__rule {
    transform: scaleX(0);
}

/* --------------------------------------------------------- REDUCED MOTION */
/* Every armed state above is mirrored here. Floating, parallax and the layered
   depth all go with it — package.js replaces the assembly with plain fades and
   keeps every asset and its lightbox fully reachable. */
@media (prefers-reduced-motion: reduce) {

    .is-armed .package__title .char {
        transform: none;
        opacity: 1;
    }

    .is-armed .package__index,
    .is-armed .package__lede,
    .is-armed .package__cta,
    .is-armed .item__enter {
        opacity: 1;
    }

    .is-armed .package__rule {
        transform: scaleX(1);
    }

    .item.is-hovered .item__lift {
        transform: none;
    }

    .package__cta:hover,
    .package__cta:focus-visible {
        transform: none;
    }

    .lightbox__panel {
        transform: none;
    }

    .lightbox.is-open .lightbox__panel {
        transform: none;
    }
}

/* ============================================================================
   SECTION 08 — THE IDENTITY BIBLE, AS A REAL BOOK
   Appended only; nothing above is edited. The book occupies exactly the slot
   model-comp.png used (.item--bible: left 42.272%, top 0.183%, width 36.308%,
   aspect 3707/3104), so the resting composition is unchanged.
   ========================================================================== */

.bible {
    position: relative;
    display: block;
    width: 100%;
    aspect-ratio: 3707 / 3104;
    perspective: 2200px;
    perspective-origin: 50% 46%;
}

.bible__stage {
    position: absolute;
    /* The boards sit inside the artwork's transparent margin, so the book's
       ink lands where the original file's ink landed. */
    left: 5.23%;
    top: 16.4%;
    width: 89.54%;
    height: 65.08%;
    transform-style: preserve-3d;
}

/* ------------------------------------------------------------- THE BOARDS */
.bible__board {
    position: absolute;
    top: 0;
    width: 50%;
    height: 100%;
    overflow: hidden;
    background: #F6F1E9;
    backface-visibility: hidden;
}

.bible__board--left {
    left: 0;
    border-radius: 5px 2px 2px 5px;
    box-shadow: inset -16px 0 26px rgba(7, 63, 76, 0.22);
}

.bible__board--right {
    left: 50%;
    border-radius: 2px 5px 5px 2px;
    box-shadow: inset 16px 0 26px rgba(7, 63, 76, 0.22);
}

.bible__board img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

/* Page thickness. Fine repeating lines read as stacked paper at any size,
   which a solid block never does. */
.bible__edges {
    position: absolute;
    top: 1.5%;
    height: 97%;
    width: 9px;
    background: repeating-linear-gradient(to right,
        #FFFFFF 0 1px, #E6DECF 1px 2px);
    pointer-events: none;
}

.bible__edges--left { left: -9px; border-radius: 3px 0 0 3px; }
.bible__edges--right { right: -9px; border-radius: 0 3px 3px 0; }

/* The gutter. A dark seam plus its own inner shadow is what stops an open
   book reading as two rectangles side by side. */
.bible__spine {
    position: absolute;
    left: 50%;
    top: -0.5%;
    width: 16px;
    height: 101%;
    transform: translateX(-50%);
    background: linear-gradient(90deg,
        rgba(7, 63, 76, 0.35) 0%,
        rgba(7, 63, 76, 0.62) 40%,
        rgba(7, 63, 76, 0.62) 60%,
        rgba(7, 63, 76, 0.35) 100%);
    pointer-events: none;
    z-index: 60;
}

.bible__shadow {
    position: absolute;
    left: -4%;
    right: -4%;
    bottom: -7%;
    height: 22px;
    border-radius: 50%;
    background: radial-gradient(ellipse at center,
        rgba(7, 63, 76, 0.34), transparent 70%);
    filter: blur(9px);
    pointer-events: none;
}

/* --------------------------------------------------------------- LEAVES */
/* Each leaf is a page pivoting on the gutter. Two faces, backface hidden, so
   exactly one is visible and they swap cleanly at the 90deg crossing. */
.leaf {
    position: absolute;
    left: 50%;
    top: 0;
    width: 50%;
    height: 100%;
    transform-style: preserve-3d;
    transform-origin: left center;
    will-change: transform;
}

.leaf__face {
    position: absolute;
    inset: 0;
    overflow: hidden;
    background: #FBF7F1;
    backface-visibility: hidden;
}

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

.leaf__face--front { border-radius: 2px 5px 5px 2px; }

.leaf__face--back {
    transform: rotateY(180deg);
    border-radius: 5px 2px 2px 5px;
}

/* Shading that deepens as the page stands upright — the single cheapest cue
   that a leaf has physical thickness and is catching light. */
.leaf__shade {
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg,
        rgba(7, 63, 76, 0.55) 0%, rgba(7, 63, 76, 0) 55%);
    opacity: 0;
    pointer-events: none;
}

/* ------------------------------------------------------- THE RESTING COVER */
/* The section's original artwork. Visible until the book opens, so nothing
   about the default composition changes. */
.bible__cover {
    position: absolute;
    left: -5.84%;
    top: -25.2%;
    width: 111.68%;
    height: auto;
    z-index: 70;
    pointer-events: none;
}

/* While the sequence runs, the whole book lifts above the collage. */
.package.is-reading .item--bible {
    z-index: 90;
}

@media (prefers-reduced-motion: reduce) {

    .leaf {
        transform: none !important;
    }

    .bible__cover {
        opacity: 1 !important;
    }
}

/* ============================================================================
   SECTION 08 — BOOK REFINEMENTS
   Appended last. Removes the flat cover, gives the open book real binding, and
   adds the page bend.
   ========================================================================== */

/* The old flat spread is gone from the markup; this clears any trace. */
.bible__cover {
    display: none !important;
}

/* ------------------------------------------------------------- BACKDROP */
/* A soft white wash that fades up as the book zooms, so the spread reads as
   an object lit on a surface rather than two photographs pasted over the
   collage. Fixed, because during the pin the book owns the whole viewport. */
.bible__backdrop {
    position: fixed;
    inset: 0;
    z-index: -1;
    background:
        radial-gradient(120% 90% at 50% 42%,
            rgba(255, 255, 255, 0.92) 0%,
            rgba(255, 255, 255, 0.72) 45%,
            rgba(250, 245, 238, 0.55) 100%);
    opacity: 0;
    pointer-events: none;
    backdrop-filter: blur(3px);
}

.package.is-reading .bible__backdrop {
    z-index: 80;
}

.package.is-reading .item--bible {
    z-index: 95;
}

/* ------------------------------------------------------------- BINDING */
/* The pages are full-bleed photographs, so without chrome the spread reads as
   two pictures side by side — which is exactly how it looked. These give it
   boards, a bound edge and weight. */
.bible__stage {
    border-radius: 6px;
    box-shadow:
        0 2px 0 rgba(255, 255, 255, 0.5) inset,
        0 40px 90px rgba(20, 30, 30, 0.34),
        0 10px 26px rgba(20, 30, 30, 0.18);
}

.bible__board--left,
.bible__board--right {
    box-shadow: none;
}

/* Gutter shading painted ON TOP of the pages, both sides, so the paper
   visibly curves down into the binding instead of meeting at a hard seam. */
.bible__stage::before,
.bible__stage::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    width: 9%;
    z-index: 55;
    pointer-events: none;
}

.bible__stage::before {
    right: 50%;
    background: linear-gradient(to right,
        rgba(20, 30, 30, 0) 0%,
        rgba(20, 30, 30, 0.06) 55%,
        rgba(20, 30, 30, 0.32) 100%);
}

.bible__stage::after {
    left: 50%;
    background: linear-gradient(to left,
        rgba(20, 30, 30, 0) 0%,
        rgba(20, 30, 30, 0.06) 55%,
        rgba(20, 30, 30, 0.32) 100%);
}

/* A finer, darker seam than before — a bound book has a crease, not a bar. */
.bible__spine {
    width: 7px;
    background: linear-gradient(90deg,
        rgba(20, 30, 30, 0.10) 0%,
        rgba(20, 30, 30, 0.55) 50%,
        rgba(20, 30, 30, 0.10) 100%);
}

/* Page edges read as stacked paper against the white backdrop. */
.bible__edges {
    width: 11px;
    background: repeating-linear-gradient(to right,
        #FFFFFF 0 1px, #DED5C6 1px 2px);
    box-shadow: 0 1px 3px rgba(20, 30, 30, 0.25);
}

/* --------------------------------------------------------- THE PAGE BEND */
/* A turning page is not a flat plane. Real curl needs the leaf split into
   segments, which would mean many more nodes animating per frame; this gets
   the read for two gradients and no extra geometry:

     · leaf__curl  — a moving highlight/shadow band that sweeps across the
                     face, so light appears to roll over a curved sheet
     · a soft drop shadow on the leaf itself, strongest mid-turn

   Both are driven from the timeline, peaking as the page stands upright. */
.leaf__curl {
    position: absolute;
    inset: 0;
    z-index: 3;
    background: linear-gradient(105deg,
        rgba(255, 255, 255, 0.00) 34%,
        rgba(255, 255, 255, 0.55) 47%,
        rgba(255, 255, 255, 0.00) 60%),
        linear-gradient(90deg,
        rgba(20, 30, 30, 0.30) 0%,
        rgba(20, 30, 30, 0.00) 22%);
    opacity: 0;
    pointer-events: none;
    border-radius: inherit;
}

.leaf {
    /* Rounded outer corner, square at the gutter — the shape of a bound page. */
    border-radius: 2px 6px 6px 2px;
}

.leaf__face {
    box-shadow: 0 0 0 1px rgba(20, 30, 30, 0.05) inset;
}

@media (prefers-reduced-motion: reduce) {

    .bible__backdrop,
    .leaf__curl {
        opacity: 0 !important;
    }
}

/* ------------------------------------------------- HEADING REVEAL SCAFFOLD */
/* The armed pre-state translates .char 40px down at opacity 0, which belonged
   to the old line-rise entrance. The rotateY reveal sets its own start state,
   so that would only fight it — and would leave the type invisible if the
   split ever failed. */
.is-armed .package__title .char {
    transform: none;
    opacity: 1;
}

/* Glyphs are inline-block with `vertical-align: top`, pinned to the TOP of
   their line box. .package__line owns the `overflow: hidden` that masks the
   reveal, so a line box shorter than Bebas' ~1.2em glyph box shears the
   bottoms off. Real height, then the surplus pulled back out — the visual
   line advance, and so the heading's position, is unchanged. */
.package__line {
    line-height: 1.3;
    padding-bottom: 0;
    margin-top: -0.03em;
    margin-bottom: -0.3em;
}

.package__title .hero__line-inner {
    display: block;
    line-height: inherit;
}

.package__title .hero__word {
    display: inline-block;
    vertical-align: top;
    white-space: nowrap;
}

.package__title .char {
    display: inline-block;
    vertical-align: top;
    backface-visibility: hidden;
    will-change: transform, opacity;
}

@media (prefers-reduced-motion: reduce) {

    .package__title .char,
    .package__title .hero__word,
    .package__title .hero__line-inner {
        transform: none !important;
        opacity: 1 !important;
        visibility: visible !important;
    }
}

/* ============================================================================
   SECTION 08 — READING-STATE STACKING FIX
   The backdrop now sits between the page and the book, not over the book.
   ========================================================================== */

/* Base state: inert and invisible. It is a direct child of the section, so it
   no longer shares the book's stacking context — which is what let it paint
   over the pages. */
.bible__backdrop {
    z-index: 3;
}

/* While the sequence runs, the order is: backdrop, then the assets layer
   holding the book, then the book itself. The assets layer must be lifted
   explicitly — .item--bible's own z-index only competes INSIDE that layer, so
   raising the card alone could never beat a backdrop above its parent. */
.package.is-reading .bible__backdrop {
    z-index: 80;
}

.package.is-reading .package__assets {
    z-index: 90;
    /* The zoomed book has to be able to reach beyond the assets box. */
    overflow: visible;
}

.package.is-reading .item--bible {
    z-index: 95;
}

/* The section clips its own overflow at rest; while reading, the book fills
   the viewport and must not be cut off by the section box. */
.package.is-reading {
    overflow: visible;
}

/* The other assets and the copy column are faded by the timeline, but their
   pointer targets must go too, or invisible hit areas still intercept. */
.package.is-reading .item:not(.item--bible),
.package.is-reading .package__copy-scroll {
    pointer-events: none;
}

/* ============================================================================
   SECTION 08 — EDGES OFF, BACKDROP UP
   ========================================================================== */

/* 1 — The page-edge strips are gone from the markup; this kills the rules so
   nothing can paint a light bar at either side of the spread. They were meant
   to read as stacked paper, but against these full-bleed photographs they just
   looked like a white border. */
.bible__edges {
    display: none !important;
}

/* The stage's inset highlight was adding a second pale line along the top
   edge — removed for the same reason. */
.bible__stage {
    box-shadow:
        0 40px 90px rgba(20, 30, 30, 0.34),
        0 10px 26px rgba(20, 30, 30, 0.18);
}

/* 2 — A properly visible translucent white wash.
   The previous one was near-white over a cream section, so it did nothing you
   could see. This is a real veil: white at the centre falling to a soft warm
   grey at the edges, with a blur behind it, so the book reads as lit on a
   surface and everything else recedes. */
.bible__backdrop {
    background:
        radial-gradient(130% 100% at 50% 44%,
            rgba(255, 255, 255, 0.97) 0%,
            rgba(255, 255, 255, 0.88) 38%,
            rgba(244, 240, 233, 0.78) 72%,
            rgba(232, 227, 218, 0.70) 100%);
    backdrop-filter: blur(8px) saturate(0.9);
    -webkit-backdrop-filter: blur(8px) saturate(0.9);
}

/* ============================================================================
   SECTION 08 — FIT WHILE PINNED
   ========================================================================== */

/* The carriers are scaled from their top edge by fitToViewport() in
   package.js, so the composition fits the screen while the section is pinned.
   They need a transform context that does not clip the scaled result. */
.package__index-pointer,
.package__copy-scroll,
.package__assets {
    will-change: transform;
}

/* The section clips at rest by design. While reading, the zoomed book has to
   reach past the section box, so the clip is lifted for the duration. */
.package.is-reading {
    overflow: visible;
}

/* ============================================================================
   SECTION 08 — CLEARANCE BETWEEN COPY AND COLLAGE
   ========================================================================== */

/* Every asset is positioned as a percentage of .package__assets, which spans
   the section. The heading is set in vw-clamped Bebas, so as the viewport
   narrows the type keeps its width for longer than the collage does — and
   below roughly 1440 the two start to meet. That is what was touching.

   The carrier is shifted right by equal-and-opposite left/right offsets, so it
   MOVES without changing width. Offsetting only `left` would have squeezed the
   collage horizontally and distorted every asset inside it.

   Done with offsets rather than a transform on purpose: fitToViewport() in
   package.js already owns the transform on this element, and a CSS transform
   here would simply be overwritten on the next refresh. */
@media (max-width: 1440px) {

    .package__assets {
        left: 4.5%;
        right: -4.5%;
    }
}

@media (max-width: 1340px) {

    .package__assets {
        left: 7.5%;
        right: -7.5%;
    }
}

@media (max-width: 1240px) {

    .package__assets {
        left: 10%;
        right: -10%;
    }
}

/* Below 1200 the section reflows to a stack and the two never share a row, so
   the offset is dropped rather than carried into a layout that does not need
   it. */
@media (max-width: 1199px) {

    .package__assets {
        left: 0;
        right: 0;
    }
}

/* ============================================================================
   SECTION 08 — HEADING SIZE, BALANCED GAPS, BOOK CLEARANCE
   ========================================================================== */

/* 1 — HEADING AT FULL SIZE.
   The clamp is untouched from the original design; this only guarantees the
   copy carrier is never scaled, so the title matches every other section
   title. (The JS fitter now scales the assets alone.) */
.package__copy-scroll,
.package__copy-pointer,
.package__index-pointer {
    transform: none;
}

/* 2 — EQUAL TOP AND BOTTOM.
   The composition's lowest asset finishes around 92% of the section box, so
   the remaining 8% read as an unbalanced gap under the collage while the top
   started almost immediately. Trimming the section's aspect brings the bottom
   edge in to match the top, and because every asset is positioned as a
   percentage of this box they all move together — the composition keeps its
   internal proportions exactly. */
.package {
    aspect-ratio: 1021 / 556;
}

/* 3 — CLEARANCE UNDER THE BOOK.
   The book sat low enough to crowd the "Model comp card" label beneath it.
   Lifting it and trimming its width gives that caption room to breathe; the
   hit area is moved with it so the two stay aligned. */
.item--bible {
    top: -1.6%;
    width: 34.4%;
}

@media (max-width: 1199px) {

    /* Stacked layout — the aspect lock and the lift both come off. */
    .package {
        aspect-ratio: auto;
    }

    .item--bible {
        top: auto;
        width: auto;
    }
}

/* ============================================================================
   SECTION 08 - THE BOOK: ONE IMAGE PER PAGE, NOTHING CROPPED
   Appended last. Nothing above is edited, and no other section is touched.

   Every file in image/book is 1200 x 1800, so a page has to be 2:3 or the
   picture inside it must be cut. The spread is two of those side by side,
   which makes the open book exactly 4:3. The slot .item--bible occupies is
   left alone, so the collage around the book does not move.
   ========================================================================== */

/* 1 - TRUE 2:3 PAGES.
   The old stage was 89.54% wide by 65.08% tall inside the slot, an aspect of
   about 1.64, so each half was 0.82:1 against a 0.667:1 picture. That gap is
   what object-fit had to eat, and it ate the top of every page. These numbers
   give the same centre point with a 4:3 spread, so each half is exactly 2:3. */
.bible__stage {
    left: 10%;
    top: 10.07%;
    width: 80%;
    height: auto;
    aspect-ratio: 4 / 3;
}

/* The hit area is the ink, so it follows the new spread. */
.item--bible .item__hit {
    left: 10%;
    top: 10.07%;
    width: 80%;
    height: 71.66%;
}

/* The spread is centred horizontally in the slot but sits above centre
   vertically, so the zoom has to scale about the spread's own middle rather
   than the slot's. Without this the book drifts as it grows. */
.bible {
    transform-origin: 50% 45.9%;
}

/* 2 - NO CROPPING, EITHER SIDE.
   The boards were on object-fit: contain, which letterboxed them, and the
   leaves on cover, which cropped them. Now that the page box and the picture
   share an aspect ratio the two are the same thing, and cover is used so a
   sub-pixel rounding difference shows as nothing rather than as a hairline. */
.bible__board img,
.leaf__face img {
    width: 100%;
    height: 100%;
    max-width: none;
    object-fit: cover;
    object-position: center;
    display: block;
}

/* 3 - THE PAGE LIFT, WITHOUT A FILTER.
   This is the mirrored-text bug. The turn shadow used to be a drop-shadow()
   filter on .leaf, and per the transforms spec any filter other than none
   forces transform-style to compute to flat. The leaf's two faces were
   therefore collapsed into a single plane, and the back face's rotateY(180deg)
   stopped being a rotation in 3D and became a plain 2D mirror, which is why
   every turned page came to rest reading backwards.

   A box-shadow does not flatten anything, so the same weight is drawn here
   instead and the timeline animates --leaf-lift from 0 to 1 and back. */
.leaf {
    box-shadow: -18px 8px 22px rgba(20, 30, 30, calc(var(--leaf-lift, 0) * 0.3));
}

/* 4 - THE BOOK IS NOT HOVERABLE WHILE IT IS BEING READ.
   .item.is-hovered puts a scale and a filter on .item__lift, an ancestor of
   the book. Landing either one mid-zoom would jump the spread and could open
   the lightbox over it, so the hit box goes quiet for the duration. */
.package.is-reading .item--bible .item__hit {
    pointer-events: none;
}

.package.is-reading .item--bible .item__lift {
    transform: none;
    filter: none;
}

@media (prefers-reduced-motion: reduce) {

    .leaf {
        box-shadow: none;
    }
}

/* ============================================================================
   SECTION 08 - HARD GUARD AGAINST THE MIRRORED PAGE
   ========================================================================== */

/* A filter of any kind on .leaf forces transform-style to compute to flat.
   The leaf's two faces then collapse into one plane, the back face's
   rotateY(180deg) stops being a rotation and becomes a mirror, and what you
   see after a turn is the FRONT face reversed. That is also the page that
   looked like it repeated: page 4 read once on the right and again, backwards,
   on the left.

   The timeline no longer writes a filter here, but this stops an inline one
   from any source ever reaching the leaf again. A stylesheet !important beats
   an inline style, so it holds even against a stale script. */
.leaf {
    filter: none !important;
    transform-style: preserve-3d !important;
}

.leaf__face {
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
}

/* ============================================================================
   SECTION 08 - SHARP PAGES WHILE ZOOMED
   ========================================================================== */

/* THE BLUR.
   Every carrier between .package__assets and .leaf declares
   will-change: transform. That is a promise to the compositor that the element
   keeps moving, so Chrome rasterizes it once at its resting size and reuses
   that bitmap for the whole scrub. The zoom then stretches a small bitmap up
   by two and a half rather than redrawing the pages, which is the softness on
   the type. It is not a resolution problem: the files are 1200 x 1800 and a
   page lands at roughly 590 device pixels wide.

   Releasing the hint for the duration of the read lets the compositor
   re-rasterize at the scale actually on screen. The pages stay composited
   anyway, because a 3D transform promotes them on its own. */
.package.is-reading .package__assets,
.package.is-reading .item--bible .item__enter,
.package.is-reading .item--bible .item__lift,
.package.is-reading .item--bible .item__depth,
.package.is-reading .item--bible .asset__float,
.package.is-reading .bible,
.package.is-reading .bible__stage,
.package.is-reading .bible__board,
.package.is-reading .leaf,
.package.is-reading .leaf__face {
    will-change: auto;
}

/* The leaves turn once per visit, on a scrub. Pinning their raster for that is
   a poor trade against a page you can read, so the hint comes off outright. */
.leaf {
    will-change: auto;
}

/* Let the browser pick its best downscale for the page art rather than the
   fast path it may fall back to inside a promoted layer. */
.bible__board img,
.leaf__face img {
    image-rendering: auto;
}

/* ============================================================================
   ========================================================================== */

/* WHY THE PAGES WERE STILL SOFT.
   .bible__stage is a preserve-3d rendering context under .bible's perspective.
   Chrome rasterizes that subtree once at its layout size and then applies the
   parent's transform to the finished layer, so a scale of 2.4 stretched a
   small bitmap. Releasing will-change does not change that, because the 3D
   context is what pins the raster, not the hint.

   So the book stops being scaled up at all. It is laid out 2.4 times larger,
   which is the size it is read at, and scaled DOWN to 1/2.4 to sit in its slot
   at rest. The zoom then finishes at a scale of about 0.99, which is the
   artwork at its own pixels. Downscaling a raster is always clean; upscaling
   one never is.

   2.4 is not a guess. The scale the zoom asks for works out at 2.369 on a
   1536x744 screen and 2.398 on 1920x1080, because the section height and the
   viewport height track each other, so one number covers every desktop.

   Every fixed pixel length inside the book is multiplied by the same factor,
   or the spine, radii and shadows would all render 2.4 times too fine. */
.bible {
    --book-hi: 2.4;

    position: absolute;
    left: calc((1 - var(--book-hi)) * 50%);
    top: calc((1 - var(--book-hi)) * 45.9%);
    width: calc(100% * var(--book-hi));
    perspective: calc(2200px * var(--book-hi));
}

/* The book is positioned against this, so it needs to be a containing block
   with a real height. The float transform still rides above it. */
.item--bible .asset__float {
    position: relative;
    height: 100%;
}

.bible__stage {
    border-radius: calc(6px * var(--book-hi));
    box-shadow:
        0 calc(40px * var(--book-hi)) calc(90px * var(--book-hi)) rgba(20, 30, 30, 0.34),
        0 calc(10px * var(--book-hi)) calc(26px * var(--book-hi)) rgba(20, 30, 30, 0.18);
}

.bible__spine {
    width: calc(7px * var(--book-hi));
}

.bible__shadow {
    height: calc(22px * var(--book-hi));
    filter: blur(calc(9px * var(--book-hi)));
}

.bible__board--left {
    border-radius: calc(5px * var(--book-hi)) calc(2px * var(--book-hi)) calc(2px * var(--book-hi)) calc(5px * var(--book-hi));
}

.bible__board--right {
    border-radius: calc(2px * var(--book-hi)) calc(5px * var(--book-hi)) calc(5px * var(--book-hi)) calc(2px * var(--book-hi));
}

.leaf {
    border-radius: calc(2px * var(--book-hi)) calc(6px * var(--book-hi)) calc(6px * var(--book-hi)) calc(2px * var(--book-hi));
    box-shadow:
        calc(-18px * var(--book-hi)) calc(8px * var(--book-hi)) calc(22px * var(--book-hi)) rgba(20, 30, 30, calc(var(--leaf-lift, 0) * 0.3));
}

.leaf__face {
    box-shadow: 0 0 0 calc(1px * var(--book-hi)) rgba(20, 30, 30, 0.05) inset;
}

.leaf__face--front {
    border-radius: calc(2px * var(--book-hi)) calc(5px * var(--book-hi)) calc(5px * var(--book-hi)) calc(2px * var(--book-hi));
}

.leaf__face--back {
    border-radius: calc(5px * var(--book-hi)) calc(2px * var(--book-hi)) calc(2px * var(--book-hi)) calc(5px * var(--book-hi));
}


/* ############################################################################
   SECTION-09.CSS
   Section 09.
   ######################################################################## */

/* ============================================================================
   SECTION-09.CSS — THE UNREAL ARCHIVE
   ========================================================================== */

/* ---------------------------------------------------------------- TOKENS */
.archive {
    /* 100 / 49.8 / 20 / 13 px at the 1512 reference, in proportion to 1920. */
    --fs-archive: clamp(2.4rem, 6.6138vw, 127px);
    --fs-archive-index: clamp(1.3rem, 3.2937vw, 63px);
    --fs-archive-copy: clamp(0.85rem, 1.3228vw, 25px);
    --fs-archive-label: clamp(0.62rem, 0.8598vw, 16px);

    --ink-09: #012024;
    --paper-09: #FFFFFF;
    --flame-09: #FF4938;
    --lime-09: #AAC102;
    /* The same flat warm grey every section's index samples to. */
    --index-09: #817E7E;

    /* The comp's rhythm, as fractions of the section height: 35px pitch,
       145px rows, the lower row dropped 135px, on a 330px band. */
    --rail-pitch: 10.606cqh;
    --arc-ease: cubic-bezier(0.22, 1, 0.36, 1);
    --arc-dur: 0.45s;
}

/* ----------------------------------------------------------------- SHELL */
.archive {
    position: relative;
    width: 100%;
    aspect-ratio: 1026 / 330;
    min-height: 420px;
    overflow: hidden;
    background: var(--ink-09);
    isolation: isolate;
}

/* --------------------------------------------------------- TRANSFORM RIGS */
/* Pointer budgets from the brief: archive 4px, heading 2px, background 1px.
   Each carrier spans the section so the percentages inside resolve against the
   section box, which is why they are pointer-transparent — a full-bleed
   carrier swallowing a hover is the bug Section 03 shipped. */
.archive__copy-pointer,
.archive__index-pointer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 4;
    will-change: transform;
}

/* The column's own controls have to be clickable again. */
.archive__copy-pointer .archive__cta,
.archive__copy-pointer .archive__nav {
    pointer-events: auto;
}

/* ------------------------------------------------------------ LEFT COLUMN */
.archive__index {
    position: absolute;
    top: 10.15%;
    left: 2.534%;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-archive-index);
    line-height: 1;
    color: var(--index-09);
}

.archive__label {
    position: absolute;
    top: 7.9%;
    left: 7.310%;
    margin: 0;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: var(--fs-archive-label);
    line-height: 1;
    /* Measured back from the comp's 217px ink across 40 characters. */
    letter-spacing: 0.108em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--paper-09);
    opacity: 0.62;
}

.archive__title {
    position: absolute;
    top: 12.1%;
    left: 7.554%;
    /* "SEEN EVERYWHERE." is the longer line at 37.4% of the section; 44%
       clears it and still stops short of the rail at 47.3%. */
    width: 44%;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-archive);
    line-height: 1;
    /* Tracked flat, not the 0.01em Sections 04-08 use: "SEEN EVERYWHERE." is
       the longest line in the project and the rack begins at 47.271%, so the
       loosest setting that still clears it is none. */
    letter-spacing: 0;
    text-transform: uppercase;
    color: var(--paper-09);
}

.archive__line {
    display: block;
    overflow: hidden;
    padding-bottom: 0.04em;
}

.archive__line-in {
    display: block;
}

.archive__line-in--flame {
    color: var(--flame-09);
}

/* ------------------------------------------------------ PROGRESS INDICATOR */
/* The comp has a hairline under the heading, exactly where an editorial
   progress bar belongs — 81px of it, at two different brightnesses across its
   two rows. That IS the indicator: the dim row is the track, the bright one
   the fill. The brief asks for a visible progress bar and the comp already
   drew one, so nothing new had to be invented for it. */
.archive__progress {
    position: absolute;
    top: 54.242%;
    left: 7.602%;
    width: 7.895%;
    height: 2px;
    overflow: hidden;
    background: rgba(255, 255, 255, 0.16);
}

.archive__progress-fill {
    display: block;
    width: 100%;
    height: 100%;
    background: rgba(255, 255, 255, 0.62);
    transform: scaleX(0);
    transform-origin: 0 50%;
    will-change: transform;
}

/* ---------------------------------------------------------------- COPY */
.archive__copy {
    position: absolute;
    top: 57.6%;
    left: 7.797%;
    width: 24%;
    margin: 0;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: var(--fs-archive-copy);
    /* The comp's 18px pitch on a 20px font. */
    line-height: 1.33;
    color: var(--paper-09);
}

/* The comp sets this as two lines, not the brief's three; the break is
   authored so it falls in the same place at every width. */
.archive__copy-line {
    display: block;
}

/* --------------------------------------------------------------- THE CTA */
/* The brief's own 226.8 x 61.8 at the 1512 reference, which is within 5% of
   the comp's box. Same premium behaviour as every button before it. */
.archive__cta {
    position: absolute;
    /* 72%, not the comp's 73.333%. Measured on the rendered section at 1920:
       the description's last line of ink ends at y 418 and the arrows begin at
       548, so the button sat 35px below the copy and 18px above the controls —
       the CTA group read as bottom-heavy even though the boxes were even. The
       difference is the half-leading under the copy's 1.33 line-height, which
       is why it has to be corrected optically rather than by the box. 72%
       leaves 27px above and 25px below. Nothing else in the column moves, and
       the rail, the drag and the arrows are untouched. */
    top: 72%;
    left: 8.090%;
    width: 15%;
    height: 60px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    padding: 0 1em;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    /* The brief gives this button both a 226.8px box and a 20px label. Inter
       400 sets "EXPLORE CAMPAIGNS" at 20px in about 220px of ink, so the two
       figures cannot both be honoured — the label would touch its own edges.
       The box is the measurement the comp agrees with, so it wins, and the
       label takes the comp's own smaller setting: 16px at the 1512 reference.
       Change the 0.8 to 1 to have the label at exactly 20px instead. */
    font-size: calc(var(--fs-archive-copy) * 0.8);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    text-decoration: none;
    white-space: nowrap;
    /* The brief specifies #012024 on the lime. The comp renders this label
       white, which on #AAC102 is about 1.9:1 — below any legibility floor.
       The brief's value is both what was asked for and the readable one. */
    color: var(--ink-09);
    background: var(--lime-09);
    border: 0;
    cursor: pointer;
    transition:
        transform var(--arc-dur) var(--arc-ease),
        background-color var(--arc-dur) var(--arc-ease),
        box-shadow var(--arc-dur) var(--arc-ease);
}

.archive__cta:hover,
.archive__cta:focus-visible {
    transform: translate3d(0, -3px, 0) scale(1.02);
    background: #BCD402;
    box-shadow: 0 18px 36px rgba(170, 193, 2, 0.28);
}

.archive__cta:focus-visible {
    outline: 2px solid var(--paper-09);
    outline-offset: 4px;
}

/* ------------------------------------------------------------ NAVIGATION */
.archive__nav {
    position: absolute;
    top: 88.788%;
    left: 8.382%;
    display: flex;
    align-items: center;
    gap: 1.462%;
}

.archive__arrow {
    display: grid;
    place-items: center;
    width: 2.729vw;
    height: 2.729vw;
    max-width: 52px;
    max-height: 52px;
    min-width: 34px;
    min-height: 34px;
    padding: 0;
    color: var(--paper-09);
    background: none;
    border: 1px solid rgba(255, 255, 255, 0.55);
    border-radius: 50%;
    cursor: pointer;
    transition:
        transform var(--arc-dur) var(--arc-ease),
        background-color var(--arc-dur) var(--arc-ease),
        border-color var(--arc-dur) var(--arc-ease),
        opacity var(--arc-dur) var(--arc-ease);
}

.archive__arrow svg {
    width: 42%;
    height: 42%;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.archive__arrow:hover,
.archive__arrow:focus-visible {
    transform: scale(1.08);
    background: rgba(255, 255, 255, 0.12);
    border-color: var(--paper-09);
}

.archive__arrow:focus-visible {
    outline: 2px solid var(--lime-09);
    outline-offset: 3px;
}

/* At either end the arrow stays present and focusable — it simply reads as
   spent. Removing it would move the other one. */
.archive__arrow[aria-disabled="true"] {
    opacity: 0.32;
    cursor: default;
}

.archive__drag {
    position: absolute;
    top: 91.5%;
    left: 18.421%;
    margin: 0;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: var(--fs-archive-label);
    line-height: 1;
    letter-spacing: 0.108em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--paper-09);
    opacity: 0.62;
}

/* ================================================================ THE RAIL */
.archive__viewport {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 47.271%;
    right: 0;
    z-index: 2;
    /* The rack has to be clipped to its own window, not to the section: the
       rail slides left as it scrolls and the works would otherwise run out
       over the heading. */
    overflow: hidden;
    /* The size container every horizontal figure in the rack is measured
       against. See the header note. */
    container-type: size;
    /* grab / grabbing, set by archive.js so the cursor only offers to drag
       when there is somewhere to drag to. */
    cursor: grab;
    touch-action: pan-y;
}

.archive__viewport.is-dragging {
    cursor: grabbing;
}

/* One carrier for the 4px pointer budget, one for the rail's own x. Two
   elements, so neither can clobber the other. */
.archive__viewport-pointer {
    position: absolute;
    inset: 0;
    will-change: transform;
}

/* ------------------------------------------------------------------ WASH */
/* The brief's 0.8x background layer. The comp's plate behind the rack is flat
   colour with nothing in it to displace, so rather than invent artwork — and
   the rules forbid placeholders — the background layer is an ambient field of
   the section's own three colours. It is the slowest thing on screen, which is
   the whole job of a background lane. */
.archive__wash {
    position: absolute;
    /* Never to the left: past the rail's own left edge is the heading. */
    inset: -14% -18% -14% 0;
    z-index: 0;
    background:
        radial-gradient(46% 62% at 20% 40%, rgba(170, 193, 2, 0.11), transparent 70%),
        radial-gradient(38% 58% at 62% 62%, rgba(255, 73, 56, 0.10), transparent 72%),
        radial-gradient(58% 78% at 96% 28%, rgba(255, 255, 255, 0.06), transparent 70%);
    pointer-events: none;
    will-change: transform;
}

.rail {
    position: absolute;
    /* 30px down, 280px tall, on the comp's 330px band: the box the two rows
       are aligned into. The upper row starts at the top of it, the lower row
       ends at the bottom of it, which is exactly the comp's 135px drop. */
    top: 9.091%;
    left: 0;
    z-index: 1;
    display: flex;
    align-items: flex-start;
    width: max-content;
    height: 84.848%;
    /* Room for the last work, whose own negative margin would otherwise pull
       max-content in over it, plus the closing gutter that says the archive
       has ended rather than been cut off. */
    padding-right: 24cqh;
    will-change: transform;
}

.slide {
    position: relative;
    flex: 0 0 auto;
    /* 145 of the rail's 280. Width follows each file's own opaque aspect. */
    height: 51.786%;
    margin: 0;
    /* Overridden per file below: pitch minus this slide's own width, which is
       what turns a flex row into the comp's woven, overlapping rack. */
    margin-right: var(--rail-pitch);
}

/* The weave: every second work sits in the lower row. flex-end against a rail
   box that ends at the comp's 310px puts it exactly 135px down without a
   percentage margin, which would have resolved against the wrong axis. */
.slide:nth-child(even) {
    align-self: flex-end;
}

/* One transform source per element, outermost first: the lane carrier takes
   the horizontal depth offset, the entrance carrier the reveal, the float
   carrier the idle drift, the lift the hover. `.slide` itself only ever
   carries opacity. */
.slide__depth,
.slide__enter,
.slide__float {
    display: block;
    width: 100%;
    height: 100%;
    will-change: transform;
}

.slide__lift {
    display: block;
    position: relative;
    width: 100%;
    height: 100%;
    transition:
        transform var(--arc-dur) var(--arc-ease),
        filter var(--arc-dur) var(--arc-ease);
}

.slide img {
    position: absolute;
    display: block;
    /* Scaled and offset so the file's opaque box exactly fills the slide —
       the per-file numbers below. */
    max-width: none;
    -webkit-user-drag: none;
    user-select: none;
}

/* ------------------------------------------------- PER-FILE SLIDE GEOMETRY */
/* aspect-ratio is the file's opaque box; margin-right is the comp's 35px pitch
   less that box's width; the img rules scale the file up and pull it back so
   the box lands on the slide's edges. */
.slide--b1 { aspect-ratio: 174 / 243; margin-right: -20.856cqh; }
.slide--b2 { aspect-ratio: 158 / 213; margin-right: -21.987cqh; }
.slide--b3 { aspect-ratio: 128 / 213; margin-right: -15.799cqh; }
.slide--b4 { aspect-ratio: 121 / 197; margin-right: -16.382cqh; }
.slide--b5 { aspect-ratio: 158 / 222; margin-right: -20.666cqh; }
.slide--b6 { aspect-ratio: 113 / 186; margin-right: -16.088cqh; }
.slide--b7 { aspect-ratio: 121 / 197; margin-right: -16.382cqh; }
.slide--b8 { aspect-ratio: 121 / 197; margin-right: -16.382cqh; }

.slide--b1 img { width: 183.908%; height: 110.700%; left: -44.828%; top: -6.996%; }
.slide--b2 img { width: 163.291%; height: 101.408%; left: -30.380%; top: -1.408%; }
.slide--b3 img { width: 210.156%; height: 105.634%; left: -48.438%; top: 0; }
.slide--b4 img { width: 204.959%; height: 105.584%; left: -46.281%; top: 0; }
.slide--b5 img { width: 184.810%; height: 109.910%; left: -44.937%; top: -6.757%; }
.slide--b6 img { width: 207.080%; height: 105.914%; left: -47.788%; top: 0; }
.slide--b7 img { width: 204.959%; height: 105.584%; left: -46.281%; top: 0; }
.slide--b8 img { width: 204.959%; height: 105.584%; left: -46.281%; top: 0; }

/* ------------------------------------------------------------------ HOVER */
/* Applied as a class, not :hover — the float has to be paused by the same
   handler, and a dragging pointer must not trigger it. The shadow is a
   drop-shadow filter rather than a box-shadow because every one of these files
   is a soft-edged cut-out: a rectangular shadow would sit visibly outside the
   artwork it belongs to. */
.slide.is-hovered {
    z-index: 20;
}

.slide.is-hovered .slide__lift {
    transform: scale(1.05);
    filter: drop-shadow(0 26px 44px rgba(0, 0, 0, 0.62)) brightness(1.08) saturate(1.04);
}

.slide__hit {
    position: absolute;
    inset: 0;
    z-index: 2;
    padding: 0;
    background: none;
    border: 0;
    cursor: inherit;
    -webkit-tap-highlight-color: transparent;
}

.slide__hit:focus-visible {
    outline: 2px solid var(--lime-09);
    outline-offset: 4px;
}

/* ------------------------------------------------------------- INFO CARD */
/* One card, moved to whichever slide is under the cursor — twenty-four cards
   sitting in the DOM waiting would be twenty-four layout boxes inside a rail
   that has to stay cheap to move. It rides inside the rail so it tracks the
   rack for free while the scroll is still running under the cursor. */
.archive__info {
    position: absolute;
    z-index: 30;
    width: clamp(190px, 58cqh, 330px);
    box-sizing: border-box;
    padding: 1.05em 1.2em 1.15em;
    font-size: calc(var(--fs-archive-copy) * 0.82);
    background: rgba(2, 26, 30, 0.88);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border: 1px solid rgba(255, 255, 255, 0.14);
    pointer-events: none;
    opacity: 0;
    transform: translate3d(0, 12px, 0);
    filter: blur(6px);
    transition:
        opacity var(--arc-dur) var(--arc-ease),
        transform var(--arc-dur) var(--arc-ease),
        filter var(--arc-dur) var(--arc-ease);
}

.archive__info.is-open {
    opacity: 1;
    transform: translate3d(0, 0, 0);
    filter: blur(0);
}

.archive__info-tag {
    display: inline-block;
    margin: 0 0 0.7em;
    padding: 0.32em 0.72em;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 500;
    font-size: 0.66em;
    line-height: 1;
    letter-spacing: 0.13em;
    text-transform: uppercase;
    color: var(--ink-09);
    background: var(--lime-09);
}

.archive__info-title {
    margin: 0 0 0.2em;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: 1.6em;
    line-height: 1;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--paper-09);
}

.archive__info-type {
    margin: 0 0 0.55em;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 500;
    font-size: 0.8em;
    line-height: 1.2;
    color: var(--flame-09);
}

.archive__info-text {
    margin: 0;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: 0.8em;
    line-height: 1.5;
    color: rgba(255, 255, 255, 0.74);
}

/* --------------------------------------------------------- THE PIN SPACER */
/* Added to ScrollTrigger's own spacer by archive.js while the section is
   pinned, so the screen below the composition stays the archive's colour
   instead of showing the page behind it. */
.archive-spacer {
    background: #012024;
}

/* ------------------------------------------------------------- THE TAIL */
/* Built by archive.js only while the archive is the last section on the page
   and the pin would otherwise run past the end of the document. See the note
   there; its height is measured, not authored. */
.archive__tail {
    width: 100%;
    background: var(--ink-09, #012024);
}

/* ============================================================== RESPONSIVE */
/* The composition holds its exact proportions to 1024. Below that the section
   stops being a fixed-aspect canvas, the copy runs full width, and the archive
   becomes a real swipeable gallery — native overflow scrolling with snap
   points, not a pinned rail. archive.js drops the pin at the same width: a
   pinned horizontal section on a touch device fights the browser's own
   gesture handling, which is why the brief rules it out there too. */

@media (max-width: 1440px) {

    .archive {
        min-height: 460px;
    }
}

@media (max-width: 1199px) {

    .archive {
        aspect-ratio: auto;
        min-height: 0;
        padding: clamp(3.5rem, 9vw, 6rem) 0;
        overflow: hidden;
    }

    .archive__copy-pointer,
    .archive__index-pointer {
        position: static;
        inset: auto;
        display: contents;
    }

    .archive__index,
    .archive__label,
    .archive__title,
    .archive__progress,
    .archive__copy,
    .archive__cta,
    .archive__nav,
    .archive__drag {
        position: static;
        width: auto;
        margin-left: clamp(1.25rem, 4vw, 3rem);
        margin-right: clamp(1.25rem, 4vw, 3rem);
    }

    .archive__index {
        display: block;
        margin-bottom: 0.3em;
    }

    .archive__label {
        display: block;
        margin-bottom: 1.4em;
    }

    .archive__progress {
        width: clamp(5rem, 22vw, 9rem);
        margin-top: clamp(1.5rem, 4vw, 2.25rem);
    }

    .archive__copy {
        max-width: 32rem;
        margin-top: clamp(1.25rem, 3vw, 1.75rem);
    }

    .archive__cta {
        width: max-content;
        height: auto;
        padding: 1.1em 2.2em;
        margin-top: clamp(1.75rem, 4vw, 2.5rem);
    }

    /* The gallery: a native swipeable strip with momentum, in flow. Size
       containment goes with the fixed-aspect canvas — the strip is now sized
       by its own contents. */
    .archive__viewport {
        position: relative;
        top: auto;
        bottom: auto;
        left: auto;
        right: auto;
        container-type: normal;
        margin-top: clamp(2.5rem, 7vw, 4rem);
        overflow-x: auto;
        overflow-y: hidden;
        scroll-snap-type: x proximity;
        scroll-padding-left: clamp(1.25rem, 4vw, 3rem);
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        cursor: default;
    }

    .archive__viewport::-webkit-scrollbar {
        display: none;
    }

    .archive__viewport-pointer {
        position: static;
        inset: auto;
    }

    .archive__wash {
        display: none;
    }

    .rail {
        position: relative;
        top: auto;
        height: auto;
        padding: 0 clamp(1.25rem, 4vw, 3rem);
        /* Stacked, the weave has nowhere to go — one clean row of works, with
           a real gap instead of an overlap. */
        align-items: center;
        gap: clamp(0.75rem, 2.5vw, 1.5rem);
        transform: none !important;
    }

    .slide {
        height: clamp(15rem, 46vw, 24rem);
        margin-right: 0;
        scroll-snap-align: start;
    }

    .slide:nth-child(even) {
        align-self: center;
    }

    .slide__depth {
        transform: none !important;
    }

    /* Nav sits under the gallery and still works. */
    .archive__nav {
        display: flex;
        margin-top: clamp(1.5rem, 4vw, 2.25rem);
    }

    .archive__drag {
        display: block;
        margin-top: 1.2em;
    }

    .archive__info {
        display: none;
    }

    :where(.archive) {
        --fs-archive: clamp(2.4rem, 8.4vw, 5.5rem);
        --fs-archive-index: clamp(1.3rem, 4.2vw, 2.8rem);
        --fs-archive-copy: clamp(0.9rem, 2vw, 1.2rem);
        --fs-archive-label: clamp(0.68rem, 1.5vw, 0.9rem);
    }

    .archive__arrow {
        width: 3rem;
        height: 3rem;
    }
}

@media (max-width: 767px) {

    :where(.archive) {
        --fs-archive: clamp(2.1rem, 11.4vw, 4.5rem);
        --fs-archive-index: clamp(1.2rem, 6vw, 2.4rem);
        --fs-archive-copy: clamp(0.9rem, 3.6vw, 1.15rem);
        --fs-archive-label: clamp(0.66rem, 2.9vw, 0.85rem);
    }

    .slide {
        height: clamp(13rem, 62vw, 20rem);
    }
}

@media (max-width: 560px) {

    :where(.archive) {
        --fs-archive: clamp(1.9rem, 12.6vw, 3.4rem);
    }
}

/* ----------------------------------------------------- TOUCH / NO HOVER */
@media (hover: none) {

    .slide.is-hovered .slide__lift {
        transform: none;
        filter: none;
    }
}

/* ------------------------------------------------------------ ARMED STATES */
/* Applied only once GSAP is confirmed (`is-armed` goes on the <html> in
   main.js). Without JS the section renders complete: the rail is a plain
   overflow-x strip and every work is reachable. */
.is-armed .archive__title .char {
    display: inline-block;
    transform: translate3d(0, 40px, 0);
    opacity: 0;
    will-change: transform, opacity;
}

.is-armed .archive__index,
.is-armed .archive__label,
.is-armed .archive__copy,
.is-armed .archive__cta,
.is-armed .archive__nav,
.is-armed .archive__drag,
.is-armed .slide {
    opacity: 0;
}

/* --------------------------------------------------------- REDUCED MOTION */
/* Every armed state above is mirrored here. The pin, the scrub, the parallax
   and the float all go with it — archive.js leaves the rail as a plain
   scrollable strip, which keeps every work reachable without any motion at
   all. */
@media (prefers-reduced-motion: reduce) {

    .is-armed .archive__title .char {
        transform: none;
        opacity: 1;
    }

    .is-armed .archive__index,
    .is-armed .archive__label,
    .is-armed .archive__copy,
    .is-armed .archive__cta,
    .is-armed .archive__nav,
    .is-armed .archive__drag,
    .is-armed .slide {
        opacity: 1;
    }

    .archive__progress-fill {
        transform: scaleX(1);
    }

    .slide.is-hovered .slide__lift {
        transform: none;
    }

    .archive__cta:hover,
    .archive__cta:focus-visible,
    .archive__arrow:hover,
    .archive__arrow:focus-visible {
        transform: none;
    }

    .archive__info {
        transform: none;
        filter: none;
    }

    .archive__info.is-open {
        transform: none;
    }

    /* The rack still has to be reachable: archive.js keeps the drag and the
       arrows, and repositions the rail outright instead of animating to it.
       What does go is every layer that only exists to create depth. */
    .archive__wash,
    .slide__depth {
        transform: none !important;
    }
}

/* ============================================================================
   SECTION 09 — HEADING REVEAL + BALANCED BAND SPACING
   Appended only; nothing above this line changed.
   ========================================================================== */

/* ------------------------------------------------------ 80px TOP + BOTTOM */
/* Equal breathing room above and below the band, added as a BORDER rather
   than padding — deliberately.
   
   Every element in this section is absolutely positioned by percentage
   (label 7.9%, title 12.1%, progress 54.242%, nav 88.788%, drag 91.5%), and
   for an absolutely positioned child the containing block is the PADDING box.
   Padding would therefore rescale every one of those percentages and shift
   the whole composition. A border sits outside the padding box, so the
   percentage system is untouched — the layout does not move by a pixel, and
   the section simply gains 80px of its own ink at each end. */
.archive {
    border-top: 80px solid var(--ink-09);
    border-bottom: 80px solid var(--ink-09);
}

/* ------------------------------------------------------- HEADING SCAFFOLD */
/* The armed pre-state translates .char 40px down at opacity 0, which belonged
   to the old line-rise entrance. The rotateY reveal sets its own start state
   when it builds, so that would only fight it — and would leave the type
   invisible if the split ever failed. Neutralised. */
.is-armed .archive__title .char {
    transform: none;
    opacity: 1;
}

/* Glyphs are inline-block with `vertical-align: top`, pinned to the TOP of
   their line box. .archive__line owns the `overflow: hidden` that masks the
   reveal, so a line box shorter than Bebas' ~1.2em glyph box shears the
   bottoms off the letters. Give the box real height, then pull the surplus
   back out so the visual line advance is unchanged. */
.archive__line {
    line-height: 1.3;
    margin-top: -0.03em;
    margin-bottom: -0.3em;
}

.archive__title .hero__line-inner {
    display: block;
    line-height: inherit;
}

.archive__title .hero__word {
    display: inline-block;
    vertical-align: top;
    white-space: nowrap;
}

.archive__title .char {
    display: inline-block;
    vertical-align: top;
    backface-visibility: hidden;
    will-change: transform, opacity;
}

/* Nothing may stay hidden if the timeline never runs. */
@media (prefers-reduced-motion: reduce) {

    .archive__title .char,
    .archive__title .hero__word,
    .archive__title .hero__line-inner {
        transform: none !important;
        opacity: 1 !important;
        visibility: visible !important;
    }
}

@media (max-width: 760px) {

    .archive {
        border-top-width: 48px;
        border-bottom-width: 48px;
    }
}

/* ============================================================================
   SECTION 09 — SIX-WORK SLIDER
   The rail was replaced by six large cards, a numbered meter and bottom-right
   nav. Palette, type family and the copy column are untouched: the ink, flame
   and lime tokens declared at the top of this file are the only colours used.
   ========================================================================== */

.archive__stage {
    position: absolute;
    inset: 0;
    display: block;
    perspective: 1800px;
    perspective-origin: 50% 45%;
}

/* ------------------------------------------------------------- THE CARDS */
.works {
    position: absolute;
    left: 50%;
    top: 46%;
    width: 100%;
    height: 74%;
    transform: translate(-50%, -50%);
    transform-style: preserve-3d;
}

.work {
    position: absolute;
    left: 50%;
    top: 50%;
    /* Substantially larger than the old rail slides — the active card is the
       section's focal point now, not one of two dozen thumbnails. */
    width: clamp(230px, 21vw, 400px);
    height: 100%;
    margin: 0;
    transform: translate(-50%, -50%);
    transform-style: preserve-3d;
    will-change: transform, opacity;
}

.work__card {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
    border-radius: 14px;
    background: #06171A;
    box-shadow: 0 30px 70px rgba(0, 0, 0, 0.55);
    transition: box-shadow 0.5s ease;
}

.work.is-active .work__card {
    box-shadow: 0 40px 90px rgba(0, 0, 0, 0.7);
    outline: 1px solid rgba(170, 193, 2, 0.55);
    outline-offset: -1px;
}

.work__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center 22%;
    display: block;
}

/* Veil deepens on hover so the detail type has something to sit on. */
.work__veil {
    position: absolute;
    inset: 0;
    background: linear-gradient(to top,
        rgba(1, 32, 36, 0.92) 0%,
        rgba(1, 32, 36, 0.55) 34%,
        rgba(1, 32, 36, 0.05) 68%);
    opacity: 0.55;
    transition: opacity 0.5s ease;
    pointer-events: none;
}

.work.is-open .work__veil,
.work.is-active .work__veil {
    opacity: 1;
}

/* ---------------------------------------------------- DETAIL, IN THE IMAGE */
/* Sits inside the card, not in a floating panel beside it. Translated down and
   clipped until the card is hovered, focused or active. */
.work__detail {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 6% 8% 7%;
    color: var(--paper-09);
    opacity: 0;
    transform: translateY(14px);
    transition: opacity 0.45s ease, transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
    pointer-events: none;
}

.work.is-open .work__detail,
.work.is-active .work__detail {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}

.work__eyebrow {
    display: flex;
    justify-content: space-between;
    margin: 0 0 6%;
    font-family: "Inter", system-ui, sans-serif;
    font-size: clamp(0.5rem, 0.62vw, 11px);
    letter-spacing: 0.18em;
    text-transform: uppercase;
    opacity: 0.75;
}

.work__title {
    margin: 0 0 2%;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: clamp(1.15rem, 1.9vw, 2.3rem);
    line-height: 1;
    letter-spacing: 0.01em;
    text-transform: uppercase;
}

.work__kind,
.work__text {
    margin: 0 0 3%;
    font-family: "Inter", system-ui, sans-serif;
    font-size: clamp(0.56rem, 0.72vw, 13px);
    line-height: 1.45;
    opacity: 0.82;
}

.work__kind {
    letter-spacing: 0.1em;
    text-transform: uppercase;
    opacity: 0.6;
}

.work__tags {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    margin: 0 0 6%;
}

.work__tags span {
    padding: 4px 9px;
    border: 1px solid rgba(255, 255, 255, 0.28);
    border-radius: 999px;
    font-family: "Inter", system-ui, sans-serif;
    font-size: clamp(0.46rem, 0.58vw, 10px);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    white-space: nowrap;
}

.work__cta {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 9px 16px;
    border: 1px solid var(--lime-09);
    border-radius: 2px;
    color: var(--lime-09);
    font-family: "Inter", system-ui, sans-serif;
    font-size: clamp(0.5rem, 0.62vw, 11px);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    text-decoration: none;
    transition: background 0.35s ease, color 0.35s ease;
}

.work__cta:hover,
.work__cta:focus-visible {
    background: var(--lime-09);
    color: var(--ink-09);
}

/* ------------------------------------------------------------- FILM CARD */
.work__play {
    position: absolute;
    left: 50%;
    top: 42%;
    z-index: 3;
    width: clamp(46px, 4.2vw, 74px);
    aspect-ratio: 1;
    transform: translate(-50%, -50%);
    display: grid;
    place-items: center;
    border: 1.5px solid rgba(255, 255, 255, 0.85);
    border-radius: 50%;
    background: rgba(1, 32, 36, 0.35);
    color: #FFFFFF;
    cursor: pointer;
    backdrop-filter: blur(3px);
    transition: background 0.35s ease, transform 0.35s ease;
}

.work__play:hover,
.work__play:focus-visible {
    background: var(--flame-09);
    transform: translate(-50%, -50%) scale(1.08);
}

.work__play svg {
    width: 34%;
    fill: currentColor;
}

/* The Between Worlds play-icon override is gone with the video itself. That
   card no longer carries a video URL, so front-page.php renders neither
   .work__play nor .work__frame for it and there is nothing left to hide.
   .work--film and the rules below stay generic, so any archive_project given
   a real video later still gets the icon and the inline player. */

.work__frame {
    position: absolute;
    inset: 0;
    z-index: 4;
    background: #000;
}

.work__frame iframe {
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
}

.work--film.is-playing .work__detail,
.work--film.is-playing .work__veil {
    opacity: 0;
    pointer-events: none;
}

/* ----------------------------------------------------- METER (BELOW RAIL) */
.works__meter {
    position: absolute;
    left: 4%;
    right: 22%;
    bottom: 12%;
    z-index: 6;
}

.works__ticks {
    display: flex;
    justify-content: space-between;
    margin-bottom: 10px;
}

.works__tick {
    font-family: "Inter", system-ui, sans-serif;
    font-size: clamp(0.5rem, 0.72vw, 13px);
    letter-spacing: 0.14em;
    color: rgba(255, 255, 255, 0.4);
    cursor: pointer;
    transition: color 0.35s ease, transform 0.35s ease;
}

.works__tick.is-on {
    color: var(--lime-09);
    transform: translateY(-2px);
}

.works__track {
    position: relative;
    height: 1px;
    background: rgba(255, 255, 255, 0.22);
}

.works__fill {
    position: absolute;
    inset: 0;
    background: var(--lime-09);
    transform: scaleX(0);
    transform-origin: left center;
}

.works__dot {
    position: absolute;
    top: 50%;
    left: 0;
    width: 9px;
    height: 9px;
    margin: -4.5px 0 0 -4.5px;
    border-radius: 50%;
    background: var(--lime-09);
    box-shadow: 0 0 0 4px rgba(170, 193, 2, 0.18);
}

.works__hint {
    position: absolute;
    left: 4%;
    bottom: 4%;
    z-index: 6;
    margin: 0;
    font-family: "Inter", system-ui, sans-serif;
    font-size: clamp(0.5rem, 0.72vw, 13px);
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.5);
}

/* --------------------------------------------------- NAV (BOTTOM RIGHT) */
.works__nav {
    position: absolute;
    right: 4%;
    bottom: 8%;
    z-index: 6;
    display: flex;
    gap: 12px;
}

.works__arrow {
    width: clamp(34px, 3vw, 52px);
    aspect-ratio: 1;
    display: grid;
    place-items: center;
    border: 1px solid rgba(255, 255, 255, 0.4);
    border-radius: 50%;
    background: transparent;
    color: var(--paper-09);
    cursor: pointer;
    transition: border-color 0.35s ease, background 0.35s ease, opacity 0.35s ease;
}

.works__arrow--next {
    border-color: var(--lime-09);
    color: var(--lime-09);
}

.works__arrow:hover:not(.is-spent) {
    background: var(--lime-09);
    border-color: var(--lime-09);
    color: var(--ink-09);
}

.works__arrow.is-spent {
    opacity: 0.3;
    cursor: default;
}

.works__arrow svg {
    width: 42%;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.archive.is-dragging .archive__stage {
    cursor: grabbing;
}

.archive__stage {
    cursor: grab;
}

/* --------------------------------------------------------- MOBILE STRIP */
@media (max-width: 1199px) {

    .archive__stage {
        perspective: none;
        cursor: auto;
    }

    .works {
        position: absolute;
        left: 0;
        top: 44%;
        width: 100%;
        height: 70%;
        transform: translateY(-50%);
        display: flex;
        gap: 14px;
        padding: 0 6vw;
        overflow-x: auto;
        overflow-y: hidden;
        scroll-snap-type: x mandatory;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
    }

    .works::-webkit-scrollbar {
        display: none;
    }

    .work {
        position: relative;
        left: auto;
        top: auto;
        flex: 0 0 auto;
        width: min(62vw, 300px);
        height: 100%;
        transform: none !important;
        opacity: 1 !important;
        scroll-snap-align: center;
    }

    /* Touch has no hover — the detail is simply always readable. */
    .work__detail {
        opacity: 1;
        transform: none;
    }

    .works__meter {
        left: 6vw;
        right: 6vw;
        bottom: 14%;
    }

    .works__nav {
        right: 6vw;
        bottom: 3%;
    }

    .works__hint {
        left: 6vw;
        bottom: 4%;
    }
}

@media (prefers-reduced-motion: reduce) {

    .work {
        transform: none !important;
        opacity: 1 !important;
    }

    .work__detail {
        opacity: 1;
        transform: none;
    }
}

/* ============================================================================
   SECTION 09 — FULL-HEIGHT BAND, CENTRED, TALLER CARDS
   Appended last, so these win. Nothing above is edited.
   ========================================================================== */

/* ------------------------------------------------------------ FULL HEIGHT */
/* The section becomes a full viewport, and the whole composition is centred
   inside it rather than stretched to fill it.

   The three carriers below are what makes that safe. Every element in this
   section is positioned by percentage against its carrier, and the carriers
   were `inset: 0` — so simply making the section taller would have stretched
   the copy column and the rack apart. Instead each carrier keeps the original
   1026:330 proportion and is centred vertically, so the internal composition
   is identical and only the surrounding space grows. */
.archive {
    aspect-ratio: auto;
    min-height: 100vh;
    /* Border replaced the old 80px pair — full height supplies the breathing
       room now, and the border would double it. */
    border-top: 0;
    border-bottom: 0;
    display: block;
}

.archive__copy-pointer,
.archive__index-pointer,
.archive__viewport {
    top: 50%;
    bottom: auto;
    height: min(86vh, calc(100vw * 330 / 1026 * 1.5));
    transform: translateY(-50%);
}

/* The pointer carriers also carry a parallax transform from the rig, which
   would overwrite the centring translate. Their own wrapper handles it. */
.archive__copy-pointer,
.archive__index-pointer {
    transform: translateY(-50%);
}

/* -------------------------------------------------------- TALLER ARTWORK */
/* The cards fill their box now: the rack occupies nearly the whole band and
   each image covers its card edge to edge. */
.works {
    top: 44%;
    height: 92%;
}

.work {
    width: clamp(250px, 23vw, 440px);
}

.work__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center center;
}

/* The meter and hint move up off the section floor now the band is centred. */
.works__meter {
    bottom: 4%;
}

.works__hint {
    bottom: -2%;
}

/* ------------------------------------------ ARROWS: LEFT COLUMN, UNDER CTA */
/* They were already in the markup under the CTA — this only makes them read
   as the slider's controls and gives them a spent state. */
.archive__nav {
    z-index: 7;
    pointer-events: auto;
}

.archive__arrow {
    cursor: pointer;
    transition: border-color 0.35s ease, background 0.35s ease,
                color 0.35s ease, opacity 0.35s ease;
}

.archive__arrow:hover:not(.is-spent) {
    background: var(--lime-09);
    border-color: var(--lime-09);
    color: var(--ink-09);
}

.archive__arrow.is-spent {
    opacity: 0.3;
    cursor: default;
}

@media (max-width: 1199px) {

    .archive {
        min-height: 0;
    }

    .archive__copy-pointer,
    .archive__index-pointer,
    .archive__viewport {
        top: auto;
        bottom: auto;
        height: auto;
        transform: none;
    }

    .works {
        top: 44%;
        height: 76%;
    }
}

/* ============================================================================
   SECTION 09 — CHROME SAFETY NET
   ========================================================================== */

/* The armed state hides the label, copy, CTA, arrows and drag hint until the
   entrance reveals them. If that timeline never runs — a JS failure, a blocked
   CDN — they would stay invisible forever, and the arrows are the slider's
   only controls. This restores them once the document is no longer armed, and
   the reveal itself still animates normally when JS is healthy. */
html:not(.is-armed) .archive__index,
html:not(.is-armed) .archive__label,
html:not(.is-armed) .archive__copy,
html:not(.is-armed) .archive__cta,
html:not(.is-armed) .archive__nav,
html:not(.is-armed) .archive__drag {
    opacity: 1;
}

/* The .slide rule targets markup the rebuild replaced; the cards must never
   inherit an armed hide. */
.is-armed .work {
    opacity: 1;
}

/* The arrows sit under the CTA in the copy column and must be clickable —
   their carrier is pointer-transparent so the rack behind stays hoverable. */
.archive__copy-pointer {
    pointer-events: none;
}

.archive__cta,
.archive__nav,
.archive__arrow {
    pointer-events: auto;
}

/* ============================================================================
   SECTION 09 — ACTIVE CARD CENTRING
   ========================================================================== */

/* The centring translate now lives in the GSAP transform (see place() in
   archive.js), so the CSS one is removed to avoid declaring it twice — the
   stylesheet only positions the anchor point. */
.work {
    transform: none;
}

/* The deck's midline is the viewport region's centre. Nudged left of the
   region's geometric centre so the active card reads as centred against the
   whole rack rather than against its own column, which is what pushed it
   toward the right edge of the screen. */
.works {
    left: 46%;
}

@media (max-width: 1199px) {

    /* The strip lays cards out in flow, so neither rule applies there. */
    .works {
        left: 0;
    }

    .work {
        transform: none !important;
    }
}

/* ============================================================================
   SECTION 09 — RESPONSIVE FIX (<= 1199px)
   Appended last, so this is the final word on the strip.

   WHAT WAS BROKEN: below 1200 the original design reflows the whole section
   into normal flow — the copy carriers become `display: contents`, and
   .archive__viewport itself becomes `position: relative` and acts as the
   horizontal scroller. My earlier rules left `.works` absolutely positioned
   with a percentage height inside that now-static viewport, so the viewport
   measured zero and the entire slider vanished. These rules put the deck back
   into flow and give it a real height.
   ========================================================================== */

@media (max-width: 1199px) {

    /* The section takes its height from content again — no viewport lock. */
    .archive {
        aspect-ratio: auto;
        min-height: 0;
        border-top: 0;
        border-bottom: 0;
    }

    /* Undo the desktop centring: below 1200 these are in flow, so a
       translate and a fixed height would only fight the reflow. */
    .archive__copy-pointer,
    .archive__index-pointer {
        top: auto;
        bottom: auto;
        height: auto;
        transform: none;
    }

    /* The viewport is the scroller here, not a window onto a 3D rack. */
    .archive__viewport {
        position: relative;
        top: auto;
        bottom: auto;
        left: auto;
        right: auto;
        height: auto;
        transform: none;
        perspective: none;
    }

    .archive__viewport-pointer,
    .archive__stage {
        position: static;
        inset: auto;
        height: auto;
        perspective: none;
        cursor: auto;
    }

    /* THE DECK, in flow: a real horizontal scroller the thumb can flick. */
    .works {
        position: relative;
        left: auto;
        top: auto;
        width: auto;
        /* An explicit height is what the absolute version was missing — with
           `height: 76%` of a zero-height parent there was nothing to show. */
        height: clamp(20rem, 62vw, 30rem);
        transform: none;
        display: flex;
        align-items: stretch;
        gap: clamp(0.75rem, 2.5vw, 1.5rem);
        padding: 0 clamp(1.25rem, 4vw, 3rem);
        overflow-x: auto;
        overflow-y: hidden;
        scroll-snap-type: x mandatory;
        scroll-padding-left: clamp(1.25rem, 4vw, 3rem);
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
    }

    .works::-webkit-scrollbar {
        display: none;
    }

    .work {
        position: relative;
        left: auto;
        top: auto;
        flex: 0 0 auto;
        width: min(68vw, 320px);
        height: 100%;
        /* GSAP's desktop transforms must not survive the breakpoint. */
        transform: none !important;
        opacity: 1 !important;
        scroll-snap-align: center;
    }

    .work__card {
        height: 100%;
    }

    /* No hover on touch — the detail is simply always legible. */
    .work__detail {
        opacity: 1;
        transform: none;
        pointer-events: auto;
    }

    .work__veil {
        opacity: 1;
    }

    /* Meter and hint return to flow beneath the strip. */
    .works__meter {
        position: relative;
        left: auto;
        right: auto;
        bottom: auto;
        margin: clamp(1.5rem, 5vw, 2.5rem) clamp(1.25rem, 4vw, 3rem) 0;
    }

    .works__hint {
        position: relative;
        left: auto;
        bottom: auto;
        margin: clamp(0.75rem, 3vw, 1.25rem) clamp(1.25rem, 4vw, 3rem) 0;
    }
}

@media (max-width: 767px) {

    .works {
        height: clamp(17rem, 74vw, 24rem);
    }

    .work {
        width: min(76vw, 280px);
    }
}

/* ============================================================================
   SECTION 09 — NO PIN BELOW 1200
   ========================================================================== */

@media (max-width: 1199px) {

    /* Nothing in this section may pin or stick at this width. ScrollTrigger's
       pin works by wrapping the element in a `.pin-spacer` and fixing it —
       if one ever survives a resize across the breakpoint, these unwind it so
       the section cannot be left stuck to the viewport. */
    .archive {
        position: relative !important;
        top: auto !important;
        left: auto !important;
        transform: none !important;
    }

    .pin-spacer:has(> .archive) {
        padding: 0 !important;
        margin: 0 !important;
        height: auto !important;
        min-height: 0 !important;
    }
}


/* ############################################################################
   SECTION-10.CSS
   Section 10.
   ######################################################################## */

/* ============================================================================
   SECTION-10.CSS — WHAT SHOULD YOUR ICON DO FIRST?
   Additive only. No selector here touches Sections 01-09, 11 or 12.

   Rebuilt for the "decision panel" comp: each row carries a number, an arrow
   button, a heading, a sub-line and its own artwork, and choosing (or previewing
   on hover) wipes a deep-teal fill across the whole row from its left edge. The
   right-hand stage now shows a full-colour composited portrait per goal, with a
   caption that rises from the bottom as the choice is made.

   Heading reveal is now the Section-09 model: per-glyph rotateY swing. The armed
   `.char` state is neutralised here and the start state is set in choose.js, so
   the type is never left invisible if GSAP or the split ever fails.
   ========================================================================== */

/* ---------------------------------------------------------------- TOKENS */
.choose {
    --fs-choose: clamp(2.4rem, 6.6138vw, 127px);
    --fs-choose-option: clamp(1.4rem, 2.35vw, 44px);
    --fs-choose-index: clamp(1.3rem, 3.2937vw, 63px);
    --fs-choose-label: clamp(0.62rem, 0.8598vw, 16px);
    --fs-pick-no: clamp(0.85rem, 1.05vw, 20px);
    --fs-pick-sub: clamp(0.78rem, 0.86vw, 16px);

    --sand-10: #F5EADF;
    --deep-10: #073F4C;
    --ink-10: #012326;
    --index-10: #817E7E;

    --pick-ease: cubic-bezier(0.22, 1, 0.36, 1);
    --pick-dur: 0.55s;
}

/* ----------------------------------------------------------------- SHELL */
.choose {
    position: relative;
    width: 100%;
    aspect-ratio: 1026 / 508;
    min-height: 560px;
    overflow: hidden;
    background: var(--sand-10);
    isolation: isolate;
}

/* --------------------------------------------------------- TRANSFORM RIGS */
.choose__copy-pointer,
.choose__index-pointer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 3;
    will-change: transform;
}

.choose__copy-pointer .pick {
    pointer-events: auto;
}

/* ------------------------------------------------------------ LEFT COLUMN */
.choose__index {
    position: absolute;
    top: 11.6%;
    left: 2.144%;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-choose-index);
    line-height: 1;
    color: var(--index-10);
}

.choose__title {
    position: absolute;
    top: 10.9%;
    left: 8.187%;
    width: 84%;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-choose);
    line-height: 1;
    letter-spacing: 0.01em;
    text-transform: uppercase;
    color: var(--deep-10);
}

/* A whole word, unbreakable — the character spans the split produces are
   inline-blocks and the browser would otherwise break between them. */
.choose__word {
    display: inline-block;
    white-space: nowrap;
}

/* --------------- HEADING GLYPHS (Section-09 rotateY swing model) --------- */
/* wrapLines() + splitWordsAndChars() build .hero__word > .char inside each
   word. These give the glyphs the transform context the swing needs. */
.choose__title .hero__line-inner {
    display: inline;
}

.choose__title .hero__word {
    display: inline-block;
    vertical-align: top;
    white-space: nowrap;
}

.choose__title .char {
    display: inline-block;
    vertical-align: top;
    backface-visibility: hidden;
    will-change: transform, opacity;
}

/* The comp's rotated caption beside the rows, reading bottom-to-top. */
.choose__label {
    position: absolute;
    top: 37.8%;
    left: 4.581%;
    margin: 0;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: var(--fs-choose-label);
    line-height: 1;
    letter-spacing: 0.055em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--ink-10);
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    transform-origin: 50% 50%;
}

/* ================================================================ THE ROWS */
.pick {
    position: absolute;
    top: 28.5%;
    bottom: 4.17%;
    left: 8.187%;
    width: 49%;
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: clamp(0.4rem, 0.9vw, 0.85rem);
}

/* Each row shares the column height equally, so the four of them fill the same
   top-to-bottom band as the portrait beside them — no dead space under row 04. */
.pick > li {
    flex: 1 1 0;
    display: flex;
    min-height: 0;
}

.pick__row {
    position: relative;
    display: grid;
    grid-template-columns: auto auto minmax(0, 1fr) 33%;
    align-items: center;
    column-gap: clamp(0.6rem, 1vw, 1.1rem);
    width: 100%;
    height: 100%;
    padding: clamp(0.55rem, 0.95vw, 0.95rem) clamp(0.7rem, 1.1vw, 1.15rem);
    border: 0;
    background: none;
    text-align: left;
    font: inherit;
    color: inherit;
    cursor: pointer;
    overflow: hidden;
    isolation: isolate;
    -webkit-tap-highlight-color: transparent;
    transition: transform var(--pick-dur) var(--pick-ease);
}

/* The divider under each row, drawn on a pseudo so the fill can sit above it
   and cover it cleanly when a row is chosen. */
.pick__row::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 1px;
    background: rgba(7, 63, 76, 0.42);
    z-index: 1;
}

.pick__row:focus-visible {
    outline: 2px solid var(--deep-10);
    outline-offset: 3px;
}

/* ---- THE FILL: the smooth teal wipe that covers the row background ---- */
.pick__fill {
    position: absolute;
    inset: 0;
    z-index: 0;
    background: var(--deep-10);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform var(--pick-dur) var(--pick-ease);
    will-change: transform;
}

.pick__row.is-active .pick__fill,
.pick__row:hover .pick__fill {
    transform: scaleX(1);
}

/* Everything visible sits above the fill. */
.pick__no,
.pick__cta,
.pick__text,
.pick__art {
    position: relative;
    z-index: 2;
}

/* ---- THE NUMBER ---- */
.pick__no {
    align-self: center;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-pick-no);
    line-height: 1;
    letter-spacing: 0.04em;
    color: rgba(7, 63, 76, 0.55);
    transition: color var(--pick-dur) var(--pick-ease);
}

/* ---- THE ARROW BUTTON ---- */
.pick__cta {
    display: grid;
    place-items: center;
    width: clamp(1.9rem, 2.5vw, 2.75rem);
    height: clamp(1.9rem, 2.5vw, 2.75rem);
    border: 1px solid rgba(7, 63, 76, 0.55);
    border-radius: 50%;
    color: var(--deep-10);
    background: transparent;
    transition:
        background var(--pick-dur) var(--pick-ease),
        border-color var(--pick-dur) var(--pick-ease),
        color var(--pick-dur) var(--pick-ease);
}

.pick__cta svg {
    width: 42%;
    height: 42%;
    display: block;
}

.pick__row.is-active .pick__cta,
.pick__row:hover .pick__cta {
    background: #FFFFFF;
    border-color: #FFFFFF;
    color: var(--deep-10);
}

/* ---- THE TEXT BLOCK ---- */
.pick__text {
    display: flex;
    flex-direction: column;
    gap: 0.18em;
    min-width: 0;
}

.pick__name {
    display: block;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-choose-option);
    line-height: 1.02;
    letter-spacing: 0.01em;
    text-transform: uppercase;
    color: var(--deep-10);
    transition: color var(--pick-dur) var(--pick-ease);
}

.pick__sub {
    display: block;
    margin: 0;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: var(--fs-pick-sub);
    line-height: 1.3;
    color: rgba(1, 35, 38, 0.68);
    transition: color var(--pick-dur) var(--pick-ease);
}

.pick__row.is-active .pick__no,
.pick__row:hover .pick__no {
    color: rgba(245, 234, 223, 0.72);
}

.pick__row.is-active .pick__name,
.pick__row:hover .pick__name {
    color: var(--sand-10);
}

.pick__row.is-active .pick__sub,
.pick__row:hover .pick__sub {
    color: rgba(245, 234, 223, 0.82);
}

/* ---- THE ROW ARTWORK ---- */
.pick__art {
    align-self: stretch;
    width: 100%;
    height: 100%;
    overflow: hidden;
    border-radius: 2px;
    background: rgba(7, 63, 76, 0.08);
}

.pick__art-in {
    display: block;
    width: 100%;
    height: 100%;
    clip-path: inset(0 0 0 0);
    will-change: clip-path;
}

.pick__art img {
    display: block;
    width: 100%;
    height: 100%;
    max-width: none;
    object-fit: cover;
    object-position: 50% 32%;
    filter: saturate(0.9);
    transform: scale(1.1);
    transition:
        opacity var(--pick-dur) var(--pick-ease),
        filter var(--pick-dur) var(--pick-ease),
        transform 0.75s var(--pick-ease);
}

.pick__row.is-active .pick__art img,
.pick__row:hover .pick__art img {
    filter: saturate(1.02);
    transform: scale(1);
}

/* ------------------------------------------------------------- THE STAGE */
/* .choose__stage {
    position: absolute;
    top: 20%;
    left: 60%;
    width: 40%;
    aspect-ratio: 768 / 640;
    z-index: 2;
    pointer-events: none;
    will-change: transform;
} */


.choose__stage {
    position: absolute;
    top: 16%;
    left: 60%;
    width: 36%;
    aspect-ratio: 686 / 860;
    z-index: 2;
    pointer-events: none;
    will-change: transform;
}

.choose__portrait {
    position: absolute;
    inset: 0;
    margin: 0;
    opacity: 0;
    isolation: isolate;
    will-change: transform, opacity;
}

.choose__portrait.is-shown {
    opacity: 1;
}

/* The supplied composites already carry their own teal halo and bottom fade,
   so they are simply covered into the stage box in full colour — no grayscale,
   no mask. The vertical anchor keeps the face in frame after the side crop. */
.choose__portrait--art img {
    display: block;
    width: 100%;
    height: 100%;
    max-width: none;
    object-fit: cover;
    object-position: 50% 16%;
}

/* The composites fade to light only at their very bottom edge, so a caption
   placed higher would sit over the dark blazer. This feathers the lower band
   back to the section's sand so the dark caption keeps its contrast — the
   image already fades this way, so it reads as more of the same fade, not a
   panel. Scoped beneath the caption by the figure's own stacking context. */
/* .choose__portrait--art::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 20%;
    z-index: 1;
    pointer-events: none;
    background: linear-gradient(
        to top,
        rgba(245, 234, 223, 0.97) 12%,
        rgba(245, 234, 223, 0.72) 42%,
        rgba(245, 234, 223, 0) 100%
    );
} */

/* ---- THE CAPTION: rises from the bottom as the choice is made ---- */
.choose__caption {
    position: absolute;
    left: 10%;
    right: 7%;
    bottom: 5.5%;
    z-index: 2;
    display: flex;
    flex-direction: column;
    gap: 0.3em;
}

.choose__caption-tag {
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 600;
    font-size: clamp(0.6rem, 0.72vw, 0.8rem);
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--deep-10);
}

.choose__caption-name {
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: clamp(1.5rem, 2.35vw, 2.9rem);
    line-height: 1;
    letter-spacing: 0.01em;
    text-transform: uppercase;
    color: var(--ink-10);
}

.choose__caption-desc {
    max-width: 26ch;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: clamp(0.78rem, 0.9vw, 1rem);
    line-height: 1.4;
    color: rgba(1, 35, 38, 0.72);
}

/* Each line starts below its resting place and lifts in with a small stagger,
   revealed only when its portrait is shown. */
.choose__caption > * {
    opacity: 0;
    transform: translateY(22px);
    transition:
        opacity 0.6s var(--pick-ease),
        transform 0.6s var(--pick-ease);
}

.choose__portrait.is-shown .choose__caption > * {
    opacity: 1;
    transform: translateY(0);
}

.choose__portrait.is-shown .choose__caption-tag {
    transition-delay: 0.12s;
}

.choose__portrait.is-shown .choose__caption-name {
    transition-delay: 0.2s;
}

.choose__portrait.is-shown .choose__caption-desc {
    transition-delay: 0.3s;
}

/* ============================================================== RESPONSIVE */
@media (max-width: 1199px) {

    .choose {
        aspect-ratio: auto;
        min-height: 0;
        padding: clamp(3.5rem, 9vw, 6rem) 0 0;
    }

    .choose__copy-pointer,
    .choose__index-pointer {
        position: static;
        inset: auto;
        display: contents;
    }

    .choose__index,
    .choose__title,
    .pick,
    .choose__stage {
        position: static;
        width: auto;
        margin-left: clamp(1.25rem, 4vw, 3rem);
        margin-right: clamp(1.25rem, 4vw, 3rem);
    }

    .choose__index {
        display: block;
        margin-bottom: 0.15em;
    }

    .choose__label {
        position: static;
        display: block;
        writing-mode: horizontal-tb;
        transform: none;
        margin: 1.4em clamp(1.25rem, 4vw, 3rem) 0;
        opacity: 0.7;
    }

    .pick {
        margin-top: clamp(1.75rem, 5vw, 2.5rem);
    }

    /* In the stack there is no fixed band to fill, so rows return to their
       natural content height. */
    .pick > li {
        flex: 0 0 auto;
    }

    .pick__row {
        grid-template-columns: auto auto minmax(0, 1fr) 34%;
        column-gap: clamp(0.6rem, 3vw, 1.25rem);
        padding: clamp(0.75rem, 2.5vw, 1.1rem) clamp(0.75rem, 2.5vw, 1.1rem);
        height: auto;
    }

    .pick__row + .pick__row {
        margin-top: clamp(0.5rem, 1.6vw, 0.9rem);
    }

    .pick__art {
        aspect-ratio: 16 / 10;
        align-self: center;
        height: auto;
    }

    .pick__name {
        white-space: normal;
    }

    /* In the stack the stage is in flow, so it needs a real box of its own. */
    .choose__stage {
        position: relative;
        bottom: auto;
        left: auto;
        display: block;
        width: min(30rem, 90%);
        margin: clamp(2rem, 6vw, 3rem) auto 0;
    }

    /* NOT :where() — equal specificity plus later position is what overrides
       the token block at the top of the file. */
    .choose {
        --fs-choose: clamp(2.4rem, 8.4vw, 5.5rem);
        --fs-choose-option: clamp(1.4rem, 4.6vw, 3rem);
        --fs-choose-index: clamp(1.3rem, 4.2vw, 2.8rem);
        --fs-choose-label: clamp(0.7rem, 1.6vw, 0.9rem);
        --fs-pick-no: clamp(0.8rem, 2.2vw, 1.15rem);
        --fs-pick-sub: clamp(0.8rem, 2vw, 0.95rem);
    }
}

@media (max-width: 767px) {

    .choose {
        --fs-choose: clamp(2.1rem, 11.4vw, 4.5rem);
        --fs-choose-option: clamp(1.3rem, 6.4vw, 2.4rem);
        --fs-choose-index: clamp(1.2rem, 6vw, 2.4rem);
        --fs-choose-label: clamp(0.68rem, 2.9vw, 0.85rem);
    }

    /* Below the phone breakpoint the letterbox thumbnail is too small to read a
       face in, so it is dropped and the row is number + arrow + text only. */
    .pick__row {
        grid-template-columns: auto auto minmax(0, 1fr);
    }

    .pick__art {
        display: none;
    }

    .choose__caption {
        left: 6%;
        right: 6%;
    }
}

@media (max-width: 560px) {

    .choose {
        --fs-choose: clamp(1.9rem, 12.6vw, 3.4rem);
    }
}

/* ----------------------------------------------------- TOUCH / NO HOVER */
/* On a touch device there is no cursor to preview with, so only the chosen row
   is filled — a `:hover` that latches after a tap would leave two rows lit. */
@media (hover: none) {

    .pick__row:hover .pick__fill {
        transform: scaleX(0);
    }

    .pick__row:hover .pick__no {
        color: rgba(7, 63, 76, 0.55);
    }

    .pick__row:hover .pick__name {
        color: var(--deep-10);
    }

    .pick__row:hover .pick__sub {
        color: rgba(1, 35, 38, 0.68);
    }

    .pick__row:hover .pick__cta {
        background: transparent;
        border-color: rgba(7, 63, 76, 0.55);
        color: var(--deep-10);
    }

    .pick__row:hover .pick__art img {
        opacity: 0.82;
        filter: saturate(0.9);
        transform: scale(1.1);
    }

    .pick__row.is-active .pick__fill {
        transform: scaleX(1);
    }

    .pick__row.is-active .pick__no {
        color: rgba(245, 234, 223, 0.72);
    }

    .pick__row.is-active .pick__name {
        color: var(--sand-10);
    }

    .pick__row.is-active .pick__sub {
        color: rgba(245, 234, 223, 0.82);
    }

    .pick__row.is-active .pick__cta {
        background: #FFFFFF;
        border-color: #FFFFFF;
        color: var(--deep-10);
    }

    .pick__row.is-active .pick__art img {
        opacity: 1;
        filter: saturate(1.02);
        transform: scale(1);
    }
}

/* ------------------------------------------------------------ ARMED STATES */
/* Applied only once GSAP is confirmed (`is-armed` on <html> in main.js).
   Without JS the section renders complete: every option is readable, every row
   is a real button, and the first is already selected. */
.is-armed .choose__index,
.is-armed .choose__label,
.is-armed .pick__row,
.is-armed .choose__stage {
    opacity: 0;
}

/* The rotateY reveal sets its own start state in choose.js; the old downward
   translate would only fight it and would strand the type invisible if the
   split ever failed. Neutralised, exactly as Section 09 does. */
.is-armed .choose__title .char {
    transform: none;
    opacity: 1;
}

/* --------------------------------------------------------- REDUCED MOTION */
@media (prefers-reduced-motion: reduce) {

    .is-armed .choose__title .char {
        transform: none;
        opacity: 1;
    }

    .is-armed .choose__index,
    .is-armed .choose__label,
    .is-armed .pick__row,
    .is-armed .choose__stage {
        opacity: 1;
    }

    .pick__fill,
    .pick__no,
    .pick__cta,
    .pick__name,
    .pick__sub,
    .pick__art img,
    .choose__caption > * {
        transition: none;
    }

    .pick__art-in {
        clip-path: none;
    }

    .pick__art img {
        transform: none;
    }

    /* The choice itself is not motion, so the caption simply sits shown. */
    .choose__portrait.is-shown .choose__caption > * {
        opacity: 1;
        transform: none;
    }
}


/* ############################################################################
   SECTION-11.CSS
   Section 11.
   ######################################################################## */

/* ============================================================================
   SECTION-11.CSS — LET'S CREATE SOMEONE IMPOSSIBLE TO IGNORE.
   Additive only. No selector here touches Sections 01-10 or 12. `.contact*`,
   `.step*`, `.goal*`, `.field*` and `.news*` were all confirmed unused before
   a line of this was written — Section 05 shipped a `.reel` collision with
   Section 01's showreel modal, and that check is now the first step of every
   section.

   Reference canvas: the comp's section band is 1026 x 460 (2.230:1). Measured
   off it in pixels:

     11 index     x 22, cap top 57
     heading      x 84, first cap top 60, cap height 51, pitch 69
     rule         x 84, y 342, width 144
     supporting   x 84, top 365, three authored lines
     the card     x 414-878, y 60-422
     the model    x 790, top 152, overlapping the card's lower right

   The heading's cap height and line pitch are the same 51 / 69-71 every
   section since 04 has used, so the type tokens are that same verified scale:
   100 / 20 / 13px at the 1512 reference.

   THE CARD IS A GRID, NOT A CANVAS. Every section before this one positions
   its contents absolutely inside a fixed-aspect box, because every section
   before this one is a picture. This one is a FORM: a field that grows to hold
   an error message, a label that wraps at 1280, a button whose text is longer
   in another language. So the card takes the comp's three column proportions
   as grid tracks and lets its rows size themselves. The composition is the
   comp's; the behaviour is a form's.
   ========================================================================== */

/* ---------------------------------------------------------------- TOKENS */
.contact {
    /* 100 / 20 / 13px at the 1512 reference, in proportion to 1920. */
    --fs-contact: clamp(2.4rem, 6.6138vw, 127px);
    --fs-contact-index: clamp(1.3rem, 3.2937vw, 63px);
    --fs-contact-copy: clamp(0.85rem, 1.3228vw, 25px);
    --fs-contact-label: clamp(0.62rem, 0.8598vw, 16px);
    --fs-step: clamp(0.72rem, 1.3228vw, 25px);

    --flame-11: #FF4938;
    --paper-11: #FFFFFF;
    --sand-11: #F5EADF;
    --deep-11: #073F4C;
    --ink-11: #012326;
    --lime-11: #AAC102;
    /* The same flat warm grey every section's index samples to, carried onto
       the flame plate at the weight the comp shows. */
    --index-11: rgba(255, 255, 255, 0.42);

    --con-ease: cubic-bezier(0.22, 1, 0.36, 1);
    --con-dur: 0.4s;
}

/* ----------------------------------------------------------------- SHELL */
.contact {
    position: relative;
    width: 100%;
    aspect-ratio: 1026 / 460;
    min-height: 560px;
    overflow: hidden;
    background: var(--flame-11);
    isolation: isolate;
}

/* --------------------------------------------------------- TRANSFORM RIGS */
/* Pointer carriers, same contract as Sections 02-09: full-bleed so the
   percentages inside resolve against the section box, and pointer-transparent
   so a carrier can never swallow a field's focus. */
.contact__copy-pointer,
.contact__index-pointer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 3;
    will-change: transform;
}

/* ------------------------------------------------------------ LEFT COLUMN */
.contact__index {
    position: absolute;
    top: 8.5%;
    left: 2.144%;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-contact-index);
    line-height: 1;
    color: var(--index-11);
}

.contact__title {
    position: absolute;
    top: 11.4%;
    left: 8.187%;
    width: 32%;
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-contact);
    line-height: 1;
    letter-spacing: 0.01em;
    text-transform: uppercase;
    color: var(--paper-11);
}

.contact__line {
    display: block;
    overflow: hidden;
    padding-bottom: 0.04em;
}

.contact__line-in {
    display: block;
}

/* Same reason as Section 10's: a split heading breaks between characters
   unless the words are held together first. */
.contact__word {
    display: inline-block;
    white-space: nowrap;
}

.contact__rule {
    position: absolute;
    top: 74.348%;
    left: 8.187%;
    width: 14.035%;
    height: 1px;
    background: rgba(255, 255, 255, 0.55);
    transform: scaleX(0);
    transform-origin: 0 50%;
    will-change: transform;
}

.contact__lede {
    position: absolute;
    top: 79.348%;
    left: 8.187%;
    width: 26%;
    margin: 0;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: var(--fs-contact-copy);
    /* The comp breaks this over three lines at a 16px pitch on a 20px face. */
    line-height: 1.28;
    color: var(--sand-11);
}

.contact__lede-line {
    display: block;
}

/* ================================================================ THE CARD */
.contact__card {
    position: absolute;
    top: 13.043%;
    left: 40.351%;
    width: 45.224%;
    z-index: 2;
    box-sizing: border-box;
    padding: 2.2% 0 0;
    background: var(--sand-11);
    color: var(--deep-11);
}

/* The three steps, and the one box the confirmation is laid over. */
.contact__steps {
    position: relative;
    display: grid;
    /* The comp's two dividers at x 560 and 690 of a 464-wide card. */
    grid-template-columns: 31.5% 28% 40.5%;
}

/* -------------------------------------------------------------- THE STEPS */
/* The three numbers are a progression, not three labels: a step marks itself
   once its own requirement is met, so 01 → 02 → 03 reads as movement through
   the form. One property changes, which is the whole of "subtle". */
.step.is-complete .step__num,
.step.is-sent .step__num {
    color: var(--lime-11);
    opacity: 1;
}

.step.is-complete .step__head,
.step.is-sent .step__head {
    color: var(--deep-11);
}

/* The comp's card breathes: 48px between radios and 58px between fields on a
   460-tall band, which is a great deal more air than a form usually gets and
   is most of what makes it read as editorial rather than as a signup. Those
   two figures set the card's height; nothing here is a round number. */
.step {
    box-sizing: border-box;
    padding: 0 5.5% 9%;
}

/* The comp's dividers run between the three steps and stop short of the
   newsletter row — a column border does exactly that and needs no element. */
.step + .step {
    border-left: 1px solid rgba(7, 63, 76, 0.22);
}

.step__head {
    display: flex;
    align-items: baseline;
    gap: 0.6em;
    margin: 0 0 1.6em;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    font-size: var(--fs-step);
    line-height: 1;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--deep-11);
}

.step__num {
    font-family: "Inter", system-ui, sans-serif;
    font-size: 0.62em;
    letter-spacing: 0.08em;
    opacity: 0.55;
}

/* ---------------------------------------------------------- 01 YOUR GOAL */
.goal {
    display: flex;
    flex-direction: column;
    /* 48 of the comp's 460 against a 13px face. */
    gap: 3.55em;
    margin: 0;
    padding: 0;
    border: 0;
}

.goal__option {
    display: flex;
    align-items: center;
    gap: 0.85em;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: var(--fs-contact-label);
    line-height: 1;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--deep-11);
    cursor: pointer;
}

/* The native input stays in the DOM and keeps every keyboard behaviour a
   radio group has — arrow keys, the roving tab stop, form submission. It is
   only made invisible, and the ring below is drawn from its state. */
.goal__input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.goal__ring {
    position: relative;
    flex: 0 0 auto;
    width: 1.05em;
    height: 1.05em;
    border: 1px solid rgba(7, 63, 76, 0.55);
    border-radius: 50%;
    transition: border-color var(--con-dur) var(--con-ease);
}

.goal__ring::after {
    content: "";
    position: absolute;
    inset: 21%;
    border-radius: 50%;
    background: var(--flame-11);
    transform: scale(0);
    transition: transform var(--con-dur) var(--con-ease);
}

.goal__input:checked + .goal__ring {
    border-color: var(--flame-11);
}

.goal__input:checked + .goal__ring::after {
    transform: scale(1);
}

.goal__input:focus-visible + .goal__ring {
    outline: 2px solid var(--flame-11);
    outline-offset: 3px;
}

.goal__option:hover .goal__ring {
    border-color: var(--flame-11);
}

/* Preselected from Section 10, or chosen here — either way it has to read as
   the active one at a glance, not only by the dot inside the ring. */
.goal__option.is-chosen {
    color: var(--flame-11);
}

/* ------------------------------------------------------- 02 YOUR DETAILS */
.field {
    position: relative;
    display: block;
    /* 58 of the comp's 460, less the field's own 44. */
    margin: 0 0 3.15em;
}

/* The comp shows the field's name inside the box. Keeping a real <label>
   above it and hiding it visually means the box can carry the placeholder and
   the field still announces itself — and the error below has somewhere to go
   that is tied to the input by aria-describedby. */
.field__label {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.field__input {
    display: block;
    box-sizing: border-box;
    width: 100%;
    padding: 1.2em 1em;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: var(--fs-contact-label);
    line-height: 1;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    text-align: center;
    color: var(--deep-11);
    background: transparent;
    border: 1px solid rgba(7, 63, 76, 0.32);
    border-radius: 0;
    transition:
        border-color var(--con-dur) var(--con-ease),
        background-color var(--con-dur) var(--con-ease);
}

.field__input::placeholder {
    color: rgba(7, 63, 76, 0.62);
    opacity: 1;
}

.field__input:hover {
    border-color: rgba(7, 63, 76, 0.55);
}

.field__input:focus {
    outline: none;
    border-color: var(--deep-11);
    background: rgba(255, 255, 255, 0.55);
}

.field__input:focus-visible {
    outline: 2px solid var(--flame-11);
    outline-offset: 2px;
}

.field.is-invalid .field__input {
    border-color: var(--flame-11);
}

/* The message lives IN the gap the comp already leaves between fields, not in
   space it has to make. Growing a field to hold an error moves every field
   below it and the card's own bottom edge — which is the layout jump the brief
   rules out — so this is taken out of flow entirely and only ever changes
   opacity and a 6px lift. */
.field__error {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    display: block;
    max-height: 2.6em;
    margin: 0.4em 0 0;
    overflow: hidden;
    font-family: "Inter", system-ui, sans-serif;
    font-size: 0.72em;
    line-height: 1.3;
    letter-spacing: 0.02em;
    color: var(--flame-11);
    opacity: 0;
    transform: translate3d(0, -4px, 0);
    pointer-events: none;
    transition:
        opacity var(--con-dur) var(--con-ease),
        transform var(--con-dur) var(--con-ease);
}

.field.is-invalid .field__error {
    opacity: 1;
    transform: none;
}

/* --------------------------------------------------- 03 ENTER THE UNREAL */
.contact__submit {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    /* The comp's button is 140 of its 188-wide column. */
    width: 76%;
    padding: 1.2em 1em;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: var(--fs-contact-label);
    line-height: 1;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    /* The lime carries the same legibility problem Section 09's CTA had:
       white on #AAC102 is about 1.9:1. The deep teal the rest of the card is
       set in reads at 5.3:1 and is already the section's own colour. */
    color: var(--deep-11);
    background: var(--lime-11);
    border: 0;
    cursor: pointer;
    transition:
        transform var(--con-dur) var(--con-ease),
        background-color var(--con-dur) var(--con-ease),
        box-shadow var(--con-dur) var(--con-ease),
        opacity var(--con-dur) var(--con-ease);
}

.contact__submit:hover,
.contact__submit:focus-visible {
    transform: translate3d(0, -2px, 0);
    background: #BCD402;
    box-shadow: 0 14px 28px rgba(1, 35, 38, 0.24);
}

.contact__submit:focus-visible {
    outline: 2px solid var(--deep-11);
    outline-offset: 3px;
}

.contact__submit[aria-busy="true"] {
    cursor: progress;
}

.contact__submit:disabled {
    /* Not dimmed to the point of looking broken: it is working, not off. */
    opacity: 0.82;
    transform: none;
    box-shadow: none;
}

/* The sending state. A band of light crossing the button once a second says
   "working" without a spinner, and it is one transform on a pseudo-element. */
.contact__submit.is-sending {
    position: relative;
    overflow: hidden;
}

.contact__submit.is-sending::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: -40%;
    width: 40%;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.55), transparent);
    animation: contact-sending 1.05s linear infinite;
}

@keyframes contact-sending {
    to { transform: translate3d(350%, 0, 0); }
}

.news__button:disabled {
    opacity: 0.72;
    cursor: progress;
}

/* The reply, in the same box the button sits in so nothing below it moves. */
.step__reply {
    margin: 0px 0 0;
    font-family: "Inter", system-ui, sans-serif;
    font-size: 0.8em;
    line-height: 1.4;
    letter-spacing: 0.02em;
    color: var(--deep-11);
    opacity: 0;
    transform: translate3d(0, 6px, 0);
    transition:
        opacity var(--con-dur) var(--con-ease),
        transform var(--con-dur) var(--con-ease);
}

.step__reply.is-shown {
    opacity: 1;
    transform: none;
}

.step__reply--error {
    color: var(--flame-11);
}

/* ------------------------------------------------------------ NEWSLETTER */
.news {
    box-sizing: border-box;
    /* Clear of the model, which overlaps the card's lower right. */
    padding: 1.5% 30% 5% 5.5%;
    border-top: 1px solid rgba(7, 63, 76, 0.22);
}

.news__label {
    display: block;
    margin: 1.4em 0 1em;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: var(--fs-contact-label);
    line-height: 1;
    letter-spacing: 0.01em;
    color: rgba(7, 63, 76, 0.78);
}

.news__row {
    display: flex;
    align-items: stretch;
    gap: 2.2%;
}

.news__input {
    flex: 1 1 auto;
    min-width: 0;
}

.news__button {
    flex: 0 0 auto;
    padding: 1.2em 1.4em;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: var(--fs-contact-label);
    line-height: 1;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--flame-11);
    background: transparent;
    border: 1px solid var(--flame-11);
    cursor: pointer;
    transition:
        color var(--con-dur) var(--con-ease),
        background-color var(--con-dur) var(--con-ease);
}

.news__button:hover,
.news__button:focus-visible {
    color: var(--sand-11);
    background: var(--flame-11);
}

.news__button:focus-visible {
    outline: 2px solid var(--deep-11);
    outline-offset: 3px;
}

.news__reply {
    margin: 0.7em 0 0;
    font-family: "Inter", system-ui, sans-serif;
    font-size: 0.78em;
    line-height: 1.3;
    letter-spacing: 0.02em;
    color: var(--deep-11);
    opacity: 0;
    transition: opacity var(--con-dur) var(--con-ease);
}

.news__reply.is-shown {
    opacity: 1;
}

.news__reply--error {
    color: var(--flame-11);
}

/* ---------------------------------------------------------- CONFIRMATION */
/* Laid over the card, at the card's own size, so nothing reflows at the moment
   it is read. Each line is masked by its own box and lifted out of it — the
   same reveal the headings in Sections 04-11 use, which is what keeps the
   confirmation inside the site's language instead of reading as a dialog. */
/* Laid over the THREE STEPS, not over the whole card. Covering the card would
   put the confirmation on top of the newsletter — and a project brief being
   received is not a reason the newsletter should stop working. The brief asks
   for the two to be independent; this is where that is either true or not.
   Out of flow, so it cannot change the card's height either. */
.contact__done {
    position: absolute;
    inset: 0;
    z-index: 5;
    display: flex;
    flex-direction: column;
    justify-content: center;
    box-sizing: border-box;
    /* Clear of the model, which overlaps the card's lower right. */
    padding: 8% 32% 8% 5.5%;
    margin: 0;
    background: var(--sand-11);
    visibility: hidden;
    opacity: 0;
    will-change: transform, opacity;
}

.contact__done.is-shown {
    visibility: visible;
    opacity: 1;
}

.contact__done-title {
    margin: 0;
    font-family: "Bebas Neue", "Inter", sans-serif;
    font-weight: 400;
    /* Half the section heading, so it belongs to the card rather than
       competing with the wall of type beside it. */
    font-size: clamp(1.5rem, 3.3vw, 3.6rem);
    line-height: 1.02;
    letter-spacing: 0.01em;
    text-transform: uppercase;
    color: var(--deep-11);
}

.contact__done-line {
    display: block;
    overflow: hidden;
    padding-bottom: 0.04em;
}

.contact__done-in {
    display: block;
}

.contact__done-note {
    margin: 1.1em 0 0;
    font-family: "Inter", system-ui, sans-serif;
    font-size: var(--fs-contact-label);
    line-height: 1.4;
    letter-spacing: 0.02em;
    color: rgba(7, 63, 76, 0.78);
}

.contact__done-back {
    align-self: flex-start;
    margin-top: 1.6em;
    padding: 0.9em 1.6em;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: var(--fs-contact-label);
    line-height: 1;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--flame-11);
    background: transparent;
    border: 1px solid var(--flame-11);
    cursor: pointer;
    transition:
        color var(--con-dur) var(--con-ease),
        background-color var(--con-dur) var(--con-ease);
}

.contact__done-back:hover,
.contact__done-back:focus-visible {
    color: var(--sand-11);
    background: var(--flame-11);
}

.contact__done-back:focus-visible {
    outline: 2px solid var(--deep-11);
    outline-offset: 3px;
}

/* ---------------------------------------------------------------- MODEL */
/* Overlaps the card's lower right exactly as the comp has it, and is set
   aside from the pointer so it can never sit between the cursor and a field. */
/* The box is the comp's: 23.392% of the band wide at x 790, top 33.043%, and
   proportioned to form-img.png's OPAQUE box (332 x 418 inside a 521 x 437
   file) rather than to the file. Sized by the file instead, 36% of the width
   would be transparent margin and she would stop two thirds of the way down
   the band instead of running off the bottom of it the way the comp has her.
   Same solve as every section since 04. */
.contact__model {
    position: absolute;
    top: 33.043%;
    left: 76.998%;
    width: 23.392%;
    /* `bottom: 0` INSTEAD OF `aspect-ratio: 332 / 418`, and that swap is the
       fix. The aspect made this box a function of the section's WIDTH while
       the band's floor is a function of its HEIGHT, and the two only agreed at
       the reference ratio: at 1920 she stopped 12px short of the bottom edge,
       and anywhere between 1025 and 1249 — where the section's min-height
       overrides its aspect-ratio — she stopped roughly 50px short. Anchoring
       the box to the floor makes her land on it at every desktop width by
       construction rather than by arithmetic that happens to come out even.
       The image below keeps its own ratio with `width: auto`, so nothing is
       stretched to close the gap. Below 1024 the responsive block puts this
       back to `position: static` with the comp's ratio, where she is a stacked
       block and `bottom` means nothing. */
    bottom: 0;
    /* <figure> carries a 1em/40px margin from the UA sheet, which walked this
       40px right and 16px down of where the comp puts it. */
    margin: 0;
    z-index: 4;
    pointer-events: none;
    will-change: transform;
}

.contact__model img {
    position: absolute;
    display: block;
    /* Height drives, width follows, so the file's 521:437 is preserved exactly
       — the brief's "touch the bottom naturally without stretching".
       104.458% / -4.458% are the old 104.545% / -4.545% restated against the
       taller box `bottom: 0` produces: same top edge on screen, bottom edge
       now exactly on the section's floor. */
    width: auto;
    height: 104.458%;
    left: -33.735%;
    top: -4.458%;
    /* Overrides the base reset in style.css, which caps every image at its
       container — the same cap that squashed three of four frames in
       Section 06. */
    max-width: none;
}

/* ============================================================== RESPONSIVE */
/* Below 1024 the section stops being a fixed-aspect canvas and becomes the
   brief's stack: heading, description, form, model. Every field grows to a
   touch-comfortable height and the card's three columns become three rows. */
@media (max-width: 1199px) {

    .contact {
        aspect-ratio: auto;
        min-height: 0;
        padding: clamp(3.5rem, 9vw, 6rem) 0 0;
    }

    .contact__copy-pointer,
    .contact__index-pointer {
        position: static;
        inset: auto;
        display: contents;
    }

    .contact__index,
    .contact__title,
    .contact__rule,
    .contact__lede,
    .contact__card,
    .contact__model {
        position: static;
        width: auto;
        margin-left: clamp(1.25rem, 4vw, 3rem);
        margin-right: clamp(1.25rem, 4vw, 3rem);
    }

    .contact__index {
        display: block;
        margin-bottom: 0.2em;
    }

    .contact__rule {
        width: clamp(5rem, 22vw, 9rem);
        margin-top: clamp(1.5rem, 4vw, 2.25rem);
        transform: none;
    }

    .contact__lede {
        max-width: 34rem;
        margin-top: clamp(1.1rem, 3vw, 1.6rem);
    }

    .contact__card {
        margin-top: clamp(2.25rem, 6vw, 3.5rem);
        padding: clamp(1.5rem, 4vw, 2.25rem) 0 0;
    }

    .contact__steps {
        grid-template-columns: 1fr;
    }

    .step {
        padding: 0 clamp(1.25rem, 4vw, 2rem) clamp(1.5rem, 4vw, 2rem);
    }

    .step + .step {
        border-left: 0;
        border-top: 1px solid rgba(7, 63, 76, 0.22);
        padding-top: clamp(1.5rem, 4vw, 2rem);
    }

    /* Stacked, the gap between fields is smaller, so the message goes back
       into flow where there is room for it to push rather than overlap. */
    .field__error {
        position: static;
        max-height: 0;
        transition:
            max-height var(--con-dur) var(--con-ease),
            opacity var(--con-dur) var(--con-ease);
    }

    .field.is-invalid .field__error {
        max-height: 4em;
    }

    .field {
        margin-bottom: 1.25em;
    }

    .field__input,
    .contact__submit,
    .news__button {
        /* Touch targets, not shrunk desktop ones. */
        min-height: 3rem;
        font-size: max(1rem, var(--fs-contact-label));
    }

    /* A radio row is a tap target, not a line of text. */
    .goal__option {
        font-size: max(0.9rem, var(--fs-contact-label));
        min-height: 2.75rem;
        padding: 0.35em 0;
    }

    .goal {
        gap: 0.4em;
    }

    .news {
        padding: 0 clamp(1.25rem, 4vw, 2rem) clamp(1.75rem, 5vw, 2.5rem);
    }

    .contact__done {
        padding: clamp(2.5rem, 8vw, 4rem) clamp(1.25rem, 4vw, 2rem);
    }

    /* The brief's stack is heading, description, form, model — in that order
       and not overlapping. The desktop overlap is a composition; stacked it
       would only cover the newsletter row. */
    .contact__model {
        display: block;
        width: min(20rem, 62%);
        aspect-ratio: 332 / 418;
        margin: clamp(2rem, 6vw, 3rem) auto 0;
    }

    /* NOT :where(). `:where()` has zero specificity, so this block would
       lose to the `.contact{...}` token block at the top of the file no matter
       which came later, and the whole mobile type scale would be declared
       and never applied. Equal specificity plus a later position is what
       actually overrides. */
    .contact {
        --fs-contact: clamp(2.4rem, 8.4vw, 5.5rem);
        --fs-contact-index: clamp(1.3rem, 4.2vw, 2.8rem);
        --fs-contact-copy: clamp(0.95rem, 2vw, 1.25rem);
        --fs-contact-label: clamp(0.72rem, 1.5vw, 0.9rem);
        --fs-step: clamp(1rem, 2.2vw, 1.35rem);
    }
}

@media (max-width: 767px) {

    .contact {
        --fs-contact: clamp(2.1rem, 11.4vw, 4.5rem);
        --fs-contact-index: clamp(1.2rem, 6vw, 2.4rem);
    }

    .news__row {
        flex-direction: column;
        gap: 0.75rem;
    }
}

@media (max-width: 560px) {

    .contact {
        --fs-contact: clamp(1.9rem, 12.6vw, 3.4rem);
    }
}

/* ------------------------------------------------------------ ARMED STATES */
/* Applied only once GSAP is confirmed (`is-armed` goes on the <html> in
   main.js). Without JS the section renders complete and the form is fully
   usable — every field, both buttons and the whole radio group. */
.is-armed .contact__title .char {
    display: inline-block;
    transform: translate3d(0, 40px, 0);
    opacity: 0;
    will-change: transform, opacity;
}

.is-armed .contact__index,
.is-armed .contact__lede,
.is-armed .contact__card,
.is-armed .contact__model {
    opacity: 0;
}

/* --------------------------------------------------------- REDUCED MOTION */
/* Every armed state above is mirrored, so nothing can be left invisible, and
   the entrance, the pointer depth and the model's arrival all go. The form's
   own state transitions stay: an error that appears instantly is still an
   error, and hiding it would remove function rather than motion. */
@media (prefers-reduced-motion: reduce) {

    .is-armed .contact__title .char {
        transform: none;
        opacity: 1;
    }

    .is-armed .contact__index,
    .is-armed .contact__lede,
    .is-armed .contact__card,
    .is-armed .contact__model {
        opacity: 1;
    }

    .contact__rule {
        transform: scaleX(1);
    }

    .contact__submit:hover,
    .contact__submit:focus-visible {
        transform: none;
    }

    /* The confirmation still arrives — it has to be noticed — but as light
       alone, with no travelling band and no lifted lines. */
    .contact__submit.is-sending::after {
        animation: none;
        opacity: 0.4;
    }

    .contact__done-in {
        transform: none;
    }
}

/* --------------------------------------------------------- MOBILE MOTION */
/* Lighter and faster on a phone, per the brief: the same states, shorter
   transitions, and no continuous loop running in the background of a form
   somebody is trying to fill in on a bus. */
@media (max-width: 767px) {

    .contact {
        --con-dur: 0.28s;
    }

    .contact__submit.is-sending::after {
        animation-duration: 1.4s;
    }
}


/* ############################################################################
   SECTION-12.CSS
   Section 12.
   ######################################################################## */

/* ============================================================================
   SECTION-12.CSS — FOOTER
   Additive only. No selector here touches Sections 01-11. `.foot*` was
   confirmed unused before a line of this was written — Section 05 shipped a
   `.reel` collision with Section 01's showreel modal, and that check is now
   the first step of every section.

   Reference canvas: the comp's footer band is 1026 x 67 on the same 1026-wide
   canvas every section before it was measured against. Measured off it:

     band height     67   (6.53% of the canvas width)
     first item      x 84
     dividers        x 219, 394, 521 — three, between the four nav items
     legal group     x 855 and 948, flush right
     type            13px Inter at the 1512 reference

   This is the one section in the project that is deliberately NOT a piece of
   motion. It is a rule, six words and a lot of air: the place the eye stops.
   The only movement is a one-off fade as it arrives and a line that draws under
   a link on hover.
   ========================================================================== */

/* ---------------------------------------------------------------- TOKENS */
.foot {
    /* The same 13px-at-1512 label token Sections 04-09 use, so the footer sets
       at exactly the size the rest of the project's small type does. */
    --fs-foot: clamp(0.62rem, 0.8598vw, 16px);

    --ink-12: #062D35;
    --paper-12: #FFF9F3;
    /* The divider and the resting link, as fractions of the paper — one
       colour, two weights, so nothing here needs a third value. */
    --rule-12: rgba(255, 249, 243, 0.22);

    --foot-ease: cubic-bezier(0.22, 1, 0.36, 1);
    --foot-dur: 0.4s;
}

/* ----------------------------------------------------------------- SHELL */
.foot {
    position: relative;
    /* 67 on 1026 at the comp, held as a floor rather than a fixed height so a
       wrapped label at a narrow width can never clip. */
    min-height: clamp(4.4rem, 6.53vw, 6.7rem);
    display: flex;
    align-items: stretch;
    background: var(--ink-12);
    color: var(--paper-12);
}

.foot__inner {
    display: flex;
    align-items: stretch;
    justify-content: space-between;
    width: 100%;
    /* The comp's first item begins at x 84 of 1026 and its last word ends at
       988, so the band is inset 8.187% on the left and 3.704% on the right. */
    padding: 0 3.704% 0 8.187%;
}

/* ------------------------------------------------------------------- NAV */
.foot__nav,
.foot__legal {
    display: flex;
    align-items: stretch;
    margin: 0;
}

/* The comp's three dividers sit BETWEEN the four navigation items and run
   nearly the full height of the band — which is what `align-items: stretch`
   plus a left border gives, with no extra element to keep in step. */
.foot__nav .foot__link + .foot__link {
    border-left: 1px solid var(--rule-12);
}

.foot__link {
    position: relative;
    display: flex;
    align-items: center;
    padding: 1.6em 3.75em;
    font-family: "Inter", system-ui, sans-serif;
    font-weight: 400;
    font-size: var(--fs-foot);
    line-height: 1;
    letter-spacing: 0.055em;
    text-transform: uppercase;
    text-decoration: none;
    white-space: nowrap;
    color: var(--paper-12);
    opacity: 0.86;
    transition: opacity var(--foot-dur) var(--foot-ease);
}

.foot__nav .foot__link:first-child {
    padding-left: 0;
}

.foot__legal .foot__link:last-child {
    padding-right: 0;
}

/* The line, drawn rather than switched on: a 1px rule scaled from its left
   edge, which is a transform and so costs nothing per frame. */
.foot__link::after {
    content: "";
    position: absolute;
    left: 3.75em;
    right: 3.75em;
    bottom: 1.35em;
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: 0 50%;
    transition: transform var(--foot-dur) var(--foot-ease);
}

.foot__nav .foot__link:first-child::after {
    left: 0;
}

.foot__legal .foot__link:last-child::after {
    right: 0;
}

a.foot__link:hover,
a.foot__link:focus-visible {
    opacity: 1;
}

a.foot__link:hover::after,
a.foot__link:focus-visible::after {
    transform: scaleX(1);
}

.foot__link:focus-visible {
    outline: 2px solid var(--paper-12);
    outline-offset: -6px;
}

/* ----------------------------------------------------------------- LEGAL */
/* Privacy and Terms are real pages now, so these render as anchors and read
   as interactive rather than as set text. */
.foot__link--legal {
    opacity: 0.9;
    cursor: pointer;
}

.foot__legal .foot__link {
    padding-left: 2.2em;
    padding-right: 2.2em;
}

.footer-brand img{
    max-width: 100px;
}

/* ------------------------------------------------------------ RESPONSIVE */
/* Not a shrunk desktop bar. Below 900 the two groups stack, the dividers stop
   being vertical rules between items and become horizontal ones between rows,
   and every target grows to a comfortable height. */
@media (max-width: 1199px) {

    .foot {
        /* The desktop token is a fraction of the viewport width, which below
           900 falls to its 0.62rem floor and sets the footer smaller than the
           rest of the page. Stacked, there is room for the real thing. */
        --fs-foot: 0.8rem;
        min-height: 0;
    }

    .foot__inner {
        flex-direction: column;
        align-items: stretch;
        padding: clamp(0.75rem, 2.5vw, 1.5rem) clamp(1.25rem, 4vw, 3rem) clamp(2rem, 6vw, 3rem);
    }

    .foot__nav {
        flex-direction: column;
        align-items: stretch;
    }

    .foot__nav .foot__link + .foot__link {
        border-left: 0;
        border-top: 1px solid var(--rule-12);
    }

    .foot__link {
        padding: 1.25em 0;
        white-space: normal;
    }

    /* The first item keeps its padding here — it is a tap target, not the
       start of a bar. The block's own top padding is trimmed instead, so the
       spacing reads the same and the target survives. */
    .foot__nav .foot__link:first-child {
        padding-left: 0;
    }

    .foot__link::after {
        left: 0;
        right: 0;
        bottom: 0.9em;
    }

    .foot__legal {
        margin-top: clamp(1.5rem, 5vw, 2.25rem);
        padding-top: clamp(1.25rem, 4vw, 1.75rem);
        border-top: 1px solid var(--rule-12);
        gap: clamp(1.5rem, 6vw, 3rem);
    }

    /* Privacy and Terms are text today and links the moment a page exists for
       them, so they are sized as targets either way. */
    .foot__legal .foot__link {
        min-height: 2.75rem;
        padding: 0;
    }

    .foot__legal .foot__link::after {
        bottom: -0.35em;
    }
}

@media (max-width: 560px) {

    .foot {
        --fs-foot: 0.75rem;
    }
}

/* ------------------------------------------------------------ ARMED STATES */
/* Applied only once GSAP is confirmed (`is-armed` goes on the <html> in
   main.js). Without JS the footer renders complete and every link works. */
.is-armed .foot__nav,
.is-armed .foot__legal {
    opacity: 0;
}

/* --------------------------------------------------------- REDUCED MOTION */
/* The armed states are mirrored, and the drawn line becomes a plain change of
   weight — the link still answers the cursor, it just does not animate. */
@media (prefers-reduced-motion: reduce) {

    .is-armed .foot__nav,
    .is-armed .foot__legal {
        opacity: 1;
    }

    .foot__link::after {
        transition: none;
    }

    a.foot__link:hover::after,
    a.foot__link:focus-visible::after {
        transform: scaleX(1);
    }
}


/* ############################################################################
   UI.CSS
   Sticky header + shared CTA. AFTER the sections on purpose: it has to win over the per-section hover rules it replaces.
   ######################################################################## */

/* ============================================================================
   UI.CSS — the two things that are site-wide rather than section-wide:
   the sticky header, and the shared button interaction.

   Loaded LAST. That is not a preference: Sections 09 and 08 each declare their
   own `:hover { transform: ... }` on their CTA, and this file replaces those
   with one system, so it has to win. Nothing here changes a colour, a box, a
   font or a piece of copy — only motion.
   ========================================================================== */

/* ========================================================= STICKY HEADER */
/* position: fixed, so appearing costs zero layout. Hidden state lives here,
   not in JS, so a blocked script leaves an invisible bar rather than a bar
   stuck across the hero. nav.js animates opacity + y and flips visibility
   through GSAP's autoAlpha. */
.topbar {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 50;
    width: 100%;
    opacity: 0;
    visibility: hidden;
    transform: translate3d(0, -20px, 0);
    will-change: transform, opacity;
    /* The hero's own bar is the design; this is that bar on a plate so it
       stays legible over all eleven backgrounds underneath it. */
    background: rgba(6, 45, 53, 0.82);
    -webkit-backdrop-filter: blur(18px) saturate(1.15);
    backdrop-filter: blur(18px) saturate(1.15);
    border-bottom: 1px solid rgba(245, 234, 223, 0.12);
}

.topbar__inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: clamp(1rem, 3vw, 3rem);
    padding: clamp(0.85rem, 1.15vw, 1.4rem) var(--edge);
    padding-top: 10px;
    padding-bottom: 10px;
}

.topbar__brand {
    font-size: 20px;
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--cream);
    white-space: nowrap;
}

.topbar__nav ul {
    display: flex;
    align-items: center;
    gap: clamp(1rem, 2.4vw, 2.75rem);
}

/* Deliberately the same rule set as .hero__nav-link — the sticky header is the
   hero's navigation, moved, not a second design. */
.topbar__link {
    position: relative;
    display: inline-block;
    font-size: var(--fs-ui);
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--cream);
    opacity: 0.82;
    transition: opacity var(--dur-ui) var(--ease-out);
}

.topbar__link::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: -4px;
    width: 100%;
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: 100% 50%;
    transition: transform 0.45s var(--ease-out);
}

.topbar__link:hover,
.topbar__link:focus-visible,
.topbar__link.is-active {
    opacity: 1;
}

.topbar__link:hover::after,
.topbar__link:focus-visible::after,
.topbar__link.is-active::after {
    transform: scaleX(1);
    transform-origin: 0 50%;
}

/* Below 1200 the five inline labels are replaced by the burger and the panel
   at the bottom of this file. The plate and the brand stay — they are what the
   burger takes its colour from, and what gives nav.js a header height to
   offset its jumps by. */
@media (max-width: 1199px) {

    .topbar__nav {
        display: none;
    }

    .topbar__inner {
        /* Room for the burger, which is fixed and therefore not in this flex
           row. Matching its own inset keeps the brand off it at every width. */
        padding-right: calc(var(--edge) + 3.75rem);
    }
}

/* ======================================================= TOUCH NAVIGATION */
/* Desktop never sees either of these. */
.burger,
.menu {
    display: none;
}

@media (max-width: 1199px) {

    /* ------------------------------------------------------------ TRIGGER */
    /* Fixed and outside .topbar on purpose: over the hero the plate is still
       faded out, and the navigation has to be reachable from the first frame.
       Above the panel in z-order so the cross is never covered by what it
       closes. */
    .burger {
        position: fixed;
        top: 0;
        right: 0;
        z-index: 120;
        display: grid;
        place-items: center;
        /* 56px of tappable area around a 24px icon — the icon is the design,
           this is the target. */
        width: 56px;
        height: 56px;
        margin: clamp(0.5rem, 1.6vw, 0.9rem) clamp(0.4rem, 2vw, 1.4rem) 0 0;
        padding: 0;
        background: none;
        border: 0;
        cursor: pointer;
        -webkit-tap-highlight-color: transparent;
    }

    .burger__box {
        position: relative;
        display: block;
        width: 26px;
        height: 16px;
    }

    .burger__line {
        position: absolute;
        left: 0;
        width: 100%;
        height: 2px;
        background: var(--ink);
        border-radius: 2px;
        /* Two properties, two curves: the lines travel to the centre first and
           only then rotate, which is what stops the cross from reading as a
           spin. */
        transition:
            transform 0.4s cubic-bezier(0.22, 1, 0.36, 1),
            opacity 0.2s linear 0.1s,
            background-color var(--dur-ui) var(--ease-out);
    }

    .burger__line:nth-child(1) { top: 0; }
    .burger__line:nth-child(2) { top: 7px; }
    .burger__line:nth-child(3) { top: 14px; }

    /* Over the dark sticky plate, and over the panel once it is open. */
    .is-past-hero .burger__line,
    .burger.is-open .burger__line {
        background: var(--cream);
    }

    .burger.is-open .burger__line:nth-child(1) {
        transform: translate3d(0, 7px, 0) rotate(45deg);
    }

    .burger.is-open .burger__line:nth-child(2) {
        opacity: 0;
        transition-delay: 0s;
    }

    .burger.is-open .burger__line:nth-child(3) {
        transform: translate3d(0, -7px, 0) rotate(-45deg);
    }

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

    /* -------------------------------------------------------------- PANEL */
    .menu {
        position: fixed;
        inset: 0;
        z-index: 110;
        display: block;
        /* The container never intercepts anything; the scrim and the panel
           inside it do. Without this the whole viewport would be a dead zone
           while the panel is closed. */
        pointer-events: none;
    }

    .menu:not([inert]) {
        pointer-events: auto;
    }

    .menu__scrim {
        position: absolute;
        inset: 0;
        background: rgba(1, 32, 36, 0.55);
        -webkit-backdrop-filter: blur(6px);
        backdrop-filter: blur(6px);
        opacity: 0;
    }

    .menu__panel {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        display: flex;
        flex-direction: column;
        justify-content: center;
        box-sizing: border-box;
        /* A large premium panel rather than a full cover on a tablet, and the
           full width on a phone, where anything less is a strip. */
        width: min(26rem, 82vw);
        padding: clamp(4.5rem, 12vh, 7rem) clamp(1.75rem, 6vw, 3.5rem) clamp(2rem, 6vh, 3rem);
        background: var(--ink);
        /* Its own scroll, contained, for a short landscape phone. */
        overflow-y: auto;
        overscroll-behavior: contain;
        transform: translate3d(100%, 0, 0);
    }

    .menu__list {
        display: grid;
        gap: clamp(1.1rem, 3.4vh, 1.9rem);
    }

    .menu__link {
        display: flex;
        align-items: baseline;
        gap: 1em;
        /* 44px minimum, met by padding rather than by growing the type. */
        min-height: 44px;
        padding: 0.25em 0;
        color: var(--cream);
        -webkit-tap-highlight-color: transparent;
    }

    .menu__num {
        font-family: "Inter", system-ui, sans-serif;
        font-size: clamp(0.62rem, 2.4vw, 0.75rem);
        font-weight: 500;
        letter-spacing: 0.16em;
        color: var(--accent);
        opacity: 0.9;
    }

    .menu__label {
        font-family: "Bebas Neue", "Inter", sans-serif;
        font-weight: 400;
        font-size: clamp(2rem, 9vw, 3.25rem);
        line-height: 0.95;
        letter-spacing: 0.02em;
        text-transform: uppercase;
        transition: color var(--dur-ui) var(--ease-out);
    }

    .menu__link:hover .menu__label,
    .menu__link:focus-visible .menu__label,
    .menu__link.is-active .menu__label {
        color: var(--btn-secondary);
    }

    .menu__foot {
        margin: clamp(2rem, 6vh, 3rem) 0 0;
        font-size: clamp(0.6rem, 2.2vw, 0.7rem);
        font-weight: 400;
        letter-spacing: 0.18em;
        text-transform: uppercase;
        color: var(--cream);
        opacity: 0;
    }
}

/* --------------------------------------------------------- THE SCROLL LOCK */
/* Lenis is configured with smooth WHEEL only, so `lenis.stop()` holds a
   trackpad and does nothing at all to a finger. This is what actually holds a
   phone still behind an open panel. Set on the root by menu.js. */
.is-menu-open,
.is-menu-open body {
    overflow: hidden;
    /* Belt and braces for iOS, where overflow alone still rubber-bands. */
    touch-action: none;
    overscroll-behavior: none;
}

/* The panel itself has to keep scrolling while the document does not. */
.is-menu-open .menu__panel {
    touch-action: pan-y;
}

/* ============================================================== BUTTONS */
/* One interaction for every CTA on the page. The motion is driven by
   buttons.js through GSAP, for one reason: Sections 08 and 09 animate their
   CTA's `scale` on entrance, which leaves an inline transform behind that a
   stylesheet `:hover { transform }` can never override — their hover lift has
   been dead since those entrances shipped. Moving all of it to the same
   inline-transform source is what makes the set behave identically.

   Everything here is the frame the motion runs inside: the clip for the shine,
   the shine itself, and the label wrapper buttons.js injects. */
/* NO `position` HERE, deliberately. The shine needs the button to be its
   containing block, but declaring `position: relative` in this file was wrong
   twice over: Sections 08 and 09 position their CTA absolutely on desktop and
   turn it static below 1024 WITHOUT clearing the desktop `top`/`left`, so a
   blanket `relative` made those inert offsets live again and walked the
   Section 08 button 47px right at 768. buttons.js promotes each button
   individually instead, only when it computes to `static`, blanking the four
   offsets in the same breath — and re-runs on resize, because which of them is
   static changes at 1024. */
.btn,
.archive__cta,
.package__cta,
.contact__submit,
.news__button {
    overflow: hidden;
    /* Both the magnet and the shine composite better on their own layer, and
       these are a handful of elements, not a page of them. */
    will-change: transform;
}

/* buttons.js wraps the existing label so it can move a hair independently of
   the box. Inline-block because a static span cannot be transformed. */
.fx-label {
    position: relative;
    z-index: 2;
    display: inline-block;
    will-change: transform;
}

/* The shine: one soft band, swept across on hover. Injected as an element
   rather than a pseudo so it cannot collide with the pseudo-elements Sections
   08, 09 and 11 already use on these same buttons. */
.fx-shine {
    position: absolute;
    top: -60%;
    left: -75%;
    z-index: 1;
    width: 45%;
    height: 220%;
    pointer-events: none;
    background: linear-gradient(
        100deg,
        rgba(255, 255, 255, 0) 0%,
        rgba(255, 255, 255, 0.28) 50%,
        rgba(255, 255, 255, 0) 100%
    );
    transform: translate3d(0, 0, 0) rotate(14deg);
    opacity: 0;
    will-change: transform, opacity;
}

/* On the two light buttons a white sweep is invisible; ink reads instead. */
.btn--secondary .fx-shine,
.archive__cta .fx-shine {
    background: linear-gradient(
        100deg,
        rgba(1, 32, 36, 0) 0%,
        rgba(1, 32, 36, 0.16) 50%,
        rgba(1, 32, 36, 0) 100%
    );
}

/* ------------------------------------------------ NEUTRALISED CSS HOVERS */
/* buttons.js owns transform on these now. Their stylesheet hovers stay for the
   colour and shadow — only the transform is handed over, so there is exactly
   one thing writing to the matrix. */
.btn:hover,
.btn:focus-visible,
.btn:active,
.archive__cta:hover,
.archive__cta:focus-visible,
.package__cta:hover,
.package__cta:focus-visible,
.contact__submit:hover,
.contact__submit:focus-visible,
.news__button:hover,
.news__button:focus-visible {
    transform: none;
}

/* --------------------------------------------------------- REDUCED MOTION */
/* No sweep, no lift, no magnet — buttons.js returns early, so this only has to
   put the colour-and-shadow feedback back where the rules above removed it. */
@media (prefers-reduced-motion: reduce) {

    .topbar {
        transition: none;
    }

    .fx-shine {
        display: none;
    }

    /* The burger still becomes a cross — that is state, not decoration, and
       removing it would leave the control ambiguous. It simply arrives
       without travelling. */
    .burger__line {
        transition-duration: 0.001ms;
    }

    .btn,
    .archive__cta,
    .package__cta,
    .contact__submit,
    .news__button {
        will-change: auto;
    }
}


/* ############################################################################
   RESPONSIVE-TOUCH.CSS
   Tablet and mobile only. Every rule inside ends at 1199px.
   ######################################################################## */

/* ============================================================================
   RESPONSIVE-TOUCH.CSS — the tablet and mobile compositions.

   Loaded last, and every rule in it lives inside a media query that ends at
   1199px. Desktop cannot see this file. That is the architecture: the approved
   1200+ design is not edited, extended or conditionally overridden — it is out
   of scope here by construction.

     TABLET   768 – 1199   an intermediate layout: two columns where the
                           content is genuinely paired, imagery larger than a
                           phone's, desktop's frame pulled in, touch controls
     MOBILE   ≤ 767        a centred editorial column: section number on top,
                           one thing at a time, nothing overlapping anything

   The per-section stylesheets already carry a stacked layout — Section 05
   already builds a snap-scroll gallery, Section 10 already builds tap rows.
   Their boundary has been moved onto the client's (1024 → 1199, 768 → 767) so
   tablet gets that layout instead of a cramped desktop one. This file does the
   intentional part on top: centring, rhythm, type scale, and the touch
   behaviour the desktop composition has no equivalent for.
   ========================================================================== */


/* ==========================================================================
   1 — SHARED  ≤ 1199
   ========================================================================== */
@media (max-width: 1199px) {

    /* The page never scrolls sideways. `clip`, not `hidden`: hidden would make
       the root a scroll container and break every pinned trigger under it.
       Each gallery declares its own contained overflow — this is the
       guarantee behind them, not a substitute for them. */
    html,
    body {
        overflow-x: clip;
    }

    /* The hero's right-hand nav is replaced by the burger in ui.css. */
    .hero__nav {
        display: none;
    }

    /* ================================================ 03 — PLAY ON THE SET */
    /* The control belongs on the picture at touch sizes, and the picture is
       its SIBLING, not its parent — the markup keeps the heading, the stage
       and the button as three children of the section so they can re-flow
       into a column. Absolute positioning has nothing to anchor to there,
       and the stage's height is a function of the viewport, so no percentage
       offset against the section would hold.
       A one-column grid solves it without touching the DOM: the stage and the
       button are given the SAME row, so the button is centred on whatever
       size the stage happens to be at. Section 03's own stack orders these
       with `order`, which grid honours identically, so nothing else moves. */
    .alive {
        display: grid;
        grid-template-columns: minmax(0, 1fr);
        grid-template-rows: auto auto auto;
        align-items: start;
    }

    /* Every one of these names its COLUMN as well as its row. Naming only the
       row leaves the column to auto-placement, and auto-placement will not put
       two items in one cell: with the play button explicitly at column 1 row 3,
       the stage was pushed into an implicit second column — which is sized to
       its content, and the stage's content is entirely absolute, so the column
       came out 0px wide and the picture vanished. */
    .alive__index {
        grid-row: 1;
        grid-column: 1;
    }

    .alive__title {
        grid-row: 2;
        grid-column: 1;
    }

    .alive__stage {
        grid-row: 3;
        grid-column: 1;
    }

    .alive__play {
        /* Same cell as the stage, centred in it. `relative` rather than
           `absolute` so it still occupies the cell and cannot be measured
           out of the layout. */
        position: relative;
        /* Section 03 places this absolutely on desktop with `left: 13.4%` and
           `top: 69%`. Those are inert while it is static and become live
           RELATIVE offsets the moment it is promoted — the button sat exactly
           103px right and 249px down of where the grid had put it, which is
           13.4% and 69% of its own cell. Blank them in the same breath as the
           promotion, the same rule buttons.js follows. */
        inset: auto;
        grid-row: 3;
        grid-column: 1;
        place-self: center;
        z-index: 8;
        width: clamp(60px, 15vw, 84px);
        height: clamp(60px, 15vw, 84px);
        margin: 0;
        /* Over the picture rather than over the cream, so it carries its own
           plate instead of borrowing the section's contrast. */
        color: var(--ink);
        background: rgba(245, 234, 223, 0.9);
        border-color: rgba(6, 45, 53, 0.25);
        -webkit-backdrop-filter: blur(6px);
        backdrop-filter: blur(6px);
        box-shadow: 0 14px 34px rgba(1, 32, 36, 0.28);
    }

    /* The brief's figure exactly: rest 1, press 1.05, and nothing in between —
       no idle pulse. Touch has no hover, so `:active` is the tap. */
    .alive__play:hover,
    .alive__play:focus-visible,
    .alive__play:active {
        color: #F5EADF;
        background: var(--ink);
        border-color: var(--ink);
        transform: scale(1.05);
    }

    .alive__play svg {
        width: 30%;
    }

    /* The ring is a second circle 8% outside the button; over the picture it
       reads as a scuff rather than as an accent. */
    .alive__play-ring {
        display: none;
    }

    /* 44px of tappable area on every control. The visual box is unchanged
       where it was already larger; this only ever raises a floor. */
    .btn,
    .archive__cta,
    .package__cta,
    .contact__submit,
    .news__button,
    .archive__arrow,
    .goal__option,
    .item__hit,
    .pick__row,
    .foot__link {
        min-height: 44px;
    }

    /* ---------------------------------------- 06 — THE TOUCH ACTIVE STATE */
    /* Section 06's emphasis is entirely `:hover`, and section-06.css correctly
       neutralises it under `hover: none` — which leaves a touch visitor with
       four identical cards and no way to see which one they have chosen.
       This is that state, set by services.js on the first tap: the same lift,
       the same rule, the same warm plate the cursor gets, arrived at by
       tapping. The description is already always visible below 1200, so only
       the selection needed a voice. */
    .service.is-active {
        z-index: 6;
    }

    .service.is-active .service__lift {
        transform: translate3d(0, -6px, 0);
        background-color: rgba(255, 253, 250, 0.55);
        box-shadow: 0 20px 46px rgba(6, 45, 53, 0.16);
    }

    .service.is-active .service__rule {
        transform: scaleX(2);
        opacity: 0.75;
    }

    .service.is-active .service__art-zoom {
        transform: scale(1.03);
        filter: brightness(1.05) contrast(1.02);
    }

    .service.is-active .service__title {
        color: var(--accent);
    }

    /* Shorter feedback on touch, per the brief: 0.4s desktop → 0.28s here. */
    .btn,
    .archive__cta,
    .package__cta,
    .contact__submit,
    .news__button {
        --dur-ui: 0.28s;
        --arc-dur: 0.28s;
        --con-dur: 0.28s;
    }

    /* Every continuous loop and cursor rig is already gated at 1200 inside the
       modules. The hero's fabric is the one that runs off a CSS keyframe
       instead, so it is damped here — slower and therefore cheaper, not
       removed, because it is the hero's only life. */
    .is-armed .hero__fabric {
        animation-duration: 20s;
    }

    /* ------------------------------------------------------- COMPOSITING */
    /* `will-change` is a promise to the compositor that costs a layer whether
       or not the promise is kept. Every one of these carriers is promising a
       transform that only ever happens above 1200 — the pointer rigs and the
       float loops are registered inside a `(min-width: 1200px)` matchMedia —
       so below it they are pure memory. Roughly sixty layers on a phone,
       none of them for anything.
       The hero's two are kept: its scroll parallax is the one rig that does
       still run here. */
    [data-pointer],
    .roster__stage .layer,
    .slide__depth,
    .slide__enter,
    .item__enter,
    .item__depth,
    .campaign__zoom,
    .service__art-enter,
    .pick__art-in,
    .stage__frames-scroll,
    .stage__frames-pointer,
    .service__art-scroll,
    .service__art-pointer,
    .manifesto__copy-scroll,
    .manifesto__portrait-scroll,
    .manifesto__portrait-float,
    .journey__copy-scroll,
    .package__copy-scroll,
    .reels__strip-scroll,
    .shot__float,
    .slide__float,
    .card__float,
    .asset__float,
    .item__lift,
    .character__life {
        will-change: auto;
    }

    /* The split characters — 248 of them on a phone, one per letter across
       eleven headings, each promising the compositor a layer for a one-off
       0.9s entrance most of them have already finished. By far the single
       largest cost on this page, and the tween runs identically without it.
       Written out rather than as a bare `.char` because each is granted by a
       three-class rule in its own section's stylesheet; matching that
       specificity is what makes this apply at all. */
    .is-armed .hero__title .char,
    .is-armed .roster__title .char,
    .is-armed .alive__title .char,
    .is-armed .manifesto__title .char,
    .is-armed .reels__title .char,
    .is-armed .services__title .char,
    .is-armed .journey__title .char,
    .is-armed .package__title .char,
    .is-armed .archive__title .char,
    .is-armed .choose__title .char,
    .is-armed .contact__title .char {
        will-change: auto;
    }
}


/* ==========================================================================
   2 — TABLET  768 – 1199
   An intermediate layout. Not the phone's, and not a squeezed desktop.
   ========================================================================== */
@media (min-width: 768px) and (max-width: 1199px) {

    /* Desktop's frame is up to 80px. A tablet wants less frame, more picture. */
    :root {
        --edge: clamp(2rem, 4.5vw, 3.25rem);
    }

    /* ------------------------------------------------- 01 — HERO, NARROW */
    /* 768 to 899 keeps the stacked hero: copy, then the artwork as a band
       beneath it. It is the same shape as the phone's, given a tablet's air.
       The two-column version below needs a column the display type can
       actually live in, and at 768 there isn't one — the first attempt at
       this file put the grid across the whole tablet range and the model
       landed on top of "FIND TALENT." at the narrow end. */
    /* The reserve below the copy and the height of the artwork are the same
       measurement written twice, and they have to stay that way: the plate is
       anchored to the hero's floor, so any height beyond the reserve is height
       that climbs back into the supporting text. Sized by height rather than
       width for exactly that reason — width let a square plate grow to 660px
       tall inside a 353px band and put ink-on-teal under the lede. */
    .hero {
        padding-bottom: clamp(19rem, 44vw, 30rem);
    }

    .hero__geometry {
        height: clamp(17rem, 40vw, 27rem);
        width: auto;
        max-width: none;
    }

    .hero__model-wrap {
        right: 3%;
        height: clamp(15rem, 35vw, 24rem);
        width: auto;
        max-width: none;
    }

    .hero__lede {
        max-width: 40ch;
    }
}

/* ---------------------------------------------- 01 — HERO, WIDE TABLET */
/* From 900 the copy column is wide enough to hold the headline at its own
   size with the artwork beside it, which is the composition the desktop hero
   is — at a tablet's proportions. The artwork layer is confined to the right
   of the split rather than spanning the section, so nothing it contains can
   reach the type no matter how the model is scaled inside it. */
@media (min-width: 900px) and (max-width: 999px) {

    .hero {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 0.92fr);
        grid-template-rows: auto 1fr auto;
        align-items: start;
        min-height: 100svh;
        padding-bottom: clamp(2.5rem, 5vw, 4rem);
    }

    .hero__bar {
        grid-column: 1 / -1;
        grid-row: 1;
    }

    .hero > .flow-layer {
        grid-column: 1;
        grid-row: 2;
        align-self: center;
        width: 100%;
    }

    .hero__panel {
        grid-column: 1;
        grid-row: 3;
        margin-top: 0;
    }

    .hero__copy {
        max-width: 100%;
        margin-top: clamp(1rem, 3vw, 2rem);
        padding-right: clamp(1rem, 3vw, 2rem);
    }

    .hero__lede {
        max-width: 32ch;
    }

    /* THE CONFINEMENT. Everything the artwork does happens inside this box. */
    .hero__art {
        left: 48%;
    }

    .hero__geometry {
        top: 0;
        bottom: auto;
        right: 0;
        width: auto;
        height: 100%;
        max-width: none;
        opacity: 1;
        /* The plate's own transparent margins, the same correction the desktop
           rule makes, restated for a box that is no longer the section. */
        transform: translate3d(8.12%, -4.617%, 0);
    }

    .hero__model-wrap {
        right: 0;
        bottom: 0;
        width: auto;
        height: 78%;
        max-width: none;
    }
}

@media (min-width: 768px) and (max-width: 1199px) {

    /* ------------------------------------------------------- CENTRED HEADS */
    /* On the sections whose tablet layout is one column, the head is centred.
       Sections 02, 06, 08 and 10 are deliberately absent: each is a genuine
       two-column pairing at this width, and a centred head over a left-aligned
       grid reads as a mistake rather than as a decision. */
    .alive__title,
    .alive__index,
    .manifesto__title,
    .manifesto__index,
    .manifesto__copy,
    .reels__title,
    .reels__index,
    .journey__title,
    .journey__index,
    .archive__title,
    .archive__index,
    .archive__label,
    .archive__copy,
    .contact__title,
    .contact__index,
    .contact__lede {
        text-align: center;
    }

    .manifesto__rule,
    .contact__rule {
        margin-inline: auto;
        transform-origin: 50% 50%;
    }

    .archive__progress,
    .archive__cta {
        margin-inline: auto;
    }

    .archive__nav,
    .journey__stages {
        justify-content: center;
    }

    /* -------------------------------------------------------- 02 — ROSTER */
    /* Five cards across is a phone-sized face on a tablet. Three is a roster. */
    .roster__cards {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    /* ------------------------------------------------------ 06 — SERVICES */
    /* Two up: the pairing the content already has, at a size where each
       model still reads. */
    .services__grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    /* ------------------------------------------------------- 08 — PACKAGE */
    .package__assets {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    /* -------------------------------------------------------- 11 — CONTACT */
    /* Two of the three steps pair naturally here; the submit takes the full
       row beneath them, which is where a form's action belongs anyway. */
    .contact__steps {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .contact__steps > .step:nth-child(3) {
        grid-column: 1 / -1;
        border-left: 0;
        border-top: 1px solid rgba(7, 63, 76, 0.22);
        padding-top: clamp(1.5rem, 4vw, 2rem);
    }

    /* --------------------------------------------------------- 12 — FOOTER */
    .foot__inner {
        text-align: center;
    }

    .foot__nav {
        justify-content: center;
    }
}


/* ==========================================================================
   3 — MOBILE  ≤ 767
   A centred editorial column.
   ========================================================================== */
@media (max-width: 767px) {

    :root {
        --edge: clamp(1.25rem, 5.5vw, 1.75rem);
    }

    /* ------------------------------------------------- THE CENTRED COLUMN */
    /* Centre is the default below 768, declared once at the section rather
       than element by element. The few places it is wrong are put back
       further down — a form's error text, a grid's own alignment. */
    .hero,
    .roster,
    .alive,
    .manifesto,
    .reels,
    .services,
    .journey,
    .package,
    .archive,
    .choose,
    .contact,
    .foot {
        text-align: center;
    }

    /* --------------------------------------------------------- TYPOGRAPHY */
    /* NO font-size here, deliberately. Every section already declares its own
       mobile type tokens — `--fs-unreal`, `--fs-manifesto`, `--fs-archive` and
       the rest — each tuned against its own longest line, and a blanket
       `clamp()` over the top would replace eleven measured scales with one
       guess. The first draft of this file did exactly that and set Section
       04's one-line statement, which is body copy, at 12.5vw.
       What is genuinely shared is the failure mode: a single word longer than
       the column. "EVERYWHERE." and "IMPOSSIBLE" are wider than a 360 screen
       at any comfortable size, and a heading may break before the page does. */
    .hero__title,
    .roster__title,
    .alive__title,
    .manifesto__title,
    .reels__title,
    .services__title,
    .journey__title,
    .package__title,
    .archive__title,
    .choose__title,
    .contact__title {
        overflow-wrap: break-word;
    }

    /* ---------------------------------------------------- SECTION NUMBERS */
    /* On desktop these are a graphic element out in the margin. Here they
       lead: their own line, centred, generously clear of the cap line beneath
       so the two can never collide. Size stays with each section, for the
       same reason as the headings above. */
    .hero__index,
    .roster__index,
    .alive__index,
    .manifesto__index,
    .reels__index,
    .services__index,
    .journey__index,
    .package__index,
    .archive__index,
    .choose__index,
    .contact__index {
        display: block;
        width: 100%;
        margin-left: 0;
        margin-right: 0;
        margin-bottom: clamp(0.7rem, 3.5vw, 1.1rem);
        letter-spacing: 0.08em;
        text-align: center;
    }

    /* The hairlines under a centred heading are scaled from their own left
       edge on desktop; centred, they have to grow from the middle. */
    .hero__rule,
    .manifesto__rule,
    .services__rule,
    .package__rule,
    .contact__rule {
        margin-inline: auto;
        transform-origin: 50% 50%;
    }

    /* Control groups centre with the type they belong to. */
    .hero__actions,
    .archive__nav,
    .services__grid,
    .goal__option {
        justify-content: center;
    }

    .archive__cta,
    .package__cta,
    .archive__progress,
    .contact__card {
        margin-inline: auto;
    }

    /* ---------------------------------------------------------- 01 — HERO */
    /* The brief's structure, in order: 01 / YOUR AGENCY / headline /
       supporting text / both buttons / the model. The model is under the
       content and never behind it — the padding below is the floor it stands
       on, set here once rather than fought over by three rules. */
    .hero {
        align-items: center;
        padding-bottom: clamp(16rem, 74vw, 23rem);
    }

    .hero__bar {
        flex-direction: column;
        align-items: center;
        gap: 0.45rem;
        width: 100%;
        /* Clear of the fixed burger in the top right corner. */
        padding-top: clamp(1rem, 4vw, 1.75rem);
    }

    .hero__index {
        margin-bottom: 0;
    }

    .hero > .flow-layer {
        width: 100%;
    }

    .hero__copy {
        margin-inline: auto;
    }

    .hero__lede {
        max-width: 34ch;
        margin-inline: auto;
    }

    .hero__panel {
        align-self: center;
        justify-items: center;
        text-align: center;
    }

    .hero__panel-meta::before {
        left: 50%;
        right: auto;
        transform: translateX(-50%);
    }

    .hero__actions {
        flex-direction: column;
        align-items: center;
        width: 100%;
    }

    .hero__actions .btn {
        width: 100%;
        max-width: 21rem;
    }

    /* --------------------------------------------------- 02 — THE ROSTER */
    /* A vertical editorial roster, not the desktop collage: the featured
       render leads at full column width, then the five names two up, where a
       face is still big enough to be a face. Five across on a phone is a row
       of thumbnails. */
    /* Section 02's index is the one that never left the absolute map — every
       other section drops its own to static in its stack. Absolute plus the
       `width: 100%` above resolved against the section's PADDING box, so the
       number sat 18px right of centre and 18px past the right edge. */
    .roster__index {
        position: static;
        margin-top: 0;
    }

    /* The brief's order — 02, MEET THE UNREAL, the main model, then the names.
       Section 02's own stack puts the featured render above the heading, which
       is the desktop composition read top to bottom; on a phone it buries the
       section's title under a 300px portrait. Four explicit rows fix the
       reading order without touching the desktop map. */
    .roster__stage {
        grid-template-columns: minmax(0, 1fr);
        justify-items: center;
    }

    .roster__title {
        grid-row: 1;
        margin-top: 0;
        text-align: center;
    }

    .roster__feature {
        grid-row: 2;
        grid-column: 1;
        justify-self: center;
        margin-top: clamp(1.25rem, 5vw, 2rem);
    }

    .roster__feature img {
        height: clamp(280px, 66vw, 400px);
    }

    .roster__feature-label {
        grid-row: 3;
        grid-column: 1;
        text-align: center;
    }

    .roster__cards {
        grid-row: 4;
    }

    /* ---------------------------------------------------- 03 — COME ALIVE */
    /* Section 03 stacks as a flex column, so its children shrink to their own
       content and `text-align: center` centres the words INSIDE a box that is
       still sitting on the left. Centring the items is what actually moves
       them; the heading was 133px of a 350px column before this. */
    .alive {
        justify-items: center;
    }

    /* ---------------------------------------------------- 04 — MANIFESTO */
    .manifesto__copy {
        max-width: 30ch;
        margin-inline: auto;
    }

    /* -------------------------------------------------------- 05 — REELS */
    /* The snap gallery is section-05.css's, built below 1200; snap stays
       `mandatory` so a release settles cleanly on the nearest frame. What
       changes here is how much of the next frame you can see.
       The frames are sized by HEIGHT — each keeps its own file's ratio at one
       shared height, which is what makes the row read as a filmstrip — so the
       item width, and therefore how many fit, is a consequence of that height.
       At the shipped 170px the row showed 1.8 frames and the second one was
       cut so late it looked like an accident. 56vw puts the frame at ~72% of
       the screen: 1.38 frames, and the partial one is unmistakably an
       invitation to swipe. */
    .campaign__frame {
        height: clamp(190px, 56vw, 300px);
    }

    .reels__index,
    .reels__title {
        margin-inline: auto;
    }

    /* ------------------------------------------------------ 06 — SERVICES */
    .service__head {
        justify-content: center;
    }

    .service__desc {
        max-width: 34ch;
        margin-inline: auto;
    }

    /* ------------------------------------------------------- 07 — JOURNEY */
    /* Define ↓ image ↓ Explore ↓ images ↓ Launch ↓ image. The stages already
       stack below 1200; this centres each one and guarantees the third has
       exactly the room the first two have. */
    .journey__stages {
        justify-items: center;
        gap: clamp(2.25rem, 9vw, 3.25rem);
    }

    .stage,
    .stage__label {
        text-align: center;
    }

    .stage__frames {
        justify-content: center;
        margin-inline: auto;
        max-width: 100%;
    }

    /* The connectors point sideways at a row that is no longer a row. */
    .journey__connectors {
        display: none;
    }

    /* ------------------------------------------------------- 08 — PACKAGE */
    .package__assets {
        grid-template-columns: minmax(0, 1fr);
        gap: clamp(1.75rem, 7vw, 2.5rem);
        justify-items: center;
    }

    .package__lede {
        max-width: 34ch;
        margin-inline: auto;
    }

    /* ------------------------------------------------------- 09 — ARCHIVE */
    /* Not the pinned desktop rack — archive.js already swaps to its strip rig
       below 1200. This makes that strip read as an editorial swipe: snap
       points that survive a flick, contained momentum, controls beneath. */
    .archive__viewport {
        scroll-snap-type: x proximity;
        overscroll-behavior-x: contain;
    }

    .slide {
        scroll-snap-align: center;
    }

    .archive__nav {
        gap: 1rem;
    }

    .archive__arrow {
        width: 46px;
        height: 46px;
        min-width: 46px;
        min-height: 46px;
    }

    .archive__copy,
    .archive__label,
    .archive__drag {
        margin-inline: auto;
    }

    /* -------------------------------------------------------- 10 — CHOOSE */
    /* Name over image rather than beside it, so both get the full column and
       the tap target is the whole row. */
    .pick__row {
        grid-template-columns: minmax(0, 1fr);
        justify-items: center;
        gap: clamp(0.5rem, 2.5vw, 0.9rem);
        padding: clamp(0.9rem, 4vw, 1.25rem) 0;
    }

    .pick__name {
        text-align: center;
    }

    .choose__label {
        text-align: center;
        margin-left: 0;
        margin-right: 0;
    }

    /* --------------------------------------------------------- 11 — FORM */
    /* Vertical, full width, and the model below it — never over an input. */
    .contact__steps {
        grid-template-columns: minmax(0, 1fr);
    }

    .step__head {
        justify-content: center;
    }

    .goal {
        align-items: center;
    }

    .field__input,
    .news__input,
    .contact__submit,
    .news__button {
        width: 100%;
        max-width: 100%;
    }

    /* An error belongs under its own field, reading left to right like the
       correction it is asking for. */
    .field__error {
        text-align: left;
    }

    .contact__model {
        margin-inline: auto;
    }

    /* --------------------------------------------------------- 12 — FOOTER */
    /* One column, centred, generously spaced — and every link a 44px target
       rather than a line of text. */
    .foot__inner {
        text-align: center;
    }

    .foot__nav {
        display: grid;
        justify-items: center;
        gap: clamp(0.4rem, 2.5vw, 0.8rem);
    }

    .foot__legal {
        display: flex;
        justify-content: center;
        gap: clamp(1.25rem, 6vw, 2rem);
    }

    .foot__link {
        display: inline-flex;
        align-items: center;
    }
}


/* ==========================================================================
   3b — SECTION 06 AS A TOUCH SLIDER  ≤ 991
   Below 992 the four services stop being a grid and become a carousel: one
   card and a clear half of the next, swiped. Its own breakpoint, because it
   is the client's — 992 is where the grid still has room for two readable
   columns and 991 is where it stops having it.

   Placed after both blocks above so it wins over the 2-up tablet grid and the
   1-up mobile grid alike.
   ========================================================================== */
@media (max-width: 991px), (min-width: 1000px) and (max-width: 1199px) {

    .services__grid {
        /* The section's own horizontal padding, restated so the strip can
           cancel it and bleed to the screen edge — a carousel that stops short
           of the edge looks like it has ended. */
        --svc-pad: clamp(1.25rem, 4vw, 3rem);

        display: flex;
        grid-template-columns: none;
        align-items: stretch;
        /* NOT centred. The mobile block above centres a list of control groups
           and this used to be one of them — as a GRID, where `justify-content`
           centres tracks inside a container that fits them. A flex container
           whose content OVERFLOWS centres by pushing the first item off the
           left edge, and `scrollLeft: 0` then showed cards 02 and 03 with 01
           285px out of reach behind them. */
        justify-content: flex-start;
        gap: clamp(0.85rem, 3vw, 1.4rem);
        margin-inline: calc(-1 * var(--svc-pad));
        padding-inline: var(--svc-pad);
        overflow-x: auto;
        overflow-y: hidden;
        overscroll-behavior-x: contain;
        /* `mandatory`, so releasing a swipe settles on a card rather than
           between two. The brief's "do not make the swipe overly sensitive"
           is the same instruction from the other side. */
        scroll-snap-type: x mandatory;
        scroll-padding-left: var(--svc-pad);
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
    }

    .services__grid::-webkit-scrollbar {
        display: none;
    }

    /* 62% of the strip. With the gap that leaves a little over half of the
       second card showing at rest — the brief's ~1.5 — at every width from
       360 to 991. */
    .service {
        flex: 0 0 68%;
        scroll-snap-align: start;
    }

    /* THE ART JOINS THE FLOW, for the whole slider range rather than only for
       phones. Section 06 hangs the art in a band reserved by `padding-bottom`
       on the card, which works while the description is one line and fails the
       moment it wraps — and inside a 62% card it wraps. In flow the card
       simply grows to whatever the words need, and every card in the strip is
       built the same way.
       `display: block` is not optional: `.service__art` is a <span>, and
       `position: absolute` was the only thing blockifying it. Without it the
       box wrapped its 435px image inside a card sized for 208px and the next
       service's title landed on the previous service's model. */
    .service {
        padding-bottom: clamp(1.5rem, 5vw, 2.25rem);
    }

    .service__art,
    .service--virtual .service__art,
    .service--flim .service__art,
    .service--campaigns .service__art,
    .service--ai .service__art {
        position: static;
        display: block;
        height: clamp(13rem, 34vw, 20rem);
        margin-top: clamp(1.1rem, 3.5vw, 1.6rem);
        overflow: clip;
    }

    .service__title {
    font-size: 20px;
    line-height: 28px;
    margin-bottom: 10px;
    margin-top: 15px;
    }

    .service__num {
    font-size: 28px;
    }

}

@media (max-width: 767px) {

    /* On a phone the card is the column, so the art can be taller. */
    .service__art,
    .service--virtual .service__art,
    .service--flim .service__art,
    .service--campaigns .service__art,
    .service--ai .service__art {
        height: clamp(13rem, 54vw, 20rem);
    }

     .service__title {
    font-size: 20px;
    line-height: 28px;
    margin-bottom: 10px;
    margin-top: 15px;
    }

     .service__num {
    font-size: 28px;
    }
}


/* ==========================================================================
   4 — IMAGES
   Aspect ratio is never traded for a fit, and no box is filled by cropping a
   face out of the frame.
   ========================================================================== */
@media (max-width: 1199px) {

    .hero__model,
    .manifesto__portrait img,
    .contact__model img,
    .stage__frames img,
    .roster__feature img,
    .card img {
        object-fit: contain;
    }

    /* Faces are the content in these; centre them rather than the box. */
    .card img,
    .pick__art img,
    .service__art img {
        object-position: center;
    }
}


/* ==========================================================================
   5 — REDUCED MOTION
   The stacked layouts stand without any of it; only the movement goes.
   ========================================================================== */
@media (max-width: 1199px) and (prefers-reduced-motion: reduce) {

    .is-armed .hero__fabric {
        animation: none;
    }

    .reels__strip,
    .archive__viewport {
        scroll-behavior: auto;
        scroll-snap-type: none;
    }
}


/* ############################################################################
   TABLET.CSS
   1000-1199 only. One media query, nothing outside it.
   ######################################################################## */

/* ============================================================================
   TABLET.CSS — the centred editorial composition, 1000px to 1199px only.

   ONE media query. Every rule in this file is inside it. Below 1000 and at
   1200 and above, this stylesheet does not exist as far as the browser is
   concerned — which is the isolation the brief asks for, expressed as
   structure rather than as discipline.

   THE MEASURE. The whole band shares one container:

       max(2.5rem, (100% - 1100px) / 2)

   — the brief's `min(100% - 80px, 1100px)`, written as the gutter instead of
   the width so it can go straight onto sections that are full-bleed by design.
   At 1199 that is a 49.5px gutter on a 1100px column; at 1100 and below it is
   a flat 40px and the column simply narrows with the screen. Content is
   therefore centred and never more than 1100px wide, and the empty side space
   the client flagged is bounded rather than left to each section's own clamp.

   WHAT THIS BAND IS. Between the desktop composition and the phone's: the
   phone's centred axis, but with the room to keep a roster horizontal, a
   collage layered and a filmstrip running. Not a shrunk desktop, not a
   stretched phone.
   ========================================================================== */

@media (min-width: 1000px) and (max-width: 1199px) {

    /* ======================================================================
       1 — THE CONTAINER
       Every section that carries its own horizontal padding is put on the one
       measure. The two fixed-aspect canvases (09, 11) pad their own contents
       instead, further down, because their children are positioned rather
       than flowed.
       ====================================================================== */
    .hero,
    .roster,
    .alive,
    .manifesto,
    .reels,
    .services,
    .journey,
    .package,
    .choose,
    .foot {
        padding-left: max(2.5rem, (100% - 1100px) / 2);
        padding-right: max(2.5rem, (100% - 1100px) / 2);
    }

    /* The brief's rhythm: 80–120px of section air, not desktop's figure and
       not the phone's. 8vw lands on 88px at 1100 and 96px at 1199. */
    .roster,
    .alive,
    .manifesto,
    .reels,
    .services,
    .journey,
    .package,
    .choose {
        padding-top: clamp(5rem, 8vw, 7.5rem);
        padding-bottom: clamp(5rem, 8vw, 7.5rem);
    }


    /* ======================================================================
       2 — THE CENTRED AXIS
       Numbers above headings, headings centred, supporting text centred under
       them, controls centred under that. Declared once here rather than
       eleven times below.
       ====================================================================== */
    .hero,
    .roster,
    .alive,
    .manifesto,
    .reels,
    .services,
    .journey,
    .package,
    .archive,
    .choose,
    .contact,
    .foot {
        text-align: center;
    }

    /* The section numerals lead. On desktop they are a graphic element out in
       the margin; here they sit on the axis, one line above the cap they
       belong to, with room that makes a collision impossible. Their SIZE is
       left to each section — every one of them already declares a scale that
       was measured against its own heading. */
    .hero__index,
    .roster__index,
    .alive__index,
    .manifesto__index,
    .reels__index,
    .services__index,
    .journey__index,
    .package__index,
    .archive__index,
    .choose__index,
    .contact__index {
        display: block;
        width: 100%;
        position: static;
        margin-left: 0;
        margin-right: 0;
        margin-bottom: clamp(0.75rem, 1.4vw, 1.1rem);
        text-align: center;
    }

    /* Headings, supporting copy and the hairlines beneath them share the
       axis. The rules are scaled from their own left edge on desktop, so the
       origin has to move with them or they grow off-centre. */
    .roster__title,
    .alive__title,
    .manifesto__copy,
    .reels__title,
    .services__title,
    .journey__title,
    .package__title,
    .package__lede,
    .archive__title,
    .archive__label,
    .archive__copy,
    .choose__title,
    .choose__label,
    .contact__title,
    .contact__lede {
        text-align: center;
        margin-left: auto;
        margin-right: auto;
    }

    .hero__rule,
    .manifesto__rule,
    .services__rule,
    .package__rule,
    .contact__rule,
    .archive__progress {
        margin-left: auto;
        margin-right: auto;
        transform-origin: 50% 50%;
    }

    .hero__actions,
    .archive__nav,
    .service__head,
    .step__head,
    .goal {
        justify-content: center;
    }

    .archive__cta,
    .package__cta,
    .archive__drag,
    .contact__card {
        margin-left: auto;
        margin-right: auto;
    }


    /* ======================================================================
       3 — SECTION 01, THE HERO
       The brief's structure: 01 / YOUR AGENCY / headline / supporting text /
       both buttons / the model beneath. The two-column tablet hero now stops
       at 999 — at this width the client read it as left-heavy, and they are
       right: a display line that fills half the screen leaves the other half
       to a plate.
       ====================================================================== */
    .hero {
        align-items: center;
        /* The floor the artwork stands on. Everything above it is type, so
           nothing can ever be behind the model. */
        padding-bottom: clamp(21rem, 31vw, 27rem);
    }

    /* The brief's own diagram — 01 above YOUR AGENCY, both on the axis — so
       the bar becomes a centred column rather than a centred row.
       It has to be stated here because the section-number rule above gives
       every index `width: 100%`, and a 100%-wide flex item in a ROW takes the
       whole line and pushes the brand off the end of it: "YOUR AGENCY" was
       wrapping to two lines against the right edge. */
    .hero__bar {
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 0.5rem;
        width: 100%;
        padding-top: clamp(0.25rem, 1vw, 0.75rem);
    }

    .hero__index {
        margin-bottom: 0;
    }

    .hero > .flow-layer {
        width: 100%;
    }

    /* Tightened, not shrunk. The type keeps desktop's sizes; only the air
       between the blocks comes in, which is what buys the artwork below its
       full height without the hero running away down the page. */
    .hero__copy {
        max-width: 46rem;
        margin-top: clamp(1.25rem, 2.5vw, 2rem);
        margin-left: auto;
        margin-right: auto;
    }

    .hero__rule {
        margin-top: clamp(1.1rem, 2vw, 1.75rem);
        margin-bottom: clamp(1rem, 1.8vw, 1.5rem);
    }

    .hero__actions {
        margin-top: clamp(1.5rem, 2.6vw, 2.25rem);
    }

    .hero__panel {
        margin-top: clamp(1.5rem, 2.6vw, 2.25rem);
    }

    .hero__lede {
        max-width: 40ch;
        margin-left: auto;
        margin-right: auto;
    }

    .hero__panel {
        justify-items: center;
        text-align: center;
    }

    .hero__panel-meta::before {
        left: 50%;
        right: auto;
        transform: translateX(-50%);
    }

    .hero__actions {
        flex-wrap: nowrap;
    }

    /* THE ARTWORK, CENTRED. Both plates are height-driven with `width: auto`,
       which makes their width definite — and a definite width is exactly what
       lets `left: 0; right: 0; margin-inline: auto` centre an absolutely
       positioned box. Done with margins rather than a transform on purpose:
       hero.js animates `.hero__model-wrap`'s x and scale on entrance, and an
       inline transform from GSAP would overwrite a CSS one every time. */
    .hero__geometry {
        top: auto;
        bottom: 0;
        left: 0;
        right: 0;
        width: auto;
        height: clamp(20rem, 30vw, 25.5rem);
        max-width: none;
        margin-left: auto;
        margin-right: auto;
        /* The file's own 8.117% transparent right margin and 4.617% top
           margin, the same correction the desktop rule makes — carried here
           because centring the BOX would otherwise leave the artwork inside
           it sitting off-axis. */
        transform: translate3d(4.06%, 0, 0);
    }

    .hero__model-wrap {
        left: 0;
        right: 0;
        bottom: 0;
        width: auto;
        height: clamp(18rem, 26.5vw, 23rem);
        max-width: none;
        margin-left: auto;
        margin-right: auto;
    }


    /* ======================================================================
       4 — SECTION 02, THE ROSTER
       Heading, then the featured render on the axis, then the five names
       horizontal beneath it. The roster stays a row — that is the whole
       reason this band is not the phone's layout — but the composition is
       built around the centre rather than hung off the left edge.
       ====================================================================== */
    .roster__stage {
        grid-template-columns: minmax(0, 1fr);
        justify-items: center;
    }

    .roster__title {
        grid-row: 1;
        grid-column: 1;
        margin-top: 0;
    }

    .roster__feature {
        grid-row: 2;
        grid-column: 1;
        justify-self: center;
        margin-top: clamp(1.75rem, 3.5vw, 3rem);
    }

    .roster__feature img {
        height: clamp(320px, 34vw, 420px);
    }

    .roster__feature-label {
        grid-row: 3;
        grid-column: 1;
        margin-top: 0.7rem;
    }

    .roster__cards {
        grid-row: 4;
        grid-template-columns: repeat(5, minmax(0, 1fr));
        margin-top: clamp(2rem, 4vw, 3.25rem);
    }


    /* ======================================================================
       5 — SECTION 03, THE SET
       Already centred and already carrying the play control on the picture.
       All this band adds is size: the set is the point of the section and it
       may not shrink to leave room for margins.
       ====================================================================== */
    .alive__stage {
        width: 100%;
        max-width: 1100px;
        margin-left: auto;
        margin-right: auto;
    }


    /* ======================================================================
       6 — SECTION 04, THE MANIFESTO
       ====================================================================== */
    .manifesto__copy {
        max-width: 42rem;
    }

    .manifesto__portrait {
        width: min(100%, 30rem);
        margin-left: auto;
        margin-right: auto;
    }


    /* ======================================================================
       7 — SECTION 05, THE FILMSTRIP
       The slider keeps its behaviour exactly. What changes is that it stops
       running to the browser edges: the track's own padding is put on the
       measure, so the first frame begins on the same axis as the heading and
       the strip reads as placed rather than as overflowing.
       ====================================================================== */
    .reels {
        padding-left: 0;
        padding-right: 0;
    }

    .reels__index,
    .reels__title {
        margin-left: auto;
        margin-right: auto;
        padding-left: max(2.5rem, (100% - 1100px) / 2);
        padding-right: max(2.5rem, (100% - 1100px) / 2);
    }

    .reels__track {
        padding-left: max(2.5rem, (100vw - 1100px) / 2);
        padding-right: max(2.5rem, (100vw - 1100px) / 2);
    }

    .reels__strip {
        scroll-padding-left: max(2.5rem, (100vw - 1100px) / 2);
    }

    .campaign__frame {
        height: clamp(220px, 22vw, 280px);
    }


    /* ======================================================================
       8 — SECTION 06, THE SERVICES CAROUSEL
       The carousel is re-enabled for this band by responsive-touch.css (a
       media query list, so the frozen 992–999 strip keeps its grid). This
       only puts it on the measure and centres what sits above it.
       ====================================================================== */
    .services__grid {
        --svc-pad: max(2.5rem, (100vw - 1100px) / 2);
    }

    .service {
        /* 58%, so the strip still opens on one card and a clear half of the
           next. 46% put two whole cards on screen, which reads as a grid with
           a gap rather than as a carousel — the partial card IS the
           affordance. */
        flex: 0 0 61%;
    }

    .service__desc {
        max-width: 34ch;
        margin-left: auto;
        margin-right: auto;
    }


    /* ======================================================================
       9 — SECTION 07, THE JOURNEY
       Three stages on one row — the horizontal reading the brief asks for
       rather than the phone's vertical one — centred as a group, with the
       Launch stage given the same room as the other two.
       ====================================================================== */
    .journey__stages {
        display: flex;
        flex-wrap: nowrap;
        align-items: flex-start;
        justify-content: center;
        gap: clamp(1.25rem, 3vw, 2.5rem);
    }

    .stage {
        flex: 1 1 0;
        min-width: 0;
        text-align: center;
    }

    .stage__label {
        display: block;
        text-align: center;
    }

    .stage__frames {
        justify-content: center;
        margin-left: auto;
        margin-right: auto;
        max-width: 100%;
    }


    /* ======================================================================
       10 — SECTION 08, THE BRAND WORLD
       Two columns rather than four, so each asset is large enough to be an
       asset, centred as a block, and nothing reaches a viewport edge.
       ====================================================================== */
    .package__assets {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: clamp(2rem, 4vw, 3.5rem);
        justify-items: center;
        margin-left: auto;
        margin-right: auto;
    }

    .package__lede {
        max-width: 44ch;
    }


    /* ======================================================================
       11 — SECTION 09, THE ARCHIVE
       The copy column and the rack were at opposite edges. Both go on the
       axis: the text centred above, the gallery running beneath it, its own
       first slide starting on the same measure.
       ====================================================================== */
    .archive {
        padding-left: 0;
        padding-right: 0;
    }

    .archive__index,
    .archive__label,
    .archive__title,
    .archive__copy,
    .archive__progress,
    .archive__cta,
    .archive__nav,
    .archive__drag {
        padding-left: max(2.5rem, (100% - 1100px) / 2);
        padding-right: max(2.5rem, (100% - 1100px) / 2);
    }

    .archive__title {
        max-width: 100%;
    }

    /* The drag hint belongs beside the arrows, not under them, now that the
       pair is centred. */
    .archive__nav {
        gap: 1rem;
    }


    /* ======================================================================
       12 — SECTION 10, THE CHOICE
       Name over strip, centred, each row a full-width target — and the
       portrait kept prominent without ever reaching the options.
       ====================================================================== */
    .pick {
        max-width: 46rem;
        margin-left: auto;
        margin-right: auto;
    }

    .pick__row {
        grid-template-columns: minmax(0, 1fr) 38%;
        align-items: center;
        gap: clamp(1rem, 2.5vw, 2rem);
    }

    .pick__name {
        text-align: left;
    }


    /* ======================================================================
       13 — SECTION 11, THE FORM
       Not the wide desktop composition: the heading on the axis, the card
       beneath it at a readable measure, the model below both and clear of
       every control.
       ====================================================================== */
    .contact {
        padding-left: max(2.5rem, (100% - 1100px) / 2);
        padding-right: max(2.5rem, (100% - 1100px) / 2);
        padding-top: clamp(5rem, 8vw, 7.5rem);
    }

    .contact__index,
    .contact__title,
    .contact__rule,
    .contact__lede,
    .contact__card,
    .contact__model {
        margin-left: auto;
        margin-right: auto;
    }

    .contact__card {
        max-width: 46rem;
    }

    .contact__steps {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .contact__steps > .step:nth-child(3) {
        grid-column: 1 / -1;
        border-left: 0;
        border-top: 1px solid rgba(7, 63, 76, 0.22);
        padding-top: clamp(1.5rem, 3vw, 2rem);
    }

    .contact__model {
        width: min(26rem, 60%);
    }


    /* ======================================================================
       14 — SECTION 12, THE FOOTER
       ====================================================================== */
    .foot__inner {
        text-align: center;
    }

    .foot__nav {
        justify-content: center;
        gap: clamp(1.25rem, 3vw, 2.5rem);
    }

    .foot__legal {
        justify-content: center;
        gap: clamp(1.25rem, 3vw, 2.5rem);
    }


    /* ======================================================================
       15 — MOTION
       Between desktop's figure and the phone's. The scroll rig reads its own
       dial from animations.js (0.75 across this whole band); this is the one
       loop that runs off a keyframe instead.
       ====================================================================== */
    .is-armed .hero__fabric {
        animation-duration: 15s;
    }
}


/* ############################################################################
   MOBILE-REFINE.CSS
   The tablet/mobile refinement pass. LAST on purpose: it wins over every block above it. Every rule inside ends at 1199px.
   ######################################################################## */

/* ============================================================================
   MOBILE-REFINE.CSS

   The tablet and mobile pass requested by the client, in one file, loaded
   LAST so it wins over every per-section stylesheet, over responsive-touch.css
   and over tablet.css.

   EVERY rule in this file lives inside a media query that ends at 1199px.
   The approved 1200+ desktop composition is out of scope by construction and
   cannot be reached from here.

   The six points, in order:

     1  every section heading at a flat 35px
     2  the site logo top left, on the same line as the burger
     3  every button 45px tall
     4  the section rhythm tightened, the dead air taken out
     5  Explore and Launch rebuilt as aligned mosaics instead of the
        desktop percentage collage
     6  Section 08's stack de-padded: the asset files carry large transparent
        margins and those are cropped out here rather than left as holes
   ========================================================================== */


/* ==========================================================================
   1 — SECTION HEADINGS, ONE SIZE
   Ten section titles, four different token systems between them. Sections 02
   and 03 size their inner SPANS rather than the heading, and four of those
   spans are set in hard px with !important, so the heading alone would not
   move them. All of it is normalised to a single 35px cap here.
   The hero's h1 is deliberately absent: it is the display line the whole
   composition is built on, not a section heading.
   ========================================================================== */
@media (max-width: 1199px) {

    .roster__title,
    .alive__title,
    .manifesto__title,
    .reels__title,
    .services__title,
    .journey__title,
    .package__title,
    .archive__title,
    .choose__title,
    .contact__title {
        font-size: 35px;
        line-height: 1.06;
    }

    /* The inner lines inherit rather than declare, so the token each section
       was using stops applying without any token being edited. */
    .roster__line,
    .roster__line--sm,
    .alive__line,
    .alive__line--watch,
    .alive__line--unreal,
    .alive__line--come,
    .manifesto__line,
    .reels__line,
    .services__line,
    .journey__line,
    .journey__line-in,
    .package__line,
    .package__line-in,
    .archive__line,
    .choose__line,
    .contact__line {
        font-size: inherit;
    }

    /* Section 02 and 03's four hard-coded px lines. !important is answered
       with !important because that is the only thing that reaches them. */
    .meetthe,
    .unreall,
    .watchthe,
    .comeale {
        font-size: 35px !important;
        line-height: 1.06 !important;
    }

    /* The reveal masks are em-based, so they follow the new cap size on their
       own. These two only stop the shorter line box from shearing Bebas'
       ascenders now that the type is smaller. */
    .roster__line {
        line-height: 1.24;
        padding-top: 0.12em;
        margin-top: -0.14em;
        margin-bottom: -0.24em;
    }

    .journey__line {
        line-height: 1.24;
        margin-top: -0.03em;
        margin-bottom: -0.24em;
    }

    /* The section numerals were scaled against headings twice this size. */
    .roster__index,
    .alive__index,
    .manifesto__index,
    .reels__index,
    .services__index,
    .journey__index,
    .package__index,
    .archive__index,
    .choose__index,
    .contact__index {
        font-size: 16px;
        line-height: 1;
        margin-bottom: 0.6rem;
    }
}


/* ==========================================================================
   1b — "UNREAL" WAS NOT RENDERING (Section 02)

   The second line of the roster heading was invisible at every width below
   1200 while "MEET THE" above it showed. Measured after a natural scroll all
   the way through the section, both lines' characters sit permanently at
   translate3d(0, 40px, 0): that is the armed PRE-state from section-02.css,
   and the reveal tween that is supposed to unwind it never runs here.

   "MEET THE" survives that only by accident. `.meetthe` carries
   `overflow: initial !important`, so its characters are still painted 40px
   below their line box. `.roster__line` has `overflow: hidden`, so the second
   line's characters are pushed out of the mask and clipped away entirely.

   Both halves are answered: the pre-state is unwound so the glyphs sit in
   their own line boxes, and the mask is opened so nothing can be cut. No
   !important on the transform, so if the reveal ever does run its inline
   tween still wins and the animation plays.
   ========================================================================== */
@media (max-width: 1199px) {

    .is-armed .roster__title .char,
    .is-armed .roster__title .hero__word,
    .is-armed .roster__title .hero__line-inner,
    .is-armed .alive__title .char,
    .is-armed .alive__title .hero__word,
    .is-armed .alive__title .hero__line-inner,
    .is-armed .manifesto__title .char,
    .is-armed .reels__title .char,
    .is-armed .services__title .char,
    .is-armed .archive__title .char,
    .is-armed .choose__title .char,
    .is-armed .contact__title .char {
        transform: none;
        opacity: 1;
        visibility: visible;
    }

    .roster__line,
    .roster__line--sm {
        overflow: visible;
    }
}


/* ==========================================================================
   1c — EVERY SECTION TITLE ON THE CENTRE AXIS

   responsive-touch.css centres the headings below 768 and tablet.css centres
   them again from 1000, but the 768–999 band was never claimed and sections
   02, 06, 08 and 10 were deliberately excluded from the 768–1199 list because
   they used to be genuine two-column pairings there. At 35px they are not,
   so the exclusions and the gap in the middle both go: one axis, ten titles,
   320 to 1199.

   The horizontal padding is for the four sections that pad their own sides to
   zero so a slider can bleed to the screen edge. Their heading is not the
   slider and must not touch the edge with it.
   ========================================================================== */
@media (max-width: 1199px) {

    .roster__title,
    .alive__title,
    .manifesto__title,
    .reels__title,
    .services__title,
    .journey__title,
    .package__title,
    .archive__title,
    .choose__title,
    .contact__title {
        text-align: center;
        margin-left: auto;
        margin-right: auto;
    }

    .reels__title,
    .archive__title,
    .choose__title,
    .contact__title {
        padding-left: clamp(1.25rem, 4vw, 3rem);
        padding-right: clamp(1.25rem, 4vw, 3rem);
    }

    /* Section 04's heading lives inside a measured copy column. Centring the
       heading inside a column that is itself off-axis only centres it in the
       wrong box, so the column goes on the axis too. */
    .manifesto__copy {
        margin-left: auto;
        margin-right: auto;
    }

    /* The section numerals ride the same axis as the cap they belong to. */
    .roster__index,
    .alive__index,
    .manifesto__index,
    .reels__index,
    .services__index,
    .journey__index,
    .package__index,
    .archive__index,
    .choose__index,
    .contact__index {
        text-align: center;
        margin-left: auto;
        margin-right: auto;
    }
}


/* ==========================================================================
   2 — THE LOGO, TOP LEFT, LEVEL WITH THE BURGER

   The burger is fixed at top right inside a 56px box with a
   clamp(0.5rem, 1.6vw, 0.9rem) top margin, so its optical centre is that
   margin plus 28px. The hero bar is given the same top padding and the same
   56px height, which puts the two on one line at every width from 320 to 1199
   without either one being positioned against the other.
   ========================================================================== */
@media (max-width: 1199px) {

    :root {
        /* ONE BAND. The logo, the burger and the sticky header's own row are
           all given this height and all centre their contents in it, so the
           three line up by construction rather than by three offsets that have
           to be kept in agreement. */
        --bar-h: 64px;
    }

    /* The bar carries the top spacing instead of the section, so it starts at
       y=0 exactly like the burger does. With the section's own
       clamp(1.5rem, 2.6vw, 50px) still in place the two were 24px apart. */
    .hero {
        padding-top: 0;
    }

    /* responsive.css pulls the burger up with `top: -13px !important`, which
       put its icon's centre at 23px while the logo's sat at 39px. The band
       above replaces both figures, so the offset and its !important go. */
    .burger {
        top: 0 !important;
        height: var(--bar-h);
        margin-top: 0;
    }

    .hero__bar {
        flex-direction: row;
        align-items: center;
        justify-content: flex-start;
        gap: 12px;
        width: 100%;
        min-height: var(--bar-h);
        margin: 0;
        padding-top: 0;
        /* Clear of the burger, which is fixed and therefore not in this row. */
        padding-right: calc(56px + clamp(0.4rem, 2vw, 1.4rem) + 0.75rem);
        text-align: left;
    }

    .hero__brand {
        display: block;
        margin: 0;
        max-width: none;
    }

    /* Sized by HEIGHT, not width. The file is roughly 1.77:1, so a width cap
       resolved to a 61px tall logo that could not centre inside the band. */
    .hero__brand img,
    .topbar__brand img {
        display: block;
        width: auto;
        height: auto;
        max-height: 46px;
        max-width: 100%;
    }

    .hero__index {
        width: auto;
        margin: 0;
        text-align: left;
    }

    /* The sticky header on the same band and the same axis. */
    .topbar__inner {
        justify-content: flex-start;
        align-items: center;
        min-height: var(--bar-h);
        padding-top: 0;
        padding-bottom: 0;
    }

    .topbar__brand {
        max-width: none;
    }
}


/* ==========================================================================
   3 — EVERY BUTTON 45px

   Three things had to be answered for this to hold: the --btn-h token, the
   em-based vertical padding Sections 08 and 09 put on their CTAs, and the
   44px min-height floor responsive-touch.css sets on the whole control group.
   Each button stays inline-flex and centres its own label, so the height is
   the box and nothing depends on line-height.
   ========================================================================== */
@media (max-width: 1199px) {

    :root {
        --btn-h: 45px;
    }

    .btn,
    .archive__cta,
    .package__cta,
    .contact__submit,
    .news__button,
    .archive__arrow {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        box-sizing: border-box;
        height: 45px;
        min-height: 45px;
        max-height: 45px;
        padding-top: 0;
        padding-bottom: 0;
        line-height: 1;
    }

    .archive__cta,
    .package__cta {
        padding-left: 2.2em;
        padding-right: 2.2em;
    }

    .archive__arrow {
        width: 45px;
        min-width: 45px;
        padding-left: 0;
        padding-right: 0;
    }

    .contact__submit,
    .news__button {
        width: 100%;
    }
}

@media (max-width: 767px) {

    .archive__arrow {
        width: 45px;
        height: 45px;
        min-width: 45px;
        min-height: 45px;
    }
}


/* ==========================================================================
   4 — THE RHYTHM

   The sections were padding 3.5–6rem top and bottom below 1199, and
   tablet.css raised that again to 5–7.5rem between 1000 and 1199. Against a
   35px heading that reads as dead air rather than as air. One shared figure
   replaces both, and the internal gaps that were measured against the old
   type scale come down with it.

   The bottoms that are deliberately zero (04, 06, 10, 11 each hand their
   floor to the artwork below them) are left at zero.
   ========================================================================== */
@media (max-width: 1199px) {

    .roster,
    .alive,
    .manifesto,
    .reels,
    .services,
    .journey,
    .package,
    .archive,
    .choose,
    .contact {
        padding-top: clamp(2.25rem, 6vw, 3.25rem);
    }

    .roster,
    .alive,
    .reels,
    .journey,
    .package,
    .archive {
        padding-bottom: clamp(2.25rem, 6vw, 3.25rem);
    }

    /* ------------------------------------------------------- 02 — ROSTER */
    .roster__stage {
        gap: clamp(0.75rem, 2.5vw, 1.25rem);
    }

    .roster__feature {
        margin-top: clamp(0.75rem, 3vw, 1.25rem);
    }

    .roster__cards {
        margin-top: clamp(0.9rem, 3vw, 1.5rem);
    }

    /* --------------------------------------------------- 03 — COME ALIVE */
    /* The heading and the set were touching. The stage is full bleed and the
       heading is not, so the gap has to be real space between two grid rows
       rather than padding on either one: the title carries it below, the
       stage carries a little above, and the two never meet. */
    .alive__title {
        margin-bottom: clamp(2rem, 7vw, 3rem);
    }

    .alive__stage {
        margin-top: clamp(1.25rem, 4vw, 2rem);
    }

    /* -------------------------------------------------------- 05 — REELS */
    .reels__track {
        margin-top: clamp(1.25rem, 3.5vw, 2rem);
    }

    /* ------------------------------------------------------ 07 — JOURNEY */
    .journey__stages {
        gap: clamp(1.5rem, 5vw, 2.25rem);
    }

    .stage {
        margin-top: clamp(1.5rem, 4.5vw, 2.25rem);
    }

    .stage:first-child {
        margin-top: clamp(1rem, 3vw, 1.5rem);
    }

    /* ------------------------------------------------------ 09 — ARCHIVE */
    .archive__viewport,
    .archive__track {
        margin-top: clamp(1.25rem, 3.5vw, 2rem);
    }

    .archive__cta {
        margin-top: clamp(1.25rem, 3.5vw, 1.75rem);
    }

    /* ------------------------------------------------------- 10 — CHOOSE */
    .pick {
        margin-top: clamp(1.25rem, 3.5vw, 1.75rem);
    }

    /* ------------------------------------------------------ 11 — CONTACT */
    .contact__card {
        margin-top: clamp(1.5rem, 4vw, 2.25rem);
    }
}


/* ==========================================================================
   5 — SECTIONS 07's EXPLORE AND LAUNCH

   The three stages are absolute percentage collages solved against the
   desktop canvas. Stacked into a phone column those percentages become
   gutters that read as holes, which is exactly what the client flagged.

   All thirteen source files are square (1500, 1750 and 2000px), so the
   premium answer is not a re-tuned collage but an aligned mosaic: the chosen
   frame full width, the supporting frames beneath it on one grid, every box
   square so `cover` crops absolutely nothing.

       DEFINE   one frame, full width
       EXPLORE  the selection, then a 3 x 2 contact sheet
       LAUNCH   the finished identity, then its four variations 2 x 2

   The parallax carrier is flattened rather than removed, so journey.js keeps
   every element it animates.
   ========================================================================== */
@media (max-width: 1199px) {

    .stage--define .stage__frames,
    .stage--explore .stage__frames,
    .stage--launch .stage__frames {
        position: relative;
        aspect-ratio: auto;
        width: 100%;
        max-width: 100%;
        margin-top: clamp(0.6rem, 2vw, 0.9rem);
    }

    /* The two carriers stop being inset overlays and become plain boxes; the
       scroll rig only ever writes translate to them, which a grid honours. */
    .stage__frames-scroll {
        position: static;
        inset: auto;
        display: block;
        width: 100%;
    }

    .stage__frames-pointer {
        position: static;
        inset: auto;
        display: grid;
        gap: 8px;
        width: 100%;
    }

    .stage--define .stage__frames-pointer {
        grid-template-columns: minmax(0, 1fr);
    }

    .stage--explore .stage__frames-pointer {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .stage--launch .stage__frames-pointer {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    /* Grid cells, not absolute rects. Square, because the source is square:
       nothing is cropped and every edge in the mosaic lines up. */
    .stage .shot {
        position: static;
        left: auto;
        top: auto;
        right: auto;
        bottom: auto;
        width: auto;
        height: auto;
        aspect-ratio: 1 / 1;
        background: rgba(6, 45, 53, 0.05);
        box-shadow: 0 10px 26px rgba(6, 45, 53, 0.16);
    }

    .stage .shot img {
        object-position: center;
    }

    /* The hero frame of each stage takes the full row. .shot--e7 is last in
       the markup, so it is ordered to the front rather than moved. */
    .shot--e7 {
        grid-column: 1 / -1;
        order: -1;
    }

    .shot--l1 {
        grid-column: 1 / -1;
    }

    /* A hair more presence on the two chosen frames, so the mosaic has a
       subject rather than reading as a uniform sheet. */
    .shot--e7,
    .shot--l1 {
        box-shadow: 0 18px 40px rgba(6, 45, 53, 0.24);
    }

    .stage__label {
        display: block;
        margin-bottom: 0;
        font-size: 16px;
        letter-spacing: 0.04em;
    }
}

/* 768–999 is the one band nothing else claims: responsive-touch.css centres
   the phone below it and tablet.css takes over at 1000, so between the two the
   stages kept section-07's 34rem cap with no auto margins and hung off the left
   edge with the right third of the screen empty. */
@media (min-width: 768px) and (max-width: 999px) {

    .stage {
        margin-left: auto;
        margin-right: auto;
        text-align: center;
    }

    .stage__label {
        text-align: center;
    }

    /* Section 02 has the same gap. responsive-touch.css restacks the roster
       below 768 and tablet.css restacks it again from 1000, so in between it
       kept section-02's own grid — which puts the heading in a side column,
       181px off the page axis. The same four rows both neighbours use. */
    .roster__stage {
        grid-template-columns: minmax(0, 1fr);
        justify-items: center;
    }

    .roster__title {
        grid-row: 1;
        grid-column: 1;
        margin-top: 0;
    }

    .roster__feature {
        grid-row: 2;
        grid-column: 1;
        justify-self: center;
        margin-top: clamp(1rem, 3vw, 1.75rem);
    }

    .roster__feature-label {
        grid-row: 3;
        grid-column: 1;
        text-align: center;
    }

    .roster__cards {
        grid-row: 4;
    }
}

/* At 1000–1199 tablet.css puts the three stages side by side, so each mosaic
   is a third of the measure. The gap comes in with it. */
@media (min-width: 1000px) and (max-width: 1199px) {

    .stage__frames-pointer {
        gap: 6px;
    }

    .stage {
        margin-top: 0;
    }
}


/* ==========================================================================
   6 — SECTION 08, THE BLANK SPACE

   This is not a padding problem. Every asset file in this section is a
   2350 x 1970 (or 1620 x 2880) canvas with the artwork sitting inside a large
   transparent margin, and .item__depth is sized to the FILE. Stacked on a
   phone that gives a column of mostly empty boxes, which is the blank space
   the client is seeing. Measured off each file's own alpha channel:

       Commercial usage    ink 21.8–80.0% wide,  10.9–93.3% tall
       Model comp card     ink  4.8–95.2% wide,  20.2–75.0% tall
       Consistency sheet   ink  5.3–95.6% wide,  17.2–82.3% tall
       Motion & film       ink  4.6–95.4% wide,  33.3–78.8% tall
       Campaign assets     ink 17.1–79.7% wide,   6.9–93.5% tall  (two plates)
       Social formats      ink  0.3–99.7% wide,   3.0–76.3% tall  (three plates)

   So .item__lift is given the INK's aspect ratio and clipped, and
   .item__depth is oversized and offset inside it by exactly the margin each
   file carries. The internal percentage map of every item is untouched, so
   the two-plate and three-plate compositions keep their own arrangement.

   The identity bible is handled differently on purpose: package.js zooms the
   book far past its slot while the section is pinned, and a clip on an
   ancestor would cut that zoom in half. Its dead margin is pulled in with
   negative margin instead, which cannot clip anything.
   ========================================================================== */
@media (max-width: 1199px) {

    .package__assets {
        --pkg-gap: clamp(1.25rem, 4vw, 2rem);
    }

    .item {
        margin-top: var(--pkg-gap);
        margin-bottom: 0;
    }

    .item__label {
        margin-bottom: 0.45em;
        font-size: 16px;
    }

    /* THE CROP FRAME. Definite ratio, definite width, so the percentage
       offsets on .item__depth below have something to resolve against. */
    .item--usage .item__lift,
    .item--comp .item__lift,
    .item--sheet .item__lift,
    .item--campaign .item__lift,
    .item--social .item__lift,
    .item--film .item__lift {
        position: relative;
        display: block;
        width: 100%;
        overflow: clip;
    }

    .item--usage .item__depth,
    .item--comp .item__depth,
    .item--sheet .item__depth,
    .item--campaign .item__depth,
    .item--social .item__depth,
    .item--film .item__depth {
        position: absolute;
        height: auto;
        aspect-ratio: var(--item-ar);
    }

    .item--usage .item__lift {
        aspect-ratio: 0.843;
    }

    .item--usage .item__depth {
        width: 171.78%;
        left: -37.5%;
        top: -13.25%;
    }

    .item--comp .item__lift {
        aspect-ratio: 1.967;
    }

    .item--comp .item__depth {
        width: 110.54%;
        left: -5.27%;
        top: -36.72%;
    }

    .item--sheet .item__lift {
        aspect-ratio: 1.655;
    }

    .item--sheet .item__depth {
        width: 110.69%;
        left: -5.84%;
        top: -26.42%;
    }

    .item--film .item__lift {
        aspect-ratio: 2.383;
    }

    .item--film .item__depth {
        width: 110.07%;
        left: -5.01%;
        top: -73.21%;
    }

    .item--campaign .item__lift {
        aspect-ratio: 0.863;
    }

    .item--campaign .item__depth {
        width: 159.57%;
        left: -27.23%;
        top: -7.95%;
    }

    .item--social .item__lift {
        aspect-ratio: 1.357;
    }

    .item--social .item__depth {
        width: 100.58%;
        left: -0.26%;
        top: -4.13%;
    }

    /* The whole item is the target now that the artwork fills its box. The
       bible keeps its own hit rect, which package.js coordinates with the
       reading state. */
    .item--usage .item__hit,
    .item--comp .item__hit,
    .item--sheet .item__hit,
    .item--campaign .item__hit,
    .item--social .item__hit,
    .item--film .item__hit {
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
    }

    /* THE BOOK. No clip, for the reason in the block comment above: the
       transparent band above and below the spread is simply overlapped. */
    .item--bible {
        margin-top: 0;
        margin-bottom: -12%;
    }

    .package__lede {
        margin-top: clamp(0.6rem, 2vw, 1rem);
    }

    .package__rule {
        margin-top: clamp(0.9rem, 3vw, 1.5rem);
        margin-bottom: clamp(0.75rem, 2.5vw, 1.25rem);
    }

    .package__cta {
        margin-top: clamp(1.1rem, 3.5vw, 1.6rem);
    }
}


/* ==========================================================================
   7 — SECTION 03: THE LEFT-HAND PAIR IS PART OF THE CAST

   `.character--guests` — the older man and the woman in the print gown, the
   left third of the desktop set — was set to `display: none` below 560px.
   The reasoning in section-03.css was that two people in one plate read as
   clutter behind the lead, and inside a 3:4 portrait crop that was true: the
   frame was simply too narrow to hold five plates.

   So the frame is what changes, not the cast. A square crop at 560 and below
   buys back the width the pair needs, and all five plates are re-mapped
   across it. Every figure keeps its own proportions — only its position and
   scale inside the frame move, which is the same thing the 767 and 560 maps
   in section-03.css already do.

   Desktop is untouched: this whole block is inside the touch media queries.
   ========================================================================== */
@media (max-width: 560px) {

    .alive__stage {
        aspect-ratio: 1 / 1;
    }

    /* The plate is re-centred with the frame. 58% holds the runway and the
       orange arc behind the cast rather than sliding them off the left. */
    .alive__bg img {
        object-position: 58% bottom;
    }

    /* The pair returns, at the far left and behind everyone — the depth the
       desktop composition gives them, at a size where two faces still read. */
    .character--guests {
        display: block;
        left: -2%;
        top: 27%;
        width: 33%;
        z-index: 2;
    }

    /* The other four step right and come down slightly to make the room. */
    .character--lead {
        left: 17%;
        top: 6%;
        width: 53%;
    }

    .character--orange {
        left: 45%;
        top: 16%;
        width: 38%;
    }

    .character--pour {
        left: 58%;
        top: 2%;
        width: 53%;
    }

    .pour {
        left: 74%;
        top: 25%;
        width: 11%;
    }
}


/* ============================================ CONTACT FORM 7 COMPATIBILITY */
/* Contact Form 7 wraps its output in .wpcf7 > form.wpcf7-form. The contact
   card is a three-column grid whose children must be the .step elements, so
   those wrappers are made layout-transparent. The DOM tree is unchanged, so
   .step + .step still matches and .contact__done still resolves its absolute
   position against .contact__steps. */
.contact__steps > .wpcf7,
.contact__steps > .wpcf7 > form.wpcf7-form,
.contact .news > .wpcf7,
.contact .news > .wpcf7 > form.wpcf7-form,
.contact .wpcf7-form-control-wrap,
.contact .wpcf7-form-control.wpcf7-radio,
.contact .wpcf7-list-item {
    display: contents;
}

/* CF7 6.x puts its hidden inputs in a fieldset. With the form transparent
   that fieldset becomes a grid cell of its own, taking column 1 and pushing
   every step one column across. Removing it from layout is safe: controls
   inside a display:none element are still submitted. */
.contact .wpcf7 .hidden-fields-container {
    display: none;
}

/* CF7's screen-reader summary, on both forms. */
.contact .wpcf7 > .screen-reader-response {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* The response line spans the whole grid row so it can never form a fourth
   column, and stays hidden unless CF7 has something to report. */
.contact .wpcf7-response-output {
    grid-column: 1 / -1;
    box-sizing: border-box;
    margin: 0 5.5% 1.5em;
    padding: 0.9em 1em;
    font-family: "Inter", system-ui, sans-serif;
    font-size: 0.72em;
    line-height: 1.3;
    letter-spacing: 0.02em;
    text-align: center;
    color: var(--flame-11);
    border: 1px solid var(--flame-11);
}

.contact form.init .wpcf7-response-output,
.contact form.sent .wpcf7-response-output,
.contact form.resetting .wpcf7-response-output {
    display: none;
}

/* Map CF7's invalid state onto the treatment this project already has. */
.contact .field__input.wpcf7-not-valid {
    border-color: var(--flame-11);
}

.contact .wpcf7-not-valid-tip {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    display: block;
    margin: 0.4em 0 0;
    font-family: "Inter", system-ui, sans-serif;
    font-size: 0.72em;
    line-height: 1.3;
    letter-spacing: 0.02em;
    color: var(--flame-11);
}

/* Mobile only. The tip is absolutely positioned at top: 100% of the field,
   so the 0.4em on desktop is the gap between the input and its error text.
   On a phone the fields sit closer together and that gap pushed the tip into
   the next label, so the tip is pulled flush to the field edge here. Only the
   margin changes; position, size, colour and layout are the base rule's. */
@media (max-width: 767px) {

    .contact .wpcf7-not-valid-tip {
        margin: 0px 0 0;
    }
}

/* CF7 renders [submit] as input type=submit rather than a button element.
   The existing class styling applies; these keep the label centred the way
   the flex button centred it. */
input.contact__submit,
input.news__button {
    text-align: center;
    -webkit-appearance: none;
    appearance: none;
}

/* The sending state is carried by the button's own is-sending class. */
.contact .wpcf7-spinner {
    display: none;
}


/* =============================================== INLINE HOMEPAGE SHOWREEL */
/* Edge to edge. The video is the section: no max-width, no margin, no
   padding, no radius and no shadow, so nothing but video touches the top,
   right, bottom and left edges. Height is set per breakpoint and the video
   crops with object-fit: cover, which is what keeps the aspect correct at
   any viewport without letterboxing. */
.reel {
    position: relative;
    width: 100%;
    margin: 0;
    padding: 0;
    background: #000;
    line-height: 0;
    overflow: hidden;
}

/* The frame is a real <button> for keyboard access, reset to a bare block. */
.reel__frame {
    display: block;
    position: relative;
    width: 100%;
    max-width: none;
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: #000;
    box-shadow: none;
    overflow: hidden;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.9s cubic-bezier(.16, 1, .3, 1);
    -webkit-appearance: none;
    appearance: none;
    /* 16:9 of the full width, never taller than the viewport. */
    height: min(56.25vw, 100vh);
}

/* Lit by the existing IntersectionObserver, opacity only so no transform can
   ever expose an edge. */
.reel.is-lit .reel__frame {
    opacity: 1;
}

.reel__frame:focus-visible {
    outline: 2px solid rgba(231, 255, 12, 0.9);
    outline-offset: -4px;
}

.reel__video {
    display: block;
    width: 100%;
    height: 100%;
    margin: 0;
    object-fit: cover;
    object-position: center;
    background: #000;
}

/* Below the 1199px source switch the portrait file loads. Full width still,
   but height is capped so a 9:16 file cannot become a 177vw tower. cover
   crops it to the band. */
@media (max-width: 1199px) {
    .reel__frame {
        height: min(125vw, 78vh);
    }
}

@media (prefers-reduced-motion: reduce) {
    .reel__frame {
        transition-duration: 0.001ms;
    }
}


/* ------------------------------------------------- LOADER LOGO */
/* The approved logo replaces the old wordmark. Intrinsic size is
   1000x567, so height is left to the aspect ratio and the image is never
   stretched or upscaled past its natural width. */
.loader__logo {
    display: block;
    width: min(320px, 46vw);
    max-width: 100%;
    height: auto;
    margin: 0 auto;
    aspect-ratio: 1000 / 567;
    object-fit: contain;
    will-change: transform, opacity;
}

@media (max-width: 600px) {
    .loader__logo {
        width: min(240px, 58vw);
    }
}


/* ================================================== LEGAL PAGES + CONSENT */
.legal {
    box-sizing: border-box;
    width: 100%;
    min-height: 100vh;
    padding: 7vh 6vw 12vh;
    background: var(--sand-11);
    color: var(--ink-11);
}

.legal__inner {
    max-width: 70rem;
    margin: 0 auto;
}

.legal__back {
    display: inline-block;
    margin: 0 0 3.2em;
    font-family: "Bebas Neue", system-ui, sans-serif;
    font-size: 1.15rem;
    letter-spacing: 0.14em;
    color: var(--deep-11);
    text-decoration: none;
}

.legal__title {
    margin: 0 0 1.2em;
    font-family: "Bebas Neue", system-ui, sans-serif;
    font-size: clamp(2.4rem, 6vw, 4rem);
    font-weight: 400;
    line-height: 1;
    letter-spacing: 0.02em;
    color: var(--ink-11);
}

.legal__body {
    font-family: "Inter", system-ui, sans-serif;
    font-size: 1rem;
    line-height: 1.7;
    color: var(--ink-11);
}

.legal__body h2 {
    margin: 2.4em 0 0.7em;
    font-family: "Bebas Neue", system-ui, sans-serif;
    font-size: 1.5rem;
    font-weight: 400;
    letter-spacing: 0.05em;
    color: var(--deep-11);
}

.legal__body p {
    margin: 0 0 1.2em;
}

.legal__body ul {
    margin: 0 0 1.4em;
    padding-left: 1.3em;
}

.legal__body li {
    margin: 0 0 0.55em;
}

.legal__body a {
    color: var(--flame-11);
    text-decoration: underline;
    text-underline-offset: 0.18em;
    word-break: break-word;
}

.legal__meta {
    padding: 0.9em 1.1em;
    border-left: 2px solid var(--flame-11);
    background: rgba(7, 63, 76, 0.05);
}

/* The lawful-bases table scrolls inside its own box rather than pushing the
   page wide, so there is no horizontal overflow on a phone. */
.legal__tablewrap {
    margin: 0 0 1.6em;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.legal__table {
    width: 100%;
    min-width: 34rem;
    border-collapse: collapse;
    font-size: 0.9rem;
}

.legal__table th,
.legal__table td {
    padding: 0.75em 0.9em;
    text-align: left;
    vertical-align: top;
    border: 1px solid rgba(7, 63, 76, 0.22);
}

.legal__table th {
    background: rgba(7, 63, 76, 0.08);
}

.legal__nav {
    display: flex;
    flex-wrap: wrap;
    gap: 1.6em;
    margin: 4em 0 0;
    padding: 1.6em 0 0;
    border-top: 1px solid rgba(7, 63, 76, 0.22);
    font-family: "Inter", system-ui, sans-serif;
    font-size: 0.85rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.legal__nav a {
    color: var(--deep-11);
    text-decoration: none;
    border-bottom: 1px solid rgba(7, 63, 76, 0.35);
}

/* Consent line under the contact form. Sits outside .contact__steps so the
   three-column grid is untouched. */
.contact__consent {
    box-sizing: border-box;
    margin: 0;
    padding: 1.2em 30% 10px 5.5%;
    font-family: "Inter", system-ui, sans-serif;
    font-size: 0.72em;
    line-height: 1.4;
    letter-spacing: 0.02em;
    color: var(--deep-11);
}

.contact__consent a {
    color: var(--flame-11);
    text-decoration: underline;
    text-underline-offset: 0.16em;
}

@media (max-width: 900px) {
    .contact__consent {
        padding-right: 5.5%;
    }
}


/* ------------------------------------------------ LEGAL PAGE CHROME */
/* The shared header is sticky and overlays content, so the legal pages
   start below it. The footer is the site's own, so no separate legal nav
   is needed any more. */
.legal {
    padding-top: 18vh;
}

.page .foot {
    margin-top: 0;
}

@media (max-width: 900px) {
    .legal {
        padding-top: 14vh;
    }
}


/* --------------------------------------------- FOOTER ON INNER PAGES */
/* main.js puts .is-armed on <html>, which sets the footer groups to
   opacity 0 ready for the homepage scroll reveal. Inner pages have no
   such trigger, so without this the footer renders but stays invisible. */
.is-armed body:not(.home) .foot__nav,
.is-armed body:not(.home) .foot__legal {
    opacity: 1;
    transform: none;
}

/* Keep the legal content clear of the footer without adding blank space. */
body:not(.home) .legal {
    padding-bottom: 8vh;
}


/* ------------------------------------------ STICKY HEADER ON INNER PAGES */
/* .topbar ships hidden (opacity 0 / visibility hidden / -20px) and nav.js
   reveals it with autoAlpha once the hero has scrolled past. Two reasons
   that never happens on an inner page: there is no hero to pass, and
   initNav() bails early when it finds no links matching a[href^="#"] --
   which is now the case here, because these pages point at
   https://iconmade.ai/#section rather than a bare fragment. So the header
   is simply shown from the start on anything that is not the homepage. */
body:not(.home) .topbar {
    opacity: 1;
    visibility: visible;
    transform: none;
}


/* ------------------------------------------- PRE-COVER ON INNER PAGES */
/* body::after paints an opaque black cover on every page so the hero can
   never flash before the loader exists. It is cleared by .intro-ready,
   which loader.js adds from inside buildLoader(). buildLoader() only runs
   on the homepage, so on any other page the cover sat there until the 4s
   keyframe failsafe faded it, leaving the page apparently blank.
   These pages have no hero to protect, so the cover is removed outright
   and they paint immediately, with no dependency on JavaScript at all. */
body:not(.home)::after {
    content: none;
    display: none;
    animation: none;
}


/* ------------------------------------------ CONTACT VALIDATION SPACING */
/* The card is top-anchored inside a fixed-aspect section with
   overflow: hidden, so extra bottom padding would simply be clipped and
   gain nothing. The crowding comes from CF7's own response banner, which
   is added to the flow only when validation fails and pushes the consent
   line and newsletter down onto the footer.
   This design already has its own place for that message: .step__reply
   beside the submit button, and .news__reply under the newsletter, both
   driven by the existing JavaScript. CF7's banner is therefore a duplicate
   as well as the cause of the height, so it is removed from the layout.
   The form keeps its full validation behaviour. */
.contact .wpcf7-response-output {
    display: none;
}

/* A little breathing room under the newsletter row, inside the card. */
.contact .news {
    padding-bottom: 6.5%;
}

/* ------------------------------------------------ CONSENT LINK COLOUR */
/* Neutral grey rather than the flame accent, which is reserved for the
   primary actions in this section. */
.contact__consent a {
    color: #817E7E;
    text-decoration: underline;
    text-underline-offset: 0.16em;
    transition: color var(--con-dur) var(--con-ease);
}

.contact__consent a:hover,
.contact__consent a:focus-visible {
    color: var(--deep-11);
}


/* ============================================================ BRAND SCROLL FX */
/* Scroll-linked atmosphere for Hero->Showreel, Models, Come Alive, Archive and
   the final CTA. Every colour is an existing token: --violet #BCA8FF,
   --lime-11 #AAC102, --yellow #E7FF0C, --deep-11 #073F4C, --ink-11 #012326.

   Rules of the system:
   - no filter: blur() anywhere; radial gradients are already feathered
   - only opacity and transform are animated
   - every rig is pointer-events: none, so nothing it covers stops responding
   - layers are injected by main.js, so no template markup changed */

.fx {
    position: absolute;
    inset: 0;
    z-index: 0;
    overflow: hidden;
    pointer-events: none;
}

.fx__layer {
    position: absolute;
    display: block;
    border-radius: 50%;
    opacity: 0;
    will-change: transform, opacity;
}

.fx__layer--violet {
    background: radial-gradient(closest-side, rgba(188, 168, 255, 0.40), rgba(188, 168, 255, 0) 70%);
}

.fx__layer--acid {
    background: radial-gradient(closest-side, rgba(170, 193, 2, 0.24), rgba(170, 193, 2, 0) 68%);
}

.fx__layer--teal {
    background: radial-gradient(closest-side, rgba(7, 63, 76, 0.60), rgba(7, 63, 76, 0) 72%);
}

/* ------------------------------------------- 01b  HERO -> SHOWREEL VEIL */
/* Sits above the edge-to-edge video, so it reads as the hero's energy
   carrying into the reel and then clearing. pointer-events: none keeps the
   click-to-fullscreen behaviour intact underneath. */
.fx--reel {
    z-index: 2;
}

.fx__veil {
    position: absolute;
    inset: 0;
    display: block;
    opacity: 0;
    background:
        linear-gradient(to bottom,
            rgba(188, 168, 255, 0.46) 0%,
            rgba(170, 193, 2, 0.12) 34%,
            rgba(7, 63, 76, 0.20) 58%,
            rgba(1, 35, 38, 0) 78%);
    will-change: opacity, transform;
}

/* --------------------------------------------------- 02  MODELS / ROSTER */
/* Behind the cards: the rig is inserted as the section's first child, so it
   paints under every model. Nothing sits over a face. */
.fx--roster .fx__layer--violet {
    top: -14%;
    right: -10%;
    width: 52vw;
    height: 52vw;
}

.fx--roster .fx__layer--teal {
    bottom: -22%;
    left: -12%;
    width: 58vw;
    height: 58vw;
}

.fx--roster .fx__layer--acid {
    top: 38%;
    left: 44%;
    width: 20vw;
    height: 20vw;
}

/* --------------------------------------------------- 03  COME ALIVE */
/* The strongest moment: a bloom that opens as the section arrives, then
   settles to a held ambient state rather than looping. */
.fx--alive .fx__layer--violet {
    top: 6%;
    left: 28%;
    width: 64vw;
    height: 64vw;
}

.fx--alive .fx__layer--teal {
    top: -18%;
    left: -16%;
    width: 70vw;
    height: 70vw;
}

.fx--alive .fx__layer--acid {
    bottom: 4%;
    right: 6%;
    width: 26vw;
    height: 26vw;
}

/* --------------------------------------------------- 09  ARCHIVE */
/* Deliberately restrained after Come Alive: one soft accent and six small
   fragments, nothing per-card. */
.fx--archive .fx__layer--violet {
    top: -10%;
    right: -8%;
    width: 40vw;
    height: 40vw;
}

.fx--archive .fx__layer--acid {
    bottom: 8%;
    left: 10%;
    width: 18vw;
    height: 18vw;
}

.fx__bit {
    position: absolute;
    width: 3px;
    height: 3px;
    border-radius: 50%;
    background: rgba(231, 255, 12, 0.7);
    box-shadow: 0 0 8px 1px rgba(231, 255, 12, 0.3);
    opacity: 0;
    will-change: transform, opacity;
}

.fx__bit:nth-child(odd) {
    background: rgba(188, 168, 255, 0.75);
    box-shadow: 0 0 9px 1px rgba(188, 168, 255, 0.32);
}

/* --------------------------------------------------- 11  FINAL CTA */
/* One short purple pulse on arrival, then nothing. The section resolves
   clean; no sustained motion behind the form. */
.fx--cta {
    z-index: 0;
}

.fx--cta .fx__layer--violet {
    top: 50%;
    left: 50%;
    width: 90vw;
    height: 90vw;
    margin: -45vw 0 0 -45vw;
}

.fx--cta .fx__layer--acid {
    top: 50%;
    left: 50%;
    width: 46vw;
    height: 46vw;
    margin: -23vw 0 0 -23vw;
}

/* ------------------------------------------------------------- MOBILE */
/* Not the desktop scene scaled down: the acid field and the archive
   fragments are removed outright so they neither render nor animate, and the
   remaining fields are resized for a portrait viewport. */
@media (max-width: 900px) {
    .fx__layer--acid,
    .fx__bit {
        display: none;
    }

    .fx--roster .fx__layer--violet,
    .fx--roster .fx__layer--teal,
    .fx--alive .fx__layer--violet,
    .fx--alive .fx__layer--teal,
    .fx--archive .fx__layer--violet {
        width: 120vw;
        height: 120vw;
    }

    .fx__veil {
        background:
            linear-gradient(to bottom,
                rgba(188, 168, 255, 0.34) 0%,
                rgba(7, 63, 76, 0.16) 52%,
                rgba(1, 35, 38, 0) 76%);
    }
}

/* ----------------------------------------------------- REDUCED MOTION */
/* The scene resolves to a quiet held state with no scroll-linked movement.
   main.js also skips building the rigs entirely in this mode; this is the
   stylesheet half of the same contract. */
@media (prefers-reduced-motion: reduce) {
    .fx,
    .fx__veil {
        display: none;
    }
}


/* ------------------------------------------------ SHOWREEL POSTER STATE */
/* The frame used to start at opacity 0 over a black box and fade in, which
   is what read as a black placeholder before the video appeared. With a
   poster in place there is something to show from the first paint, so the
   frame is simply visible. */
.reel__frame {
    opacity: 1;
}

/* The poster is painted by the video element itself, so it inherits the
   same cover crop as the video and cannot jump when playback starts. */
.reel__video {
    object-fit: cover;
    background-color: transparent;
}


/* ==========================================================================
   COLOUR SHIFT ON SCROLL  -  SECTION 06 -> SECTION 07 ONLY

   Scope is deliberately two sections: .services (BUILT FOR ATTENTION,
   #F5EADF) and .journey (FROM AN IDEA TO AN ICON, #BCA8FF). Nothing else on
   the page is touched. Every other section still paints its own opaque
   background, so although the plate below is fixed behind the whole
   document, it is only ever visible across these two and the gap between
   them.

   This particular boundary is the one place on the site where a long, slow
   blend is safe: both sections are light and both set their type in the same
   #062D35 ink, so the contrast never flips and no intermediate colour can
   make the headings, the four service cards or Define/Explore/Launch hard to
   read. That is why the transition can be given a full scroll range here
   rather than being rushed through a gap.

   Nothing in this block applies until js/main.js adds `has-shift` to <html>,
   and it only does that once the plate is already carrying the starting
   cream. If GSAP never loads, both sections keep their own backgrounds and
   the page is exactly as it was.
   ========================================================================== */

/* The plate. Fixed and clipped, so the oversize fill inside it can never
   contribute scrollable overflow on a phone. */
.shiftplate {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 0;
    overflow: hidden;
    pointer-events: none;
    visibility: hidden;
}

.has-shift .shiftplate {
    visibility: visible;
}

/* The colour itself. Bled 2px past every edge to kill the hairline some
   mobile browsers show at a fractional device pixel ratio, and given a large
   viewport floor so a retracting iOS URL bar cannot expose a gap. */
.shiftplate__fill {
    position: absolute;
    top: -2px;
    right: -2px;
    bottom: -2px;
    left: -2px;
    min-height: 100vh;
    background-color: #F5EADF;
}

@supports (min-height: 100lvh) {

    .shiftplate__fill {
        min-height: 100lvh;
    }
}

/* ONLY these two stop painting themselves, and ONLY the background colour is
   dropped. `overflow`, `isolation`, `aspect-ratio` and `min-height` are all
   load-bearing in the rules above and are left exactly as they are, as is
   every card, image and panel background inside either section. Both are
   already `position: relative`, so the z-index adds no layout whatsoever -
   it only states that the content sits over the plate rather than leaving it
   to document order. */
.has-shift .services,
.has-shift .journey {
    background-color: transparent;
    z-index: 1;
}

/* ------------------------------------------------- THE TRANSITION RUNWAY */
/* Section 06 is only 0.59 of a viewport tall at 1920x1080 and section 07 is
   0.57, and they butt straight against each other. Without a run-up the
   blend would have to happen entirely on top of live content.

   MARGIN, deliberately, not padding or height: every child of these two
   sections is absolutely positioned as a percentage of the padding box and
   both are sized by `aspect-ratio` on a border box, so padding or a height
   change would rescale the whole internal composition. Margin sits outside
   all of it - neither section changes size, shape or internal layout by a
   pixel, and no other section moves.

   Sized to be a transition, not a void: at 1080 this is roughly 0.42 of a
   viewport, which is read as a gradient rather than as blank space because
   the colour is moving the entire way through it. */
:root {
    --shift-gap: clamp(240px, 42vh, 460px);
}

@media (min-width: 768px) and (max-width: 1199px) {

    :root {
        --shift-gap: clamp(200px, 36vh, 380px);
    }
}

@media (max-width: 767px) {

    /* Shorter on a phone, and not only because the screen is smaller. Below
       1200 both sections re-form as stacked columns that already run well
       past a viewport and carry far more of their own breathing room in
       padding, so a desktop-sized runway would read as an empty screen. */
    :root {
        --shift-gap: clamp(130px, 26vh, 260px);
    }
}

.has-shift .services {
    margin-bottom: 0px;
}


/* ==========================================================================
   RECTANGULAR PARTICLE TRANSITION

   Straight port of the reference demo particle rules. Every visible shape is
   an independent rectangle driven by its own --x --y --w --h --r --s --d
   --z. No SVG, no polygon, no clip-path, no single mask shape.

   The field is a CHILD of the OUTGOING section and carries the INCOMING
   colour in --next, exactly as the reference does:

     .reels   (teal)   holds a field of #F5EADF  ->  previews .services
     .journey (violet) holds a field of #FFF9F3  ->  previews .package

   The section overflow:hidden is not an obstacle here, it is part of the
   effect: the rectangles start roughly 70-105vh BELOW their resting place,
   are clipped out of sight by it, and rise up through the section bottom
   edge into view as the page scrolls.
   ========================================================================== */

.particle-field {
    position: absolute;
    z-index: 0;
    left: 0;
    right: 0;
    bottom: 0;
    height: 80vh;
    pointer-events: none;
    overflow: visible;
}

.particle {
    position: absolute;
    display: block;
    left: var(--x);
    top: var(--y);
    width: var(--w);
    height: var(--h);
    background: var(--next);
    transform-origin: center center;
    transform:
        translate3d(-50%, calc((1 - var(--progress, 0)) * 105vh), 0)
        rotate(var(--r));
    opacity: calc(0.08 + (var(--progress, 0) * 0.92));
    will-change: transform, opacity;
    z-index: calc(10 + (var(--z) * 10));
}

.particle::after {
    content: '';
    position: absolute;
    inset: 0;
    background: inherit;
}

@media (max-width: 900px) {

    /* Raised alongside the base rule. The reference keeps the band LARGER on
       small screens (10% -> 20%), and leaving this at 20 while the base went
       to 25 would have inverted that. */
    .particle-field {
        height: 80vh;
    }
}

@media (max-width: 600px) {

    /* The reference thins the bank on small screens rather than redesigning
       it, so the look survives and the paint cost does not. */
    .particle:nth-child(n + 43) {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {

    .particle {
        transform: translate3d(-50%, 0, 0) rotate(var(--r));
    }
}


/* ==========================================================================
   CUSTOM CURSOR

   A single decorative element that trails the pointer: a small outlined
   square, slightly rotated, that leans and swells with movement and settles
   when the pointer stops.

   It does NOT replace the native cursor. The arrow stays exactly as it is,
   so every existing cursor: pointer / grab / grabbing / progress rule below
   still reads correctly and no affordance is lost.

   Structure is two elements on purpose: .cursor carries the translation and
   .cursor__shape carries rotation and scale, so the follow lerp and the
   reaction never write to the same transform and never fight each other.

   Layout-inert by construction: position fixed, no size on the positioned
   element itself, pointer-events none, appended last in <body>. It cannot
   push content, change a section height or intercept a click.
   ========================================================================== */

.cursor {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 100000;
    display: block;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.3s cubic-bezier(0.22, 1, 0.36, 1);
    will-change: transform;
}

/* Revealed only after the first real pointer movement, so it never sits
   parked in a corner on load. */
.cursor.is-live {
    opacity: 1;
}

/* The opening curtain owns the screen until main.js marks the intro done.
   Higher specificity than .is-live, so it wins for those first seconds. */
html:not(.intro-ready) .cursor.is-live {
    opacity: 0;
}

.cursor__shape {
    display: block;
    width: 16px;
    height: 16px;
    margin-top: -8px;
    margin-left: -8px;
    background-color: #062D35;
    border-radius: 4px 1px 3px 2px;
    transition: background-color 0.35s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.3s cubic-bezier(0.22, 1, 0.36, 1);
    will-change: transform;
}

/* Set by js/main.js when the background under the pointer is dark. Both
   colours are the existing palette: --ink for light sections, --cream for
   dark ones. No new brand colour is introduced. */
.cursor--light .cursor__shape {
    background-color: #F5EADF;
}

/* Over a link, button or card: recede rather than compete. The site's own
   hover states are untouched. */
.cursor.is-hot .cursor__shape {
    opacity: 0.45;
}

/* A coarse pointer has nothing to follow. */
@media (hover: none), (pointer: coarse) {

    .cursor {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {

    .cursor {
        display: none;
    }
}


/* ############################################################################
   CONSENT.CSS
   Cookie banner, preferences dialog, embed gate and the footer policy row.
   Appended last so it wins over nothing: every selector here is new and
   namespaced (.cc*, .foot__policy*, .legal__cookiebtn, .legal__colophon), and
   none of them appear anywhere earlier in this stylesheet. Sections 01-12 are
   untouched.

   Layering, against the values already in use:
     100000  custom cursor            (pointer-events: none, stays on top)
      99999  loader + showreel modal  (the intro must not be interrupted)
      99998  body::after pre-cover
      99995  consent dialog           <- here
      99990  consent banner           <- here
        120  lightbox, burger
   The banner therefore sits above all site chrome but below the intro cover,
   which is why consent.js waits for `intro-ready` before showing it.
   ######################################################################## */

.cc {
    --cc-ink: #062D35;
    --cc-paper: #FFF9F3;
    --cc-cream: #F5EADF;
    --cc-rule: rgba(255, 249, 243, 0.26);
    --cc-ease: cubic-bezier(0.22, 1, 0.36, 1);
    --cc-dur: 0.32s;
}

/* ============================================================== BANNER == */
.cc__banner {
    position: fixed;
    z-index: 99990;
    right: clamp(0.75rem, 2vw, 2rem);
    bottom: clamp(0.75rem, 2vw, 2rem);
    left: clamp(0.75rem, 2vw, 2rem);
    max-width: 46rem;
    margin-left: auto;
    box-sizing: border-box;
    padding: clamp(1.15rem, 2.2vw, 1.75rem);
    background: var(--cc-ink);
    color: var(--cc-paper);
    border: 1px solid var(--cc-rule);
    box-shadow: 0 18px 48px rgba(0, 0, 0, 0.34);
    opacity: 0;
    transform: translate3d(0, 16px, 0);
    transition: opacity var(--cc-dur) var(--cc-ease), transform var(--cc-dur) var(--cc-ease);
}

.cc__banner.is-in {
    opacity: 1;
    transform: none;
}

.cc__title {
    margin: 0 0 0.5em;
    font-family: "Bebas Neue", system-ui, sans-serif;
    font-size: clamp(1.25rem, 1.7vw, 1.6rem);
    font-weight: 400;
    letter-spacing: 0.05em;
    line-height: 1;
    color: var(--cc-paper);
}

.cc__text {
    margin: 0 0 1.1em;
    font-family: "Inter", system-ui, sans-serif;
    font-size: clamp(0.78rem, 0.95vw, 0.9rem);
    font-weight: 400;
    line-height: 1.6;
    color: rgba(255, 249, 243, 0.88);
}

.cc__link {
    color: var(--cc-paper);
    text-decoration: underline;
    text-underline-offset: 0.18em;
}

.cc__link:hover,
.cc__link:focus-visible {
    color: #FFFFFF;
}

/* ============================================================= ACTIONS == */
/* Accept, Reject and Preferences share one class and one treatment. Equal
   size, equal weight, equal contrast: the choice has to be genuinely free,
   so no variant, no accent fill and no visual hierarchy is defined here. */
.cc__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
}

.cc__btn {
    flex: 1 1 auto;
    min-width: 9.5rem;
    min-height: 2.75rem;
    box-sizing: border-box;
    padding: 0.75em 1.4em;
    font-family: "Inter", system-ui, sans-serif;
    font-size: clamp(0.72rem, 0.85vw, 0.82rem);
    font-weight: 500;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    text-align: center;
    color: var(--cc-paper);
    background: transparent;
    border: 1px solid var(--cc-paper);
    border-radius: 0;
    cursor: pointer;
    transition: background var(--cc-dur) var(--cc-ease), color var(--cc-dur) var(--cc-ease);
}

.cc__btn:hover,
.cc__btn:focus-visible {
    background: var(--cc-paper);
    color: var(--cc-ink);
}

.cc__btn:focus-visible {
    outline: 2px solid var(--cc-paper);
    outline-offset: 2px;
}

.cc__btn--sm {
    min-width: 0;
    min-height: 2.25rem;
    padding: 0.5em 1em;
    font-size: 0.7rem;
}

/* ============================================================== DIALOG == */
.cc__overlay {
    position: fixed;
    z-index: 99995;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: clamp(0.75rem, 3vw, 2rem);
    background: rgba(6, 45, 53, 0.72);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    opacity: 0;
    transition: opacity var(--cc-dur) var(--cc-ease);
}

.cc__overlay.is-in {
    opacity: 1;
}

.cc__panel {
    display: flex;
    flex-direction: column;
    width: min(38rem, 100%);
    max-height: min(85vh, 44rem);
    box-sizing: border-box;
    padding: clamp(1.25rem, 2.4vw, 2rem);
    background: var(--cc-ink);
    color: var(--cc-paper);
    border: 1px solid var(--cc-rule);
    box-shadow: 0 24px 64px rgba(0, 0, 0, 0.42);
    transform: translate3d(0, 14px, 0);
    transition: transform var(--cc-dur) var(--cc-ease);
}

.cc__overlay.is-in .cc__panel {
    transform: none;
}

.cc__panel-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
}

.cc__close {
    flex: 0 0 auto;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    font-size: 1rem;
    line-height: 1;
    color: var(--cc-paper);
    background: transparent;
    border: 1px solid var(--cc-rule);
    cursor: pointer;
    transition: background var(--cc-dur) var(--cc-ease), color var(--cc-dur) var(--cc-ease);
}

.cc__close:hover,
.cc__close:focus-visible {
    background: var(--cc-paper);
    color: var(--cc-ink);
}

/* -------------------------------------------------------------- ROWS -- */
.cc__list {
    flex: 1 1 auto;
    margin: 0.4rem 0 1.25rem;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    border-top: 1px solid var(--cc-rule);
}

.cc__row {
    border-bottom: 1px solid var(--cc-rule);
}

.cc__row-label {
    display: flex;
    align-items: flex-start;
    gap: 0.9rem;
    padding: 1rem 0.15rem;
    cursor: pointer;
}

/* The native control stays in the accessibility tree and keeps its own
   keyboard behaviour; only its painting is replaced by .cc__switch. */
.cc__check {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

.cc__switch {
    position: relative;
    flex: 0 0 auto;
    display: block;
    width: 2.6rem;
    height: 1.45rem;
    margin-top: 0.1rem;
    background: transparent;
    border: 1px solid var(--cc-paper);
    border-radius: 1rem;
    transition: background var(--cc-dur) var(--cc-ease);
}

.cc__knob {
    position: absolute;
    top: 50%;
    left: 0.2rem;
    width: 0.9rem;
    height: 0.9rem;
    background: var(--cc-paper);
    border-radius: 50%;
    transform: translate3d(0, -50%, 0);
    transition: transform var(--cc-dur) var(--cc-ease), background var(--cc-dur) var(--cc-ease);
}

.cc__check:checked + .cc__switch {
    background: var(--cc-paper);
}

.cc__check:checked + .cc__switch .cc__knob {
    background: var(--cc-ink);
    transform: translate3d(1.05rem, -50%, 0);
}

.cc__check:disabled + .cc__switch {
    opacity: 0.55;
    cursor: not-allowed;
}

.cc__check:focus-visible + .cc__switch {
    outline: 2px solid var(--cc-paper);
    outline-offset: 2px;
}

.cc__row-text {
    display: block;
}

.cc__row-name {
    display: block;
    margin-bottom: 0.2em;
    font-family: "Inter", system-ui, sans-serif;
    font-size: 0.85rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--cc-paper);
}

.cc__row-desc {
    display: block;
    font-family: "Inter", system-ui, sans-serif;
    font-size: 0.78rem;
    font-weight: 400;
    line-height: 1.55;
    color: rgba(255, 249, 243, 0.74);
}

.cc__actions--panel {
    flex: 0 0 auto;
}

/* ========================================================== EMBED GATE == */
/* Sits over the archive card whose film is blocked. Absolute inside
   .work__card, which is already positioned, so nothing reflows. */
.cc__embedgate {
    position: absolute;
    z-index: 8;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.9rem;
    box-sizing: border-box;
    padding: clamp(1rem, 3vw, 1.75rem);
    text-align: center;
    background: rgba(6, 45, 53, 0.9);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
    opacity: 0;
    transition: opacity var(--cc-dur) var(--cc-ease);
}

.cc__embedgate.is-in {
    opacity: 1;
}

.cc__embedgate-text {
    margin: 0;
    max-width: 26rem;
    font-family: "Inter", system-ui, sans-serif;
    font-size: clamp(0.74rem, 1vw, 0.85rem);
    line-height: 1.55;
    color: var(--cc-paper);
}

.cc__embedgate-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    justify-content: center;
}

.cc__embedgate .cc__btn {
    flex: 0 1 auto;
    color: var(--cc-paper);
    border-color: var(--cc-paper);
}

/* ======================================================= FOOTER POLICIES */
/* A band of its own beneath the existing footer row, on the same #062D35
   ground, separated by a hairline and running the full width. The four
   policy links and the consent control sit horizontally across it.

   .foot is switched to a column so this can sit under the band above, and
   .foot__inner is pinned to the height it already had, so the original row
   above is unchanged. */
.foot {
    flex-direction: column;
}

.foot__inner {
    min-height: clamp(4.4rem, 6.53vw, 6.7rem);
}

.foot__policies {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    justify-content: flex-start;
    box-sizing: border-box;
    width: 100%;
    /* Same inset as .foot__inner, so the first policy link starts on the
       logo edge and the band reads as part of the same grid. */
    padding: 0 3.704% 0 8.187%;
    background: #062D35;
    border-top: 1px solid var(--rule-12);
}

.foot__policy {
    position: relative;
    display: flex;
    align-items: center;
    margin: 0;
    padding: 1.5em 2.6em;
    font-family: "Inter", system-ui, sans-serif;
    font-size: var(--fs-foot);
    font-weight: 400;
    line-height: 1;
    letter-spacing: 0.055em;
    text-transform: uppercase;
    text-decoration: none;
    text-align: left;
    white-space: nowrap;
    color: var(--paper-12);
    background: transparent;
    border: 0;
    opacity: 0.78;
    cursor: pointer;
    transition: opacity var(--foot-dur) var(--foot-ease);
}

/* The same vertical dividers the row above uses, for the same reason:
   a left border on every item after the first, stretched by the flex. */
.foot__policy + .foot__policy {
    border-left: 1px solid var(--rule-12);
}

.foot__policies .foot__policy:first-child {
    padding-left: 0;
}

.foot__policy::after {
    content: "";
    position: absolute;
    left: 2.6em;
    right: 2.6em;
    bottom: 1.25em;
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: 0 50%;
    transition: transform var(--foot-dur) var(--foot-ease);
}

.foot__policies .foot__policy:first-child::after {
    left: 0;
}

.foot__policy:hover,
.foot__policy:focus-visible {
    opacity: 1;
}

.foot__policy:hover::after,
.foot__policy:focus-visible::after {
    transform: scaleX(1);
}

.foot__policy:focus-visible {
    outline: 2px solid var(--paper-12);
    outline-offset: -6px;
}

/* Between roughly 1200 and 1500 the five labels are wider than the band.
   They wrap rather than overflow, so the dividers are dropped (a border
   on a wrapped first-in-row item reads as a stray mark) and the padding
   tightens. */
@media (max-width: 1560px) and (min-width: 1200px) {

    .foot__policy {
        padding: 1.35em 1.9em;
    }
}

@media (max-width: 1199px) {

    .foot__policies {
        flex-direction: column;
        align-items: stretch;
        padding: 0 clamp(1.25rem, 4vw, 3rem) clamp(1rem, 4vw, 2rem);
    }

    /* Stacked, the divider becomes a horizontal rule between rows, exactly
       as .foot__nav does at this width. */
    .foot__policy + .foot__policy {
        border-left: 0;
        border-top: 1px solid var(--rule-12);
    }

    .foot__policy,
    .foot__policies .foot__policy:first-child {
        min-height: 2.75rem;
        padding: 1.1em 0;
        white-space: normal;
    }

    .foot__policy::after,
    .foot__policies .foot__policy:first-child::after {
        left: 0;
        right: 0;
        bottom: 0.75em;
    }
}

/* ================================================== LEGAL PAGE HELPERS == */
/* The Cookie Policy names the footer control in running text, so that
   mention is the control itself rather than a dead reference to it. */
.legal__cookiebtn {
    padding: 0;
    font: inherit;
    color: var(--flame-11);
    background: none;
    border: 0;
    text-decoration: underline;
    text-underline-offset: 0.18em;
    cursor: pointer;
}

.legal__cookiebtn:hover,
.legal__cookiebtn:focus-visible {
    color: var(--deep-11);
}

.legal__colophon {
    margin: 3em 0 0;
    padding-top: 1.4em;
    border-top: 1px solid rgba(7, 63, 76, 0.22);
    font-size: 0.8rem;
    letter-spacing: 0.04em;
    color: var(--deep-11);
}

/* ========================================================= SMALL SCREENS */
@media (max-width: 560px) {

    .cc__banner {
        right: 0;
        bottom: 0;
        left: 0;
        max-width: none;
        border-left: 0;
        border-right: 0;
        border-bottom: 0;
    }

    .cc__btn {
        flex: 1 1 100%;
        min-width: 0;
    }
}

/* ======================================================= REDUCED MOTION */
@media (prefers-reduced-motion: reduce) {

    .cc__banner,
    .cc__overlay,
    .cc__panel,
    .cc__embedgate,
    .cc__knob,
    .cc__switch,
    .cc__btn,
    .cc__policy,
    .foot__policy::after {
        transition: none;
    }

    .cc__banner {
        transform: none;
    }
}


/* ========================================================= FOOTER SOCIAL */
/* Two inline SVGs at the head of the .foot__legal row. No icon library and no
   new asset: this follows the project's existing icon convention exactly --
   a 24x24 viewBox with stroke: currentColor, as .pick__cta and
   .archive__arrow already do.

   Why FIRST in that row rather than appended: the :last-child rules above
   zero the email's right padding and inset its underline. An item after the
   email would stop those matching and shift it. Leading the row means every
   existing link keeps its exact position.

   No JavaScript was added. footer.js already tweens .foot__legal as a whole,
   so these arrive with the row they sit in.

   Nothing is declared on .foot, .foot__legal or .foot__link, so the footer's
   height and existing spacing are untouched. */
.foot__social {
    display: inline-flex;
    align-items: center;
    /* Tighter than the 2.2em between text labels: two icons read as one
       group, and the gap to Privacy is supplied by its own padding-left. */
    gap: 1.4em;
}

.foot__social-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Sized in em off the same --fs-foot token the labels beside them use, so
       the icons track the text at every breakpoint instead of needing their
       own type scale. Roughly the cap height of the row's uppercase labels. */
    width: 1.5em;
    height: 1.5em;
    color: var(--paper-12);
    /* The same resting weight as .foot__link, so the icons sit at the same
       visual density as the words beside them. */
    opacity: 0.86;
    transition: opacity var(--foot-dur) var(--foot-ease);
}

.foot__social-link svg {
    display: block;
    width: 100%;
    height: 100%;
}

.foot__social-link:hover,
.foot__social-link:focus-visible {
    opacity: 1;
}

.foot__social-link:focus-visible {
    /* Offset outward, unlike the inset outline on .foot__link: these targets
       are the size of the glyph itself, so an inset ring would cross it. */
    outline: 2px solid var(--paper-12);
    outline-offset: 3px;
}

/* Stacked footer. The row is already a centred flex line here, so the icons
   centre with it; they only need a real touch target. */
@media (max-width: 1199px) {
    .foot__social {
        gap: 0.75rem;
    }

    .foot__social-link {
        box-sizing: border-box;
        width: 2.75rem;
        height: 2.75rem;
        padding: 0.65rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .foot__social-link {
        transition: none;
    }
}

/* ------------------------------------------------ ROOM FOR A SECOND ICON */
/* Added with the TikTok icon. One icon fitted this row at every width; two
   do not at two ranges, where the row ran past the edge of the screen and
   cut off the email address. Nothing below applies outside those two
   ranges, and nothing below changes the icons' size, colour or order.

   1200-1599px: the single-row footer is already full here. The two icons
      sit a little closer together and Privacy / Terms / email a little
      closer to each other, so the email keeps its original right-hand
      position. The hover underline inset moves by the same 0.7em as the
      padding, so each underline keeps its length and place under its word.

   480px and below: the icons take their own centred line above Privacy /
      Terms / email instead of being squeezed onto one line with them. */
@media (min-width: 1200px) and (max-width: 1599px) {
    .foot__social {
        gap: 0.9em;
    }

    .foot__legal .foot__link {
        padding-left: 1.5em;
        padding-right: 1.5em;
    }

    .foot__legal .foot__link::after {
        left: 3.05em;
        right: 3.05em;
    }
}

@media (max-width: 480px) {
    .foot__legal {
        flex-wrap: wrap;
        row-gap: 0.25rem;
    }

    .foot__social {
        flex-basis: 100%;
        justify-content: center;
    }
}
