/* ==========================================================================
   KUMA — MOTION
   animations.css : keyframes · scroll reveals · marquees · spin/loop systems
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. KEYFRAMES
   -------------------------------------------------------------------------- */
@keyframes k-spin {
  to {
    transform: rotate(360deg);
  }
}

@keyframes k-spin-rev {
  to {
    transform: rotate(-360deg);
  }
}

/* Each track slides exactly its own width; JS clones the track so the
   second copy takes over seamlessly at the hand-off point. */
@keyframes k-marquee {
  from {
    transform: translate3d(0, 0, 0);
  }
  to {
    transform: translate3d(-100%, 0, 0);
  }
}

@keyframes k-marquee-rev {
  from {
    transform: translate3d(-100%, 0, 0);
  }
  to {
    transform: translate3d(0, 0, 0);
  }
}

@keyframes k-fade-up {
  from {
    opacity: 0;
    transform: translate3d(0, 1.25rem, 0);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes k-blink {
  0%,
  45% {
    opacity: 1;
  }
  50%,
  95% {
    opacity: 0.15;
  }
  100% {
    opacity: 1;
  }
}

@keyframes k-pulse-ring {
  0% {
    transform: scale(0.85);
    opacity: 0.75;
  }
  100% {
    transform: scale(1.5);
    opacity: 0;
  }
}

@keyframes k-ray-sweep {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

@keyframes k-eq {
  0%,
  100% {
    transform: scaleY(0.22);
  }
  50% {
    transform: scaleY(1);
  }
}

@keyframes k-float {
  0%,
  100% {
    transform: translate3d(0, 0, 0);
  }
  50% {
    transform: translate3d(0, -0.9rem, 0);
  }
}

@keyframes k-scroll-cue {
  0% {
    transform: translateY(-100%);
  }
  50% {
    transform: translateY(0);
  }
  100% {
    transform: translateY(100%);
  }
}

@keyframes k-glitch-a {
  0%,
  92%,
  100% {
    transform: translate3d(0, 0, 0);
    opacity: 0;
  }
  93% {
    transform: translate3d(-6px, 2px, 0);
    opacity: 0.85;
  }
  96% {
    transform: translate3d(5px, -3px, 0);
    opacity: 0.85;
  }
}

@keyframes k-glitch-b {
  0%,
  90%,
  100% {
    transform: translate3d(0, 0, 0);
    opacity: 0;
  }
  91% {
    transform: translate3d(7px, -2px, 0);
    opacity: 0.7;
  }
  95% {
    transform: translate3d(-4px, 3px, 0);
    opacity: 0.7;
  }
}

@keyframes k-wipe-in {
  from {
    clip-path: inset(0 100% 0 0);
  }
  to {
    clip-path: inset(0 0 0 0);
  }
}

@keyframes k-wipe-out {
  from {
    clip-path: inset(0 0 0 0);
  }
  to {
    clip-path: inset(0 0 0 100%);
  }
}

@keyframes k-count-pop {
  from {
    transform: translate3d(0, 0.7em, 0);
    opacity: 0;
  }
  to {
    transform: none;
    opacity: 1;
  }
}

@keyframes k-stamp {
  from {
    transform: scale(1.45) rotate(-6deg);
    opacity: 0;
  }
  to {
    transform: scale(1) rotate(0);
    opacity: 1;
  }
}

/* --------------------------------------------------------------------------
   2. SCROLL REVEAL SYSTEM
   Elements start hidden only when JS is present & motion is allowed.

   NOTE ON clip-path REVEALS: Chromium treats an element's own clip-path as
   clipping for IntersectionObserver, so an element that clips itself to
   `inset(… 100% …)` reports an empty intersection rect and never fires `is-in`.
   Apply the `clip*` variants to a CHILD of the observed element, or use the
   `mask-up` variant (hidden by an ancestor's overflow instead).
   -------------------------------------------------------------------------- */
.js .reveal {
  opacity: 0;
  will-change: transform, opacity;
}

/* :where() keeps these variant selectors at the same specificity as
   `.reveal.is-in`, so the revealed state (later in the file) actually wins.
   Without it the offsets below would survive the reveal and every section
   would sit permanently nudged off its grid. */
.js .reveal:where([data-reveal="up"]) {
  transform: translate3d(0, 2.5rem, 0);
}

.js .reveal:where([data-reveal="down"]) {
  transform: translate3d(0, -2rem, 0);
}

.js .reveal:where([data-reveal="left"]) {
  transform: translate3d(-3rem, 0, 0);
}

.js .reveal:where([data-reveal="right"]) {
  transform: translate3d(3rem, 0, 0);
}

.js .reveal:where([data-reveal="scale"]) {
  transform: scale(0.9);
}

.js .reveal:where([data-reveal="clip"]) {
  opacity: 1;
  clip-path: inset(0 100% 0 0);
}

.js .reveal:where([data-reveal="clip-up"]) {
  opacity: 1;
  clip-path: inset(100% 0 0 0);
}

.js .reveal:where([data-reveal="clip-center"]) {
  opacity: 1;
  clip-path: inset(50% 0 50% 0);
}

.js .reveal:where([data-reveal="stamp"]) {
  opacity: 0;
  transform: scale(1.5) rotate(-7deg);
}

/* Mask reveal — the OBSERVED element must never be the clipped one, or
   IntersectionObserver sees an empty rect and the reveal never fires.
   So the host is observed and its child does the sliding. The host's own
   opacity stays 1; only the child moves. */
.js .reveal:where([data-reveal="mask"]) {
  opacity: 1;
}

.js .reveal:where([data-reveal="mask"]) > * {
  display: block;
  transform: translate3d(0, 102%, 0);
  will-change: transform;
}

.reveal.is-in:where([data-reveal="mask"]) > * {
  transform: none;
  transition: transform 1.15s var(--e-out);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: auto;
}

.reveal.is-in {
  opacity: 1;
  transform: none;
  transition: opacity var(--d-reveal) var(--e-out),
    transform var(--d-reveal) var(--e-out),
    clip-path var(--d-reveal) var(--e-out);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: auto;
}

/* Only clip-variant reveals get a clip-path in the revealed state. Applying
   `inset(0)` to every reveal would silently chop off anything a child
   overhangs by — badges, hard shadows — and would also make each section a
   containing block for fixed-position descendants. */
.reveal.is-in:where([data-reveal^="clip"]) {
  clip-path: inset(0 0 0 0);
}

/* staggered children */
.js .reveal-stagger > * {
  opacity: 0;
  transform: translate3d(0, 1.75rem, 0);
  will-change: transform, opacity;
}

.reveal-stagger.is-in > * {
  opacity: 1;
  transform: none;
  transition: opacity 0.7s var(--e-out), transform 0.7s var(--e-out);
  transition-delay: calc(var(--i, 0) * var(--stagger, 70ms));
}

/* per-letter headline reveal */
.js .split-line {
  display: block;
  overflow: hidden;
}

.js .split-line > span {
  display: block;
  transform: translate3d(0, 105%, 0);
  transition: transform 1.05s var(--e-out);
  transition-delay: calc(var(--i, 0) * 85ms);
}

.js .split-line.is-in > span {
  transform: none;
}

/* --------------------------------------------------------------------------
   3. MARQUEE
   -------------------------------------------------------------------------- */
.marquee {
  position: relative;
  display: flex;
  overflow: hidden;
  user-select: none;
  background: var(--mq-bg, var(--c-ink));
  color: var(--mq-fg, var(--c-cream));
  border-block: var(--rule-thick) solid var(--c-ink);
  padding-block: clamp(0.6rem, 1.4vw, 1.05rem);
}

/* JS clones .marquee__track so the loop has no seam. */
.marquee__track {
  display: flex;
  flex: none;
  min-width: max-content;
  align-items: center;
  gap: var(--mq-gap, 2.5rem);
  padding-right: var(--mq-gap, 2.5rem);
  animation: k-marquee var(--mq-dur, 28s) linear infinite;
  will-change: transform;
}

.marquee--rev .marquee__track {
  animation-name: k-marquee-rev;
}

.marquee:hover .marquee__track {
  animation-play-state: paused;
}

.marquee__item {
  display: inline-flex;
  align-items: center;
  gap: var(--mq-gap, 2.5rem);
  font-family: var(--f-display);
  font-size: clamp(1rem, 2.1vw, 1.85rem);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}

.marquee__item .mq-star {
  color: var(--c-amber);
  font-size: 0.72em;
}

.marquee--red {
  --mq-bg: var(--c-red);
  --mq-fg: var(--c-cream-50);
}

.marquee--cream {
  --mq-bg: var(--c-cream);
  --mq-fg: var(--c-ink);
}

.marquee--amber {
  --mq-bg: var(--c-amber);
  --mq-fg: var(--c-ink);
}

.marquee--outline {
  --mq-bg: var(--c-ink);
  --mq-fg: transparent;
  -webkit-text-stroke: 1.5px var(--c-cream);
}

.marquee--big {
  padding-block: clamp(0.9rem, 2.2vw, 1.75rem);
}

.marquee--big .marquee__item {
  font-size: clamp(2rem, 7vw, 6rem);
  letter-spacing: 0.01em;
}

/* --------------------------------------------------------------------------
   4. LOOPING MOTIFS
   -------------------------------------------------------------------------- */
.spin {
  animation: k-spin var(--spin-dur, 8s) linear infinite;
  transform-origin: 50% 50%;
}
.spin--rev {
  animation-name: k-spin-rev;
}
.spin--slow {
  --spin-dur: 22s;
}
.spin:hover,
.spin-halt:hover .spin {
  animation-play-state: paused;
}

.eq {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 1.1em;
}
.eq > i {
  display: block;
  width: 3px;
  height: 100%;
  background: currentColor;
  transform-origin: bottom;
  transform: scaleY(0.25);
  animation: k-eq 1s var(--e-in-out) infinite;
}
.eq > i:nth-child(1) { animation-duration: 0.72s; animation-delay: -0.1s; }
.eq > i:nth-child(2) { animation-duration: 1.05s; animation-delay: -0.35s; }
.eq > i:nth-child(3) { animation-duration: 0.83s; animation-delay: -0.2s; }
.eq > i:nth-child(4) { animation-duration: 1.24s; animation-delay: -0.5s; }
.eq > i:nth-child(5) { animation-duration: 0.94s; animation-delay: -0.05s; }
.eq > i:nth-child(6) { animation-duration: 1.15s; animation-delay: -0.42s; }
.eq > i:nth-child(7) { animation-duration: 0.78s; animation-delay: -0.28s; }

.eq--still > i {
  animation-play-state: paused;
  transform: scaleY(0.18);
}

.blink {
  animation: k-blink 1.15s steps(1, end) infinite;
}

.float {
  animation: k-float 5.5s var(--e-in-out) infinite;
}

/* glitch misregistration for display type */
.glitch {
  position: relative;
}
.glitch::before,
.glitch::after {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
}
.glitch::before {
  color: var(--c-amber);
  animation: k-glitch-a 7s steps(1, end) infinite;
}
.glitch::after {
  color: var(--c-teal);
  animation: k-glitch-b 7s steps(1, end) infinite;
}
