{% comment %} One nav row's badge, in both of its states. Shared by the shell's first render (partials/_sidebar_items.html) and by the out-of-band response fragments that keep the number live (notifications/partials/_badge.html), because the two have to agree: an out-of-band swap replaces the whole element, so the markup one swap puts in is the markup the next swap has to find. **Zero is an empty slot, not nothing.** The count is polled — _bell.html refetches it every 60s — and htmx resolves an out-of-band target by id, so a row whose badge is simply absent at zero gives every poll nothing to swap into and logs `htmx:oobErrorNoTarget` once a minute for the whole session. Rendering the span unconditionally, empty and `hidden`, gives the swap a permanent target without breaking the shell's contract that a zero badge shows nothing at all: the `sidebar-badge` class *is* the visible pill, and it is on the non-zero branch only. An empty `display: none` span takes no space either, so it cannot push the glyph off-centre in the .sidebar-nav-item it sits in — the one place this slot is rendered, now that the settings rows are sidebar rows too. Args: key: the nav item's key. The id is `nav-badge-`. count: the number to show. Falsy renders the empty slot. oob: True in a response fragment, where the element carries hx-swap-oob. Never set on a first render — hx-swap-oob is meaningful only in a response, and apps/notifications/tests/test_shell.py fails the page if one leaks in. {% endcomment %}{% spaceless %} {% if count %} {{ count }} {% else %} {% endif %} {% endspaceless %}