{# @var form \Symfony\Component\Form\FormView #}
{# @var categories list<\Ferienpass\CoreBundle\Entity\OfferCategory> #}

{# ---------- Helpers ---------- #}

{% macro queryWithout(keys) %}
    {%- set q = app.request.query.all -%}
    {%- set q = q|merge({page: null}) -%}
    {%- for k in keys -%}{% set q = q|merge({(k): null}) %}{%- endfor -%}
    {{- path(app.current_route, (app.current_route_parameters|merge(q))|filter(v => v))|raw -}}
{% endmacro %}

{% macro singleUrl(key, value) %}
    {%- set q = app.request.query.all -%}
    {%- set q = q|merge({page: null}) -%}
    {%- set current = q[key]|default(null) -%}
    {%- if current == value -%}{% set q = q|merge({(key): null}) %}{%- else -%}{% set q = q|merge({(key): value}) %}{%- endif -%}
    {{- path(app.current_route, (app.current_route_parameters|merge(q))|filter(v => v))|raw -}}
{% endmacro %}

{% macro setUrl(key, value) %}
    {%- set q = app.request.query.all -%}
    {%- set q = q|merge({page: null, (key): value}) -%}
    {{- path(app.current_route, (app.current_route_parameters|merge(q))|filter(v => v))|raw -}}
{% endmacro %}

{% macro clearUrl(key) %}
    {%- set q = app.request.query.all -%}
    {%- set q = q|merge({page: null, (key): null}) -%}
    {{- path(app.current_route, (app.current_route_parameters|merge(q))|filter(v => v))|raw -}}
{% endmacro %}

{#
    Toggle URL for a multi-value filter — emits `?key=v1,v2,v3` with values
    sorted alphabetically. Sorting canonicalizes the URL: clicking three
    chips in any order produces an identical URL, which helps the HTTP
    cache hit rate.
#}
{% macro multiUrl(key, value) %}
    {%- set q = app.request.query.all -%}
    {%- set q = q|merge({page: null}) -%}
    {# `?? ''` for missing keys (strict_variables). An iterable here means a
       legacy `?key[]=` URL — treat as "no prior selection" so the click
       canonicalises the URL to the comma-string form. #}
    {%- set raw = q[key] ?? '' -%}
    {%- set current = (raw is iterable or raw is empty) ? [] : raw|split(',') -%}
    {%- if value in current -%}
        {%- set current = current|filter(v => v != value) -%}
    {%- else -%}
        {%- set current = current|merge([value]) -%}
    {%- endif -%}
    {%- set current = current|sort -%}
    {%- set q = q|merge({(key): current|length ? current|join(',') : null}) -%}
    {{- path(app.current_route, (app.current_route_parameters|merge(q))|filter(v => v))|raw -}}
{% endmacro %}

{% macro categoryIcon(alias) %}
    {%- set map = {
        'kochen': 'lucide:chef-hat',
        'kochen-backen': 'lucide:chef-hat',
        'backen': 'lucide:chef-hat',
        'basteln': 'lucide:palette',
        'basteln-werken': 'lucide:palette',
        'kreativ': 'lucide:palette',
        'werken': 'lucide:hammer',
        'sport': 'lucide:volleyball',
        'bewegung': 'lucide:dumbbell',
        'ausflug': 'lucide:bus',
        'ausfluege': 'lucide:bus',
        'tiere': 'lucide:bird',
        'natur': 'lucide:leaf',
        'tiere-natur': 'lucide:leaf',
        'wissen': 'lucide:book-open',
        'bildung': 'lucide:book-open',
        'abenteuer': 'lucide:tent',
        'theater': 'lucide:drama',
        'kultur': 'lucide:drama',
        'theater-kultur': 'lucide:drama',
        'musik': 'lucide:music',
        'medien': 'lucide:clapperboard',
        'technik': 'lucide:wrench',
        'wasser': 'lucide:waves',
        'reisen': 'lucide:map',
        'spielen': 'lucide:joystick'
    } -%}
    {{- map[alias]|default('lucide:tag') -}}
{% endmacro %}

{# ---------- State derived from URL ---------- #}

{# Multi-value filters use a single comma-separated string only:
   `?kategorie=sport,basteln&alter=3-6,7&liste=<uuid1>,<uuid2>`. We sort
   values alphabetically before serializing so the same selection in any
   click order produces an identical URL — better cache hit rate.

   We read multi-value params via `q.all[…]` instead of `q.get(…)`:
   InputBag::get() throws BadRequestException on non-scalar values, so a
   stray legacy URL like `?kategorie[]=sport` would otherwise 500. Anything
   non-scalar here is treated as "no filter" (it's a malformed URL we
   intentionally don't support). #}

{% set q = app.request.query %}
{% set qSearch = q.get('titel', '') %}

{% set qCatsRaw = q.all['kategorie'] ?? '' %}
{% set qCats = (qCatsRaw is iterable or qCatsRaw is empty) ? [] : qCatsRaw|split(',')|filter(v => v != '') %}

{% set qWhen = q.get('wann', '') %}
{% set qVon = q.get('von', '') %}
{% set qBis = q.get('bis', '') %}

{% set qAgesRaw = q.all['alter'] ?? '' %}
{% set qAges = (qAgesRaw is iterable or qAgesRaw is empty) ? [] : qAgesRaw|split(',')|filter(v => v != '') %}
{% set qPreis = q.get('preis', '') %}
{% set qGebuehr = q.get('gebuehr', '') %}
{% set qExtras = {
    sofortbuchung: q.get('sofortbuchung'),
    ohne_anmeldung: q.get('ohne_anmeldung'),
    rollstuhl: q.get('rollstuhl'),
    kostenlos: q.get('kostenlos'),
} %}

{# When von/bis is set without a preset key, treat it as the "Eigener Zeitraum" preset #}
{% set effectiveWhen = qWhen %}
{% if not effectiveWhen and (qVon or qBis) %}{% set effectiveWhen = 'eigen' %}{% endif %}

{# Price preset implicitly "custom" when gebuehr set without preset key, "0" if kostenlos #}
{% set effectivePreis = qPreis %}
{% if not effectivePreis and qExtras.kostenlos %}{% set effectivePreis = '0' %}{% endif %}
{% if not effectivePreis and qGebuehr %}{% set effectivePreis = 'custom' %}{% endif %}

{% set bandValues = ['3-6', '6-12', '12-16', '16+'] %}
{% set ageBands = qAges|filter(v => v in bandValues) %}
{% set ageNumbers = qAges|filter(v => v not in bandValues) %}

{# ---------- Markup ---------- #}

{# Note: the wishlists controller is mounted on the parent <turbo-frame>
   (in fragment/offer_list.html.twig), not here — its targets need to
   include both the popover/toast/trigger inside this section AND the offer
   cards in the sibling masonry section, which only works if the controller
   root contains both. #}
<section {{ attributes.defaults({
    'data-controller': 'ferienpass--cms-filter',
    'aria-label': 'filter.ariaLabel'|trans({}, 'cms'),
    class: 'rounded-md border border-gray-200 bg-white p-5 dark:border-white/5 dark:bg-gray-800 sm:p-6',
})|raw }}>

    <form method="get"
          data-ferienpass--cms-filter-target="form"
          data-action="change->ferienpass--cms-filter#submit submit->ferienpass--cms-filter#beforeSubmit"
          class="space-y-6">

        {# --- Search --- #}
        <div class="relative flex items-center rounded-xl bg-gray-50 ring-1 ring-transparent focus-within:bg-white focus-within:ring-primary-500/40 focus-within:shadow-sm dark:bg-white/5 dark:focus-within:bg-white/10">
            <twig:ux:icon name="lucide:search" class="ml-4 h-4 w-4 text-gray-400" />
            <input type="search"
                   name="titel"
                   value="{{ qSearch }}"
                   placeholder="{{ 'filter.searchPlaceholder'|trans({}, 'cms') }}"
                   autocomplete="off"
                   data-ferienpass--cms-filter-target="search"
                   data-action="input->ferienpass--cms-filter#debounceSubmit keydown.enter->ferienpass--cms-filter#submit"
                   class="!w-full !border-0 !bg-transparent !shadow-none focus:!ring-0 focus:!border-0 text-base placeholder:text-gray-400" />
        </div>

        {# Hidden inputs carry current chip state across free-text submits.
           Multi-value filters use the comma-joined form so the canonical URL
           stays compact (also helpful for cache-hit rate). #}
        {% if qCats|length %}<input type="hidden" name="kategorie" value="{{ qCats|sort|join(',') }}">{% endif %}
        {% if qAges|length %}<input type="hidden" name="alter" value="{{ qAges|sort|join(',') }}">{% endif %}
        {% if qWhen %}<input type="hidden" name="wann" value="{{ qWhen }}">{% endif %}
        {% if qPreis %}<input type="hidden" name="preis" value="{{ qPreis }}">{% endif %}
        {% for k, v in qExtras %}{% if v %}<input type="hidden" name="{{ k }}" value="{{ v }}">{% endif %}{% endfor %}
        {# Preserve `liste` (active wishlist filter) across form-submit
           navigations triggered by the search input or custom range fields. #}
        {% set qListeRaw = q.all['liste'] ?? '' %}
        {% if qListeRaw is not iterable and qListeRaw is not empty %}<input type="hidden" name="liste" value="{{ qListeRaw }}">{% endif %}

        {# --- Categories --- #}
        {% if categories|length %}
            <div>
                <div class="mb-2.5 flex items-baseline gap-2">
                    <span class="text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400">{{ 'filter.section.what'|trans({}, 'cms') }}</span>
                    <span class="text-xs text-gray-400">{{ 'filter.hint.multi'|trans({}, 'cms') }}</span>
                </div>
                <div class="grid grid-cols-2 gap-2 sm:grid-cols-4 lg:grid-cols-8">
                    {% for cat in categories %}
                        {% set pressed = cat.alias in qCats %}
                        <a href="{{ _self.multiUrl('kategorie', cat.alias) }}"
                           aria-pressed="{{ pressed ? 'true' : 'false' }}"
                           class="group flex flex-col items-center gap-2 rounded-xl border border-gray-200 bg-white px-2 py-3 text-center text-xs font-medium leading-tight text-gray-700 transition hover:border-gray-300 hover:bg-gray-50 active:translate-y-px aria-pressed:border-primary-500 aria-pressed:bg-primary-50 aria-pressed:text-primary-900 dark:border-white/10 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-white/5 dark:aria-pressed:border-primary-400 dark:aria-pressed:bg-primary-400/15">
                            <span class="grid h-10 w-10 place-items-center rounded-full bg-gray-100 text-gray-700 transition group-aria-pressed:bg-primary-600 group-aria-pressed:text-white dark:bg-white/10 dark:text-gray-200">
                                <twig:ux:icon :name="_self.categoryIcon(cat.alias)|trim" class="h-5 w-5" />
                            </span>
                            {# Category names come from the OfferCategory entity in the
                               tenant DB — authored in German. machine_translate auto-
                               renders them in the visitor's locale via DeepL (see
                               MachineTranslationExtension). Same pattern as
                               offer_list_item.html.twig:82 for offer titles. #}
                            <span>{{ cat.name|machine_translate }}</span>
                        </a>
                    {% endfor %}
                </div>
            </div>
        {% endif %}

        <div class="grid gap-6 md:grid-cols-3">
            {# --- When --- #}
            <div>
                <div class="mb-2.5 flex items-baseline gap-2">
                    <span class="text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400">{{ 'filter.section.when'|trans({}, 'cms') }}</span>
                </div>
                <div class="flex flex-wrap gap-2">
                    {% set whenOptions = [
                        ['', 'filter.when.any'],
                        ['heute', 'filter.when.today'],
                        ['wochenende', 'filter.when.weekend'],
                        ['woche', 'filter.when.week'],
                        ['monat', 'filter.when.month'],
                        ['eigen', 'filter.when.custom'],
                    ] %}
                    {% for opt in whenOptions %}
                        {% set value = opt[0] %}
                        {% set label = opt[1] %}
                        {% set pressed = (value == '' and not effectiveWhen) or (value != '' and effectiveWhen == value) %}
                        {% if value == '' %}
                            {% set href = _self.queryWithout(['wann', 'von', 'bis']) %}
                        {% elseif value == 'eigen' %}
                            {% set href = _self.setUrl('wann', 'eigen') %}
                        {% else %}
                            {% set q2 = app.request.query.all|merge({page: null, von: null, bis: null, wann: value}) %}
                            {% set href = path(app.current_route, (app.current_route_parameters|merge(q2))|filter(v => v)) %}
                        {% endif %}
                        <a href="{{ href }}"
                           aria-pressed="{{ pressed ? 'true' : 'false' }}"
                           class="inline-flex items-center gap-1.5 rounded-full border border-gray-200 bg-white px-3.5 py-2 text-sm font-medium text-gray-700 transition hover:border-gray-300 hover:bg-gray-50 aria-pressed:border-gray-900 aria-pressed:bg-gray-900 aria-pressed:text-white dark:border-white/10 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-white/5 dark:aria-pressed:bg-white dark:aria-pressed:text-gray-900">
                            {{ label|trans({}, 'cms') }}
                        </a>
                    {% endfor %}
                </div>
                <div class="{{ effectiveWhen == 'eigen' ? 'mt-3' : 'hidden' }} grid grid-cols-2 gap-2">
                    <label class="block rounded-lg bg-gray-50 px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:bg-white/5 dark:text-gray-400">
                        {{ 'filter.dateFrom'|trans({}, 'cms') }}
                        <input type="date" name="von" value="{{ qVon }}" class="!mt-0.5 !block !w-full !border-0 !bg-transparent !p-0 !text-sm !font-normal !uppercase !tracking-normal !text-gray-900 focus:!ring-0 dark:!text-gray-100" />
                    </label>
                    <label class="block rounded-lg bg-gray-50 px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:bg-white/5 dark:text-gray-400">
                        {{ 'filter.dateTo'|trans({}, 'cms') }}
                        <input type="date" name="bis" value="{{ qBis }}" class="!mt-0.5 !block !w-full !border-0 !bg-transparent !p-0 !text-sm !font-normal !uppercase !tracking-normal !text-gray-900 focus:!ring-0 dark:!text-gray-100" />
                    </label>
                </div>
            </div>

            {# --- Age --- #}
            <div>
                <div class="mb-2.5 flex items-baseline gap-2">
                    <span class="text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400">{{ 'filter.section.age'|trans({}, 'cms') }}</span>
                    <span class="text-xs text-gray-400">{{ 'filter.hint.multi'|trans({}, 'cms') }}</span>
                </div>
                <div class="flex flex-wrap items-center gap-2">
                    {% set anyAgePressed = qAges is empty %}
                    <a href="{{ _self.clearUrl('alter') }}"
                       aria-pressed="{{ anyAgePressed ? 'true' : 'false' }}"
                       class="inline-flex items-center gap-1.5 rounded-full border border-gray-200 bg-white px-3.5 py-2 text-sm font-medium text-gray-700 transition hover:border-gray-300 hover:bg-gray-50 aria-pressed:border-gray-900 aria-pressed:bg-gray-900 aria-pressed:text-white dark:border-white/10 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-white/5 dark:aria-pressed:bg-white dark:aria-pressed:text-gray-900">
                        {{ 'filter.age.any'|trans({}, 'cms') }}
                    </a>
                    {% for band in bandValues %}
                        {% set pressed = band in ageBands %}
                        {% set labelKey = 'filter.age.band.' ~ band|replace({'+': 'plus'}) %}
                        <a href="{{ _self.multiUrl('alter', band) }}"
                           aria-pressed="{{ pressed ? 'true' : 'false' }}"
                           class="inline-flex items-center gap-1.5 rounded-full border border-gray-200 bg-white px-3.5 py-2 text-sm font-medium text-gray-700 transition hover:border-gray-300 hover:bg-gray-50 aria-pressed:border-gray-900 aria-pressed:bg-gray-900 aria-pressed:text-white dark:border-white/10 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-white/5 dark:aria-pressed:bg-white dark:aria-pressed:text-gray-900">
                            {{ labelKey|trans({}, 'cms') }}
                        </a>
                    {% endfor %}
                    {% for n in ageNumbers %}
                        <span class="inline-flex items-center gap-1 rounded-full border border-primary-200 bg-primary-50 py-1 pl-3 pr-1 text-sm font-medium text-primary-900 dark:border-primary-400/30 dark:bg-primary-400/15 dark:text-primary-100">
                            {{ 'filter.age.years'|trans({value: n}, 'cms') }}
                            <a href="{{ _self.multiUrl('alter', n) }}" aria-label="{{ 'filter.remove'|trans({}, 'cms') }}"
                               class="grid h-5 w-5 place-items-center rounded-full bg-black/10 text-current hover:bg-black/20">
                                <twig:ux:icon name="lucide:x" class="h-3 w-3" />
                            </a>
                        </span>
                    {% endfor %}
                    <span class="inline-flex items-center gap-1.5 rounded-full border border-dashed border-gray-300 bg-white py-1 pl-3 pr-1 text-sm text-gray-600 dark:border-white/15 dark:bg-gray-800 dark:text-gray-300"
                          data-ferienpass--cms-filter-target="ageAdd">
                        <input type="number" min="1" max="27" placeholder="{{ 'filter.age.addPlaceholder'|trans({}, 'cms') }}"
                               data-ferienpass--cms-filter-target="ageInput"
                               data-action="input->ferienpass--cms-filter#updateAgeAdd keydown.enter->ferienpass--cms-filter#addAge"
                               class="!w-20 !border-0 !bg-transparent !p-0 !text-sm !font-medium !shadow-none focus:!ring-0 placeholder:!text-gray-400" />
                        <button type="button"
                                data-ferienpass--cms-filter-target="ageAddBtn"
                                data-action="ferienpass--cms-filter#addAge"
                                aria-label="{{ 'filter.age.add'|trans({}, 'cms') }}"
                                disabled
                                class="grid h-6 w-6 place-items-center rounded-full bg-gray-900 text-white disabled:bg-gray-300 dark:bg-white dark:text-gray-900 dark:disabled:bg-white/30">
                            <twig:ux:icon name="lucide:plus" class="h-3.5 w-3.5" />
                        </button>
                    </span>
                </div>
                <p class="mt-2 text-xs text-gray-500 dark:text-gray-400">{{ 'filter.age.tip'|trans({}, 'cms')|raw }}</p>
            </div>

            {# --- Price --- #}
            <div>
                <div class="mb-2.5 flex items-baseline gap-2">
                    <span class="text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400">{{ 'filter.section.price'|trans({}, 'cms') }}</span>
                </div>
                <div class="flex flex-wrap gap-2">
                    {% set priceOptions = [
                        ['', 'filter.price.any', false],
                        ['0', 'filter.price.free', true],
                        ['10', 'filter.price.upTo10', false],
                        ['25', 'filter.price.upTo25', false],
                        ['custom', 'filter.price.custom', false],
                    ] %}
                    {% for opt in priceOptions %}
                        {% set value = opt[0] %}
                        {% set label = opt[1] %}
                        {% set isFree = opt[2] %}
                        {% set pressed = (value == '' and not effectivePreis) or (value != '' and effectivePreis == value) %}
                        {% if value == '' %}
                            {% set href = _self.queryWithout(['preis', 'kostenlos', 'gebuehr']) %}
                        {% else %}
                            {% set q2 = app.request.query.all|merge({page: null, kostenlos: null, gebuehr: null, preis: value}) %}
                            {% set href = path(app.current_route, (app.current_route_parameters|merge(q2))|filter(v => v)) %}
                        {% endif %}
                        <a href="{{ href }}"
                           aria-pressed="{{ pressed ? 'true' : 'false' }}"
                           class="inline-flex items-center gap-1.5 rounded-full border border-gray-200 bg-white px-3.5 py-2 text-sm font-medium text-gray-700 transition hover:border-gray-300 hover:bg-gray-50 {% if isFree %}aria-pressed:border-green-700 aria-pressed:bg-green-700 aria-pressed:text-white{% else %}aria-pressed:border-gray-900 aria-pressed:bg-gray-900 aria-pressed:text-white{% endif %} dark:border-white/10 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-white/5 dark:aria-pressed:bg-white dark:aria-pressed:text-gray-900">
                            {{ label|trans({}, 'cms') }}
                        </a>
                    {% endfor %}
                </div>
                <div class="{{ effectivePreis == 'custom' ? 'mt-3' : 'hidden' }}">
                    <label class="block rounded-lg bg-gray-50 px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:bg-white/5 dark:text-gray-400">
                        {{ 'filter.price.maxLabel'|trans({}, 'cms') }}
                        <input type="number" min="0" step="any" name="gebuehr" value="{{ qGebuehr }}"
                               placeholder="{{ 'filter.price.maxPlaceholder'|trans({}, 'cms') }}"
                               class="!mt-0.5 !block !w-full !border-0 !bg-transparent !p-0 !text-sm !font-normal !uppercase !tracking-normal !text-gray-900 focus:!ring-0 dark:!text-gray-100" />
                    </label>
                </div>
            </div>
        </div>

        {# --- Extras --- #}
        <div>
            <div class="mb-2.5 flex items-baseline gap-2">
                <span class="text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400">{{ 'filter.section.extras'|trans({}, 'cms') }}</span>
                <span class="text-xs text-gray-400">{{ 'filter.hint.optional'|trans({}, 'cms') }}</span>
            </div>
            <div class="flex flex-wrap gap-2">
                {% if applicationSystem is not null and applicationSystem.task.applicationSystem == 'firstcome' %}
                    <a href="{{ _self.singleUrl('sofortbuchung', 'ja') }}"
                       aria-pressed="{{ qExtras.sofortbuchung ? 'true' : 'false' }}"
                       class="inline-flex items-center gap-1.5 rounded-full border border-gray-200 bg-white px-3.5 py-2 text-sm font-medium text-gray-700 transition hover:border-gray-300 hover:bg-gray-50 aria-pressed:border-amber-600 aria-pressed:bg-amber-600 aria-pressed:text-white dark:border-white/10 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-white/5">
                        <twig:ux:icon name="lucide:zap" class="h-3.5 w-3.5" />
                        {{ 'filter.chip.instant'|trans({}, 'cms') }}
                    </a>
                {% endif %}
                <a href="{{ _self.singleUrl('ohne_anmeldung', 'ja') }}"
                   aria-pressed="{{ qExtras.ohne_anmeldung ? 'true' : 'false' }}"
                   class="inline-flex items-center gap-1.5 rounded-full border border-gray-200 bg-white px-3.5 py-2 text-sm font-medium text-gray-700 transition hover:border-gray-300 hover:bg-gray-50 aria-pressed:border-gray-900 aria-pressed:bg-gray-900 aria-pressed:text-white dark:border-white/10 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-white/5">
                    {{ 'filter.chip.noApplication'|trans({}, 'cms') }}
                </a>
                <a href="{{ _self.singleUrl('rollstuhl', 'ja') }}"
                   aria-pressed="{{ qExtras.rollstuhl ? 'true' : 'false' }}"
                   class="inline-flex items-center gap-1.5 rounded-full border border-gray-200 bg-white px-3.5 py-2 text-sm font-medium text-gray-700 transition hover:border-gray-300 hover:bg-gray-50 aria-pressed:border-gray-900 aria-pressed:bg-gray-900 aria-pressed:text-white dark:border-white/10 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-white/5">
                    <twig:ux:icon name="lucide:accessibility" class="h-3.5 w-3.5" />
                    {{ 'filter.chip.wheelchair'|trans({}, 'cms') }}
                </a>
                {% if 'cms_favorites_link' != app.current_route %}
                    <button type="button"
                            data-action="ferienpass--cms-wishlists#openFilterMenu"
                            data-ferienpass--cms-wishlists-target="filterTrigger"
                            aria-haspopup="dialog"
                            aria-expanded="false"
                            class="inline-flex items-center gap-1.5 rounded-full border border-gray-200 bg-white px-3.5 py-2 text-sm font-medium text-gray-700 transition hover:border-gray-300 hover:bg-gray-50 aria-pressed:border-amber-600 aria-pressed:bg-amber-600 aria-pressed:text-white dark:border-white/10 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-white/5">
                        <twig:ux:icon name="lucide:heart" class="h-3.5 w-3.5" />
                        {{ 'filter.chip.wishlists'|trans({}, 'cms') }}
                        <span data-ferienpass--cms-wishlists-target="filterBadge" hidden
                              class="grid h-5 min-w-5 place-items-center rounded-full bg-gray-900 px-1.5 text-[11px] font-bold text-white aria-[pressed=true]:bg-white/25">0</span>
                        <twig:ux:icon name="lucide:chevron-down" class="h-3 w-3 opacity-70" />
                    </button>
                {% endif %}
            </div>
        </div>

        {# --- Foot: count + reset --- #}
        <div class="flex items-center justify-between border-t border-dashed border-gray-200 pt-4 dark:border-white/10">
            <div class="text-sm text-gray-600 dark:text-gray-400">
                {{ 'filter.countLabel'|trans({}, 'cms')|raw }}
                <strong data-ferienpass--cms-filter-target="count" class="font-semibold text-gray-900 dark:text-gray-100">{{ totalCount|default(0) }}</strong>
                {{ 'filter.countLabelSuffix'|trans({count: totalCount|default(0)}, 'cms') }}
            </div>
            <div class="flex items-center gap-2">
                <noscript>
                    <button type="submit"
                            class="rounded-md bg-primary-600 px-3 py-1.5 text-sm font-semibold text-white hover:bg-primary-500">
                        {{ 'filter.apply'|trans({}, 'cms') }}
                    </button>
                </noscript>
                {% set dirty = qSearch or qCats|length or qWhen or qVon or qBis or qAges|length or qPreis or qGebuehr or qExtras.sofortbuchung or qExtras.ohne_anmeldung or qExtras.rollstuhl or qExtras.kostenlos or qListeRaw %}
                <a href="{{ path(app.current_route, app.current_route_parameters|filter(v => v)) }}"
                   data-ferienpass--cms-filter-target="reset"
                   class="rounded-md px-2 py-1.5 text-sm text-gray-500 hover:bg-gray-100 hover:text-gray-800 dark:text-gray-400 dark:hover:bg-white/10 dark:hover:text-gray-200 {{ dirty ? '' : 'pointer-events-none opacity-40' }}">
                    {{ 'filter.reset'|trans({}, 'cms') }}
                </a>
            </div>
        </div>
    </form>

    {# Popover + toast targets — extracted so the offer detail page can reuse them. #}
    {% include '@Contao/components/wishlists_host.html.twig' %}
</section>
