/*
 * MVLS custom styles — pieces that don't map cleanly to Tailwind utilities
 * (layered hero scrims, keyframed sequences).
 */

/* Hero photo scrims — horizontal fade for copy legibility, vertical fade into the page */
.hero-scrim-x {
    background: linear-gradient(
        90deg,
        rgba(4, 18, 47, 0.92) 0%,
        rgba(4, 18, 47, 0.72) 38%,
        rgba(4, 18, 47, 0.15) 72%,
        transparent 100%
    );
}

.hero-scrim-y {
    background: linear-gradient(0deg, rgba(4, 18, 47, 0.7) 0%, transparent 40%);
}

/* ------------------------------------------------------- lead hero (home)
   A statement, an image and one button. The panel re-keys on every slide
   change, so its entrance animation is the transition — no crossfade wrapper
   to keep in step with the timer. */

.hero-lead {
    /* The viewport, less whatever chrome sits above it. --site-chrome is set
       on <main> by the layout: 68px for the navbar alone, 104px once the
       announcement bar is above it. The fallback is the navbar on its own, so
       anything rendering this outside that layout is unchanged. */
    min-height: calc(100svh - var(--site-chrome, 68px));
}

.hero-lead-stack {
    /* Eurostile Extended is set at size-adjust 62%, so the line box can sit
       far tighter than the nominal font size suggests */
    font-size: clamp(2.2rem, 6.4vw, 6rem);
    /* Loose enough that wrapped lines on a phone stay apart — below the small
       breakpoint a line is allowed to break, and 0.55 would collide them. */
    line-height: 0.78;
    letter-spacing: -0.015em;
}

/* Two lines is the design; a third from a wrap would push the button off the
   fold. Below the small breakpoint there is no room to promise that, so the
   lines are allowed to break there. */
@media (min-width: 640px) {
    .hero-lead-stack {
        /* The drop headline's ratio. Eurostile carries a lot of air above the
           caps, so the two lines only read as one stacked block this far in. */
        line-height: 0.55;
    }

    .hero-lead-stack > span {
        white-space: nowrap;
    }
}

/* ------------------------------------------------------------- drop (home)
   Set fluidly for the same reason the lead hero above it is: at a fixed 72px
   the second line ("Confidence") wanted 415px inside a 327px column on a
   375px phone, and the section's overflow-hidden cropped it mid-word. The
   ceiling is the old lg size, so nothing changes from the small breakpoint up. */
.drop-stack {
    font-size: clamp(2.9rem, 14vw, 7.25rem);
    /* Loose below sm, where a longer headline than the two words shipped today
       is allowed to wrap; 0.55 would collide the lines it wrapped into. */
    line-height: 0.82;
    /* An admin can type a word longer than the column is wide. Better a broken
       word than one that runs off the screen. */
    overflow-wrap: break-word;
}

@media (min-width: 640px) {
    .drop-stack {
        /* The ratio the inline line-height encoded: 64px against the 116px
           desktop size. Eurostile carries a lot of air above the caps, so the
           two lines only read as one stacked block this far in. */
        line-height: 0.55;
    }
}

/* Each child arrives just behind the one above it, so the eye is walked down to
   the button rather than shown everything at once. The class rides the active
   slide, so adding it replays the stagger on every change.

   Transform only, never opacity: the headline is the page's LCP element, and
   Vue adds this class on mount — so a from{opacity:0} here left the largest
   text painted-but-invisible until the animation ran after hydration, pushing
   LCP ~2s past the point the text was actually ready. The whole panel still
   cross-fades between slides via its inline :style opacity binding. */
.hero-lead-panel > * {
    animation: hero-lead-rise 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
}

.hero-lead-panel > :nth-child(2) {
    animation-delay: 0.08s;
}
.hero-lead-panel > :nth-child(3) {
    animation-delay: 0.16s;
}
.hero-lead-panel > :nth-child(4) {
    animation-delay: 0.22s;
}
.hero-lead-panel > :nth-child(5) {
    animation-delay: 0.3s;
}

@keyframes hero-lead-rise {
    from {
        transform: translateY(22px);
    }
}

/* Only transform is animated here: opacity belongs to the cross-fade between
   slides, and an entrance animation holding it with fill: both would pin a
   layer visible when the slide changes. The drift is slow enough to read as
   depth rather than movement. */
.hero-lead-art img {
    animation: hero-lead-drift 30s ease-in-out infinite alternate;
}

@keyframes hero-lead-art-in {
    from {
        opacity: 0;
    }
}

@keyframes hero-lead-drift {
    from {
        transform: scale(1.02);
    }
    to {
        transform: scale(1.08) translate(-1.5%, -1%);
    }
}

.hero-lead-cue {
    animation: hero-lead-art-in 0.8s ease both;
    animation-delay: 1s;
}

.hero-lead-bob {
    animation: hero-lead-bob 2.4s ease-in-out infinite;
}

@keyframes hero-lead-bob {
    0%,
    100% {
        transform: translateY(-2px);
    }
    50% {
        transform: translateY(3px);
    }
}

/* -------------------------------------------------- reviews carousel (home)
   The bar is the auto-advance clock: JS listens for its animationend and
   pauses it on hover/focus, so the timing has a single source of truth. */

