/* =============================================
   PRELOADER
   Full-screen intro overlay ported from the JFA
   Awards site. Metrics below are quoted from its
   live DOM measured at 1440x900.
   ============================================= */

.preloader {
    /* Reference backdrop is a flat neutral (#f0f0f0) — the brand's near-white
       purple plays the same role. No gradient: the reference has none. */
    --preloader-bg: var(--color-light-purple);
    --preloader-fg: var(--color-dark-purple);
    /* 40px / 41.67px against the reference's 1440 frame. */
    --preloader-space-x: clamp(20px, 2.78vw, 48px);
    --preloader-space-y: clamp(34px, 2.89vw, 50px);

    position: fixed;
    inset: 0;
    z-index: 9500;
    background: var(--preloader-bg);
    color: var(--preloader-fg);
    font-size: max(0.93vw, 12px);
    overflow: hidden;
    will-change: transform;
}

/* Scroll stays locked until the overlay hands the page over. Lenis is
   stopped separately in JS — this only covers the native scroller. */
body.is-preloading {
    overflow: hidden;
}

/* The SVG only exists to host <clipPath>; it must stay in the render tree
   for clip-path: url() to resolve, so it is collapsed rather than hidden. */
.preloader__defs {
    position: absolute;
    width: 0;
    height: 0;
    pointer-events: none;
}

/* ---- The blob layer ----
   One full-viewport panel clipped to the blob. Placement and growth are
   driven entirely by the transform on the clip path itself (see the seed
   value in template-parts/preloader.php), so this element never moves. */
.preloader__morph {
    position: absolute;
    inset: 0;
    z-index: 2;
    clip-path: url(#preloader-blob);
}

/* Reference fills this with a video on a flat pale ground.

   The tint stays underneath the artwork rather than over it: it is what the
   blob is made of for the few hundred milliseconds before a megabyte of
   illustration has decoded, and what it falls back to where AVIF is not
   supported.

   Scaled well past the viewport because the reference's video fills its frame
   edge to edge and this illustration does not — it is a centred scene on empty
   ground, so at plain `cover` the blob spends half its travel over blank tint.
   Blowing it up pushes artwork out to where the corners are. The script walks
   the zoom back to 1 across the reveal; the literal below is only the value it
   starts from, for the frames before the script has run. */
.preloader__morph-media {
    position: absolute;
    inset: 0;
    /* Isolated so the blend below multiplies against this tint and not against
       the page behind the overlay. */
    isolation: isolate;
    transform: scale(var(--preloader-media-scale, 1.55));
}

/* The tint itself, on its own layer so the script can retire it across the
   reveal (--preloader-tint) without touching the artwork multiplying over it.
   First in paint order inside the isolated group, which is what makes it the
   backdrop that blend resolves against. */
.preloader__morph-media::before {
    content: '';
    position: absolute;
    /* The media layer is scaled up, so its own edges sit outside the viewport;
       the tint has to reach them or the blob shows a hard seam mid-reveal. */
    inset: 0;
    background: var(--color-accent-02);
    opacity: var(--preloader-tint, 1);
}

/* Never sized to the blob — the blob slides over a fixed image, which is the
   whole point of the effect: every position shows a different part of the
   scene. `cover` against the viewport keeps that framing stable as the
   window resizes.

   Multiplied rather than laid on flat. The reference's fill is a video with
   body in every frame, so its blob always reads as a shape; this illustration
   has large near-white passages that vanish straight into the backdrop, and
   the blob comes apart into floating fragments wherever it lands on one.
   Multiplying floors the whole layer at the tint, so the silhouette holds
   everywhere while the artwork still comes through. */
.preloader__morph-img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    mix-blend-mode: multiply;
}

/* ---- The borrowed hero scene ----
   Host for the live .hero-scene section while the intro owns it. The section is
   already exactly one screen tall in the page (see _section-hero_scene.css), so
   sitting it at inset 0 here costs it nothing — which is the whole reason the
   hand-back can be a straight slide with no scaling. */
.preloader__scene {
    position: absolute;
    inset: 0;
    z-index: 1;
}

.preloader__scene .hero-scene {
    /* The page grants the header its height when the scene opens a document.
       Nothing sits above it here. */
    --hero-scene-header: 0px;

    height: 100%;
}

/* The still and the scene are alternatives, not layers. */
.preloader--has-scene .preloader__morph-img {
    display: none;
}

/* Behind a scene the tint is scaffolding, not fill: it holds the blob's
   silhouette while the blob is small and the scene is mostly transparent, then
   gets out of the way before the hand-over. The script drives the fade (see
   drawBlob); the fallback keeps it up for any frame that runs before it. */
.preloader--has-scene .preloader__morph-media {
    opacity: var(--preloader-tint, 1);
}

/* ---- Hand-over ----
   Applied by the script, never by default: a page whose intro never runs has
   to render its heading as normal. */
.home-hero.is-intro-pending .home-hero__title,
.home-hero.is-intro-pending .home-hero__desc {
    opacity: 0;
}

/* The scene travels up over the heading on its way down into its slot, so for
   the length of that move it has to paint above it. */
.hero-scene.is-intro-travelling {
    position: relative;
    z-index: 2;
}

/* ---- Counter + label ----
   Each sits in an overflow-clipped box so its inner div can be slid in and
   out from behind the edge — the reference's MaskElement. */
.preloader__mask {
    position: absolute;
    z-index: 3;
    overflow: hidden;
    pointer-events: none;
}

.preloader__mask-inner {
    transform: translateY(100%);
    will-change: transform;
}

/* Reference: 177.5px (12.33vw), weight 400, line-height 0.849,
   letter-spacing -0.02em, 40px from the left, 41.67px from the bottom. */
.preloader__progress {
    bottom: var(--preloader-space-y);
    left: var(--preloader-space-x);
    font-family: var(--font-display);
    font-size: clamp(72px, 12.33vw, 213px);
    font-weight: var(--fw-regular);
    line-height: 0.849;
    letter-spacing: -0.02em;
    /* Digits must not reflow the layout as the count climbs. */
    font-variant-numeric: tabular-nums;
}

/* Reference: 13.33px, weight 600, letter-spacing normal, uppercase,
   41.67px from the bottom, 40px from the right. */
.preloader__label {
    right: var(--preloader-space-x);
    bottom: var(--preloader-space-y);
    font-family: var(--font-body);
    font-size: 1em;
    font-weight: var(--fw-semibold);
    letter-spacing: normal;
    line-height: 1.2;
    text-transform: uppercase;
}

/* ---- Mobile: counter and label ride the top edge as a single row ---- */
@media only screen and (max-width: 1024px) {
    .preloader {
        --preloader-space-x: 20px;
        --preloader-space-y: 22px;

        display: flex;
        flex-wrap: wrap;
        align-content: flex-start;
        justify-content: space-between;
        padding: max(2.2vh, 22px) var(--preloader-space-x);
        font-size: 12px;
    }

    .preloader__progress,
    .preloader__label {
        position: static;
    }

    .preloader__progress {
        font-family: var(--font-body);
        font-size: 12px;
        font-weight: var(--fw-semibold);
        line-height: 1.2;
        letter-spacing: normal;
    }
}

/* ---- Reduced motion: no intro at all ----
   JS also bails early; this keeps the overlay off screen even on the first
   frame, before the script has had a chance to run. */
@media (prefers-reduced-motion: reduce) {
    .preloader {
        display: none;
    }
}
