/* ============================================================
   components/scroll-fade.css — a scrolling edge is never silent (#201)
   design-language.md §4.1 is the rule; this is the only
   implementation of it. Three had drifted apart before this.

   Markup — the fade sits on a box that COINCIDES with the
   scrolling box, because an edge fade that is not on the edge is
   worse than none:

     <div class="dd-scroll-fade" data-dd-scroll-fade>
       <ul data-dd-scroll-fade-scroller>…</ul>
     </div>

   or, where the element is clipped rather than scrolled (a clamp,
   e.g. the song lyrics preview), the host measures itself:

     <div class="lyrics-panel" data-dd-scroll-fade>…</div>

   The pseudos are on the HOST and never on the scroller: a pseudo
   inside a scrolling box scrolls away with the content it exists
   to cover.

   `data-dd-scroll-fade="x"` fades the left/right edges instead.

   **The gradient only exists once components.js has taken the
   element over** (`data-dd-scroll-fade-live`, set at bind time,
   the `.dd-js` idea from reveal.css narrowed to one element). A
   fade with nothing measuring it paints "there is more" over
   lists that do not scroll, which is exactly how the old one
   drifted: the CSS shipped on pages the controller didn't.

   Knobs, all optional:
     --dd-fade-size        how far it fades       (default 3em)
     --dd-fade-size-start  one edge on its own — `0` is how a call site
     --dd-fade-size-end    says "not this edge", e.g. a scroller whose
                           head is pinned to its top
     --dd-fade-color       what it fades INTO     (default var(--surface))
     --dd-fade-inset       cross-axis inset, for a scroller whose content
                           is padded away from the edge
   ============================================================ */

[data-dd-scroll-fade] {
  position: relative;
}

/* The wrapper shape: it stands in for the scroller in its parent's layout,
   and hands its own height straight back down. Call sites that wrap a
   scroller which was already a flex child need nothing else. */
.dd-scroll-fade {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
}

.dd-scroll-fade > * {
  flex: 1 1 auto;
  min-height: 0;
}

[data-dd-scroll-fade][data-dd-scroll-fade-live]::before,
[data-dd-scroll-fade][data-dd-scroll-fade-live]::after {
  content: "";
  position: absolute;
  z-index: 1;
  pointer-events: none;
  border-radius: inherit;
  transition: opacity var(--dur-base) var(--ease-soft);
}

/* ── vertical, the default axis ─────────────────────────────── */
[data-dd-scroll-fade][data-dd-scroll-fade-live]::before {
  top: 0;
  left: var(--dd-fade-inset, 0);
  right: var(--dd-fade-inset, 0);
  height: var(--dd-fade-size-start, var(--dd-fade-size, 3em));
  background: linear-gradient(to bottom, var(--dd-fade-color, var(--surface)), transparent);
}

[data-dd-scroll-fade][data-dd-scroll-fade-live]::after {
  bottom: 0;
  left: var(--dd-fade-inset, 0);
  right: var(--dd-fade-inset, 0);
  height: var(--dd-fade-size-end, var(--dd-fade-size, 3em));
  background: linear-gradient(to top, var(--dd-fade-color, var(--surface)), transparent);
}

/* ── horizontal ─────────────────────────────────────────────── */
[data-dd-scroll-fade="x"][data-dd-scroll-fade-live]::before {
  top: var(--dd-fade-inset, 0);
  bottom: var(--dd-fade-inset, 0);
  left: 0;
  right: auto;
  width: var(--dd-fade-size-start, var(--dd-fade-size, 3em));
  height: auto;
  background: linear-gradient(to right, var(--dd-fade-color, var(--surface)), transparent);
}

[data-dd-scroll-fade="x"][data-dd-scroll-fade-live]::after {
  top: var(--dd-fade-inset, 0);
  bottom: var(--dd-fade-inset, 0);
  right: 0;
  left: auto;
  width: var(--dd-fade-size-end, var(--dd-fade-size, 3em));
  height: auto;
  background: linear-gradient(to left, var(--dd-fade-color, var(--surface)), transparent);
}

/* ── the class is the truth ─────────────────────────────────────
   Content that fits is at both ends at once, so it wears neither —
   the case that is easiest to forget and the one that makes the
   cue lie. Same specificity as the rules above, so these come
   after them on purpose. */
[data-dd-scroll-fade].at-scroll-start::before {
  opacity: 0;
}

[data-dd-scroll-fade].at-scroll-end::after {
  opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
  [data-dd-scroll-fade][data-dd-scroll-fade-live]::before,
  [data-dd-scroll-fade][data-dd-scroll-fade-live]::after {
    transition: none;
  }
}
