/* ==================================================================
   Study Hub — Motion layer
   study-hub-motion.css  ·  pairs with js/study-hub-motion.js
   ------------------------------------------------------------------
   One stylesheet, every page. It only ever touches `opacity`,
   `translate`, `scale`, `rotate`, `filter` and `box-shadow`, so the
   pages' own hover rules (which use `transform`) keep working.

   Initial "hidden" states are scoped to `html.shm`, a class the
   script adds only after it has prepared the page — so if JavaScript
   never runs, every page renders exactly as it did before.

   Nothing here animates when the visitor asks for reduced motion.
   ================================================================== */

:root {
  --shm-ease: cubic-bezier(0.19, 1, 0.22, 1);
  --shm-ease-soft: cubic-bezier(0.33, 1, 0.68, 1);
  --shm-accent: #6ea8ff;
  --shm-accent-2: #a78bfa;
  --shm-accent-3: #34d3ee;
  --shm-rise: 26px;
  --shm-slide: 36px;
}

@media (prefers-reduced-motion: no-preference) {
  html.shm {
    scroll-behavior: smooth;
  }
}

/* ---------------------------------------------------------------- */
/* 1. Reveal — every prepared element waits here                      */
/* ---------------------------------------------------------------- */

.shm [data-shm-ready]:not(.shm-in) {
  opacity: 0;
}

.shm [data-shm-ready="down"]:not(.shm-in) {
  translate: 0 calc(-1 * var(--shm-rise));
}

.shm [data-shm-ready="left"]:not(.shm-in) {
  translate: calc(-1 * var(--shm-slide)) 0;
}

.shm [data-shm-ready="right"]:not(.shm-in) {
  translate: var(--shm-slide) 0;
}

.shm [data-shm-ready="zoom"]:not(.shm-in) {
  scale: 0.94;
}

.shm [data-shm-ready="blur"]:not(.shm-in) {
  filter: blur(10px);
}

.shm [data-shm-ready].shm-in {
  animation: shmRise 0.85s var(--shm-ease) both;
  animation-delay: var(--shm-delay, 0ms);
}

.shm [data-shm-ready="down"].shm-in {
  animation-name: shmDrop;
}

.shm [data-shm-ready="left"].shm-in {
  animation-name: shmFromLeft;
}

.shm [data-shm-ready="right"].shm-in {
  animation-name: shmFromRight;
}

.shm [data-shm-ready="zoom"].shm-in {
  animation-name: shmZoom;
}

.shm [data-shm-ready="blur"].shm-in {
  animation-name: shmBlurIn;
}

/* A variant that only fades — used for text that must not move. */
.shm [data-shm-ready="fade"].shm-in {
  animation-name: shmFade;
  animation-duration: 0.7s;
}

/* Wrappers (split headlines, stagger boxes) hold the hidden state for
   their children; they never animate themselves. */
.shm [data-shm-ready="none"].shm-in {
  animation: none;
  opacity: 1;
}

@keyframes shmRise {
  from {
    opacity: 0;
    translate: 0 var(--shm-rise);
  }
  to {
    opacity: 1;
    translate: 0 0;
  }
}

@keyframes shmDrop {
  from {
    opacity: 0;
    translate: 0 calc(-1 * var(--shm-rise));
  }
  to {
    opacity: 1;
    translate: 0 0;
  }
}

@keyframes shmFromLeft {
  from {
    opacity: 0;
    translate: calc(-1 * var(--shm-slide)) 0;
  }
  to {
    opacity: 1;
    translate: 0 0;
  }
}

@keyframes shmFromRight {
  from {
    opacity: 0;
    translate: var(--shm-slide) 0;
  }
  to {
    opacity: 1;
    translate: 0 0;
  }
}

@keyframes shmZoom {
  from {
    opacity: 0;
    scale: 0.94;
  }
  to {
    opacity: 1;
    scale: 1;
  }
}

@keyframes shmBlurIn {
  from {
    opacity: 0;
    filter: blur(10px);
    translate: 0 12px;
  }
  to {
    opacity: 1;
    filter: blur(0);
    translate: 0 0;
  }
}

@keyframes shmFade {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/* ---------------------------------------------------------------- */
/* 2. Split headlines — one word at a time, masked and blurred        */
/* ---------------------------------------------------------------- */

.shm .shm-split {
  /* A split heading is just a wrapper; the words do the work. */
  --shm-word-dur: 0.9s;
}

.shm .shm-w {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  /* Gives descenders (g, y, p) room inside the mask so the line is
     never clipped once the word has landed. */
  padding: 0.16em 0.03em 0.2em;
  margin: -0.16em -0.03em -0.2em;
}

.shm .shm-wi {
  display: inline-block;
  transform: translate3d(0, 118%, 0);
  opacity: 0;
}

.shm .shm-split.shm-in .shm-wi {
  animation: shmWord var(--shm-word-dur) var(--shm-ease) both;
  animation-delay: calc(
    var(--shm-delay, 0ms) + var(--shm-i, 0) * 46ms
  );
}

@keyframes shmWord {
  from {
    transform: translate3d(0, 118%, 0) scale(1.04);
    opacity: 0;
    filter: blur(6px);
  }
  60% {
    filter: blur(0);
  }
  to {
    transform: translate3d(0, 0, 0) scale(1);
    opacity: 1;
    filter: blur(0);
  }
}

/* Character-by-character (data-shm-split="chars") — tighter stagger. */
.shm .shm-split[data-shm-units="chars"] .shm-wi {
  animation-delay: calc(
    var(--shm-delay, 0ms) + var(--shm-i, 0) * 26ms
  );
}

/* The sweep that runs once across a headline as it lands. */
.shm .shm-sheen {
  position: relative;
}

.shm .shm-sheen::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
  background: linear-gradient(
    100deg,
    transparent 35%,
    rgba(255, 255, 255, 0.16) 48%,
    rgba(255, 255, 255, 0.02) 55%,
    transparent 66%
  );
  background-size: 260% 100%;
  background-position: 120% 0;
  opacity: 0;
}

