/* ==========================================================================
   SACHDEVA GROUP — SCROLL FX
   The CSS half of js/scroll-drift.js and js/scroll-fx.js for every page
   except index.html
   ==========================================================================
   Load order : LAST — after css/page-fx.css and any page-specific *-fx.css.
                index.html does not load this; css/motion.css does its job.
   Engines    : GSAP 3.13 + ScrollTrigger + SplitText via js/scroll-drift.js
                and js/scroll-fx.js, which write these custom properties and
                no layout property at all:

     --sfx-gy       px,    on a [data-drift] section    ground layer travel
     --sfx-iy       -1..1, on a [data-drift-img] box    image travel
     --sfx-io       1.14..1, on a [data-sfx-open] box   the "open" settle
     --sfx-curtain  0..100%, on <body>                  the load curtain

   Every rule below resolves to its resting value when the property is
   missing, so a page without the scripts — blocked CDN, old browser, motion
   switched off — renders exactly as it did before this file existed. The
   only two things hidden ahead of time are the curtain and the line-reveal
   targets, and both sit behind gates js/motion-policy.js pulls on a timer.

   WHY THE HOVER ZOOM IS A REGISTERED PROPERTY
     The photograph's transform is now written every frame from --sfx-iy. A
     `transition: transform` on the same element would lag the scrub by its
     own duration, and dropping the transition would snap the hover zoom. So
     the scale rides its own @property (--sfx-iz), which is the one thing that
     transitions; the translate reads live. Browsers without @property still
     zoom on hover — they just do it without the ease.
   ========================================================================== */

/* ==========================================================================
   1. TOKENS
   ========================================================================== */

:root {
    /* how far a photograph slides inside its box, as a share of its height.
       Paired with the scale below: 1.24 bleeds 12% top and bottom, so a 10%
       slide leaves 2% of guard before an edge could show. The reference runs
       ±20% on a 120%-tall image; ±10% is the most a box that also drifts
       ±26px against the page can carry before the two read as one move. */
    --sfx-img-amp: 10%;
    --sfx-img-scale: 1.24;

    /* the ground layers are stretched this far past the section top and
       bottom; js/scroll-drift.js moves them +/-34px inside that */
    --sfx-ground-over: 64px;
}

/* js/motion-policy.js sets this when it is overriding an OS reduce-motion
   request: motion runs, at smaller travel — reduced, not removed */
html.sg-forced {
    --sfx-img-amp: 5%;
    --sfx-img-scale: 1.14;
}

@property --sfx-iz {
    syntax: '<number>';
    inherits: false;
    initial-value: 1;
}

/* ==========================================================================
   2. GROUND — the section's own material moves under the copy
   .mrn-soft / .mrn-dark paint an engineering grid on ::before and .mrn-dark a
   corner bloom on ::after (css/index-theme.css section 2). .env-commit draws
   its own grid. All three get overscanned and translated by --sfx-gy; the
   section's overflow:hidden (css/marine-pages.css) clips the overhang.
   ========================================================================== */

.mrnp-section[data-drift].mrn-soft::before,
.mrnp-section[data-drift].mrn-dark::before,
.mrnp-section[data-drift].mrn-dark::after,
.mrnp-section[data-drift].env-commit::before {
    top: calc(var(--sfx-ground-over) * -1);
    bottom: calc(var(--sfx-ground-over) * -1);
    transform: translate3d(0, var(--sfx-gy, 0px), 0);
}

/* .mrn-light had no ground at all, so a section on white read as flat next
   to the two that move. A pair of blooms — cyan high right, gold low left,
   both well under the alpha the dark band uses — gives it the same depth
   without turning the white into a tint. .env-commit already paints its own
   wash and is left alone. */
.mrnp-section[data-drift].mrn-light:not(.env-commit)::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: calc(var(--sfx-ground-over) * -1);
    bottom: calc(var(--sfx-ground-over) * -1);
    pointer-events: none;
    z-index: 0;
    background:
        radial-gradient(720px 420px at 90% 8%, rgba(53, 198, 244, .11), transparent 68%),
        radial-gradient(560px 360px at 6% 96%, rgba(255, 173, 24, .08), transparent 70%);
    transform: translate3d(0, var(--sfx-gy, 0px), 0);
}

