/* ==========================================================================
   FINE ART PRODUCTION — Motion Layer
   Page loader, scroll reveals, split-text, parallax, magnetic cursor.
   Every effect degrades gracefully under prefers-reduced-motion.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. PAGE LOADER / CURTAIN
   -------------------------------------------------------------------------- */
.loader {
  position: fixed;
  inset: 0;
  z-index: var(--z-loader);
  display: grid;
  place-items: center;
  background: var(--ink-900);
  color: var(--gold-300);
  /* The curtain rises with a transform, not a clip-path: Chrome counts a
     clip-path change on a viewport-sized element as a layout shift, which
     scored this page 0.89 for a wipe nobody saw move. */
  transition: transform 1s var(--ease-in-out);
  transform: translateY(0);
  will-change: transform;
}
.loader.is-done {
  transform: translateY(-101%);
  pointer-events: none;
}

.loader__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.4rem;
  transition: opacity 0.5s var(--ease-out), transform 0.6s var(--ease-out);
}
.loader.is-done .loader__inner { opacity: 0; transform: translateY(-14px); }

.loader__mark {
  width: 62px;
  height: 62px;
  object-fit: contain;
  border-radius: 50%;
  animation: loaderPulse 2s var(--ease-in-out) infinite;
}
@keyframes loaderPulse {
  0%, 100% { opacity: 0.55; transform: scale(0.94); }
  50% { opacity: 1; transform: scale(1); }
}

.loader__name {
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: 400;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  text-indent: 0.42em;
  color: var(--gold-200);
}

.loader__bar {
  width: 190px;
  height: 1px;
  background: rgba(217, 189, 133, 0.22);
  overflow: hidden;
}
.loader__bar::after {
  content: "";
  display: block;
  width: 100%;
  height: 100%;
  background: var(--gold-300);
  transform: scaleX(var(--load, 0));
  transform-origin: left;
  transition: transform 0.4s var(--ease-out);
}

/* --------------------------------------------------------------------------
   2. SCROLL REVEALS
   Elements start hidden only once JS confirms it is running (html.js),
   so no-JS visitors still see everything.
   -------------------------------------------------------------------------- */
.js [data-reveal] {
  opacity: 0;
  transition: opacity 0.9s var(--ease-out), transform 1s var(--ease-out), clip-path 1.1s var(--ease-out),
    filter 0.9s var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}

/* Default: rise */
.js [data-reveal] { transform: translateY(34px); }

/* Variants */
.js [data-reveal="fade"] { transform: none; }
.js [data-reveal="left"] { transform: translateX(-46px); }
.js [data-reveal="right"] { transform: translateX(46px); }
.js [data-reveal="zoom"] { transform: scale(0.94); }
.js [data-reveal="blur"] { transform: translateY(20px); filter: blur(9px); }

/* Wipe variants clip their CHILD, never themselves.
   Clipping the observed element to zero height makes its intersection area
   zero, so IntersectionObserver never reports it as visible and it stays
   hidden forever. Keeping the element's own box intact avoids that deadlock. */
.js [data-reveal="wipe"],
.js [data-reveal="wipe-x"] {
  opacity: 1;
  transform: none;
  clip-path: none;
}
.js [data-reveal="wipe"] > *,
.js [data-reveal="wipe-x"] > * {
  transition: clip-path 1.1s var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}
.js [data-reveal="wipe"] > * { clip-path: inset(0 0 100% 0); }
.js [data-reveal="wipe-x"] > * { clip-path: inset(0 100% 0 0); }
.js [data-reveal="wipe"].is-in > *,
.js [data-reveal="wipe-x"].is-in > * { clip-path: inset(0 0 0 0); }

/* Revealed state */
.js [data-reveal].is-in {
  opacity: 1;
  transform: none;
  filter: none;
}

/* Staggered children */
.js [data-stagger] > * {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.8s var(--ease-out), transform 0.9s var(--ease-out);
}
.js [data-stagger].is-in > * { opacity: 1; transform: none; }
.js [data-stagger].is-in > *:nth-child(1) { transition-delay: 0ms; }
.js [data-stagger].is-in > *:nth-child(2) { transition-delay: 90ms; }
.js [data-stagger].is-in > *:nth-child(3) { transition-delay: 180ms; }
.js [data-stagger].is-in > *:nth-child(4) { transition-delay: 270ms; }
.js [data-stagger].is-in > *:nth-child(5) { transition-delay: 360ms; }
.js [data-stagger].is-in > *:nth-child(6) { transition-delay: 450ms; }
.js [data-stagger].is-in > *:nth-child(7) { transition-delay: 540ms; }
.js [data-stagger].is-in > *:nth-child(8) { transition-delay: 630ms; }
.js [data-stagger].is-in > *:nth-child(n + 9) { transition-delay: 700ms; }

/* --------------------------------------------------------------------------
   3. SPLIT TEXT (word-by-word headline reveal)
   -------------------------------------------------------------------------- */
/* overflow:hidden is what lets the words slide up from nothing — but it also
   clips descenders (the g in "light"). The padding gives them room back, and
   the negative margin keeps the line box the same height. */
.split-line {
  display: block;
  overflow: hidden;
  padding-bottom: 0.2em;
  margin-bottom: -0.2em;
}

.split-word {
  display: inline-block;
  transform: translateY(105%) rotate(3deg);
  opacity: 0;
  transition: transform 1.05s var(--ease-out), opacity 0.8s var(--ease-out);
  transition-delay: calc(var(--w, 0) * 55ms);
  will-change: transform;
}
.is-in .split-word,
.split-ready .split-word { transform: none; opacity: 1; }

