// Composants : Header, ProfileSelector, FilterBar, ActiveFilterChips,
// MobileFilterDrawer, ListingCard, ListingMap.

const { useState, useMemo, useRef, useEffect } = React;

function _normalize(s) {
  return (s || '').toLowerCase().normalize('NFD').replace(/[̀-ͯ]/g, '');
}

// ---- Header (avec autocomplete ville/quartier, multi-zones chips) ----
function Header({ lang, setLang, t, query, setQuery, autocompleteValues, setAutocompleteValues, suggestions, activeProfile, onOpenProfileModal, userProfile, setUserProfile, onOpenLogin, currentUser }) {
  const [open, setOpen] = useState(false);
  const [highlightIdx, setHighlightIdx] = useState(0);
  const wrapRef = useRef(null);
  const inputRef = useRef(null);

  const matches = useMemo(() => {
    const q = _normalize(query.trim());
    if (!q) return [];
    const score = (label) => {
      const lc = _normalize(label);
      if (lc.startsWith(q)) return 2;
      if (lc.includes(q))   return 1;
      return 0;
    };
    // Exclure les zones déjà sélectionnées des suggestions
    const selectedKeys = new Set(autocompleteValues.map(av => `${av.kind}::${av.value}`));
    const cityMatches = (suggestions?.cities || [])
      .filter(c => !selectedKeys.has(`city::${c.value}`))
      .map(c => ({ ...c, _s: score(c.value) }))
      .filter(c => c._s > 0);
    const neighMatches = (suggestions?.neighborhoods || [])
      .filter(n => !selectedKeys.has(`neighborhood::${n.value}`))
      .map(n => ({ ...n, _s: score(n.value) }))
      .filter(n => n._s > 0);
    const merged = [...cityMatches, ...neighMatches]
      .sort((a, b) => b._s - a._s || b.count - a.count)
      .slice(0, 8);
    return merged;
  }, [query, suggestions, autocompleteValues]);

  useEffect(() => {
    const onClick = (e) => {
      if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
    };
    document.addEventListener('mousedown', onClick);
    return () => document.removeEventListener('mousedown', onClick);
  }, []);

  useEffect(() => { setHighlightIdx(0); }, [query]);

  const onKeyDown = (e) => {
    // Backspace sur input vide → retire le dernier chip
    if (e.key === 'Backspace' && !query && autocompleteValues.length > 0) {
      setAutocompleteValues(autocompleteValues.slice(0, -1));
      return;
    }
    if (!open || matches.length === 0) return;
    if (e.key === 'ArrowDown') { e.preventDefault(); setHighlightIdx(i => Math.min(i + 1, matches.length - 1)); }
    else if (e.key === 'ArrowUp') { e.preventDefault(); setHighlightIdx(i => Math.max(i - 1, 0)); }
    else if (e.key === 'Enter')  { e.preventDefault(); selectMatch(matches[highlightIdx]); }
    else if (e.key === 'Escape') { setOpen(false); }
  };

  // Ajoute la zone sélectionnée à l'array (sans doublons)
  const selectMatch = (m) => {
    if (!m) return;
    const key = `${m.kind}::${m.value}`;
    const alreadyIn = autocompleteValues.some(av => `${av.kind}::${av.value}` === key);
    if (!alreadyIn) {
      setAutocompleteValues([...autocompleteValues, { kind: m.kind, value: m.value }]);
    }
    setQuery('');
    setOpen(false);
    inputRef.current?.focus();
  };

  // Retire un chip par index
  const removeChip = (idx) => {
    setAutocompleteValues(autocompleteValues.filter((_, i) => i !== idx));
    inputRef.current?.focus();
  };

  return (
    <header className="header">
      <a href="#" className="brand" onClick={(e) => e.preventDefault()}>
        <span className="brand-mark">V</span>
        <span className="brand-text">
          <span className="brand-name">{t.brand_full}</span>
          <span className="brand-tag">{t.brand_tagline}</span>
        </span>
      </a>
      <nav className="header-nav">
        <a href="#" className="nav-link active" onClick={(e) => e.preventDefault()}>{t.nav_buy}</a>
        <a href="#" className="nav-link" onClick={(e) => e.preventDefault()}>{t.nav_analyze}</a>
        <a href="#" className="nav-link" onClick={(e) => e.preventDefault()}>{t.nav_market}</a>
        <a href="#" className="nav-link" onClick={(e) => e.preventDefault()}>{t.nav_about}</a>
        <a
          href="#/tarifs"
          className="nav-link"
          onClick={(e) => { e.preventDefault(); window.navigateTo('#/tarifs'); }}
          style={{ fontWeight: 600 }}
        >
          {lang === 'fr' ? 'Tarifs' : 'Pricing'}
        </a>
      </nav>
      <div className="header-search search-autocomplete search-multizone" ref={wrapRef}>
        <span className="search-icon">
          <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
            <circle cx="6" cy="6" r="4.5" stroke="currentColor" strokeWidth="1.4"/>
            <path d="M9.5 9.5L13 13" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round"/>
          </svg>
        </span>
        {/* Chips des zones sélectionnées */}
        {autocompleteValues.map((av, idx) => (
          <div key={`${av.kind}-${av.value}`} className="search-active-pill">
            <span className={`pill-kind pill-kind-${av.kind}`}>
              {av.kind === 'city'
                ? (lang === 'fr' ? 'Ville' : 'City')
                : (lang === 'fr' ? 'Quartier' : 'Nbhd')}
            </span>
            <span className="pill-value">{av.value}</span>
            <button className="pill-clear" onClick={() => removeChip(idx)}
              aria-label={lang === 'fr' ? 'Retirer' : 'Remove'}>
              <svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round">
                <path d="M2 2l6 6M8 2l-6 6"/>
              </svg>
            </button>
          </div>
        ))}
        {/* Input toujours visible pour saisir une nouvelle zone */}
        <input
          ref={inputRef}
          type="search"
          placeholder={autocompleteValues.length === 0 ? t.search_placeholder : (lang === 'fr' ? 'Ajouter une zone…' : 'Add a zone…')}
          value={query}
          onChange={(e) => { setQuery(e.target.value); setOpen(true); }}
          onFocus={() => { if (query) setOpen(true); }}
          onKeyDown={onKeyDown}
          autoComplete="off"
          className="search-zone-input"
        />
        {open && matches.length > 0 && (
          <div className="search-autocomplete-list">
            {matches.map((m, i) => (
              <button
                key={`${m.kind}-${m.value}-${m.city || ''}`}
                className={`search-autocomplete-item search-autocomplete-item--${m.kind} ${i === highlightIdx ? 'search-autocomplete-item--highlighted' : ''}`}
                onMouseEnter={() => setHighlightIdx(i)}
                onClick={() => selectMatch(m)}
              >
                <span className="ac-icon" aria-hidden="true">
                  {m.kind === 'city' ? '🏙' : '📍'}
                </span>
                <span className="ac-main">
                  <span className="ac-label">{m.value}</span>
                  {m.kind === 'neighborhood' && m.city && (
                    <span className="ac-sub">{m.city}</span>
                  )}
                </span>
                <span className="ac-count">{m.count}</span>
              </button>
            ))}
          </div>
        )}
      </div>
      <div className="header-actions">
        {activeProfile && PROFILE_UI_CONFIG.find(p => p.id === activeProfile) && (() => {
          const profileCfg = PROFILE_UI_CONFIG.find(p => p.id === activeProfile);
          const icons = typeof window.LUCIDE_ICONS !== 'undefined' ? window.LUCIDE_ICONS : null;
          const profileIconMap = typeof window.PROFILE_ICONS !== 'undefined' ? window.PROFILE_ICONS : null;
          const iconSvg = icons && profileIconMap && profileIconMap[activeProfile]
            ? icons[profileIconMap[activeProfile]]
            : null;
          return (
            <button
              className="profile-chip"
              onClick={() => onOpenProfileModal && onOpenProfileModal()}
              title={lang === 'en' ? 'Edit your profile' : 'Modifier votre profil'}
              type="button"
            >
              {iconSvg ? (
                <span
                  className="profile-chip-icon"
                  dangerouslySetInnerHTML={{ __html: window.DOMPurify ? window.DOMPurify.sanitize(iconSvg, { USE_PROFILES: { svg: true, html: true } }) : iconSvg }}
                />
              ) : (
                <span className="profile-chip-icon">{profileCfg.icon}</span>
              )}
              <span className="profile-chip-label">
                {lang === 'en' ? profileCfg.en : profileCfg.fr}
              </span>
            </button>
          );
        })()}
        <div className="lang-switch">
          <button className={lang === "fr" ? "active" : ""} onClick={() => setLang("fr")}>FR</button>
          <button className={lang === "en" ? "active" : ""} onClick={() => setLang("en")}>EN</button>
        </div>
        {/* Upgrade button — visible pour les utilisateurs Découverte connectés */}
        {window.vestora && window.vestora.auth && window.vestora.auth.currentUser
          && window.vestora.auth.currentUser.plan === 'decouverte' && (
          <a
            href="#/tarifs"
            onClick={(e) => { e.preventDefault(); window.navigateTo('#/tarifs'); }}
            style={{
              display: 'inline-flex', alignItems: 'center', gap: 5,
              padding: '6px 14px', borderRadius: 6,
              background: 'var(--accent-soft)', color: 'var(--accent-ink)',
              textDecoration: 'none', fontSize: 13, fontWeight: 600,
              border: '1px solid oklch(0.36 0.07 155 / 0.2)',
            }}
          >
            <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
              <path d="M6 1L8 4.5H11L8.5 6.8L9.5 10L6 8L2.5 10L3.5 6.8L1 4.5H4L6 1Z" fill="currentColor"/>
            </svg>
            Upgrade
          </a>
        )}
        {typeof window.AccountChip !== 'undefined'
          ? <window.AccountChip lang={lang} onOpenLogin={onOpenLogin} />
          : <button className="btn-ghost btn" onClick={() => onOpenLogin && onOpenLogin()}>{lang === "fr" ? "Se connecter" : "Sign in"}</button>
        }
      </div>
    </header>
  );
}

