For each field in order: resolve its value, skip it when empty EXCEPT for the two
required kinds (a title field and a badge), render it through the shared widget
dispatch, and wrap it in a collapsible section when the declaration asks for one.
Gallery events are wired against the shared lightbox singleton afterwards, and
accordions get exclusive-open behaviour — opening one collapses the others.
⚠️ A field with no declared widget used to be dropped BEFORE the dispatch, which
is what made "all" render an empty panel: the implicit fallback emitted
descriptors carrying a path and nothing else, and every one of them was skipped
here. The guard is gone with "all" itself — an undeclared widget now defaults
to text, exactly as it always did on the popup, so the two surfaces agree.
Builds the side-panel body for a feature.
For each field in order: resolve its value, skip it when empty EXCEPT for the two required kinds (a title field and a badge), render it through the shared widget dispatch, and wrap it in a collapsible section when the declaration asks for one.
Gallery events are wired against the shared lightbox singleton afterwards, and accordions get exclusive-open behaviour — opening one collapses the others.
⚠️ A field with no declared widget used to be dropped BEFORE the dispatch, which is what made
"all"render an empty panel: the implicit fallback emitted descriptors carrying a path and nothing else, and every one of them was skipped here. The guard is gone with"all"itself — an undeclared widget now defaults totext, exactly as it always did on the popup, so the two surfaces agree.