/* ==========================================================================
   Hadiya - motion layer

   The static design system (wobbly borders, tape, shadows) lives in the
   inline <style> block in index.html. This file owns movement only.

   THE js-anim CONTRACT
   Everything that starts hidden is scoped to html.js-anim. That class is
   added synchronously by a small inline script in <head>, and removed again
   by a watchdog in that same script if animations.js has not reported in
   within 2.5s - which is what happens when the anime.js CDN is slow or
   blocked. Under prefers-reduced-motion the class is never added at all.

   The consequence, and the reason it is built this way: with no JS, a failed
   CDN, or reduced motion, none of the rules below apply and the page renders
   completely visible. Never move an initial hidden state out of html.js-anim.

   TRANSFORMS ARE OWNED BY JS
   Cards carry a Tailwind `rotate-1` / `-rotate-2` that must survive. If CSS
   set a transform here it would clobber that rotation, so CSS only ever sets
   opacity; animations.js reads each element's real rotation from its computed
   style and animates around it.
   ========================================================================== */

/* --- Initial states, only once JS is confirmed --------------------------- */

html.js-anim [data-anim],
html.js-anim [data-reveal],
html.js-anim [data-reveal-heading],
html.js-anim .sketch,
html.js-anim .tape,
html.js-anim .tape-corner,
html.js-anim .thumbtack {
  opacity: 0;
}

html.js-anim [data-anim],
html.js-anim [data-reveal] {
  will-change: transform, opacity;
}

/* The curtain only exists as a visible thing while js-anim is on. */
.curtain {
  display: none;
}

html.js-anim .curtain {
  display: flex;
  position: fixed;
  inset: 0;
  z-index: 9999;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

/* The paper is its own layer so it can clear on its own while the logo above
   it stays fully opaque all the way to its seat in the nav. Fading .curtain
   itself would take the logo down with it. */
.curtain-bg {
  position: absolute;
  inset: 0;
  background-color: #fdfbf7;
  background-image: radial-gradient(#e5e0d8 1px, transparent 1px);
  background-size: 24px 24px;
}

/* Starts hidden because animations.js may wait a beat for the image to load
   before it measures - opacity is handed over to paint() from the first
   frame. transform-origin must stay centred: the flight delta is computed
   centre-to-centre, and moving the origin would offset the landing. */
.curtain img {
  position: relative;
  z-index: 1;
  width: 180px;
  max-width: 45vw;
  height: auto;
  opacity: 0;
  filter: invert(0.82);
  transform-origin: 50% 50%;
  will-change: transform, opacity;
}

/* --- Scribble underlines draw themselves ---------------------------------
   The underline is a background image on ::after, which JS cannot target,
   so the draw is a CSS keyframe on the pseudo-element and JS only adds the
   .is-drawn class that starts it. */

html.js-anim .scribble-underline::after {
  width: 0;
}

html.js-anim .scribble-underline.is-drawn::after {
  animation: scribble-draw 0.55s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}

@keyframes scribble-draw {
  from {
    width: 0;
  }
  to {
    width: 100%;
  }
}

/* --- Pencil trail canvas ------------------------------------------------- */

.ink-trail {
  position: fixed;
  inset: 0;
  z-index: 9998;
  pointer-events: none;
  display: none;
}

html.js-anim .ink-trail {
  display: block;
}

/* --- Ink-splat ripple, spawned at the click point ------------------------ */

.ink-splat {
  position: fixed;
  z-index: 9997;
  pointer-events: none;
  width: 14px;
  height: 14px;
  margin: -7px 0 0 -7px;
  border-radius: 60% 40% 55% 45% / 45% 55% 40% 60%;
  background-color: #ff4d4d;
  opacity: 0.65;
}

/* --- Hover: paper lifting off a desk ------------------------------------- */
/* Tailwind's hover:-translate-y-2 handles the lift on cards. This adds the
   shadow growth, which transform utilities cannot express. */

#services article,
#engagement .shadow-hard {
  transition: box-shadow 0.18s ease-out;
}

#services article:hover {
  box-shadow: 8px 9px 0 0 #2d2d2d !important;
}

/* Tape flutters while its card is hovered. Uses rotate only via a keyframe
   that restates the element's own base transform, so it cannot drift. */

#services article:hover .tape {
  animation: tape-flutter 1.6s ease-in-out infinite;
}

@keyframes tape-flutter {
  0%,
  100% {
    transform: translateX(-50%) rotate(-3deg);
  }
  50% {
    transform: translateX(-50%) rotate(-6deg);
  }
}

/* --- Nav hide on scroll down, return on scroll up ------------------------ */

nav {
  transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}

nav.nav-tucked {
  transform: translateY(-130%);
}

/* --- Magnetic buttons ---------------------------------------------------- */
/* JS writes the offset; the easing back to rest is CSS so releasing feels
   springy without another animation frame loop. */

.magnetic {
  transition: transform 0.25s cubic-bezier(0.2, 0.9, 0.3, 1.4);
}

.magnetic.is-pulled {
  transition: transform 0.08s linear;
}

/* --- Reduced motion: stand everything down ------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html.js-anim [data-anim],
  html.js-anim [data-reveal],
  html.js-anim [data-reveal-heading],
  html.js-anim .sketch,
  html.js-anim .tape,
  html.js-anim .tape-corner,
  html.js-anim .thumbtack {
    opacity: 1 !important;
  }

  html.js-anim .curtain,
  .ink-trail,
  .ink-splat {
    display: none !important;
  }

  html.js-anim .scribble-underline::after,
  html.js-anim .scribble-underline.is-drawn::after {
    width: 100% !important;
    animation: none !important;
  }

  #services article:hover .tape {
    animation: none !important;
  }

  nav,
  .magnetic {
    transition: none !important;
  }

  nav.nav-tucked {
    transform: none !important;
  }
}
