/* global React, window, Icon, PageHead, CHART, niceNum */
/* ============================================================
   Price Compare — 价格走势对比
   GTA region map + two-period picker + comparison table +
   30-year trend with comparison bands.
   All figures illustrative (TRREB-style), not real data.
   ============================================================ */
const { useState: useCmp } = React;

/* ---------- Property types (brand PROP_COLORS, not screenshot palette) ---------- */
const CMP_TYPES = [
  { key: 'all',   zh: '全部',   mult: 1,    color: CHART.ink,       share: 1 },
  { key: 'det',   zh: '独立屋', mult: 1.34, color: CHART.green,     share: 0.45 },
  { key: 'semi',  zh: '半独立', mult: 0.92, color: CHART.steel,     share: 0.06 },
  { key: 'town',  zh: '镇屋',   mult: 0.84, color: CHART.greenDeep, share: 0.20 },
  { key: 'condo', zh: '公寓',   mult: 0.60, color: CHART.gold,      share: 0.29 },
];
const CMP_TYPE = Object.fromEntries(CMP_TYPES.map((t) => [t.key, t]));

/* ---------- 30-year yearly base (全部 avg, $K) — 1996 → 2026 ---------- */
const CMP_BASE = [198,210,224,240,258,277,299,323,348,374,396,420,402,430,495,560,600,642,705,798,910,985,948,1005,1098,1292,1382,1302,1242,1216,1118];
const CMP_VOL  = [1620,1780,1950,2080,2160,2240,2480,2520,2600,2680,2540,2700,2100,2360,2620,2780,2700,2820,3050,3280,3420,2980,2540,2860,2680,3380,2360,2280,2520,2740,2860];
const CMP_SEAS = [0.965,0.97,0.99,1.02,1.035,1.02,0.995,0.975,0.985,1.0,0.985,0.96]; // Jan..Dec
const CMP_Y0 = 1996, CMP_Y1 = 2026;
const CMP_YEARS = Array.from({ length: CMP_Y1 - CMP_Y0 + 1 }, (_, i) => CMP_Y0 + i);

/* ---------- Exact anchor periods (reproduce the reference screenshots) ---------- */
const CMP_EXACT = {
  '2005-1': { p: { all: 361, det: 426, semi: 310, town: 263, condo: 271 }, c: { all: 181, det: 102, semi: 21, town: 36, condo: 22 } },
  '2010-1': { p: { all: 512, det: 625, semi: 396, town: 372, condo: 307 }, c: { all: 205, det: 122, semi: 11, town: 36, condo: 36 } },
  '2025-4': { p: { all: 1239, det: 1693, semi: 1211, town: 1118, condo: 693 }, c: { all: 229, det: 99, semi: 12, town: 47, condo: 71 } },
  '2026-4': { p: { all: 1140, det: 1510, semi: 1060, town: 977, condo: 683 }, c: { all: 239, det: 110, semi: 14, town: 44, condo: 71 } },
};

const cmpClampY = (y) => Math.max(CMP_Y0, Math.min(CMP_Y1, y));
function cmpPrice(year, month, key, muniMult = 1) {
  const ex = CMP_EXACT[year + '-' + month];
  if (ex && ex.p[key] != null && muniMult === 1) return ex.p[key];
  const base = CMP_BASE[cmpClampY(year) - CMP_Y0];
  return Math.round(base * CMP_SEAS[month - 1] * CMP_TYPE[key].mult * muniMult);
}
function cmpCount(year, month, key, muniMult = 1) {
  const ex = CMP_EXACT[year + '-' + month];
  if (ex && ex.c[key] != null && muniMult === 1) return ex.c[key];
  const monthly = (CMP_VOL[cmpClampY(year) - CMP_Y0] / 12) * CMP_SEAS[month - 1];
  return Math.max(1, Math.round(monthly * CMP_TYPE[key].share * muniMult));
}
const cmpYearPrice = (year, key, mm = 1) => Math.round(CMP_BASE[cmpClampY(year) - CMP_Y0] * CMP_TYPE[key].mult * mm);
const cmpYearVol = (year, key, mm = 1) => Math.round(CMP_VOL[cmpClampY(year) - CMP_Y0] * CMP_TYPE[key].share * mm);

