.text-muted {
  color: var(--c-muted);
}

.mt-4 {
  margin-top: var(--space-4);
}

/* Reveal on scroll */
[data-reveal],
[data-reveal-child] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.6s var(--ease), transform 0.6s var(--ease);
}

[data-reveal].is-revealed,
[data-reveal-child].is-revealed {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  [data-reveal],
  [data-reveal-child] {
    opacity: 1;
    transform: none;
  }
}

/* Hero page-load sequence */
.hero__copy > * {
  opacity: 0;
  transform: translateY(20px);
  animation: rise 0.85s var(--ease) forwards;
}

.hero__copy > *:nth-child(1) { animation-delay: 0.10s; }
.hero__copy > *:nth-child(2) { animation-delay: 0.24s; }
.hero__copy > *:nth-child(3) { animation-delay: 0.38s; }

@keyframes rise {
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .hero__copy > * {
    opacity: 1;
    transform: none;
    animation: none;
  }
}

/* Failsafe: the hero entry animation starts at opacity 0, so if it never
   runs the headline and the panel would stay invisible. main.js sets this
   shortly after load regardless of whether the animation fired.
   animation:none is required — a running (or stalled) animation outranks a
   plain declaration, so without cancelling it the transform would stick. */
.is-animated .hero__copy > * {
  animation: none;
  opacity: 1;
  transform: none;
}

/* ---- Spacing helpers ---- */
.mt-5 { margin-top: var(--space-5); }
.mt-6 { margin-top: var(--space-6); }
.mt-7 { margin-top: var(--space-7); }

/* Pulls a block up over the band above it, so the two overlap */
.pull-up {
  position: relative;
  z-index: 2;
  margin-top: calc(var(--space-8) * -1);
}
