/* ==========================================================================
   Xpertlab Magical Moments — front-end styles
   Faithful reproduction of the original React "MagicalMoments" section.
   All rules are namespaced under .xmm-magical-moments so they never clash
   with the active theme.
   ========================================================================== */

@font-face {
    font-family: xmm-bebasneue;
    src: url("../assets/fonts/BebasNeue-Regular.ttf") format("truetype");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

.xmm-magical-moments {
    --xmm-primary-red: #e32024;
    box-sizing: border-box;
    width: 100%;
    overflow: hidden;
    border-radius: 25px;   /* rounded corners on the whole section container */
}
.xmm-magical-moments *,
.xmm-magical-moments *::before,
.xmm-magical-moments *::after {
    box-sizing: border-box;
}

/* ── Title + description ─────────────────────────────────────────────── */
.xmm-main-title {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding-left: 2rem;
    padding-right: 2rem;
    margin-top: 1rem;
    margin-bottom: 2.25rem;   /* space after the title + description */
}
.xmm-title {
    font-family: 'Syne', sans-serif;
    font-weight: 700;
    font-size: 40px;
    line-height: 100%;
    letter-spacing: 0;
    text-align: center;
    color: #191919;
    margin: 0 0 18px;
}
.xmm-desc {
    font-family: 'Google Sans', sans-serif;
    font-weight: 400;
    font-size: 22px;
    line-height: 100%;
    letter-spacing: 0;
    text-align: center;
    color: #778aa9;
    margin: 0;
    max-width: 720px;
}

/* ── Desktop video showcase ──────────────────────────────────────────── */
.xmm-videos {
    margin-top: 2rem;
    margin-bottom: 4.5rem;
    padding-left: 2rem;
    padding-right: 2rem;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-wrap: wrap;
    gap: 2rem;
}
.xmm-video {
    display: flex;
    justify-content: center;
}
.xmm-video video {
    width: 500px;
    height: 300px;
    object-fit: cover;
    object-position: 50% 50%;
    border-radius: 12rem;
    background: #000;
    margin-top: -2rem;
}

/* ── Mobile / tablet carousel (hidden on desktop) ────────────────────── */
.xmm-carousel {
    display: none;
    position: relative;
    margin: 2.5rem 0;
    padding-left: 2rem;
    padding-right: 2rem;
    margin-bottom: 2rem;
    overflow: hidden;
}
.xmm-carousel-track {
    display: flex;
    transition: transform 0.5s ease;
    will-change: transform;
}
.xmm-carousel-slide {
    flex: 0 0 100%;
    max-width: 100%;
    padding: 0 0.25rem;
}
.xmm-carousel-slide video {
    width: 100%;
    max-height: 100%;
    object-fit: cover;
    border-radius: 1rem;
    background: #000;
    display: block;
}
.xmm-carousel-btn {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 42px;
    height: 42px;
    border: none;
    border-radius: 50%;
    background: var(--xmm-primary-red);
    color: #fff;
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    z-index: 5;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0.9;
}
.xmm-carousel-btn:hover { opacity: 1; }
.xmm-carousel-prev { left: 2.25rem; }
.xmm-carousel-next { right: 2.25rem; }

/* ── Infinite auto-scrolling gallery ─────────────────────────────────── */
.xmm-gallery-wrap {
    position: relative;
    overflow: hidden;
    z-index: 1;
    padding-left: 2rem;
    padding-right: 2rem;
    height: 1500px;
    /* Pulls the following section up over the gallery's tail. Kept SMALL on
       purpose: the section is overflow:hidden, so this margin also cuts the
       gallery short of its own bottom — and anything cut before the bottom fade
       reaches solid white leaves a visible image edge (see the tablet rule
       below, where a bigger pull is what exposed exactly that). */
    margin-bottom: -2rem;
}
/* Row of vertical columns (built by JS). Each column scrolls on its own so
   columns of unequal height never open a gap at the loop seam. */
.xmm-gallery {
    display: flex;
    align-items: flex-start;
    gap: 18px;
    /* Full visible height so each column's bottom fade lands at the visible
       bottom edge rather than at the bottom of the tall scrolling track. */
    height: 100%;
}
/* Each column's track holds an even number of identical image sets and is
   translated up by exactly half its height, so its loop is always seamless
   regardless of how tall the other columns are. */
.xmm-gallery-col {
    flex: 1 1 0;
    min-width: 0;
    overflow: hidden;
    position: relative;
    height: 100%;         /* clip the scrolling track to the visible area */
    /* Own stacking context so the animating track composites on its own layer —
       prevents the images from flickering. */
    isolation: isolate;
    /* Top corners only. The bottom of a column is meant to DISSOLVE, so a
       rounded bottom clip gave the last visible image a curved, card-like
       outline — read as a stray "bottom border" on the image. Square there, and
       the fade takes the edge away instead. */
    border-radius: 25px 25px 0 0;

    /* ── Bottom dissolve — a MASK, not a white overlay ──
       This replaces the `::after` gradient that used to be painted on top of the
       images. Painting white over them could only ever hide an edge if the
       gradient happened to be fully opaque exactly where that edge landed, which
       depends on the column's height and on how tall the images work out at the
       current column width — so the fade had to be re-tuned per breakpoint and
       still leaked a visible line on tablets.

       A mask removes the pixels instead of covering them, which makes the leak
       impossible by construction: the only hard cut in a column is the one
       `overflow: hidden` makes at its bottom edge, and the mask is fully
       transparent exactly there. Nothing to tune, and it holds at any column
       width or image height.

       It also reclaims the space: the old fade was only half gradient — the
       lower half of every one was solid #ffffff, a blank white slab sitting at
       the bottom of the section. The dissolve now runs the full length of the
       fade, so the same distance shows fading images rather than nothing.

       Any mask-image also forces the rounded clip onto the GPU-composited
       scrolling track (some browsers ignore border-radius on transformed
       children) — the job the old no-op radial-gradient mask was doing.

       ── The curve is what makes it read as SMOKE ──
       A two-stop ramp (opaque → transparent) fades out, but it does not look
       like smoke: alpha falls at a constant rate, and the sudden change in that
       rate where the ramp begins draws a faint horizontal line across the strip
       — the top of the fade becomes visible as a band edge.

       These stops trace an S-curve instead: it leaves solid almost
       imperceptibly, does most of its work through the middle, and approaches
       zero slowly. There is no point along it where the rate of change jumps, so
       there is no edge to see anywhere — the images just thin out into nothing,
       which is the soft, hazy dissolve in the design.

       ── It finishes early, and that is deliberate ──
       The curve reaches zero at 45% of the fade, leaving the bottom of the column
       genuinely empty rather than faintly grey — measured as the cleanest of the
       three shapes tried (see the ramp note below). The staircase reads from where
       each column's fade BEGINS, which is staggered per column, so ending early
       costs the diagonal nothing.

       Positions are written as fractions of --xmm-fade so the whole curve
       stretches or compresses with that one length, and the per-column staircase
       below stays a single-number change. */
    --xmm-fade: 24%;
    /* ── The shape of the ramp ──
       These are the shipped S-curve stops, kept after measuring three shapes
       against the live page. The two alternatives both left an outline showing:

         · spread evenly over the whole fade — no steep band, but the images then
           sit at 20–50% alpha for hundreds of pixels, and a photo at 20% over
           white is still a grey card with crisp rounded corners. The sharp
           outline becomes a long ghost instead.
         · a cubic fall-off, steep only at the top — better in the middle of the
           band, but it never reaches a truly clean zero, so a faint ghost trails
           to the bottom. Measured on the live page at 768px: up to 10/255 of
           darkness left in the bottom of the dissolve, against 3/255 for these
           stops (and 3/255 was flat across every animation phase sampled).

       So the fade's LENGTH is what got fixed for the reported bug, not its shape —
       see the tablet block near the end of this file. The curve leaves solid
       almost imperceptibly, does most of its work through the middle, and is at
       zero well before the column's bottom edge, which is what keeps the last
       stretch genuinely empty rather than faintly grey.

       Positions are fractions of --xmm-fade, so the whole curve stretches with
       that one length and the per-column staircase below stays a single-number
       change. */
    --xmm-smoke: linear-gradient(
        180deg,
        #000                0%,
        #000                calc(100% - var(--xmm-fade)),
        rgba(0, 0, 0, 0.88) calc(100% - var(--xmm-fade) * 0.90),
        rgba(0, 0, 0, 0.72) calc(100% - var(--xmm-fade) * 0.86),
        rgba(0, 0, 0, 0.44) calc(100% - var(--xmm-fade) * 0.74),
        rgba(0, 0, 0, 0.22) calc(100% - var(--xmm-fade) * 0.64),
        rgba(0, 0, 0, 0.07) calc(100% - var(--xmm-fade) * 0.55),
        rgba(0, 0, 0, 0)    calc(100% - var(--xmm-fade) * 0.45),
        rgba(0, 0, 0, 0)    100%
    );
    -webkit-mask-image: var(--xmm-smoke);
    mask-image: var(--xmm-smoke);
}

/* ── The staircase ────────────────────────────────────────────────────────────
   Each column dissolves higher up than the one to its left, so the line where the
   strip gives way to white climbs from the bottom-left to the top-right. Every
   column's OWN fade stays horizontal — the steps are made purely by staggering
   where they start, which is how the design does it too.

   That is also the only shape available: a tilted gradient inside a column would
   leave one bottom corner short of transparent, and that corner is exactly where
   an image edge would then show.

   ── Every column gets its OWN step ──
   This used to end with `nth-child(n + 3)`, holding every column from the third
   onwards at the same 36%. With three columns that was invisible, but the desktop
   grid is four columns at 1920px and five on a wider screen — so columns 3 and 4
   dissolved at exactly the same height (measured: both ending 243px above the
   column's bottom) and that pair read as one STRAIGHT horizontal line across the
   right-hand half. Which is the "not step-wise" being reported.

   Listing each column instead keeps the ramp monotonic, so no two neighbours are
   ever level and every join in the line is a step. The increment is 7 points of
   the column height — at the desktop 1500px box that moves each column's
   dissolve-end 47px above its neighbour's, which reads clearly as a step without
   the last column giving up so much of the collage that the wall looks empty.

   The hold at `n + 6` is a backstop, not a design choice: the JS lands 3–5 columns
   in practice (`floor((avail + gap) / (cw + gap))`), so a sixth would only appear
   on an unusually wide screen, and holding is better there than a 66% fade.

   ── Why plain positions and not `4n + x` ──
   The count is decided at runtime, so a `4n + x` cycle restarted at the shallowest
   value on a 5th column, putting the deepest and shallowest columns side by side
   and breaking the diagonal into a V. Addressing the positions directly is what
   keeps it monotonic at any count. */
.xmm-gallery-col:nth-child(1) { --xmm-fade: 24%; }
.xmm-gallery-col:nth-child(2) { --xmm-fade: 31%; }
.xmm-gallery-col:nth-child(3) { --xmm-fade: 38%; }
.xmm-gallery-col:nth-child(4) { --xmm-fade: 45%; }
.xmm-gallery-col:nth-child(5) { --xmm-fade: 52%; }
.xmm-gallery-col:nth-child(n + 6) { --xmm-fade: 59%; }
.xmm-gallery-col-track {
    display: flex;
    flex-direction: column;
    animation: xmm-scroll 40s linear infinite;
    /* Promote to its own GPU layer so the scroll stays smooth even at high
       speed (no per-frame repaint of the images). */
    will-change: transform;
    transform: translateZ(0);
    backface-visibility: hidden;
}
.xmm-gallery-col-track img {
    display: block;
    width: 100%;
    height: auto;
    margin-bottom: 18px;   /* trailing gap keeps the vertical rhythm across the seam */
    border-radius: 25px;   /* rounded corners on all four sides */
    cursor: default;       /* gallery images are not clickable */
}

/* Fallback layout before JS runs (and if JS is disabled). */
.xmm-gallery > .xmm-gimg {
    width: 330px;
    max-width: 100%;
    height: auto;
    margin: 0 9px 18px;
    border-radius: 25px;
}

@keyframes xmm-scroll {
    0%   { transform: translate3d(0, 0, 0); }
    100% { transform: translate3d(0, -50%, 0); }
}

/* ── The full-width white floor is gone ───────────────────────────────────
   There used to be a `.xmm-gallery-wrap::before` band here: a 26%-tall white
   gradient spanning every column, painted above them, as insurance in case a
   column's own fade was still short of opaque at its last pixel.

   The mask makes that impossible — a column's bottom pixel is now always fully
   transparent — so the band had no failure left to catch. It was also the
   single largest piece of the problem it was meant to solve looking bad: 26% of
   a 1500px gallery is ~390px of solid white sitting under the images, which is
   most of the empty space that used to end the section.

   If a full-width fade is ever wanted back for looks, add it as a mask on
   .xmm-gallery-wrap rather than as a painted white overlay. */

/* The bottom fade is applied per-column (.xmm-gallery-col's mask) to create the
   staircase/diagonal effect. This full-width band is a fallback for the
   pre-JS / no-JS state, before the columns are built. */
.xmm-gallery-wrap:not(:has(.xmm-gallery-col))::after {
    content: "";
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 45%;
    background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.6) 52%, #ffffff 100%);
    pointer-events: none;
    z-index: 10;
}

