{#
  A reply as the sequence it was: thinking, prose, a tool call, more prose.

  Used by BOTH branches of `_message.html` -- the streaming shell and the
  finished bubble -- from the same builder, so a reply cannot rearrange itself
  the moment the stream ends. That was the point of putting the marks on the row
  rather than only on the running generation.

  Closed steps ONLY. The step still being written lives in `_message.html`, in
  two containers that are **siblings of this one, never inside it**.

  That is not tidiness, it is the whole reason this works. This container is
  itself an `sse-swap` target: every time a round closes, its `innerHTML` is
  replaced. Anything inside it carrying an `sse-swap` of its own is therefore
  torn out and rebuilt at every round boundary — and the frames aimed at it in
  the same pass have nowhere to land. Nesting them here is what made an agent
  reply show nothing at all from its first tool call onwards, while an ordinary
  chat was fine: an ordinary chat closes no steps, so the swap never happened.

  One `sse-swap` element must never contain another. There is a test.
#}
{% for step in steps %}
  {% include "chat/_step.html" %}
{% endfor %}
