{# Executive — ASI radar chart (Narrative port). 10-axis Chart.js radar of Adversarial Surface Index per category. Falls back to all-zeros so an empty/pre-run scan still renders an empty shape rather than disappearing. Mounted by executive_charts.js; offscreen mirrors values for screen readers. Payload fields consumed: - asi_rows[*].name — axis label - asi_rows[*].score_pct — radial value - asi_rows[*].code — ASI code - asi_rows[*].is_pending — pending categories plot at 0 (not dropped) QA-069 (2026-06-04): always emit ALL 10 axes. The previous build dropped ``is_pending`` rows, so early in a live scan — when only one category had a score — the radar collapsed to a single axis (a meaningless 1-spoke shape). A radar needs a stable frame: every category is an axis from the first paint, pending ones plot at 0 and fill in as scores arrive. The live SSE patcher (``executive_charts.js``) updates the values in place against this fixed axis order. #} {% set _radar_rows = [] %} {% for r in asi_rows %} {% set _ = _radar_rows.append({'label': r.name, 'value': (0 if r.is_pending else r.score_pct), 'code': r.code}) %} {% endfor %} {% set _radar_payload = { 'labels': _radar_rows | map(attribute='label') | list, 'values': _radar_rows | map(attribute='value') | list, 'codes': _radar_rows | map(attribute='code') | list, } %}
{# QA-028 sub-ask 3b — FIG. 2 eyebrow removed; the serif headline alone carries the section voice on the Executive theme. #}

Adversarial Surface Index — per category

Higher is stronger. Scores below 70 indicate the agent handled fewer than 70 % of probes in that category safely. 100 = no successful exploit; 0 = every probe in this category landed.

{# QA-045 (2026-06-02) — radar label-gutter envelope. Decorative SVG sized at 480 × 480 so the geometric envelope (4 concentric rings at 20 / 40 / 60 / 80 % of the radius) sits behind the Chart.js canvas and gives the pointLabels (Privilege abuse / Supply chain / Cascading failure) the breathing room to render without truncation. aria-hidden: the offscreen
below already carries the per-category numbers for SR users. #}
Show data table
{% for row in _radar_rows %} {% endfor %}
Category Score
{{ row.label }} {{ row.value }} / 100