/* ── Lightbox ────────────────────────────────────────────────────────── */
.xmm-lightbox {
    position: fixed;
    inset: 0;
    z-index: 99999;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.85);
    padding: 2rem;
}
.xmm-lightbox[hidden] { display: none; }
.xmm-lightbox-img {
    max-width: 90vw;
    max-height: 85vh;
    border-radius: 8px;
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
}
.xmm-lightbox-close {
    position: absolute;
    top: 1rem;
    right: 1.25rem;
    width: 44px;
    height: 44px;
    border: none;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.15);
    color: #fff;
    font-size: 28px;
    line-height: 1;
    cursor: pointer;
}
.xmm-lightbox-close:hover { background: rgba(255, 255, 255, 0.3); }

/* ==========================================================================
   Responsive — mirrors the original breakpoints
   ========================================================================== */

/* Title + description scaling */
@media (max-width: 768px) {
    .xmm-title { font-size: 34px; }
    .xmm-desc  { font-size: 18px; }
}
@media (max-width: 480px) {
    .xmm-title { font-size: 28px; }
    .xmm-desc  { font-size: 16px; }
}

/* Video showcase sizes */
@media (max-width: 1079px) {
    .xmm-video video { width: 400px; height: 300px; }
}

/* Swap showcase → carousel at ≤991px (matches the original) */
@media (max-width: 991px) {
    .xmm-videos   { display: none; }
    .xmm-carousel { display: block; }
    .xmm-video video { width: 300px; height: 240px; margin-top: -2.4rem; }
}

