/**
 * Atom Side Cart — animations.
 *
 * All motion is CSS-driven:
 *   - Panel slide: transition on `transform`, triggered by flipping aria-hidden
 *     (JS just sets aria-hidden="true|false"; the transition does the rest).
 *   - Cart badge pop: keyframe animation, triggered by adding `.is-popping`.
 *   - New-item flash: keyframe animation, triggered by adding `.is-flashing`.
 *
 * GPU-composited transform/opacity only; no main-thread tweening.
 */

/* ---------------------------------------------------------------------------
 * Panel + backdrop entry.
 * Mobile (<640px): the panel fades in over a frosted backdrop; motion is
 * carried by the internal choreography below (header drops, footer rises,
 * items stagger up), not by sliding the whole panel.
 * Desktop (>=640px): the panel is a 420px rail that slides in from the right as
 * one piece (see the desktop slide block after the choreography), so the
 * internal stagger is cancelled there to avoid doubling the motion.
 * ------------------------------------------------------------------------ */
.asc-overlay {
  position: fixed;
  inset: 0;
  z-index: 9998; /* one below .asc-panel (z-[9999]) so it sits behind it */
  background: rgba(0, 0, 0, 0.2);
  -webkit-backdrop-filter: blur(32px);
  backdrop-filter: blur(32px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 200ms ease;
}

.asc-overlay.is-open {
  opacity: 1;
  pointer-events: auto;
}

/* Desktop: no frosted backdrop. The panel is a 420px rail (w-full sm:w-[420px])
 * and the rest of the page stays clear + interactive. Matches Tailwind's `sm`
 * breakpoint so the overlay tracks the panel's own full-width → rail switch.
 * Click-outside-to-close still works (handled in cart.js, not the overlay). */
@media (min-width: 640px) {
  .asc-overlay {
    display: none;
  }
}

.asc-panel {
  opacity: 0;
  pointer-events: none;
  transition: opacity 200ms ease;
  will-change: opacity;
}

.asc-panel[aria-hidden="false"] {
  opacity: 1;
  pointer-events: auto;
}

/* ---------------------------------------------------------------------------
 * Entry choreography.
 * Driven by a transient `.asc-opening` class (added on open, removed once the
 * sequence finishes) — NOT the persistent open state. This matters because the
 * footer and item rows live inside `.asc-body`, which WooCommerce fragments
 * re-render on every cart mutation. Keyframe animations gated on `.asc-opening`
 * play exactly once per open and leave the element at its natural rest state,
 * so a qty change mid-session never re-triggers the slide-in.
 *
 * Honour reduced-motion: skip the staged entry, just let the fades stand.
 * ------------------------------------------------------------------------ */
@media (prefers-reduced-motion: no-preference) {
  .asc-panel.asc-opening .asc-head {
    animation: asc-head-in 380ms cubic-bezier(0.22, 1, 0.36, 1) 80ms both;
  }

  .asc-panel.asc-opening .asc-foot {
    animation: asc-foot-in 400ms cubic-bezier(0.22, 1, 0.36, 1) 80ms both;
  }

  .asc-panel.asc-opening .asc-item {
    animation: asc-line-in 380ms cubic-bezier(0.22, 1, 0.36, 1) both;
  }

  .asc-panel.asc-opening .asc-item:nth-child(1) {
    animation-delay: 300ms;
  }
  .asc-panel.asc-opening .asc-item:nth-child(2) {
    animation-delay: 360ms;
  }
  .asc-panel.asc-opening .asc-item:nth-child(3) {
    animation-delay: 420ms;
  }
  .asc-panel.asc-opening .asc-item:nth-child(4) {
    animation-delay: 480ms;
  }
  .asc-panel.asc-opening .asc-item:nth-child(5) {
    animation-delay: 540ms;
  }
  .asc-panel.asc-opening .asc-item:nth-child(6) {
    animation-delay: 600ms;
  }
  .asc-panel.asc-opening .asc-item:nth-child(n + 7) {
    animation-delay: 660ms;
  }
}

/* ---------------------------------------------------------------------------
 * Desktop slide-in.
 * The panel is a 420px rail (w-full sm:w-[420px]); on desktop it glides in from
 * the right as one piece instead of fading. The internal entry choreography is
 * cancelled here so the rail doesn't slide while its contents stagger — that
 * stagger is for the mobile fade. A short opacity fade rides along to hide the
 * panel's drop shadow while it sits off-screen at rest. Gated on no-preference
 * so a reduced-motion desktop user falls back to the plain fade above.
 * ------------------------------------------------------------------------ */
@media (min-width: 640px) and (prefers-reduced-motion: no-preference) {
  .asc-panel {
    transform: translateX(100%);
    transition:
      transform 320ms cubic-bezier(0.22, 1, 0.36, 1),
      opacity 320ms ease;
    will-change: transform, opacity;
  }

  .asc-panel[aria-hidden="false"] {
    transform: none;
  }

  .asc-panel.asc-opening .asc-head,
  .asc-panel.asc-opening .asc-foot,
  .asc-panel.asc-opening .asc-item {
    animation: none;
  }
}

@keyframes asc-head-in {
  from {
    transform: translateY(-100%);
  }
  to {
    transform: none;
  }
}

@keyframes asc-foot-in {
  from {
    transform: translateY(100%);
  }
  to {
    transform: none;
  }
}

@keyframes asc-line-in {
  from {
    opacity: 0;
    transform: translateY(16px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ---------------------------------------------------------------------------
 * Cart count "pop" on add-to-cart.
 * Smooth zoom to 1.35× and back. Triggered by toggling `.is-popping`.
 * ------------------------------------------------------------------------ */
@keyframes asc-cart-pop {
  0%,
  100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.35);
  }
}

.asc-cart-count.is-popping {
  transform-origin: center center;
  animation: asc-cart-pop 400ms cubic-bezier(0.45, 0, 0.55, 1);
}

/* ---------------------------------------------------------------------------
 * Cart icon "bounce" on add-to-cart.
 * The header trigger's icon hops + tilts so the whole cart reacts, not just the
 * count. Triggered by toggling `.asc-react` on the `.asc-toggle` element; the
 * animation runs on its child <svg>. Pairs with the badge pop above.
 * ------------------------------------------------------------------------ */
@keyframes asc-cart-bounce {
  0% {
    transform: translateY(0) rotate(0) scale(1);
  }
  22% {
    transform: translateY(-7px) rotate(-10deg) scale(1.14);
  }
  46% {
    transform: translateY(0) rotate(7deg) scale(1.04);
  }
  68% {
    transform: translateY(-2px) rotate(-4deg) scale(1.02);
  }
  100% {
    transform: translateY(0) rotate(0) scale(1);
  }
}

.asc-toggle.asc-react svg {
  transform-origin: 50% 60%;
  animation: asc-cart-bounce 600ms cubic-bezier(0.34, 1.56, 0.64, 1);
}

@media (prefers-reduced-motion: reduce) {
  .asc-toggle.asc-react svg {
    animation: none;
  }
}

/* ---------------------------------------------------------------------------
 * Newly added item flash.
 * Yellow wash that fades to transparent. Triggered by `.is-flashing`.
 * ------------------------------------------------------------------------ */
@keyframes asc-item-flash {
  0% {
    background-color: #fef3c7;
  }
  100% {
    background-color: transparent;
  }
}

.asc-item.is-flashing {
  animation: asc-item-flash 900ms ease-out;
}

/* ---------------------------------------------------------------------------
 * Taktil press feedback (Version A — "Taktil morph").
 * The cart's own controls squash while held, then gel-bounce back on release —
 * the same tactile language as the add-to-cart button in the prototype. Driven
 * by JS classes (see the press module in cart.js), NOT `:active`: the click
 * handlers are delegated on `document`, so iOS Safari never fires `:active` on
 * the actual button, and a quick tap is too brief to see a plain `:active`
 * transition. Toggling a class lets the release bounce play in full on any tap.
 * Colour and spacing come from the theme's Tailwind classes; only the motion is
 * ported from the prototype.
 * ------------------------------------------------------------------------ */
.asc-pressing {
  transform: scale(0.9);
  transition: transform 100ms ease;
  transform-origin: center center;
}

.asc-releasing {
  transform-origin: center center;
  animation: asc-press-gel 420ms cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes asc-press-gel {
  0% {
    transform: scale(0.9);
  }
  45% {
    transform: scale(1.04);
  }
  100% {
    transform: scale(1);
  }
}

/* Reduced-motion: drop the squash + bounce, keep the controls fully usable. */
@media (prefers-reduced-motion: reduce) {
  .asc-pressing {
    transform: none;
  }
  .asc-releasing {
    animation: none;
  }
}

/* ---------------------------------------------------------------------------
 * Quantity <input type="number"> — hide the native spinner so our +/- buttons
 * are the visible controls, while still allowing direct typing.
 * ------------------------------------------------------------------------ */
.asc-qty-input {
  -moz-appearance: textfield;
}
.asc-qty-input::-webkit-outer-spin-button,
.asc-qty-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* ---------------------------------------------------------------------------
 * Free-shipping progress bar.
 *
 * Two motions, structured like the rest of this file:
 *   - On open: the fill springs 0% → its target width once per open, gated on the
 *     transient `.asc-opening` class (added on open, removed at 1100ms by cart.js).
 *   - On live qty change: a width transition glides the fill when mergeCartHtml()
 *     rewrites the inline width.
 *
 * Deliberate exception to this file's "transform/opacity only" contract: the fill
 * animates `width` (a layout property) rather than transform: scaleX(), because
 * scaleX distorts the bar's rounded end-caps into ellipses. The cost is negligible —
 * one thin bar, animated only on open and on qty change, never continuously. The
 * track is `overflow:hidden` (set in the markup) so the spring overshoot can't bulge
 * the fill past 100% at the free-shipping-reached state.
 * ------------------------------------------------------------------------ */

/* Base (unconditional): glide on qty-driven width changes (mergeCartHtml). */
.asc-free-ship-fill {
  transition: width 450ms cubic-bezier(0.22, 1, 0.36, 1);
}

@media (prefers-reduced-motion: no-preference) {
  /* Spring fill-up on open. delay + duration = 420 + 500 = 920ms, which MUST stay
	 * <= ~1040ms: cart.js removes `.asc-opening` at 1100ms, so a longer run would be
	 * torn off mid-spring. 420ms starts just after the footer has risen
	 * (asc-foot-in = 80 + 400 = 480ms) and lands with ~180ms slack. Do NOT bump the
	 * cart.js 1100ms timer to fit a longer animation — that widens the window in
	 * which a mid-open fragment re-render could re-arm every gated animation.
	 * Intentionally NOT added to the desktop cancel list above, so the reveal plays
	 * on desktop too. */
  .asc-panel.asc-opening .asc-free-ship-fill {
    animation: asc-fill-in 500ms cubic-bezier(0.34, 1.56, 0.64, 1) 420ms both;
  }
}

/* `both` holds the `from` (width:0) through the 420ms delay so the bar starts empty
 * rather than painting at its target then snapping to 0. The keyframe has no explicit
 * `to`, so its implicit end value IS the inline width:NN% — the bar settles exactly
 * there when the animation ends and when `.asc-opening` is later removed. */
@keyframes asc-fill-in {
  from {
    width: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .asc-free-ship-fill {
    transition: none;
  }
}

/* ---------------------------------------------------------------------------
 * Free-shipping info bubble.
 * A small dark tooltip above the ⓘ icon clarifying which shipping method goes
 * free. Shown on hover/focus (desktop) and via `.asc-tip-open` which cart.js
 * toggles on tap (touch); dismissed on outside-click / Escape. Lives inside the
 * position:fixed footer, so it escapes the .asc-body overflow clip.
 * ------------------------------------------------------------------------ */
.asc-free-ship-tip {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  z-index: 10;
  width: max-content;
  max-width: 220px;
  padding: 6px 10px;
  border-radius: 8px;
  background: #000;
  color: #fff;
  font-size: 0.8rem;
  line-height: 1.4;
  font-weight: 500;
  letter-spacing: 0.03em;
  text-align: center;
  white-space: normal;
  text-transform: uppercase;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity 150ms ease,
    transform 150ms ease;
}

/* Arrow pointing down at the icon. */
.asc-free-ship-tip::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top-color: #000;
}

.asc-free-ship-info-wrap:hover .asc-free-ship-tip,
.asc-free-ship-info-wrap:focus-within .asc-free-ship-tip,
.asc-free-ship-info-wrap.asc-tip-open .asc-free-ship-tip {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .asc-free-ship-tip {
    transition: none;
  }
}
