{% for p in products %}
  {% include "_types/browse/_product_card.html" %}
{% endfor %}
{% if page < total_pages|int %}

  
  <div
    id="sentinel-{{ page }}-m"
    class="block md:hidden h-[60vh] opacity-0 pointer-events-none js-mobile-sentinel"
    hx-get="{{ (current_local_href ~ {'page': page + 1}|qs)|host }}"
    hx-trigger="intersect once delay:250ms, sentinelmobile:retry"
    hx-swap="outerHTML"
    _="
      init
        if not me.dataset.retryMs then set me.dataset.retryMs to 1000 end
        if window.matchMedia('(min-width: 768px)').matches then set @hx-disabled to '' end

      on resize from window
        if window.matchMedia('(min-width: 768px)').matches then set @hx-disabled to '' else remove @hx-disabled end

      on htmx:beforeRequest
        if window.matchMedia('(min-width: 768px)').matches then halt end
        add .hidden to .js-neterr in me
        remove .hidden from .js-loading in me
        remove .opacity-100 from me
        add .opacity-0 to me

      def backoff()
        set ms to me.dataset.retryMs
        if ms > 30000 then set ms to 30000 end
        -- show big SVG panel & make sentinel visible
        add .hidden to .js-loading in me
        remove .hidden from .js-neterr in me
        remove .opacity-0 from me
        add .opacity-100 to me
        wait ms ms
        trigger sentinelmobile:retry
        set ms to ms * 2
        if ms > 30000 then set ms to 30000 end
        set me.dataset.retryMs to ms
      end

      on htmx:sendError     call backoff()
      on htmx:responseError call backoff()
      on htmx:timeout       call backoff()
    "
    role="status"
    aria-live="polite"
    aria-hidden="true"
  >
    {% include "sentinel/mobile_content.html" %}
  </div>
  <!-- DESKTOP sentinel (custom scroll container) -->
  <div
    id="sentinel-{{ page }}-d"
    class="hidden md:block h-4 opacity-0 pointer-events-none"
    hx-get="{{ (current_local_href ~ {'page': page + 1}|qs)|host}}"
    hx-trigger="intersect once delay:250ms, sentinel:retry"
    hx-swap="outerHTML"
     _="
      init
        if not me.dataset.retryMs then set me.dataset.retryMs to 1000 end

      on htmx:beforeRequest(event)
        add .hidden to .js-neterr in me
        remove .hidden from .js-loading in me
        remove .opacity-100 from me
        add .opacity-0 to me

        set trig to null
        if event.detail and event.detail.triggeringEvent then
          set trig to event.detail.triggeringEvent
        end
        if trig and trig.type is 'intersect'
          set scroller to the closest .js-grid-viewport
          if scroller is null then halt end
          if scroller.scrollTop < 20 then halt end
        end

      def backoff()
        set ms to me.dataset.retryMs
        if ms > 30000 then set ms to 30000 end
        add .hidden to .js-loading in me
        remove .hidden from .js-neterr in me
        remove .opacity-0 from me
        add .opacity-100 to me
        wait ms ms
        trigger sentinel:retry
        set ms to ms * 2
        if ms > 30000 then set ms to 30000 end
        set me.dataset.retryMs to ms
      end

      on htmx:sendError     call backoff()
      on htmx:responseError call backoff()
      on htmx:timeout       call backoff()
    "
    role="status"
    aria-live="polite"
    aria-hidden="true"
  >
    {% include "sentinel/desktop_content.html" %}
  </div>
{% else %}
  <div class="col-span-full mt-4 text-center text-xs text-stone-400">End of results</div>
{% endif %}

