/* ============================================================
   Three Wishes Bakery — the genie layer
   ------------------------------------------------------------
   Smoke, sparkle, and wish-granting effects. Pure CSS + the
   companion magic.js. No libraries, no build step.

   Everything here is motion-only decoration: it sits on top of
   a site that already works, and the reduced-motion block at
   the bottom switches all of it off for anyone who asks.
   ============================================================ */

:root{
  /* The genie palette. The bakery's rose stays the brand colour; iris and
     amethyst are the magic — smoke, veils, shockwaves, sparks. */
  --iris:#9867c5;
  --amethyst:#a45ee5;
  --iris-deep:#5b3a80;
  --iris-dark:#2b1840;
  --smoke:rgba(255,255,255,.85);
  --smoke-rose:rgba(164,94,229,.55);
  --gold:#c9962f;
}

/* ------------------------------------------------------------
   1. Particle field — the shared stage for poofs and sparkles.
   Fixed, non-interactive, above content but below the drawer.
   ------------------------------------------------------------ */
.fx-layer{
  position:fixed;inset:0;z-index:110;
  pointer-events:none;overflow:hidden;
}

/* a single puff of genie smoke */
.puff{
  position:absolute;border-radius:50%;
  background:radial-gradient(circle at 35% 35%,var(--smoke),var(--smoke-rose) 55%,transparent 72%);
  filter:blur(6px);
  will-change:transform,opacity;
}
.puff.rose{
  background:radial-gradient(circle at 35% 35%,rgba(214,180,245,.9),rgba(107,63,158,.5) 55%,transparent 72%);
}
.puff.dark{
  background:radial-gradient(circle at 35% 35%,rgba(90,60,66,.65),rgba(28,16,48,.35) 55%,transparent 72%);
}

