/*
 * Free-gift "Gift Vault" — a deliberately loud reward panel in the cart.
 *
 * Design intent: the cart page is the store's light scheme (#f7f5fa). This
 * block goes DARK on purpose, so the gift offer reads as a separate reward
 * zone rather than another cart row a customer scrolls past. Everything else
 * on the page is a thing you pay for; this is the thing you don't.
 *
 * Palette is the store's own theme scheme colours, not invented:
 *   #ff4fa3 pink   — primary action (scheme-1/3/4 button)
 *   #ffc857 gold   — the reward currency (scheme-2/5 button)
 *   #2ad4ff cyan   — secondary accent (scheme-3/4 secondary label)
 *   #26143f purple — dark scheme background (scheme-3)
 *
 * There is no shared "budget" bar here on purpose — every gift-pool product
 * carries its own independent spend threshold (see shared/gift-eligibility.js),
 * so what's shown is an unlock CHECKLIST, not a progress meter toward one
 * number. Each card states its own threshold.
 *
 * SIZING: type is em-based off the block's inherited font-size, never rem.
 * Dawn-derived themes set the root font-size to 62.5% (10px) — rem values
 * tuned for that silently render ~1.6x too large on a theme that doesn't,
 * and this block has to survive theme changes. Inheriting from the cart's
 * body text is the one measurement that is correct everywhere, and it keeps
 * the block scaling with a customer's own font-size preference.
 *
 * The block still sets its OWN floor (below) rather than pure inherit — on
 * a live cart page the inherited size came out small enough to read as
 * cramped (14px), so max() guarantees a legible minimum while still scaling
 * up on themes/customers with a larger base size.
 */

.wwgift {
  /* Brand */
  --ww-pink: #ff4fa3;
  --ww-pink-lift: #ff6fb5;
  --ww-gold: #ffc857;
  --ww-cyan: #2ad4ff;
  --ww-ink: #1a0b2e;

  /* Panel */
  --ww-panel-top: #2e1550;
  --ww-panel-bottom: #150826;
  --ww-text: #ffffff;
  --ww-muted: rgba(255, 255, 255, 0.62);
  --ww-faint: rgba(255, 255, 255, 0.38);
  --ww-line: rgba(255, 255, 255, 0.13);
  --ww-well: rgba(0, 0, 0, 0.3);

  display: block;
  margin: 0 0 1.6em;
  /* Floor + scale — see SIZING above. Raised twice now (14px inherited on
     the live theme read as cramped, then 17px still wasn't enough) — this
     block does not get to inherit a small theme font-size and stay small. */
  font-size: max(20px, 1.2em);
  line-height: 1.45;
}

/* ── Panel ─────────────────────────────────────────────────────────────── */

.wwgift__panel {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  /* Capped so it reads as a card sitting in the cart, not a banner
     stretched edge-to-edge across a wide desktop cart page — but wide
     enough that three gift cards sit comfortably in a row instead of
     cramping down to a narrow column. */
  max-width: 56em;
  margin-inline: auto;
  padding: 1.7em 1.9em 2em;
  border-radius: 16px;
  color: var(--ww-text);
  background: linear-gradient(160deg, var(--ww-panel-top), var(--ww-panel-bottom));
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.08) inset,
    0 18px 40px -24px rgba(26, 11, 46, 0.9);
}

/* Decorative corner glows. Purely atmospheric — aria-hidden in the markup. */
.wwgift__aura {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(38em 18em at 8% -10%, rgba(255, 79, 163, 0.4), transparent 60%),
    radial-gradient(32em 16em at 96% 108%, rgba(42, 212, 255, 0.24), transparent 62%);
}

/* Once at least one gift is unlocked the panel earns a lit rim. */
.wwgift__panel--won {
  box-shadow:
    0 0 0 1px rgba(255, 200, 87, 0.42) inset,
    0 1px 0 rgba(255, 255, 255, 0.1) inset,
    0 18px 40px -26px rgba(255, 79, 163, 0.45);
}

/*
 * Level-up: played once, only when the customer unlocks more gifts than
 * they had on the previous render. A flourish that fires on every cart
 * change is wallpaper; one that fires only on the moment it is celebrating
 * is worth watching.
 */
.wwgift__panel--levelup {
  animation: wwgift-levelup 1.1s cubic-bezier(0.22, 1, 0.36, 1);
}

