/* Affiliate-link visual cue — two variants, opt-in via ?cue=red or ?cue=sweep.
   Marker: .article-body a.aff (Kramdown {:.aff} in markdown).
   Scroll-linked: JS sets --cue (0..1) and --phase (0 entering, 1 leaving)
   on each link every frame based on its viewport position. */

.article-body a.aff {
  color: inherit;
  text-decoration: none;
}

/* ---------- Variant: red color fade ---------- */
body.cue-red .article-body a.aff {
  --cue: 0;
  color: color-mix(in oklch, var(--ink), var(--accent) calc(var(--cue, 0) * 100%));
  transition: color 250ms linear;
}

/* ---------- Variant: underline sweep ----------
   Enter (phase 0, anchored left): size grows 0% -> 100% from left.
   Leave (phase 1, anchored right): size shrinks 100% -> 0%, eating from the left. */
body.cue-sweep .article-body a.aff {
  --cue: 0;
  --phase: 0;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: calc(var(--phase, 0) * 100%) 100%;
  background-size: calc(var(--cue, 0) * 100%) 1.5px;
  padding-bottom: 1px;
}

/* ---------- Variant: underline sweep, red ---------- */
body.cue-sweep-red .article-body a.aff {
  --cue: 0;
  --phase: 0;
  background-image: linear-gradient(var(--accent), var(--accent));
  background-repeat: no-repeat;
  background-position: calc(var(--phase, 0) * 100%) 100%;
  background-size: calc(var(--cue, 0) * 100%) 1.5px;
  padding-bottom: 1px;
}

/* ---------- Reduced motion: show peak state, ignore scroll ---------- */
@media (prefers-reduced-motion: reduce) {
  body.cue-red .article-body a.aff {
    color: var(--accent);
  }
  body.cue-sweep .article-body a.aff,
  body.cue-sweep-red .article-body a.aff {
    background-position: 0 100%;
    background-size: 100% 1.5px;
  }
}
