/* NIVROKU home loader — Round 14: the vertical translate unit was wrong.
   --cell (the grid's column width) was being reused as the row-height
   unit for every --ty offset — objects with vertical offsets (logo worst
   of all) overshot past their intended stage position, while horizontal-
   only ones (unico, okko-pulls) happened to land correctly. Split into
   --cell-x (unchanged, still CSS) and --cell-y (measured at runtime from
   the grid's actual rendered row height — no equivalent CSS unit exists
   for it — see loader-footer.js's setCellUnits()). logo-shift and
   obj-settle also now bounce (--loader-ease-bounce) instead of easing
   plainly, matching the entrances. Round 13's failsafe/centering fixes
   and Round 12's entrance-travel overshoot are unchanged below.

   Home cells and offsets are unchanged since Round 10 (the brief's
   coordinates match exactly) — the sequencing is eight strictly sequential
   beats (one object per beat, ~700ms apart, nothing simultaneous but the
   final settle — see loader-footer.js), and the counter is a fully
   independent GSAP tween that no longer gates or is gated by the object
   schedule.

   Scope: .loader-grid's six .loader-obj images, and .home-loader's own
   display/centering/failsafe (both owned here since Round 13).
   .loader-grid's own grid-template/margin/justify-content are Designer-
   owned and already correct per the brief — not redeclared here; only
   --cell-x/--cell-y are added, since this file's offset math needs them. */