@keyframes wwgift-levelup {
  0% {
    transform: scale(1);
    box-shadow:
      0 0 0 1px rgba(255, 200, 87, 0.42) inset,
      0 18px 40px -26px rgba(255, 79, 163, 0.45);
  }
  22% {
    transform: scale(1.012);
    box-shadow:
      0 0 0 2px rgba(255, 200, 87, 0.95) inset,
      0 0 60px -6px rgba(255, 200, 87, 0.65);
  }
  100% {
    transform: scale(1);
    box-shadow:
      0 0 0 1px rgba(255, 200, 87, 0.42) inset,
      0 18px 40px -26px rgba(255, 79, 163, 0.45);
  }
}

.wwgift__panel--levelup .wwgift__buddy,
.wwgift__panel--levelup .wwgift__eyebrow {
  animation: wwgift-pop 0.9s 0.1s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

@keyframes wwgift-pop {
  0% {
    transform: scale(0.86);
  }
  45% {
    transform: scale(1.09);
  }
  100% {
    transform: scale(1);
  }
}

/* ── Header ────────────────────────────────────────────────────────────── */

.wwgift__head {
  display: flex;
  align-items: center;
  gap: 1.2em;
  flex-wrap: wrap;
}

/* Fills the row beside the mascot instead of shrinking to its own content
   width — without this the headline hit its max-width early, wrapped to
   two short lines, and left a dead gap of unused panel width beside it. */
.wwgift__head-text {
  flex: 1 1 240px;
  min-width: 0;
}

/*
 * The store's heading face is Bungee — a heavy display type built for short
 * signage, not sentences. It goes on this label-length string and nowhere
 * else; a wrapped headline set in it would be a wall.
 */
.wwgift__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  margin: 0 0 0.4em;
  font-family: var(--font-heading-family, inherit);
  font-size: 0.72em;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ww-gold);
}

.wwgift__spark {
  font-size: 1.1em;
  line-height: 1;
}

.wwgift__headline {
  margin: 0;
  font-weight: 700;
  font-size: 1.22em;
  line-height: 1.25;
  letter-spacing: -0.01em;
  /* Was 34ch — with the wider panel that wrapped early and left a dead gap
     beside it (see .wwgift__head-text). 52ch lets it use the row it's
     actually been given before wrapping. */
  max-width: 52ch;
  text-wrap: balance;
}

/* ── Store mascot ──────────────────────────────────────────────────────── */

/*
 * Empty (renders nothing) until data-mascot1/2/3 are set on the block —
 * see README "Store mascot". The three images stack in the same slot and
 * cross-fade by toggling .is-active, keyed to excitementLevel() in the JS.
 */
.wwgift__buddy {
  position: relative;
  flex: 0 0 auto;
  /* em-scaled, but with a hard floor and ceiling in real px — this is a
     character meant to be noticed, not a small status icon, and it must
     stay that way even if the theme's inherited font-size is tiny. */
  width: clamp(120px, 11em, 220px);
  height: clamp(120px, 11em, 220px);
  margin: -0.6em -0.1em -0.6em 0;
}

.wwgift__buddy-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0;
  transform: scale(0.7) translateY(8px) rotate(-4deg);
  transition:
    opacity 0.3s ease,
    transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
  filter: drop-shadow(0 6px 10px rgba(0, 0, 0, 0.5));
}

.wwgift__buddy-img.is-active {
  opacity: 1;
  transform: scale(1) translateY(0) rotate(0deg);
}

/* ── Progress bar ──────────────────────────────────────────────────────── */

/*
 * What share of the order's value is free right now, not "how close to
 * some threshold" — see the JS comment on progressPercent for why. Moves
 * only when a gift is actually added or removed, never from paid product
 * alone changing the cart total.
 */
.wwgift__ladder {
  position: relative;
  margin: 1.8em 0 1.4em;
  padding: 0 0.6em;
}

/* Deliberately thick and loud — this bar is the centrepiece of the panel,
   not a quiet status line, so it gets the same visual weight as the "Add
   free" buttons rather than sitting beneath the cards as an afterthought. */
.wwgift__rail {
  position: relative;
  height: 20px;
  border-radius: 999px;
  background: var(--ww-well);
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.09) inset,
    0 2px 10px rgba(0, 0, 0, 0.4) inset;
  overflow: visible;
}

.wwgift__rail-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--ww-cyan), var(--ww-pink) 62%, var(--ww-gold));
  background-size: 200% 100%;
  transition: width 0.55s cubic-bezier(0.22, 1, 0.36, 1);
  position: relative;
  overflow: hidden;
  animation:
    wwgift-glow-pulse 2s ease-in-out infinite,
    wwgift-gradient-drift 4s ease-in-out infinite;
}

/* The glow breathes, the gradient itself creeps back and forth — a static
   fill reads as a loading bar, not a reward meter. Overdone on purpose. */