.testimonial-clock {
    animation: testimonial-tick 7s linear forwards;
}

@keyframes testimonial-tick {
    from {
        transform: scaleX(0);
    }
    to {
        transform: scaleX(1);
    }
}

@media (prefers-reduced-motion: reduce) {
    .hero-lead-panel > *,
    .hero-lead-cue,
    .hero-lead-bob {
        animation: none;
    }
    /* Art holds still, slightly in so no edge shows */
    .hero-lead-art img {
        animation: none;
        transform: scale(1.04);
    }
    /* No animationend fires, so the carousel stays on manual controls */
    .testimonial-clock {
        animation: none;
        transform: scaleX(1);
    }
}

/* Hide Vue-managed elements until the app mounts */
[v-cloak] {
    display: none !important;
}

/* ---------------------------------------------------------------- motion */

/* Loading shimmer sweep (skeletons, image placeholders) */
.shimmer {
    position: relative;
    overflow: hidden;
    background-color: rgba(255, 255, 255, 0.04);
}
.shimmer::after {
    content: '';
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background: linear-gradient(90deg, transparent, rgba(127, 176, 255, 0.09), transparent);
    animation: shimmer-sweep 1.6s infinite;
}
@keyframes shimmer-sweep {
    100% {
        transform: translateX(100%);
    }
}

/* Product images fade in over their shimmer once loaded */
.img-fade {
    opacity: 0;
    transition: opacity 0.45s ease, transform 0.45s ease;
}
.img-fade.is-loaded {
    opacity: 1;
}

/* Card/section entrance; [data-stagger] children get --i from app.js */
.fade-up {
    animation: fade-up 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
    animation-delay: calc(var(--i, 0) * 50ms);
}
@keyframes fade-up {
    from {
        opacity: 0;
        transform: translateY(14px);
    }
    to {
        opacity: 1;
        transform: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .fade-up,
    .shimmer::after {
        animation: none;
    }
    .img-fade {
        opacity: 1;
        transition: none;
    }
}

/* Vue <transition> hooks */
.drawer-enter-active,
.drawer-leave-active {
    transition: transform 0.32s cubic-bezier(0.16, 1, 0.3, 1);
}
.drawer-enter-from,
.drawer-leave-to {
    transform: translateX(100%);
}

.fade-enter-active,
.fade-leave-active {
    transition: opacity 0.25s ease;
}
.fade-enter-from,
.fade-leave-to {
    opacity: 0;
}

.pop-enter-active,
.pop-leave-active {
    transition: opacity 0.28s ease, transform 0.28s cubic-bezier(0.16, 1, 0.3, 1);
}
.pop-enter-from,
.pop-leave-to {
    opacity: 0;
    transform: scale(0.96) translateY(10px);
}

.toast-enter-active,
.toast-leave-active {
    transition: opacity 0.3s ease, transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.toast-enter-from,
.toast-leave-to {
    opacity: 0;
    transform: translateY(12px) scale(0.97);
}
.toast-move {
    transition: transform 0.3s ease;
}

/* List reshuffles inside the cart drawer/page */
.list-enter-active,
.list-leave-active {
    transition: opacity 0.25s ease, transform 0.25s ease;
}
.list-enter-from,
.list-leave-to {
    opacity: 0;
    transform: translateX(12px);
}
.list-move {
    transition: transform 0.25s ease;
}

/* -------------------------------------------------------- horizontal rows
   The card rows (Shop all, review logos, product thumbnails) scroll sideways.
   Left to the OS, that meant a fat pale scrollbar sitting across the dark
   sections. This is a slim, on-brand one — plus a gentle proximity snap so a
   card comes to rest at its own edge rather than mid-cut, and a thin-edge fade
   is avoided in favour of the peek the layout already gives. */
.scroll-row {
    scrollbar-width: thin;                                   /* Firefox */
    scrollbar-color: rgba(127, 176, 255, 0.35) transparent;  /* thumb / track */
    scroll-snap-type: x proximity;
    scroll-padding-left: 1.5rem;   /* clears the section's px-6 edge bleed */
    scroll-behavior: smooth;
    overscroll-behavior-x: contain;
}

@media (min-width: 1024px) {
    /* The bleed is gone at lg (mx-0 px-0), so the snap line is the true edge. */
    .scroll-row { scroll-padding-left: 0; }
}

.scroll-row > * {
    scroll-snap-align: start;
}

/* WebKit / Blink. The track only shows through the section, so it is barely
   there; the thumb picks up the ice accent and firms up on hover. */
.scroll-row::-webkit-scrollbar {
    height: 6px;
}
.scroll-row::-webkit-scrollbar-track {
    background: rgba(255, 255, 255, 0.05);
    border-radius: 999px;
}
.scroll-row::-webkit-scrollbar-thumb {
    background: rgba(127, 176, 255, 0.3);
    border-radius: 999px;
}
.scroll-row::-webkit-scrollbar-thumb:hover {
    background: rgba(127, 176, 255, 0.55);
}

/* A drag/flick shouldn't fight a reduced-motion preference. */
@media (prefers-reduced-motion: reduce) {
    .scroll-row { scroll-behavior: auto; }
}