/* ==========================================================================
   3. IMAGES — the photograph slides inside its frame
   The frame carries the scrubbed "open" (js/scroll-fx.js) and the tilt sits
   on .mrnp-split__media, so the <img> is the free layer on every one of
   these components. Its scale is three factors: the bleed for the slide
   (--sfx-iz at rest), the hover zoom (--sfx-iz on :hover) and the open
   settle (--sfx-io, 1.14 → 1 as the box scrolls in).
   ========================================================================== */

/* --- 3.1 the split-section frame ------------------------------------------ */

.mrnp-split__frame[data-drift-img] img {
    --sfx-iz: var(--sfx-img-scale);
    transform: translate3d(0, calc(var(--sfx-iy, 0) * var(--sfx-img-amp)), 0) scale(calc(var(--sfx-iz) * var(--sfx-io, 1)));
    transition: filter .8s var(--e-out), --sfx-iz 1.1s var(--e-out);
    will-change: transform;
    backface-visibility: hidden;
}

.mrnp-split__media:hover .mrnp-split__frame[data-drift-img] img {
    --sfx-iz: 1.26;
    transform: translate3d(0, calc(var(--sfx-iy, 0) * var(--sfx-img-amp)), 0) scale(calc(var(--sfx-iz) * var(--sfx-io, 1)));
}

/* --- 3.2 gallery tiles ----------------------------------------------------- */
/* css/gallery-fx.css sets these at (1,2,1) under #yard-details, so the same
   id is needed here to be heard — this file loads after it, so equal
   specificity is enough. */

#yard-details .mrnp-gal__item[data-drift-img] img {
    --sfx-iz: 1.16;
    transform: translate3d(0, calc(var(--sfx-iy, 0) * var(--sfx-img-amp)), 0) scale(calc(var(--sfx-iz) * var(--sfx-io, 1)));
    transition: filter .7s var(--e-out), --sfx-iz 1s var(--e-out);
    will-change: transform;
    backface-visibility: hidden;
}

#yard-details .mrnp-gal__item[data-drift-img]:hover img,
#yard-details .mrnp-gal__item[data-drift-img]:focus-visible img {
    --sfx-iz: 1.24;
    transform: translate3d(0, calc(var(--sfx-iy, 0) * var(--sfx-img-amp)), 0) scale(calc(var(--sfx-iz) * var(--sfx-io, 1)));
}

/* --- 3.3 about_us.html ----------------------------------------------------- */
/* css/about-fx.css keys its photo rules on #profile / #units */

#profile .abt-frame[data-drift-img] img {
    --sfx-iz: var(--sfx-img-scale);
    transform: translate3d(0, calc(var(--sfx-iy, 0) * var(--sfx-img-amp)), 0) scale(calc(var(--sfx-iz) * var(--sfx-io, 1)));
    transition: filter .8s var(--ab-ease), --sfx-iz 1.2s var(--ab-ease);
    will-change: transform;
    backface-visibility: hidden;
}

#profile .abt-split__media:hover .abt-frame[data-drift-img] img {
    --sfx-iz: 1.25;
    transform: translate3d(0, calc(var(--sfx-iy, 0) * var(--sfx-img-amp)), 0) scale(calc(var(--sfx-iz) * var(--sfx-io, 1)));
}

#units .abt-unit__media[data-drift-img] img {
    --sfx-iz: 1.2;
    transform: translate3d(0, calc(var(--sfx-iy, 0) * var(--sfx-img-amp)), 0) scale(calc(var(--sfx-iz) * var(--sfx-io, 1)));
    transition: filter .9s var(--ab-ease), --sfx-iz 1.3s var(--ab-ease);
    will-change: transform;
    backface-visibility: hidden;
}

#units .abt-unit:hover .abt-unit__media[data-drift-img] img {
    --sfx-iz: 1.3;
    transform: translate3d(0, calc(var(--sfx-iy, 0) * var(--sfx-img-amp)), 0) scale(calc(var(--sfx-iz) * var(--sfx-io, 1)));
}