@keyframes wwgift-glow-pulse {
  0%,
  100% {
    box-shadow:
      0 0 10px 0px var(--ww-pink),
      0 0 0px 0px rgba(255, 255, 255, 0);
  }
  50% {
    box-shadow:
      0 0 26px 4px var(--ww-pink),
      0 0 14px 2px rgba(255, 255, 255, 0.55);
  }
}

@keyframes wwgift-gradient-drift {
  0%,
  100% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
}

/* Travelling highlight — reads as "live", which a flat bar does not. */
.wwgift__rail-fill::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    transparent 20%,
    rgba(255, 255, 255, 0.75) 50%,
    transparent 80%
  );
  transform: translateX(-100%);
  animation: wwgift-sheen 1.8s ease-in-out infinite;
}

@keyframes wwgift-sheen {
  0%,
  45% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(100%);
  }
}

/* Sparkles scattered above and below the rail, twinkling independently —
   see the JS comment on SPARKLES for why fixed positions, not random.
   Purely decorative: aria-hidden, and skipped along with everything else
   under prefers-reduced-motion. */
.wwgift__sparkle {
  position: absolute;
  transform: translate(-50%, -50%) scale(0);
  font-size: calc(1em * var(--wwgift-sparkle-size, 0.8));
  line-height: 1;
  pointer-events: none;
  animation-name: wwgift-twinkle;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  z-index: 2;
}

.wwgift__sparkle--pink {
  color: var(--ww-pink-lift);
  text-shadow: 0 0 6px var(--ww-pink);
}

.wwgift__sparkle--gold {
  color: var(--ww-gold);
  text-shadow: 0 0 6px var(--ww-gold);
}

.wwgift__sparkle--cyan {
  color: var(--ww-cyan);
  text-shadow: 0 0 6px var(--ww-cyan);
}

@keyframes wwgift-twinkle {
  0%,
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.2) rotate(0deg);
  }
  50% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1) rotate(25deg);
  }
}

/* What share of the order is free right now — the one line of context the
   bar needs. Sits above the rail; see the JS comment on progressPercent
   for why this is "value claimed / order total", not "distance to a
   threshold". */
.wwgift__claim-label {
  margin: 0 0 0.5em;
  font-size: 0.92em;
  color: var(--ww-muted);
  font-variant-numeric: tabular-nums;
}

.wwgift__claim-amount {
  color: var(--ww-gold);
  font-weight: 700;
}

/* One-line explainer so a first-time customer doesn't read the amount
   above as the gift's price — small and muted on purpose, a footnote, not
   a second headline. */
.wwgift__claim-note {
  margin: 0 0 0.9em;
  font-size: 0.82em;
  color: var(--ww-faint);
  line-height: 1.4;
}

/* ── Gift grid ─────────────────────────────────────────────────────────── */

.wwgift__grid {
  list-style: none;
  margin: 1.3em 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(10.5em, 1fr));
  gap: 1em;
}

.wwgift__card {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 0.7em 0.7em 0.85em;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.055);
  box-shadow: 0 0 0 1px var(--ww-line) inset;
  transition:
    transform 0.22s ease,
    box-shadow 0.22s ease,
    background 0.22s ease;
}

.wwgift__card:hover {
  transform: translateY(-2px);
  background: rgba(255, 255, 255, 0.09);
}

.wwgift__card.is-chosen {
  background: rgba(255, 79, 163, 0.16);
  box-shadow:
    0 0 0 2px var(--ww-pink) inset,
    0 10px 26px -18px var(--ww-pink);
}

/* Dimmed to read as "not yet yours", but never so far that the product stops
   being appealing — this card is the advertisement for spending more. */
.wwgift__card.is-locked {
  opacity: 0.72;
}

.wwgift__card.is-locked:hover {
  transform: none;
}

.wwgift__thumb {
  position: relative;
  aspect-ratio: 1;
  border-radius: 9px;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.32);
}

.wwgift__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.wwgift__card.is-locked .wwgift__thumb img {
  filter: grayscale(0.7) brightness(0.82);
}

/* Stands in for a missing product image so cards stay the same shape. */
.wwgift__thumb-blank {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  font-size: 2em;
  color: var(--ww-faint);
}

/* Spelled out as "Free at $X", not a bare "$X" — a bare number in this
   exact spot (bottom-left of a product image) reads as a price tag, which
   is the one thing this must never look like: the item isn't for sale. */