/* Column width scaling is handled by the script (data-col-* attributes),
   matching the original calculateWidth: 330 → 230 → 130. The fallback image
   width is adjusted here for the pre-JS state. */
@media (max-width: 1425px) { .xmm-gallery > .xmm-gimg { width: 230px; } }
@media (max-width: 550px)  { .xmm-gallery > .xmm-gimg { width: 110px; } }

/* ── Tablets & large phones (iPad mini 768, and everything down to 501px) ──
   This band used to fall through to the desktop rule — a 1500px (previously
   1800px) box with a negative bottom margin. Two things went wrong there: the
   fade is a PERCENTAGE of the column, so on a tall box it sat far below the
   fold, and the negative margin then clipped the section before that fade had
   reached solid white — leaving the bottom edge of the last image showing.
   Anchoring the box to roughly one screen, with no negative pull, puts the
   fade back where it is visible and lets it finish. */
/* ── …and it stops at 1024, not 991 ──
   991px was the carousel's breakpoint, borrowed for the gallery, and it left the
   992–1024px strip on the DESKTOP rule: a 1500px box with the desktop staircase.
   That strip is iPad Pro in portrait (1024) and iPad Pro 11" (834 → covered, but
   1024 was not), which is where "on iPad the bottom border is visible" was
   measured — the columns come out ~245px wide with images ~440px tall, so an
   image is nearly a third of the fade and its edge sits well inside the
   semi-transparent band. The two concerns are independent, so the gallery's band
   now ends at 1024 and the video carousel keeps its own 991. */
