/* The homepage motion hooks, scoped to detail content only. */
.detail-refined [data-intro-text] { position: relative; }
.detail-motion-accessible { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.detail-refined [data-intro-char] { display: inline-block; }
.detail-motion-punctuation { white-space: nowrap; }
.detail-refined .intro-motion-ready [data-intro-char] {
  opacity: calc(.45 + var(--intro-char-progress, 1) * .55);
  filter: blur(calc((1 - var(--intro-char-progress, 1)) * 2px));
}
/* Only opening copy receives the initial-reading guard. Gallery progress stays scroll-linked. */
.detail-refined .detail-motion-readable [data-intro-char] { opacity: 1; filter: none; }
.detail-refined img.spring-pending { opacity: .65; transform: translateY(20px); }
.detail-refined img.spring-pending[data-motion-direction="from-right"] { transform: translateX(24px); }
.detail-motion-fallback .detail-refined [data-intro-char] { opacity: 1; filter: none; }
.detail-motion-fallback .detail-refined img[data-intro-image] { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .detail-refined .intro-motion-ready [data-intro-char] { opacity: 1; filter: none; }
  .detail-refined img[data-intro-image] { opacity: 1; transform: none; }
}

/* Isolate paragraph painting in WebKit without changing the accepted glyph
   opacity, blur, timing, text flow, or image animation. Promote paragraphs,
   not thousands of individual characters. Reduced/fallback modes need no hint. */
@media (prefers-reduced-motion: no-preference) {
  html.has-site-js:not(.motion-reduced):not(.detail-motion-fallback) .detail-refined [data-intro-text] {
    will-change: opacity;
  }
}
