{# Shared cover renderer for list cards (feed + portfolio).
Single source of truth for the three-way image branch so the favicon
chip / og-image / glyph fallback never drifts between surfaces. Before
this macro the portfolio template lacked the favicon branch and stretched
a 32px /favicon.ico across the full cover, rendering it blurry.
Args:
image_url — tech_items.image_url (attacker-controllable; crawled).
category — CategoryType enum or None (tints the fallback gradient).
href — in-app reader URL for the card tap target.
source_url — used only to label the favicon chip with its domain.
transition_id — optional id. Exposed as ``data-vt`` so view-transitions.js
can name *this one* cover when navigating to (or back
from) its detail page, where the hero carries the same
name. Never named statically: on feed→feed navigations
(filter/sort) named covers would fly around on their own
while the rest of each card merely crossfaded.
#}
{% macro cover(image_url, category, href, source_url, transition_id=None) %}
{%- set cat = (category.value if category else 'none')|lower -%}
{%- set vt = (' data-vt="c' ~ transition_id.hex ~ '"') if transition_id else '' -%}
{%- if image_url and image_url | is_favicon -%}
{# image_url is attacker-controllable (crawled): escaped only, never CSS url(). #}
{{ source_url | domain }}
{%- elif image_url -%}
{# og:image is attacker-controllable (crawled): render via an HTML-escaped
rather than inline CSS url() to avoid CSS-context injection. #}
{%- else -%}
{%- endif -%}
{% endmacro %}
{# Source stack (ARG-243) — how many outlets reported this, at a glance.
Overlapping monogram discs, one per distinct domain (first four, earliest
report first), then the count. A lone document shows one disc and
"출처 1곳" — the same component, so single and multi-source cards never
look like two different things. Monograms instead of fetched favicons: no
third-party request per card. #}
{% macro source_stack(domains, doc_count=1) %}
{%- set n = domains | length -%}
출처 {{ n }}곳{% if doc_count > n %} 기사 {{ doc_count }}건{% endif %}
{%- endmacro %}