// Drawer : fiche détaillée complète — score, marché, analyse experte, stress test, calcul, historique
// Sprint 2 PR4 : gating KPIs premium + Market Position + Stress test + quota 5/mois

const { useState: useStateD, useMemo: useMemoD, useEffect: useEffectD, useRef: useRefD } = React;

// ── Import dynamique tier-gate (ESM → window cache) ─────────────────────────
async function _getTierGate() {
  if (window.__tierGate) return window.__tierGate;
  try {
    const mod = await import('/src/lib/tier-gate.js');
    window.__tierGate = mod;
    return mod;
  } catch {
    // Fallback no-op si import échoue (build sans ESM)
    const noop = {
      getCurrentTier: async () => 'decouverte',
      checkAccess: () => ({ allowed: true, reason: 'ok' }),
      incrementQuota: () => ({ ok: true, remaining: Infinity }),
      getQuotaUsage: () => ({ used: 0, limit: 5, remaining: 5, resetsOn: '' }),
      FEATURES: {
        DETAILED_ANALYSIS:      'detailed_analysis',
        DRAWER_KPI_ADVANCED:    'drawer_kpi_advanced',
        DRAWER_MARKET_POSITION: 'drawer_market_position',
        DRAWER_STRESS_TEST:     'drawer_stress_test',
      },
    };
    window.__tierGate = noop;
    return noop;
  }
}

// ── Icône cadenas SVG inline ─────────────────────────────────────────────────
function LockSvg({ size = 14 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none"
      stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
      style={{ display: 'inline-block', verticalAlign: 'middle' }}
      aria-hidden="true">
      <rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
      <path d="M7 11V7a5 5 0 0 1 10 0v4"/>
    </svg>
  );
}

// ── Wrapper section premium (blur + overlay) ─────────────────────────────────
function PremiumSection({ isPremiumLocked, onUnlock, children }) {
  if (!isPremiumLocked) return children;
  return (
    <div style={{ position: 'relative' }}>
      {/* Contenu flouté */}
      <div style={{ filter: 'blur(4px)', pointerEvents: 'none', userSelect: 'none' }}>
        {children}
      </div>
      {/* Overlay cadenas */}
      <div style={{
        position: 'absolute', inset: 0,
        display: 'flex', flexDirection: 'column',
        alignItems: 'center', justifyContent: 'center', gap: 10,
        background: 'rgba(255,255,255,0.55)',
        backdropFilter: 'blur(1px)',
        borderRadius: 'var(--radius, 8px)',
        zIndex: 2,
      }}>
        <div style={{
          background: 'var(--accent-soft, oklch(0.95 0.04 155))',
          color: 'var(--accent-ink, oklch(0.36 0.07 155))',
          borderRadius: 10,
          width: 40, height: 40,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>
          <LockSvg size={20}/>
        </div>
        <button
          onClick={onUnlock}
          style={{
            background: 'var(--accent, oklch(0.36 0.07 155))',
            color: '#fff', border: 'none', borderRadius: 'var(--radius, 8px)',
            padding: '8px 16px', fontSize: 13, fontWeight: 600,
            cursor: 'pointer', fontFamily: 'inherit',
          }}
        >
          Débloquer (Investisseur)
        </button>
      </div>
    </div>
  );
}

// ── KPI Card avec blur + cadenas cliquable ───────────────────────────────────
function LockedKpiCard({ label, isPremiumLocked, onUnlock, children }) {
  if (!isPremiumLocked) return children;
  return (
    <div className="kpi-card" style={{ position: 'relative', overflow: 'hidden' }}>
      {/* Faux contenu flouté */}
      <div style={{ filter: 'blur(4px)', pointerEvents: 'none', userSelect: 'none' }}>
        <div className="kpi-label">{label}</div>
        <div className="kpi-value">••••</div>
      </div>
      {/* Overlay cadenas plein */}
      <button
        onClick={onUnlock}
        title="Fonctionnalité Investisseur — cliquer pour débloquer"
        style={{
          position: 'absolute', inset: 0,
          background: 'rgba(255,255,255,0.6)',
          border: 'none', cursor: 'pointer',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          flexDirection: 'column', gap: 4,
          color: 'var(--accent-ink, oklch(0.36 0.07 155))',
          fontSize: 11, fontWeight: 600, fontFamily: 'inherit',
        }}
      >
        <LockSvg size={16}/>
        <span>Investisseur</span>
      </button>
    </div>
  );
}

// ── Signal chips (tags + temporel) ──────────────────────────────────────────
function SignalChips({ listing, lang }) {
  const chips = [];

  // Tags générés par le backend
  if (listing.tags && listing.tags.length > 0) {
    listing.tags.forEach(tag => chips.push({ text: tag, cls: 'chip-tag' }));
  }

  // Ancienneté
  const dom = listing.daysOnMarket;
  if (dom != null && dom >= 30) {
    chips.push({
      text: lang === 'fr' ? `${dom} j` : `${dom}d`,
      title: lang === 'fr' ? `${dom} jours sur le marché` : `${dom} days on market`,
      cls: dom >= 90 ? 'chip-warn' : 'chip-info'
    });
  }

  // Réductions de prix
  if (listing.priceCutCount > 0) {
    chips.push({
      text: lang === 'fr' ? `${listing.priceCutCount}× réduit` : `${listing.priceCutCount}× reduced`,
      cls: 'chip-warn'
    });
  }

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

  return (
    <div className="signal-chips">
      {chips.map((c, i) => (
        <span key={i} className={`signal-chip ${c.cls}`} title={c.title || ''}>
          {c.text}
        </span>
      ))}
    </div>
  );
}

// ── Barre source ─────────────────────────────────────────────────────────────
function SourceLinkBar({ listing, lang }) {
  if (!listing.url) return null;
  const sourceLabel = {
    centris: 'Centris', remax: 'Re/Max', royallepage: 'Royal LePage',
    sothebys: "Sotheby's", century21: 'Century 21'
  }[listing.source] || listing.source;
  return (
    <div className="source-link-bar">
      <span className="source-name">{sourceLabel}</span>
      {listing.sourceId && <span className="source-id">#{listing.sourceId}</span>}
      <a href={listing.url} target="_blank" rel="noopener noreferrer" className="source-link-btn">
        {lang === 'fr' ? "Voir l'annonce originale" : 'View original listing'}
        <svg width="11" height="11" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.5">
          <path d="M7 1h4v4M11 1L5 7M4 3H2a1 1 0 00-1 1v6a1 1 0 001 1h6a1 1 0 001-1V8"/>
        </svg>
      </a>
    </div>
  );
}

// ── Position de marché ───────────────────────────────────────────────────────
// ── Badge taux d'inoccupation SCHL ──────────────────────────────────────────
function VacancyBadge({ listing, lang }) {
  const rate = listing.vacancyRate;
  const rmr  = listing.vacancyRmr;
  const year = listing.vacancyYear;
  const tone = listing.vacancyTone;

  if (rate == null) return null;

  const pct = (rate * 100).toFixed(1) + ' %';

  const toneStyles = {
    tight:    { bg: 'oklch(0.95 0.08 145)', color: 'oklch(0.32 0.10 145)', label: lang === 'fr' ? 'Marché serré' : 'Tight market' },
    balanced: { bg: 'oklch(0.97 0.07 75)',  color: 'oklch(0.45 0.10 75)',  label: lang === 'fr' ? 'Marché équilibré' : 'Balanced market' },
    soft:     { bg: 'oklch(0.97 0.06 25)',  color: 'oklch(0.45 0.10 25)',  label: lang === 'fr' ? 'Surplus de l\'offre' : 'Soft market' },
    unknown:  { bg: 'var(--bg-warm,#f9f9f6)', color: 'var(--ink-3,#888)', label: lang === 'fr' ? 'Inconnu' : 'Unknown' },
  };
  const style = toneStyles[tone] || toneStyles.unknown;
  const rmrShort = rmr ? rmr.replace(' (RMR)', '').replace(' (AR)', '').replace(' (partie QC)', '') : '';

  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 10,
      padding: '10px 14px',
      background: style.bg,
      borderRadius: 'var(--radius, 8px)',
      marginTop: 10,
    }}>
      <div style={{ flex: 1 }}>
        <div style={{ fontSize: 11, color: 'var(--ink-3,#888)', marginBottom: 2 }}>
          {lang === 'fr' ? `Taux d'inoccupation${rmrShort ? ` · ${rmrShort}` : ''}` : `Vacancy rate${rmrShort ? ` · ${rmrShort}` : ''}`}
          {year && <span style={{ marginLeft: 4 }}>({year})</span>}
        </div>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
          <span style={{ fontSize: 20, fontWeight: 700, color: style.color, fontFamily: 'var(--font-mono, monospace)' }}>
            {pct}
          </span>
          <span style={{ fontSize: 11, fontWeight: 600, color: style.color, textTransform: 'uppercase', letterSpacing: '0.04em' }}>
            {style.label}
          </span>
        </div>
      </div>
      <div style={{
        width: 8, height: 8, borderRadius: '50%',
        background: style.color, flexShrink: 0,
      }}/>
    </div>
  );
}

