# Inline Filters (Pill-as-Dropdown) Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Replace the modal-based filter form on `/report/` with interactive pill-dropdowns (Apply button appears only when pending changes exist, bidirectional project↔team cross-filter auto-removes invalid selections), and retire `_report_config_modal.html` entirely. **Architecture:** Template-only change except for one backend tweak. The existing filter-pill strip (shipped with Executive Report v2) becomes clickable — each pill opens a Bootstrap-like popover containing the relevant editable widget (date picker or Choices.js multi-select). A scoped IIFE JS module inside `report.html` manages popover state, tracks dirty filters, and rebuilds the submit URL on Apply. Cross-filter data comes from a single JSON ` ``` **Step 4.3: Check syntax** ```bash USE_SQLITE=true DJANGO_DEBUG=true python manage.py check ``` Expected: only the `staticfiles.W004` warning. **Step 4.4: Commit** ```bash git add core/templates/core/report.html git commit -m "$(cat <<'EOF' JS: pill-popover interactive module for inline filters One scoped IIFE that wires up the three filter pills into an interactive, state-managed UI: - Click pill -> open popover (lazy-inits Choices.js on first open) - Click outside / Esc / other pill -> close - OK commits popover's local edits into pending state; dirty pills get orange outline + pulsing dot; Apply button slides in at the right end of the strip - Cross-filter: picking projects auto-removes now-invalid teams with toast notice ("Removed Team X — no logs on selected projects"), and vice versa. Scope = entire history. - Apply -> rebuilds querystring from pending state + navigates (full page reload, same URL scheme as the retired modal) - Reset -> reverts all pills to URL-current values XSS-safe throughout: textContent and createElement; no innerHTML with user data. Matches the pattern in base.html's work-log-payroll modal from the previous feature. Graceful fallback: if Choices.js CDN fails to load, the module bails early with a console warning; native pre-selection: stringify the IDs so # the template's `{% if p.id|stringformat:"s" in selected_project_ids %}` # comparison works (Django templates compare strings to strings). context['selected_project_ids'] = [str(p) for p in (project_ids or [])] context['selected_team_ids'] = [str(t) for t in (team_ids or [])] ``` **Keep** the `projects` / `teams` context keys (the pills' popovers still use them to render `