const fmtPrice = (vK) => (vK >= 1000 ? '$' + (vK / 1000).toFixed(2) + 'M' : '$' + vK + 'K');
const fmtCount = (v) => v.toLocaleString();

/* ---------- GTA regions + municipalities (schematic geography) ---------- */
const CMP_REGIONS = [
  { id: 'dufferin', zh: '杜弗林', en: 'Dufferin', color: '#2FA39B', poly: [[40,28],[152,30],[162,118],[50,128]] },
  { id: 'york',     zh: '约克',   en: 'York',     color: '#5C6B68', poly: [[362,18],[700,16],[712,158],[372,168]] },
  { id: 'durham',   zh: '杜伦',   en: 'Durham',   color: '#C0922E', poly: [[718,26],[966,40],[950,252],[718,208]] },
  { id: 'peel',     zh: '皮尔',   en: 'Peel',     color: '#C2553F', poly: [[172,88],[352,80],[362,250],[186,258]] },
  { id: 'halton',   zh: '霍尔顿', en: 'Halton',   color: '#2F6FB5', poly: [[112,250],[372,262],[344,350],[96,340]] },
  { id: 'toronto',  zh: '多伦多', en: 'Toronto',  color: '#64BC64', poly: [[384,250],[700,226],[690,352],[398,354]] },
];
const CMP_REGION = Object.fromEntries(CMP_REGIONS.map((r) => [r.id, r]));

// Real GTA municipal boundaries (Stats Canada census subdivisions, generalized),
// projected to SVG by build_geo.py → gta-geo.js (window.CMP_GEO). Each muni:
// { name, zh, region, d (svg path), lx, ly (label pos), lab (show label/pill), m (price mult) }.
const CMP_GEO = (typeof window !== 'undefined' && window.CMP_GEO) || { vb: [1000, 854], munis: [] };
const CMP_MUNIS = CMP_GEO.munis;

/* ---------- Segmented control (unique name; Seg is taken in screen-charts) ---------- */
function CmpSeg({ options, value, onChange, size = 12 }) {
  return (
    <div style={{ display: 'inline-flex', background: 'var(--panel-2)', border: '1px solid var(--line)', borderRadius: 7, padding: 3, gap: 2 }}>
      {options.map((o) => (
        <button key={o.v} onClick={() => onChange(o.v)} style={{
          border: 'none', cursor: 'pointer', fontFamily: 'inherit', fontSize: size, fontWeight: 700, padding: '6px 13px', borderRadius: 5,
          background: value === o.v ? 'var(--accent)' : 'transparent', color: value === o.v ? '#16241B' : 'var(--ink-2)', transition: 'all 140ms',
        }}>{o.label}</button>
      ))}
    </div>
  );
}