:root {
    --loader-ease-out: cubic-bezier(0.23, 1, 0.32, 1);
    --loader-ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* ==========================================================================
   The cover — unchanged, still correct
   ========================================================================== */

html:not(.loader-done) .home-loader {
    display: flex;
}
html:not(.loader-done) {
    overflow: hidden;
}
/* Centering owned here rather than left to Designer's Flex/Align-center
   settings on .home-loader — those weren't taking effect live, whatever
   the canvas showed. display:flex itself only applies while visible (the
   rule above), so these two are inert the rest of the time. */
.home-loader {
    align-items: center;
    justify-content: center;
    transition-property: opacity;
    transition-duration: 320ms;
    transition-timing-function: var(--loader-ease-out);
}
html.loader-done .home-loader {
    opacity: 0;
    pointer-events: none;
}
/* A genuine safety net, not an active cutoff: 10s clears the longest
   legitimate run (8s counter, or the ~5.45s object sequence, whichever is
   longer) with room to spare. The previous 5s was firing mid-settle on
   every run once the object sequence and counter durations grew past 5s —
   it snapped .home-loader to hidden ~100ms into beat 8's settle, before
   it could ever be seen finishing. */
html:not(.loader-done) .home-loader {
    animation: loader-failsafe 0s linear 10s forwards;
}
@keyframes loader-failsafe {
    to {
        opacity: 0;
        visibility: hidden;
        pointer-events: none;
    }
}

/* ==========================================================================
   .loader-grid — offset unit + settle rotation, grid-template left to Designer
   ========================================================================== */

.loader-grid {
    /* Column width — matches the desktop 6-column track (16.6666666667vw)
       exactly; calc() keeps it exact without repeating the 13-digit
       literal. Row height isn't expressible the same way (not specified
       anywhere in the source), so --cell-y is measured at runtime instead
       — see loader-footer.js's setCellUnits(). Kept as two separate
       properties rather than one reused for both axes: a square cell
       would make that harmless, but the grid's cells aren't square here,
       and reusing the column width as the row unit is exactly the bug
       that sent --ty offsets (logo worst of all) overshooting past their
       intended stage position. */
    --cell-x: calc(100vw / 6);
    /* Settle rotation, one variable per row, referenced by --rot-final
       below rather than hardcoded per object — magnitude (15deg) is firm,
       direction isn't confirmed yet, so flipping it is this one line, not
       five. mhp ignores both; it's a fixed exception, not tied to either
       row. */
    --row1-rotation: 15deg;
    --row2-rotation: -15deg;
}
@media (max-width: 767px) {
    .loader-grid {
        --cell-x: 25vw; /* matches the small/tiny 25vw track */
    }
}

/* ==========================================================================
   .loader-obj — six Image elements, .loader-grid's direct children

   Each object's home cell (--gc/--gr, beat 8) is fixed from the start —
   grid-column/row can't transition, so every earlier beat is a
   `transform: translate()` offset from home, held constant from the
   object's own appearance beat until beat 8 resolves it to (0,0).

   Opacity is deliberately NOT part of any @keyframes below — it's a plain
   property flipped by .is-revealed and animated by its own `transition`,
   so it can never be dropped by a later rule swapping animation-name to a
   different (transform-only) keyframe. See the header note above.
   ========================================================================== */

.loader-obj {
    grid-column: var(--gc);
    grid-row: var(--gr);
    opacity: 0;
    transition: opacity 300ms var(--loader-ease-out);
    transform: translate(var(--tx, 0px), var(--ty, 0px)) scale(0.92);
    will-change: transform;
}

/* Bounce-in — shared by every object's own appearance beat (logo alone at
   beat 1; unico/okko-pulls/bag/mhp/spl each their own beat, 3-7). --tx/--ty
   already encode this object's appear-offset; `from` overshoots 1.25x past
   it along the same line the object is already traveling, so the entrance
   reads as arriving from further out rather than growing in place (confirmed
   against loader.mov — the logo rises in from above, not a scale trick).
   easeOutBack, not flattened — the 1.56 control point is what makes it
   visibly overshoot and settle back; that's the point of the whole
   animation, not a garnish. */
@keyframes obj-pop {
    from {
        transform: translate(calc(var(--tx, 0px) * 1.25), calc(var(--ty, 0px) * 1.25)) scale(0.92);
    }
    to {
        transform: translate(var(--tx, 0px), var(--ty, 0px)) scale(1);
    }
}
.loader-obj.is-revealed {
    opacity: 1;
    animation-name: obj-pop;
    animation-duration: 560ms;
    animation-timing-function: var(--loader-ease-bounce);
    animation-fill-mode: both;
}

/* logo only — the brief's one two-step object: appears at its beat-1
   offset, then shifts one column toward home at beat 2, before settling
   with everyone else at beat 8. Literal from/to (not var(--tx)) because
   by the time this animation starts, --tx has already been reassigned to
   the beat-2 value below — reading the variable here would collapse
   `from` and `to` onto the same point. On-screen movement rather than an
   entrance, but still bounces on the same curve as everything else that
   moves (--loader-ease-bounce) rather than easing plainly. animation-name
   swaps away from obj-pop here, but opacity (above) isn't part of either
   keyframe, so it's unaffected. */
@keyframes logo-shift {
    from {
        transform: translate(calc(var(--cell-x) * 3), calc(var(--cell-y) * -1)) scale(1);
    }
    to {
        transform: translate(calc(var(--cell-x) * 2), calc(var(--cell-y) * -1)) scale(1);
    }
}
.loader-grid.is-beat-2 [data-slot="logo"] {
    --tx: calc(var(--cell-x) * 2);
    animation-name: logo-shift;
    animation-duration: 500ms;
    animation-timing-function: var(--loader-ease-bounce);
    animation-fill-mode: both;
}

/* Beat 8 — loader exit and hero rest state in one frame, no DOM swap. All
   six converge on home simultaneously and rotate, on the same bounce
   curve as the entrances rather than a plain ease — everything that
   moves here bounces, not just what appears. Duration must match
   loader-footer.js's SETTLE_MS. */
@keyframes obj-settle {
    from {
        transform: translate(var(--tx, 0px), var(--ty, 0px)) scale(1) rotate(0deg);
    }
    to {
        transform: translate(0, 0) scale(1) rotate(var(--rot-final, 0deg));
    }
}
.loader-grid.is-settled .loader-obj {
    animation-name: obj-settle;
    animation-duration: 650ms;
    animation-timing-function: var(--loader-ease-bounce);
    animation-fill-mode: both;
}

/* ==========================================================================
   Per-object home cells + beat offsets + settle rotation

   --gc/--gr: home cell (beat 8), 1-indexed — matches the brief's table
   directly.
   --tx/--ty: offset from home at this object's own appearance beat, in
   --cell-x/--cell-y units respectively (column width vs. runtime-measured
   row height — see loader-footer.js's setCellUnits()). Held constant
   until is-settled resolves it to (0,0). logo is the exception handled
   above.
   --rot-final: settle rotation. Row 1 objects take --row1-rotation, row 2
   take --row2-rotation (both defined once on .loader-grid above) — except
   mhp, hardcoded 0deg per the brief's named exception, not tied to either
   row variable.
   ========================================================================== */

[data-slot="mhp"] {
    /* home (col1,row1); appears beat 6 at (col1,row2) -> offset (0,+1); never rotates. */
    --gc: 1;
    --gr: 1;
    --ty: calc(var(--cell-y) * 1);
    --rot-final: 0deg;
}
[data-slot="unico"] {
    /* home (col3,row1); appears beat 3 at (col2,row1) -> offset (-1,0). */
    --gc: 3;
    --gr: 1;
    --tx: calc(var(--cell-x) * -1);
    --rot-final: var(--row1-rotation);
}
[data-slot="044 GARDEN bag"] {
    /* home (col5,row1); appears beat 5 at (col5,row2) -> offset (0,+1). */
    --gc: 5;
    --gr: 1;
    --ty: calc(var(--cell-y) * 1);
    --rot-final: var(--row1-rotation);
}
[data-slot="logo"] {
    /* home (col2,row2); appears beat 1 (alone) at (col5,row1) -> offset (+3,-1). */
    --gc: 2;
    --gr: 2;
    --tx: calc(var(--cell-x) * 3);
    --ty: calc(var(--cell-y) * -1);
    --rot-final: var(--row2-rotation);
}
[data-slot="okko-pulls"] {
    /* home (col4,row2); appears beat 4 at (col3,row2) -> offset (-1,0). */
    --gc: 4;
    --gr: 2;
    --tx: calc(var(--cell-x) * -1);
    --rot-final: var(--row2-rotation);
}
[data-slot="spl"] {
    /* home (col6,row2); appears beat 7 at (col6,row1) -> offset (0,-1). */
    --gc: 6;
    --gr: 2;
    --ty: calc(var(--cell-y) * -1);
    --rot-final: var(--row2-rotation);
}

@media (max-width: 767px) {
    [data-slot="mhp"] {
        /* home (col2,row1); appears beat 6 at (col1,row1) -> offset (-1,0). */
        --gc: 2;
        --gr: 1;
        --tx: calc(var(--cell-x) * -1);
        --ty: 0px;
    }
    [data-slot="unico"] {
        /* home (col2,row3); appears beat 3 at (col2,row2) -> offset (0,-1). */
        --gc: 2;
        --gr: 3;
        --tx: 0px;
        --ty: calc(var(--cell-y) * -1);
    }
    [data-slot="044 GARDEN bag"] {
        /* home (col2,row5); appears beat 5 at (col1,row5) -> offset (-1,0). */
        --gc: 2;
        --gr: 5;
        --tx: calc(var(--cell-x) * -1);
        --ty: 0px;
    }
    [data-slot="logo"] {
        /* home (col1,row2); appears beat 1 at (col1,row4) -> offset (0,+2). */
        --gc: 1;
        --gr: 2;
        --tx: 0px;
        --ty: calc(var(--cell-y) * 2);
    }
    [data-slot="okko-pulls"] {
        /* home (col1,row4); appears beat 4 at (col1,row3) -> offset (0,-1). */
        --gc: 1;
        --gr: 4;
        --tx: 0px;
        --ty: calc(var(--cell-y) * -1);
    }
    [data-slot="spl"] {
        /* home (col1,row6); appears beat 7 at (col2,row6) -> offset (+1,0). */
        --gc: 1;
        --gr: 6;
        --tx: calc(var(--cell-x) * 1);
        --ty: 0px;
    }

    /* logo's mobile shift: beat 1 (col1,row4) -> beat 2 (col2,row4), same
       row, one column right — mirrors the desktop keyframe above with
       this breakpoint's offsets. */
    @keyframes logo-shift {
        from {
            transform: translate(0px, calc(var(--cell-y) * 2)) scale(1);
        }
        to {
            transform: translate(calc(var(--cell-x) * 1), calc(var(--cell-y) * 2)) scale(1);
        }
    }
    .loader-grid.is-beat-2 [data-slot="logo"] {
        --tx: calc(var(--cell-x) * 1);
    }
}

/* ==========================================================================
   .loader-counter — unchanged markup, driven by loader-footer.js

   No transition on width: --loader-progress is already a smooth,
   continuously-tweened value (GSAP), so a CSS transition on top would just
   add lag against the number text, which updates instantly and exactly.
   ========================================================================== */

.loader-bar {
    width: calc(var(--loader-progress, 0) * 1%);
}

/* ==========================================================================
   prefers-reduced-motion: fade only, straight to home

   Simpler than it would have been before the opacity fix above: opacity
   is never part of a keyframe, so there's nothing to redefine there —
   just pin every object's transform to its home position and turn off
   the pop/shift/settle animations, leaving the plain opacity fade as the
   only motion.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
    .loader-obj {
        transform: translate(0, 0);
        transition-duration: 220ms;
    }
    .loader-obj.is-revealed,
    .loader-grid.is-beat-2 [data-slot="logo"],
    .loader-grid.is-settled .loader-obj {
        animation: none;
        transform: translate(0, 0);
    }
}
