/* =========================================================
   ANIMATIONS — keyframet ja scroll-reveal
   Animaatiobudjetti: yksi paljastus per osio, ei enempää.
   ========================================================= */

/* ---------- Keyframet ---------- */

@keyframes rise-in {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes ripple {
  to {
    transform: scale(26);
    opacity: 0;
  }
}

@keyframes drift {
  0%,
  100% {
    transform: translate3d(0, 0, 0) scale(1);
  }
  33% {
    transform: translate3d(4%, 6%, 0) scale(1.1);
  }
  66% {
    transform: translate3d(-5%, 3%, 0) scale(0.95);
  }
}

@keyframes marquee {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-50%);
  }
}

/* ---------- Scroll-reveal ---------- */
/* JS lisää .is-visible kun elementti tulee näkyviin. */

[data-reveal] {
  opacity: 0;
  transform: translateY(22px);
  transition:
    opacity var(--dur-reveal) var(--ease-out),
    transform var(--dur-reveal) var(--ease-out);
  will-change: opacity, transform;
}

[data-reveal].is-visible {
  opacity: 1;
  transform: none;
  will-change: auto;
}

/* Porrastus — CSS-muuttuja asetetaan HTML:ssä tai JS:ssä */
[data-reveal] {
  transition-delay: var(--reveal-delay, 0ms);
}

/* Vaihtoehtoiset suunnat */
[data-reveal="left"] {
  transform: translateX(-24px);
}

[data-reveal="right"] {
  transform: translateX(24px);
}

[data-reveal="fade"] {
  transform: none;
}

[data-reveal="scale"] {
  transform: scale(0.97);
}

[data-reveal].is-visible {
  transform: none;
}

/* Jos JS ei lataudu, sisältö näkyy silti */
.no-js [data-reveal] {
  opacity: 1;
  transform: none;
}

/* ---------- Otsikon rivipaljastus ---------- */
/* JS kietoo jokaisen sanan kahteen kuoreen: ulompi leikkaa, sisempi
   liukuu ylös. Sanat porrastetaan RIVEITTÄIN, joten vaikutelma on
   rivi kerrallaan nouseva otsikko — mutta DOM:ia ei järjestellä
   uudelleen, joten <span class="dot"> ja <strong> säilyttävät tyylinsä.

   Rivipohjainen uudelleenrakennus olisi näyttänyt samalta mutta olisi
   vaatinut sanojen siirtämistä pois esi-isistään, jolloin ne olisivat
   menettäneet muotoilunsa. Sanakohtainen kuori antaa saman ilmeen
   ilman sitä riskiä. */

.split__w {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
  /* Ilman tätä overflow leikkaisi alapidennykset (g, j, p, y).
     Negatiivinen margin kumoaa lisätyn tilan asettelusta. */
  padding-block-end: 0.14em;
  margin-block-end: -0.14em;
}

.split__wi {
  display: inline-block;
  transform: translateY(115%);
  transition: transform 820ms var(--ease-out);
  transition-delay: var(--w-delay, 0ms);
  will-change: transform;
}

[data-split].is-split-in .split__wi {
  transform: none;
  will-change: auto;
}

/* ---------- Marquee ---------- */

.marquee {
  display: flex;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}

.marquee__track {
  display: flex;
  flex-shrink: 0;
  gap: clamp(1.5rem, 1rem + 2.5vw, 3.5rem);
  padding-inline-end: clamp(1.5rem, 1rem + 2.5vw, 3.5rem);
  animation: marquee 42s linear infinite;
  will-change: transform;
}

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

/* Kun JS ottaa nauhan haltuun, se liikuttaa sitä itse ja reagoi
   vierityksen nopeuteen ja suuntaan. CSS-animaatio on silloin pois
   päältä — mutta se jää oletukseksi, joten ilman JS:ää nauha pyörii
   silti. */
.marquee.is-js .marquee__track {
  animation: none;
}

/* ---------- Napin nuoli ---------- */
/* Nuoli lähtee oikealta ulos ja palaa vasemmalta sisään. Toteutus on
   puhdasta CSS:ää eikä vaadi muutoksia yhteenkään nappiin. */