.wwgift__tag {
  position: absolute;
  left: 0.45em;
  bottom: 0.45em;
  padding: 0.28em 0.65em;
  border-radius: 999px;
  font-size: 0.7em;
  font-weight: 700;
  color: var(--ww-ink);
  background: var(--ww-gold);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.wwgift__badge {
  position: absolute;
  top: 0.45em;
  right: 0.45em;
  width: 1.9em;
  height: 1.9em;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: #fff;
  background: var(--ww-pink);
  box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.6);
}

.wwgift__badge--lock {
  color: var(--ww-text);
  background: rgba(10, 4, 20, 0.72);
}

.wwgift__card-title {
  margin: 0.65em 0 0.75em;
  font-size: 0.9em;
  line-height: 1.3;
  /* Two lines, then ellipsis — ragged card heights make the grid look broken. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Push the action to the bottom so buttons line up across a ragged row. */
.wwgift__card-foot {
  margin-top: auto;
}

.wwgift__btn {
  width: 100%;
  cursor: pointer;
  font: inherit;
  font-size: 0.88em;
  font-weight: 700;
  padding: 0.7em 0.5em;
  border: 0;
  border-radius: 10px;
  /* Dark ink on pink, not white: white on #ff4fa3 is only 3:1 and this is
     small text. Ink is 6:1, and the store already pairs dark labels with a
     bright button in scheme-2. */
  color: var(--ww-ink);
  background: var(--ww-pink);
  transition:
    background 0.18s ease,
    transform 0.12s ease;
}

.wwgift__btn:hover {
  background: var(--ww-pink-lift);
}

.wwgift__btn:active {
  transform: scale(0.97);
}

.wwgift__btn--remove {
  color: var(--ww-text);
  background: rgba(255, 255, 255, 0.14);
}

.wwgift__btn--remove:hover {
  background: rgba(255, 255, 255, 0.22);
}

.wwgift__btn[disabled] {
  opacity: 0.6;
  cursor: default;
}

.wwgift__need {
  display: block;
  text-align: center;
  padding: 0.7em 0.5em;
  font-size: 0.84em;
  font-weight: 700;
  color: var(--ww-gold);
  font-variant-numeric: tabular-nums;
}

/*
 * A gift still in the cart that the cart no longer covers. Shopify has
 * already stopped discounting it, so the customer WILL be charged unless
 * they act — this is the one message on the panel that has to be
 * impossible to skim past, hence the alarm colour rather than the gold
 * used for every other "not yet" state.
 */
.wwgift__charged {
  display: block;
  margin-bottom: 0.5em;
  padding: 0.5em 0.6em;
  border-radius: 8px;
  font-size: 0.78em;
  font-weight: 700;
  line-height: 1.35;
  text-align: center;
  color: #fff;
  background: rgba(215, 38, 94, 0.9);
}

.wwgift__card.is-charged {
  box-shadow:
    0 0 0 2px var(--ww-pink) inset,
    0 10px 26px -18px var(--ww-pink);
}

/* Shown under an already-free card when the cart holds as many free units
   of this item as the merchant allows — explains why there's no button. */
.wwgift__cap-note {
  display: block;
  margin-top: 0.4em;
  text-align: center;
  font-size: 0.8em;
  color: var(--ww-faint);
}

/* Keyboard focus must stay obvious against a dark panel. */
.wwgift__btn:focus-visible {
  outline: 3px solid var(--ww-cyan);
  outline-offset: 2px;
}

/* ── Error ─────────────────────────────────────────────────────────────── */

.wwgift__error {
  margin: 0.9em 0 0;
  padding: 0.6em 0.9em;
  border-radius: 10px;
  font-size: 0.85em;
  color: #fff;
  background: rgba(215, 38, 94, 0.85);
}

.wwgift__error[hidden] {
  display: none;
}

/* ── Narrow screens ────────────────────────────────────────────────────── */

@media (max-width: 30em) {
  .wwgift__panel {
    padding: 1.3em 1.1em 1.4em;
  }

  .wwgift__grid {
    grid-template-columns: repeat(auto-fill, minmax(9em, 1fr));
  }
}

/* ── Reduced motion ────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  .wwgift__card,
  .wwgift__btn,
  .wwgift__buddy-img,
  .wwgift__rail-fill {
    transition: none;
  }

  .wwgift__rail-fill {
    animation: none;
  }

  .wwgift__rail-fill::after {
    animation: none;
    display: none;
  }

  .wwgift__sparkle {
    display: none;
  }

  .wwgift__panel--levelup,
  .wwgift__panel--levelup .wwgift__buddy,
  .wwgift__panel--levelup .wwgift__eyebrow {
    animation: none;
  }
}
