/* ==========================================================================
   Motion below the hero: blur-to-focus reveals and four small suit moments.
   Driven by js/motion.js. Every hidden state is gated on html.js.motion-ready
   (added only once the observer exists) and on no-preference for motion, so
   the page reads fully with JS off, on a JS error, or with reduced motion.
   Nothing here touches the hero or the hero-to-Magic transition.
   ========================================================================== */

:root {
  /* Text reveal */
  --m-blur: 5px;
  --m-rise: 12px;
  --m-dur: 560ms;
  --m-stagger: 70ms;
  --m-ease: var(--ease-out, cubic-bezier(0.2, 0.7, 0.2, 1));

  /* Suit moments */
  --m-suit-dur: 720ms;
  --m-suit-gap: 110ms; /* read by motion.js: spacing between suits arriving */
  --m-ink: #f5efe4;
}

@media (max-width: 599.98px) {
  :root {
    --m-rise: 10px;
    --m-dur: 500ms;
    --m-stagger: 45ms;
    --m-suit-gap: 80ms;
  }
}

/* --------------------------------------------------------------------------
   Blur-to-focus text reveal (js adds [data-m], then .is-in once, never removed)
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  html.js.motion-ready [data-m]:not(.is-in) {
    opacity: 0;
    filter: blur(var(--m-blur));
    transform: translateY(var(--m-rise));
  }

  html.js.motion-ready [data-m] {
    transition:
      opacity var(--m-dur) var(--m-ease),
      filter var(--m-dur) var(--m-ease),
      transform var(--m-dur) var(--m-ease);
    transition-delay: calc(var(--m-i, 0) * var(--m-stagger));
  }

  /* The portrait: a soft fade and a very small lift. No blur on the face. */
  html.js.motion-ready [data-m="portrait"]:not(.is-in) {
    filter: none;
    transform: translateY(8px);
  }

  html.js.motion-ready [data-m="portrait"] {
    transition:
      opacity 900ms var(--m-ease),
      transform 900ms var(--m-ease);
  }

  /* Arrived with the card transition (The Magic intro): media-rise already
     animates the block, so the text simply appears with it. */
  html.js.motion-ready [data-m].m-now {
    transition: none;
  }
}

/* --------------------------------------------------------------------------
   Suits (shared)
   -------------------------------------------------------------------------- */

.m-deco {
  position: absolute;
  pointer-events: none;
  user-select: none;
}

.m-deco svg {
  display: block;
  overflow: visible;
}

.m-suit {
  display: block;
  width: 100%;
  height: 100%;
}

/* Settled pose is the default, so reduced motion and a missed observer both
   show the finished graphic. Motion only adds the "before" state. */
@media (prefers-reduced-motion: no-preference) {
  html.js.motion-ready .m-piece:not(.is-in) {
    opacity: 0;
    filter: blur(4px);
    transform: translate(var(--from-x, 0), var(--from-y, 10px)) rotate(var(--from-r, 0deg)) scale(0.82);
  }

  html.js.motion-ready .m-piece {
    transition:
      opacity var(--m-suit-dur) var(--m-ease),
      filter var(--m-suit-dur) var(--m-ease),
      transform var(--m-suit-dur) var(--m-ease);
  }
}

/* --------------------------------------------------------------------------
   1. The Magic: a small cluster assembles below the CTA, in the bottom padding.
   Appended as the LAST child of .media so the nth-child delays on the real
   children are unchanged; kept out of media-rise and out of the width rule.
   -------------------------------------------------------------------------- */

.media {
  position: relative; /* anchor for the cluster only; no geometry change */
}

.media > .m-cluster,
.media.is-revealed > .m-cluster {
  max-width: none;
  margin: 0;
  animation: none;
}

.m-cluster {
  --s: clamp(20px, 1.2rem + 0.6vw, 30px);
  right: max(var(--gutter), (100% - 1320px) / 2);
  bottom: clamp(1.1rem, 4.5vw, 3rem);
  width: calc(var(--s) * 3.4);
  height: calc(var(--s) * 2.3);
}

.m-cluster .m-piece {
  position: absolute;
  width: var(--s);
  height: var(--s);
  transform: rotate(var(--r, 0deg));
}