.shm .shm-sheen.shm-in::after {
  animation: shmSheen 1.5s 0.35s var(--shm-ease-soft) 1 both;
}

@keyframes shmSheen {
  0% {
    opacity: 0;
    background-position: 120% 0;
  }
  22% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    background-position: -40% 0;
  }
}

/* ---------------------------------------------------------------- */
/* 3. Stagger containers — cards arrive one after another             */
/* ---------------------------------------------------------------- */

.shm .shm-stagger > * {
  opacity: 0;
}

.shm .shm-stagger.shm-in > * {
  animation: shmStagger 0.8s var(--shm-ease) both;
  animation-delay: calc(var(--shm-delay, 0ms) + var(--shm-i, 0) * 68ms);
}

@keyframes shmStagger {
  from {
    opacity: 0;
    translate: 0 22px;
    scale: 0.985;
  }
  to {
    opacity: 1;
    translate: 0 0;
    scale: 1;
  }
}

/* ---------------------------------------------------------------- */
/* 4. Tilt + pointer glow — desktop pointers only                     */
/* ---------------------------------------------------------------- */

/* The pointer glow is a box-shadow, so it can never clash with a page's
   own `transform` hover. Pages that animate box-shadow themselves keep
   their transition; the script only swaps the value. */
.shm .shm-hover {
  will-change: box-shadow;
}

/* ---------------------------------------------------------------- */
/* 5. Click ripple                                                    */
/* ---------------------------------------------------------------- */

.shm-ripple {
  position: fixed;
  z-index: 9998;
  width: 14px;
  height: 14px;
  margin: -7px 0 0 -7px;
  border-radius: 999px;
  pointer-events: none;
  background: radial-gradient(
    circle,
    rgba(255, 255, 255, 0.55) 0%,
    rgba(255, 255, 255, 0.18) 45%,
    transparent 72%
  );
  animation: shmRipple 0.62s var(--shm-ease-soft) forwards;
}

@keyframes shmRipple {
  from {
    opacity: 0.95;
    scale: 0.2;
  }
  to {
    opacity: 0;
    scale: 7;
  }
}

/* ---------------------------------------------------------------- */
/* 6. Reading progress + ambient pointer glow                         */
/* ---------------------------------------------------------------- */

#shm-progress {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 55;
  height: 3px;
  width: 100%;
  transform-origin: 0 50%;
  scale: var(--shm-progress, 0) 1;
  pointer-events: none;
  background: linear-gradient(
    90deg,
    var(--shm-accent) 0%,
    var(--shm-accent-3) 45%,
    var(--shm-accent-2) 100%
  );
  box-shadow: 0 0 14px -2px rgba(110, 168, 255, 0.6);
  opacity: 0;
  transition: opacity 0.3s ease;
}

#shm-progress.shm-on {
  opacity: 1;
}

#shm-glow {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 3;
  width: 560px;
  height: 560px;
  margin: -280px 0 0 -280px;
  border-radius: 999px;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(
    circle,
    rgba(110, 168, 255, 0.085) 0%,
    rgba(167, 139, 250, 0.045) 38%,
    transparent 68%
  );
  transition: opacity 0.45s ease;
  will-change: translate;
}

#shm-glow.shm-on {
  opacity: 1;
}

@media (hover: none), (pointer: coarse) {
  #shm-glow {
    display: none;
  }
}

/* ---------------------------------------------------------------- */
/* 7. Sticky headers react to scrolling                               */
/* ---------------------------------------------------------------- */

.shm-nav {
  box-shadow: 0 20px 44px -30px rgba(0, 0, 0, 0.9);
}

/* ---------------------------------------------------------------- */
/* 8. Leaving a page                                                  */
/* ---------------------------------------------------------------- */

html.shm-leaving body {
  animation: shmExit 0.26s ease forwards;
  pointer-events: none;
}

@keyframes shmExit {
  from {
    opacity: 1;
    translate: 0 0;
  }
  to {
    opacity: 0;
    translate: 0 -10px;
  }
}

/* ---------------------------------------------------------------- */
/* 9. Reduced motion — everything is simply already in place          */
/* ---------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .shm [data-shm-ready],
  .shm .shm-stagger > *,
  .shm .shm-wi {
    opacity: 1 !important;
    animation: none !important;
    transform: none !important;
    translate: none !important;
    scale: none !important;
    filter: none !important;
  }

  html.shm {
    scroll-behavior: auto;
  }

  #shm-glow {
    display: none;
  }

  html.shm-leaving body {
    animation: none;
  }
}