/* ---------- Region map banner (real GTA municipal boundaries) ---------- */
function PriceMap({ muni, setMuni }) {
  const [hover, setHover] = useCmp(null);
  const [vw, vh] = CMP_GEO.vb;
  const munis = CMP_GEO.munis;
  const colorOf = (id) => (CMP_REGION[id] ? CMP_REGION[id].color : '#999');
  return (
    <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '13px var(--card-pad)', borderBottom: '1px solid var(--line)' }}>
        <div style={{ display: 'inline-flex', alignItems: 'center', gap: 9 }}>
          <Icon name="map" size={17} color="var(--accent)" />
          <h3 style={{ margin: 0, fontSize: 14.5, fontWeight: 700 }}>大多伦多区域地图</h3>
          <span style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>· 真实行政边界 · 点选市镇查看该地价格</span>
        </div>
        <div style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
          <span style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>当前：</span>
          <span style={{ fontSize: 12.5, fontWeight: 700, color: 'var(--ink)' }}>{muni ? muni.zh + ' · ' + CMP_REGION[muni.region].zh : 'GTA 全域'}</span>
          {muni && <button className="chip" style={{ padding: '4px 9px' }} onClick={() => setMuni(null)}><Icon name="x" size={12} />清除</button>}
        </div>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0,1fr) 132px', gap: 0 }}>
        <div style={{ background: 'linear-gradient(160deg,#E9F0F5,#F3F6F2)', height: 'clamp(380px, 40vw, 540px)', display: 'flex' }}
          onMouseLeave={() => setHover(null)}>
          <svg viewBox={'0 0 ' + vw + ' ' + vh} preserveAspectRatio="xMidYMid meet"
            style={{ width: '100%', height: '100%', display: 'block', fontFamily: "'Open Sans',sans-serif" }}>
            {/* municipality shapes */}
            {munis.map((mu) => {
              const sel = muni && muni.name === mu.name;
              const hot = hover === mu.name;
              return (
                <path key={mu.name} d={mu.d} fill={colorOf(mu.region)} fillRule="evenodd"
                  stroke="#FFFFFF" strokeWidth={sel ? 2.4 : 1.4}
                  opacity={sel ? 1 : hot ? 0.92 : (muni ? 0.62 : 0.82)}
                  style={{ cursor: 'pointer', transition: 'opacity 120ms' }}
                  onMouseEnter={() => setHover(mu.name)}
                  onClick={() => setMuni(sel ? null : mu)} />
              );
            })}
            {/* labels (major munis) */}
            {munis.filter((m) => m.lab).map((mu) => {
              const sel = muni && muni.name === mu.name;
              return (
                <text key={'t' + mu.name} x={mu.lx} y={mu.ly} textAnchor="middle"
                  fontSize={sel ? 20 : 17} fontWeight={sel ? 800 : 600}
                  fill="#FFFFFF" stroke="rgba(31,40,31,0.40)" strokeWidth="2.6" paintOrder="stroke"
                  style={{ pointerEvents: 'none' }}>{mu.zh}</text>
              );
            })}
          </svg>
        </div>
        {/* legend */}
        <div style={{ borderLeft: '1px solid var(--line)', padding: '14px 12px', display: 'flex', flexDirection: 'column', gap: 11, background: 'var(--panel)' }}>
          <div className="eyebrow">区域</div>
          {CMP_REGIONS.map((r) => (
            <div key={r.id} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12, fontWeight: 600, color: 'var(--ink-2)' }}>
              <span style={{ width: 11, height: 11, borderRadius: 3, background: r.color, flexShrink: 0 }} />
              <span>{r.en}</span>
            </div>
          ))}
          <div style={{ marginTop: 'auto', fontSize: 9.5, color: 'var(--ink-3)', lineHeight: 1.5 }}>边界：StatCan 普查子区划（概化）</div>
        </div>
      </div>
    </div>
  );
}

/* ---------- Municipality pills ---------- */
function MuniPills({ muni, setMuni }) {
  return (
    <div className="scroll" style={{ display: 'flex', gap: 8, overflowX: 'auto', paddingBottom: 4 }}>
      {CMP_MUNIS.filter((m) => m.lab).map((mu) => {
        const on = muni && muni.name === mu.name;
        const col = CMP_REGION[mu.region].color;
        return (
          <button key={mu.name} onClick={() => setMuni(on ? null : mu)} className="chip" data-on={on}
            style={{ flexShrink: 0, borderColor: on ? col : undefined }}>
            <span style={{ width: 7, height: 7, borderRadius: 999, background: col, flexShrink: 0 }} />{mu.zh}
          </button>
        );
      })}
    </div>
  );
}