// ---- ProfileSelector ----
const PROFILE_UI_CONFIG = [
  { id: "cashflow_hunter", icon: "💰", fr: "Rendement",  en: "Cash Flow",  desc_fr: "Cap rate & flux positif",     desc_en: "Cap rate & positive cash flow" },
  { id: "first_buyer",     icon: "🏠", fr: "1er achat",  en: "First Buy",  desc_fr: "Plex avec occupation",        desc_en: "Owner-occupant plex" },
  { id: "buy_and_hold",    icon: "📈", fr: "Long terme", en: "Hold",       desc_fr: "Patrimoine 15–30 ans",        desc_en: "15–30 yr wealth build" },
  { id: "value_add",       icon: "🔧", fr: "Value add",  en: "Value Add",  desc_fr: "Loyers sous le marché",       desc_en: "Below-market rents" },
  { id: "brrrr",           icon: "♻️", fr: "BRRRR",      en: "BRRRR",      desc_fr: "Refinancement après rénov.",  desc_en: "Renovate & refinance" },
  { id: "student_housing", icon: "🎓", fr: "Étudiant",   en: "Student",    desc_fr: "Par chambre, haute densité",  desc_en: "Room-by-room rental" },
];

function ProfileSelector({ activeProfile, setActiveProfile, lang }) {
  return (
    <div className="profile-selector" role="tablist" aria-label={lang === "fr" ? "Profil investisseur" : "Investor profile"}>
      {PROFILE_UI_CONFIG.map(p => (
        <button
          key={p.id}
          role="tab"
          aria-selected={activeProfile === p.id}
          className={`profile-tab ${activeProfile === p.id ? "active" : ""}`}
          onClick={() => setActiveProfile(p.id)}
          title={lang === "fr" ? p.desc_fr : p.desc_en}
        >
          <span className="profile-tab-icon">{p.icon}</span>
          <span className="profile-tab-label">{lang === "fr" ? p.fr : p.en}</span>
        </button>
      ))}
    </div>
  );
}

// ---- Generic popover ----
function Popover({ trigger, active, children, width = 260, disabled = false, onDisabledClick }) {
  const [open, setOpen] = useState(false);
  const [pos, setPos] = useState({ left: 0, top: 0 });
  const ref = useRef(null);
  const btnRef = useRef(null);
  useEffect(() => {
    const onClick = (e) => {
      if (ref.current && !ref.current.contains(e.target) && btnRef.current && !btnRef.current.contains(e.target)) setOpen(false);
    };
    document.addEventListener('mousedown', onClick);
    return () => document.removeEventListener('mousedown', onClick);
  }, []);
  useEffect(() => {
    if (!open || !btnRef.current) return;
    const r = btnRef.current.getBoundingClientRect();
    let left = r.left;
    if (left + width > window.innerWidth - 12) left = window.innerWidth - width - 12;
    if (left < 12) left = 12;
    setPos({ left, top: r.bottom + 6 });
  }, [open, width]);

  const handleClick = () => {
    if (disabled) {
      if (typeof onDisabledClick === 'function') onDisabledClick();
    } else {
      setOpen(!open);
    }
  };

  return (
    <>
      <button
        ref={btnRef}
        className={`filter-pill ${active ? 'active' : ''} ${disabled ? 'filter-pill--locked' : ''}`}
        onClick={handleClick}
        style={disabled ? { opacity: 0.72, cursor: 'pointer' } : undefined}
        title={disabled ? (typeof trigger === 'string' ? `${trigger} — Plan Investisseur requis` : 'Plan Investisseur requis') : undefined}
      >
        {trigger}
        <svg width="10" height="10" viewBox="0 0 10 10"><path d="M2 4l3 3 3-3" stroke="currentColor" strokeWidth="1.2" fill="none"/></svg>
      </button>
      {!disabled && open && (
        <div ref={ref} className="popover-panel" style={{
          position: 'fixed', top: pos.top, left: pos.left, zIndex: 510,
          background: 'var(--bg-elev)', border: '1px solid var(--line)', borderRadius: 10,
          padding: 14, width, boxShadow: 'var(--shadow-md)'
        }}>
          {children(() => setOpen(false))}
        </div>
      )}
    </>
  );
}

// Helper : tableau des types de biens
const PROPERTY_TYPES = [
  { k: "all", l: { fr: "Tous", en: "All" } },
  { k: "Triplex", l: { fr: "Triplex", en: "Triplex" } },
  { k: "Quadruplex", l: { fr: "Quadruplex", en: "Quadruplex" } },
  { k: "Sextuplex", l: { fr: "Sextuplex", en: "6-plex" } },
  { k: "8-Logements", l: { fr: "8 logements", en: "8-unit" } },
  { k: "Duplex", l: { fr: "Duplex", en: "Duplex" } },
  { k: "Condo", l: { fr: "Condo", en: "Condo" } },
  { k: "Unifamiliale", l: { fr: "Unifamiliale", en: "Single-family" } }
];

const UNITS_OPTS = [
  { k: "all", l: { fr: "Toutes", en: "All" } },
  { k: "1", l: { fr: "1", en: "1" } },
  { k: "2", l: { fr: "2", en: "2" } },
  { k: "3", l: { fr: "3", en: "3" } },
  { k: "4+", l: { fr: "4+", en: "4+" } }
];

const VS_MARKET_OPTS = [
  { k: "below",   l: { fr: "Sous le marché",   en: "Below market" } },
  { k: "at",      l: { fr: "Au marché",        en: "At market" } },
  { k: "premium", l: { fr: "Prime de marché",  en: "Premium" } },
];

// ---- Icône cadenas inline (filtres premium) ----
function LockBadge() {
  return (
    <svg
      width="11" height="11" viewBox="0 0 24 24" fill="none"
      stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"
      style={{ display: 'inline-block', verticalAlign: 'middle', marginLeft: 4, flexShrink: 0, opacity: 0.7 }}
      aria-label="Filtre premium"
    >
      <rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
      <path d="M7 11V7a5 5 0 0 1 10 0v4"/>
    </svg>
  );
}