@media (min-width: 501px) and (max-width: 1024px) {
    .xmm-gallery-wrap {
        height: 78vh;
        min-height: 520px;
        max-height: 900px;
        /* ── Reclaiming the dead tail ──
           The longer fade above buys a clean dissolve at the cost of empty space:
           the mask is at zero from 45% of the fade downwards, so with a 62% fade
           the bottom 27.9% of the box — 21.8vh of the 78vh — paints nothing at
           all. Left as `margin-bottom: 0` that is a blank white band between the
           collage and whatever follows.
           Pulling up by 18vh takes back most of it and still stops ~4vh short of
           the last pixel the mask actually draws, so unlike the desktop rule's
           pull (see the note on .xmm-gallery-wrap) this one cannot cut into a
           still-visible image and re-expose an edge. It is expressed in vh so it
           tracks the box, which is also vh. */
        margin-bottom: -18vh;
    }

    /* ── This band no longer needs an outsized fade ──
       It used to run 40/46/52% (against the desktop 24/30/36%) plus a taller
       white floor, and that was pure compensation: the columns are `flex: 1 1 0`,
       so `data-col-md` only decides HOW MANY columns there are — the actual
       width is whatever the row divides evenly. At ~768px that lands 2 columns
       of roughly 300px, so each image is around 200px tall, and a white overlay
       shorter than one image left the bottom image's edge sitting in the
       gradient's still-transparent upper region, reading as a border. The fix
       was to keep growing the overlay until it out-measured an image — which is
       also what put a slab of white across half the visible gallery here.

       The mask has no such dependency: it cuts the pixels at the column's bottom
       edge whatever height they are, so an image can never out-run it — the
       column's last pixel row is at alpha 0 by definition, which is exactly the
       "no border on the next image" guarantee, and it holds at any column width.

       40 / 46 / 52 are the original values this band used, kept as-is so the
       staircase sits where it always did. They are longer than desktop because
       the box is shorter and the images are proportionally much larger here. */
    /* ── These have to out-measure ONE IMAGE, and they did not ──
       Measured on the live page at 768px: 2 columns of 261px, images 407px tall,
       pitch (image + 18px gap) 425px — against a 40% fade of a 900px column, i.e.
       360px. The fade was SHORTER than a single image, so no image could ever
       finish dissolving inside it: every one still had most of its body opaque
       when its bottom edge reached the clear zone, which is precisely the border
       that shows. iPad Pro at 1024 was the same story with 3 columns of 245px and
       440px images.

       62 / 70 / 78% of the 520–900px box clears the tallest pitch measured in this
       band (490px in column 1 at 768px, where a 54% fade still came up 59px short —
       the columns get one image each from a round-robin, so column 1 can draw the
       tallest picture in the set). The steps keep the same 8-point spacing as
       before, so the diagonal is unchanged in shape — only its length grew.

       Measured effect at 768px, sampling the bottom 150px of column 1 across five
       animation phases: the darkest pixel left in that strip goes from 6/255 to
       3/255, and stays 3 at every phase rather than drifting with the scroll. */
    .xmm-gallery-col:nth-child(1) { --xmm-fade: 62%; }
    .xmm-gallery-col:nth-child(2) { --xmm-fade: 70%; }
    /* Third column listed on its own rather than as `n + 3`: this band lands 2 or
       3 columns, and with the old hold a third column was level with a fourth if
       one ever appeared. 78% is the deepest this band can take — beyond that the
       dissolve would out-run the box — so 4+ holds there deliberately. */
    .xmm-gallery-col:nth-child(3) { --xmm-fade: 78%; }
    .xmm-gallery-col:nth-child(n + 4) { --xmm-fade: 78%; }
}

