    /* flex:1 + min-height:0 fills whatever space is left under the header — a fixed calc()
       assumed a 1-line 52px header, but on narrow screens the nav wraps to 2 lines and the
       real header is taller, which pushed .explore past the viewport and made the whole page
       scrollable instead of just the map filling the screen. See body--fill on <body>.
       overflow:clip (not just hidden) — confirmed live on mobile 2026-08-23: .explore__panel's
       closed (translateY(100%)) mobile sheet is out-of-flow (position:absolute) but still counts
       toward .explore's *scrollable* overflow because it's the sheet's containing block, so
       .explore's scrollHeight ends up taller than its viewport. overflow:hidden still allows
       that phantom area to be scrolled into programmatically (a tap/focus near the sheet was
       enough to set .explore's scrollTop to ~518px), which silently shifted .explore__map and
       everything nested in it (the group-panel sheet) upward by that same amount, making the
       group-panel render mostly off-screen. overflow:clip additionally disables all scrolling
       (programmatic included) on a box that was never meant to scroll in the first place — the
       fix is at the container, not the sheet, since anything absolutely positioned inside .explore
       could trigger the same class of bug. Kept the `hidden` line above it as a fallback for the
       handful of browsers that don't understand `clip`; the later declaration wins where supported. */
    .explore {
        display: flex; flex: 1; min-height: 0; position: relative; overflow: hidden; overflow: clip;
        /* Desktop sidebar (list) width — roughly a half-and-half split with the map so more
           announcements are visible at once, and the results grid (minmax auto-fill) naturally
           lands 2–3 cards per row (user feedback 2026-09-02). Mobile ignores this (the panel is
           off-canvas full-width below). One var so the .group-panel offset stays in sync. */
        --sidebar-w: clamp(420px, 50vw, 900px);
    }
    .explore__panel {
        width: var(--sidebar-w); flex-shrink: 0; padding: .85rem 1rem 1.25rem;
        border-right: 1px solid var(--border); background: var(--panel);
        display: flex; flex-direction: column; gap: .65rem;
        /* Anchors .filters-drawer below. No longer the scroll container itself (see
           .explore__list-section) — user feedback 2026-08-23: filters and results used to share
           one continuously-scrolling column, so opening filters could shove the results out of
           view. Filters is now a drawer over this whole panel; results get their own independent
           scroll region below whatever stays fixed (the filters trigger + active chips). */
        position: relative;
    }
    /* The one thing that actually scrolls in the sidebar now — everything above it (filters
       trigger, active-filter chips) stays put via flex's default (no flex-shrink needed, block
       children don't shrink below content size by default). min-height:0 is required for flex:1
       to actually constrain height instead of just growing to fit content — same pattern already
       used for .explore__map's own flex:1 sibling and body.body--fill main. */
    .explore__list-section {
        flex: 1; min-height: 0; overflow-y: auto;
        /* .65rem -> .4rem — user feedback 2026-08-24: too much empty space between the chart card
           and the results list below it. */
        display: flex; flex-direction: column; gap: .4rem;
    }
    /* Full-panel overlay (not just the map, not just the results) — desktop only; reset back to
       normal in-flow behavior for mobile below, where #filters-form already has its own working
       tab-based show/hide via mobileTab/openMobileTab and doesn't need a second overlay mechanism
       layered on top. */
    .filters-drawer {
        position: absolute; inset: 0; z-index: 20; overflow-y: auto;
        background: var(--panel); border-radius: inherit;
        /* Top padding clears the floating .panel-tabs strip (which overlays this at z-index 21) so
           the segmento toggle (Todos/Comprar/Alugar) isn't hidden behind it — 3.35rem was short
           of the strip's real height (~4rem: .85rem panel pad + ~2.6rem tab + .55rem strip pad),
           so the toggle's top was cut off (user report 2026-09-02). The drawer scrolls, so the
           extra space is free. Sides/bottom unchanged from the old .85/1/1.25rem. */
        padding: 4.6rem 1rem 1.25rem;
    }
    .explore__map { flex: 1; position: relative; min-width: 0; }
    #mappia-frame { width: 100%; height: 100%; border: 0; display: block; }
    .map-loading-bar {
        position: absolute; top: 0; left: 0; right: 0; height: 3px; z-index: 45;
        overflow: hidden; background: rgba(38,96,224,.15);
    }
    .map-loading-bar::after {
        content: ""; position: absolute; top: 0; bottom: 0; left: -30%; width: 30%;
        background: var(--accent); border-radius: 2px;
        animation: map-loading-sweep 1.1s ease-in-out infinite;
    }
    @keyframes map-loading-sweep {
        0% { left: -30%; }
        100% { left: 100%; }
    }
    /* Small circular spinner echoing the map's own loading bar. Lives on the Lista button (moved
       there from Filtros — user feedback 2026-08-17: mapLoading means listings are loading, which
       is what "Lista" opens to, not the filter form). Uses currentColor so it reads correctly on
       both buttons' different backgrounds without a separate variant. */
    .explore__mobile-toggle-spinner {
        width: 14px; height: 14px; border-radius: 999px; flex-shrink: 0;
        border: 2px solid color-mix(in srgb, currentColor 35%, transparent); border-top-color: currentColor;
        animation: explore-toggle-spin .7s linear infinite;
    }
    @keyframes explore-toggle-spin { to { transform: rotate(360deg); } }

    .panel-heading { font-size: .72rem; margin: 0; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); font-weight: 700; }
    /* Filter form redesign, loosely inspired by QuintoAndar's mobile filter sheet (2026-08-14
       reference): stacked labeled sections with big, obvious tap targets instead of a dense
       unlabeled 2-column grid of bare selects/inputs. */
    .filters { display: flex; flex-direction: column; gap: .95rem; }
    .filters select, .filters input {
        width: 100%; padding: .62rem .6rem; border-radius: 8px; border: 1px solid var(--border);
        background: var(--bg); color: var(--text); font-size: 1rem;
    }
    .filter-section__label {
        font-size: .74rem; font-weight: 750; color: var(--muted); text-transform: uppercase;
        letter-spacing: .04em; margin-bottom: .4rem;
    }
    .segmento-toggle { display: flex; background: var(--bg); border: 1px solid var(--border); border-radius: 999px; padding: .2rem; gap: .2rem; }
    .segmento-toggle button {
        flex: 1; border: none; background: none; padding: .55rem .4rem; border-radius: 999px;
        font-size: .86rem; font-weight: 700; color: var(--muted); cursor: pointer;
    }
    .segmento-toggle button.is-active { background: var(--accent); color: var(--accent-contrast); }
    .chip-toggle-row { display: flex; flex-wrap: wrap; gap: .4rem; }
    .chip-toggle {
        border: 1.5px solid var(--border); background: var(--bg); border-radius: 999px;
        padding: .42rem .85rem; font-size: .84rem; font-weight: 650; color: var(--text); cursor: pointer;
    }
    .chip-toggle.is-active { border-color: var(--accent); background: rgba(38,96,224,.1); color: var(--accent); }
    .range-pair { display: grid; grid-template-columns: 1fr 1fr; gap: .5rem; }
    .range-pair label { font-size: .72rem; color: var(--muted); margin-bottom: .25rem; display: block; }
    .ac-wrap { position: relative; }
    .ac-list {
        position: absolute; left: 0; right: 0; top: 100%; z-index: 40; margin: 0; padding: .25rem 0;
        list-style: none; background: var(--panel); border: 1px solid var(--border); border-radius: 0 0 8px 8px;
        max-height: 200px; overflow-y: auto; box-shadow: 0 8px 20px var(--shadow-color);
    }
    .ac-list li { padding: .45rem .65rem; cursor: pointer; font-size: .88rem; }
    .ac-list li:hover, .ac-list li[aria-selected="true"] { background: rgba(38,96,224,.12); color: var(--accent); }
    /* These <li>s are keyboard-operable (tabindex+role=button, see the HTML) but not natively
       focusable elements, so browsers don't give them a default focus ring the way a real
       <button> gets one — needs an explicit style or Tab-ing to them would be silently invisible. */
    .ac-list li:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; background: rgba(38,96,224,.12); color: var(--accent); }
    .filter-chip-row { display: flex; flex-wrap: wrap; gap: .35rem; }
    .filter-chip {
        display: inline-flex; align-items: center; gap: .3rem; padding: .2rem .5rem; border-radius: 999px;
        background: rgba(38,96,224,.1); border: 1px solid rgba(38,96,224,.3); color: var(--accent);
        font-size: .75rem; font-weight: 600;
    }
    /* ::before expands the real tappable area invisibly (was ~8x14px, just the × glyph itself)
       without growing the chip's own visible size — same technique .explore__panel-close uses. */
    .filter-chip button { background: none; border: none; color: inherit; cursor: pointer; padding: 0; line-height: 1; font-size: .9rem; position: relative; }
    .filter-chip button::before { content: ""; position: absolute; top: -10px; bottom: -10px; left: -10px; right: -10px; }
    /* Sits alongside the per-chip x's — those remove one filter at a time, this clears all of
       them in one tap. Lives in .filter-chip-row (not the form's own .filters-footer__clear)
       specifically because that row stays visible in both Filtros and Lista mobile views, while
       the form (and its footer button) is hidden whenever Lista is showing — without this,
       clearing filters from the list view wasn't possible at all except one chip at a time. */
    .filter-chip-row__clear {
        background: none; border: none; color: var(--muted); font-size: .75rem; font-weight: 650;
        text-decoration: underline; cursor: pointer; padding: .2rem .1rem; white-space: nowrap;
    }
    /* Moved right after Tipo de imóvel (was near the bottom, past Área/Quartos) and restyled
       as a green chip — user feedback 2026-08-18: wanted it earlier in the form and visually
       tied to the same green already used everywhere else a "below market price" deal shows up
       (good_deal pin color, .badge-deal, the chart's states.select fill — all #34d399), instead
       of a plain unstyled checkbox that gave no visual hint of what it does. */
    .deal-only-chip {
        display: flex; align-items: center; gap: .55rem; width: 100%;
        border: 1.5px solid rgba(52,211,153,.35); background: rgba(52,211,153,.07); color: var(--text);
        border-radius: 10px; padding: .62rem .8rem; font-size: .88rem; font-weight: 650; cursor: pointer;
        font: inherit; text-align: left;
    }
    .deal-only-chip__icon {
        display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
        width: 18px; height: 18px; border-radius: 999px; border: 1.5px solid rgba(52,211,153,.5);
        color: transparent; font-size: .72rem; line-height: 1;
    }
    .deal-only-chip.is-active {
        border-color: #34d399; background: rgba(52,211,153,.16);
    }
    .deal-only-chip.is-active .deal-only-chip__icon {
        background: #34d399; border-color: #34d399; color: #08130d;
    }
    /* Sticky within .explore__panel's own scroll container, same reasoning as the group-panel's
       sticky header — the OK/Limpar bar must stay reachable regardless of how long the form or
       result list scrolls, and doubles as the explicit "close this panel" affordance requested
       alongside the drag handle. */
    .filters-footer {
        position: sticky; bottom: 0; display: flex; align-items: center; gap: .6rem;
        padding: .7rem 0 .15rem; margin-top: -.1rem; background: var(--panel);
        border-top: 1px solid var(--border);
    }
    .filters-footer__clear {
        background: none; border: none; color: var(--muted); font-size: .84rem; font-weight: 650;
        text-decoration: underline; cursor: pointer; padding: .5rem .3rem; flex-shrink: 0; white-space: nowrap;
    }
    .filters-footer__ok {
        flex: 1; background: var(--accent); color: var(--accent-contrast); border: none; border-radius: 999px;
        padding: .78rem 1rem; font-weight: 750; font-size: .92rem; cursor: pointer;
    }
    .btn-ghost {
        background: transparent; border: 1px solid var(--border); color: var(--muted); border-radius: 8px;
        padding: .45rem .6rem; font-size: .8rem; cursor: pointer;
    }
    .viewport-count { color: var(--muted); font-size: .82rem; }

    /* "N imóveis ocultos pelos filtros" note — sits right under the area count, collapsed to one
       tappable line, expands to a per-filter breakdown with inline "remover" actions. */
    .filtered-note {
        border: 1px solid var(--border); border-radius: 10px; background: var(--bg);
        margin: .1rem 0 .15rem; overflow: hidden;
    }
    .filtered-note__summary {
        display: flex; align-items: center; justify-content: space-between; gap: .5rem; width: 100%;
        background: none; border: none; cursor: pointer; font: inherit; color: var(--text);
        font-size: .82rem; padding: .5rem .6rem; text-align: left;
    }
    .filtered-note__summary strong { color: var(--accent); }
    .filtered-note__body { padding: .1rem .6rem .55rem; display: flex; flex-direction: column; gap: .35rem; }
    .filtered-note__row {
        display: flex; align-items: center; justify-content: space-between; gap: .5rem;
        font-size: .8rem; color: var(--muted);
    }
    .filtered-note__row strong { color: var(--text); }
    .filtered-note__warn {
        font-size: .78rem; color: var(--text); background: rgba(251,146,60,.12);
        border-radius: 8px; padding: .4rem .5rem; margin: .1rem 0 .2rem; line-height: 1.45;
    }
    .filtered-note__clear-all {
        margin-top: .2rem; width: 100%; background: var(--panel); border: 1px solid var(--border);
        color: var(--text); border-radius: 8px; padding: .5rem; font: inherit; font-size: .8rem;
        font-weight: 650; cursor: pointer;
    }
    .filtered-note__clear-all:hover { border-color: var(--accent); }
    .linklike {
        background: none; border: none; padding: 0; font: inherit; font-size: inherit;
        color: var(--accent); font-weight: 650; text-decoration: underline; cursor: pointer;
        white-space: nowrap;
    }
    .list-toolbar { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
    .list-toolbar select {
        background: var(--bg); color: var(--text); border: 1px solid var(--border); border-radius: 8px;
        padding: .35rem .5rem; font-size: .8rem;
    }
    .listing-card {
        display: flex; gap: .65rem; text-decoration: none; color: inherit; cursor: pointer;
        border: 1px solid var(--border); border-radius: 10px; padding: .55rem; background: var(--bg);
    }
    .listing-card:hover { border-color: var(--accent); }
    /* Keyboard-operable (tabindex+role=button, see the HTML) — a plain <article> gets no default
       focus ring at all, so without this, Tab-ing to a card would be completely invisible. */
    .listing-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    .listing-card__thumb { width: 76px; height: 76px; }
    .listing-card__price { font-weight: 700; font-size: .95rem; }
    .listing-card__meta { font-size: .76rem; color: var(--muted); }
    .sidebar-chart-section {
        /* Sides/bottom trimmed from .6rem/.3rem — user feedback 2026-08-23 ("reducing the space
           after the graphic and on the sides... to make it fits the area better"). */
        border: 1px solid var(--border); border-radius: 10px; padding: .5rem .4rem .2rem; background: var(--panel);
    }
    .sidebar-chart { width: 100%; height: 180px; } /* -10% (was 200px) — user feedback 2026-08-23 */
    /* Base-level (not scoped to a media query) so the same tab row works for both the sidebar
       chart (desktop) and the group-panel chart (mobile + desktop bairro/cluster drill-down) —
       unlike .explore__panel-tab, which is mobile-Filtros/Lista-specific. */
    .chart-tabs { display: flex; gap: .35rem; margin-bottom: .45rem; }
    .chart-tab {
        background: var(--bg); border: 1px solid var(--border); color: var(--muted);
        border-radius: 999px; padding: .3rem .65rem; font-size: .74rem; font-weight: 700;
        cursor: pointer; white-space: nowrap;
    }
    .chart-tab.is-active-tab { background: var(--accent); color: var(--accent-contrast); border-color: var(--accent); }
    .results-label {
        font-size: .72rem; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
        color: var(--muted); margin: .15rem 0 -.2rem;
    }
    .selected-group-hint {
        display: flex; align-items: center; justify-content: space-between; gap: .5rem;
        background: rgba(38,96,224,.08); border: 1px solid rgba(38,96,224,.25); color: var(--text);
        border-radius: 8px; padding: .45rem .6rem; font-size: .78rem; font-weight: 600;
        margin: -.15rem 0 .1rem;
    }
    .selected-group-hint__back {
        position: relative; background: none; border: none; color: var(--accent);
        font-size: .78rem; font-weight: 700; line-height: 1; cursor: pointer; padding: .1rem .2rem;
        flex-shrink: 0; white-space: nowrap; text-decoration: underline;
    }
    .selected-group-hint__back::before { content: ""; position: absolute; top: -10px; bottom: -10px; left: -10px; right: -10px; }

    .map-controls {
        position: absolute; top: .7rem; left: .7rem; z-index: 15;
        display: flex; flex-direction: column; gap: .4rem; align-items: flex-start;
        max-width: min(340px, calc(100% - 1.4rem));
        pointer-events: none;
    }
    .map-controls > * { pointer-events: auto; }
    /* Reused inside the filter sheet's "Bairros nesta área" section now (see filters-form) —
       was the floating map-top panel's own styling; kept scoped to just the pieces still used. */
    .bairros-panel__search {
        padding: .4rem .55rem; border-radius: 8px; border: 1px solid var(--border);
        background: var(--bg); color: var(--text); font-size: .8rem; flex-shrink: 0;
    }
    .bairros-panel__list { padding: .1rem 0; }
    .bairros-panel__row {
        width: 100%; display: grid; grid-template-columns: 1fr auto; gap: .15rem .65rem;
        align-items: baseline; text-align: left; background: none; border: none; color: inherit;
        cursor: pointer; padding: .5rem .7rem; font: inherit; border-radius: 8px;
    }
    .bairros-panel__row:hover { background: rgba(38,96,224,.1); }
    .bairros-panel__name { font-weight: 650; font-size: .86rem; grid-column: 1; }
    .bairros-panel__count {
        font-weight: 700; font-size: .82rem; color: var(--accent); grid-column: 2; grid-row: 1 / span 2;
        align-self: center; white-space: nowrap;
    }
    .bairros-panel__sub { font-size: .72rem; color: var(--muted); grid-column: 1; }
    .bairros-panel__empty { padding: .75rem; font-size: .8rem; color: var(--muted); text-align: center; }
    /* Collapsed by default, expands on click, collapses again on click-outside (see
       @click.outside on .explore__bairros-section) — user feedback 2026-08-23: the full ranked
       list used to always render open, taking up sidebar space below the results even when
       nobody was using it. */
    .bairros-section__toggle {
        display: flex; align-items: center; justify-content: space-between; width: 100%;
        background: none; border: none; padding: 0; margin-bottom: .4rem; cursor: pointer; text-align: left;
    }
    .bairros-section__caret {
        color: var(--muted); font-size: 1.1rem; line-height: 1; transition: transform .15s ease;
    }
    .bairros-section__caret--open { transform: rotate(90deg); }

    /* Group sheet: same map, same filters — chart + list for the selected scope.
       Header lives outside .group-panel__body's padding and is sticky within the panel's own
       scroll container, so it (and its close/zoom buttons) never scrolls out of reach on a long
       list — confirmed live 2026-08-14 that without this, scrolling the list scrolled the close
       button away entirely with no way back short of scrolling back up. */
    .group-panel {
        /* Desktop only: starts past the sidebar list (same width value as .explore__panel) so
           opening a cluster/bairro group never covers the persistent full-area list behind it —
           user feedback 2026-08-20: selecting a cluster should stay legible against "the big
           picture" of all imóveis, not hide it under a full-width overlay. Overridden back to
           left:0 on mobile below, where .explore__panel is off-canvas anyway (no "big picture"
           list visible behind it to preserve). */
        position: absolute; left: var(--sidebar-w); right: 0; bottom: 0; background: var(--panel);
        border-top: 1px solid var(--border); border-radius: 16px 16px 0 0;
        box-shadow: 0 -8px 28px var(--shadow-color); z-index: 30;
        /* Fixed, not max-height — user feedback 2026-08-23: a 2-listing group used to make the
           whole sheet shrink to fit, which read as visually inconsistent panel-to-panel. Always
           the same height regardless of how many imóveis are in the group; short lists just leave
           empty space below rather than the sheet itself resizing. overflow-y:scroll (not auto)
           for the same reason — the scrollbar's presence shouldn't be another thing that changes
           per group. */
        height: 72vh; overflow-y: scroll;
        transform: translateY(100%); transition: transform .22s ease-out;
        /* Stops an imprecise downward drag from bubbling into the browser's own pull-to-refresh
           — confirmed live 2026-08-14 that without this, dragging the sheet down could reload the
           whole page instead of just dismissing it. The drag handle below handles the *intended*
           gesture; this is the backstop for any drag this script doesn't itself capture. */
        overscroll-behavior-y: contain;
    }
    .group-panel--open { transform: translateY(0); }
    .group-panel--dragging { transition: none; }
    .group-panel__header {
        /* z-index bumped from 2 — user feedback 2026-08-23: scrolling the list underneath painted
           each card's .source-badge ("zap", "quinto andar", ...) over this header. Both were
           z-index:2, and .source-badge's cards come later in the DOM than this header, so equal
           z-index fell back to DOM order and the badges won. .source-badge itself (layouts/
           app.blade.php) is shared across the whole site (deal cards, sidebar list, ...), so it's
           safer to raise this header above it than to lower a badge used in several other places
           that don't have this conflict. */
        position: sticky; top: 0; z-index: 5; background: var(--panel); border-radius: 16px 16px 0 0;
        padding-top: .4rem; touch-action: none; cursor: grab;
    }
    /* User feedback 2026-08-14: dragging should work from anywhere in the header (title included),
       not just this narrow bar — the drag handlers now live on .group-panel__header itself (see
       groupDragStart's own guard for excluding the buttons). This bar is now just the visual
       affordance signalling "draggable," not the only place that responds to it. */
    .group-panel__handle {
        width: 40px; height: 5px; border-radius: 999px; background: var(--border);
        margin: 0 auto .5rem; flex-shrink: 0;
    }
    .group-panel__header-row {
        display: flex; justify-content: space-between; align-items: center; gap: .6rem;
        padding: 0 1rem .6rem;
    }
    .group-panel__title { font-size: 1rem; font-weight: 750; line-height: 1.25; flex: 1; min-width: 0; }
    /* cursor:auto overrides the header's own cursor:grab — buttons must stay normal tap targets,
       not part of the drag surface's cursor affordance. */
    .group-panel__header-actions { display: flex; align-items: center; gap: .4rem; flex-shrink: 0; cursor: auto; }
    .group-panel__zoom-btn {
        display: inline-flex; align-items: center; gap: .3rem; background: var(--bg);
        border: 1px solid var(--border); border-radius: 999px; padding: .35rem .65rem;
        font-size: .78rem; font-weight: 700; color: var(--text); cursor: pointer; white-space: nowrap;
    }
    .group-panel__zoom-btn:hover { border-color: var(--accent); color: var(--accent); }
    .group-panel__close {
        position: relative;
        background: none; border: none; color: var(--muted); font-size: 1.4rem; cursor: pointer;
        line-height: 1; padding: 0 .15rem; flex-shrink: 0;
    }
    .group-panel__close::before { content: ""; position: absolute; top: -12px; bottom: -12px; left: -12px; right: -12px; }
    .group-panel__scroll-top {
        display: flex; align-items: center; gap: .35rem; width: fit-content; margin: 0 1rem 0 auto;
        position: sticky; bottom: .85rem; z-index: 3;
        background: var(--accent); color: var(--accent-contrast); border: none; border-radius: 999px;
        padding: .5rem .9rem; font-size: .78rem; font-weight: 700; cursor: pointer;
        box-shadow: 0 4px 14px var(--shadow-color);
    }
    .group-panel__body { padding: 0 1rem 1.1rem; }
    .group-panel__chart { width: 100%; height: 235px; } /* -~10% (was 260px) — user feedback 2026-08-23 */
    .group-panel__chart-hint {
        font-size: .72rem; color: var(--muted); margin: .15rem 0 .35rem; line-height: 1.35;
    }
    /* min-height reserves the line's full height whether or not there's text in it right now —
       see the HTML comment above this element for why (fixed chart size, no layout shift). */
    .group-panel__selected-summary {
        min-height: 1.3rem; margin: .5rem 0 0; font-size: .82rem; font-weight: 700; color: var(--text);
    }
    /* Same auto-fill grid as the sidebar/bottom-sheet list (see its own comment) — lands as many
       cards per row as the panel width allows (the sheet is full-width on mobile; on desktop it
       starts past the now-wider sidebar, so ~2 columns). */
    .group-panel__list { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: .45rem; margin-top: .75rem; }
    /* Marks whichever list item corresponds to the last chart point clicked — replaces an
       earlier separate "summary card" above the list, which duplicated the same listing the
       list's own reordering (see groupListSorted in explore-app.js) already put first, making it
       look like the imóvel appeared twice (user feedback 2026-08-15). One reordered, highlighted
       list item plus a matching marked point on the chart itself (see imoveis-chart.js's
       states.select) now carries that connection instead. */
    .listing-card--selected { border-color: var(--accent); border-width: 2px; padding: calc(.55rem - 1px); background: rgba(38,96,224,.06); }
    .listing-card__cta {
        align-self: center; flex-shrink: 0; background: var(--accent); color: var(--accent-contrast); border-radius: 999px;
        padding: .45rem .8rem; font-size: .78rem; font-weight: 700; text-decoration: none; white-space: nowrap;
    }
    /* Desktop left-panel tab strip (2026-08-30) — sits above .filters-drawer (z-index) so it's
       always the way back to the list; its own opaque background masks the drawer behind the
       gap between the two buttons. Hidden < 900px (mobile has .explore__panel-tabs). */
    .panel-tabs {
        position: relative; z-index: 21; display: flex; gap: .4rem;
        background: var(--panel); padding-bottom: .55rem;
    }
    .panel-tab {
        flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
        background: var(--bg); border: 1px solid var(--border); border-radius: 10px;
        padding: .6rem .55rem; font: inherit; font-weight: 700; font-size: .92rem;
        color: var(--muted); cursor: pointer; box-shadow: 0 1px 2px var(--shadow-color);
    }
    .panel-tab:hover { border-color: var(--accent); }
    .panel-tab--active { background: var(--accent); color: var(--accent-contrast); border-color: var(--accent); }
    .panel-tab__count { font-size: .82rem; font-weight: 700; opacity: .9; }

    .filters-toggle {
        display: flex; align-items: center; gap: .55rem;
        width: 100%; background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
        cursor: pointer; padding: .7rem .85rem; font: inherit; text-align: left;
        font-size: .95rem; font-weight: 700; color: var(--text);
        box-shadow: 0 1px 2px var(--shadow-color);
    }
    .filters-toggle:hover { border-color: var(--accent); }
    .filters-toggle__icon { display: inline-flex; color: var(--accent); flex-shrink: 0; }
    .filters-toggle__label { flex: 1; }
    .filter-count-badge {
        background: var(--accent); color: var(--accent-contrast); border-radius: 999px;
        font-size: .75rem; font-weight: 700; padding: .12rem .5rem; min-width: 1.4rem; text-align: center;
    }
    .filters-toggle__chevron {
        font-size: 1.2rem; color: var(--muted); transition: transform .2s ease; line-height: 1;
    }
    .filters-toggle__chevron--open { transform: rotate(180deg); }
    .side-section-hint { font-size: .75rem; color: var(--muted); line-height: 1.4; margin: 0; }
    .bairro-row {
        display: grid; grid-template-columns: 1fr auto; gap: .1rem .5rem; align-items: baseline;
        width: 100%; text-align: left; background: var(--bg); border: 1px solid var(--border);
        border-radius: 10px; padding: .55rem .65rem; color: inherit; cursor: pointer; font: inherit;
    }
    .bairro-row:hover { border-color: var(--accent); }
    .bairro-row__name { font-weight: 650; font-size: .88rem; }
    .bairro-row__count { font-weight: 700; color: var(--accent); font-size: .86rem; }
    .bairro-row__sub { font-size: .72rem; color: var(--muted); grid-column: 1 / -1; }

    /* Listing detail overlays the map (SPA) */
    .detail-drawer {
        position: absolute; top: 0; right: 0; bottom: 0; width: min(420px, 100%); z-index: 40;
        background: var(--panel); border-left: 1px solid var(--border);
        box-shadow: -8px 0 28px var(--shadow-color); transform: translateX(100%);
        transition: transform .22s ease-out; overflow-y: auto;
    }
    .detail-drawer--open { transform: translateX(0); }
    .detail-drawer__bar {
        /* z-index bumped from 2 to stay above .source-badge (also z-index:2) on the "similar
           listings" cards further down this drawer — same fix/reasoning as .group-panel__header
           above. */
        position: sticky; top: 0; z-index: 5; display: flex; justify-content: space-between; align-items: center;
        gap: .5rem; padding: .65rem .85rem; background: rgba(255,255,255,.96); border-bottom: 1px solid var(--border);
        backdrop-filter: blur(6px);
    }
    /* Breadcrumb trail so it's unambiguous you're inside a detail view nested over the map/list,
       not a whole new page — each non-current segment is a real navigation shortcut (jumps
       straight to that level via the existing overlayStack, same as the browser back button
       would eventually reach), the last segment is the current, non-clickable location. */
    .detail-breadcrumb { display: flex; align-items: center; flex-wrap: wrap; gap: .25rem; min-width: 0; }
    .detail-breadcrumb__link {
        position: relative;
        background: none; border: none; color: var(--muted); font-size: .78rem; font-weight: 650;
        cursor: pointer; padding: .15rem .2rem; border-radius: 4px; white-space: nowrap;
    }
    /* Was ~19px tall — the breadcrumb text itself can't grow much without looking odd inline, so
       the extra tappable height is invisible padding instead. */
    .detail-breadcrumb__link::before { content: ""; position: absolute; top: -8px; bottom: -8px; left: -4px; right: -4px; }
    .detail-breadcrumb__link:hover { color: var(--accent); background: var(--bg); }
    .detail-breadcrumb__sep { color: var(--border); font-size: .78rem; flex-shrink: 0; }
    .detail-breadcrumb__current {
        font-size: .78rem; font-weight: 750; color: var(--text); white-space: nowrap;
        overflow: hidden; text-overflow: ellipsis;
    }
    .detail-hero { position: relative; }
    .detail-hero img { width: 100%; height: 220px; object-fit: cover; display: block; background: var(--bg); }
    /* Loading skeleton — user feedback 2026-08-24: the carousel should show a placeholder while
       an image is still loading instead of a blank/background-colored box. Sits over the <img>
       (same size, absolutely positioned within .detail-hero's own position:relative) rather than
       replacing it, so the <img> itself can keep loading underneath and this just disappears the
       instant its own @load fires — see heroImageLoading in explore-app.js. */
    .detail-hero__skeleton {
        position: absolute; inset: 0; z-index: 1;
        background: linear-gradient(90deg, var(--bg) 25%, var(--border) 50%, var(--bg) 75%);
        background-size: 200% 100%;
        animation: detail-hero-skeleton-shimmer 1.4s ease-in-out infinite;
    }
    @keyframes detail-hero-skeleton-shimmer {
        0% { background-position: 200% 0; }
        100% { background-position: -200% 0; }
    }
    /* Real carousel controls — user feedback 2026-08-18: swipe-only (still supported) and small
       tap-to-select thumbnails weren't discoverable as "how do I move through the photos". */
    .detail-hero__nav {
        position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
        width: 32px; height: 32px; border-radius: 50%; border: none;
        background: rgba(20,28,40,.55); color: #fff; font-size: 20px; line-height: 1;
        display: flex; align-items: center; justify-content: center; cursor: pointer; padding: 0;
    }
    .detail-hero__nav:hover { background: rgba(20,28,40,.8); }
    .detail-hero__nav--prev { left: 8px; }
    .detail-hero__nav--next { right: 8px; }
    .detail-hero__dots {
        position: absolute; bottom: 8px; left: 50%; transform: translateX(-50%); z-index: 2;
        display: flex; gap: 5px;
    }
    .detail-hero__dot { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,.55); }
    .detail-hero__dot--active { background: #fff; }
    /* overflow-x:auto (was a plain flex row with no scroll handling at all) — with more than a
       handful of photos the thumbnails either got clipped or forced the whole drawer to scroll
       sideways instead of just this strip. */
    .detail-hero__thumbs {
        display: flex; gap: .4rem; padding: .55rem 1rem 0; overflow-x: auto;
        -webkit-overflow-scrolling: touch; scrollbar-width: thin;
    }
    .detail-hero__thumb { width: 56px; height: 56px; object-fit: cover; border-radius: 6px; cursor: pointer; border: 2px solid transparent; flex-shrink: 0; }
    .detail-hero__thumb--active { border-color: var(--accent); }
    .detail-hero__more { display: block; margin: .5rem 1rem 0; font-size: .8rem; color: var(--accent); text-decoration: underline; }
    .detail-body { padding: .85rem 1rem 1.5rem; }
    .detail-address {
        display: flex; align-items: flex-start; gap: .35rem; color: var(--text);
        font-size: .88rem; font-weight: 600; margin: .55rem 0 0; line-height: 1.35;
    }
    .detail-address svg { margin-top: .15rem; color: var(--accent); }
    /* white-space:pre-line renders the real \n line breaks ListingRepository::description() already
       normalized scraped HTML (<br> tags etc.) into, without needing x-html/raw markup here. */
    .detail-description { margin: 0; font-size: .85rem; line-height: 1.5; color: var(--text); white-space: pre-line; }
    /* Separate, deliberately lighter Mappia embed (?minimap=1 on its own iframe src, see
       imoveis-tenant-query.js's runMinimapMode) — skips the whole fetch/cluster/filter pipeline
       the main map needs, so it loads faster for what's just a fixed location marker. */
    .detail-minimap { margin: .75rem 0; border-radius: 10px; overflow: hidden; border: 1px solid var(--border); }
    /* pointer-events:none — user feedback 2026-08-23: a real, confirmed bug where this iframe
       intercepted taps meant for the "Lista" toggle pill underneath it. Root cause: .detail-drawer
       clips overflow via scroll (overflow-y:auto) and the iframe is correctly *painted* clipped to
       that, but a cross-origin iframe's hit-testing doesn't respect an ancestor's overflow
       clipping the way ordinary DOM content does in Chromium — its full, unclipped 220px-tall box
       kept intercepting clicks below the drawer's visible bottom edge even while scrolled out of
       view. This minimap was already a passive, chrome-stripped "just show one fixed marker" embed
       (see runMinimapMode) with no pan/zoom controls exposed, so it was never meant to be
       interactive in the first place — disabling pointer events entirely is the correct fix, not
       a workaround. */
    .detail-minimap iframe { width: 100%; height: 220px; border: 0; display: block; pointer-events: none; }
    .chip-row { display: flex; gap: .45rem; flex-wrap: wrap; margin: .75rem 0; }
    .chip {
        background: var(--bg); border: 1px solid var(--border); border-radius: 8px;
        padding: .4rem .6rem; font-size: .8rem;
    }
    .detail-body .price { font-size: 1.45rem; font-weight: 800; }

    .explore__mobile-toggles { display: none; }
    .explore__mobile-toggle { display: none; }
    .explore__panel-header { display: contents; }
    .explore__panel-header-row { display: none; }
    .explore__panel-close { display: none; }
    .explore__panel-x { display: none; }
    /* Lives in .map-controls now, on the map itself (see the HTML) rather than inside the
       sidebar — user feedback 2026-08-18/20: first, a small unlabeled icon-only toggle was easy
       to miss/read as "the address search is gone"; then, once it was a real always-visible bar,
       feedback asked for it to sit "on the main page" the way the old segmento badge did, not
       tucked inside the sidebar/Filtros sheet. Matches the standard map-app convention (Google
       Maps/Zillow/Airbnb all float a search bar directly on the map). Sized explicitly here since
       .map-controls is a flex column with align-items:flex-start — children don't stretch to its
       max-width on their own the way a block-level sidebar row used to. */
    .map-controls .address-search-bar-wrap { width: min(300px, 100%); }
    .address-search-bar {
        position: relative; display: flex; align-items: center;
        background: var(--panel); border: 1px solid var(--border); border-radius: 999px;
        padding: 0 .5rem 0 .9rem; height: 44px; /* WCAG 2.5.8 min target size */
    }
    .address-search-bar:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(38,96,224,.15); }
    .address-search-bar__icon { color: var(--muted); flex-shrink: 0; display: flex; }
    .address-search-bar input {
        flex: 1; min-width: 0; border: none; background: none; padding: 0 .6rem;
        font-size: .92rem; color: var(--text); height: 100%;
    }
    .address-search-bar input:focus { outline: none; }
    /* Suppress the browser's own native clear-X for type="search" (WebKit/Blink/Edge all add
       one once there's text) — confirmed live 2026-08-20 it rendered right next to the custom
       .address-search-bar__clear button below, showing two ×'s side by side. */
    .address-search-bar input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
    .address-search-bar__clear {
        display: flex; align-items: center; justify-content: center;
        width: 32px; height: 32px; border-radius: 50%; border: none; background: none;
        color: var(--muted); cursor: pointer; font-size: 1.1rem; flex-shrink: 0;
    }
    .address-search-bar__clear:hover { background: var(--bg); color: var(--text); }
    /* Anchored to the bar itself (not .ac-wrap, which the bairro autocomplete owns) so the
       dropdown lines up under the pill regardless of the row's own padding. */
    .address-search-bar-wrap { position: relative; }
    .address-search-bar-wrap .ac-list { top: calc(100% + 4px); }
    .backdrop {
        /* left:0, not the old left:min(400px,100%) — .backdrop's actual parent is .explore__map,
           which already starts at x=400 (right after the sidebar) in its own layout, so that
           400px was a *second* offset stacked on top of the first, doubling to 800 and only
           dimming the right half of the map instead of all of it (or all of nothing, see below).
           .explore__map's own box is already exactly "everything right of the sidebar." Kept
           correct (not deleted) even though desktop no longer shows this at all, in case a future
           reason to re-enable it there comes up. */
        display: none; position: absolute; top: 0; left: 0; right: 0; bottom: 0;
        z-index: 28; background: rgba(0,0,0,.35);
    }
    /* Desktop: no backdrop at all — user feedback 2026-08-24 ("on the computer there is no need
       to this shadow"). The persistent sidebar list stays visible either way; unlike mobile's
       full-width off-canvas sheet, there's no reason to dim the map behind an overlay here.
       Re-enabled for mobile inside its own media query below (.backdrop--show there). */

    /* Brief, dismissible — confirms the restored filters/map position (localStorage, see
       loadSavedSession in explore-app.js) actually happened, without demanding interaction.
       Auto-hides itself after a few seconds (see init()); the close button and "Recomeçar" just
       let the user act sooner than that. */
    .session-restored-badge {
        background: var(--panel); border: 1px solid var(--border); border-radius: 999px;
        padding: .42rem .5rem .42rem .75rem; font-size: .8rem; font-weight: 600; color: var(--text);
        box-shadow: 0 2px 12px var(--shadow-color); display: inline-flex; align-items: center; gap: .5rem;
    }
    .session-restored-badge__reset {
        background: none; border: none; color: var(--accent); font-weight: 700; font-size: .78rem;
        cursor: pointer; padding: 0; text-decoration: underline; white-space: nowrap;
    }
    .session-restored-badge__close {
        background: none; border: none; color: var(--muted); font-size: 1.1rem; cursor: pointer;
        line-height: 1; padding: 0; flex-shrink: 0;
    }

    /* "Ver essa área" prompt (2026-08-30) — offered on open when the neighbourhood filter points
       off-screen. Accent-bordered card in the map-controls stack. */
    .fit-prompt {
        pointer-events: auto; background: var(--panel); border: 1px solid var(--accent);
        border-radius: 12px; padding: .6rem .7rem; font-size: .82rem; color: var(--text);
        box-shadow: 0 4px 18px var(--shadow-color); max-width: min(320px, 100%);
        display: flex; flex-direction: column; gap: .5rem; line-height: 1.35;
    }
    .fit-prompt__actions { display: flex; align-items: center; gap: .5rem; }
    .fit-prompt__go {
        flex: 1; background: var(--accent); color: var(--accent-contrast); border: none;
        border-radius: 999px; padding: .45rem .8rem; font-weight: 700; font-size: .8rem;
        cursor: pointer; white-space: nowrap;
    }
    .fit-prompt__close {
        background: none; border: none; color: var(--muted); font-size: 1.2rem; cursor: pointer;
        line-height: 1; padding: 0 .2rem; flex-shrink: 0;
    }

    /* The results list while a desktop map-cluster view is active (desktopClusterActive) — an
       accent rail + faint tint so it reads as "a selected group", not your normal full list. */
    .explore__list-section--group {
        border-left: 3px solid var(--accent); padding-left: .5rem;
        background: linear-gradient(90deg, rgba(38,96,224,.05), transparent 60%);
        border-radius: 0 8px 8px 0;
    }

    .map-legend {
        background: var(--panel); border: 1px solid var(--border); border-radius: 8px;
        padding: .4rem .55rem; font-size: .68rem; color: var(--muted); font-weight: 650;
        display: inline-flex; align-items: center; gap: .35rem; white-space: nowrap;
        box-shadow: 0 2px 10px var(--shadow-color); cursor: default;
    }
    .map-legend__swatches { display: flex; gap: 2px; flex-shrink: 0; }
    .map-legend__swatch { width: 11px; height: 11px; border-radius: 3px; }

    .entry-modal {
        position: absolute; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center;
        background: rgba(20,28,42,.55); padding: 1rem;
    }
    .entry-modal__card {
        position: relative; width: min(420px, 100%); background: var(--panel); border-radius: 16px; border: 1px solid var(--border);
        box-shadow: 0 20px 50px rgba(20,30,50,.3); padding: 1.5rem 1.35rem; text-align: center;
    }
    .entry-modal__close {
        position: absolute; top: .6rem; right: .6rem; background: none; border: none; color: var(--muted);
        font-size: 1.4rem; cursor: pointer; line-height: 1; padding: .3rem; flex-shrink: 0;
    }
    .entry-modal__title { font-size: 1.25rem; font-weight: 800; margin: 0 0 .4rem; color: var(--text); }
    .entry-modal__sub { color: var(--muted); font-size: .9rem; margin: 0 0 1.1rem; }
    .entry-modal__options { display: flex; flex-direction: column; gap: .65rem; }
    .entry-modal__option {
        display: flex; align-items: center; gap: .7rem; width: 100%; text-align: left;
        background: var(--bg); border: 1.5px solid var(--border); border-radius: 12px;
        padding: .85rem 1rem; cursor: pointer; font: inherit; font-weight: 700; font-size: 1rem; color: var(--text);
        transition: border-color .15s ease, background .15s ease;
    }
    .entry-modal__option:hover { border-color: var(--accent); background: rgba(38,96,224,.06); }
    .entry-modal__option-icon { font-size: 1.5rem; line-height: 1; flex-shrink: 0; }
    .entry-modal__skip {
        display: inline-block; margin-top: 1.1rem; background: none; border: none; color: var(--muted);
        font-size: .85rem; text-decoration: underline; cursor: pointer; padding: .3rem;
    }
    /* Reuses .chip-toggle's existing look (already global) — one-tap picks for the entry flow's
       bairro step so it isn't typing-only, same reasoning as the tipo-de-imóvel step's buttons. */
    .entry-modal__bairro-chips { display: flex; flex-wrap: wrap; gap: .4rem; justify-content: center; }

    @media (max-width: 900px) {
        .explore { display: block; }
        .explore__map { position: absolute; inset: 0; }
        /* Bigger on mobile (was 76px everywhere) — user feedback 2026-08-24: "put the listing
           image a little bigger... to make it more visible." */
        .listing-card__thumb { width: 92px; height: 92px; }
        .explore__panel {
            position: absolute; left: 0; right: 0; bottom: 0; top: auto; width: auto;
            max-height: 78vh; border-radius: 16px 16px 0 0; border-right: none;
            border-top: 1px solid var(--border); box-shadow: 0 -6px 24px rgba(20,30,50,.18);
            transform: translateY(100%); transition: transform .22s ease-out; z-index: 25;
            overscroll-behavior-y: contain;
            /* The desktop split (fixed header + independently-scrolling .explore__list-section,
               filters as a full-panel drawer) is desktop-only — restored here to exactly how it
               worked before that existed: the whole sheet is one scroll region, and #filters-form
               shows/hides in place via mobileTab/openMobileTab's own tab switch, not an overlay. */
            overflow-y: auto;
        }
        /* Keep the flex-column + gap (consistent spacing between the toolbar/headline/chart/list,
           same as desktop) but drop the flex:1/overflow-y:auto that makes it desktop's independent
           scroll region — mobile's outer .explore__panel (restored to overflow-y:auto just above)
           is the one and only scroll container here, same as before this whole restructure. */
        .explore__list-section { flex: none; min-height: auto; overflow-y: visible; }
        .filters-drawer { position: static; inset: auto; overflow-y: visible; padding: 0; background: none; }
        .explore__panel--open { transform: translateY(0); }
        /* Tap-outside-to-close, independent of the drag handle — the panel covers up to 78vh,
           which was hiding the neighborhood picker in .map-controls with no way back if the
           handle wasn't hit exactly. z-index sits between .map-controls (15) and the panel (25). */
        .panel-backdrop { display: none; position: absolute; inset: 0; z-index: 20; background: rgba(0,0,0,.25); }
        .panel-backdrop--show { display: block; }
        /* Sticky so the drag handle + X survive scrolling a long filter form / result list —
           confirmed live 2026-08-14: without this, both scrolled away with the rest of the
           content (same underlying issue the group-panel's header had, fixed the same way
           there), leaving no visible way to close the sheet short of scrolling back to the top. */
        .explore__panel-header {
            /* Base (desktop) rule sets display:contents so this wrapper disappears into the
               flow there — an element with display:contents generates no box at all, so
               position:sticky on it is a silent no-op unless display is overridden back to a
               real box here. Confirmed live 2026-08-14: without this line the header never
               stuck, despite every other sticky property being set correctly. */
            display: flex; flex-direction: column;
            /* z-index bumped from 3 — user feedback 2026-08-23: scrolling the Filtros tab's own
               form content could paint over this sticky header (confirmed only on Filtros, not
               Lista — the form has its own internally-positioned elements, e.g. the bairro
               autocomplete dropdown at z-index:40, that outrank a low z-index here). Raised well
               past anything else in either tab's content so this header — the only way to switch
               tabs or close the sheet while scrolled down — always wins the stacking order,
               "similarly to the buttons limpar e mostrar," per that same feedback.
            */
            position: sticky; top: 0; z-index: 50; background: var(--panel);
            flex-shrink: 0; border-radius: 16px 16px 0 0;
        }
        .explore__panel-close {
            /* flex-shrink: 0 matters — .explore__panel-header is a flex column, and this button
               has no content, so its min-content height is ~0. Without shrink:0 the flex
               algorithm squeezes it to literally 0px height before it shrinks the (much bigger)
               form/list content, making the drag handle invisible and untappable — confirmed
               live 2026-08-14 via getComputedStyle: height came back 0px despite height:5px here. */
            display: block; flex-shrink: 0; position: relative; width: 40px; height: 5px;
            border-radius: 999px; background: var(--border); margin: .35rem auto .5rem;
            border: none; padding: 0; touch-action: none; cursor: grab;
        }
        /* Enlarges the tap/drag target well past the visible 40x5 bar without affecting layout. */
        .explore__panel-close::before {
            content: ""; position: absolute; top: -14px; bottom: -14px; left: -30px; right: -30px;
        }
        .explore__panel--dragging { transition: none; }
        /* Title row sits below the drag handle bar, inside the same sticky header, so it
           survives scrolling the form/list the same way the handle and X already did —
           confirmed live 2026-08-14 that without a title here, a scrolled-down panel showed
           only a bare handle+X with nothing indicating what the sheet was. */
        .explore__panel-header-row {
            display: flex; align-items: center; justify-content: space-between; gap: .6rem;
            padding: 0 1rem .5rem;
        }
        .explore__panel-tabs { display: flex; gap: .4rem; flex: 1; min-width: 0; }
        .explore__panel-tab {
            display: flex; align-items: center; gap: .3rem;
            background: var(--bg); border: 1px solid var(--border); color: var(--muted);
            border-radius: 999px; padding: .35rem .8rem; font-size: .82rem; font-weight: 700;
            cursor: pointer; white-space: nowrap;
        }
        .explore__panel-tab.is-active-tab { background: var(--accent); color: var(--accent-contrast); border-color: var(--accent); }
        /* The drag handle bar is a subtle affordance (and, per its own comment above, was
           invisible until fixed) — an explicit X is a more conventional, unambiguous "close
           this" target for anyone who doesn't try swiping it. Sits inline in the title row now
           instead of floating absolutely, so it can't overlap the title text. */
        .explore__panel-x {
            display: flex; background: none; border: none; color: var(--muted);
            font-size: 1.4rem; cursor: pointer; line-height: 1; padding: 0 .15rem; flex-shrink: 0;
        }
        /* Hidden on mobile — user feedback 2026-08-19 ("click list, then click filter... it put
           together" + close becoming unresponsive): this button stayed visible and tappable even
           while the Lista tab was open (it isn't gated by mobileTab/filtersOpen at all, only the
           #filters-form it toggles is), so it read as a second, redundant "switch to filters"
           control sitting right on top of the list — except tapping it only ever set
           filtersOpen, never mobileTab, leaving the two out of sync (bottom nav still highlighted
           "Lista" while filters were actually showing) and leaving mobilePanelOpen untouched if
           it were ever the very first tap. The bottom-nav Filtros/Lista pair (openMobileTab) is
           the single source of truth for switching tabs on mobile now; this toggle is desktop-only
           again, where there's no tab bar and it's the only way to show/hide the filter form. */
        .filters-toggle { display: none; }
        /* Desktop tab strip is desktop-only — mobile switches Lista/Filtros via
           .explore__panel-tabs in the sheet header instead. */
        .panel-tabs { display: none; }
        .explore__list-section--group { border-left: none; padding-left: 0; background: none; }
        /* Two buttons (Filtros / Lista) — user feedback 2026-08-17: it wasn't clear tapping a
           single combined button could mean either. A third "Bairros" tab existed briefly
           (2026-08-18) for the ranked "Bairros nesta área" list, but user feedback the same day
           asked for it to be removed from mobile entirely — bairro selection there still works
           fully through Filtros (search, "Bairros próximos", "Por região" chips); only the ranked
           list view itself is desktop-only now (see .explore__bairros-section below). Both
           remaining buttons open the same underlying sheet, just to a different tab. */
        .explore__mobile-toggles {
            display: flex; gap: .3rem; flex-wrap: wrap; justify-content: center;
            position: absolute; bottom: 1rem; left: 50%; transform: translateX(-50%);
            width: calc(100% - 1.2rem); z-index: 20;
        }
        .explore__mobile-toggle {
            display: flex; align-items: center; gap: .3rem;
            background: var(--accent); color: var(--accent-contrast); border: none; border-radius: 999px;
            padding: .55rem .8rem; font-weight: 750; font-size: .78rem; cursor: pointer;
            box-shadow: 0 6px 18px rgba(20,30,50,.28); white-space: nowrap;
        }
        .explore__mobile-toggle--list {
            background: var(--panel); color: var(--text); border: 1px solid var(--border);
        }
        /* Whichever tab was open last stays visibly marked once the sheet is closed again — a
           quiet "this is what you'll see if you tap here" cue, not just two identical-looking
           pills with no memory of state. */
        .explore__mobile-toggle.is-active-tab { box-shadow: 0 0 0 2px var(--accent), 0 6px 18px rgba(20,30,50,.28); }
        /* Filtros vs Lista is meant to show ONE thing at a time on mobile — user feedback
           2026-08-17: with both always rendered, opening Filtros still showed the full result
           list scrolled below the form, defeating the point of having two separate buttons.
           Scoped to this mobile media query only, so desktop (which has no Filtros/Lista split
           and always shows filters + list together) is unaffected regardless of filtersOpen. */
        .explore__list-section--hidden-mobile { display: none; }
        /* Bottom-sheet: pull the sticky OK/Limpar bar flush against the sheet's bottom edge —
           user feedback 2026-08-30: the sheet's 1.25rem bottom padding (and the active-filter
           chip row, which renders after the form) left a dead gap below the button while
           scrolling, wasting space that could show more filters. Negative margin eats the
           padding; the chip row is hidden while the form is open (redundant then anyway). */
        .filters-footer {
            margin-bottom: -1.25rem;
            padding-bottom: calc(.4rem + env(safe-area-inset-bottom, 0px));
        }
        .filter-chip-row--hidden-mobile { display: none; }
        /* Ranked "Bairros nesta área" list — desktop-only as of 2026-08-18 (user feedback: the
           dedicated mobile "Bairros" tab was more clutter than value once bairro selection was
           already reachable through Filtros). mobileTab can no longer become 'bairros' since
           nothing sets it anymore, so this simply never shows on mobile now. */
        .explore__bairros-section { display: none; }
        /* Was bottom-anchored, stacking segmento badge + bairros button + hint right on top of
           the "Filtros e lista" toggle and the map's own attribution/zoom controls at the
           bottom — confirmed live 2026-08-14, it visibly crowded and overlapped both. Top
           placement (same as desktop) leaves the whole bottom strip to the one toggle pill. */
        .map-controls { left: .55rem; right: .55rem; max-width: calc(100% - 1.1rem); }
        /* box-shadow direction corrected for a bottom sheet — user feedback 2026-08-23 ("the
           shadow size is strange"): the base rule's -8px 0 (a shadow cast to the left) was built
           for the desktop drawer's right-edge slide-in and never got its own override here, so
           the mobile bottom sheet kept a sideways shadow that didn't match the top edge it
           actually slides in from. 0 -8px (cast upward) is the same value .group-panel already
           uses for the same bottom-sheet shape. */
        .detail-drawer { width: 100%; border-left: none; border-top: 1px solid var(--border); top: 8%; border-radius: 16px 16px 0 0; box-shadow: 0 -8px 28px var(--shadow-color); }
        /* Merging title+subtitle into one line and dropping the instruction paragraph (see
           groupHeaderText()/groupChartHint() in explore-app.js) freed enough height to give the
           chart itself more room, which was the actual point of trimming that text. */
        .group-panel__chart { height: 270px; } /* -10% (was 300px) — user feedback 2026-08-23 */
        /* Full width again on mobile — .explore__panel is off-canvas here, so there's no sidebar
           list behind the group panel/backdrop to preserve visibility of (see the desktop rules
           above). */
        .group-panel { left: 0; }
        .backdrop { left: 0; }
        /* Restores the backdrop for mobile only — the base rule no longer shows it at all (see
           its own comment above), but a full-width off-canvas sheet still benefits from dimming
           whatever's behind it the way a modal normally does. */
        .backdrop--show { display: block; }
    }
