/* ==========================================================================
   AppanDesign — Animations
   Keyframes plus the state classes that motion.js toggles.
   Every effect degrades to "no motion" under prefers-reduced-motion.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Keyframes
   -------------------------------------------------------------------------- */
@keyframes ad-marquee {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

@keyframes ad-scrollcue {
  0%   { transform: translateY(0); opacity: 0; }
  20%  { opacity: 1; }
  100% { transform: translateY(26px); opacity: 0; }
}

@keyframes ad-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

@keyframes ad-grain {
  0%   { transform: translate(0, 0); }
  25%  { transform: translate(-3%, 2%); }
  50%  { transform: translate(2%, -3%); }
  75%  { transform: translate(-2%, -2%); }
  100% { transform: translate(0, 0); }
}

@keyframes ad-fade-up {
  from { opacity: 0; transform: translateY(30px); }
  to   { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   2. Scroll reveal
   Elements marked [data-r] start hidden and are released by IntersectionObserver.
   The stagger index is written to --r-delay by motion.js.
   -------------------------------------------------------------------------- */
.js [data-r] {
  opacity: 0;
  transform: translateY(30px);
  transition:
    opacity 1s var(--ease) var(--r-delay, 0ms),
    transform 1.15s var(--ease) var(--r-delay, 0ms);
}
.js [data-r].is-visible {
  opacity: 1;
  transform: none;
}
/* Technology chips: a quick, sequential pop-in instead of the long slide. */
.js .chip[data-r] {
  transform: translateY(14px) scale(0.9);
  transition:
    opacity 0.55s var(--ease) var(--r-delay, 0ms),
    transform 0.65s cubic-bezier(0.34, 1.4, 0.64, 1) var(--r-delay, 0ms),
    box-shadow var(--duration-base) var(--ease),
    color var(--duration-base) var(--ease);
}
.js .chip[data-r].is-visible { transform: none; }

/* --------------------------------------------------------------------------
   3. Hero headline — words rise out of their own mask
   -------------------------------------------------------------------------- */
.word {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  padding-right: 0.26em;
}
.word__in {
  display: inline-block;
  transform: translateY(105%);
  transition: transform 1.15s var(--ease) var(--w-delay, 0ms);
}
.word__in.is-in { transform: none; }
/* Zero-width real space between words: text reads correctly, layout unchanged. */
.word__gap { font-size: 0; line-height: 0; }

/* --------------------------------------------------------------------------
   4. Clip-path image wipe
   -------------------------------------------------------------------------- */
/* The clip lives on the frame's children, not the observed frame itself:
   Chrome reports a fully clipped target as 0% intersecting, so a clipped
   frame would never be released by the IntersectionObserver. */
.js [data-mask] > * {
  clip-path: inset(0 0 100% 0);
  transition: clip-path 1.4s var(--ease);
}
.js [data-mask].is-visible > * { clip-path: inset(0 0 0 0); }

/* --------------------------------------------------------------------------
   5. Hairline draw-in (footer, contact rail)
   -------------------------------------------------------------------------- */
.js [data-line] { width: 0; }
.js [data-line].is-visible { width: 100%; }

/* --------------------------------------------------------------------------
   6. Pointer-driven layers
   motion.js writes the transforms; these declare the surfaces.
   -------------------------------------------------------------------------- */
[data-px],
[data-magnet],
[data-rot] { will-change: transform; }

[data-tilt] {
  transform-style: preserve-3d;
  transition: transform 0.9s var(--ease);
}

[data-light] {
  position: absolute;
  inset: 0;
  pointer-events: none;
  transition: transform 1.4s var(--ease);
}
.glow-center { background: radial-gradient(45% 45% at 50% 50%, rgba(236, 176, 20, 0.16), transparent 70%); }
.glow-soft   { background: radial-gradient(45% 45% at 50% 50%, rgba(236, 176, 20, 0.14), transparent 70%); }
.glow-right  { background: radial-gradient(40% 40% at 70% 40%, rgba(236, 176, 20, 0.10), transparent 70%); }
.glow-corner { background: radial-gradient(40% 40% at 75% 30%, rgba(236, 176, 20, 0.12), transparent 70%); }

/* Slowly counter-rotating rings behind the monogram */
.ring {
  position: absolute;
  inset: 0;
  border: 1px solid var(--border-hair);
  border-radius: 50%;
}
.ring--diamond { inset: 12%; border-color: var(--amber); border-radius: 0; transform: rotate(45deg); }
.ring--square  { inset: 26%; border-radius: 0; }
.monogram-wrap {
  position: relative;
  width: clamp(200px, 24vw, 360px);
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  transform-style: preserve-3d;
  transition: transform 0.9s var(--ease);
}
.monogram-wrap--lg { width: clamp(220px, 26vw, 380px); }
.monogram-mark {
  position: relative;
  display: block;
  width: 46%;
  color: var(--text-heading);   /* the inlined mark paints with currentColor */
  transform: translateZ(60px);
  filter: drop-shadow(0 24px 40px rgba(15, 15, 15, 0.18));
}
.monogram-mark svg { width: 100%; height: auto; display: block; }
.monogram-text {
  position: relative;
  font-family: var(--font-display);
  font-weight: var(--weight-semibold);
  font-size: clamp(72px, 9vw, 168px);
  line-height: 1;
  letter-spacing: var(--track-tight);
  color: var(--text-heading);
}

/* --------------------------------------------------------------------------
   7. Texture overlays
   -------------------------------------------------------------------------- */
.grain {
  position: absolute;
  inset: -30%;
  pointer-events: none;
  opacity: 0.16;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/></filter><rect width='160' height='160' filter='url(%23n)' opacity='0.5'/></svg>");
  animation: ad-grain 7s steps(4) infinite;
}

.is-dark .gridlines { --gridline-color: rgba(255, 255, 255, 0.07); }
.gridlines {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.5;
  background-image:
    linear-gradient(var(--gridline-color, rgba(15, 15, 15, 0.06)) 1px, transparent 1px),
    linear-gradient(90deg, var(--gridline-color, rgba(15, 15, 15, 0.06)) 1px, transparent 1px);
  background-size: clamp(60px, 7vw, 110px) clamp(60px, 7vw, 110px);
}

.glow-pool {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(60% 50% at 50% 45%, rgba(236, 176, 20, 0.12), transparent 70%);
}


/* --------------------------------------------------------------------------
   8. Portfolio filtering
   -------------------------------------------------------------------------- */
[data-item] {
  transition: opacity 0.6s var(--ease), transform 0.7s var(--ease);
}
[data-item].is-hidden {
  opacity: 0;
  transform: scale(0.97);
}

/* --------------------------------------------------------------------------
   9. Reduced motion — strip everything back to instant
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .js [data-r],
  .js [data-mask],
  .js [data-mask] > * {
    opacity: 1;
    transform: none;
    clip-path: none;
  }
  .word__in { transform: none; }
  [data-line] { width: 100%; }
  .grain { animation: none; }
}