@media (max-width: 500px) {
    /* Anchor the gallery to roughly one screen so the bottom fade lands at the
       visible bottom (a taller box pushes the fade below the fold, which is why
       the bottom image showed through before). Smaller columns (data-col-sm)
       mean several images are still visible. */
    .xmm-gallery-wrap {
        height: 76vh;
        min-height: 440px;
        max-height: 680px;
        /* Same trade as the tablet block: the longer fade leaves the bottom
           45% x 54% = 24% of the box painting nothing, so pull the next section up
           over most of it. -15vh of a 76vh box stops ~3vh short of the last pixel
           the mask actually draws, so it cannot cut into a visible image. */
        margin-bottom: -15vh;
    }

    /* ── Phones need their own staircase after all ──
       This block used to hand phones the DESKTOP steps (24 / 30 / 36) on the
       theory that the mask alone was enough, since a column's bottom row is at
       alpha 0 whatever the fade length is. That reasoning is wrong for the same
       reason it was wrong on tablets: alpha 0 at the very bottom does not help if
       an IMAGE's own bottom edge is still high up in the band, where the mask is
       barely started — that edge, with its 25px rounded corners, is what shows.

       Measured on the live page at iPhone XR (414 x 896): 2 columns of 146px, a
       680px box, so a 24% fade is 163px — against an image pitch (image + 18px
       gap) of 282px. The fade could not contain even 60% of one image, so an edge
       was always exposed. The same arithmetic on the narrower, shorter phones is
       worse in ratio, not better: 375 x 667 lands a 246px pitch in a 507px column,
       i.e. 48.5%.

       54 / 62 / 70% clears the worst of those (48.5%) with room to spare, and
       keeps the 8-point spacing the other breakpoints use so the diagonal has the
       same shape everywhere. */
    .xmm-gallery-col:nth-child(1) { --xmm-fade: 54%; }
    .xmm-gallery-col:nth-child(2) { --xmm-fade: 62%; }
    .xmm-gallery-col:nth-child(3) { --xmm-fade: 70%; }
    /* Phones land 2 columns, occasionally 3; a 4th would have to hold, and 70% is
       already most of a 680px box. */
    .xmm-gallery-col:nth-child(n + 4) { --xmm-fade: 70%; }


    .xmm-videos { margin-top: 1rem; }
}