// ---- FilterBar (desktop) ----
function FilterBar({ t, lang, filters, setFilters, sort, setSort, count, suggestions, onResetAll, activeProfile, onOpenMobileFilters, dataAvailability }) {
  const da = dataAvailability || {};

  // ---- Gating tier ----
  const [userTier, setUserTier] = useState('decouverte');
  const [paywallOpen, setPaywallOpen] = useState(false);

  useEffect(() => {
    const tg = window.vestora && window.vestora.tierGate;
    if (tg && typeof tg.getCurrentTier === 'function') {
      tg.getCurrentTier().then(tier => setUserTier(tier)).catch(() => setUserTier('decouverte'));
    }
  }, []);

  // isPremiumLocked : true si le tier actuel ne peut pas accéder aux filtres premium
  const isPremiumLocked = userTier === 'decouverte';

  const typeLabel = PROPERTY_TYPES.find(x => x.k === filters.type)?.l[lang] || filters.type;
  const unitLabel = UNITS_OPTS.find(x => x.k === filters.units)?.l[lang] || filters.units;

  const priceActive = filters.priceMin > 0 || filters.priceMax < 3000000;
  const priceLabel = priceActive
    ? `${window.fmt.moneyShort(filters.priceMin)} – ${window.fmt.moneyShort(filters.priceMax)}`
    : `${t.filter_price}`;

  const cityLabel = filters.cities.length === 0
    ? (lang === 'fr' ? 'Villes' : 'Cities')
    : filters.cities.length === 1
      ? filters.cities[0]
      : `${lang === 'fr' ? 'Villes' : 'Cities'} (${filters.cities.length})`;

  const neighLabel = filters.neighborhoods.length === 0
    ? (lang === 'fr' ? 'Quartiers' : 'Neighborhoods')
    : filters.neighborhoods.length === 1
      ? filters.neighborhoods[0]
      : `${lang === 'fr' ? 'Quartiers' : 'Neighborhoods'} (${filters.neighborhoods.length})`;

  const scoreLabel = filters.scoreMin > 0
    ? `${t.filter_score} ${filters.scoreMin}+`
    : t.filter_score;

  const capRateLabel = filters.capRateMin > 0
    ? `Cap ≥ ${filters.capRateMin}%`
    : (lang === 'fr' ? 'Cap rate' : 'Cap rate');

  const vsMarketLabel = filters.vsMarket.length === 0
    ? (lang === 'fr' ? 'Marché' : 'Market')
    : filters.vsMarket.length === 1
      ? VS_MARKET_OPTS.find(o => o.k === filters.vsMarket[0])?.l[lang]
      : `${lang === 'fr' ? 'Marché' : 'Market'} (${filters.vsMarket.length})`;

  const moreActive = filters.dscrMin1 || filters.cashFlowPositive || filters.daysOnMarketMin30
    || filters.yearBuiltMin || filters.bedroomsMin || filters.bathroomsMin || filters.areaMin;
  const moreCount = [
    filters.dscrMin1, filters.cashFlowPositive, filters.daysOnMarketMin30,
    !!filters.yearBuiltMin, !!filters.bedroomsMin, !!filters.bathroomsMin, !!filters.areaMin
  ].filter(Boolean).length;
  const moreLabel = moreActive
    ? `${lang === 'fr' ? 'Plus' : 'More'} (${moreCount})`
    : (lang === 'fr' ? 'Plus de filtres' : 'More filters');

  // Suggestions de villes triées par count
  const cityOptions = useMemo(() => suggestions?.cities || [], [suggestions]);
  const neighOptions = useMemo(() => {
    if (!suggestions?.neighborhoods) return [];
    if (filters.cities.length !== 1) return [];
    return suggestions.neighborhoods.filter(n => n.city === filters.cities[0]);
  }, [suggestions, filters.cities]);

  // Mobile : un seul bouton "Filtres" avec badge
  const mobileTriggerCount = useMemo(() => {
    let n = 0;
    if (filters.type !== "all") n++;
    if (priceActive) n++;
    if (filters.scoreMin > 0) n++;
    if (filters.units !== "all") n++;
    if (filters.cities.length) n++;
    if (filters.neighborhoods.length) n++;
    if (filters.capRateMin > 0) n++;
    if (filters.vsMarket.length) n++;
    if (filters.dscrMin1) n++;
    if (filters.cashFlowPositive) n++;
    if (filters.daysOnMarketMin30) n++;
    if (filters.yearBuiltMin) n++;
    if (filters.bedroomsMin) n++;
    if (filters.bathroomsMin) n++;
    if (filters.areaMin) n++;
    return n;
  }, [filters, priceActive]);

  return (
    <div className="filterbar">
      <button className="mobile-filter-trigger" onClick={onOpenMobileFilters}>
        <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.4">
          <path d="M2 3h10M3.5 7h7M5 11h4"/>
        </svg>
        <span>{lang === 'fr' ? 'Filtres' : 'Filters'}</span>
        {mobileTriggerCount > 0 && <span className="mobile-filter-trigger-badge">{mobileTriggerCount}</span>}
      </button>

      <div className="filterbar-pills">
        <Popover active={filters.type !== "all"} trigger={`${t.filter_type}: ${typeLabel}`}>
          {(close) => (
            <div className="popover-list">
              {PROPERTY_TYPES.map(o => (
                <button key={o.k} className={`popover-item ${filters.type === o.k ? 'active' : ''}`}
                  onClick={() => { setFilters({ ...filters, type: o.k }); close(); }}>
                  {o.l[lang]}
                  {filters.type === o.k && <Check/>}
                </button>
              ))}
            </div>
          )}
        </Popover>

        <Popover active={filters.cities.length > 0} trigger={cityLabel} width={300}>
          {(close) => (
            <div>
              <div className="popover-section-title">{lang === 'fr' ? 'Villes' : 'Cities'}</div>
              <div className="popover-list popover-list-scroll">
                {cityOptions.map(c => {
                  const checked = filters.cities.includes(c.value);
                  return (
                    <button key={c.value}
                      className={`popover-item ${checked ? 'active' : ''}`}
                      onClick={() => {
                        const next = checked
                          ? filters.cities.filter(x => x !== c.value)
                          : [...filters.cities, c.value];
                        const nextNeigh = next.length === 1 ? filters.neighborhoods : [];
                        setFilters({ ...filters, cities: next, neighborhoods: nextNeigh });
                      }}>
                      <span><span style={{ marginRight: 8 }}>{checked ? '☑' : '☐'}</span>{c.value}</span>
                      <span style={{ fontSize: 11, color: 'var(--ink-3)' }}>{c.count}</span>
                    </button>
                  );
                })}
              </div>
              {filters.cities.length > 0 && (
                <button className="btn btn-ghost" style={{ width: '100%', marginTop: 8 }}
                  onClick={() => setFilters({ ...filters, cities: [], neighborhoods: [] })}>
                  {lang === 'fr' ? 'Effacer la sélection' : 'Clear selection'}
                </button>
              )}
            </div>
          )}
        </Popover>

        {filters.cities.length === 1 && neighOptions.length > 0 && (
          <Popover active={filters.neighborhoods.length > 0} trigger={neighLabel} width={300}>
            {(close) => (
              <div>
                <div className="popover-section-title">{lang === 'fr' ? `Quartiers de ${filters.cities[0]}` : `Neighborhoods of ${filters.cities[0]}`}</div>
                <div className="popover-list popover-list-scroll">
                  {neighOptions.map(n => {
                    const checked = filters.neighborhoods.includes(n.value);
                    return (
                      <button key={n.value}
                        className={`popover-item ${checked ? 'active' : ''}`}
                        onClick={() => {
                          const next = checked
                            ? filters.neighborhoods.filter(x => x !== n.value)
                            : [...filters.neighborhoods, n.value];
                          setFilters({ ...filters, neighborhoods: next });
                        }}>
                        <span><span style={{ marginRight: 8 }}>{checked ? '☑' : '☐'}</span>{n.value}</span>
                        <span style={{ fontSize: 11, color: 'var(--ink-3)' }}>{n.count}</span>
                      </button>
                    );
                  })}
                </div>
                {filters.neighborhoods.length > 0 && (
                  <button className="btn btn-ghost" style={{ width: '100%', marginTop: 8 }}
                    onClick={() => setFilters({ ...filters, neighborhoods: [] })}>
                    {lang === 'fr' ? 'Effacer la sélection' : 'Clear selection'}
                  </button>
                )}
              </div>
            )}
          </Popover>
        )}

        <Popover active={priceActive} trigger={priceLabel} width={300}>
          {(close) => (
            <div>
              <div className="popover-section-title">{t.filter_price}</div>
              <div style={{ display: 'flex', justifyContent: 'space-between', fontFamily: 'var(--mono)', fontSize: 12, marginBottom: 8 }}>
                <span>{window.fmt.moneyShort(filters.priceMin)}</span>
                <span>{window.fmt.moneyShort(filters.priceMax)}</span>
              </div>
              <input type="range" min="0" max="3000000" step="50000" value={filters.priceMin}
                onChange={(e) => setFilters({ ...filters, priceMin: Math.min(+e.target.value, filters.priceMax - 100000) })}
                style={{ width: '100%' }}/>
              <input type="range" min="0" max="3000000" step="50000" value={filters.priceMax}
                onChange={(e) => setFilters({ ...filters, priceMax: Math.max(+e.target.value, filters.priceMin + 100000) })}
                style={{ width: '100%', marginTop: 8 }}/>
              <div style={{ display: 'flex', gap: 8, marginTop: 12 }}>
                <button className="btn btn-ghost" style={{ flex: 1 }}
                  onClick={() => { setFilters({ ...filters, priceMin: 0, priceMax: 3000000 }); }}>
                  {lang === "fr" ? "Effacer" : "Clear"}
                </button>
                <button className="btn" style={{ flex: 1 }} onClick={close}>{lang === "fr" ? "Appliquer" : "Apply"}</button>
              </div>
            </div>
          )}
        </Popover>

        <Popover active={filters.scoreMin > 0} trigger={scoreLabel} width={280}>
          {(close) => (
            <div>
              <div className="popover-section-title">{t.filter_score}</div>
              <div style={{ display: 'flex', justifyContent: 'space-between', fontFamily: 'var(--mono)', fontSize: 12, marginBottom: 8 }}>
                <span>{filters.scoreMin}</span>
                <span>100</span>
              </div>
              <input type="range" min="0" max="100" step="5" value={filters.scoreMin}
                onChange={(e) => setFilters({ ...filters, scoreMin: +e.target.value })}
                style={{ width: '100%' }}/>
              <div style={{ display: 'flex', gap: 6, marginTop: 12, flexWrap: 'wrap' }}>
                {[0, 50, 65, 80].map(v => (
                  <button key={v} className={`chip-quick ${filters.scoreMin === v ? 'active' : ''}`}
                    onClick={() => setFilters({ ...filters, scoreMin: v })}>
                    {v === 0 ? (lang === 'fr' ? 'Tous' : 'All') : `${v}+`}
                  </button>
                ))}
              </div>
            </div>
          )}
        </Popover>

        <Popover active={filters.units !== "all"} trigger={`${t.filter_units}: ${unitLabel}`}>
          {(close) => (
            <div className="popover-list">
              {UNITS_OPTS.map(o => (
                <button key={o.k} className={`popover-item ${filters.units === o.k ? 'active' : ''}`}
                  onClick={() => { setFilters({ ...filters, units: o.k }); close(); }}>
                  {o.l[lang]}
                  {filters.units === o.k && <Check/>}
                </button>
              ))}
            </div>
          )}
        </Popover>

        {/* === FILTRES PREMIUM — cadenas si tier decouverte === */}
          <Popover
            active={!isPremiumLocked && filters.capRateMin > 0}
            trigger={<span style={{ display: 'inline-flex', alignItems: 'center' }}>{capRateLabel}{isPremiumLocked && <LockBadge />}</span>}
            width={280}
            disabled={isPremiumLocked}
            onDisabledClick={() => setPaywallOpen(true)}
          >
            {(close) => (
              <div>
                <div className="popover-section-title">{lang === 'fr' ? 'Cap rate minimum' : 'Minimum cap rate'}</div>
                <div style={{ display: 'flex', justifyContent: 'space-between', fontFamily: 'var(--mono)', fontSize: 12, marginBottom: 8 }}>
                  <span>{filters.capRateMin}%</span>
                  <span>15%</span>
                </div>
                <input type="range" min="0" max="15" step="0.5" value={filters.capRateMin}
                  onChange={(e) => setFilters({ ...filters, capRateMin: +e.target.value })}
                  style={{ width: '100%' }}/>
                <div style={{ display: 'flex', gap: 6, marginTop: 12, flexWrap: 'wrap' }}>
                  {[0, 4, 5, 6, 7, 8].map(v => (
                    <button key={v} className={`chip-quick ${filters.capRateMin === v ? 'active' : ''}`}
                      onClick={() => setFilters({ ...filters, capRateMin: v })}>
                      {v === 0 ? (lang === 'fr' ? 'Tous' : 'All') : `${v}%+`}
                    </button>
                  ))}
                </div>
              </div>
            )}
          </Popover>

          <Popover
            active={!isPremiumLocked && filters.vsMarket.length > 0}
            trigger={<span style={{ display: 'inline-flex', alignItems: 'center' }}>{vsMarketLabel}{isPremiumLocked && <LockBadge />}</span>}
            width={260}
            disabled={isPremiumLocked}
            onDisabledClick={() => setPaywallOpen(true)}
          >
            {(close) => (
              <div>
                <div className="popover-section-title">{lang === 'fr' ? 'Position vs marché' : 'Market position'}</div>
                <div className="popover-list">
                  {VS_MARKET_OPTS.map(o => {
                    const checked = filters.vsMarket.includes(o.k);
                    return (
                      <button key={o.k}
                        className={`popover-item ${checked ? 'active' : ''}`}
                        onClick={() => {
                          const next = checked
                            ? filters.vsMarket.filter(x => x !== o.k)
                            : [...filters.vsMarket, o.k];
                          setFilters({ ...filters, vsMarket: next });
                        }}>
                        <span><span style={{ marginRight: 8 }}>{checked ? '☑' : '☐'}</span>{o.l[lang]}</span>
                      </button>
                    );
                  })}
                </div>
              </div>
            )}
          </Popover>

        <Popover
          active={!isPremiumLocked && moreActive}
          trigger={<span style={{ display: 'inline-flex', alignItems: 'center' }}>{moreLabel}{isPremiumLocked && <LockBadge />}</span>}
          width={300}
          disabled={isPremiumLocked}
          onDisabledClick={() => setPaywallOpen(true)}
        >
          {(close) => (
            <div>
              <div className="popover-section-title">{lang === 'fr' ? 'Critères additionnels' : 'Additional criteria'}</div>
              <label className="popover-checkbox">
                <input type="checkbox" checked={filters.dscrMin1}
                  onChange={(e) => setFilters({ ...filters, dscrMin1: e.target.checked })}/>
                <span>{lang === 'fr' ? 'DSCR ≥ 1 (cash-flow couvre la dette)' : 'DSCR ≥ 1 (covers debt)'}</span>
              </label>
              <label className="popover-checkbox">
                <input type="checkbox" checked={filters.cashFlowPositive}
                  onChange={(e) => setFilters({ ...filters, cashFlowPositive: e.target.checked })}/>
                <span>{lang === 'fr' ? 'Cash-flow positif' : 'Positive cash flow'}</span>
              </label>
              <label className="popover-checkbox">
                <input type="checkbox" checked={filters.daysOnMarketMin30}
                  onChange={(e) => setFilters({ ...filters, daysOnMarketMin30: e.target.checked })}/>
                <span>{lang === 'fr' ? '≥ 30 jours sur le marché' : '≥ 30 days on market'}</span>
              </label>

              {da.yearBuilt && (
                <div className="popover-section-title" style={{ marginTop: 12 }}>
                  {lang === 'fr' ? t.filter_year_built : t.filter_year_built}
                </div>
              )}
              {da.yearBuilt && (
                <div>
                  <div style={{ display: 'flex', justifyContent: 'space-between', fontFamily: 'var(--mono)', fontSize: 12, marginBottom: 6 }}>
                    <span>{filters.yearBuiltMin ? `${lang === 'fr' ? 'Après' : 'After'} ${filters.yearBuiltMin}` : (lang === 'fr' ? 'Toutes années' : 'All years')}</span>
                    <span>2025</span>
                  </div>
                  <input type="range" min="1900" max="2025" step="5"
                    value={filters.yearBuiltMin || 1900}
                    onChange={(e) => setFilters({ ...filters, yearBuiltMin: +e.target.value === 1900 ? null : +e.target.value })}
                    style={{ width: '100%' }}/>
                  <div style={{ display: 'flex', gap: 6, marginTop: 8, flexWrap: 'wrap' }}>
                    {[null, 1960, 1980, 1990, 2000, 2010].map(v => (
                      <button key={v ?? 'all'} className={`chip-quick ${(filters.yearBuiltMin === v) ? 'active' : ''}`}
                        onClick={() => setFilters({ ...filters, yearBuiltMin: v })}>
                        {v === null ? (lang === 'fr' ? 'Tous' : 'All') : `${v}+`}
                      </button>
                    ))}
                  </div>
                </div>
              )}

              {da.bedrooms && (
                <div className="popover-section-title" style={{ marginTop: 12 }}>
                  {lang === 'fr' ? t.filter_bedrooms_min : t.filter_bedrooms_min}
                </div>
              )}
              {da.bedrooms && (
                <div className="filter-toggle-group">
                  {[1, 2, 3, 4].map(n => (
                    <button key={n}
                      className={`filter-toggle-btn ${filters.bedroomsMin === n ? 'active' : ''}`}
                      onClick={() => setFilters({ ...filters, bedroomsMin: filters.bedroomsMin === n ? null : n })}>
                      {n}+
                    </button>
                  ))}
                </div>
              )}

              {da.bathrooms && (
                <div className="popover-section-title" style={{ marginTop: 12 }}>
                  {lang === 'fr' ? t.filter_bathrooms_min : t.filter_bathrooms_min}
                </div>
              )}
              {da.bathrooms && (
                <div className="filter-toggle-group">
                  {[1, 2, 3].map(n => (
                    <button key={n}
                      className={`filter-toggle-btn ${filters.bathroomsMin === n ? 'active' : ''}`}
                      onClick={() => setFilters({ ...filters, bathroomsMin: filters.bathroomsMin === n ? null : n })}>
                      {n}+
                    </button>
                  ))}
                </div>
              )}

              {da.area && (
                <div className="popover-section-title" style={{ marginTop: 12 }}>
                  {lang === 'fr' ? t.filter_area_min : t.filter_area_min}
                </div>
              )}
              {da.area && (
                <div>
                  <div style={{ display: 'flex', justifyContent: 'space-between', fontFamily: 'var(--mono)', fontSize: 12, marginBottom: 6 }}>
                    <span>{filters.areaMin ? `≥ ${filters.areaMin} ${t.sqft}` : (lang === 'fr' ? 'Tous' : 'All')}</span>
                    <span>5 000 {t.sqft}</span>
                  </div>
                  <input type="range" min="500" max="5000" step="100"
                    value={filters.areaMin || 500}
                    onChange={(e) => setFilters({ ...filters, areaMin: +e.target.value === 500 ? null : +e.target.value })}
                    style={{ width: '100%' }}/>
                  <div style={{ display: 'flex', gap: 6, marginTop: 8, flexWrap: 'wrap' }}>
                    {[null, 1000, 1500, 2000, 3000].map(v => (
                      <button key={v ?? 'all'} className={`chip-quick ${(filters.areaMin === v) ? 'active' : ''}`}
                        onClick={() => setFilters({ ...filters, areaMin: v })}>
                        {v === null ? (lang === 'fr' ? 'Tous' : 'All') : `${v}+`}
                      </button>
                    ))}
                  </div>
                </div>
              )}
            </div>
          )}
        </Popover>
      </div>

      <div className="filter-divider" />
      <select className="sort-select" value={sort} onChange={(e) => setSort(e.target.value)}>
        <option value="score_desc">{t.sort_score_desc}</option>
        <option value="caprate_desc">{t.sort_caprate_desc}</option>
        <option value="price_asc">{t.sort_price_asc}</option>
        <option value="price_desc">{t.sort_price_desc}</option>
      </select>

      <div className="results-count"><strong>{count}</strong> {t.results_count(count).split(" ").slice(1).join(" ")}</div>

      {/* PaywallModal — ouvert si un filtre premium est cliqué en tier decouverte */}
      {paywallOpen && (
        <window.PaywallModal
          open={paywallOpen}
          onClose={() => setPaywallOpen(false)}
          feature="filters_premium"
          featureLabel="Filtres premium"
          requiredTier="investisseur"
          currentTier={userTier}
          reason="tier_too_low"
        />
      )}
    </div>
  );
}

