/* ============================================
   OVERCHENKO STUDIO — animations.css
   Initial states + keyframes
   ============================================ */

/* Initial states — щоб уникнути FOUC до GSAP init */
html:not(.is-ready) .preloader { opacity: 1; }
html.is-ready .preloader { pointer-events: none; }

/* ============================================
   PAGE TRANSITION — curtain wipe
   ============================================ */
.page-transition {
  position: fixed;
  inset: 0;
  background: var(--color-bg);
  z-index: 9998; /* under preloader/cursor, over content */
  transform: translateY(100%);
  pointer-events: none;
  will-change: transform;
}
html.os-curtain-in .page-transition {
  transform: translateY(0%);
  pointer-events: auto;
}
.page-transition.is-active {
  pointer-events: auto;
}

/* Initial reveal states */
.js-fade-in,
.js-rise,
.js-rise-lg,
.js-scale-in,
.manifesto__card,
.service-row,
.work-card,
.stats__item,
.faq-item,
.award,
.tier,
.process-step {
  opacity: 0;
  transform: translateY(50px);
  transition:
    opacity 1.8s var(--ease-out-expo),
    transform 1.8s var(--ease-out-expo);
  will-change: transform, opacity;
}

.js-rise-lg { transform: translateY(90px); }
.js-fade-in { transform: none; }

.js-scale-in {
  transform: scale(0.94);
}

.js-clip-bottom {
  clip-path: inset(0 0 100% 0);
  transition: clip-path 2.2s var(--ease-out-expo);
  will-change: clip-path;
}

/* Revealed state */
.is-in,
.manifesto__card.is-in,
.service-row.is-in,
.work-card.is-in,
.stats__item.is-in,
.faq-item.is-in,
.award.is-in,
.tier.is-in,
.process-step.is-in {
  opacity: 1;
  transform: translateY(0) scale(1);
}

.js-clip-bottom.is-in {
  clip-path: inset(0 0 0 0);
}

/* Stagger using nth-child within container — doubled for premium pacing */
.manifesto__card:nth-child(2)  { transition-delay: 0.16s; }
.manifesto__card:nth-child(3)  { transition-delay: 0.32s; }
.manifesto__card:nth-child(4)  { transition-delay: 0.48s; }
.service-row:nth-child(2)      { transition-delay: 0.16s; }
.service-row:nth-child(3)      { transition-delay: 0.32s; }
.stats__item:nth-child(2)      { transition-delay: 0.16s; }
.stats__item:nth-child(3)      { transition-delay: 0.32s; }
.stats__item:nth-child(4)      { transition-delay: 0.48s; }
.work-card:nth-child(even)     { transition-delay: 0.16s; }
.tier:nth-child(2)             { transition-delay: 0.2s; }
.tier:nth-child(3)             { transition-delay: 0.4s; }
.award:nth-child(2)            { transition-delay: 0.16s; }
.award:nth-child(3)            { transition-delay: 0.32s; }
.award:nth-child(4)            { transition-delay: 0.48s; }
.process-step:nth-child(2)     { transition-delay: 0.16s; }
.process-step:nth-child(3)     { transition-delay: 0.32s; }
.process-step:nth-child(4)     { transition-delay: 0.48s; }
.process-step:nth-child(5)     { transition-delay: 0.64s; }
.team-pillar:nth-child(2)      { transition-delay: 0.16s; }
.team-pillar:nth-child(3)      { transition-delay: 0.32s; }
.team-proof__stat:nth-child(2) { transition-delay: 0.16s; }
.team-proof__stat:nth-child(3) { transition-delay: 0.32s; }

.split-line .split-word {
  transform: translateY(0);
}

/* Tilt 3D */
[data-tilt] {
  transform-style: preserve-3d;
  perspective: 1200px;
}

[data-tilt-inner] {
  transform-style: preserve-3d;
  transition: transform var(--dur-normal) var(--ease-out-expo);
  will-change: transform;
}

