/* ---------------------------------------------------------------------------
 * Site-wide loading spinner - 12-bar "tick" spinner
 *
 * This is the ONE loader for the whole storefront. It is registered globally
 * from Magento_Theme::layout/default_head_blocks.xml, so `.config-spinner` is
 * available on every frontend page - it is no longer configurator-only.
 *
 * Markup contract - 12 <i> children, nothing else required:
 *   <div class="config-spinner config-spinner--md" role="status">
 *       <?= str_repeat('<i></i>', 12) ?>
 *       <span class="sr-only">Loading...</span>
 *   </div>
 *
 * Sizes (the box must stay square - the bars are positioned in percentages of
 * it, so width/height alone rescales the whole spinner):
 *   --xs  20px  in-button spinners
 *   --sm  28px  small in-button / drawer spinners
 *   --md  40px  the default for section and page overlays
 *   --lg  80px  full-screen overlays (bare .config-spinner is 80px too)
 *
 * Context modifiers:
 *   --on-dark  white bars, for a spinner sitting on a dark or brand-coloured
 *              button or backdrop, where the default #3c3c3c is invisible
 *   --step     checkout step number ring (see below)
 *
 * `nth-of-type` (not `nth-child`) is deliberate: it keeps the opacity ramp
 * correct no matter where the sr-only span sits.
 *
 * The initial PDP load overlay (#pdp-initial-loader) is NOT styled here: it has
 * to paint before any stylesheet arrives, so it carries a copy of the spinner
 * rules inline. See Magento_Catalog::product/view/initial-loader.phtml.
 * ------------------------------------------------------------------------- */

.config-spinner {
    position: relative;
    display: inline-block;
    width: 80px;
    height: 80px;
    color: #3c3c3c;
    animation: config-spinner-rotate 1s steps(12, end) infinite;
}

.config-spinner > i {
    position: absolute;
    display: block;
    top: 5%;
    left: 50%;
    width: 11%;
    height: 25%;
    margin-left: -5.5%;
    border-radius: 9999px;
    background-color: currentColor;

    /* 5% top + 1.8 x 25% height puts the pivot exactly on the container centre */
    transform-origin: 50% 180%;
}

.config-spinner > i:nth-of-type(1)  { transform: rotate(0deg);   opacity: 1;    }
.config-spinner > i:nth-of-type(2)  { transform: rotate(30deg);  opacity: .92;  }
.config-spinner > i:nth-of-type(3)  { transform: rotate(60deg);  opacity: .83;  }
.config-spinner > i:nth-of-type(4)  { transform: rotate(90deg);  opacity: .74;  }
.config-spinner > i:nth-of-type(5)  { transform: rotate(120deg); opacity: .65;  }
.config-spinner > i:nth-of-type(6)  { transform: rotate(150deg); opacity: .56;  }
.config-spinner > i:nth-of-type(7)  { transform: rotate(180deg); opacity: .47;  }
.config-spinner > i:nth-of-type(8)  { transform: rotate(210deg); opacity: .39;  }
.config-spinner > i:nth-of-type(9)  { transform: rotate(240deg); opacity: .32;  }
.config-spinner > i:nth-of-type(10) { transform: rotate(270deg); opacity: .26;  }
.config-spinner > i:nth-of-type(11) { transform: rotate(300deg); opacity: .21;  }
.config-spinner > i:nth-of-type(12) { transform: rotate(330deg); opacity: .16;  }

@keyframes config-spinner-rotate {
    to {
        transform: rotate(360deg);
    }
}

/* Size modifiers – the percentages above make width/height all that is needed,
   except at the two small sizes: 11% of 20px is a 2px hairline, so the bars are
   widened and shortened a little to stay legible. */
.config-spinner--xs {
    width: 20px;
    height: 20px;
}

.config-spinner--xs > i {
    width: 15%;
    height: 27%;
    margin-left: -7.5%;

    /* 5% top + 1.72 x 27% height keeps the pivot on the container centre */
    transform-origin: 50% 172%;
}

.config-spinner--sm {
    width: 28px;
    height: 28px;
}

.config-spinner--sm > i {
    width: 13%;
    height: 26%;
    margin-left: -6.5%;
    transform-origin: 50% 173%;
}

.config-spinner--md {
    width: 40px;
    height: 40px;
}

.config-spinner--lg {
    width: 80px;
    height: 80px;
}

/* On a dark or brand-coloured button/backdrop the default #3c3c3c disappears. */
.config-spinner--on-dark {
    color: #fff;
}

/* For a host whose colour is only known at runtime (a CMS-styled button, say):
   the bars follow whatever text colour the host resolves to. */