/* Respect reduced-motion preferences */
@media (prefers-reduced-motion: reduce) {
    .xmm-gallery { animation: none; }
}

/* ==========================================================================
   SCROLL-REVEAL — "Aperture Unveil"  (premium, unique to this section)
   `.xmm-reveal` is server-rendered, so the section starts hidden from the
   very first paint (no flash on a refresh while already in view). The JS
   only ever ADDS `.xmm-in`, so the SAME entrance plays in every state —
   scrolled-into-view or refreshed-in-view. The <noscript> block in the
   markup and the reduced-motion query below cover the no-JS / no-motion
   cases. Only CONTAINER blocks animate — never the gallery's inner
   `.xmm-gallery-col-track`, so its infinite auto-scroll is untouched.

   The signature: the title unmasks upward out of a soft focus, the copy
   rises and sharpens a beat later, the video showcase lifts out of a
   gentle 3D depth (staggered left→right), and the image wall unrolls open
   like a curtain from the top.
   ========================================================================== */

/* Shared transition envelope — slow, weighted, premium easing. */
.xmm-magical-moments.xmm-reveal .xmm-title,
.xmm-magical-moments.xmm-reveal .xmm-desc,
.xmm-magical-moments.xmm-reveal .xmm-video,
.xmm-magical-moments.xmm-reveal .xmm-carousel,
.xmm-magical-moments.xmm-reveal .xmm-gallery-wrap {
    will-change: opacity, transform, filter, clip-path;
    transition:
        opacity   1.05s cubic-bezier(0.16, 1, 0.3, 1),
        transform 1.20s cubic-bezier(0.16, 1, 0.3, 1),
        filter    1.05s cubic-bezier(0.16, 1, 0.3, 1),
        clip-path 1.25s cubic-bezier(0.16, 1, 0.3, 1);
}