/* a four-point wish sparkle */
.spark{
  position:absolute;width:14px;height:14px;
  background:#e8b84b;
  clip-path:polygon(50% 0%,58% 42%,100% 50%,58% 58%,50% 100%,42% 58%,0% 50%,42% 42%);
  will-change:transform,opacity;
}
.spark.rose{background:var(--rose)}
.spark.white{background:#fff}

/* ------------------------------------------------------------
   2. The lamp — hero centrepiece that breathes smoke.
   ------------------------------------------------------------ */
.lamp-stage{
  position:relative;width:100%;max-width:340px;height:170px;margin-top:30px;
  pointer-events:none;
}
/* Sit the lamp at the right edge of the text column at every width, so the
   plume rises through empty space instead of straight up through the
   Shop Cookies button — which it did, badly, at both desktop and phone sizes. */
.lamp-stage{margin-left:auto;margin-right:0}
.lamp{
  position:absolute;bottom:0;left:0;width:250px;height:auto;
  /* already a 3D render, so it just needs grounding and a glow */
  filter:drop-shadow(0 14px 22px rgba(152,103,197,.55));
  animation:lampSway 5.5s ease-in-out infinite;
  transform-origin:58% 88%;
}
@keyframes lampSway{
  0%,100%{transform:rotate(-3.5deg) translateY(0)}
  50%{transform:rotate(3.5deg) translateY(-8px)}
}
/* the plume curling out of the spout — deliberately opaque enough to read
   against the blush background, which the first pass was not */
.lamp-smoke{
  /* the spout tip: ~3% across, ~29% down the lamp artwork */
  position:absolute;left:0;bottom:96px;width:46px;height:46px;border-radius:50%;
  background:radial-gradient(circle at 38% 38%,
    rgba(255,255,255,.98),
    rgba(214,180,245,.85) 45%,
    rgba(107,63,158,.5) 70%,
    transparent 80%);
  filter:blur(7px);opacity:0;
  animation:wisp 4.6s ease-out infinite;
}
.lamp-smoke:nth-of-type(2){animation-delay:.9s}
.lamp-smoke:nth-of-type(3){animation-delay:1.8s}
.lamp-smoke:nth-of-type(4){animation-delay:2.7s}
.lamp-smoke:nth-of-type(5){animation-delay:3.6s}
/* Peak opacity is deliberately held around .55 — high enough to read as smoke
   against the blush background, low enough that any text it drifts across
   stays legible. */
@keyframes wisp{
  0%{opacity:0;transform:translate(0,0) scale(.35)}
  14%{opacity:.55}
  55%{opacity:.4;transform:translate(46px,-110px) scale(1.7) rotate(90deg)}
  100%{opacity:0;transform:translate(14px,-215px) scale(3.4) rotate(200deg)}
}
/* the invitation — only worth showing where there's a cursor to rub with */
.lamp-hint{
  position:absolute;left:0;bottom:-6px;white-space:nowrap;
  font-family:var(--font-mono);font-size:.66rem;letter-spacing:.16em;
  /* 3.84:1 -> AA at this size */
  /* #6b3f9e measured 2.65:1 on the midnight page */
  text-transform:uppercase;color:#c79bf0;font-weight:700;
  animation:hintFade 3.4s ease-in-out infinite;
}
@keyframes hintFade{0%,100%{opacity:.4}50%{opacity:1}}
@media (max-width:600px){
  .lamp-stage{height:150px;margin-top:24px}
  .lamp{width:200px}
  .lamp-smoke{bottom:76px}
  .lamp-hint{left:auto;right:0;bottom:30px;font-size:.58rem}
}

/* ------------------------------------------------------------
   3. Genie shimmer — a light sweep across product photos.
   Fires on hover, and once on scroll-in via .is-revealed.
   ------------------------------------------------------------ */
.card-photo::after{
  content:"";position:absolute;inset:0;pointer-events:none;z-index:2;
  background:linear-gradient(115deg,
    transparent 26%,
    rgba(255,255,255,.75) 43%,
    rgba(245,196,81,.85) 50%,
    rgba(255,255,255,.75) 57%,
    transparent 74%);
  transform:translateX(-130%);
  opacity:0;
}
.card:hover .card-photo::after,
.card-photo.is-revealed::after{
  animation:genieSweep 1.15s cubic-bezier(.4,0,.2,1);
}
@keyframes genieSweep{
  0%{transform:translateX(-130%);opacity:0}
  15%{opacity:1}
  100%{transform:translateX(130%);opacity:0}
}
/* the card itself lifts into a rose glow on hover */
.card{transition:transform .18s,box-shadow .25s}
.card:hover{box-shadow:6px 10px 0 0 var(--rose),0 0 34px rgba(164,94,229,.45)}
.drop-tile:hover{box-shadow:5px 9px 0 0 var(--rose),0 0 30px rgba(164,94,229,.42)}

/* ------------------------------------------------------------
   4. Scroll reveal — sections rise out of the smoke.
   .reveal is added by magic.js so no-JS visitors see everything.
   ------------------------------------------------------------ */
.reveal{opacity:0;transform:translateY(22px);transition:opacity .7s ease,transform .7s cubic-bezier(.2,.7,.3,1)}
.reveal.in{opacity:1;transform:none}
.reveal-stagger>*{opacity:0;transform:translateY(18px) scale(.985);transition:opacity .6s ease,transform .6s cubic-bezier(.2,.7,.3,1)}
.reveal-stagger.in>*{opacity:1;transform:none}
.reveal-stagger.in>*:nth-child(1){transition-delay:0ms}
.reveal-stagger.in>*:nth-child(2){transition-delay:80ms}
.reveal-stagger.in>*:nth-child(3){transition-delay:160ms}
.reveal-stagger.in>*:nth-child(4){transition-delay:240ms}
.reveal-stagger.in>*:nth-child(5){transition-delay:320ms}
.reveal-stagger.in>*:nth-child(6){transition-delay:400ms}

/* ------------------------------------------------------------
   5. Wish-granted button state — the add-to-box moment.
   ------------------------------------------------------------ */
.add-btn.granting{animation:grantPop .5s cubic-bezier(.34,1.56,.64,1)}
@keyframes grantPop{
  0%{transform:scale(1)}
  40%{transform:scale(.94)}
  100%{transform:scale(1)}
}
/* cart button reacts when a wish lands in it */
.cart-btn.filled{animation:cartBump .55s cubic-bezier(.34,1.56,.64,1)}
@keyframes cartBump{
  0%{transform:scale(1) rotate(0)}
  35%{transform:scale(1.14) rotate(-5deg)}
  100%{transform:scale(1) rotate(0)}
}

/* toast arrives on a curl of smoke */
.toast{
  background:linear-gradient(135deg,var(--cocoa),#2c1a1e);
  box-shadow:0 0 0 3px rgba(164,94,229,.25),0 10px 30px rgba(28,16,48,.35);
}
.toast.show{animation:toastRise .35s cubic-bezier(.2,.8,.3,1)}
@keyframes toastRise{
  0%{opacity:0;transform:translate(-50%,26px) scale(.94)}
  100%{opacity:1;transform:translate(-50%,0) scale(1)}
}

/* ------------------------------------------------------------
   6. Mystery loaf — real swirling smoke instead of a flat "?".
   ------------------------------------------------------------ */
.mystery-photo{overflow:hidden}
.mystery-photo .fog{
  position:absolute;width:150%;height:150%;left:-25%;top:-25%;
  background:
    radial-gradient(circle at 25% 40%,rgba(164,94,229,.4),transparent 38%),
    radial-gradient(circle at 70% 30%,rgba(255,255,255,.22),transparent 34%),
    radial-gradient(circle at 55% 75%,rgba(107,63,158,.42),transparent 40%);
  filter:blur(14px);
  animation:fogTurn 22s linear infinite;
}
.mystery-photo .fog.b{animation-duration:31s;animation-direction:reverse;opacity:.7}
@keyframes fogTurn{to{transform:rotate(360deg)}}
.mystery-mark{position:relative;z-index:1}

/* ------------------------------------------------------------
   7. Wish form — the three inputs glow as they're filled.
   ------------------------------------------------------------ */
.wish-field{position:relative}
.wish-field input:focus{
  border-color:var(--rose);
  box-shadow:0 0 0 4px rgba(164,94,229,.28);
  outline:none;
}
.wish-field.granted::after{
  content:"✦";position:absolute;right:14px;top:50%;
  transform:translateY(-50%);color:var(--gold);font-size:1.1rem;
  animation:sparkIn .4s cubic-bezier(.34,1.56,.64,1);
}
@keyframes sparkIn{
  0%{opacity:0;transform:translateY(-50%) scale(0) rotate(-90deg)}
  100%{opacity:1;transform:translateY(-50%) scale(1) rotate(0)}
}

/* ------------------------------------------------------------
   8. Respect the setting. Everything above is decoration —
   if motion is unwelcome, the site simply stands still.
   ------------------------------------------------------------ */
@media (prefers-reduced-motion:reduce){
  /* The lamp stays — it's brand, not motion. It just stops moving, and the
     "rub me" invitation goes away because rubbing does nothing here. */
  .lamp,.lamp-smoke,.mystery-photo .fog{animation:none}
  .lamp-smoke,.lamp-hint{display:none}
  .card-photo::after{display:none}
  .reveal,.reveal-stagger>*{opacity:1;transform:none;transition:none}
  .add-btn.granting,.cart-btn.filled,.toast.show{animation:none}
}

/* ============================================================
   9. THE WISH REVEAL — each image is a wish being granted
   ------------------------------------------------------------
   An image starts fully hidden behind heavy iris smoke. When it
   scrolls into view the lamp rattles, the smoke BLASTS outward
   with a flash and a shockwave, and the photo is exposed.

   The veil is injected by JS, never written into the HTML, so if
   the script fails the photos are simply visible. A store must
   never hide its products behind a broken animation.
   ============================================================ */

.wish-shot{position:relative;overflow:hidden}

/* --- the smoke curtain: opaque, not a blur --- */
.veil{
  /* inset:0, not -2px — the bleed escaped the snap-scroll carousel on phones
     and pushed the document 2px wide, producing a horizontal scrollbar */
  position:absolute;inset:0;z-index:4;pointer-events:none;
  background:
    radial-gradient(circle at 22% 30%, var(--amethyst) 0 18%, transparent 46%),
    radial-gradient(circle at 74% 22%, var(--iris) 0 16%, transparent 44%),
    radial-gradient(circle at 58% 72%, var(--amethyst) 0 20%, transparent 48%),
    radial-gradient(circle at 18% 80%, var(--iris-deep) 0 18%, transparent 46%),
    radial-gradient(circle at 88% 66%, var(--iris) 0 14%, transparent 40%),
    linear-gradient(150deg, var(--iris-deep), var(--iris-dark));
  filter:blur(3px) saturate(1.15);
  transform:scale(1.06);
}
.veil::after{
  /* a second, slowly turning layer so the curtain churns while it waits */
  content:"";position:absolute;inset:-30%;
  background:
    radial-gradient(circle at 40% 40%, rgba(255,255,255,.16) 0 22%, transparent 45%),
    radial-gradient(circle at 66% 62%, rgba(164,94,229,.5) 0 24%, transparent 48%);
  filter:blur(10px);
  animation:veilChurn 14s linear infinite;
}
@keyframes veilChurn{to{transform:rotate(360deg)}}

/* the little lamp that sits on an unrevealed image */
.veil-lamp{
  position:absolute;left:50%;top:50%;width:150px;height:auto;
  transform:translate(-50%,-50%);
  z-index:5;pointer-events:none;
  filter:drop-shadow(0 14px 22px rgba(152,103,197,.6));
}
.veil-label{
  position:absolute;left:0;right:0;bottom:16px;z-index:5;text-align:center;
  font-family:var(--font-mono);font-size:.58rem;letter-spacing:.24em;
  text-transform:uppercase;color:rgba(255,255,255,.92);
  text-shadow:0 2px 6px rgba(0,0,0,.6);
}

/* --- the rattle: lamp shakes hard just before it blows --- */
.wish-shot.rattling .veil-lamp{animation:rattle 1.1s cubic-bezier(.36,.07,.19,.97)}
/* translate(-50%,-50%) has to be carried through every frame, or the lamp
   jumps out of centre the moment the animation starts. */
/* lifted from the Lovable build's lamp-shake, with the centring transform
   carried through every frame so the lamp doesn't jump out of position */
@keyframes rattle{
  0%{transform:translate(-50%,-50%) rotate(0)}
  10%{transform:translate(calc(-50% - 3px),-50%) rotate(-7deg)}
  20%{transform:translate(calc(-50% + 3px),-50%) rotate(7deg)}
  30%{transform:translate(calc(-50% - 4px),-50%) rotate(-9deg) scale(1.04)}
  40%{transform:translate(calc(-50% + 4px),-50%) rotate(9deg) scale(1.06)}
  50%{transform:translate(calc(-50% - 2px),-50%) rotate(-6deg) scale(1.1)}
  58%{transform:translate(-50%,-50%) rotate(4deg) scale(1.14)}
  70%{transform:translate(-50%,-50%) rotate(-2deg) scale(1)}
  100%{transform:translate(-50%,-50%) rotate(0) scale(1)}
}
.wish-shot.rattling .veil{animation:veilTense .5s ease-in}
@keyframes veilTense{
  0%{transform:scale(1.06)}
  100%{transform:scale(1.14);filter:blur(2px) saturate(1.5) brightness(1.2)}
}

/* --- the POW: veil blasts outward and clears --- */
.wish-shot.blown .veil{animation:veilBlast .72s cubic-bezier(.16,.9,.3,1) forwards}
@keyframes veilBlast{
  0%{transform:scale(1.14);opacity:1;filter:blur(2px) brightness(1.3)}
  22%{transform:scale(1.5);opacity:.85;filter:blur(9px) brightness(1.5)}
  100%{transform:scale(2.5);opacity:0;filter:blur(26px) brightness(1.6)}
}
.wish-shot.blown .veil-lamp{animation:lampRecoil .6s cubic-bezier(.3,1.5,.5,1) forwards}
@keyframes lampRecoil{
  0%{transform:translate(-50%,-50%) scale(1.16) rotate(4deg);opacity:1}
  30%{transform:translate(-50%,-50%) scale(1.5) rotate(-8deg);opacity:.9}
  100%{transform:translate(-50%,-64%) scale(2.4) rotate(-20deg);opacity:0}
}
.wish-shot.blown .veil-label{animation:labelOut .3s ease-out forwards}
@keyframes labelOut{to{opacity:0;transform:translateY(-10px)}}

/* the flash */
.pow-flash{
  position:absolute;inset:0;z-index:6;pointer-events:none;
  background:radial-gradient(circle at 50% 50%,
    rgba(255,255,255,.98) 0 12%,
    rgba(245,196,81,.75) 26%,
    rgba(164,94,229,.45) 44%,
    transparent 66%);
  opacity:0;
}
.wish-shot.blown .pow-flash{animation:powFlash .5s ease-out forwards}
@keyframes powFlash{
  0%{opacity:0;transform:scale(.3)}
  12%{opacity:1;transform:scale(1)}
  100%{opacity:0;transform:scale(1.7)}
}

/* the shockwave ring */
.pow-ring{
  position:absolute;left:50%;top:50%;width:40px;height:40px;margin:-20px 0 0 -20px;
  z-index:6;pointer-events:none;border-radius:50%;
  border:4px solid rgba(255,255,255,.95);
  box-shadow:0 0 26px 8px rgba(164,94,229,.6),inset 0 0 18px rgba(245,196,81,.5);
  opacity:0;
}
.wish-shot.blown .pow-ring{animation:powRing .78s cubic-bezier(.1,.8,.2,1) forwards}
@keyframes powRing{
  0%{opacity:0;transform:scale(.2)}
  10%{opacity:1;transform:scale(.5)}
  100%{opacity:0;transform:scale(6);border-width:1px}
}

/* the photo itself lands with a kick */
.wish-shot.blown img{animation:shotLand .8s cubic-bezier(.2,1.3,.35,1)}
@keyframes shotLand{
  0%{transform:scale(1.3)}
  100%{transform:scale(1)}
}

/* --- WISH GRANTED stamp --- */
.granted-tag{
  position:absolute;left:50%;top:50%;z-index:7;
  transform:translate(-50%,-50%) rotate(-9deg);
  background:linear-gradient(135deg,var(--amethyst),var(--iris-deep));
  color:#fff;border:2px solid rgba(255,255,255,.9);
  border-radius:999px;padding:9px 20px;white-space:nowrap;
  font-family:var(--font-mono);font-weight:700;font-size:.66rem;
  letter-spacing:.18em;text-transform:uppercase;
  box-shadow:0 6px 22px rgba(43,24,64,.55);
  opacity:0;pointer-events:none;
}
.wish-shot.blown .granted-tag{animation:grantedStamp 1.9s cubic-bezier(.34,1.56,.64,1) forwards}
@keyframes grantedStamp{
  0%{opacity:0;transform:translate(-50%,-50%) rotate(-9deg) scale(2.6)}
  16%{opacity:1;transform:translate(-50%,-50%) rotate(-9deg) scale(1)}
  70%{opacity:1;transform:translate(-50%,-50%) rotate(-9deg) scale(1)}
  100%{opacity:0;transform:translate(-50%,-64%) rotate(-9deg) scale(.94)}
}

/* purple-ise the shared particle layer */
.puff{
  background:radial-gradient(circle at 35% 35%,
    rgba(255,255,255,.92),rgba(164,94,229,.6) 52%,transparent 74%);
}
.puff.rose{
  background:radial-gradient(circle at 35% 35%,
    rgba(226,196,255,.95),rgba(91,58,128,.62) 52%,transparent 74%);
}
.spark.rose{background:var(--amethyst)}

/* the hero lamp's plume picks up the iris tone too */
.lamp-smoke{
  background:radial-gradient(circle at 38% 38%,
    rgba(255,255,255,.95),
    rgba(196,150,236,.8) 44%,
    rgba(120,74,170,.5) 70%,
    transparent 80%);
}

@media (prefers-reduced-motion:reduce){
  /* no curtain at all — go straight to the product photo */
  .veil,.veil-lamp,.veil-label,.pow-flash,.pow-ring,.granted-tag{display:none!important}
  .wish-shot.blown img{animation:none}
}