function MarketPositionCard({ listing, t, lang }) {
  const mc = listing.marketContext;
  if (!mc) return null;
  const { vsMarket, capRatePercentile, cityCapRateMedian, groupCount } = mc;
  const hasVacancy = listing.vacancyRate != null;
  if (capRatePercentile == null && vsMarket === 'inconnu' && !hasVacancy) return null;

  const vmMap = {
    'sous le marché':   { label: t.below_market,   cls: 'vm-below',   icon: '↓' },
    'dans la moyenne':  { label: t.at_market,       cls: 'vm-at',      icon: '→' },
    'prime de marché':  { label: t.premium_market,  cls: 'vm-premium', icon: '↑' },
    'inconnu':          { label: t.unknown_market,  cls: 'vm-unknown', icon: '?' }
  };
  const vm = vmMap[vsMarket] || vmMap['inconnu'];

  const pctColor = capRatePercentile >= 75 ? 'var(--score-excellent)' :
                   capRatePercentile >= 50 ? 'var(--score-good)' :
                   capRatePercentile >= 25 ? 'var(--score-fair)' : 'var(--score-avoid)';

  return (
    <section id="section-market">
      <h3 className="section-title">
        {t.market_position}
        {groupCount > 0 && <small>{groupCount} {t.comparables}</small>}
      </h3>
      <div className="market-position-card">
        <div className={`vs-market-badge ${vm.cls}`}>
          <span className="vm-icon">{vm.icon}</span>
          <span className="vm-label">{vm.label}</span>
        </div>

        {capRatePercentile != null && (
          <div className="percentile-section">
            <div className="percentile-header">
              <span>{t.cap_rate_rank}</span>
              <span className="percentile-value" style={{ color: pctColor }}>
                {t.top_pct(capRatePercentile)}
                {' · '}
                <span style={{ fontWeight: 400, fontSize: 11, color: 'var(--ink-3)' }}>
                  {t.percentile_label(capRatePercentile)}
                </span>
              </span>
            </div>
            <div className="percentile-bar">
              <div className="percentile-fill" style={{ width: `${capRatePercentile}%`, background: pctColor }}/>
            </div>
          </div>
        )}

        {cityCapRateMedian != null && (
          <div className="cap-rate-compare">
            <div className="crc-item">
              <div className="crc-label">{t.this_listing}</div>
              <div className="crc-value" style={{
                color: listing.metrics.capRate >= cityCapRateMedian ? 'var(--score-excellent)' : 'var(--score-avoid)'
              }}>
                {window.fmt.pct(listing.metrics.capRate)}
              </div>
            </div>
            <div className="crc-divider">vs</div>
            <div className="crc-item">
              <div className="crc-label">{t.city_median(listing.city)}</div>
              <div className="crc-value" style={{ color: 'var(--ink-2)' }}>
                {window.fmt.pct(cityCapRateMedian)}
              </div>
            </div>
          </div>
        )}

        {/* Badge taux d'inoccupation SCHL */}
        <VacancyBadge listing={listing} lang={lang} />
      </div>
    </section>
  );
}

// ── Analyse experte ──────────────────────────────────────────────────────────
function DeepAnalysisSection({ listing, t, lang }) {
  const da = listing.deepAnalysis;
  const [expanded, setExpanded] = useStateD({});
  const LIMIT = 3;

  const hasContent = da.strengths.length > 0 || da.weaknesses.length > 0 ||
                     da.risks.length > 0 || da.investorProfile || da.finalNote;
  if (!hasContent) return null;

  const toggle = (key) => setExpanded(prev => ({ ...prev, [key]: !prev[key] }));

  function BulletList({ items, cls, icon, sectionKey }) {
    if (!items || items.length === 0) return null;
    const isExp = expanded[sectionKey];
    const visible = isExp ? items : items.slice(0, LIMIT);
    const extra = items.length - LIMIT;
    return (
      <div className={`analysis-bullets ${cls}`}>
        {visible.map((item, i) => (
          <div key={i} className="analysis-item">
            <span className="analysis-icon">{icon}</span>
            <span>{item}</span>
          </div>
        ))}
        {extra > 0 && (
          <button className="analysis-expand-btn" onClick={() => toggle(sectionKey)}>
            {isExp ? `▲ ${t.see_less}` : `▼ ${t.see_more(extra)}`}
          </button>
        )}
      </div>
    );
  }

  return (
    <section id="section-analysis">
      <h3 className="section-title">{t.expert_analysis}</h3>

      {listing.summary && listing.summary[lang] && (
        <p className="summary-text" style={{ marginBottom: 16 }}>{listing.summary[lang]}</p>
      )}

      <div className="deep-analysis-grid">
        {da.strengths.length > 0 && (
          <div className="analysis-col">
            <div className="analysis-col-header strengths">
              <span className="col-icon">✓</span>
              {t.strengths} ({da.strengths.length})
            </div>
            <BulletList items={da.strengths} cls="strengths" icon="✓" sectionKey="str"/>
          </div>
        )}
        {da.weaknesses.length > 0 && (
          <div className="analysis-col">
            <div className="analysis-col-header weaknesses">
              <span className="col-icon">~</span>
              {t.weaknesses} ({da.weaknesses.length})
            </div>
            <BulletList items={da.weaknesses} cls="weaknesses" icon="~" sectionKey="wk"/>
          </div>
        )}
        {da.risks.length > 0 && (
          <div className="analysis-col">
            <div className="analysis-col-header risks">
              <span className="col-icon">✕</span>
              {t.risks_detail} ({da.risks.length})
            </div>
            <BulletList items={da.risks} cls="risks" icon="✕" sectionKey="rk"/>
          </div>
        )}
      </div>

      {(da.investorProfile || da.finalNote) && (
        <div className="analysis-footer">
          {da.investorProfile && (
            <div className="investor-profile-row">
              <span className="investor-label">{t.investor_profile}</span>
              <span className="investor-tag">{da.investorProfile}</span>
            </div>
          )}
          {da.finalNote && (
            <div className="final-note-box">
              <span className="final-note-icon">→</span>
              {da.finalNote}
            </div>
          )}
        </div>
      )}
    </section>
  );
}