/* The wrapper keeps a literal space between words, so no extra margin here —
   adding one would double the gap. */

/* --------------------------------------------------------------------------
   4. PARALLAX & SCROLL-LINKED
   -------------------------------------------------------------------------- */
[data-parallax] {
  will-change: transform;
  transition: transform 0.15s linear;
}

/* Hero slideshow — only the active still is visible, and only it drifts */
.hero-slides { position: absolute; inset: 0; }
.hero-slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 1.6s var(--ease-in-out);
}
.hero-slide.is-active { opacity: 1; }
.hero-slide img { width: 100%; height: 100%; object-fit: cover; }

.hero-slide.is-active img {
  animation: kenBurns 14s var(--ease-in-out) forwards;
}
@keyframes kenBurns {
  from { transform: scale(1.06) translate3d(0, 0, 0); }
  to { transform: scale(1.16) translate3d(-1.6%, -1.4%, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .hero-slide.is-active img { animation: none; transform: none; }
  .hero-slide { transition: none; }
}

/* Image mask reveal for editorial media */
.js .media[data-reveal="wipe"] img { transform: scale(1.14); transition: transform 1.4s var(--ease-out); }
.js .media[data-reveal="wipe"].is-in img { transform: scale(1); }

/* --------------------------------------------------------------------------
   5. MAGNETIC CURSOR (desktop, fine pointer only)
   -------------------------------------------------------------------------- */
.cursor,
.cursor-dot {
  position: fixed;
  top: 0;
  left: 0;
  z-index: var(--z-cursor);
  pointer-events: none;
  border-radius: 50%;
  opacity: 0;
  transition: opacity 0.3s var(--ease-out), width 0.35s var(--ease-out), height 0.35s var(--ease-out),
    background-color 0.35s var(--ease-out), border-color 0.35s var(--ease-out);
  mix-blend-mode: difference;
}

.cursor {
  width: 34px;
  height: 34px;
  border: 1px solid rgba(255, 255, 255, 0.8);
  transform: translate3d(-50%, -50%, 0);
}
.cursor-dot {
  width: 5px;
  height: 5px;
  background: #fff;
  transform: translate3d(-50%, -50%, 0);
}

body.cursor-ready .cursor,
body.cursor-ready .cursor-dot { opacity: 1; }

body.cursor-hover .cursor {
  width: 66px;
  height: 66px;
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.95);
}
body.cursor-hover .cursor-dot { opacity: 0; }

body.cursor-zoom .cursor {
  width: 82px;
  height: 82px;
  background: rgba(255, 255, 255, 0.14);
  border-color: transparent;
}
body.cursor-zoom .cursor::after {
  content: "VIEW";
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-family: var(--font-sans);
  font-size: 0.56rem;
  font-weight: 500;
  letter-spacing: 0.24em;
  color: #fff;
}

@media (hover: none), (pointer: coarse) {
  .cursor, .cursor-dot { display: none; }
}

/* Magnetic pull target */
[data-magnetic] {
  transition: transform 0.35s var(--ease-out);
  will-change: transform;
}

/* --------------------------------------------------------------------------
   6. THEME SWITCH TRANSITION
   -------------------------------------------------------------------------- */
.theme-ripple {
  position: fixed;
  z-index: 8500;
  border-radius: 50%;
  pointer-events: none;
  transform: translate(-50%, -50%) scale(0);
  animation: themeRipple 0.75s var(--ease-in-out) forwards;
}
@keyframes themeRipple {
  to { transform: translate(-50%, -50%) scale(1); opacity: 0; }
}

/* --------------------------------------------------------------------------
   7. MICRO-ANIMATIONS
   -------------------------------------------------------------------------- */
@keyframes floatY {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-12px); }
}
.float { animation: floatY 6s var(--ease-in-out) infinite; }

@keyframes shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}
.shimmer-text {
  background: linear-gradient(90deg, var(--text) 25%, var(--accent) 50%, var(--text) 75%);
  background-size: 200% auto;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: shimmer 5s linear infinite;
}

@keyframes spinSlow { to { transform: rotate(360deg); } }
.spin-badge {
  animation: spinSlow 22s linear infinite;
  transform-origin: center;
}

/* Counter roll-in */
.counter { display: inline-block; font-variant-numeric: tabular-nums; }

/* Lazy image fade */
img[loading="lazy"] { background: var(--bg-alt); }
.img-fade { opacity: 0; transition: opacity 0.7s var(--ease-out); }
.img-fade.is-loaded { opacity: 1; }

/* Hover lift utility */
.lift { transition: transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out); }
.lift:hover { transform: translateY(-6px); box-shadow: var(--shadow-lg); }

/* Underline sweep for inline links inside prose */
.prose-link {
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 0% 1px;
  background-repeat: no-repeat;
  background-position: 0 100%;
  transition: background-size 0.45s var(--ease-out);
  color: var(--accent);
}
.prose-link:hover { background-size: 100% 1px; }

/* --------------------------------------------------------------------------
   8. REDUCED MOTION — flatten everything
   -------------------------------------------------------------------------- */
@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-reveal],
  .js [data-stagger] > * {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    clip-path: none !important;
  }

  .js [data-reveal="wipe"] > *,
  .js [data-reveal="wipe-x"] > * { clip-path: none !important; }

  .split-word { opacity: 1 !important; transform: none !important; }
  .hero__media img, .hero__media video { animation: none; transform: none; }
  .cursor, .cursor-dot { display: none; }
  .marquee__track { animation: none; }
  .grain { display: none; }
}