/* ---------- Period picker (left panel) ---------- */
function PeriodPicker({ a, b, setA, setB, active, setActive, range, setRange, metric, setMetric }) {
  const cur = active === 'A' ? a : b;
  const setCur = active === 'A' ? setA : setB;
  const months = Array.from({ length: 12 }, (_, i) => i + 1);
  return (
    <div className="card" style={{ padding: 'var(--card-pad)', display: 'flex', flexDirection: 'column', gap: 14 }}>
      <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap' }}>
        <div>
          <h3 style={{ margin: 0, fontSize: 15.5, fontWeight: 700 }}>价格走势</h3>
          <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 3 }}>年度均价 · TRREB Market Watch</div>
        </div>
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
          <CmpSeg options={[{ v: '30', label: '30年' }, { v: '10', label: '10年' }, { v: '5', label: '5年' }]} value={range} onChange={setRange} />
          <CmpSeg options={[{ v: 'price', label: '均价' }, { v: 'vol', label: '成交' }]} value={metric} onChange={setMetric} />
        </div>
      </div>

      {/* A / B period tabs */}
      <div style={{ display: 'flex', gap: 8 }}>
        {[['A', a, CHART.green], ['B', b, CHART.steel]].map(([id, p, col]) => (
          <button key={id} onClick={() => setActive(id)} style={{
            flex: 1, textAlign: 'left', cursor: 'pointer', fontFamily: 'inherit',
            border: '1.5px solid ' + (active === id ? col : 'var(--line)'), borderRadius: 8, padding: '9px 12px',
            background: active === id ? col + '14' : 'var(--panel-2)', transition: 'all 140ms',
          }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 10.5, fontWeight: 700, letterSpacing: '0.06em', color: col }}>
              <span style={{ width: 8, height: 8, borderRadius: 999, background: col }} />对比期 {id}
            </div>
            <div className="tabnum" style={{ fontSize: 15, fontWeight: 800, marginTop: 3, color: 'var(--ink)' }}>{p.year} 年 {p.month} 月</div>
          </button>
        ))}
      </div>

      {/* year grid */}
      <div>
        <div className="eyebrow" style={{ marginBottom: 7 }}>年份 · 设置对比期 {active}</div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(8, 1fr)', gap: 4 }}>
          {CMP_YEARS.slice().reverse().map((y) => {
            const isA = a.year === y, isB = b.year === y;
            const col = isA ? CHART.green : isB ? CHART.steel : null;
            return (
              <button key={y} onClick={() => setCur({ ...cur, year: y })} className="tabnum" style={{
                cursor: 'pointer', fontFamily: 'inherit', fontSize: 11.5, fontWeight: col ? 800 : 600,
                padding: '7px 0', borderRadius: 5, border: '1px solid ' + (col || 'var(--line)'),
                background: col ? col : 'var(--panel)', color: col ? '#fff' : 'var(--ink-2)', transition: 'all 120ms',
              }}>{y}</button>
            );
          })}
        </div>
      </div>

      {/* month rows */}
      {[['A', a, setA, CHART.green], ['B', b, setB, CHART.steel]].map(([id, p, setP, col]) => (
        <div key={id}>
          <div className="eyebrow" style={{ marginBottom: 6, color: col }}>{id} · {p.year} 年 — 选月份</div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(12, 1fr)', gap: 4 }}>
            {months.map((m) => {
              const on = p.month === m;
              return (
                <button key={m} onClick={() => setP({ ...p, month: m })} className="tabnum" style={{
                  cursor: 'pointer', fontFamily: 'inherit', fontSize: 11, fontWeight: on ? 800 : 600,
                  padding: '6px 0', borderRadius: 5, border: '1px solid ' + (on ? col : 'var(--line)'),
                  background: on ? col : 'var(--az-green-20,#E0F1E0)', color: on ? '#fff' : 'var(--ink-2)', transition: 'all 120ms',
                }}>{m}</button>
              );
            })}
          </div>
        </div>
      ))}
    </div>
  );
}

