{% comment %} The bell dropdown's contents. Re-fetched on every open, so it is never stale. Ends with the out-of-band badge fragment, so marking something read updates the count in the same response — no second request, and no custom JS event (which would need Alpine's `.camel` modifier to survive htmx's lowercase event names). {% endcomment %}
Notifications {% if unread_notification_count %} {% endif %}
{% for n in notifications %} {% include "partials/_nav_icon.html" with name=n.payload.icon|default:"bell" %} {{ n.title }} {% if n.body %} {{ n.body|truncatechars:120 }} {% endif %} {{ n.created_at|timesince }} ago {% empty %}
{% include "partials/_nav_icon.html" with name="bell" %}

All caught up

Nothing needs you right now.

{% endfor %}
{% if notifications %}
View all notifications
{% endif %} {% comment %} The badge is not included here either: it rides on the write responses, so this partial stays a pure rendering of the panel and can be swapped in without carrying ids that live elsewhere in the shell. {% endcomment %}