/* --- 3.4 certificate cards ------------------------------------------------- */
/* they open (clip) but do not slide — a document should sit still */
.mrnp-certcard[data-sfx-open] img {
    transform: scale(var(--sfx-io, 1));
    transition: filter .6s var(--e-out);
}

.mrnp-certcard[data-sfx-open]:hover img {
    transform: scale(calc(var(--sfx-io, 1) * 1.03));
}

/* ==========================================================================
   4. ENTRANCES — the index-theme.css section 9 vocabulary, calmed
   Every rule here matches only while the element still carries its
   data-sg-* attribute, which js/index-motion.js strips the moment the
   entrance settles — so nothing below can outlive the entrance or fight a
   later hover.
   ========================================================================== */

/* 4.0 One move for everything that is not a title or a photograph.
   js/page-fx.js hands out seven directions (left, right, rise, tilt, sink,
   zoom, up) across cards, lists and facts. The reference site has two moves
   in total — lines that rise and boxes that open — and reads as calm
   because of it. Next to the masked titles, a card sliding in from the side
   or tipping in on a 3D hinge now reads as noise, so every direction folds
   into one short rise; zoom keeps a whisper of scale for the tiles and
   stamps that still use it. The stagger and timing are untouched. */
html.sg-motion [data-sg-in],
html.sg-motion [data-sg-in="left"],
html.sg-motion [data-sg-in="right"],
html.sg-motion [data-sg-in="rise"],
html.sg-motion [data-sg-in="tilt"],
html.sg-motion [data-sg-in="sink"] {
    --sg-from: translate3d(0, 28px, 0);
}

html.sg-motion [data-sg-in="zoom"] {
    --sg-from: translate3d(0, 18px, 0) scale(.985);
}

/* 4.1 Media that is not taken over by the scrubbed open (flip cards, stamps)
   still arrives from soft focus. Kept off the text entrances. */
html.sg-motion [data-sg-in="clip"],
html.sg-motion [data-sg-in="zoom"] {
    filter: blur(12px);
    transition-property: opacity, transform, clip-path, filter !important;
    transition-duration: .8s, 1s, 1s, 1.15s !important;
}

html.sg-motion [data-sg-in="clip"].sg-in,
html.sg-motion [data-sg-in="zoom"].sg-in {
    filter: blur(0);
}

/* 4.2 The eyebrow's rule draws itself once the label has landed. */
html.sg-motion .mrn-eyebrow[data-sg-in]::before {
    transform: scaleX(0);
    transform-origin: 0 50%;
    transition: transform .9s var(--e-out) calc(var(--sg-d, 0ms) + 260ms);
}

html.sg-motion .mrn-eyebrow[data-sg-in].sg-in::before {
    transform: scaleX(1);
}

/* calm mode (reduced motion with JS alive) fades only — see index-theme.css */
html.sg-calm.sg-motion [data-sg-in] {
    filter: none !important;
}

html.sg-calm.sg-motion .mrn-eyebrow[data-sg-in]::before {
    transform: none !important;
    transition: none !important;
}

/* ==========================================================================
   5. LINES — the masked line reveal (js/scroll-fx.js section 2)
   SplitText wraps each visual line in a mask it sets to overflow:clip; the
   line rises from 110% of its own height inside it. The mask gets a little
   padding below and the same negative margin, so descenders are not shaved
   off by the clip on tight display line-heights.
   ========================================================================== */

/* armed by the pre-pass only once GSAP + SplitText are present; cleared per
   element the moment its lines are split, by the failsafe otherwise */
html.sfx-lines [data-sfx-lines]:not(.sfx-split) {
    visibility: hidden;
}

.sfx-mask {
    padding-bottom: .14em;
    margin-bottom: -.14em;
}

/* the hero sub-heading used to fade itself in from opacity:0 (marine-pages.css)
   — its lines carry the entrance now, so the box must start visible */
.mrnp-hero__sub[data-sfx-lines] {
    opacity: 1;
    animation: none;
}

