/*
 * Public editorial: the shared scroll-response layer (2026-09-02). Two
 * pieces here, all pure transform/opacity, never pointer-driven, and switched
 * off under prefers-reduced-motion:
 *
 *   1. Word-by-word heading reveal (WordReveal, src/components/common/).
 *   2. Hero parallax: `--hero-p` (0…1) is written on the hero by
 *      useScrollProgress (src/hooks/), the aside and its caption read it.
 *
 * The other two pieces of the rhythm were split out at the 400-line limit:
 * the sticky card deck (StackDeck) lives in public-editorial-deck.css and
 * the full-bleed culmination band (.page-bleed + CatalogFeature) in
 * public-editorial-feature.css.
 *
 * Loaded by PublicLayout for every public route. No backdrop-filter, no
 * filter: blur(), no will-change – the runtime blur budget stays untouched.
 */

/* ---------- Word reveal ---------- */

.word-reveal__word {
  display: inline-block;
}

[data-words='pending'] .word-reveal__word {
  opacity: 0;
  transform: translateY(0.35em);
}

[data-words='visible'] .word-reveal__word {
  opacity: 1;
  transform: translateY(0);
  transition:
    opacity 0.55s var(--ease-glass),
    transform 0.55s var(--ease-glass);
  transition-delay: calc(var(--i, 0) * 55ms);
}

/* ---------- Hero parallax ---------- */

/* --hero-p starts at 0 so the transform is the identity until the scroll
   listener writes a real value: no jump on first paint, nothing moves before
   JavaScript, and the LCP image is never transformed while it loads. */
.page-hero {
  --hero-p: 0;
}

/* The caption sinks against the rising aside. Its drop stays below the
   caption's inset from the figure edge (1.25rem / 0.9rem in pages.css), so
   the overflow-hidden aside never clips it (the homepage lesson). */
.page-hero[data-parallax] .page-hero__aside {
  transform: translate3d(0, calc(var(--hero-p) * -56px), 0);
}

.page-hero[data-parallax] .page-hero__aside figcaption {
  transform: translate3d(0, calc(var(--hero-p) * 18px), 0);
}

@media (max-width: 1023px) {
  .page-hero[data-parallax] .page-hero__aside {
    transform: translate3d(0, calc(var(--hero-p) * -28px), 0);
  }

  .page-hero[data-parallax] .page-hero__aside figcaption {
    transform: translate3d(0, calc(var(--hero-p) * 10px), 0);
  }
}

/* Homepage hero (HeroCompact): the portrait rises while its paper caption
   sinks. Lives here because home-editorial-core.css sits at the 400-line
   limit. The image box is exactly one travel taller than its overflow-hidden
   figure, so the frame stays filled all the way up without zooming the
   photo; the caption's drop never exceeds its inset from the bottom edge,
   so it is never clipped. At --hero-p 0 both transforms are the identity. */
.home-hero {
  --hero-p: 0;
  --hero-travel: 70px;
  --hero-caption-drop: 18px;
}

.home-hero__portrait--parallax .home-hero__image {
  height: calc(100% + var(--hero-travel));
  transform: translate3d(0, calc(var(--hero-p) * var(--hero-travel) * -1), 0);
}

.home-hero__portrait--parallax figcaption {
  transform: translate3d(0, calc(var(--hero-p) * var(--hero-caption-drop)), 0);
}

@media (max-width: 1023px) {
  .home-hero {
    --hero-travel: 32px;
    --hero-caption-drop: 10px;
  }
}

/* About hero (AboutHero, /apie): the portrait card rises while its paper
   caption sinks; the copy stays put. The card's rise stays under the hero's
   own padding (2rem and up) for as long as the hero is on screen, and the
   caption's drop stays under its 1.25rem bottom inset (info.css). */
.po-about-hero {
  --hero-p: 0;
}

.po-about-hero__visual--parallax {
  transform: translate3d(0, calc(var(--hero-p) * -48px), 0);
}

.po-about-hero__visual--parallax figcaption {
  transform: translate3d(0, calc(var(--hero-p) * 18px), 0);
}

@media (max-width: 1023px) {
  .po-about-hero__visual--parallax {
    transform: translate3d(0, calc(var(--hero-p) * -24px), 0);
  }

  .po-about-hero__visual--parallax figcaption {
    transform: translate3d(0, calc(var(--hero-p) * 10px), 0);
  }
}

/* Detail-page covers (course + workshop hero, article hero): the cover photo
   rises inside its own 16:9 box, which is one travel taller than the frame so
   nothing empty shows; --hero-p is written on the hero / wrapper by
   useScrollProgress. The article travel is the smaller one on purpose. */
.po-detail-hero,
.po-article-hero-image {
  --hero-p: 0;
  --cover-travel: 40px;
}

.po-detail-hero__visual--parallax .po-detail-hero__image {
  overflow: hidden;
  aspect-ratio: 16 / 9;
}

.po-detail-hero__visual--parallax .po-detail-hero__image img,
.po-article-hero-image[data-parallax] img {
  height: calc(100% + var(--cover-travel));
  aspect-ratio: auto;
  transform: translate3d(0, calc(var(--hero-p) * var(--cover-travel) * -1), 0);
}

@media (max-width: 1023px) {
  .po-detail-hero,
  .po-article-hero-image {
    --cover-travel: 24px;
  }
}

/* ---------- Reduced motion: everything static ---------- */

@media (prefers-reduced-motion: reduce) {
  [data-words] .word-reveal__word {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .page-hero[data-parallax] .page-hero__aside,
  .page-hero[data-parallax] .page-hero__aside figcaption,
  .home-hero__portrait--parallax .home-hero__image,
  .home-hero__portrait--parallax figcaption,
  .po-about-hero__visual--parallax,
  .po-about-hero__visual--parallax figcaption {
    transform: none;
  }

  .po-detail-hero__visual--parallax .po-detail-hero__image img,
  .po-article-hero-image[data-parallax] img {
    height: 100%;
    transform: none;
  }
}