// ── Stress test des taux ─────────────────────────────────────────────────────
function RateStressTable({ rateSensitivity, t, lang }) {
  if (!rateSensitivity) return null;

  const scenarios = [
    { key: '0.05', label: lang === 'fr' ? '5,0 %' : '5.0%', isBase: false },
    { key: '0.06', label: lang === 'fr' ? '6,0 %' : '6.0%', isBase: true },
    { key: '0.07', label: lang === 'fr' ? '7,0 %' : '7.0%', isBase: false }
  ];

  const dscrStyle = (dscr) => {
    if (dscr == null) return {};
    if (dscr >= 1.20) return { color: 'var(--score-excellent)' };
    if (dscr >= 1.00) return { color: 'var(--severity-warning)' };
    return { color: 'var(--score-avoid)' };
  };
  const dscrIcon = (dscr) => {
    if (dscr == null) return '';
    if (dscr >= 1.20) return '✓';
    if (dscr >= 1.00) return '⚠';
    return '✕';
  };

  return (
    <section id="section-stress">
      <h3 className="section-title">
        {t.rate_stress_test}
        <small>{t.stress_subtitle}</small>
      </h3>
      <div className="stress-table">
        <div className="stress-header">
          <div>{t.stress_rate}</div>
          <div>DSCR</div>
          <div>{t.stress_cashflow}</div>
          <div>{t.stress_coc}</div>
        </div>
        {scenarios.map(({ key, label, isBase }) => {
          const d = rateSensitivity[key];
          if (!d) return null;
          const cf = d.annualCashflow;
          const coc = d.cashOnCash;
          return (
            <div key={key} className={`stress-row ${isBase ? 'base' : ''}`}>
              <div className="stress-rate-label">
                {label}
                {isBase && <span className="stress-base-tag">{t.stress_base}</span>}
              </div>
              <div className="stress-dscr" style={dscrStyle(d.dscr)}>
                {d.dscr != null ? d.dscr.toFixed(2) : '—'}
                <span className="stress-icon">{dscrIcon(d.dscr)}</span>
              </div>
              <div className={`stress-val ${cf > 0 ? 'pos' : cf < 0 ? 'neg' : ''}`}>
                {cf != null ? (cf > 0 ? '+' : '') + window.fmt.moneyShort(cf) : '—'}
              </div>
              <div className={`stress-val ${coc > 0.04 ? 'pos' : coc < 0 ? 'neg' : ''}`}>
                {coc != null ? window.fmt.pct(coc) : '—'}
              </div>
            </div>
          );
        })}
      </div>
      <p className="stress-note">{t.stress_note}</p>
    </section>
  );
}

// ── Walk Score — Localisation ────────────────────────────────────────────────
function WalkScoreSection({ listing, lang }) {
  const walk    = listing.walkScore;
  const transit = listing.transitScore;
  const bike    = listing.bikeScore;

  // Si aucun score n'est disponible, ne pas afficher la section
  if (walk == null && transit == null && bike == null) return null;

  const scoreColor = (score) => {
    if (score == null) return 'var(--ink-4, #bbb)';
    if (score >= 70)   return 'var(--score-excellent, #16a34a)';
    if (score >= 50)   return 'var(--severity-warning, #b45309)';
    return 'var(--score-avoid, #dc2626)';
  };

  const scoreBg = (score) => {
    if (score == null) return 'var(--bg-warm, #f5f5f0)';
    if (score >= 70)   return 'oklch(0.96 0.05 150)';
    if (score >= 50)   return 'oklch(0.97 0.06 70)';
    return 'oklch(0.97 0.05 25)';
  };

  const scoreLabel = (score) => {
    if (score == null) return lang === 'fr' ? 'N/D' : 'N/A';
    return String(score);
  };

  const items = [
    {
      key: 'walk',
      label: lang === 'fr' ? 'Marchabilité' : 'Walkability',
      score: walk,
      icon: '🚶',
    },
    {
      key: 'transit',
      label: lang === 'fr' ? 'Transport' : 'Transit',
      score: transit,
      icon: '🚌',
    },
    {
      key: 'bike',
      label: lang === 'fr' ? 'Vélo' : 'Biking',
      score: bike,
      icon: '🚲',
    },
  ];

  return (
    <section id="section-walkscore">
      <h3 className="section-title">
        {lang === 'fr' ? 'Localisation' : 'Location scores'}
        <small>Walk Score®</small>
      </h3>
      <div style={{
        display: 'grid',
        gridTemplateColumns: 'repeat(3, 1fr)',
        gap: 10,
        marginBottom: 8,
      }}>
        {items.map(({ key, label, score, icon }) => (
          <div key={key} style={{
            background: scoreBg(score),
            borderRadius: 'var(--radius, 8px)',
            padding: '12px 8px',
            textAlign: 'center',
            border: '1px solid var(--line, #e8e8e0)',
          }}>
            <div style={{ fontSize: 20, marginBottom: 4 }}>{icon}</div>
            <div style={{
              fontSize: 26,
              fontWeight: 700,
              color: scoreColor(score),
              lineHeight: 1,
              marginBottom: 2,
            }}>
              {scoreLabel(score)}
            </div>
            <div style={{
              fontSize: 11,
              color: 'var(--ink-3, #888)',
              textTransform: 'uppercase',
              letterSpacing: '0.04em',
              fontWeight: 500,
            }}>
              {label}
            </div>
          </div>
        ))}
      </div>
      {listing.wsLink && (
        <p style={{ fontSize: 11, color: 'var(--ink-4, #bbb)', marginTop: 4 }}>
          <a
            href={listing.wsLink}
            target="_blank"
            rel="noopener noreferrer"
            style={{ color: 'inherit' }}
          >
            Walk Score® données
          </a>
          {' '}— 0-49 {lang === 'fr' ? 'faible' : 'low'} · 50-69 {lang === 'fr' ? 'moyen' : 'moderate'} · 70+ {lang === 'fr' ? 'élevé' : 'high'}
        </p>
      )}
    </section>
  );
}

