{# Inline Chart.js radar — ASI sub-scores per category. Reads payload data from a `data-chart` JSON attribute so this partial is trivial to render in tests without a JS runtime. The actual chart is mounted by narrative_charts.js on DOMContentLoaded; canvas mirrors the data in an offscreen for screen-reader access (AC: a11y). #} {% set _radar_rows = [] %} {% for r in asi_rows %} {% if not r.is_pending %} {% set _ = _radar_rows.append({'label': r.name, 'value': r.score_pct, 'code': r.code}) %} {% endif %} {% endfor %} {# Fallback: if every row is pending (pre-run), seed all to 0 so the radar still renders an empty shape instead of disappearing. #} {% if not _radar_rows %} {% for r in asi_rows %} {% set _ = _radar_rows.append({'label': r.name, 'value': 0, 'code': r.code}) %} {% endfor %} {% endif %} {% set _radar_payload = { 'labels': _radar_rows | map(attribute='label') | list, 'values': _radar_rows | map(attribute='value') | list, 'codes': _radar_rows | map(attribute='code') | list, } %}
FIG. 1

Adversarial Surface Index — per category

Higher is stronger. 100 = no successful exploit in this category; 0 = every probe in this category landed.

Show data table
{% for row in _radar_rows %} {% endfor %}
CategoryScore
{{ row.label }} {{ row.value }} / 100