// ---- ActiveFilterChips ----
// Calcule la liste des chips actifs (différents des défauts du profil) et les expose comme boutons removables.
function diffFromProfileDefaults(filters, activeProfile, autocompleteValues, mapBounds, lang, t) {
  // Le helper n'est pas exposé globalement — on en met une copie locale pour comparer.
  const profileDefaults = ((pid) => {
    const base = { type: "all", priceMin: 0, priceMax: 3000000, scoreMin: 0, units: "all",
      cities: [], neighborhoods: [], capRateMin: 0, dscrMin1: false, cashFlowPositive: false,
      vsMarket: [], daysOnMarketMin30: false,
      yearBuiltMin: null, bedroomsMin: null, bathroomsMin: null, areaMin: null };
    switch (pid) {
      case 'cashflow_hunter':  return { ...base, scoreMin: 30, cashFlowPositive: true };
      case 'first_buyer':      return { ...base, scoreMin: 30, units: "2" };
      case 'buy_and_hold':     return { ...base, scoreMin: 40, daysOnMarketMin30: true };
      case 'value_add':        return { ...base, scoreMin: 30, capRateMin: 5, vsMarket: ['below'] };
      case 'brrrr':            return { ...base, scoreMin: 30, units: "4+", capRateMin: 4, vsMarket: ['below'] };
      case 'student_housing':  return { ...base, scoreMin: 25, units: "3" };
      default: return base;
    }
  })(activeProfile);

  const chips = [];

  // Un chip par zone multi-sélectionnée
  (autocompleteValues || []).forEach((av, idx) => {
    chips.push({
      key: `autocomplete-${idx}`,
      label: av.value,
      kind: 'place',
      onRemove: { type: 'remove-autocomplete', idx }
    });
  });

  if (mapBounds) {
    chips.push({
      key: 'mapBounds',
      label: lang === 'fr' ? 'Zone de la carte' : 'Map area',
      kind: 'place',
      onRemove: 'mapBounds'
    });
  }

  if (filters.type !== profileDefaults.type) {
    chips.push({ key: 'type', label: `${t.filter_type}: ${PROPERTY_TYPES.find(p => p.k === filters.type)?.l[lang] || filters.type}`, onRemove: { type: profileDefaults.type } });
  }
  if (filters.priceMin !== profileDefaults.priceMin || filters.priceMax !== profileDefaults.priceMax) {
    chips.push({
      key: 'price',
      label: `${window.fmt.moneyShort(filters.priceMin)} – ${window.fmt.moneyShort(filters.priceMax)}`,
      onRemove: { priceMin: profileDefaults.priceMin, priceMax: profileDefaults.priceMax }
    });
  }
  if (filters.scoreMin !== profileDefaults.scoreMin) {
    chips.push({ key: 'scoreMin', label: `${t.filter_score} ${filters.scoreMin}+`, onRemove: { scoreMin: profileDefaults.scoreMin } });
  }
  if (filters.units !== profileDefaults.units) {
    chips.push({ key: 'units', label: `${UNITS_OPTS.find(u => u.k === filters.units)?.l[lang] || filters.units} ${t.units}`, onRemove: { units: profileDefaults.units } });
  }
  filters.cities.forEach(c => {
    if (!profileDefaults.cities.includes(c)) {
      chips.push({ key: `city-${c}`, label: c, onRemove: { type: 'remove-city', value: c } });
    }
  });
  filters.neighborhoods.forEach(n => {
    if (!profileDefaults.neighborhoods.includes(n)) {
      chips.push({ key: `neigh-${n}`, label: n, onRemove: { type: 'remove-neigh', value: n } });
    }
  });
  if (filters.capRateMin !== profileDefaults.capRateMin) {
    chips.push({ key: 'capRateMin', label: `Cap ≥ ${filters.capRateMin}%`, onRemove: { capRateMin: profileDefaults.capRateMin } });
  }
  if (filters.dscrMin1 !== profileDefaults.dscrMin1) {
    chips.push({ key: 'dscrMin1', label: 'DSCR ≥ 1', onRemove: { dscrMin1: profileDefaults.dscrMin1 } });
  }
  if (filters.cashFlowPositive !== profileDefaults.cashFlowPositive) {
    chips.push({ key: 'cashFlowPositive', label: lang === 'fr' ? 'Cash-flow positif' : 'Positive cash flow', onRemove: { cashFlowPositive: profileDefaults.cashFlowPositive } });
  }
  filters.vsMarket.forEach(v => {
    if (!profileDefaults.vsMarket.includes(v)) {
      chips.push({ key: `vsm-${v}`, label: VS_MARKET_OPTS.find(o => o.k === v)?.l[lang] || v, onRemove: { type: 'remove-vsm', value: v } });
    }
  });
  if (filters.daysOnMarketMin30 !== profileDefaults.daysOnMarketMin30) {
    chips.push({ key: 'daysOnMarketMin30', label: lang === 'fr' ? '≥ 30 jours' : '≥ 30 days', onRemove: { daysOnMarketMin30: profileDefaults.daysOnMarketMin30 } });
  }
  if (filters.yearBuiltMin !== profileDefaults.yearBuiltMin && filters.yearBuiltMin) {
    chips.push({ key: 'yearBuiltMin', label: `${t.built} ≥ ${filters.yearBuiltMin}`, onRemove: { yearBuiltMin: profileDefaults.yearBuiltMin } });
  }
  if (filters.bedroomsMin !== profileDefaults.bedroomsMin && filters.bedroomsMin) {
    chips.push({ key: 'bedroomsMin', label: `${filters.bedroomsMin}+ ${t.bedrooms}`, onRemove: { bedroomsMin: profileDefaults.bedroomsMin } });
  }
  if (filters.bathroomsMin !== profileDefaults.bathroomsMin && filters.bathroomsMin) {
    chips.push({ key: 'bathroomsMin', label: `${filters.bathroomsMin}+ ${t.bathrooms}`, onRemove: { bathroomsMin: profileDefaults.bathroomsMin } });
  }
  if (filters.areaMin !== profileDefaults.areaMin && filters.areaMin) {
    chips.push({ key: 'areaMin', label: `≥ ${filters.areaMin} ${t.sqft}`, onRemove: { areaMin: profileDefaults.areaMin } });
  }

  return { chips, profileDefaults };
}