/* Shimmer accent для accent-слів */
@keyframes shimmer {
  0%, 100% { filter: brightness(1); }
  50%      { filter: brightness(1.35); }
}

.hero__heading .accent {
  animation: shimmer 7.2s ease-in-out infinite;
}

/* Hover image scale */
.work-card__media {
  transition: transform var(--dur-slow) var(--ease-out-expo);
}

.work-card:hover .work-card__media {
  transform: scale(1.02);
}

/* Floating decorative elements */
@keyframes float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-12px); }
}

.float-slow { animation: float 12s ease-in-out infinite; }
.float-med  { animation: float 9s ease-in-out infinite; animation-delay: -4s; }

/* Marquee already handled in pages.css */

/* ============================================
   HERO REVEAL — pure CSS (after .is-loaded on body)
   ============================================ */
/* Nav items rise in one after another (stagger), instead of the whole bar */
.nav__brand,
.nav__pill .nav__link,
.nav__lang,
.nav__cta,
.nav__burger {
  opacity: 0;
  transform: translateY(-18px);
}

/* Brand mark — 3D rotateX rise on page load */
.nav__brand-mark {
  transform: perspective(600px) rotateX(30deg);
  transform-origin: center bottom;
}

.hero__canvas,
.hero__particles {
  opacity: 0;
}

body.is-loaded .nav__brand          { animation: navItemRise 0.8s 0.05s var(--ease-out-expo) forwards; }
body.is-loaded .nav__burger         { animation: navItemRise 0.8s 0.12s var(--ease-out-expo) forwards; }
body.is-loaded .nav__pill .nav__link:nth-child(1) { animation: navItemRise 0.8s 0.12s var(--ease-out-expo) forwards; }
body.is-loaded .nav__pill .nav__link:nth-child(2) { animation: navItemRise 0.8s 0.18s var(--ease-out-expo) forwards; }
body.is-loaded .nav__pill .nav__link:nth-child(3) { animation: navItemRise 0.8s 0.24s var(--ease-out-expo) forwards; }
body.is-loaded .nav__pill .nav__link:nth-child(4) { animation: navItemRise 0.8s 0.30s var(--ease-out-expo) forwards; }
body.is-loaded .nav__pill .nav__link:nth-child(5) { animation: navItemRise 0.8s 0.36s var(--ease-out-expo) forwards; }
body.is-loaded .nav__lang           { animation: navItemRise 0.8s 0.42s var(--ease-out-expo) forwards; }
body.is-loaded .nav__cta            { animation: navItemRise 0.8s 0.50s var(--ease-out-expo) forwards; }

body.is-loaded .nav__brand-mark {
  animation: logoRise 1.4s 0.25s var(--ease-out-expo) forwards;
}

@keyframes navItemRise {
  to { opacity: 1; transform: translateY(0); }
}

@keyframes logoRise {
  to { transform: perspective(600px) rotateX(0deg); }
}

/* Particles fade; canvas gets a GSAP spring "kick" (page-load.js), so it
   only falls back to this fade when GSAP/motion is unavailable. */
body.is-loaded .hero__canvas,
body.is-loaded .hero__particles {
  animation: fadeIn 3.2s 0.6s var(--ease-smooth) forwards;
}

/* Hero topline + scroll-cue reveal, after the nav stagger.
   topline rises via translateY; scroll-cue keeps its translateX(-50%) centring
   (pages.css) so it only fades — its inner line keeps the looping pulse. */
.hero__topline {
  opacity: 0;
  transform: translateY(-14px);
}
.hero__scroll-cue {
  opacity: 0;
}

body.is-loaded .hero__topline {
  animation: heroReveal 1.2s 0.7s var(--ease-out-expo) forwards;
}
body.is-loaded .hero__scroll-cue {
  animation: fadeIn 1.2s 0.95s var(--ease-out-expo) forwards;
}