/* Title — unmasks upward (clip wipe) while rising out of a soft blur. */
.xmm-magical-moments.xmm-reveal .xmm-title {
    opacity: 0;
    transform: translateY(42px);
    filter: blur(12px);
    clip-path: inset(-0.3em 0 105% 0);
    transition-delay: 0.05s;
}

/* Description — rises and focuses in, just after the title. */
.xmm-magical-moments.xmm-reveal .xmm-desc {
    opacity: 0;
    transform: translateY(28px);
    filter: blur(8px);
    transition-delay: 0.18s;
}

/* Video showcase — each tile lifts up out of a gentle 3D depth. */
.xmm-magical-moments.xmm-reveal .xmm-video {
    opacity: 0;
    transform: perspective(1200px) translateY(48px) rotateX(9deg) scale(0.94);
    transform-origin: 50% 100%;
    filter: blur(3px);
    transition-delay: 0.30s;
}
/* Left → right stagger across the showcase tiles. */
.xmm-magical-moments.xmm-reveal .xmm-video:nth-child(2) { transition-delay: 0.42s; }
.xmm-magical-moments.xmm-reveal .xmm-video:nth-child(3) { transition-delay: 0.54s; }
.xmm-magical-moments.xmm-reveal .xmm-video:nth-child(4) { transition-delay: 0.66s; }
.xmm-magical-moments.xmm-reveal .xmm-video:nth-child(5) { transition-delay: 0.78s; }

/* Mobile / tablet carousel — rises and scales in as one unit. */
.xmm-magical-moments.xmm-reveal .xmm-carousel {
    opacity: 0;
    transform: translateY(40px) scale(0.96);
    filter: blur(4px);
    transition-delay: 0.30s;
}

/* Image wall — unrolls open like a curtain from the top (no blur: keeps the
   scrolling gallery composited cheaply during the reveal). */
.xmm-magical-moments.xmm-reveal .xmm-gallery-wrap {
    opacity: 0;
    transform: translateY(52px);
    clip-path: inset(0 0 100% 0);
    transition-delay: 0.46s;
}

/* Revealed resting state (JS adds `.xmm-in`; later in source → wins). */
.xmm-magical-moments.xmm-in .xmm-title,
.xmm-magical-moments.xmm-in .xmm-desc,
.xmm-magical-moments.xmm-in .xmm-video,
.xmm-magical-moments.xmm-in .xmm-carousel,
.xmm-magical-moments.xmm-in .xmm-gallery-wrap {
    opacity: 1;
    transform: none;
    filter: blur(0);
    clip-path: inset(-0.3em 0 -0.3em 0);
}
.xmm-magical-moments.xmm-in .xmm-gallery-wrap {
    clip-path: inset(0 0 0 0);
}

/* No-JS / reduced-motion: appear in place, no travel / mask / blur. */
@media (prefers-reduced-motion: reduce) {
    .xmm-magical-moments.xmm-reveal .xmm-title,
    .xmm-magical-moments.xmm-reveal .xmm-desc,
    .xmm-magical-moments.xmm-reveal .xmm-video,
    .xmm-magical-moments.xmm-reveal .xmm-carousel,
    .xmm-magical-moments.xmm-reveal .xmm-gallery-wrap {
        opacity: 1 !important;
        transform: none !important;
        filter: none !important;
        clip-path: none !important;
        transition: none !important;
    }
}