function ActiveFilterChips({ t, lang, filters, setFilters, autocompleteValues, setAutocompleteValues, mapBounds, setMapBounds, activeProfile, onResetAll }) {
  const { chips } = diffFromProfileDefaults(filters, activeProfile, autocompleteValues, mapBounds, lang, t);

  if (chips.length === 0) return null;

  const handleRemove = (chip) => {
    if (chip.onRemove?.type === 'remove-autocomplete') {
      setAutocompleteValues((autocompleteValues || []).filter((_, i) => i !== chip.onRemove.idx));
      return;
    }
    if (chip.onRemove === 'mapBounds')    { setMapBounds(null); return; }
    if (chip.onRemove?.type === 'remove-city') {
      const c = chip.onRemove.value;
      setFilters({ ...filters, cities: filters.cities.filter(x => x !== c), neighborhoods: filters.cities.filter(x => x !== c).length === 1 ? filters.neighborhoods : [] });
      return;
    }
    if (chip.onRemove?.type === 'remove-neigh') {
      setFilters({ ...filters, neighborhoods: filters.neighborhoods.filter(x => x !== chip.onRemove.value) });
      return;
    }
    if (chip.onRemove?.type === 'remove-vsm') {
      setFilters({ ...filters, vsMarket: filters.vsMarket.filter(x => x !== chip.onRemove.value) });
      return;
    }
    setFilters({ ...filters, ...chip.onRemove });
  };

  return (
    <div className="active-filter-chips">
      {chips.map(chip => (
        <button key={chip.key} className={`filter-chip ${chip.kind === 'place' ? 'filter-chip--place' : ''}`}
          onClick={() => handleRemove(chip)}>
          <span>{chip.label}</span>
          <svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round">
            <path d="M2 2l6 6M8 2l-6 6"/>
          </svg>
        </button>
      ))}
      <button className="filter-chip-clear-all" onClick={onResetAll}>
        {lang === 'fr' ? 'Réinitialiser' : 'Reset to profile'}
      </button>
    </div>
  );
}

