/* ============================================================
   LITANY — a list recited past a fixed reading band.

   A sticky block holds a static lead phrase in place while a column of
   words scrolls past it. Whichever word is crossing the band lights up.
   The final word pins there while the rest of the page rises over it.

   Portable: every colour and metric is a custom property. Set them on
   the .litany element (or :root) and nothing else needs touching.
   Requires litany.js. Pairs with the markup in demo.html.
   ============================================================ */

.litany{
  /* --- geometry --- */
  --litany-band:50vh;      /* where the reading band sits. litany.js reads this. */
  --litany-navh:0px;       /* height of a sticky site header, 0 if none */
  --litany-dwell:34vh;     /* how long the final word holds before the page rises */
  --litany-size:clamp(1.9rem,5.4vw,4.4rem);
  --litany-leading:1.15;
  --litany-gap:.3em;       /* space between the lead phrase and the words */

  /* --- colour --- */
  --litany-ink:#edf0ea;    /* the static lead phrase */
  --litany-dim:#3a4034;    /* an unlit word. Must be opaque: color-mix blends it. */
  --litany-lit:#86c232;    /* a word crossing the band */
  --litany-glow:134,194,50;/* rgb triplet for the halo, matches --litany-lit */
  --litany-page:#090b0a;   /* the page colour that rises over the pinned word */
}

/* ---- the sticky block ----
   IMPORTANT: .litany must sit on a container tall enough for this block to
   travel through, normally <main> or a wrapper holding the rest of the page.
   Put it on a section that contains only the words and the block cannot move:
   a sticky element's travel is (container height - its own height), which is
   zero in that case, and the effect silently does nothing. */
/* Phones. The desktop clamp bottoms out at its 1.9rem floor below ~563px, which
   leaves the words small against the screen on a handset that has the width to
   spare. This curve scales them with the viewport instead: 320px gets 30px,
   430px gets 41px, and the longest line still clears the gutters at both ends. */
@media(max-width:620px){
  .litany{--litany-size:clamp(1.9rem,9.6vw,2.7rem)}
}

.litany__stick{
  position:sticky;
  top:calc((var(--litany-count) - 1) * -1lh + var(--litany-navh));
  display:flex;width:100%;
  margin-bottom:var(--litany-dwell);
  font-size:var(--litany-size);
  line-height:var(--litany-leading);
}
.litany__stick > *{width:100%}   /* your layout wrapper, e.g. .wrap / .container */

.litany__row{
  display:flex;align-items:flex-start;gap:var(--litany-gap);
  /* --litany-navh appears in both this padding and the sticky top above, so the
     two shift together and the pinned word still lands centred on the band. */
  padding-top:calc(var(--litany-band) - .5lh - var(--litany-navh));
}

.litany__lead{
  position:sticky;top:calc(var(--litany-band) - .5lh);
  margin:0;font:inherit;color:var(--litany-ink);white-space:nowrap;
}

/* optional kicker above the lead phrase. litany.js fades it out as the
   words begin to travel through the space it occupies. */
.litany__kicker{
  position:absolute;bottom:calc(100% + .5em);left:0;
  white-space:nowrap;transition:opacity .2s linear;
}
/* a nowrap kicker beside the words pushes scrollWidth past a phone viewport */
@media(max-width:620px){ .litany__kicker{display:none} }

.litany__words{list-style:none;margin:0;padding:0;font:inherit}
.litany__words li{--t:0;--o:1;white-space:nowrap}

/* ---- the band ----
   Painted from JS-set --t (0 = far from the band, 1 = dead centre) rather than
   the background-attachment:fixed + background-clip:text trick this pattern is
   usually built with. iOS Safari renders fixed attachment as scroll, which
   makes every word uniformly lit or uniformly dim on iPhone.
   Without JS the words stay legible, just unanimated. */
.litany__words[data-lit] li{
  color:color-mix(in oklab, var(--litany-dim), var(--litany-lit) calc(var(--t) * 100%));
  text-shadow:0 0 calc(var(--t) * 44px) rgba(var(--litany-glow), calc(var(--t) * .34));
  opacity:var(--o);
}
@supports not (color:color-mix(in oklab,#000,#fff 50%)){
  .litany__words[data-lit] li{color:var(--litany-ink);text-shadow:none}
}
@media(prefers-reduced-motion:reduce){
  .litany__words[data-lit] li{text-shadow:none}
}

/* ---- the page rises over the pinned final word ----
   Opaque on purpose. The sticky block stays pinned for the remainder of its
   container, so anything transparent here shows the final word through it. */
.litany__after{
  position:relative;z-index:1;
  background:var(--litany-page);
  border-top:1px solid rgba(var(--litany-glow), .22);
  border-radius:28px 28px 0 0;
  box-shadow:0 -34px 90px -46px rgba(var(--litany-glow), .45);
}

/* ---- optional backdrop scrim ----
   Only needed when the act plays over busy artwork (video, WebGL, photo).
   litany.js toggles .is-on while the act is on screen. */
.litany__scrim{
  position:fixed;inset:0;z-index:1;pointer-events:none;opacity:0;
  transition:opacity .5s ease;
  background:linear-gradient(100deg,
    color-mix(in srgb, var(--litany-page) 94%, transparent) 0%,
    color-mix(in srgb, var(--litany-page) 68%, transparent) 46%,
    transparent 78%);
}
.litany__scrim.is-on{opacity:1}
@media(max-width:860px){
  .litany__scrim{background:linear-gradient(180deg,
    color-mix(in srgb, var(--litany-page) 88%, transparent) 0%,
    color-mix(in srgb, var(--litany-page) 72%, transparent) 45%,
    color-mix(in srgb, var(--litany-page) 50%, transparent) 100%)}
}

.litany__sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
