{# Funnel chart region. Stacked proportional bars rendering conversion through ordered stages. Previously this template wrapped its own chrome (`card bg-card shadow`) and emitted its own title. Converted to the shared region_card pattern per #794 second follow-up: dashboard slot owns chrome + title. Contract: ~/.claude/skills/ux-architect/components/funnel-chart-region.md (UX-070) v0.62 CSS refactor: per-stage opacity fade now driven by `data-dz-funnel-step="{idx}"` attribute selectors in components/regions.css — replaces the dynamic `bg-[hsl(var(--primary)/{{ pct }})]` Tailwind-arbitrary value that was JIT-invisible to downstream consumers. #} {% from 'macros/region_wrapper.html' import region_card %} {% call region_card(None) %}
{% if kanban_columns and items and group_by %} {# Funnel uses kanban_columns for ordered stages #} {% set stage_counts = {} %} {% for item in items %} {% set key = item[group_by] | default("Unknown") %} {% if key in stage_counts %} {% set _ = stage_counts.update({key: stage_counts[key] + 1}) %} {% else %} {% set _ = stage_counts.update({key: 1}) %} {% endif %} {% endfor %} {% set first_count = stage_counts.get(kanban_columns[0], 0) if kanban_columns else 1 %} {% set base = first_count if first_count > 0 else 1 %}
{% for stage in kanban_columns %} {% set count = stage_counts.get(stage, 0) %} {% set pct = (count / base * 100) | int %} {% set width = pct if pct >= 20 else 20 %}
{{ stage }} ({{ count }})
{% endfor %}

{{ total }} total

{% elif metrics %} {# Fallback: render metrics as funnel stages #} {% set first_val = metrics[0].value if metrics else 1 %} {% set base = first_val if first_val > 0 else 1 %}
{% for metric in metrics %} {% set pct = (metric.value / base * 100) | int %} {% set width = pct if pct >= 20 else 20 %}
{{ metric.label }} ({{ metric.value }})
{% endfor %}
{% else %}

{{ empty_message | default("No data available.") }}

{% endif %}
{% endcall %}