// ---- MobileFilterDrawer ----
function MobileFilterDrawer({ t, lang, filters, setFilters, sort, setSort, count, suggestions, activeProfile, onResetAll, onClose, dataAvailability }) {
  const da = dataAvailability || {};
  const cityOptions = useMemo(() => suggestions?.cities || [], [suggestions]);
  const neighOptions = useMemo(() => {
    if (filters.cities.length !== 1) return [];
    return (suggestions?.neighborhoods || []).filter(n => n.city === filters.cities[0]);
  }, [suggestions, filters.cities]);

  return (
    <div className="mobile-filter-drawer" role="dialog" aria-modal="true">
      <div className="mobile-filter-drawer-backdrop" onClick={onClose}/>
      <div className="mobile-filter-drawer-panel">
        <div className="mobile-filter-drawer-header">
          <h3>{lang === 'fr' ? 'Filtres' : 'Filters'}</h3>
          <button className="mobile-filter-drawer-close" onClick={onClose} aria-label={lang === 'fr' ? 'Fermer' : 'Close'}>
            <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round">
              <path d="M3 3l8 8M11 3l-8 8"/>
            </svg>
          </button>
        </div>
        <div className="mobile-filter-drawer-body">
          {/* Type */}
          <div className="mobile-filter-section">
            <h4>{t.filter_type}</h4>
            <div className="chip-multiselect">
              {PROPERTY_TYPES.map(o => (
                <button key={o.k} className={`chip-multiselect-option ${filters.type === o.k ? 'active' : ''}`}
                  onClick={() => setFilters({ ...filters, type: o.k })}>{o.l[lang]}</button>
              ))}
            </div>
          </div>

          {/* Prix */}
          <div className="mobile-filter-section">
            <h4>{t.filter_price}</h4>
            <div style={{ display: 'flex', justifyContent: 'space-between', fontFamily: 'var(--mono)', fontSize: 12, marginBottom: 8 }}>
              <span>{window.fmt.moneyShort(filters.priceMin)}</span>
              <span>{window.fmt.moneyShort(filters.priceMax)}</span>
            </div>
            <input type="range" min="0" max="3000000" step="50000" value={filters.priceMin}
              onChange={(e) => setFilters({ ...filters, priceMin: Math.min(+e.target.value, filters.priceMax - 100000) })}
              style={{ width: '100%' }}/>
            <input type="range" min="0" max="3000000" step="50000" value={filters.priceMax}
              onChange={(e) => setFilters({ ...filters, priceMax: Math.max(+e.target.value, filters.priceMin + 100000) })}
              style={{ width: '100%', marginTop: 6 }}/>
          </div>

          {/* Score */}
          <div className="mobile-filter-section">
            <h4>{t.filter_score}: {filters.scoreMin}+</h4>
            <input type="range" min="0" max="100" step="5" value={filters.scoreMin}
              onChange={(e) => setFilters({ ...filters, scoreMin: +e.target.value })}
              style={{ width: '100%' }}/>
          </div>

          {/* Unités */}
          <div className="mobile-filter-section">
            <h4>{t.filter_units}</h4>
            <div className="chip-multiselect">
              {UNITS_OPTS.map(o => (
                <button key={o.k} className={`chip-multiselect-option ${filters.units === o.k ? 'active' : ''}`}
                  onClick={() => setFilters({ ...filters, units: o.k })}>{o.l[lang]}</button>
              ))}
            </div>
          </div>

          {/* Villes */}
          <div className="mobile-filter-section">
            <h4>{lang === 'fr' ? 'Villes' : 'Cities'}</h4>
            <div className="chip-multiselect chip-multiselect-wrap">
              {cityOptions.slice(0, 12).map(c => {
                const checked = filters.cities.includes(c.value);
                return (
                  <button key={c.value} className={`chip-multiselect-option ${checked ? 'active' : ''}`}
                    onClick={() => {
                      const next = checked
                        ? filters.cities.filter(x => x !== c.value)
                        : [...filters.cities, c.value];
                      setFilters({ ...filters, cities: next, neighborhoods: next.length === 1 ? filters.neighborhoods : [] });
                    }}>
                    {c.value} <small>({c.count})</small>
                  </button>
                );
              })}
            </div>
          </div>

          {filters.cities.length === 1 && neighOptions.length > 0 && (
            <div className="mobile-filter-section">
              <h4>{lang === 'fr' ? `Quartiers de ${filters.cities[0]}` : `Neighborhoods of ${filters.cities[0]}`}</h4>
              <div className="chip-multiselect chip-multiselect-wrap">
                {neighOptions.map(n => {
                  const checked = filters.neighborhoods.includes(n.value);
                  return (
                    <button key={n.value} className={`chip-multiselect-option ${checked ? 'active' : ''}`}
                      onClick={() => {
                        const next = checked
                          ? filters.neighborhoods.filter(x => x !== n.value)
                          : [...filters.neighborhoods, n.value];
                        setFilters({ ...filters, neighborhoods: next });
                      }}>
                      {n.value} <small>({n.count})</small>
                    </button>
                  );
                })}
              </div>
            </div>
          )}

          {/* Cap rate */}
          <div className="mobile-filter-section">
            <h4>{lang === 'fr' ? 'Cap rate min.' : 'Min. cap rate'}: {filters.capRateMin}%</h4>
            <input type="range" min="0" max="15" step="0.5" value={filters.capRateMin}
              onChange={(e) => setFilters({ ...filters, capRateMin: +e.target.value })}
              style={{ width: '100%' }}/>
          </div>

          {/* vs Marché */}
          <div className="mobile-filter-section">
            <h4>{lang === 'fr' ? 'Position vs marché' : 'Market position'}</h4>
            <div className="chip-multiselect">
              {VS_MARKET_OPTS.map(o => {
                const checked = filters.vsMarket.includes(o.k);
                return (
                  <button key={o.k} className={`chip-multiselect-option ${checked ? 'active' : ''}`}
                    onClick={() => {
                      const next = checked
                        ? filters.vsMarket.filter(x => x !== o.k)
                        : [...filters.vsMarket, o.k];
                      setFilters({ ...filters, vsMarket: next });
                    }}>{o.l[lang]}</button>
                );
              })}
            </div>
          </div>

          {/* Critères additionnels */}
          <div className="mobile-filter-section">
            <h4>{lang === 'fr' ? 'Critères additionnels' : 'Additional criteria'}</h4>
            <label className="popover-checkbox">
              <input type="checkbox" checked={filters.dscrMin1}
                onChange={(e) => setFilters({ ...filters, dscrMin1: e.target.checked })}/>
              <span>{lang === 'fr' ? 'DSCR ≥ 1' : 'DSCR ≥ 1'}</span>
            </label>
            <label className="popover-checkbox">
              <input type="checkbox" checked={filters.cashFlowPositive}
                onChange={(e) => setFilters({ ...filters, cashFlowPositive: e.target.checked })}/>
              <span>{lang === 'fr' ? 'Cash-flow positif' : 'Positive cash flow'}</span>
            </label>
            <label className="popover-checkbox">
              <input type="checkbox" checked={filters.daysOnMarketMin30}
                onChange={(e) => setFilters({ ...filters, daysOnMarketMin30: e.target.checked })}/>
              <span>{lang === 'fr' ? '≥ 30 jours sur le marché' : '≥ 30 days on market'}</span>
            </label>
          </div>

          {/* Année construction */}
          {da.yearBuilt && (
            <div className="mobile-filter-section">
              <h4>{t.filter_year_built}: {filters.yearBuiltMin ? `${lang === 'fr' ? 'après' : 'after'} ${filters.yearBuiltMin}` : (lang === 'fr' ? 'toutes' : 'all')}</h4>
              <input type="range" min="1900" max="2025" step="5"
                value={filters.yearBuiltMin || 1900}
                onChange={(e) => setFilters({ ...filters, yearBuiltMin: +e.target.value === 1900 ? null : +e.target.value })}
                style={{ width: '100%' }}/>
              <div className="chip-multiselect" style={{ marginTop: 8 }}>
                {[null, 1960, 1980, 1990, 2000, 2010].map(v => (
                  <button key={v ?? 'all'} className={`chip-multiselect-option ${filters.yearBuiltMin === v ? 'active' : ''}`}
                    onClick={() => setFilters({ ...filters, yearBuiltMin: v })}>
                    {v === null ? (lang === 'fr' ? 'Tous' : 'All') : `${v}+`}
                  </button>
                ))}
              </div>
            </div>
          )}

          {/* Chambres minimum */}
          {da.bedrooms && (
            <div className="mobile-filter-section">
              <h4>{t.filter_bedrooms_min}</h4>
              <div className="chip-multiselect">
                {[1, 2, 3, 4].map(n => (
                  <button key={n} className={`chip-multiselect-option ${filters.bedroomsMin === n ? 'active' : ''}`}
                    onClick={() => setFilters({ ...filters, bedroomsMin: filters.bedroomsMin === n ? null : n })}>
                    {n}+
                  </button>
                ))}
              </div>
            </div>
          )}

          {/* Salles de bain minimum */}
          {da.bathrooms && (
            <div className="mobile-filter-section">
              <h4>{t.filter_bathrooms_min}</h4>
              <div className="chip-multiselect">
                {[1, 2, 3].map(n => (
                  <button key={n} className={`chip-multiselect-option ${filters.bathroomsMin === n ? 'active' : ''}`}
                    onClick={() => setFilters({ ...filters, bathroomsMin: filters.bathroomsMin === n ? null : n })}>
                    {n}+
                  </button>
                ))}
              </div>
            </div>
          )}

          {/* Surface habitable minimum */}
          {da.area && (
            <div className="mobile-filter-section">
              <h4>{t.filter_area_min}: {filters.areaMin ? `≥ ${filters.areaMin} ${t.sqft}` : (lang === 'fr' ? 'tous' : 'all')}</h4>
              <input type="range" min="500" max="5000" step="100"
                value={filters.areaMin || 500}
                onChange={(e) => setFilters({ ...filters, areaMin: +e.target.value === 500 ? null : +e.target.value })}
                style={{ width: '100%' }}/>
              <div className="chip-multiselect" style={{ marginTop: 8 }}>
                {[null, 1000, 1500, 2000, 3000].map(v => (
                  <button key={v ?? 'all'} className={`chip-multiselect-option ${filters.areaMin === v ? 'active' : ''}`}
                    onClick={() => setFilters({ ...filters, areaMin: v })}>
                    {v === null ? (lang === 'fr' ? 'Tous' : 'All') : `${v}+`}
                  </button>
                ))}
              </div>
            </div>
          )}

          {/* Tri */}
          <div className="mobile-filter-section">
            <h4>{t.sort_label}</h4>
            <select className="sort-select" style={{ width: '100%' }} value={sort} onChange={(e) => setSort(e.target.value)}>
              <option value="score_desc">{t.sort_score_desc}</option>
              <option value="caprate_desc">{t.sort_caprate_desc}</option>
              <option value="price_asc">{t.sort_price_asc}</option>
              <option value="price_desc">{t.sort_price_desc}</option>
            </select>
          </div>
        </div>
        <div className="mobile-filter-drawer-footer">
          <button className="btn btn-ghost" onClick={onResetAll}>
            {lang === 'fr' ? 'Réinitialiser' : 'Reset'}
          </button>
          <button className="btn" onClick={onClose}>
            {lang === 'fr' ? `Voir ${count} résultats` : `Show ${count} results`}
          </button>
        </div>
      </div>
    </div>
  );
}