// ── Évaluation foncière MAMH ─────────────────────────────────────────────────
function MunicipalAssessmentSection({ listing, lang }) {
  const ass = listing.assessment;
  if (!ass || !ass.assessedValue) return null;

  const fmt = window.fmt;
  const ratio = ass.priceToAssessmentRatio;

  // Coloration du ratio prix/évaluation
  //   < 1.0  → vert  (prix < évaluation : potentiellement sous-évalué)
  //   1.0–1.3 → orange (zone normale)
  //   > 1.3  → rouge  (prix nettement supérieur à l'évaluation)
  const ratioColor =
    ratio == null ? 'var(--ink-2)' :
    ratio < 1.0   ? 'var(--score-excellent)' :
    ratio <= 1.3  ? 'var(--severity-warning)' :
                    'var(--score-avoid)';

  const ratioLabel =
    ratio == null ? '—' :
    ratio < 1.0   ? (lang === 'fr' ? 'Sous évaluation' : 'Below assessment') :
    ratio <= 1.3  ? (lang === 'fr' ? 'Dans la norme'   : 'Within range') :
                    (lang === 'fr' ? 'Au-dessus'        : 'Above');

  const title   = lang === 'fr' ? 'Évaluation municipale' : 'Municipal Assessment';
  const srcNote = lang === 'fr'
    ? `Source : MAMH — Données Québec${ass.matchQuality === 'partial' ? ' (correspondance approximative)' : ''}`
    : `Source: MAMH — Données Québec${ass.matchQuality === 'partial' ? ' (approximate match)' : ''}`;

  return (
    <section id="section-assessment">
      <h3 className="section-title">{title}</h3>
      <div className="assessment-card" style={{
        background: 'var(--surface, #f8f9f6)',
        border: '1px solid var(--border, #e0e4dd)',
        borderRadius: 'var(--radius, 8px)',
        padding: '16px',
        display: 'flex',
        flexDirection: 'column',
        gap: 12,
      }}>

        {/* Valeur totale + ratio */}
        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', flexWrap: 'wrap', gap: 8 }}>
          <div>
            <div style={{ fontSize: 11, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 4 }}>
              {lang === 'fr' ? 'Valeur totale évaluée' : 'Total Assessed Value'}
            </div>
            <div style={{ fontSize: 22, fontWeight: 700, color: 'var(--ink-1)', fontFamily: 'JetBrains Mono, monospace' }}>
              {fmt.money(ass.assessedValue)}
            </div>
          </div>
          {ratio != null && (
            <div style={{ textAlign: 'right' }}>
              <div style={{ fontSize: 11, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 4 }}>
                {lang === 'fr' ? 'Ratio prix / éval.' : 'Price / Assessment'}
              </div>
              <div style={{ fontSize: 20, fontWeight: 700, color: ratioColor, fontFamily: 'JetBrains Mono, monospace' }}>
                {ratio.toFixed(2)}×
              </div>
              <div style={{ fontSize: 11, color: ratioColor, fontWeight: 600, marginTop: 2 }}>
                {ratioLabel}
              </div>
            </div>
          )}
        </div>

        {/* Terrain vs Bâtiment */}
        {(ass.landValue != null || ass.buildingValue != null) && (
          <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
            {ass.landValue != null && (
              <div style={{
                flex: 1, minWidth: 100,
                background: 'var(--surface-2, #fff)',
                border: '1px solid var(--border, #e0e4dd)',
                borderRadius: 6, padding: '10px 12px',
              }}>
                <div style={{ fontSize: 10, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.04em' }}>
                  {lang === 'fr' ? 'Terrain' : 'Land'}
                </div>
                <div style={{ fontSize: 15, fontWeight: 600, color: 'var(--ink-1)', fontFamily: 'JetBrains Mono, monospace', marginTop: 3 }}>
                  {fmt.moneyShort(ass.landValue)}
                </div>
              </div>
            )}
            {ass.buildingValue != null && (
              <div style={{
                flex: 1, minWidth: 100,
                background: 'var(--surface-2, #fff)',
                border: '1px solid var(--border, #e0e4dd)',
                borderRadius: 6, padding: '10px 12px',
              }}>
                <div style={{ fontSize: 10, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.04em' }}>
                  {lang === 'fr' ? 'Bâtiment' : 'Building'}
                </div>
                <div style={{ fontSize: 15, fontWeight: 600, color: 'var(--ink-1)', fontFamily: 'JetBrains Mono, monospace', marginTop: 3 }}>
                  {fmt.moneyShort(ass.buildingValue)}
                </div>
              </div>
            )}
          </div>
        )}

        {/* Métadonnées supplémentaires */}
        {(ass.yearBuilt != null || ass.numUnits != null || ass.lotArea != null) && (
          <div style={{ display: 'flex', gap: 16, flexWrap: 'wrap', paddingTop: 4, borderTop: '1px solid var(--border, #e0e4dd)' }}>
            {ass.yearBuilt != null && (
              <div style={{ fontSize: 12, color: 'var(--ink-2)' }}>
                <span style={{ color: 'var(--ink-3)' }}>
                  {lang === 'fr' ? 'Const. ' : 'Built '}
                </span>
                {ass.yearBuilt}
              </div>
            )}
            {ass.numUnits != null && (
              <div style={{ fontSize: 12, color: 'var(--ink-2)' }}>
                <span style={{ color: 'var(--ink-3)' }}>
                  {lang === 'fr' ? 'Logements ' : 'Units '}
                </span>
                {ass.numUnits}
              </div>
            )}
            {ass.lotArea != null && (
              <div style={{ fontSize: 12, color: 'var(--ink-2)' }}>
                <span style={{ color: 'var(--ink-3)' }}>
                  {lang === 'fr' ? 'Terrain ' : 'Lot '}
                </span>
                {ass.lotArea.toLocaleString(lang === 'fr' ? 'fr-CA' : 'en-CA')} m²
              </div>
            )}
          </div>
        )}

        {/* Note source */}
        <div style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 2 }}>
          {srcNote}
        </div>
      </div>
    </section>
  );
}

// ── Section Quartier (données Census StatCan) ────────────────────────────────
function QuartierSection({ listing, t, lang }) {
  const cd = listing.censusData;
  if (!cd || (cd.medianIncome == null && cd.pctRenters == null)) return null;

  const { medianIncome, pctRenters, population, source } = cd;

  // Ratio prix / revenu médian
  const priceToIncome = medianIncome && listing.price
    ? (listing.price / medianIncome).toFixed(1)
    : null;

  // Couleur du ratio selon affordability conventionnelle
  const ratioColor = priceToIncome == null ? 'var(--ink-2)' :
    priceToIncome < 3 ? 'var(--score-excellent)' :
    priceToIncome < 5 ? 'var(--score-good)' :
    priceToIncome < 8 ? 'var(--severity-warning, #b45309)' : 'var(--score-avoid)';

  const ratioLabel = priceToIncome == null ? null :
    priceToIncome < 3 ? (lang === 'fr' ? 'abordable' : 'affordable') :
    priceToIncome < 5 ? (lang === 'fr' ? 'modéré' : 'moderate') :
    priceToIncome < 8 ? (lang === 'fr' ? 'tendu' : 'stretched') :
    (lang === 'fr' ? 'très tendu' : 'very stretched');

  // Formater revenu en $ canadien avec espaces
  const fmtIncome = (v) => {
    if (v == null) return '—';
    return v.toLocaleString(lang === 'fr' ? 'fr-CA' : 'en-CA') + ' $';
  };

  const isFallback = source && source.startsWith('provincial_fallback');

  return (
    <section id="section-quartier">
      <h3 className="section-title">
        {lang === 'fr' ? 'Quartier' : 'Neighbourhood'}
        <small>StatCan Census 2021</small>
      </h3>

      {isFallback && (
        <div style={{
          fontSize: 11, color: 'var(--ink-3)', marginBottom: 10,
          padding: '6px 10px', background: 'var(--bg-warm, #f9f9f6)',
          borderRadius: 6, border: '1px solid var(--line)',
        }}>
          {lang === 'fr'
            ? 'Données provinciales QC (zone non couverte par le cache local)'
            : 'QC provincial data (zone not covered by local cache)'}
        </div>
      )}

      <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>

        {/* Revenu médian */}
        {medianIncome != null && (
          <div className="quartier-row">
            <div className="quartier-label">
              {lang === 'fr' ? 'Revenu médian ménages' : 'Median household income'}
            </div>
            <div className="quartier-value">{fmtIncome(medianIncome)}</div>
          </div>
        )}

        {/* % locataires avec barre */}
        {pctRenters != null && (
          <div>
            <div className="quartier-row" style={{ marginBottom: 4 }}>
              <div className="quartier-label">
                {lang === 'fr' ? '% locataires' : '% renters'}
              </div>
              <div className="quartier-value">
                {pctRenters.toFixed(1)} %
              </div>
            </div>
            <div style={{
              height: 6, background: 'var(--line)', borderRadius: 3, overflow: 'hidden',
            }}>
              <div style={{
                height: '100%',
                width: `${Math.min(100, pctRenters)}%`,
                background: pctRenters > 60
                  ? 'var(--score-excellent)'
                  : pctRenters > 40
                    ? 'var(--score-good)'
                    : 'var(--score-fair)',
                borderRadius: 3,
                transition: 'width 0.4s ease',
              }}/>
            </div>
            <div style={{ fontSize: 10, color: 'var(--ink-4)', marginTop: 3 }}>
              {pctRenters > 60
                ? (lang === 'fr' ? 'Marché locatif fort' : 'Strong rental market')
                : pctRenters > 40
                  ? (lang === 'fr' ? 'Marché mixte' : 'Mixed market')
                  : (lang === 'fr' ? 'Marché propriétaires' : 'Owner-dominant market')}
            </div>
          </div>
        )}

        {/* Ratio prix / revenu */}
        {priceToIncome != null && (
          <div className="quartier-row">
            <div className="quartier-label">
              {lang === 'fr' ? 'Ratio prix / revenu' : 'Price-to-income ratio'}
              <div style={{ fontSize: 10, color: 'var(--ink-4)', fontWeight: 400 }}>
                {lang === 'fr' ? 'prix ÷ revenu médian annuel' : 'price ÷ annual median income'}
              </div>
            </div>
            <div style={{ textAlign: 'right' }}>
              <div style={{ fontFamily: 'var(--mono)', fontSize: 18, fontWeight: 600, color: ratioColor }}>
                {priceToIncome}×
              </div>
              <div style={{ fontSize: 11, color: ratioColor, fontWeight: 500 }}>
                {ratioLabel}
              </div>
            </div>
          </div>
        )}

        {/* Population (discret) */}
        {population != null && (
          <div style={{ fontSize: 11, color: 'var(--ink-4)', paddingTop: 2 }}>
            {lang === 'fr' ? 'Population du secteur' : 'Tract population'}: {population.toLocaleString(lang === 'fr' ? 'fr-CA' : 'en-CA')}
          </div>
        )}

      </div>
    </section>
  );
}

// ── Signaux temporels ────────────────────────────────────────────────────────
function TimeSignalsSection({ listing, t, lang }) {
  const dom = listing.daysOnMarket;
  const cuts = listing.priceCutCount;
  const history = listing.priceHistory || [];

  const hasBadges = (dom != null && dom > 0) || (cuts != null && cuts > 0);
  if (!hasBadges && history.length === 0) return null;

  const fmtDate = (dateStr) => {
    if (!dateStr) return '';
    try {
      return new Date(dateStr).toLocaleDateString(
        lang === 'fr' ? 'fr-CA' : 'en-CA',
        { month: 'short', day: 'numeric', year: 'numeric' }
      );
    } catch { return dateStr; }
  };

  // priceHistory = [{price, date}] — prix précédents du plus ancien au plus récent
  // On affiche : historique ancien → prix actuel (dernier)
  const allPrices = [
    ...history.map(e => ({ price: e.price, date: e.date, isCurrent: false })),
    { price: listing.price, date: null, isCurrent: true }
  ];

  return (
    <section>
      <h3 className="section-title">{t.time_signals}</h3>

      {hasBadges && (
        <div className="time-badges-row">
          {dom != null && dom > 0 && (
            <div className={`time-badge ${dom >= 90 ? 'warn' : dom >= 45 ? 'note' : ''}`}>
              <span className="time-badge-num">{dom}</span>
              <span className="time-badge-label">
                {lang === 'fr' ? 'jours sur le marché' : 'days on market'}
              </span>
            </div>
          )}
          {cuts != null && cuts > 0 && (
            <div className="time-badge note">
              <span className="time-badge-num">{cuts}×</span>
              <span className="time-badge-label">
                {lang === 'fr' ? 'réduction de prix' : 'price reduced'}
              </span>
            </div>
          )}
        </div>
      )}

      {allPrices.length > 1 && (
        <div className="price-timeline">
          <div className="price-timeline-header">{t.price_history}</div>
          {allPrices.map((entry, i) => {
            const prev = allPrices[i - 1];
            const isDown = prev && entry.price < prev.price;
            const isUp = prev && entry.price > prev.price;
            return (
              <div key={i} className={`price-timeline-item ${isDown ? 'down' : isUp ? 'up' : ''}`}>
                <span className="ptl-icon">{isDown ? '↓' : isUp ? '↑' : '·'}</span>
                <span className="ptl-price">
                  {window.fmt.moneyShort(entry.price)}
                </span>
                {entry.isCurrent
                  ? <span className="ptl-current-tag">{lang === 'fr' ? 'actuel' : 'current'}</span>
                  : <span className="ptl-date">{fmtDate(entry.date)}</span>
                }
              </div>
            );
          })}
        </div>
      )}
    </section>
  );
}

// ── Composants utilitaires (inchangés) ───────────────────────────────────────
function KpiCard({ label, value, tone, help, sub }) {
  return (
    <div className="kpi-card">
      <div className="kpi-label">
        {label}
        {sub && <span style={{ fontSize: 10, marginLeft: 4, color: 'var(--ink-4)', textTransform: 'none', letterSpacing: 0 }}>{sub}</span>}
      </div>
      <div className={`kpi-value ${tone || ''}`}>{value}</div>
      {help && <div className="kpi-help">{help}</div>}
    </div>
  );
}

function CalcSlider({ label, display, min, max, step, value, onChange }) {
  return (
    <div className="calc-row">
      <label>{label}</label>
      <div className="calc-value-display">{display}</div>
      <input type="range" min={min} max={max} step={step}
        value={value} onChange={(e) => onChange(+e.target.value)}/>
    </div>
  );
}

function CalcRow({ label, value, tone }) {
  return (
    <div className="calc-result-row">
      <span className="label">{label}</span>
      <span className={`value ${tone || ''}`}>{value}</span>
    </div>
  );
}

// ── Recalcul live badge ──────────────────────────────────────────────────────
// Défauts backend alignés sur scoring_rules.json (adapter.js DEFAULT_ASSUMPTIONS)
const BACKEND_DEFAULTS = {
  downPaymentPct:    0.25,
  mortgageRate:      0.055,
  amortizationYears: 25,
  vacancyPct:        0.07,
};

function hasCustomProfile(userProfile) {
  if (!userProfile) return false;
  return (
    (userProfile.downPaymentPct   != null && Math.abs(userProfile.downPaymentPct   - BACKEND_DEFAULTS.downPaymentPct)   > 0.001) ||
    (userProfile.mortgageRate     != null && Math.abs(userProfile.mortgageRate     - BACKEND_DEFAULTS.mortgageRate)     > 0.0001) ||
    (userProfile.amortizationYears != null && userProfile.amortizationYears        !== BACKEND_DEFAULTS.amortizationYears) ||
    (userProfile.vacancyPct       != null && Math.abs(userProfile.vacancyPct       - BACKEND_DEFAULTS.vacancyPct)       > 0.001)
  );
}

// Badge ★ discret affiché quand une métrique est recalculée
function RecalcBadge({ lang }) {
  return (
    <span
      title={lang === 'fr' ? 'Recalculé selon vos hypothèses' : 'Recalculated with your assumptions'}
      style={{ fontSize: 10, color: '#6b7280', marginLeft: 4, fontWeight: 400, letterSpacing: 0 }}
    >
      ★ {lang === 'fr' ? 'recalculé' : 'recalc.'}
    </span>
  );
}

// ── Drawer principal ─────────────────────────────────────────────────────────
function Drawer({ listing, t, lang, activeProfile, open, onClose, compareIds, onToggleCompare, userProfile: userProfileProp }) {
  const [assumptions, setAssumptions] = useStateD(window.DEFAULT_ASSUMPTIONS);
  const [thumbIdx, setThumbIdx] = useStateD(0);
  const asideRef = useRefD(null);

  // ── Gating state ───────────────────────────────────────────────────────────
  const [currentTier, setCurrentTier] = useStateD('decouverte');
  const [paywallOpen, setPaywallOpen] = useStateD(false);
  const [paywallReason, setPaywallReason] = useStateD('tier_too_low');
  const [paywallFeature, setPaywallFeature] = useStateD('drawer_kpi_advanced');
  const [paywallQuotaInfo, setPaywallQuotaInfo] = useStateD(null);
  const [quotaUsage, setQuotaUsage] = useStateD({ used: 0, limit: 5, remaining: 5, resetsOn: '' });
  const quotaIncrementedRef = useRefD(false);

  // Quota : vérifier + incrémenter UNE SEULE FOIS par ouverture (par listing.id)
  useEffectD(() => {
    if (!open || !listing) return;
    quotaIncrementedRef.current = false;

    _getTierGate().then(tg => {
      // Tier non-decouverte : accès illimité, pas de compteur
      tg.getCurrentTier().then(tier => {
        setCurrentTier(tier);
        if (tier !== 'decouverte') {
          setQuotaUsage({ used: 0, limit: null, remaining: null, resetsOn: '' });
          return;
        }

        const access = tg.checkAccess(tg.FEATURES.DETAILED_ANALYSIS, tier);
        if (!access.allowed && access.reason === 'quota_exceeded') {
          // Quota dépassé → PaywallModal immédiat + fermer le drawer
          setPaywallQuotaInfo(access.quotaInfo);
          setPaywallReason('quota_exceeded');
          setPaywallFeature(tg.FEATURES.DETAILED_ANALYSIS);
          setPaywallOpen(true);
          onClose();
          return;
        }

        // Quota OK → incrémenter UNE SEULE FOIS par ouverture
        if (!quotaIncrementedRef.current) {
          quotaIncrementedRef.current = true;
          tg.incrementQuota(tg.FEATURES.DETAILED_ANALYSIS);
        }
        // Rafraîchir compteur affiché
        const usage = tg.getQuotaUsage(tg.FEATURES.DETAILED_ANALYSIS);
        setQuotaUsage(usage);
      });
    });
  }, [open, listing && listing.id]); // eslint-disable-line react-hooks/exhaustive-deps

  // Tier premium = pas de blur
  const isPremiumLocked = currentTier === 'decouverte';

  // Helpers ouverture paywall
  const openKpiPaywall = () => {
    _getTierGate().then(tg => {
      setPaywallFeature(tg.FEATURES.DRAWER_KPI_ADVANCED);
      setPaywallReason('tier_too_low');
      setPaywallQuotaInfo(null);
      setPaywallOpen(true);
    });
  };

  const openSectionPaywall = (featureKey) => {
    setPaywallFeature(featureKey);
    setPaywallReason('tier_too_low');
    setPaywallQuotaInfo(null);
    setPaywallOpen(true);
  };

  // PaywallModal partagé (quota_exceeded peut s'afficher même sans listing)
  const featureLabelFor = (f) => {
    if (f === 'drawer_kpi_advanced')     return 'KPIs avancés (COC, NOI, GRM, Expense Ratio, Prix/unité)';
    if (f === 'drawer_market_position')  return 'Position de marché (percentile + médiane)';
    if (f === 'drawer_stress_test')      return 'Stress test 3 taux';
    return 'Analyse détaillée';
  };

  const paywallNode = window.PaywallModal ? (
    <window.PaywallModal
      open={paywallOpen}
      onClose={() => setPaywallOpen(false)}
      feature={paywallFeature}
      featureLabel={featureLabelFor(paywallFeature)}
      requiredTier="investisseur"
      currentTier={currentTier}
      reason={paywallReason}
      quotaInfo={paywallQuotaInfo}
    />
  ) : null;

  // ── Profil investisseur ────────────────────────────────────────────────────
  // Résoudre le profil actif (prop > window.userProfile > null)
  const resolvedProfile = userProfileProp || window.userProfile || null;
  const isCustomProfile = hasCustomProfile(resolvedProfile);

  // Recalcul live des métriques quand listing ou userProfile changent
  const liveMetrics = useMemoD(() => {
    if (!listing || !isCustomProfile) return null;
    if (!window.underwriting || typeof window.underwriting.computeMetrics !== 'function') return null;
    try {
      return window.underwriting.computeMetrics(listing, resolvedProfile);
    } catch (e) {
      return null;
    }
  }, [listing && listing.id, isCustomProfile, resolvedProfile]);

  useEffectD(() => {
    setAssumptions(window.DEFAULT_ASSUMPTIONS);
    setThumbIdx(0);
  }, [listing && listing.id]);

  // Marquer le bien comme consulté
  useEffectD(() => {
    if (listing && listing.id && open) {
      window.listingHistory?.markViewed(listing.id);
    }
  }, [listing && listing.id, open]);

  useEffectD(() => {
    const onKey = (e) => { if (e.key === 'Escape' && open) onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [open, onClose]);

  const calc = useMemoD(() => {
    if (!listing) return null;
    return window.computeInvestment(listing, assumptions);
  }, [listing, assumptions]);

  // Scroll vers une section dans le drawer
  const scrollTo = (sectionId) => {
    const aside = asideRef.current;
    const el = aside && aside.querySelector('#' + sectionId);
    if (!aside || !el) return;
    const navH = 48; // hauteur mini-nav
    const elRect = el.getBoundingClientRect();
    const asideRect = aside.getBoundingClientRect();
    const offset = aside.scrollTop + elRect.top - asideRect.top - navH;
    aside.scrollTo({ top: Math.max(0, offset), behavior: 'smooth' });
  };

  if (!listing) return (
    <>
      <div className={`drawer-overlay ${open ? 'open' : ''}`} onClick={onClose}/>
      <aside className={`drawer ${open ? 'open' : ''}`}/>
      {paywallNode}
    </>
  );

  const profileData = listing.scoresByProfile?.[activeProfile];
  const score = profileData ? profileData.score : listing.score;
  const v = window.scoreVerdict(score);
  const ringColor = window.scoreColor(score);
  const circumference = 2 * Math.PI * 42;
  const dashOffset = circumference - (score / 100) * circumference;

  const altImages = [
    `linear-gradient(135deg, ${listing.images[0]}, ${listing.images[2]})`,
    `linear-gradient(160deg, ${listing.images[1]}, ${listing.images[0]})`,
    `linear-gradient(45deg, ${listing.images[2]}, ${listing.images[1]})`
  ];

  const hasMarket = (listing.marketContext &&
    (listing.marketContext.capRatePercentile != null || listing.marketContext.vsMarket !== 'inconnu')) ||
    listing.vacancyRate != null;
  const hasStress = listing.rateSensitivity != null;
  const hasTime = (listing.daysOnMarket != null && listing.daysOnMarket > 0) || (listing.priceHistory && listing.priceHistory.length > 0);
  const hasQuartier = listing.censusData && (listing.censusData.medianIncome != null || listing.censusData.pctRenters != null);

  return (
    <>
      <div className={`drawer-overlay ${open ? 'open' : ''}`} onClick={onClose}/>

      {/* PaywallModal — hors du drawer pour éviter z-index conflicts */}
      {paywallNode}

      <aside ref={asideRef} className={`drawer ${open ? 'open' : ''}`} aria-hidden={!open}>
        <button className="drawer-close" onClick={onClose} aria-label={t.close}>
          <svg width="14" height="14" viewBox="0 0 14 14" stroke="currentColor" strokeWidth="1.4" fill="none">
            <path d="M2 2l10 10M12 2L2 12"/>
          </svg>
        </button>

        {/* Compteur quota — uniquement en tier decouverte */}
        {isPremiumLocked && quotaUsage.limit != null && (
          <div style={{
            display: 'flex', alignItems: 'center', gap: 6,
            padding: '6px 16px 6px 14px',
            background: quotaUsage.remaining <= 1 ? 'oklch(0.97 0.04 50)' : 'var(--bg-warm, #f9f9f6)',
            borderBottom: '1px solid var(--line, #e8e8e0)',
            fontSize: 12, color: 'var(--ink-3, #888)',
          }}>
            <LockSvg size={11}/>
            <span>
              Analyses ce mois&nbsp;:&nbsp;
              <strong style={{ color: quotaUsage.remaining <= 1 ? 'var(--severity-warning, #b45309)' : 'var(--ink-1)' }}>
                {quotaUsage.used}/{quotaUsage.limit}
              </strong>
            </span>
            {quotaUsage.remaining === 0 && (
              <span style={{ marginLeft: 4, color: 'var(--severity-warning, #b45309)', fontWeight: 500 }}>
                · Quota atteint
              </span>
            )}
          </div>
        )}

        {/* Hero */}
        <div className="drawer-hero">
          <div className="img-bg" style={{ background: altImages[thumbIdx] }}/>
          <div className="img-stripes"/>
          <div className="img-label">PHOTO PLACEHOLDER · {listing.type.toUpperCase()}</div>
          <div className="drawer-thumbs">
            {altImages.map((bg, i) => (
              <button key={i}
                className={`drawer-thumb ${i === thumbIdx ? 'active' : ''}`}
                onClick={() => setThumbIdx(i)}
                style={{ background: bg }}
                aria-label={`Photo ${i + 1}`}/>
            ))}
          </div>
        </div>

        {/* Mini-nav sticky */}
        <div className="drawer-mini-nav">
          <button
            className="drawer-mini-nav-btn drawer-analyze-btn"
            onClick={() => window.navigateTo && window.navigateTo(`#/analyze/${listing.id}`)}
          >
            {t.full_analysis || 'Analyse complète'}
          </button>
          {onToggleCompare && (() => {
            const isInCompare = compareIds && compareIds.includes(String(listing.id));
            const isFull = compareIds && compareIds.length >= 4 && !isInCompare;
            return (
              <button
                className="drawer-mini-nav-btn"
                onClick={() => onToggleCompare(listing.id)}
                disabled={isFull}
                title={isFull ? (lang === 'fr' ? 'Comparaison pleine (max 4)' : 'Compare full (max 4)') : ''}
                style={{
                  opacity: isFull ? 0.45 : 1,
                  cursor: isFull ? 'not-allowed' : 'pointer',
                  color: isInCompare ? 'var(--brand, #2c3e2d)' : undefined,
                  fontWeight: isInCompare ? 600 : undefined,
                }}
              >
                {isInCompare
                  ? (lang === 'fr' ? '✓ Dans la comparaison' : '✓ In compare')
                  : (lang === 'fr' ? '➕ Comparer' : '➕ Compare')}
              </button>
            );
          })()}
          <div className="drawer-mini-nav-divider"/>
          <button className="drawer-mini-nav-btn" onClick={() => scrollTo('section-financials')}>
            {t.nav_financials}
          </button>
          {hasMarket && (
            <button className="drawer-mini-nav-btn" onClick={() => scrollTo('section-market')}>
              {t.nav_market_pos}
            </button>
          )}
          <button className="drawer-mini-nav-btn" onClick={() => scrollTo('section-analysis')}>
            {t.nav_analysis}
          </button>
          {hasStress && (
            <button className="drawer-mini-nav-btn" onClick={() => scrollTo('section-stress')}>
              {t.nav_stress}
            </button>
          )}
          <button className="drawer-mini-nav-btn" onClick={() => scrollTo('section-calc')}>
            {t.nav_calc}
          </button>
          {hasTime && (
            <button className="drawer-mini-nav-btn" onClick={() => scrollTo('section-time')}>
              {lang === 'fr' ? 'Signaux' : 'Signals'}
            </button>
          )}
          {hasQuartier && (
            <button className="drawer-mini-nav-btn" onClick={() => scrollTo('section-quartier')}>
              {lang === 'fr' ? 'Quartier' : 'Neighbourhood'}
            </button>
          )}
        </div>

        <div className="drawer-body">

          {/* Head : prix, adresse, score, chips */}
          <div className="drawer-head">
            <div className="drawer-title-block">
              <div className="eyebrow">{t.sale_price} · {listing.type}</div>
              <h2 className="drawer-title">
                {window.fmt.money(listing.price, lang === 'fr' ? 'fr-CA' : 'en-CA')}
              </h2>
              <p className="drawer-address">
                {[listing.address, listing.neighborhood, listing.city].filter(Boolean).join(', ')}
              </p>
              <div className="drawer-meta-row">
                <span><strong>{listing.units}</strong>&nbsp;{listing.units > 1 ? t.units : t.unit}</span>
                {listing.bedrooms > 0 && <><span>·</span><span><strong>{listing.bedrooms}</strong>&nbsp;{t.bedrooms}</span></>}
                {listing.bathrooms > 0 && <><span>·</span><span><strong>{listing.bathrooms}</strong>&nbsp;{t.bathrooms}</span></>}
                {listing.grossArea > 0 && <><span>·</span><span><strong>{window.fmt.num(listing.grossArea)}</strong>&nbsp;{t.sqft}</span></>}
                {listing.yearBuilt > 0 && <><span>·</span><span>{t.built}&nbsp;<strong>{listing.yearBuilt}</strong></span></>}
              </div>
              <SignalChips listing={listing} lang={lang}/>
            </div>
            <div className="drawer-score-block">
              <div className="score-ring-wrap">
                <svg width="96" height="96">
                  <circle cx="48" cy="48" r="42" className="score-ring-bg" strokeWidth="6" fill="none"/>
                  <circle cx="48" cy="48" r="42" className="score-ring-fg"
                    stroke={ringColor} strokeWidth="6" fill="none"
                    strokeDasharray={circumference} strokeDashoffset={dashOffset}/>
                </svg>
                <div className="score-ring-text">
                  <div className="score-ring-num">{score}</div>
                  <div className="score-ring-label">{t.score}</div>
                </div>
              </div>
              <div className="verdict-tag" style={{ background: ringColor }}>{t[v]}</div>
            </div>
          </div>

          {/* Barre source */}
          <SourceLinkBar listing={listing} lang={lang}/>

          {/* Position de marché — PREMIUM (blur en decouverte) */}
          {hasMarket && (
            <PremiumSection
              isPremiumLocked={isPremiumLocked}
              onUnlock={() => openSectionPaywall('drawer_market_position')}
            >
              <MarketPositionCard listing={listing} t={t} lang={lang}/>
            </PremiumSection>
          )}

          {/* KPI financiers */}
          <section id="section-financials">
            <h3 className="section-title">
              {t.investment_summary}
              <small>{t.financials}</small>
              {liveMetrics && (
                <span style={{ fontSize: 10, color: '#6b7280', marginLeft: 8, fontWeight: 400 }}>
                  ★ {lang === 'fr' ? 'selon vos hypothèses' : 'per your assumptions'}
                </span>
              )}
            </h3>
            <div className="kpi-grid">
              {(() => {
                const capRate   = liveMetrics ? liveMetrics.capRate   : listing.metrics.capRate;
                const dscr      = liveMetrics ? liveMetrics.dscr      : listing.metrics.dscr;
                const cashFlow  = liveMetrics ? liveMetrics.cashFlow  : listing.metrics.cashFlow;
                const coc       = liveMetrics ? liveMetrics.cashOnCash : listing.metrics.cashOnCash;
                const noi       = liveMetrics ? liveMetrics.noi       : listing.metrics.noi;
                const badge     = liveMetrics ? <RecalcBadge lang={lang}/> : null;
                return (
                  <>
                    {/* ── KPIs FREE (3) : toujours visibles ── */}
                    <KpiCard t={t}
                      label={<>{t.cap_rate}{badge}</>}
                      value={window.fmt.pct(capRate)}
                      tone={capRate >= 0.05 ? 'pos' : 'neg'}
                      help={t.cap_rate_def}/>
                    <KpiCard t={t}
                      label={<>DSCR{badge}</>}
                      value={dscr != null ? dscr.toFixed(2) : '—'}
                      tone={dscr >= 1.2 ? 'pos' : dscr < 1 ? 'neg' : ''}
                      help={t.dscr_def}/>
                    <KpiCard t={t}
                      label={<>{t.annual_cash_flow}{badge}</>}
                      value={window.fmt.moneyShort(cashFlow)}
                      tone={cashFlow > 0 ? 'pos' : 'neg'}
                      help={t.cash_flow_def}/>
                    {/* ── KPIs PREMIUM (5) : blurés en decouverte ── */}
                    <LockedKpiCard label={t.coc} isPremiumLocked={isPremiumLocked} onUnlock={openKpiPaywall}>
                      <KpiCard t={t}
                        label={<>{t.coc}{badge}</>}
                        value={window.fmt.pct(coc)}
                        tone={coc >= 0.04 ? 'pos' : coc < 0 ? 'neg' : ''}
                        help={t.coc_def}/>
                    </LockedKpiCard>
                    <LockedKpiCard label={t.noi} isPremiumLocked={isPremiumLocked} onUnlock={openKpiPaywall}>
                      <KpiCard t={t}
                        label={<>{t.noi}{badge}</>}
                        value={window.fmt.moneyShort(noi)}
                        sub={t.annually}/>
                    </LockedKpiCard>
                  </>
                );
              })()}
              {/* expenseRatio, GRM, pricePerUnit — aussi PREMIUM */}
              <LockedKpiCard label={t.expense_ratio} isPremiumLocked={isPremiumLocked} onUnlock={openKpiPaywall}>
                <KpiCard t={t} label={t.expense_ratio}
                  value={listing.metrics.expenseRatio != null ? window.fmt.pct(listing.metrics.expenseRatio) : '—'}/>
              </LockedKpiCard>
              <LockedKpiCard label={t.grm} isPremiumLocked={isPremiumLocked} onUnlock={openKpiPaywall}>
                <KpiCard t={t} label={t.grm}
                  value={listing.metrics.grm != null ? listing.metrics.grm.toFixed(1) : '—'}/>
              </LockedKpiCard>
              <LockedKpiCard label={t.price_per_unit} isPremiumLocked={isPremiumLocked} onUnlock={openKpiPaywall}>
                <KpiCard t={t} label={t.price_per_unit}
                  value={window.fmt.moneyShort(listing.metrics.pricePerUnit)}/>
              </LockedKpiCard>
            </div>
          </section>

          {/* Analyse experte */}
          <DeepAnalysisSection listing={listing} t={t} lang={lang}/>

          {/* Évaluation foncière municipale (MAMH) */}
          <MunicipalAssessmentSection listing={listing} lang={lang}/>

          {/* Stress test des taux — PREMIUM (blur en decouverte) */}
          {hasStress && (
            <PremiumSection
              isPremiumLocked={isPremiumLocked}
              onUnlock={() => openSectionPaywall('drawer_stress_test')}
            >
              <RateStressTable rateSensitivity={listing.rateSensitivity} t={t} lang={lang}/>
            </PremiumSection>
          )}

          {/* Calculateur en direct */}
          <section id="section-calc">
            <h3 className="section-title">
              {t.invest_panel_title}
              <small>{t.invest_panel_subtitle}</small>
            </h3>
            <div className="calc-card">
              <div className="calc-controls">
                <CalcSlider
                  label={t.down_payment}
                  display={`${(assumptions.downPaymentPct * 100).toFixed(0)} % · ${window.fmt.moneyShort(listing.price * assumptions.downPaymentPct)}`}
                  min={5} max={50} step={1}
                  value={assumptions.downPaymentPct * 100}
                  onChange={(v) => setAssumptions({ ...assumptions, downPaymentPct: v / 100 })}/>
                <CalcSlider
                  label={t.mortgage_rate}
                  display={`${(assumptions.mortgageRate * 100).toFixed(2)} %`}
                  min={2} max={10} step={0.05}
                  value={assumptions.mortgageRate * 100}
                  onChange={(v) => setAssumptions({ ...assumptions, mortgageRate: v / 100 })}/>
                <CalcSlider
                  label={t.amortization}
                  display={`${assumptions.amortizationYears} ${t.years}`}
                  min={15} max={30} step={1}
                  value={assumptions.amortizationYears}
                  onChange={(v) => setAssumptions({ ...assumptions, amortizationYears: v })}/>
              </div>
              <div className="calc-results">
                <CalcRow label={t.down_payment} value={window.fmt.money(calc.downPayment)}/>
                <CalcRow label={lang === 'fr' ? 'Prêt hypothécaire' : 'Loan amount'} value={window.fmt.money(calc.loanAmount)}/>
                <CalcRow label={lang === 'fr' ? 'Paiement mensuel' : 'Monthly payment'} value={window.fmt.money(calc.monthlyPayment)}/>
                <CalcRow label={t.noi} value={window.fmt.money(calc.noi)}/>
                <CalcRow label="DSCR" value={calc.dscr.toFixed(2)}
                  tone={calc.dscr >= 1.2 ? 'pos' : calc.dscr < 1 ? 'neg' : ''}/>
                <div className="calc-result-row highlight">
                  <span className="label">{t.annual_cash_flow}</span>
                  <span className={`value ${calc.cashFlow > 0 ? 'pos' : calc.cashFlow < 0 ? 'neg' : ''}`}>
                    {calc.cashFlow > 0 ? '+' : ''}{window.fmt.money(calc.cashFlow)}
                  </span>
                </div>
                <CalcRow label={t.coc} value={window.fmt.pct(calc.cashOnCash)}
                  tone={calc.cashOnCash > 0.04 ? 'pos' : calc.cashOnCash < 0 ? 'neg' : ''}/>
              </div>
            </div>
          </section>

          {/* Signaux temporels */}
          {hasTime && (
            <div id="section-time">
              <TimeSignalsSection listing={listing} t={t} lang={lang}/>
            </div>
          )}

          {/* Walk Score — Localisation */}
          <WalkScoreSection listing={listing} lang={lang}/>
          {/* Quartier — données socio-économiques StatCan Census 2021 */}
          {hasQuartier && (
            <QuartierSection listing={listing} t={t} lang={lang}/>
          )}

          {/* Risques & signaux (flags) */}
          {listing.flags && listing.flags.length > 0 && (
            <section>
              <h3 className="section-title">{t.risks}</h3>
              <div className="risk-list">
                {listing.flags.map((f, i) => {
                  const sev = f.severity;
                  const sevLabel = t[`severity_${sev}`] || sev;
                  const icon = sev === 'critical' ? '!' : sev === 'ok' ? '✓' : sev === 'warning' ? '⚠' : 'i';
                  return (
                    <div key={i} className={`risk-item ${sev}`}>
                      <div className="severity-icon">{icon}</div>
                      <div>
                        <div className="risk-tag">{sevLabel}</div>
                        <h5>{f.title[lang]}</h5>
                        <p>{f.desc[lang]}</p>
                      </div>
                    </div>
                  );
                })}
              </div>
            </section>
          )}

          {/* Méthodologie */}
          <section className="methodology">
            <strong>{t.methodology} —</strong> {t.methodology_desc}
          </section>

        </div>
      </aside>
    </>
  );
}

Object.assign(window, { Drawer });