/* when a curtain was drawn for this page view (html.sfx-curtain, set by
   js/motion-policy.js and never removed) the crumb and rule wait for it to
   clear; on a plain click between our own pages there is no curtain and
   they keep marine-pages.css's own timing, so the hero still arrives
   top-down */
html.sfx-curtain .mrnp-crumb {
    animation-delay: .6s;
}

html.sfx-curtain .mrnp-hero__rule {
    animation-delay: 1.35s;
}

/* ==========================================================================
   6. CURTAIN — the load intro (js/scroll-fx.js section 4)
   Drawn from the very first frame on pages whose <html> carries
   data-sfx-intro, because js/motion-policy.js sets html.sfx-arm in <head>.
   The bottom inset is --sfx-curtain on <body>, tweened 0 → 100% so the
   sheet lifts upward and the hero photograph settles in behind it.
   ========================================================================== */

html.sfx-arm body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 2147483000;
    pointer-events: none;
    background: linear-gradient(160deg, #071a2e 0%, #0a2340 100%);
    -webkit-clip-path: inset(0 0 var(--sfx-curtain, 0%) 0);
    clip-path: inset(0 0 var(--sfx-curtain, 0%) 0);
}

/* ==========================================================================
   7. FOOTER + HEADER (js/scroll-fx.js sections 5 and 6)
   ========================================================================== */

/* the footer starts 30% of its height UNDER the yard band, which paints
   white at z-index 2 — so the footer must sit below it in the stack */
.sfx-footer {
    z-index: 1;
}

/* js/main.js makes the bar sticky with animate.css's fadeInDown, whose
   fill-mode pins transform for as long as .animated is on the element.
   js/scroll-fx.js drops that class the first time the bar has to hide and
   marks the bar .sfx-head; from then on both moves are one transition on
   transform — up and out of the way, back down the moment you scroll up. */
.header.sticky_menu.sfx-head {
    animation: none !important;
    transition: transform .55s var(--e-out, cubic-bezier(.16, 1, .3, 1));
}

.header.sticky_menu.sfx-head-hide {
    transform: translate3d(0, -110%, 0);
}

/* ==========================================================================
   8. RESPONSIVE
   The section drift is desktop-only in the JS. The image slide runs on every
   width, at a smaller amplitude and bleed — a phone screen is short, so the
   same 7% would cross the frame in one thumb gesture.
   ========================================================================== */

@media (max-width: 860px) {

    :root {
        --sfx-img-amp: 5%;
        --sfx-img-scale: 1.14;
    }

    #yard-details .mrnp-gal__item[data-drift-img] img {
        --sfx-iz: 1.1;
    }

    #units .abt-unit__media[data-drift-img] img {
        --sfx-iz: 1.14;
    }

    /* a 12px blur on six tiles at once is the one thing here a mid-range
       phone would drop frames on */
    html.sg-motion [data-sg-in="clip"],
    html.sg-motion [data-sg-in="zoom"] {
        filter: none;
    }
}

/* ==========================================================================
   9. REDUCED MOTION
   Only reached when js/motion-policy.js is in "auto" or "off" mode (the
   default "on" rewrites these blocks away). The scripts then return at their
   own guards, so nothing below is ever written; these rules exist for the
   case where the preference flips while the page is open and a value is
   already parked on an element.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {

    [data-drift-img] img,
    .mrnp-certcard[data-sfx-open] img {
        transform: none !important;
        transition: filter .7s !important;
        will-change: auto !important;
    }

    .mrnp-section[data-drift]::before,
    .mrnp-section[data-drift]::after {
        transform: none !important;
    }

    [data-sfx-open] {
        clip-path: none !important;
    }

    html.sfx-lines [data-sfx-lines] {
        visibility: visible !important;
    }

    .sfx-line {
        transform: none !important;
    }

    html.sfx-arm body::before {
        display: none;
    }

    .sfx-footer,
    .header.sticky_menu.sfx-head,
    .header.sticky_menu.sfx-head-hide {
        transform: none !important;
        transition: none !important;
    }

    html.sg-motion [data-sg-in] {
        filter: none !important;
    }

    .mrn-eyebrow::before {
        transform: none !important;
        transition: none !important;
    }
}