function Check() {
  return (
    <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
      <path d="M2.5 6.5L4.5 8.5L9.5 3.5" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}

// ---- Listing card ----
function ListingCard({ listing, t, lang, activeProfile, active, selected, onHover, onLeave, onClick, isFav, toggleFav }) {
  const cardRef = useRef(null);
  const profileData = listing.scoresByProfile?.[activeProfile];
  const score = profileData ? profileData.score : listing.score;
  const cf = listing.metrics.cashFlow;

  // Scroll into view quand sélectionné depuis la map
  useEffect(() => {
    if (selected && cardRef.current) {
      cardRef.current.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
    }
  }, [selected]);

  const hasViewed = window.listingHistory?.hasViewed(listing.id);

  return (
    <article ref={cardRef} className={`listing-card ${active ? 'active' : ''} ${selected ? 'selected' : ''}`}
      onMouseEnter={onHover} onMouseLeave={onLeave} onClick={onClick}>
      <div className="listing-image">
        <div className="img-bg" style={{ background: `linear-gradient(135deg, ${listing.images[0]} 0%, ${listing.images[1]} 60%, ${listing.images[2]} 100%)` }}/>
        <div className="img-stripes"/>
        <div className="img-label">{listing.type} · {listing.address.split(',')[0]}</div>
        <div className="score-badge">
          <span className="score-dot" style={{ background: window.scoreColor(score) }}>{score}</span>
          <span>{t[window.scoreVerdict(score)]}</span>
        </div>
        {hasViewed && (
          <div className="viewed-badge" title={lang === 'fr' ? 'Bien déjà consulté' : 'Listing already viewed'}>
            👁 {lang === 'fr' ? 'Consulté' : 'Viewed'}
          </div>
        )}
        <button className={`fav-btn ${isFav ? 'active' : ''}`} onClick={(e) => { e.stopPropagation(); toggleFav(); }}>
          <svg width="16" height="16" viewBox="0 0 16 16" fill={isFav ? "currentColor" : "none"} stroke="currentColor" strokeWidth="1.4">
            <path d="M8 14s-5-3.2-5-7.2C3 4.7 4.5 3 6.5 3 7.6 3 8 4 8 4s.4-1 1.5-1c2 0 3.5 1.7 3.5 3.8 0 4-5 7.2-5 7.2z"/>
          </svg>
        </button>
      </div>
      <div className="listing-body">
        <div className="listing-price">
          {window.fmt.money(listing.price, lang === "fr" ? "fr-CA" : "en-CA")}
          {listing.units > 1 && <span className="listing-price-suffix">{window.fmt.moneyShort(listing.metrics.pricePerUnit)} / {t.unit}</span>}
        </div>
        <div className="listing-meta">
          <span><strong>{listing.units}</strong> {listing.units > 1 ? t.units : t.unit}</span>
          {listing.bedrooms > 0 && <><span>·</span><span><strong>{listing.bedrooms}</strong> {t.bedrooms}</span></>}
          {listing.bathrooms > 0 && <><span>·</span><span><strong>{listing.bathrooms}</strong> {t.bathrooms}</span></>}
          {listing.livingArea > 0 && <><span>·</span><span><strong>{listing.livingArea}</strong> {t.sqft}</span></>}
        </div>
        <div className="listing-address">{listing.address}{listing.neighborhood ? `, ${listing.neighborhood}` : ''}, {listing.city}</div>
        <div className="listing-kpis">
          <div className="kpi-mini"><div className="kpi-mini-label">{t.cap_rate}</div><div className="kpi-mini-value">{window.fmt.pct(listing.metrics.capRate)}</div></div>
          <div className="kpi-mini"><div className="kpi-mini-label">DSCR</div><div className="kpi-mini-value">{listing.metrics.dscr != null ? listing.metrics.dscr.toFixed(2) : '—'}</div></div>
          <div className="kpi-mini">
            <div className="kpi-mini-label">{t.cash_flow}</div>
            <div className={`kpi-mini-value ${cf > 0 ? 'pos' : cf < 0 ? 'neg' : ''}`}>{cf > 0 ? '+' : ''}{window.fmt.moneyShort(cf)}</div>
          </div>
        </div>
      </div>
    </article>
  );
}

// ---- Map (Leaflet + OSM) ----
function ListingMap({
  listings, t, lang, activeProfile,
  hoveredId, activeId, selectedId,
  setHoveredId, setSelectedListingId, onPinClick,
  onBoundsChange, lockBounds, showSearchArea, onApplySearchArea
}) {
  const containerRef = useRef(null);
  const mapRef = useRef(null);
  const markersRef = useRef(new Map());
  const userPannedRef = useRef(false);
  const boundsTimerRef = useRef(null);
  const programmaticUntilRef = useRef(0);

  // Init Leaflet map once
  useEffect(() => {
    if (!containerRef.current || mapRef.current || !window.L) return;
    const map = window.L.map(containerRef.current, {
      center: [46.2, -72.5],
      zoom: 7,
      zoomControl: false,
      attributionControl: true
    });
    window.L.control.zoom({ position: 'topright' }).addTo(map);
    window.L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
      attribution: '© OpenStreetMap, © CARTO',
      subdomains: 'abcd',
      maxZoom: 19
    }).addTo(map);
    mapRef.current = map;

    // Phase d'init : ignorer tous les events pendant ~1.2s pour éviter les
    // moveend issus du fitBounds initial qui simuleraient un pan utilisateur.
    const initDoneRef = { current: false };
    const initTimer = setTimeout(() => { initDoneRef.current = true; }, 1200);

    // Détecter les pans/zooms utilisateur (vs programmatique)
    const onUserStart = () => {
      if (!initDoneRef.current) return;
      userPannedRef.current = true;
    };
    map.on('dragstart', onUserStart);
    map.on('zoomstart', onUserStart);

    const onMoveEnd = () => {
      if (!initDoneRef.current) return;
      if (Date.now() < programmaticUntilRef.current) return; // ignore les moveend pendant fitBounds programmatique
      if (!userPannedRef.current) return; // ignore les move sans drag/zoom utilisateur
      if (boundsTimerRef.current) clearTimeout(boundsTimerRef.current);
      boundsTimerRef.current = setTimeout(() => {
        const b = map.getBounds();
        const next = {
          north: b.getNorth(), south: b.getSouth(),
          east:  b.getEast(),  west:  b.getWest()
        };
        if (onBoundsChange) onBoundsChange(next);
      }, 200);
    };
    map.on('moveend', onMoveEnd);

    setTimeout(() => map.invalidateSize(), 50);

    return () => {
      clearTimeout(initTimer);
      map.off('dragstart', onUserStart);
      map.off('zoomstart', onUserStart);
      map.off('moveend', onMoveEnd);
      if (boundsTimerRef.current) clearTimeout(boundsTimerRef.current);
      map.remove();
      mapRef.current = null;
    };
  }, []);

  // Effet : (re)build markers quand listings ou profil changent
  useEffect(() => {
    const map = mapRef.current;
    if (!map || !window.L) return;

    // Clear old
    markersRef.current.forEach(m => map.removeLayer(m));
    markersRef.current = new Map();

    const geoListings = listings.filter(l => l.lat != null && l.lng != null);
    if (geoListings.length === 0) return;

    geoListings.forEach(l => {
      const pinScore = l.scoresByProfile?.[activeProfile]?.score ?? l.score;
      const color = window.scoreColor(pinScore);
      const isActive = activeId === l.id || hoveredId === l.id || selectedId === l.id;
      const html = `
        <div class="leaflet-pin ${isActive ? 'active' : ''}" style="--pin-color:${color}" data-listing-id="${l.id}">
          <span class="leaflet-pin-bubble leaflet-pin-bubble--score" data-price="${window.fmt.moneyShort(l.price)}">${pinScore}</span>
        </div>`;
      const icon = window.L.divIcon({
        html, className: 'leaflet-pin-wrap', iconSize: [44, 30], iconAnchor: [22, 30]
      });
      const marker = window.L.marker([l.lat, l.lng], { icon, riseOnHover: true });
      marker.on('mouseover', () => setHoveredId(l.id));
      marker.on('mouseout',  () => setHoveredId(prev => prev === l.id ? null : prev));
      marker.on('click', () => {
        if (setSelectedListingId) setSelectedListingId(l.id);
        if (onPinClick) onPinClick(l);
      });
      marker.addTo(map);
      markersRef.current.set(l.id, marker);
    });

    // Fit bounds une seule fois en initial OU quand bounds non verrouillés
    if (!lockBounds) {
      const bounds = window.L.latLngBounds(geoListings.map(l => [l.lat, l.lng]));
      if (bounds.isValid()) {
        userPannedRef.current = false;
        programmaticUntilRef.current = Date.now() + 800; // ignore moveend pendant l'animation fitBounds
        map.fitBounds(bounds, { padding: [50, 50], maxZoom: 12, animate: true });
      }
    }
  }, [listings, activeProfile, lockBounds]);

  // Effet séparé : highlight (mutation classList sans recréer markers)
  useEffect(() => {
    markersRef.current.forEach((marker, id) => {
      const el = marker.getElement();
      if (!el) return;
      const pin = el.querySelector('.leaflet-pin');
      if (!pin) return;
      const isActive = activeId === id || hoveredId === id || selectedId === id;
      pin.classList.toggle('active', isActive);
    });
  }, [hoveredId, activeId, selectedId]);

  const [cityView, setCityView] = useState('all');
  const flyTo = (city) => {
    const map = mapRef.current;
    if (!map) return;
    setCityView(city);
    // FlyTo intentionnel : on attend que l'animation finisse, puis on simule un pan
    // utilisateur pour que le bouton "Rechercher dans cette zone" apparaisse.
    programmaticUntilRef.current = Date.now() + 1000;
    if (city === 'qc') map.flyTo([46.81, -71.22], 12, { duration: 0.8 });
    else if (city === 'mtl') map.flyTo([45.53, -73.59], 12, { duration: 0.8 });
    else map.flyTo([46.2, -72.5], 7, { duration: 0.8 });
    setTimeout(() => { userPannedRef.current = true; }, 1100);
  };

  return (
    <div className="map-pane">
      <div ref={containerRef} className="leaflet-container-wrap"/>
      {showSearchArea && (
        <button className="search-this-area-btn" onClick={onApplySearchArea}>
          <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round">
            <circle cx="5" cy="5" r="3.2"/>
            <path d="M7.5 7.5L10 10"/>
          </svg>
          {lang === 'fr' ? 'Rechercher dans cette zone' : 'Search this area'}
        </button>
      )}
      <div className="map-legend">
        <h4>{t.map_legend}</h4>
        <div className="legend-row"><span className="legend-swatch" style={{ background: 'var(--score-excellent)' }}/> 80+ {t.excellent}</div>
        <div className="legend-row"><span className="legend-swatch" style={{ background: 'var(--score-good)' }}/> 65–79 {t.good}</div>
        <div className="legend-row"><span className="legend-swatch" style={{ background: 'var(--score-fair)' }}/> 50–64 {t.fair}</div>
        <div className="legend-row"><span className="legend-swatch" style={{ background: 'var(--score-avoid)' }}/> &lt;50 {t.avoid}</div>
      </div>
      <div className="map-toggle">
        <button className={cityView === 'all' ? 'active' : ''} onClick={() => flyTo('all')}>{lang === 'fr' ? 'Province' : 'Province'}</button>
        <button className={cityView === 'mtl' ? 'active' : ''} onClick={() => flyTo('mtl')}>Montréal</button>
        <button className={cityView === 'qc' ? 'active' : ''} onClick={() => flyTo('qc')}>Québec</button>
      </div>
    </div>
  );
}

// ---- Skeleton loading cards ----
// Affiche 6 cartes grises animées avec pulse pendant le chargement de LISTINGS
function SkeletonCards({ count = 6 }) {
  return (
    <>
      {Array.from({ length: count }).map((_, i) => (
        <div key={i} className="skeleton-card">
          <div className="skeleton-image"/>
          <div className="skeleton-body">
            <div className="skeleton-price"/>
            <div className="skeleton-meta"/>
            <div className="skeleton-meta" style={{ width: "85%" }}/>
            <div className="skeleton-address"/>
            <div className="skeleton-kpis">
              <div className="skeleton-kpi"/>
              <div className="skeleton-kpi"/>
              <div className="skeleton-kpi"/>
            </div>
          </div>
        </div>
      ))}
    </>
  );
}

