{{/* app_list_row / app_list_row_end — THE canonical app-list row (v0.126.0).
     One grammar on every list surface: icon + name (+ optional one-line secondary) left,
     caller-provided action block right. A row without a secondary line stays compact.

     Usage (the layout_start/layout_end idiom):
         {{template "app_list_row" dict "Slug" .Slug "Name" .DisplayName}}
             ...caller action buttons / status...
         {{template "app_list_row_end"}}

     dict keys: Slug (icon lookup), Name, Secondary (optional one-liner),
     RowClass (optional extra row class, e.g. stack-state-run), Href (optional data-href),
     FallbackIcon (optional last-resort icon URL). DEFAULTS to /static/app-placeholder.svg (the
     generic app-grid glyph) when a caller omits it — so a logo-less app shows a placeholder, not a
     hidden dead-end. Infra rows pass /static/infra-logo.svg to override that with the server glyph.

     Do NOT hand-roll app rows — scripts/app_row_dedup_gate.py asserts this markup exists
     here ONCE (the backups_apps expander header is the single allowlisted aligned copy). */}}
{{define "app_list_row"}}
<div class="app-row{{with .RowClass}} {{.}}{{end}}"{{with .Href}} data-href="{{.}}"{{end}}>
    <img class="app-row-icon" src="{{logoURL .Slug}}" alt="" data-fallback="{{if .FallbackIcon}}{{.FallbackIcon}}{{else}}/static/app-placeholder.svg{{end}}"
         onerror="if(!this.dataset.step){this.dataset.step='1';this.src='{{logoPNGURL .Slug}}';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
    <div class="app-row-text">
        <span class="app-row-name">{{.Name}}</span>
        {{with .Secondary}}<span class="app-row-secondary">{{.}}</span>{{end}}
    </div>
    <div class="app-row-actions">
{{end}}

{{define "app_list_row_end"}}
    </div>
</div>
{{end}}
