{% block title %}DemandTrail Messaging{% endblock %}
{% comment %}
**Leg 1 of the anti-flash contract.** The sidebar can start a page at
either of two widths, and the stored one has to be known before first
paint — so this runs synchronously, ahead of the stylesheet's effect and
long before Alpine. Legs 2 and 3 are the
{% block extra_head %}{% endblock %}
{% csrf_token %}
{% include "partials/_toast_host.html" %}
{% if show_app_shell %}
{% comment %}
Two pieces of shell state. `sidebarCollapsed` is desktop-only and persisted —
x-effect writes it back on every change through the head script's guarded
helper, and the pre-paint script beside that helper reads it before Alpine
exists. `mobileMenuOpen` is the drawer and is deliberately NOT persisted: a
drawer that reopens itself on the next page is a drawer covering the page
you asked for.
`wsOpen` used to be here because two controls opened one panel. There is one
trigger now, so it lives on the switcher — see partials/_app_sidebar.html.
{% endcomment %}
{% comment %}
`lg:hidden`, never a bare `hidden`: Alpine shows an element by clearing
its inline display, after which an unconditional display:none utility
reasserts itself and the element stays invisible forever. A responsive
variant is fine, and
test_no_element_combines_x_show_with_a_display_none_utility splits on
tokens for exactly that reason.
{% endcomment %}
{% comment %}
The one sidebar, on every page of the product. The block is not a seam
for replacing it — a second
{% else %}
{% comment %}
Issue #31 owns the auth templates. They extend this file and fill this
block; .auth-card and .auth-bg are already in the design system, so a
login page is `{% extends "base.html" %}` plus a card.
{% endcomment %}
{% block auth_content %}{% endblock %}
{% endif %}
{% comment %}
Vendored JS (deviation 6). Studio pulls flatpickr, Chart.js and Sortable off a
public CDN, which forces that origin into script-src and style-src. Every
library here is served from 'self'; see scripts/vendor-js.mjs.
A Django comment rather than an HTML one on purpose: an HTML comment ships to
the browser, so a page documenting the removal of a CDN origin would still
contain that origin's hostname — and the test asserting no CDN reference
survives in any rendered template would fail on its own explanation.
Order is load-bearing: htmx synchronously first, then any inline script that
DEFINES an Alpine component, then Alpine itself deferred so those definitions
exist by the time it boots.
{% endcomment %}
{% include "partials/_modal_behaviours.html" %}
{% block alpine_components %}{% endblock %}
{% block extra_js %}{% endblock %}