// ---- Empty state amélioré ----
// Affiche une loupe barrée, titre, sous-titre, et 3 boutons CTA
function EmptyState({ lang, t, filters, setFilters, onResetFilters, onChangeProfile }) {
  const handleLowerScore = () => {
    setFilters({ ...filters, scoreMin: Math.max(0, filters.scoreMin - 10) });
  };

  return (
    <div className="empty-state">
      {/* Illustration SVG : loupe barrée */}
      <svg className="empty-state-icon" viewBox="0 0 80 80" fill="none">
        <circle cx="32" cy="32" r="22" stroke="currentColor" strokeWidth="2.5"/>
        <path d="M48 48L72 72" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"/>
        <line x1="15" y1="65" x2="65" y2="15" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"/>
      </svg>

      <h2 className="empty-state-title">{t.no_results || (lang === 'fr' ? 'Aucun bien ne correspond' : 'No properties found')}</h2>
      <p className="empty-state-subtitle">
        {lang === 'fr' ? 'Essaie d\'élargir tes critères :' : 'Try adjusting your criteria:'}
      </p>

      <div className="empty-state-actions">
        <button
          className="btn btn-empty-state"
          onClick={handleLowerScore}
          title={lang === 'fr' ? 'Diminue le score minimum de 10 points' : 'Lower minimum score by 10 points'}
        >
          <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round">
            <circle cx="7" cy="7" r="5.5"/>
            <path d="M4 7h6"/>
            <path d="M7 4v6"/>
          </svg>
          {lang === 'fr' ? 'Élargir le score min' : 'Lower score minimum'}
        </button>

        <button
          className="btn btn-empty-state"
          onClick={onResetFilters}
          title={lang === 'fr' ? 'Réinitialise tous les filtres à la valeur par défaut du profil' : 'Reset all filters to profile defaults'}
        >
          <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round">
            <path d="M2 7a5 5 0 1 0 10 0A5 5 0 0 0 2 7"/>
            <path d="M7 4v3l2.5 1.5"/>
          </svg>
          {lang === 'fr' ? 'Effacer les filtres' : 'Clear all filters'}
        </button>

        <button
          className="btn btn-empty-state"
          onClick={onChangeProfile}
          title={lang === 'fr' ? 'Ouvre le sélecteur de profil investisseur' : 'Open investor profile selector'}
        >
          <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round">
            <circle cx="7" cy="4.5" r="2.5"/>
            <path d="M2.5 11.5c0-1.4 2-2.5 4.5-2.5s4.5 1.1 4.5 2.5"/>
          </svg>
          {lang === 'fr' ? 'Changer de profil' : 'Change profile'}
        </button>
      </div>
    </div>
  );
}

// ---- HypothesesStrip ----
// Bandeau compact entre Header et FilterBar exposant 3 hypothèses financières :
// mise de fonds, taux hypothécaire, amortissement.
// Chaque changement met à jour userProfile (React state) + window.userProfile.
// Sprint 2 PR5 : sliders gated — disabled + cadenas si tier < investisseur.
function HypothesesStrip({ userProfile, setUserProfile }) {
  const profile = userProfile || window.userProfile || {};

  // Valeurs avec fallback
  const downPayment    = profile.downPayment    ?? 20;   // % (10-35)
  const mortgageRate   = profile.mortgageRate   ?? 5.5;  // % (3.5-9)
  const amortization   = profile.amortization   ?? 25;   // ans (15|20|25)

  // Collapsed state persisté en localStorage
  const [collapsed, setCollapsed] = useState(() => {
    try { return localStorage.getItem('vestora_hyp_collapsed') === 'true'; } catch { return false; }
  });

  // Gating tier — sliders réservés au plan Investisseur+
  const [userTier, setUserTier] = useState('decouverte');
  const [paywallOpen, setPaywallOpen] = useState(false);

  useEffect(() => {
    const tg = window.vestora?.tierGate;
    if (tg && typeof tg.getCurrentTier === 'function') {
      tg.getCurrentTier().then(t => setUserTier(t));
    }
  }, []);

  const slidersLocked = window.vestora?.tierGate
    ? !window.vestora?.tierGate.canAccess('hypotheses_sliders', userTier)
    : false;

  const toggleCollapsed = () => {
    setCollapsed(prev => {
      const next = !prev;
      try { localStorage.setItem('vestora_hyp_collapsed', String(next)); } catch {}
      return next;
    });
  };

  const update = (field, value) => {
    if (slidersLocked) { setPaywallOpen(true); return; }
    const next = { ...profile, [field]: value };
    if (setUserProfile) setUserProfile(next);
    window.userProfile = next;
    try { localStorage.setItem('vestora_user_profile', JSON.stringify(next)); } catch {}
  };

  const handleLockedClick = (e) => {
    e.preventDefault();
    setPaywallOpen(true);
  };

  // Icône cadenas inline
  const LockInline = () => (
    React.createElement('svg', {
      width: 11, height: 11, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor',
      strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',
      style: { display: 'inline-block', verticalAlign: 'middle', marginLeft: 4, opacity: 0.6 },
      'aria-hidden': 'true'
    },
      React.createElement('rect', { x: 3, y: 11, width: 18, height: 11, rx: 2 }),
      React.createElement('path', { d: 'M7 11V7a5 5 0 0110 0v4' })
    )
  );

  // Détecte si les valeurs ont été modifiées par rapport aux défauts
  const isModified = downPayment !== 20 || mortgageRate !== 5.5 || amortization !== 25;

  if (collapsed) {
    return (
      <div className="hypotheses-strip collapsed">
        <span className="hypotheses-strip-label">Hypotheses</span>
        <button
          className="hyp-summary-chip"
          onClick={toggleCollapsed}
          title="Modifier les hypotheses"
        >
          {'↓'} {downPayment}%{'·'}{mortgageRate}%{'·'}{amortization}{' '}ans
          {isModified && <span style={{ marginLeft: 4, fontSize: 10, opacity: 0.75 }}>(mod.)</span>}
        </button>
        <button className="hyp-collapse-btn" onClick={toggleCollapsed} style={{ marginLeft: 'auto' }}>
          {'▾'} afficher
        </button>
      </div>
    );
  }

  return (
    <div className="hypotheses-strip">
      <span className="hypotheses-strip-label">Hypotheses</span>

      <div className="hyp-sep"/>

      {/* Mise de fonds */}
      <div className="hypotheses-strip-control" style={slidersLocked ? { opacity: 0.65 } : {}}>
        <span className="hypotheses-strip-control-label">
          Mise de fonds{slidersLocked && <LockInline />}
        </span>
        <span className="hypotheses-strip-value">{downPayment}%</span>
        <input
          type="range"
          className="hyp-slider"
          min="10" max="35" step="5"
          value={downPayment}
          onChange={(e) => update('downPayment', +e.target.value)}
          onClick={slidersLocked ? handleLockedClick : undefined}
          disabled={slidersLocked}
          title={slidersLocked ? 'Plan Investisseur requis' : `Mise de fonds : ${downPayment}%`}
          style={slidersLocked ? { cursor: 'not-allowed', pointerEvents: 'auto' } : {}}
        />
      </div>

      <div className="hyp-sep"/>

      {/* Taux hypothecaire */}
      <div className="hypotheses-strip-control" style={slidersLocked ? { opacity: 0.65 } : {}}>
        <span className="hypotheses-strip-control-label">
          Taux{slidersLocked && <LockInline />}
        </span>
        <span className="hypotheses-strip-value">{mortgageRate.toFixed(2)}%</span>
        <input
          type="range"
          className="hyp-slider"
          min="3.5" max="9" step="0.25"
          value={mortgageRate}
          onChange={(e) => update('mortgageRate', +e.target.value)}
          onClick={slidersLocked ? handleLockedClick : undefined}
          disabled={slidersLocked}
          title={slidersLocked ? 'Plan Investisseur requis' : `Taux hypothecaire : ${mortgageRate}%`}
          style={slidersLocked ? { cursor: 'not-allowed', pointerEvents: 'auto' } : {}}
        />
      </div>

      <div className="hyp-sep"/>

      {/* Amortissement */}
      <div className="hypotheses-strip-control" style={slidersLocked ? { opacity: 0.65 } : {}}>
        <span className="hypotheses-strip-control-label">
          Amort.{slidersLocked && <LockInline />}
        </span>
        <div className="hyp-chips">
          {[15, 20, 25].map(yr => (
            <button
              key={yr}
              className={`hyp-chip${amortization === yr ? ' active' : ''}`}
              onClick={slidersLocked ? handleLockedClick : () => update('amortization', yr)}
              title={slidersLocked ? 'Plan Investisseur requis' : `Amortissement ${yr} ans`}
              disabled={slidersLocked}
              style={slidersLocked ? { cursor: 'not-allowed' } : {}}
            >
              {yr}{' '}ans
            </button>
          ))}
        </div>
      </div>

      {/* Bouton collapse */}
      <button className="hyp-collapse-btn" onClick={toggleCollapsed} title="Reduire le bandeau">
        {'▴'} reduire
      </button>

      {/* PaywallModal — sliders (Sprint 2 PR5) */}
      {window.PaywallModal && (
        <window.PaywallModal
          open={paywallOpen}
          onClose={() => setPaywallOpen(false)}
          feature="hypotheses_sliders"
          featureLabel="Sliders Hypothèses (mise de fonds, taux, amortissement)"
          requiredTier="investisseur"
          currentTier={userTier}
          reason="tier_too_low"
        />
      )}
    </div>
  );
}

Object.assign(window, { Header, ProfileSelector, FilterBar, ActiveFilterChips, MobileFilterDrawer, ListingCard, ListingMap, SkeletonCards, EmptyState, HypothesesStrip });
