{# One card per consist — the table shape doesn't work well for a variable-length member list, so we render each consist as its own inline card with an actions strip. Outer wrapper carries the id so htmx swaps replace the whole set. #}
{% if create_error %}
Couldn't create consist: {{ create_error }}
{% endif %} {% if refresh_notice %}
{{ refresh_notice }}
{% endif %} {% if not consists %}

No consists defined yet.

{% else %} {% for c in consists %}

{{ c.address }}

{{ c.kind }} {% if c.comment %}{{ c.comment }}{% endif %}
{# Members. Each row: address, direction flag, remove. #}
Members {% if not c.locos %}

no members yet

{% else %} {% for m in c.locos %} {% endfor %}
PosAddressLongOrient
{{ m.position }}{% if m.position == 0 %} lead{% endif %} {{ m.address }} {% if m.long_address %}yes{% else %}no{% endif %} {% if m.direction_normal %}forward{% else %}reversed{% endif %}
{% endif %}
{# Forward is the default orientation, so the box is only checked when a member is oriented backwards. The route translates a missing/absent value into direction_normal=True. #}
{# Fan-out controls: speed slider + direction toggle + e-stop. Speed slider dispatches on ``change`` (release) so we don't spam the CS during a drag. Slider position and the active direction button are driven from the manager's per-consist drive state (``c.speed`` / ``c.direction`` in the projection) so re-renders after a command don't wipe them out. #}
Drive {# Speed magnitude 0..1 to match the /throttle page — direction lives in the Forward/Reverse buttons below. A bipolar -1..+1 range would be dangerous UX: the natural "off" position is the middle, which reads as "stopped" visually but is ambiguous next to the direction buttons. #}
{% endfor %} {% endif %}