@media (prefers-reduced-motion: reduce) {
  .nav__brand, .nav__pill .nav__link, .nav__lang, .nav__cta, .nav__burger,
  .hero__topline, .hero__canvas, .hero__particles {
    opacity: 1;
    transform: none;
    animation: none;
  }
  .hero__scroll-cue { opacity: 1; animation: none; }
  .hero__scroll-line { animation: none; }
}

/* Hero-compact pages (services/work/contact) — простіша анімація */
.hero-compact__heading,
.hero-compact__sub,
.hero-compact__crumbs {
  opacity: 0;
  transform: translateY(30px);
}

body.is-loaded .hero-compact__crumbs {
  animation: heroReveal 1.4s 0.2s var(--ease-out-expo) forwards;
}
body.is-loaded .hero-compact__heading {
  animation: heroReveal 2s 0.5s var(--ease-out-expo) forwards;
}
body.is-loaded .hero-compact__sub {
  animation: heroReveal 1.6s 1.1s var(--ease-out-expo) forwards;
}

@keyframes heroReveal {
  to { opacity: 1; transform: translateY(0); }
}

@keyframes fadeIn {
  to { opacity: 1; }
}

/* Team hero v2 — same reveal pattern as hero-compact */
.team-hero-v2__crumbs,
.team-hero-v2__divider,
.team-hero-v2__heading,
.team-hero-v2__sub {
  opacity: 0;
  transform: translateY(30px);
}

body.is-loaded .team-hero-v2__crumbs {
  animation: heroReveal 1.4s 0.2s var(--ease-out-expo) forwards;
}
body.is-loaded .team-hero-v2__divider {
  animation: heroReveal 1.2s 0.4s var(--ease-out-expo) forwards;
}
body.is-loaded .team-hero-v2__heading {
  animation: heroReveal 2s 0.55s var(--ease-out-expo) forwards;
}
body.is-loaded .team-hero-v2__sub {
  animation: heroReveal 1.6s 1.1s var(--ease-out-expo) forwards;
}

/* Print/no-js fallback */
html.no-js .js-fade-in,
html.no-js .js-rise,
html.no-js .js-rise-lg,
html.no-js .js-scale-in {
  opacity: 1 !important;
  transform: none !important;
}

html.no-js .js-clip-bottom { clip-path: none !important; }

/* Reduced motion overrides — анімації вимикаються в main.css; тут страховка для marquee */
@media (prefers-reduced-motion: reduce) {
  .marquee__track,
  .float-slow, .float-med,
  .hero__heading .accent,
  .nav__logo-img {
    animation: none !important;
    transform: none !important;
  }
}

html.no-js .nav__logo-img {
  transform: none !important;
}

/* ============================================
   WOW LAYER — GSAP-only reveals (html.os-wow)
   Activated by wow.js. Never present without GSAP + motion allowed,
   so these rules never hide content on the degraded path.
   ============================================ */

/* Word-mask split for [data-split] headings */
.wow-word-mask {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
  /* room for descenders so масковані хвостики g/у не обрізаються */
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}
.wow-word {
  display: inline-block;
  will-change: transform;
}

/* GSAP owns the reveal of these components: hide them up-front and kill
   the CSS reveal transition so it cannot fight the GSAP tween. GSAP sets
   inline opacity:1 on enter (and clears only the transform afterwards). */
html.os-wow .work-card,
html.os-wow .service-row,
html.os-wow .tier,
html.os-wow .award,
html.os-wow .process-step,
html.os-wow .manifesto__card,
html.os-wow .stats__item,
html.os-wow .case-chapter,
html.os-wow .case-impact__stat,
html.os-wow .case-stack__chip {
  opacity: 0;
  /* GSAP owns position via inline transform; null the base reveal transform
     so clearProps:'transform' after the tween lands them in place (not the
     CSS translateY) and frees the element for CSS :hover transforms. */
  transform: none;
  transition: none;
}

/* Safety: if motion is reduced, wow.js never runs and never splits text,
   but guard the word state anyway. */
@media (prefers-reduced-motion: reduce) {
  .wow-word { transform: none !important; }
}
