/* Motion. The Figma prototype specifies exactly two interactions — the button's
 * hover variant and the 300ms ease-out scroll on "Learn more" — so everything
 * else here is ours, and the brief is restraint: one idea per section, no
 * overshoot. A cubic-bezier whose y exceeds 1 bounces, and bounce reads dated.
 */

/* The starting state is behind `scripting: enabled` on purpose. Hiding content
 * that only JavaScript can reveal means a blank page whenever the script fails
 * to load — and the CSP here is script-src 'self' with no inline allowance, so
 * the usual `<script>document.documentElement.className='js'</script>` trick is
 * blocked by our own policy. A browser too old for this media feature simply
 * shows everything, which is the right way to fail. */
@media (scripting: enabled) {
  [data-reveal] {
    opacity: 0;
  }

  [data-reveal="hero"] { transform: translateY(16px); }
  [data-reveal="card"] { transform: translateY(14px); }
  [data-reveal="beat"] { transform: translateY(8px); }
  [data-reveal="group"] { transform: translateY(10px); }

  .hero__line-in {
    transform: translateY(105%);
  }
}

/* Four tiers, not one treatment repeated. Sixteen elements arriving with the
 * same opacity, distance, duration and curve is uniformity, and uniformity is
 * what makes "polish" read as template. The hero is the page's one moment; the
 * section beats are quieter and quicker; the three step cards keep a stagger
 * because they are an ordered sequence; the prize tiles arrive together
 * because they are parallel options, not steps. Static text — the legal note —
 * does not animate at all. */
[data-reveal].is-in {
  opacity: 1;
  transform: none;
}

/* Sequence lives here, not in a style attribute on the element. The CSP is
 * style-src 'self' with no inline allowance, so `style="--i:2"` is blocked at
 * the edge — which cost the stagger silently and threw nine console errors on
 * the first deployed preview. It never showed locally because a plain static
 * server does not send _headers. */
[data-reveal="hero"].is-in {
  transition:
    opacity var(--dur-reveal) var(--ease-out),
    transform var(--dur-reveal) var(--ease-out);
}

.hero__lead.is-in { transition-delay: 210ms; }
.hero__points.is-in { transition-delay: 280ms; }
.hero__actions.is-in { transition-delay: 350ms; }

[data-reveal="card"].is-in {
  transition:
    opacity 420ms var(--ease-out),
    transform 420ms var(--ease-out);
}

.steps__list .card:nth-child(2).is-in { transition-delay: 70ms; }
.steps__list .card:nth-child(3).is-in { transition-delay: 140ms; }

[data-reveal="beat"].is-in,
[data-reveal="group"].is-in {
  transition:
    opacity 380ms var(--ease-out),
    transform 380ms var(--ease-out);
}

.closing__action.is-in { transition-delay: 60ms; }

/* The headline wipes up from a mask rather than fading in place: the page gets
 * exactly one crafted moment and this is it. The lines carry no opacity change
 * — the mask does the hiding, which reads as type being set rather than as a
 * generic fade. */
.hero__title.is-in .hero__line-in {
  transform: none;
  transition: transform 560ms var(--ease-out);
}

.hero__title.is-in .hero__line:nth-child(2) .hero__line-in {
  transition-delay: 90ms;
}

/* Parallax: one transform, set from JS, disabled on small screens. The hint is
 * applied only while a scroll is in flight — a permanent will-change holds a
 * compositor layer for the page's whole life, which costs memory on the phones
 * least able to spare it. */
.hero__art img.is-parallaxing {
  will-change: transform;
}

/* The language list is a utility control, so it opens fast and quietly.
 * @starting-style is what lets a transition run on an element that only just
 * gained a box; browsers without it simply show the list instantly. */
.lang[open] .lang__list {
  opacity: 1;
  transform: translateY(0);
  transition:
    opacity 150ms var(--ease-out),
    transform 150ms var(--ease-out);
}

@starting-style {
  .lang[open] .lang__list {
    opacity: 0;
    transform: translateY(-6px);
  }
}

/* The link scrolls the page down and the mock gives no sign of that. One arrow,
 * nudging on hover, is the whole affordance. */
.hero__more-arrow {
  transition: transform var(--dur-fast) var(--ease-out);
}

.hero__more:hover .hero__more-arrow {
  transform: translateY(3px);
}

.card:hover,
.tile:hover {
  transform: translateY(-4px);
}

.card:hover .card__media img,
.tile:hover .tile__media img {
  transform: scale(1.03);
}

/* The exit is quicker than the entrance: dismissal should get out of the way,
 * and matching both directions makes closing feel sluggish. */
.menu {
  opacity: 0;
  transition: opacity 200ms var(--ease-out);
}

.menu.is-open {
  opacity: 1;
  transition: opacity var(--dur-base) var(--ease-out);
}

.menu__nav a,
.menu__lang {
  opacity: 0;
  transform: translateY(10px);
}

.menu.is-open .menu__nav a,
.menu.is-open .menu__lang {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--dur-base) var(--ease-out),
    transform var(--dur-base) var(--ease-out);
}

.menu.is-open .menu__nav a:nth-child(1) { transition-delay: 60ms; }
.menu.is-open .menu__nav a:nth-child(2) { transition-delay: 120ms; }
.menu.is-open .menu__lang { transition-delay: 180ms; }

/* Reduced motion disables movement rather than shortening it. The last rules
 * matter most: without them a reader who never triggers the observer would be
 * left looking at permanently invisible content. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  [data-reveal],
  [data-reveal].is-in,
  .hero__line-in,
  .hero__title.is-in .hero__line-in,
  .menu__nav a,
  .menu__lang,
  .lang[open] .lang__list {
    opacity: 1;
    transform: none;
  }

  .card:hover,
  .tile:hover,
  .card:hover .card__media img,
  .tile:hover .tile__media img,
  .hero__more:hover .hero__more-arrow {
    transform: none;
  }

  /* Its own rule: a browser without :has() drops the whole selector list. */
  .tile:has(> a:active) { transform: none; }

  .hero__art img { transform: none !important; }
}