/* Spade, heart, club, diamond: loosely fanned, as if just dropped on the table. */
.m-cluster .m-piece:nth-child(1) { left: 0;     top: 10%;  --r: -14deg; --from-x: -18px; --from-y: -10px; --from-r: -34deg; }
.m-cluster .m-piece:nth-child(2) { left: 30%;   top: 0;    --r: 6deg;   --from-x: 4px;   --from-y: -18px; --from-r: 24deg; }
.m-cluster .m-piece:nth-child(3) { left: 14%;   top: 52%;  --r: 10deg;  --from-x: -12px; --from-y: 16px;  --from-r: 30deg; }
.m-cluster .m-piece:nth-child(4) { left: 56%;   top: 36%;  --r: 18deg;  --from-x: 18px;  --from-y: 8px;   --from-r: 40deg; }

@media (max-width: 599.98px) {
  .m-cluster {
    --s: 18px;
    bottom: 1.1rem;
  }
}

/* --------------------------------------------------------------------------
   2. About: a suit beside the eyebrow turns edge-on and shows a different suit.
   -------------------------------------------------------------------------- */

.m-flip {
  position: relative;
  display: inline-block;
  width: 1.4em;
  height: 1em;
  margin-left: 0.75em;
  vertical-align: -0.1em;
  perspective: 240px;
  pointer-events: none;
}

.m-flip__card {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 20px;
  height: 20px;
  margin: -10px 0 0 -10px;
  transform-style: preserve-3d;
  /* Settled: showing the second suit. */
  transform: rotateY(180deg);
}

.m-flip__face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}

.m-flip__face--back {
  transform: rotateY(180deg);
}

@media (prefers-reduced-motion: no-preference) {
  /* Shows the first suit until the turn; .is-settled skips straight to the end. */
  html.js.motion-ready .m-flip:not(.is-turned, .is-settled) .m-flip__card {
    transform: rotateY(0deg);
  }

  html.js.motion-ready .m-flip.is-turned .m-flip__card {
    animation: m-turn 1100ms var(--m-ease) both;
  }
}

/* Anticipation (a slight lean back), the turn through edge-on, a small settle. */
@keyframes m-turn {
  0% { transform: rotateY(0deg); }
  20% { transform: rotateY(-16deg); animation-timing-function: cubic-bezier(0.55, 0, 0.35, 1); }
  72% { transform: rotateY(190deg); animation-timing-function: cubic-bezier(0.3, 0, 0.3, 1); }
  100% { transform: rotateY(180deg); }
}

/* --------------------------------------------------------------------------
   3. Reviews: the five stars arrive one after another as each panel enters.
   Visible by default; only hidden once motion is armed and before .is-in.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  html.js.motion-ready .review__stars[data-m-stars]:not(.is-in) .star {
    opacity: 0;
    transform: scale(0.5);
  }

  html.js.motion-ready .review__stars.is-in .star {
    animation: m-star 520ms var(--m-ease) calc(var(--n, 0) * var(--m-star-gap, 90ms)) both;
  }
}

.review__stars .star:nth-child(2) { --n: 1; }
.review__stars .star:nth-child(3) { --n: 2; }
.review__stars .star:nth-child(4) { --n: 3; }
.review__stars .star:nth-child(5) { --n: 4; }

@media (max-width: 599.98px) {
  .review__stars {
    --m-star-gap: 70ms;
  }
}

@keyframes m-star {
  0% { opacity: 0; transform: scale(0.5); filter: blur(2px); }
  55% { opacity: 1; transform: scale(1.08); filter: blur(0) drop-shadow(0 0 5px rgba(240, 210, 143, 0.6)); }
  100% { opacity: 1; transform: scale(1); filter: blur(0) drop-shadow(0 0 0 rgba(240, 210, 143, 0)); }
}

/* --------------------------------------------------------------------------
   4. Invitation: a row of four suits resolves above the heading.
   The wrapper has no height, so the heading does not move.
   -------------------------------------------------------------------------- */

.m-row-anchor {
  position: relative;
  height: 0;
  margin: 0;
  pointer-events: none;
}

.m-row {
  --s: 16px;
  position: absolute;
  bottom: clamp(1rem, 2.5vw, 1.6rem);
  display: flex;
  gap: calc(var(--s) * 0.9);
}

.m-row--center {
  left: 50%;
  transform: translateX(-50%);
}

.m-row .m-piece {
  width: var(--s);
  height: var(--s);
}

@media (min-width: 900px) {
  .m-row {
    --s: 20px;
  }
}

/* --------------------------------------------------------------------------
   Printing: everything visible and still.
   -------------------------------------------------------------------------- */

@media print {
  [data-m],
  .m-piece,
  .m-flip,
  .review__stars .star {
    opacity: 1 !important;
    filter: none !important;
    transform: none !important;
  }
}