/* ---------- Comparison table (right panel) ---------- */
function CompareTable({ a, b, muni }) {
  const mm = muni ? muni.m : 1;
  return (
    <div className="card" style={{ padding: 0, overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
      <div style={{ padding: '14px var(--card-pad)', borderBottom: '1px solid var(--line)' }}>
        <h3 style={{ margin: 0, fontSize: 15.5, fontWeight: 700 }}>
          {a.year} 年 {a.month} 月 <span style={{ color: 'var(--ink-3)', fontWeight: 600 }}>vs</span> {b.year} 年 {b.month} 月 对比
        </h3>
        <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 3 }}>{muni ? muni.zh : 'GTA 全域'} · 均价与成交套数</div>
      </div>
      <div style={{ overflowX: 'auto' }}>
        <table className="tabnum" style={{ width: '100%', borderCollapse: 'collapse', fontSize: 12.5 }}>
          <thead>
            <tr>
              <th rowSpan={2} style={cmpTh(true)}>类型</th>
              <th colSpan={3} style={cmpThGrp()}>均价</th>
              <th colSpan={2} style={cmpThGrp()}>套数</th>
            </tr>
            <tr>
              <th style={cmpTh()}>{String(a.year).slice(2)}/{a.month}</th>
              <th style={cmpTh()}>{String(b.year).slice(2)}/{b.month}</th>
              <th style={cmpTh()}>Δ</th>
              <th style={cmpTh()}>{String(a.year).slice(2)}/{a.month}</th>
              <th style={cmpTh()}>{String(b.year).slice(2)}/{b.month}</th>
            </tr>
          </thead>
          <tbody>
            {CMP_TYPES.map((t) => {
              const pa = cmpPrice(a.year, a.month, t.key, mm), pb = cmpPrice(b.year, b.month, t.key, mm);
              const ca = cmpCount(a.year, a.month, t.key, mm), cb = cmpCount(b.year, b.month, t.key, mm);
              const d = ((pb - pa) / pa) * 100;
              const up = d >= 0;
              return (
                <tr key={t.key} style={{ borderBottom: '1px solid var(--line-2)', background: t.key === 'all' ? 'var(--panel-2)' : 'transparent' }}>
                  <td style={{ padding: '11px 16px', whiteSpace: 'nowrap' }}>
                    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 7, fontWeight: t.key === 'all' ? 800 : 600 }}>
                      <span style={{ width: 8, height: 8, borderRadius: 999, background: t.color }} />{t.zh}
                    </span>
                  </td>
                  <td style={cmpTd()}>{fmtPrice(pa)}</td>
                  <td style={cmpTd(true)}>{fmtPrice(pb)}</td>
                  <td style={{ ...cmpTd(), color: up ? CHART.up : CHART.down, fontWeight: 800 }}>
                    {up ? '▲' : '▼'}{Math.abs(d).toFixed(1)}%
                  </td>
                  <td style={{ ...cmpTd(), color: 'var(--ink-2)' }}>{fmtCount(ca)}</td>
                  <td style={{ ...cmpTd(), color: 'var(--ink-2)' }}>{fmtCount(cb)}</td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
      <div style={{ padding: '11px var(--card-pad)', borderTop: '1px solid var(--line)', fontSize: 10.5, color: 'var(--ink-3)', lineHeight: 1.5 }}>
        数据为 TRREB 风格示例值，仅供演示，非真实成交。
      </div>
    </div>
  );
}
const cmpTh = (left) => ({ textAlign: left ? 'left' : 'right', padding: '9px 16px', fontSize: 10, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', color: 'var(--ink-3)', borderBottom: '1px solid var(--line)', whiteSpace: 'nowrap', verticalAlign: 'bottom' });
const cmpThGrp = () => ({ textAlign: 'center', padding: '9px 16px 3px', fontSize: 10, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', color: 'var(--ink-3)', borderLeft: '1px solid var(--line-2)' });
const cmpTd = (strong) => ({ textAlign: 'right', padding: '11px 16px', fontWeight: strong ? 800 : 600, whiteSpace: 'nowrap' });

/* ---------- 30-year trend chart with comparison bands ---------- */
function TrendChart({ a, b, range, metric, type, muni }) {
  const [hover, setHover] = useCmp(null);
  const mm = muni ? muni.m : 1;
  const span = range === '5' ? 5 : range === '10' ? 10 : 30;
  const start = cmpClampY(CMP_Y1 - span);
  const years = CMP_YEARS.filter((y) => y >= start);
  const valOf = (y) => (metric === 'price' ? cmpYearPrice(y, type, mm) : cmpYearVol(y, type, mm));
  const series = years.map(valOf);
  const W = 1000, H = 300, padL = 64, padR = 26, padT = 22, padB = 38;
  const iw = W - padL - padR, ih = H - padT - padB;
  const max = Math.max(...series), min = Math.min(...series);
  const sp = max - min || 1, top = max + sp * 0.12, bot = Math.max(0, min - sp * 0.16), rng = top - bot || 1;
  const x = (i) => padL + (years.length === 1 ? iw / 2 : (i / (years.length - 1)) * iw);
  const y = (v) => padT + ih - ((v - bot) / rng) * ih;
  const pts = series.map((v, i) => [x(i), y(v)]);
  const d = pts.map((p, i) => (i ? 'L' : 'M') + p[0].toFixed(1) + ' ' + p[1].toFixed(1)).join(' ');
  const area = d + ' L ' + x(years.length - 1) + ' ' + (padT + ih) + ' L ' + padL + ' ' + (padT + ih) + ' Z';
  const fmt = metric === 'price' ? (v) => (v >= 1000 ? '$' + (v / 1000).toFixed(1) + 'M' : '$' + v + 'K') : fmtCount;
  const everyN = span === 5 ? 1 : span === 10 ? 2 : 5;
  const ticks = 4;
  const grid = Array.from({ length: ticks + 1 }, (_, i) => bot + (rng * i) / ticks);
  const halfStep = years.length > 1 ? iw / (years.length - 1) / 2 : iw / 2;
  const bands = [{ year: a.year, col: CHART.green, label: 'A ' + a.year }, { year: b.year, col: CHART.steel, label: 'B ' + b.year }]
    .filter((bd) => bd.year >= start);
  const tcol = CMP_TYPE[type].color;

  return (
    <svg viewBox={'0 0 ' + W + ' ' + H} width="100%" style={{ display: 'block', fontFamily: "'Open Sans',sans-serif" }}
      onMouseLeave={() => setHover(null)}
      onMouseMove={(e) => {
        const r = e.currentTarget.getBoundingClientRect();
        const mx = ((e.clientX - r.left) / r.width) * W;
        const i = Math.round(((mx - padL) / iw) * (years.length - 1));
        if (i >= 0 && i < years.length) setHover(i);
      }}>
      {/* comparison bands — merge visually when adjacent; de-overlap labels */}
      {(() => {
        const info = bands.map((bd) => ({ ...bd, cx: x(years.indexOf(bd.year)) }));
        const lx = info.map((b) => b.cx);
        const minX = padL + 28, maxX = W - padR - 28;
        if (info.length === 2 && Math.abs(lx[0] - lx[1]) < 58) {
          let mid = Math.min(Math.max((lx[0] + lx[1]) / 2, minX + 29), maxX - 29);
          const lo = lx[0] <= lx[1] ? 0 : 1; // keep earlier-year label on the left
          lx[lo] = mid - 29; lx[1 - lo] = mid + 29;
        }
        const clampX = (v) => Math.min(Math.max(v, minX), maxX);
        return info.map((bd, k) => (
          <g key={bd.label}>
            <rect x={bd.cx - halfStep} y={padT} width={halfStep * 2} height={ih} fill="#E15A78" opacity="0.10" />
            <line x1={bd.cx} x2={bd.cx} y1={padT} y2={padT + ih} stroke={bd.col} strokeWidth="1.2" strokeDasharray="4 3" opacity="0.55" />
            {Math.abs(clampX(lx[k]) - bd.cx) > 1 && <line x1={bd.cx} x2={clampX(lx[k])} y1={padT - 3} y2={padT - 11} stroke={bd.col} strokeWidth="1" opacity="0.5" />}
            <g transform={'translate(' + clampX(lx[k]) + ',' + (padT - 6) + ')'}>
              <rect x="-27" y="-13" width="54" height="17" rx="4" fill={bd.col} />
              <text x="0" y="-1" textAnchor="middle" fontSize="10.5" fontWeight="700" fill="#fff">{bd.label}</text>
            </g>
          </g>
        ));
      })()}
      {/* gridlines */}
      {grid.map((v, i) => (
        <g key={i}>
          <line x1={padL} x2={W - padR} y1={y(v)} y2={y(v)} stroke={CHART.grid} strokeWidth="1" />
          <text x={padL - 10} y={y(v) + 4} textAnchor="end" fontSize="11" fill="#969696">{fmt(Math.round(v))}</text>
        </g>
      ))}
      {/* x labels */}
      {years.map((yr, i) => ((i % everyN === 0 || i === years.length - 1) &&
        <text key={yr} x={x(i)} y={H - 12} textAnchor="middle" fontSize="10.5" fill="#969696">{yr}</text>
      ))}
      {/* area + line */}
      <path d={area} fill={tcol} opacity="0.09" />
      <path d={d} fill="none" stroke={tcol} strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" />
      {/* year markers */}
      {pts.map((p, i) => {
        const isBand = bands.some((bd) => years.indexOf(bd.year) === i);
        return <circle key={i} cx={p[0]} cy={p[1]} r={isBand ? 4.5 : 2.4} fill={isBand ? tcol : '#fff'} stroke={tcol} strokeWidth="1.8" />;
      })}
      {/* hover */}
      {hover != null && (
        <g>
          <circle cx={x(hover)} cy={y(series[hover])} r="5" fill="#fff" stroke={tcol} strokeWidth="2.6" />
          <g transform={'translate(' + Math.min(Math.max(x(hover), padL + 46), W - padR - 46) + ',' + (padT + 4) + ')'}>
            <rect x="-44" y="-2" width="88" height="38" rx="4" fill={CHART.ink} />
            <text x="0" y="12" textAnchor="middle" fontSize="10" fill="#9AA39A">{years[hover]}</text>
            <text x="0" y="28" textAnchor="middle" fontSize="13.5" fontWeight="700" fill="#fff">{fmt(series[hover])}</text>
          </g>
        </g>
      )}
    </svg>
  );
}

/* ---------- Screen ---------- */
function Compare({ setView }) {
  const [a, setA] = useCmp({ year: 2005, month: 1 });
  const [b, setB] = useCmp({ year: 2010, month: 1 });
  const [active, setActive] = useCmp('A');
  const [range, setRange] = useCmp('30');
  const [metric, setMetric] = useCmp('price');
  const [type, setType] = useCmp('det');
  const [muni, setMuni] = useCmp(null);

  return (
    <div className="fade-up" style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <PageHead eyebrow="Price Compare" title="价格走势对比"
        sub="选两个时间点，按区域与房型对比 GTA 均价与成交。"
        actions={<>
          <button className="btn btn-ghost"><Icon name="download" size={15} />导出 PNG</button>
          <button className="btn btn-primary" onClick={() => setView && setView('live')}><Icon name="play" size={14} />加入直播</button>
        </>} />

      <PriceMap muni={muni} setMuni={setMuni} />
      <MuniPills muni={muni} setMuni={setMuni} />

      <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1.05fr) minmax(0, 1fr)', gap: 16, alignItems: 'start' }}>
        <PeriodPicker a={a} b={b} setA={setA} setB={setB} active={active} setActive={setActive}
          range={range} setRange={setRange} metric={metric} setMetric={setMetric} />
        <CompareTable a={a} b={b} muni={muni} />
      </div>

      <div className="card" style={{ padding: 'var(--card-pad)' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12, flexWrap: 'wrap', gap: 10 }}>
          <div>
            <h3 style={{ margin: 0, fontSize: 16, fontWeight: 700 }}>
              {metric === 'price' ? '均价走势' : '成交走势'} <span style={{ color: 'var(--ink-3)', fontWeight: 600, fontSize: 13 }}>· {CMP_TYPE[type].zh} · {start1996Label(range)}</span>
            </h3>
            <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 3 }}>{muni ? muni.zh : 'GTA 全域'} · 粉色带为对比期 · Source: TRREB Market Watch（示例数据）</div>
          </div>
          <CmpSeg options={CMP_TYPES.map((t) => ({ v: t.key, label: t.zh }))} value={type} onChange={setType} />
        </div>
        <TrendChart a={a} b={b} range={range} metric={metric} type={type} muni={muni} />
      </div>
    </div>
  );
}
function start1996Label(range) {
  const span = range === '5' ? 5 : range === '10' ? 10 : 30;
  return (CMP_Y1 - span) + '–' + CMP_Y1;
}

window.Compare = Compare;
