{% load intelligence_extras %}
{% comment %} Header, niche identity + total gap count. {% endcomment %}
{{ result.niche.name }}
{{ result.niche.slug }}
{% if result.gaps %} {{ result.gaps|length }} gap{{ result.gaps|length|pluralize }} {% endif %}
{% if not result.gaps %}

No gaps found for this niche.

{% else %} {% comment %} One card per gap. Stacked vertically; the outer result panel scrolls if the page gets long. {% endcomment %}
{% for gap in result.gaps %}
{% comment %} Header row, gap_type pill + opportunity score badge {% endcomment %}
{% if gap.gap_type %} {% if gap.gap_type == 'underserved' %} {% elif gap.gap_type == 'stale' %} {% else %} {% endif %} {{ gap.gap_type }} {% endif %}
Opportunity {{ gap.opportunity_score }}
{% comment %} Canonical title, what's currently dominating the niche {% endcomment %}
Current top title
{{ gap.canonical_title }}
{% comment %} Why the model thinks this is a gap {% endcomment %} {% if gap.explanation %}
Why it's a gap

{{ gap.explanation }}

{% endif %} {% comment %} Suggested angles, the actionable output {% endcomment %} {% if gap.suggested_angles %}
Try these angles
    {% for angle in gap.suggested_angles %}
  • → {{ angle }}
  • {% endfor %}
{% endif %} {% comment %} Component breakdown. Demand / supply / recency are 0–1 floats. Render as three small bars with raw percentages. Tooltips explain each because the direction isn't obvious (high demand = good, but low supply / low recency = also good, they signal undersupplied or stale niches). {% endcomment %} {% if gap.components %}
Signals
Demand: search volume + trends interest. Higher is better.
Supply: how saturated the niche is with existing content. Lower is better (less competition).
Recency: how fresh the top-ranking videos are. Lower is better (existing content is older, ripe for refresh).
{% with c=gap.components %}
Demand
{{ c.demand|pretty_percent:0 }}
Supply
{{ c.supply|pretty_percent:0 }}
Recency
{{ c.recency|pretty_percent:0 }}
{% endwith %}
{% endif %} {% comment %} Evidence chips. Small at-a-glance stats: newest competitor age, trends appearances, residual outliers. Skip nulls. {% endcomment %} {% if gap.evidence %} {% with ev=gap.evidence %}
{% if ev.newest_quality_video_age_days is not None %} Newest top video: {{ ev.newest_quality_video_age_days }}d old {% endif %} {% if ev.trends_appearance_count %} {{ ev.trends_appearance_count }} trend signal{{ ev.trends_appearance_count|pluralize }} {% endif %} {% if ev.residual_outlier_count %} {{ ev.residual_outlier_count }} overperformer{{ ev.residual_outlier_count|pluralize }} {% endif %} {% if ev.autocomplete_rank %} Autocomplete rank #{{ ev.autocomplete_rank }} {% endif %}
{% comment %} Top competitors, collapsible via native
. No JS, CSP-safe, keyboard-accessible. Default closed so the card stays compact unless the user opts in. {% endcomment %} {% if ev.top_competitors %}
Top {{ ev.top_competitors|length }} competitor{{ ev.top_competitors|length|pluralize }} ranking now
    {% for c in ev.top_competitors %}
  • {{ c.title }}
    {{ c.channel_title }} {% if c.subscriber_count %}· {{ c.subscriber_count|humanize_count }} subs{% endif %} {% if c.view_count is not None %}· {{ c.view_count|humanize_count }} views{% endif %} {% if c.age_days is not None %}· {{ c.age_days }}d old{% endif %}
  • {% endfor %}
{% endif %} {% if ev.related_queries %}
Related searches
{% for q in ev.related_queries %} {{ q }} {% endfor %}
{% endif %} {% endwith %} {% endif %}
{% endfor %}
{% endif %}