.config-spinner--inherit {
    color: inherit;
}

@media (prefers-reduced-motion: reduce) {
    .config-spinner {
        animation-duration: 2.4s;
        animation-timing-function: steps(12, end);
    }
}

/* ---------------------------------------------------------------------------
 * Below 1024px the spinner is centred on the screen, not on a gallery.
 *
 * The large gallery scrolls out of view and the small one shrinks to the 200px
 * scroll thumbnail, so an overlay pinned to either is easily missed while the
 * user is working through the options. gallery.js appends
 * .configurator-screen-loader to <body> instead, and gallery.phtml renders its
 * in-swiper overlay only from 1024px up, so only one spinner is ever visible.
 * ------------------------------------------------------------------------- */

.configurator-screen-loader {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;

    /* Same wash as the in-gallery overlay, so the spinner reads over whatever
       part of the page happens to be on screen. */
    background-color: rgba(255, 255, 255, .7);

    /* Changing another option mid-render is supported (the request is aborted),
       so the backdrop must not swallow taps. */
    pointer-events: none;

    /* Held invisible for the first 120ms: a cached or already generated image
       comes back almost immediately and would otherwise flash the backdrop. */
    animation: configurator-screen-loader-fade-in .18s ease-out .12s both;
}

@keyframes configurator-screen-loader-fade-in {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

@media (prefers-reduced-motion: reduce) {
    /* Keep the 120ms hold (it prevents the flash), drop the fade itself. */
    .configurator-screen-loader {
        animation-duration: 0s;
    }
}

@media (min-width: 1024px) {
    /* Desktop keeps the in-gallery overlay; the screen loader is not created there. */
    .configurator-screen-loader {
        display: none;
    }
}

/* ---------------------------------------------------------------------------
 * Tab switch overlay (Sitzbank 48cm / Hochbank 68cm ... on configurator PDPs)
 *
 * js/src/main.js appends .configurator-tab-loading to #configurator_ajax_container
 * for the whole switch - fetch, DOM swap and Alpine re-init - and sets
 * .configurator-tab-switching on <body> while it is up. The body class hides the
 * fullscreen mobile spinner so only one loader is ever visible below 1024px.
 * ------------------------------------------------------------------------- */

#configurator_ajax_container {
    position: relative;
}

.configurator-tab-loading {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: rgba(255, 255, 255, .85);

    /* Same 120ms hold as the screen loader: a preloaded tab swaps almost
       immediately and would otherwise flash the backdrop. */
    animation: configurator-screen-loader-fade-in .18s ease-out .12s both;
}

body.configurator-tab-switching .configurator-screen-loader {
    display: none;
}

/* ---------------------------------------------------------------------------
 * Checkout step number (Onepage checkout: Customer / Billing / Shipping / ...)
 *
 * The spinner shares its 40x40 box with the step number, which .title-number
 * .number centres on top of it. The bars only reach 27% in from the edge (5%
 * top + 22% height), leaving an ~18px circle clear in the middle for the digit.
 *
 * Idle it is a static, flat-opacity ring - the decoration the old green
 * .section-loader border used to provide. checkout.js drives :class="{'is-loading'}",
 * which brings back the opacity ramp and the rotation.
 * ------------------------------------------------------------------------- */

.config-spinner--step {
    position: absolute;
    top: 0;
    left: 0;
    width: 40px;
    height: 40px;
    animation: none;
}

.config-spinner--step > i {
    width: 9%;
    height: 22%;
    margin-left: -4.5%;

    /* 5% top + 2.05 x 22% height keeps the pivot on the container centre */
    transform-origin: 50% 205%;
}

/* Flat while idle. The :not() beats the .config-spinner > i:nth-of-type() ramp
   on specificity, so this holds no matter what order the stylesheets load in. */
.config-spinner--step:not(.is-loading) > i:nth-of-type(n) {
    opacity: .28;
}

.config-spinner--step.is-loading {
    animation: config-spinner-rotate 1s steps(12, end) infinite;
}

@media (prefers-reduced-motion: reduce) {
    .config-spinner--step.is-loading {
        animation-duration: 2.4s;
    }
}

/* ---------------------------------------------------------------------------
 * OX_InfiniteScroll "Load more" / "Load less" button
 *
 * infinite-scroll.js toggles .loading on the spinner itself (showLoader /
 * hideLoader), so the element is always in the DOM and hidden until then.
 * ------------------------------------------------------------------------- */

.load-btn .config-spinner {
    visibility: hidden;
    animation: none;
}

.load-btn .config-spinner.loading {
    visibility: visible;
    animation: config-spinner-rotate 1s steps(12, end) infinite;
}