@keyframes nuoli-lapi {
  0% {
    transform: translateX(0);
    opacity: 1;
  }
  45% {
    transform: translateX(15px);
    opacity: 0;
  }
  46% {
    transform: translateX(-15px);
    opacity: 0;
  }
  100% {
    transform: translateX(0);
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  /* Sanakuoret jäävät paikoilleen: JS ei jaa otsikoita lainkaan, mutta
     jos jako on jo tehty ennen asetuksen vaihtoa, teksti ei saa jäädä
     piiloon leikkurin taakse. */
  .split__wi {
    transform: none;
    transition: none;
  }

  .marquee {
    -webkit-mask-image: none;
    mask-image: none;
  }

  .marquee__track {
    animation: none;
    flex-wrap: wrap;
    justify-content: center;
  }

  .marquee__track[aria-hidden="true"] {
    display: none;
  }
}

/* ---------- Sivunvaihdon siirtymä ---------- */
/* View Transitions API, cross-document. Puhdasta CSS:ää, ei riviäkään
   JS:ää eikä reitittäjää.

   Tuki: Chrome/Edge 126+, Safari 18.2+. Firefoxissa lipun takana.
   Tämä on puhdas progressiivinen parannus — selain joka ei tue
   @view-transitionia navigoi täsmälleen kuten ennenkin, joten mitään
   ei tarvitse varmistaa erikseen eikä mikään voi rikkoutua.

   Vaatii saman säännön MOLEMMILLE sivuille (lähtevälle ja tulevalle).
   Koska kaikki sivut lataavat saman animations.css:n, se täyttyy. */
@view-transition {
  navigation: auto;
}

/* Lähtevä sivu vaimenee ja vetäytyy hieman taakse, tuleva pyyhkäisee
   sisään alhaalta. Suunta on sama kuin footerin hillovalumassa ja
   nappien täytössä — sivusto liikkuu johdonmukaisesti ylöspäin. */
::view-transition-old(root) {
  animation: sivu-ulos 240ms var(--ease-out) both;
}

::view-transition-new(root) {
  animation: sivu-sisaan 480ms var(--ease-out) both;
}

@keyframes sivu-ulos {
  to {
    opacity: 0.35;
    transform: scale(0.985);
  }
}

@keyframes sivu-sisaan {
  from {
    clip-path: inset(100% 0 0 0);
  }
  to {
    clip-path: inset(0 0 0 0);
  }
}

/* Otsikkopalkki ei osallistu pyyhkäisyyn vaan pysyy paikallaan.
   Ilman tätä se välähtäisi joka navigoinnilla, koska se on molemmilla
   sivuilla identtinen. Nimen on oltava uniikki dokumentissa — headereitä
   on yksi, joten se on. */
.header {
  view-transition-name: hm-header;
}

@media (prefers-reduced-motion: reduce) {
  @view-transition {
    navigation: none;
  }
}

/* ---------- Heron avaussekvenssi ---------- */
/* Heron paljastusanimaatiot laukeavat IntersectionObserverista heti
   sivun latauduttua — eli latausnäytön TAKANA. Ilman tätä esitys on
   jo ohi siinä vaiheessa kun näyttö nousee.

   .hero--odottaa pitää elementit lähtöasennossaan voittamalla
   .is-visible-säännöt spesifisyydellä (0,4,0 vs 0,2,0). Kun
   latausnäyttö sulkeutuu, JS poistaa luokan ja kaikki porrastukset
   lähtevät siitä hetkestä — ne ovat jo paikoillaan, joten mitään
   ajastusta ei tarvitse laskea uudelleen.

   Luokkaa EI lisätä lainkaan jos latausnäyttöä ei ole (toinen
   sivulataus istunnossa), ja varmistusajastin poistaa sen joka
   tapauksessa. Hero ei siis voi jäädä piiloon. */

.hero.hero--odottaa [data-reveal].is-visible {
  opacity: 0;
  transform: translateY(22px);
}

.hero.hero--odottaa [data-reveal="fade"].is-visible {
  transform: none;
}

.hero.hero--odottaa [data-split].is-split-in .split__wi {
  transform: translateY(115%);
}

/* Video vetäytyy lähtökuvaan: pieni skaalaus tekee avauksesta
   elokuvallisen ilman että mikään hyppää. */
.hero__bg video {
  transition: transform 1600ms var(--ease-out);
}

.hero--odottaa .hero__bg video {
  transform: scale(1.06);
}

@media (prefers-reduced-motion: reduce) {
  .hero__bg video,
  .hero--odottaa .hero__bg video {
    transition: none;
    transform: none;
  }
}
