// 院校详情页 — Wave 7 / B1 / Wave 10-C
// Route: /#/gaokao/school/:id?province=HI&year=2025&score=658&rank=4217
// Data: window.PB_SCHOOLS (metadata) + window.PB_CUTOFFS (cutoff data)
//        + per-major data from /data/admissions-majors/<province>/<year>/<code>.json
//        + /data/schools/meta.json (院校元数据 + 学科评估)
//        + /data/employment/<province>/<year>.json (就业数据)
//        + /data/admissions-plan/<province>/<year>.json (招生计划)
//        + /data/admission-rules/<province>/<year>.json (招生章程 + 限制)
// No new API endpoints — all client-side.

// ─── Wave 10-C — browser-side loaders (mirroring page-gaokao.jsx patterns) ──

// School metadata + discipline rank loader
// Fetches /data/schools/meta.json and /data/schools/discipline-rank.json once,
// builds code-indexed maps for sync lookup.
// Returns a promise that resolves when the cache is populated (handles concurrent calls).
const _META_CACHE = { status: 'idle', byCode: null, rankByCode: null, _promise: null };
function _loadSchoolMetaForBrowser() {
  if (_META_CACHE.status === 'loaded' || _META_CACHE.status === 'error') {
    return Promise.resolve();
  }
  if (_META_CACHE._promise) return _META_CACHE._promise;
  _META_CACHE.status = 'loading';
  _META_CACHE._promise = Promise.all([
    fetch('/data/schools/meta.json').then(r => r.ok ? r.json() : { schools: [] }),
    fetch('/data/schools/discipline-rank.json').then(r => r.ok ? r.json() : { schools: {} }),
  ]).then(([metaJson, rankJson]) => {
    const byCode = {};
    for (const s of (metaJson.schools || [])) {
      if (s && s.code) byCode[String(s.code)] = s;
    }
    const rankByCode = {};
    const rankSchools = rankJson.schools || {};
    for (const code of Object.keys(rankSchools)) {
      const e = rankSchools[code];
      if (e && Array.isArray(e.ranks)) rankByCode[String(code)] = e.ranks;
    }
    // Seed from meta disciplineRank as fallback
    for (const s of (metaJson.schools || [])) {
      if (s && s.code && !rankByCode[String(s.code)] &&
          Array.isArray(s.disciplineRank) && s.disciplineRank.length) {
        rankByCode[String(s.code)] = s.disciplineRank;
      }
    }
    _META_CACHE.byCode = byCode;
    _META_CACHE.rankByCode = rankByCode;
    _META_CACHE.status = 'loaded';
  }).catch(() => {
    _META_CACHE.status = 'error';
    _META_CACHE.byCode = {};
    _META_CACHE.rankByCode = {};
  });
  return _META_CACHE._promise;
}

// 双一流 loader — fetches /data/shuangyiliu.json once, keyed by school name
const _SYL_CACHE = { status: 'idle', schools: null, _promise: null };
function _loadShuangyiliuForBrowser() {
  if (_SYL_CACHE.status === 'loaded' || _SYL_CACHE.status === 'error') {
    return Promise.resolve();
  }
  if (_SYL_CACHE._promise) return _SYL_CACHE._promise;
  _SYL_CACHE.status = 'loading';
  _SYL_CACHE._promise = fetch('/data/shuangyiliu.json')
    .then(r => r.ok ? r.json() : null)
    .then(json => {
      _SYL_CACHE.schools = (json && json.schools) ? json.schools : {};
      _SYL_CACHE.status = 'loaded';
    })
    .catch(() => {
      _SYL_CACHE.schools = {};
      _SYL_CACHE.status = 'error';
    });
  return _SYL_CACHE._promise;
}

// Employment loader — mirrors page-gaokao.jsx _loadEmploymentForBrowser
const _SCHOOL_EMPLOYMENT_CACHE = {};
async function _loadSchoolEmploymentForBrowser(province, year) {
  const key = `${province}-${year}`;
  if (_SCHOOL_EMPLOYMENT_CACHE[key] !== undefined) return _SCHOOL_EMPLOYMENT_CACHE[key];
  try {
    const res = await fetch(`/data/employment/${province}/${year}.json`);
    if (!res.ok) { _SCHOOL_EMPLOYMENT_CACHE[key] = null; return null; }
    const data = await res.json();
    const byCode = {};
    const byName = {};
    for (const s of (data.schools || [])) {
      if (!s || !s.schoolCode) continue;
      const code = String(s.schoolCode);
      if (!byCode[code]) byCode[code] = s;
      if (s.schoolName && !byName[s.schoolName]) byName[s.schoolName] = s;
    }
    _SCHOOL_EMPLOYMENT_CACHE[key] = { byCode, byName };
    return _SCHOOL_EMPLOYMENT_CACHE[key];
  } catch (_e) {
    _SCHOOL_EMPLOYMENT_CACHE[key] = null;
    return null;
  }
}

// Plan totals loader — mirrors page-gaokao.jsx _loadPlanTotalsForBrowser
const _SCHOOL_PLAN_CACHE = {};
async function _loadSchoolPlanTotalsForBrowser(province, year) {
  const key = `${province}-${year}`;
  if (_SCHOOL_PLAN_CACHE[key] !== undefined) return _SCHOOL_PLAN_CACHE[key];
  try {
    const res = await fetch(`/data/admissions-plan/${province}/${year}.json`);
    if (!res.ok) { _SCHOOL_PLAN_CACHE[key] = null; return null; }
    const data = await res.json();
    const totals = {};
    for (const r of (data.rows || [])) {
      if (!r || !r.schoolCode || typeof r.plan !== 'number' || r.plan <= 0) continue;
      const code = String(r.schoolCode);
      totals[code] = (totals[code] || 0) + r.plan;
    }
    _SCHOOL_PLAN_CACHE[key] = totals;
    return totals;
  } catch (_e) {
    _SCHOOL_PLAN_CACHE[key] = null;
    return null;
  }
}

// Admission-rules loader — mirrors page-gaokao.jsx _loadAdmissionRulesForBrowser
const _SCHOOL_RULES_CACHE = {};
async function _loadSchoolAdmissionRulesForBrowser(province, year) {
  const key = `${province}-${year}`;
  if (_SCHOOL_RULES_CACHE[key] !== undefined) return _SCHOOL_RULES_CACHE[key];
  // QA 2026-06-10 — 与 page-gaokao.jsx 同步：章程数据滞后于估分年时按年回退，
  // 避免 2025/2026 年份下报考限制信息整体消失。
  const y0 = Number(year) || 2025;
  let data = null;
  let usedYear = null;
  for (const y of [y0, y0 - 1, y0 - 2]) {
    try {
      const res = await fetch(`/data/admission-rules/${province}/${y}.json`);
      if (!res.ok) continue;
      data = await res.json();
      usedYear = y;
      break;
    } catch (_e) { /* try the next year down */ }
  }
  if (!data) { _SCHOOL_RULES_CACHE[key] = null; return null; }
  const byCode = {};
  const byName = {};
  const rows = Array.isArray(data) ? data : (data.rows || []);
  for (const s of rows) {
    if (!s || !s.schoolCode) continue;
    const code = String(s.schoolCode);
    if (!byCode[code]) byCode[code] = s;
    if (s.schoolName && !byName[s.schoolName]) byName[s.schoolName] = s;
  }
  _SCHOOL_RULES_CACHE[key] = { byCode, byName, _sourceYear: usedYear };
  return _SCHOOL_RULES_CACHE[key];
}

// ── Wave 11-D: Campus-life loader ───────────────────────────────────────────
const _SCHOOL_CAMPUS_LIFE_CACHE = {};
async function _loadCampusLifeForBrowser(province, year) {
  const key = `${province}-${year}`;
  if (_SCHOOL_CAMPUS_LIFE_CACHE[key] !== undefined) return _SCHOOL_CAMPUS_LIFE_CACHE[key];
  try {
    const res = await fetch(`/data/schools/campus-life/${province}/${year}.json`);
    if (!res.ok) { _SCHOOL_CAMPUS_LIFE_CACHE[key] = null; return null; }
    const data = await res.json();
    const byCode = {};
    const byName = {};
    for (const s of (data.schools || [])) {
      if (!s || !s.schoolCode) continue;
      const code = String(s.schoolCode);
      if (!byCode[code]) byCode[code] = s;
      if (s.schoolName && !byName[s.schoolName]) byName[s.schoolName] = s;
    }
    _SCHOOL_CAMPUS_LIFE_CACHE[key] = { byCode, byName };
    return _SCHOOL_CAMPUS_LIFE_CACHE[key];
  } catch (_e) {
    _SCHOOL_CAMPUS_LIFE_CACHE[key] = null;
    return null;
  }
}

// ── Wave 11-D: Awards loader ─────────────────────────────────────────────────
const _SCHOOL_AWARDS_CACHE = {};
async function _loadAwardsForBrowser(province, year) {
  const key = `${province}-${year}`;
  if (_SCHOOL_AWARDS_CACHE[key] !== undefined) return _SCHOOL_AWARDS_CACHE[key];
  try {
    const res = await fetch(`/data/schools/awards/${province}/${year}.json`);
    if (!res.ok) { _SCHOOL_AWARDS_CACHE[key] = null; return null; }
    const data = await res.json();
    const byCode = {};
    const byName = {};
    for (const s of (data.schools || [])) {
      if (!s || !s.schoolCode) continue;
      const code = String(s.schoolCode);
      if (!byCode[code]) byCode[code] = s;
      if (s.schoolName && !byName[s.schoolName]) byName[s.schoolName] = s;
    }
    _SCHOOL_AWARDS_CACHE[key] = { byCode, byName };
    return _SCHOOL_AWARDS_CACHE[key];
  } catch (_e) {
    _SCHOOL_AWARDS_CACHE[key] = null;
    return null;
  }
}

// 历年志愿失误警示 (翻车案例) is seed/derived data (off HI's score scale, no
// provenance), not real verified cases — hidden until real data is sourced.
// Flip to a per-province check to re-enable. (#data-authenticity, 2026-05)
const MISTAKES_SECTION_ENABLED = false;

// ── Wave 15-C: Gaokao-mistakes loader ───────────────────────────────────────
const _MISTAKES_CACHE = {};
async function _loadMistakesForBrowser(province, year) {
  const key = `${province}-${year}`;
  if (_MISTAKES_CACHE[key] !== undefined) return _MISTAKES_CACHE[key];
  try {
    const res = await fetch(`/data/gaokao-mistakes/${province}/${year}.json`);
    if (!res.ok) { _MISTAKES_CACHE[key] = null; return null; }
    const data = await res.json();
    const cases = Array.isArray(data.cases) ? data.cases : [];
    const bySchoolCode = {};
    for (const c of cases) {
      if (!c || !c.schoolCode) continue;
      const code = String(c.schoolCode);
      if (!bySchoolCode[code]) bySchoolCode[code] = [];
      bySchoolCode[code].push(c);
    }
    _MISTAKES_CACHE[key] = { cases, bySchoolCode };
    return _MISTAKES_CACHE[key];
  } catch (_e) {
    _MISTAKES_CACHE[key] = null;
    return null;
  }
}

// ── Wave 11-D: Academic-policy loader ───────────────────────────────────────
const _SCHOOL_ACADEMIC_POLICY_CACHE = {};
async function _loadAcademicPolicyForBrowser(province, year) {
  const key = `${province}-${year}`;
  if (_SCHOOL_ACADEMIC_POLICY_CACHE[key] !== undefined) return _SCHOOL_ACADEMIC_POLICY_CACHE[key];
  try {
    const res = await fetch(`/data/schools/academic-policy/${province}/${year}.json`);
    if (!res.ok) { _SCHOOL_ACADEMIC_POLICY_CACHE[key] = null; return null; }
    const data = await res.json();
    const byCode = {};
    const byName = {};
    for (const s of (data.schools || [])) {
      if (!s || !s.schoolCode) continue;
      const code = String(s.schoolCode);
      if (!byCode[code]) byCode[code] = s;
      if (s.schoolName && !byName[s.schoolName]) byName[s.schoolName] = s;
    }
    _SCHOOL_ACADEMIC_POLICY_CACHE[key] = { byCode, byName };
    return _SCHOOL_ACADEMIC_POLICY_CACHE[key];
  } catch (_e) {
    _SCHOOL_ACADEMIC_POLICY_CACHE[key] = null;
    return null;
  }
}

// ── PB_SCHOOLS string id → province-specific school code map ───────────────
// Used to fetch /data/admissions-majors/<province>/<year>/<code>.json from the
// school detail page. Currently only HI is wired (W4-A H3-C 50-school sample).
// Other provinces fall back to the string id, matching legacy behavior.
const PB_SCHOOL_CODE_MAP = {
  HI: {
    pku: '0001',       ruc: '0002',       tsinghua: '0003',  beihang: '0006',
    bit: '0007',       bupt: '0013',      fudan: '0699',     sjtu: '0701',
    tongji: '0700',    nju: '0772',       ustc: '1056',      zju: '0944',
    hit: '0618',       xjtu: '2642',      whu: '1715',       hust: '1716',
    sysu: '1996',      scut: '1999',      xidian: '2645',
    // cee (华北电力大学) not yet in HI sample; falls through to string id fetch
  },
};

// ── 院校详情页 overhaul: cn-school-ref profile-detail + basic-card wiring ──────
// Resolves ANY school → its rich profile-detail (data/cn-school-ref/profile-detail
// /<code>.json) by NORMALIZED NAME, bridging the meta↔profile code divergence
// (the two schemes only share ~444 codes). A basic info card (province/city/level/
// nature/985-211-双一流) covers every national university even with no full profile.
// Normalizer mirrors scripts/data-fixes/enrich-national-tags.js norm() — keep in sync.
const _normSchoolName = (s) => String(s || '')
  .replace(/[()（）]/g, '')
  .replace(/(医学部|分校|[^一-龥]?校区|威海|深圳|盘锦|苏州|沙河|宣城|荣昌|保定|珠海)$/, '')
  .trim();

const _BASIC_CARDS_CACHE = { status: 'idle', byName: null, byCode: null, _promise: null };
function _loadSchoolBasicCards() {
  if (_BASIC_CARDS_CACHE.status === 'loaded' || _BASIC_CARDS_CACHE.status === 'error') {
    return Promise.resolve(_BASIC_CARDS_CACHE);
  }
  if (_BASIC_CARDS_CACHE._promise) return _BASIC_CARDS_CACHE._promise;
  _BASIC_CARDS_CACHE.status = 'loading';
  _BASIC_CARDS_CACHE._promise = fetch('/data/cn-school-ref/school-basic-cards.json')
    .then((r) => (r.ok ? r.json() : { byName: {} }))
    .then((j) => {
      const byName = (j && j.byName) || {};
      const byCode = {};
      for (const k of Object.keys(byName)) {
        const c = byName[k];
        if (!c) continue;
        if (c.profileCode) byCode[String(c.profileCode)] = c;
        if (c.metaCode) byCode[String(c.metaCode)] = c;
      }
      _BASIC_CARDS_CACHE.byName = byName;
      _BASIC_CARDS_CACHE.byCode = byCode;
      _BASIC_CARDS_CACHE.status = 'loaded';
      return _BASIC_CARDS_CACHE;
    })
    .catch(() => {
      _BASIC_CARDS_CACHE.byName = {};
      _BASIC_CARDS_CACHE.byCode = {};
      _BASIC_CARDS_CACHE.status = 'error';
      return _BASIC_CARDS_CACHE;
    });
  return _BASIC_CARDS_CACHE._promise;
}

const _PROFILE_DETAIL_CACHE = {};
function _loadProfileDetail(code) {
  if (!code) return Promise.resolve(null);
  const key = String(code);
  if (Object.prototype.hasOwnProperty.call(_PROFILE_DETAIL_CACHE, key)) {
    return Promise.resolve(_PROFILE_DETAIL_CACHE[key]);
  }
  return fetch(`/data/cn-school-ref/profile-detail/${key}.json`)
    .then((r) => (r.ok ? r.json() : null))
    .then((j) => { _PROFILE_DETAIL_CACHE[key] = j; return j; })
    .catch(() => { _PROFILE_DETAIL_CACHE[key] = null; return null; });
}

const _HI_ADMISSIONS_CACHE = {};
function _loadHiAdmissionsForBrowser(year) {
  const y = String(year || '');
  if (!/^\d{4}$/.test(y)) return Promise.resolve([]);
  if (_HI_ADMISSIONS_CACHE[y]) return _HI_ADMISSIONS_CACHE[y];
  _HI_ADMISSIONS_CACHE[y] = fetch(`/data/admissions/HI/${y}-3plus3.json`)
    .then((r) => (r.ok ? r.json() : null))
    .then((j) => (j && Array.isArray(j.schools)) ? j.schools : [])
    .catch(() => []);
  return _HI_ADMISSIONS_CACHE[y];
}

async function _resolveHiSchoolCodeForBrowser(year, school) {
  const rawId = String((school && school.id) || '');
  const nameZh = school && school.name && school.name.zh;
  const schools = await _loadHiAdmissionsForBrowser(year);
  const normName = _normSchoolName(nameZh);
  const entry = schools.find((s) => s && s.name === nameZh)
    || (normName ? schools.find((s) => s && _normSchoolName(s.name) === normName) : null)
    || (/^\d{4}$/.test(rawId) ? schools.find((s) => s && s.code === rawId) : null);
  return entry && entry.code ? String(entry.code) : null;
}

const GaokaoSchoolDetailPage = () => {
  const lang = useLang();

  // ── Parse URL: extract schoolId from path + query params ────────────────────
  const parseUrl = () => {
    const h = (window.location.hash || '');
    const qi = h.indexOf('?');
    const path = qi >= 0 ? h.slice(0, qi) : h;
    const qs   = qi >= 0 ? new URLSearchParams(h.slice(qi + 1)) : new URLSearchParams();
    // path: #/gaokao/school/<id>
    const m = path.match(/\/gaokao\/school\/([^/?#]+)/);
    return {
      schoolId: m ? m[1] : null,
      province: qs.get('province') || 'HI',
      year:     parseInt(qs.get('year')  || '2025', 10),
      score:    parseInt(qs.get('score') || '0',    10),
      rank:     parseInt(qs.get('rank')  || '0',    10),
    };
  };

  const [params, setParams] = React.useState(parseUrl);

  React.useEffect(() => {
    const onHash = () => setParams(parseUrl());
    window.addEventListener('hashchange', onHash);
    return () => window.removeEventListener('hashchange', onHash);
  }, []);

  // ── 院校详情页 overhaul: resolve rich profile-detail + basic card (async) ─────
  // MUST run on every render (placed before any conditional return) so hook order
  // stays stable. Resolves the school id/slug/code/name → profile-detail by name,
  // and a basic info card so even profile-less national schools render real content.
  const [resolved, setResolved] = React.useState({ status: 'loading', card: null, profileCode: null, profile: null });
  React.useEffect(() => {
    let cancelled = false;
    let id = params.schoolId;
    if (!id) { setResolved({ status: 'ready', card: null, profileCode: null, profile: null }); return undefined; }
    try { id = decodeURIComponent(id); } catch (e) { /* keep raw id (e.g. malformed %) */ }
    setResolved((prev) => ({ ...prev, status: 'loading' }));
    (async () => {
      const cards = await _loadSchoolBasicCards();
      const SCH = (typeof window !== 'undefined' && window.PB_SCHOOLS) || [];
      const MET = (typeof window !== 'undefined' && window.PB_META_SCHOOLS && window.PB_META_SCHOOLS.schools) || [];
      const legacy = SCH.find((s) => s.id === id);
      const metaHit = MET.find((s) => s.code === id);
      const nameZh = (legacy && legacy.name && legacy.name.zh) || (metaHit && metaHit.nameZh) || null;
      let card = null;
      if (nameZh) card = (cards.byName || {})[_normSchoolName(nameZh)] || null;
      if (!card) card = (cards.byCode || {})[id] || null;
      if (!card && !legacy && !metaHit) card = (cards.byName || {})[_normSchoolName(id)] || null;
      const profileCode = (card && card.profileCode) || (/^\d{3,5}$/.test(id) ? id : null);
      let profile = null;
      if (profileCode) profile = await _loadProfileDetail(profileCode);
      if (cancelled) return;
      setResolved({
        status: 'ready',
        card,
        profileCode: profile ? profileCode : ((card && card.profileCode) || null),
        profile,
      });
    })().catch(() => { if (!cancelled) setResolved({ status: 'ready', card: null, profileCode: null, profile: null }); });
    return () => { cancelled = true; };
  }, [params.schoolId]);

  // Redirect to the search page when there's no schoolId. Registered
  // unconditionally (NOT inside an `if`) so the parent's hook order is stable
  // across renders — see SchoolDetailBody note below re: the hooks-rule fix.
  React.useEffect(() => {
    if (!params.schoolId) window.location.hash = '#/gaokao/search';
  }, [params.schoolId]);

  // ── Data shims ───────────────────────────────────────────────────────────────
  const SCHOOLS = window.PB_SCHOOLS || [];
  const CUTOFFS = window.PB_CUTOFFS || {};

  // B4 fix v2 (Wave 22 hotfix): sync lookup from pre-loaded window.PB_META_SCHOOLS
  // (loaded by inline script in index.html before React mounts). Avoids the
  // hooks-rule violation that arose from doing useState+useEffect here while
  // downstream useMemo/useState hooks would only run when school resolved.
  const META = (typeof window !== 'undefined' && window.PB_META_SCHOOLS) || null;
  const legacyHit = SCHOOLS.find(s => s.id === params.schoolId);
  const metaHit = META && META.schools && META.schools.find(s => s.code === params.schoolId);
  // Basic-card fallback (resolved async): lets any national university — including
  // those absent from PB_SCHOOLS and meta.json — still render a real detail page.
  const basicCard = resolved.card;
  const cardTier = basicCard
    ? ((basicCard.tags || []).includes('985') ? '985'
      : ((basicCard.tags || []).includes('211') ? '211' : ''))
    : '';
  const school = legacyHit || (metaHit ? {
    id:       metaHit.code,
    name:     { zh: metaHit.nameZh, en: metaHit.nameEn || metaHit.nameZh },
    location: { zh: metaHit.location || '', en: metaHit.location || '' },
    tier:     metaHit.tier || '',
    tags:     metaHit.tags || [],
    type:     metaHit.schoolType || '',
  } : (basicCard ? {
    id:       params.schoolId,
    name:     { zh: basicCard.nameZh, en: basicCard.nameZh },
    location: { zh: basicCard.city || basicCard.province || '', en: basicCard.city || basicCard.province || '' },
    tier:     cardTier,
    tags:     basicCard.tags || [],
    type:     basicCard.nature || '',
  } : null));

  // ── Redirect: no id → search (effect registered unconditionally above) ──────
  if (!params.schoolId) return null;

  // ── Resolving state ──────────────────────────────────────────────────────────
  // While the basic-card / profile lookup is still in flight, show a loading shell
  // rather than the "not found" page (which would flash for profile-only schools).
  if (!school && resolved.status === 'loading') {
    return (
      <div className="pb-scroll" data-testid="school-detail-page">
        <PBNav active="gaokao" />
        <section style={{ padding: '120px 48px', textAlign: 'center' }} data-testid="school-detail-loading">
          <div style={{ fontSize: 40, marginBottom: 16 }}>🏫</div>
          <div style={{ fontSize: 15, color: 'var(--pb-fg-muted)' }}>
            {lang === 'zh' ? '正在加载院校信息…' : 'Loading school information…'}
          </div>
        </section>
        <PBFooter />
      </div>
    );
  }

  // ── Not found state ──────────────────────────────────────────────────────────
  if (!school) {
    return (
      <div className="pb-scroll" data-testid="school-detail-page">
        <PBNav active="gaokao" />
        <section style={{ padding: '120px 48px', textAlign: 'center' }}>
          <div style={{ fontSize: 48, marginBottom: 16 }}>🏫</div>
          <div style={{ fontFamily: 'var(--pb-serif)', fontSize: 24, fontWeight: 600, marginBottom: 12 }}>
            {lang === 'zh' ? '该院校暂无数据' : 'No data for this school'}
          </div>
          <p style={{ fontSize: 15, color: 'var(--pb-fg-muted)', marginBottom: 24 }}>
            {lang === 'zh'
              ? `院校 "${params.schoolId}" 暂未收录，请返回查校页搜索。`
              : `School "${params.schoolId}" not found. Return to search.`}
          </p>
          <button
            className="pb-btn pb-btn-primary"
            onClick={() => { window.location.hash = '#/gaokao'; }}
          >
            {lang === 'zh' ? '返回测算页' : 'Back to Calculator'}
          </button>
        </section>
        <PBFooter />
      </div>
    );
  }

  // school resolved → render the body. All school-dependent hooks live in
  // SchoolDetailBody so they only mount once `school` is non-null, keeping a
  // stable hook order (fixes "Rendered more hooks than during the previous
  // render" crash on async-resolved schools, e.g. 海南大学).
  return (
    <SchoolDetailBody school={school} params={params} resolved={resolved} lang={lang} />
  );
};

// ─────────────────────────────────────────────────────────────────────────────
// SchoolDetailBody — holds every school-dependent hook + the full detail render.
// Mounted by GaokaoSchoolDetailPage only after `school` resolves, so its hook
// order never changes between renders (no conditional / early-return-before-hook).
// ─────────────────────────────────────────────────────────────────────────────
const SchoolDetailBody = ({ school, params, resolved, lang }) => {
  const SCHOOLS = (typeof window !== 'undefined' && window.PB_SCHOOLS) || [];
  const CUTOFFS = (typeof window !== 'undefined' && window.PB_CUTOFFS) || {};

  // ── Province / year constants ────────────────────────────────────────────────
  const PROVINCE_LIST = [
    { code: 'HI', zh: '海南',  en: 'Hainan'    },
    { code: 'BJ', zh: '北京',  en: 'Beijing'   },
    { code: 'SH', zh: '上海',  en: 'Shanghai'  },
    { code: 'GD', zh: '广东',  en: 'Guangdong' },
    { code: 'JS', zh: '江苏',  en: 'Jiangsu'   },
    { code: 'ZJ', zh: '浙江',  en: 'Zhejiang'  },
    { code: 'SD', zh: '山东',  en: 'Shandong'  },
  ];
  const YEARS = [2025, 2024, 2023, 2022];

  // ── Probability calc (same formula as compare/search pages) ─────────────────
  const getProb = (schoolId, province, year, userScore, userRank) => {
    const provCutoffs = (CUTOFFS[province] || {})[schoolId];
    if (!provCutoffs) return null;
    const yearData = provCutoffs[String(year)] || provCutoffs[String(year - 1)];
    if (!yearData) return null;
    const cutoffScore = yearData[0];
    const cutoffRank  = yearData[1];

    // #338 — prefer the unified 位次法 Gaussian model (same source of truth as the
    // main results table + 30-slot plan) when the user's 位次 and the school's
    // cutoff-rank history are available. Falls back to 线差法 (score delta) only
    // when rank data is missing — one product-wide probability story.
    const AM = (typeof window !== 'undefined' && window.PBAdmissionModel) || null;
    if (AM && userRank > 0 && cutoffRank > 0) {
      const yrs = Object.keys(provCutoffs).filter((k) => /^\d{4}$/.test(k)).sort();
      const cutoffRanksByYear = yrs.map((k) => (provCutoffs[k] && provCutoffs[k][1] != null) ? provCutoffs[k][1] : null);
      const A = AM.assess({ userRank: userRank, cutoffRanksByYear: cutoffRanksByYear.length ? cutoffRanksByYear : [cutoffRank] });
      if (A) return { prob: A.probability, bucket: A.bucket, cutoffScore, cutoffRank };
    }
    // Fallback: 线差法 (score delta) — only when no rank data.
    const delta = userScore - cutoffScore;
    let prob;
    if (delta >= 20)  prob = Math.min(98, 85 + delta * 0.5);
    else if (delta >= 0)   prob = Math.round(70 + delta * 0.75);
    else if (delta >= -20) prob = Math.max(20, 60 + delta * 2);
    else if (delta >= -40) prob = Math.max(5,  30 + delta * 1.5);
    else prob = 5;
    const bucket = delta >= 0 ? (delta >= 20 ? 'safety' : 'match') : (delta >= -20 ? 'match' : 'reach');
    return { prob: Math.round(prob), bucket, cutoffScore, cutoffRank };
  };

  const bucketColor = { safety: 'var(--pb-success)', match: 'var(--pb-navy-700)', reach: 'var(--pb-danger)' };
  const bucketLabel = {
    safety: lang === 'zh' ? '保底' : 'Safety',
    match:  lang === 'zh' ? '匹配' : 'Match',
    reach:  lang === 'zh' ? '冲刺' : 'Reach',
  };
  // #338 — 5-tier 搏/冲/稳/保/垫 display relabel from a probability. getProb already
  // returns a unified-model probability; this only picks a finer chip. Keys match
  // window.PBAdmissionModel.fineBucket.
  const fineColorMap = { bo: '#b91c1c', chong: 'var(--pb-danger)', wen: 'var(--pb-navy-700)', bao: 'var(--pb-success)', dian: '#047857' };
  const fineLabelMap = lang === 'zh'
    ? { bo: '搏', chong: '冲', wen: '稳', bao: '保', dian: '垫' }
    : { bo: 'Gamble', chong: 'Reach', wen: 'Match', bao: 'Safety', dian: 'Floor' };
  const fineKeyFor = (prob) => {
    const M = (typeof window !== 'undefined' && window.PBAdmissionModel) || null;
    const p = (typeof prob === 'number' ? prob : 50) / 100;
    return (M && M.fineBucket) ? M.fineBucket(p) : 'wen';
  };

  const tierColors = {
    '985-A': { bg: '#fef3c7', color: '#92400e' },
    '985':   { bg: '#dbeafe', color: '#1e40af' },
    '211':   { bg: '#d1fae5', color: '#065f46' },
  };

  // ── Current province cutoff data ─────────────────────────────────────────────
  const currentCutoff = ((CUTOFFS[params.province] || {})[school.id] || {})[String(params.year)];
  const probResult = params.score > 0
    ? getProb(school.id, params.province, params.year, params.score, params.rank)
    : null;

  // ── Trend data (BJ cutoff for 2022/2023/2024) ────────────────────────────────
  const trendData = React.useMemo(() => {
    const provData = (CUTOFFS[params.province] || {})[school.id] || {};
    return YEARS.map(y => {
      const d = provData[String(y)];
      return d ? { year: y, score: d[0], rank: d[1] } : null;
    }).filter(Boolean).reverse(); // oldest to newest
  }, [params.province, school.id]);

  // ── HI real admissions (issue #296 item 3) ──────────────────────────────────
  // PB_CUTOFFS is a ~50-school sample keyed by internal id; name-resolved HI
  // schools (school.id === Chinese name) are absent from it, so 投档分/位次/趋势
  // showed "数据不足". Real HI data lives in /data/admissions/HI/<year>-3plus3.json
  // keyed by school name. Match by EXACT name (entry.name === school.name.zh) and
  // use minScore/minRank (the entry bar — NEVER maxScore). Load it for every
  // province so the sidebar can accurately enable 海南 even when viewing BJ/SH.
  const hiYears = [2025, 2024, 2023, 2022, 2021, 2020];
  const [hiAdmit, setHiAdmit] = React.useState({ status: 'idle', byYear: {}, found: false, source: null });
  const _hiSchoolName = school.name && school.name.zh;

  React.useEffect(() => {
    let cancelled = false;
    setHiAdmit({ status: 'loading', byYear: {}, found: false, source: null });
    (async () => {
      const byYear = {};
      let source = null;
      for (const y of hiYears) {
        try {
          const res = await fetch(`/data/admissions/HI/${y}-3plus3.json`);
          if (!res.ok) continue;
          const json = await res.json();
          const schools = (json && Array.isArray(json.schools)) ? json.schools : [];
          const entry = schools.find(s => s && s.name === _hiSchoolName);
          if (entry && typeof entry.minScore === 'number') {
            byYear[y] = {
              minScore: entry.minScore, maxScore: entry.maxScore,
              minRank: entry.minRank, maxRank: entry.maxRank,
              numGroups: entry.numGroups,
            };
          }
          if (y === 2025 && json && json.source) source = json.source;
        } catch (_e) { /* skip this year, keep others */ }
      }
      if (cancelled) return;
      setHiAdmit({ status: 'ready', byYear, found: Object.keys(byYear).length > 0, source });
    })().catch(() => { if (!cancelled) setHiAdmit({ status: 'ready', byYear: {}, found: false, source: null }); });
    return () => { cancelled = true; };
  }, [school.id, _hiSchoolName]);

  // ── Effective cutoff / trend (prefer real HI data over PB_CUTOFFS sample) ────
  // Shapes are kept byte-identical to the originals so all downstream JSX works
  // unchanged: hiCutoff is [score, rank] like currentCutoff; hiTrend is
  // [{year,score,rank}] like trendData. Non-HI or unmatched → null → fallback.
  // A HI school has many 院校专业组, each with its own 投档线 — so the honest
  // figure is a RANGE (e.g. 海南大学 542–668), NOT a single minScore (542 alone
  // would imply 海大≈542 while its top groups need ~668). hiRange holds the
  // current-year min/max; the trend line uses the school's TOP-group line
  // (maxScore) per year. Non-HI / unmatched → null/[] → original PB_CUTOFFS path.
  const hiRange = (params.province === 'HI' && hiAdmit.byYear[params.year])
    ? hiAdmit.byYear[params.year]
    : null;
  const hiTrend = (params.province === 'HI')
    ? Object.keys(hiAdmit.byYear).map(Number).sort((a, b) => a - b)
        .map(y => ({ year: y, score: hiAdmit.byYear[y].maxScore, rank: hiAdmit.byYear[y].maxRank }))
    : [];
  const effectiveCutoff = currentCutoff; // non-HI unchanged; HI uses hiRange in the cards
  const effectiveTrend = (params.province === 'HI' && hiTrend.length) ? hiTrend : trendData;
  // A single school-level admit % is misleading for a multi-专业组 school, so keep
  // the original (null for name-resolved HI → card points the user to the tool).
  const effectiveProbResult = probResult;
  const activeProvince = PROVINCE_LIST.find(p => p.code === params.province);
  const activeProvinceName = activeProvince
    ? (lang === 'zh' ? activeProvince.zh : activeProvince.en)
    : params.province;
  const activeCutoffLoading = params.province === 'HI' && hiAdmit.status === 'loading';
  const activeCutoffMissing = !activeCutoffLoading && !hiRange && !effectiveCutoff;

  // ── Per-major data fetch (HI only — W4-A H3-C 50-school sample) ────────────
  // Looks up the province-specific school code from PB_SCHOOL_CODE_MAP, then
  // fetches /data/admissions-majors/<province>/<year>/<code>.json. Falls back
  // gracefully to a no-data state when the file is not covered yet.
  const [majorsData, setMajorsData] = React.useState({ status: 'idle', data: null });

  React.useEffect(() => {
    let cancelled = false;
    setMajorsData({ status: 'loading', data: null });

    (async () => {
      const provMap = PB_SCHOOL_CODE_MAP[params.province] || {};
      let code = provMap[school.id] || null;
      if (params.province === 'HI') {
        code = code || await _resolveHiSchoolCodeForBrowser(params.year, school);
        if (!code) {
          if (!cancelled) setMajorsData({ status: 'empty', data: null });
          return;
        }
      } else {
        code = code || school.id;
      }

      const res = await fetch(`/data/admissions-majors/${params.province}/${params.year}/${code}.json`);
      const json = res.ok ? await res.json() : null;
      if (cancelled) return;
      if (!json || !Array.isArray(json.majors)) {
        setMajorsData({ status: 'empty', data: null });
      } else {
        setMajorsData({ status: 'ready', data: json });
      }
    })().catch(() => {
      if (!cancelled) setMajorsData({ status: 'empty', data: null });
    });

    return () => { cancelled = true; };
  }, [school.id, params.province, params.year, _hiSchoolName]);

  // ── A5c: 双一流 data (shuangyiliu.json, loaded once, keyed by school name) ───
  const [sylData, setSylData] = React.useState(null);
  React.useEffect(() => {
    let cancelled = false;
    _loadShuangyiliuForBrowser().then(() => {
      if (!cancelled) setSylData(_SYL_CACHE.schools || {});
    });
    return () => { cancelled = true; };
  }, []);
  // Entry for this school: exact key lookup on Chinese name
  const sylEntry = (sylData && school.name && school.name.zh)
    ? (sylData[school.name.zh] || null)
    : null;
  // True if this school is in the 双一流 list
  const isSyl = sylEntry !== null;
  // Disciplines from shuangyiliu.json ([] = 整体建设, i.e. 一流大学建设高校 no separate list)
  const sylDisciplines = (sylEntry && Array.isArray(sylEntry.disciplines)) ? sylEntry.disciplines : [];
  // Existing tags already have 双一流? (from school-basic-cards)
  const existingTagsSyl = (school.tags || []).includes('双一流');

  // ── Wave 10-C: School meta + discipline rank (browser fetch) ────────────────
  // Resolves by the province-specific 4-digit code (same as majors fetch).
  const [metaData, setMetaData] = React.useState({ status: 'idle', meta: null, ranks: null });

  React.useEffect(() => {
    let cancelled = false;
    setMetaData({ status: 'loading', meta: null, ranks: null });
    _loadSchoolMetaForBrowser().then(() => {
      if (cancelled) return;
      const provMap = PB_SCHOOL_CODE_MAP[params.province] || {};
      const code = provMap[school.id] || school.id;
      const meta = (_META_CACHE.byCode || {})[String(code)] || null;
      const ranks = (_META_CACHE.rankByCode || {})[String(code)] || null;
      setMetaData({ status: 'ready', meta, ranks });
    }).catch(() => {
      if (!cancelled) setMetaData({ status: 'ready', meta: null, ranks: null });
    });
    return () => { cancelled = true; };
  }, [school.id, params.province]);

  // ── Wave 10-C: Employment data (browser fetch) ──────────────────────────────
  const [employData, setEmployData] = React.useState({ status: 'idle', data: null });

  React.useEffect(() => {
    let cancelled = false;
    setEmployData({ status: 'loading', data: null });
    // Employment files are keyed by the selected data cycle; do not silently
    // fall back to another year because HI school codes are reused annually.
    const empYear = params.year;
    _loadSchoolEmploymentForBrowser(params.province, empYear).then(cache => {
      if (cancelled) return;
      const provMap = PB_SCHOOL_CODE_MAP[params.province] || {};
      const code = provMap[school.id] || school.id;
      const found = cache
        ? (cache.byCode[String(code)] || cache.byName[school.name && school.name.zh] || null)
        : null;
      setEmployData({ status: 'ready', data: found });
    }).catch(() => {
      if (!cancelled) setEmployData({ status: 'ready', data: null });
    });
    return () => { cancelled = true; };
  }, [school.id, params.province, params.year]);

  // ── Wave 10-C: Enrollment plan (browser fetch) ──────────────────────────────
  const [planData, setPlanData] = React.useState({ status: 'idle', total: null });

  React.useEffect(() => {
    let cancelled = false;
    setPlanData({ status: 'loading', total: null });
    _loadSchoolPlanTotalsForBrowser(params.province, params.year).then(totals => {
      if (cancelled) return;
      const provMap = PB_SCHOOL_CODE_MAP[params.province] || {};
      const code = provMap[school.id] || school.id;
      const total = totals ? (totals[String(code)] || null) : null;
      setPlanData({ status: 'ready', total });
    }).catch(() => {
      if (!cancelled) setPlanData({ status: 'ready', total: null });
    });
    return () => { cancelled = true; };
  }, [school.id, params.province, params.year]);

  // ── Wave 10-C: Admission rules (browser fetch) ──────────────────────────────
  const [rulesData, setRulesData] = React.useState({ status: 'idle', entry: null });

  React.useEffect(() => {
    let cancelled = false;
    setRulesData({ status: 'loading', entry: null });
    _loadSchoolAdmissionRulesForBrowser(params.province, params.year).then(cache => {
      if (cancelled) return;
      const provMap = PB_SCHOOL_CODE_MAP[params.province] || {};
      const code = provMap[school.id] || school.id;
      const found = cache
        ? (cache.byCode[String(code)] || cache.byName[school.name && school.name.zh] || null)
        : null;
      setRulesData({ status: 'ready', entry: found });
    }).catch(() => {
      if (!cancelled) setRulesData({ status: 'ready', entry: null });
    });
    return () => { cancelled = true; };
  }, [school.id, params.province, params.year]);

  // ── Wave 11-D: Campus life (browser fetch) ──────────────────────────────────
  const [campusLifeData, setCampusLifeData] = React.useState({ status: 'idle', entry: null });

  React.useEffect(() => {
    let cancelled = false;
    setCampusLifeData({ status: 'loading', entry: null });
    _loadCampusLifeForBrowser(params.province, 2024).then(cache => {
      if (cancelled) return;
      const provMap = PB_SCHOOL_CODE_MAP[params.province] || {};
      const code = provMap[school.id] || school.id;
      const found = cache
        ? (cache.byCode[String(code)] || cache.byName[school.name && school.name.zh] || null)
        : null;
      setCampusLifeData({ status: 'ready', entry: found });
    }).catch(() => {
      if (!cancelled) setCampusLifeData({ status: 'ready', entry: null });
    });
    return () => { cancelled = true; };
  }, [school.id, params.province]);

  // ── Wave 11-D: Awards (browser fetch) ───────────────────────────────────────
  const [awardsData, setAwardsData] = React.useState({ status: 'idle', entry: null });

  React.useEffect(() => {
    let cancelled = false;
    setAwardsData({ status: 'loading', entry: null });
    _loadAwardsForBrowser(params.province, 2024).then(cache => {
      if (cancelled) return;
      const provMap = PB_SCHOOL_CODE_MAP[params.province] || {};
      const code = provMap[school.id] || school.id;
      const found = cache
        ? (cache.byCode[String(code)] || cache.byName[school.name && school.name.zh] || null)
        : null;
      setAwardsData({ status: 'ready', entry: found });
    }).catch(() => {
      if (!cancelled) setAwardsData({ status: 'ready', entry: null });
    });
    return () => { cancelled = true; };
  }, [school.id, params.province]);

  // ── Wave 11-D: Academic policy (browser fetch) ───────────────────────────────
  const [academicPolicyData, setAcademicPolicyData] = React.useState({ status: 'idle', entry: null });

  React.useEffect(() => {
    let cancelled = false;
    setAcademicPolicyData({ status: 'loading', entry: null });
    _loadAcademicPolicyForBrowser(params.province, 2024).then(cache => {
      if (cancelled) return;
      const provMap = PB_SCHOOL_CODE_MAP[params.province] || {};
      const code = provMap[school.id] || school.id;
      const found = cache
        ? (cache.byCode[String(code)] || cache.byName[school.name && school.name.zh] || null)
        : null;
      setAcademicPolicyData({ status: 'ready', entry: found });
    }).catch(() => {
      if (!cancelled) setAcademicPolicyData({ status: 'ready', entry: null });
    });
    return () => { cancelled = true; };
  }, [school.id, params.province]);

  // ── Wave 15-C: Mistakes cases (browser fetch) ────────────────────────────────
  const [mistakesData, setMistakesData] = React.useState({ status: 'idle', cases: [] });

  React.useEffect(() => {
    let cancelled = false;
    setMistakesData({ status: 'loading', cases: [] });
    _loadMistakesForBrowser(params.province, params.year).then(cache => {
      if (cancelled) return;
      const provMap = PB_SCHOOL_CODE_MAP[params.province] || {};
      const code = provMap[school.id] || school.id;
      const found = cache ? (cache.bySchoolCode[String(code)] || []) : [];
      setMistakesData({ status: 'ready', cases: found });
    }).catch(() => {
      if (!cancelled) setMistakesData({ status: 'ready', cases: [] });
    });
    return () => { cancelled = true; };
  }, [school.id, params.province, params.year]);

  // ── M8: 非HI省诚实降级 — 次级数据区块全空时合并占位 ──────────────────────────
  // All 5 secondary-data sections (employment / plan / rules / campus / awards /
  // academic) only have real data for HI. When every section is done loading and
  // carries no data, collapse individual "补充中" boxes into ONE consolidated notice
  // so non-HI users don't see a wall of 5-6 identical placeholders.
  // Tied to DATA PRESENCE (not a hard province check) so a future non-HI data
  // drop automatically restores normal rendering for that section.
  // Only show consolidated notice once ALL 6 loaders have settled to 'ready'
  // (not 'idle' or 'loading') — avoids a flash before fetches begin.
  const _secDone = [employData, planData, rulesData, campusLifeData, awardsData, academicPolicyData]
    .every(s => s.status === 'ready');
  const _secHasAnyData =
    !!employData.data ||
    planData.total != null ||
    !!rulesData.entry ||
    !!campusLifeData.entry ||
    !!awardsData.entry ||
    !!academicPolicyData.entry;
  // True once all loaders have settled and none returned real data.
  const secondarySectionsAllEmpty = _secDone && !_secHasAnyData;

  // ── Add to compare ───────────────────────────────────────────────────────────
  const [inCompare, setInCompare] = React.useState(() => {
    try {
      const stored = JSON.parse(localStorage.getItem('pb-gaokao-compare') || '[]');
      return stored.includes(school.id);
    } catch { return false; }
  });

  const toggleCompare = () => {
    try {
      const stored = JSON.parse(localStorage.getItem('pb-gaokao-compare') || '[]');
      let next;
      if (stored.includes(school.id)) {
        next = stored.filter(id => id !== school.id);
      } else {
        next = [...stored, school.id].slice(-4); // max 4
      }
      localStorage.setItem('pb-gaokao-compare', JSON.stringify(next));
      setInCompare(next.includes(school.id));
    } catch {}
  };

  // ── Similar schools (by rank proximity) ─────────────────────────────────────
  const similarSchools = React.useMemo(() => {
    if (!currentCutoff) return [];
    const myRank = currentCutoff[1];
    return SCHOOLS
      .filter(s => s.id !== school.id)
      .map(s => {
        const d = ((CUTOFFS[params.province] || {})[s.id] || {})[String(params.year)];
        if (!d) return null;
        return { ...s, cutoffRank: d[1], cutoffScore: d[0], dist: Math.abs(d[1] - myRank) };
      })
      .filter(Boolean)
      .sort((a, b) => a.dist - b.dist)
      .slice(0, 4);
  }, [school.id, params.province, params.year]);

  // ── One-line positioning ─────────────────────────────────────────────────────
  const schoolTagline = () => {
    const tags = school.tags || [];
    const parts = [];
    if (tags.includes('C9'))    parts.push(lang === 'zh' ? 'C9联盟' : 'C9 League');
    if (tags.includes('985'))   parts.push('985');
    if (tags.includes('双一流')) parts.push(lang === 'zh' ? '双一流' : 'Double First Class');
    return parts.join(' · ') || (lang === 'zh' ? '普通高等学校' : 'University');
  };

  // ── Styles ───────────────────────────────────────────────────────────────────
  const cardStyle = {
    background: 'var(--pb-surface)',
    border: '1px solid var(--pb-border)',
    borderRadius: 10,
    padding: '20px 24px',
  };

  const labelStyle = {
    fontSize: 12,
    color: 'var(--pb-fg-muted)',
    marginBottom: 4,
    fontWeight: 500,
    letterSpacing: '0.03em',
    textTransform: 'uppercase',
  };

  const bigNumStyle = {
    fontFamily: 'var(--pb-mono)',
    fontSize: 28,
    fontWeight: 700,
    lineHeight: 1.15,
    color: 'var(--pb-fg)',
  };

  const schoolName = lang === 'zh' ? school.name.zh : school.name.en;
  const cityName   = lang === 'zh' ? school.location.zh : school.location.en;
  const tierStyle  = tierColors[school.tier] || { bg: 'var(--pb-surface-alt)', color: 'var(--pb-fg-muted)' };

  // ── 院校详情页 overhaul: derived profile-detail content (cn-school-ref) ───────
  const prof = (resolved.profile && resolved.profile.dimensions) || null;
  const _dedupeMajors = (arr) => Array.from(new Set((arr || []).filter((m) => m && m !== school.name.zh)));
  const profStarMajors    = prof && prof['王牌专业'] ? _dedupeMajors(prof['王牌专业'].majors) : [];
  const profFeatureMajors = prof && prof['特色专业'] ? _dedupeMajors(prof['特色专业'].majors) : [];
  const profFirstClass    = prof && prof['一流专业'] && Array.isArray(prof['一流专业'].majors)
    ? Array.from(new Set(prof['一流专业'].majors.map((m) => (m && m.name) || '').filter((n) => n && n !== school.name.zh)))
    : [];
  const profDisc       = prof && prof['学科评估'] && Array.isArray(prof['学科评估'].disciplines) ? prof['学科评估'].disciplines : [];
  const profIntro      = prof && prof['院校介绍'] ? prof['院校介绍'] : null;
  const profScoreTbls  = prof && prof['录取分数'] && Array.isArray(prof['录取分数'].tables) ? prof['录取分数'].tables : [];
  const profPlanTbls   = prof && prof['招生计划'] && Array.isArray(prof['招生计划'].tables) ? prof['招生计划'].tables : [];
  const profRules      = prof && prof['录取规则'] && Array.isArray(prof['录取规则'].sections) ? prof['录取规则'].sections : [];
  const profFees       = (prof && (prof['收费标准'] || prof['学费标准'])) || null;
  const profScholar    = prof && prof['奖助学金'] ? prof['奖助学金'] : null;
  const profEmployment = prof && prof['就业情况'] ? prof['就业情况'] : null;
  const profSyl        = prof && prof['双一流吗'] ? prof['双一流吗'] : null;

  const renderProfTable = (t, max = 12) => {
    if (!t || !Array.isArray(t.rows) || !t.rows.length) return null;
    const headers = Array.isArray(t.headers) ? t.headers : [];
    return (
      <div style={{ overflowX: 'auto', border: '1px solid var(--pb-border)', borderRadius: 8 }}>
        <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 12.5 }}>
          {headers.length > 0 && (
            <thead>
              <tr style={{ background: 'var(--pb-surface-alt)' }}>
                {headers.map((h, i) => (
                  <th key={i} style={{ padding: '8px 10px', textAlign: 'left', fontWeight: 600, color: 'var(--pb-fg-muted)', whiteSpace: 'nowrap' }}>{h}</th>
                ))}
              </tr>
            </thead>
          )}
          <tbody>
            {t.rows.slice(0, max).map((row, ri) => (
              <tr key={ri} style={{ borderTop: '1px solid var(--pb-border)' }}>
                {(Array.isArray(row) ? row : []).map((cell, ci) => (
                  <td key={ci} style={{ padding: '7px 10px', verticalAlign: 'top' }}>{cell}</td>
                ))}
              </tr>
            ))}
          </tbody>
        </table>
        {t.rows.length > max && (
          <div style={{ padding: '6px 10px', fontSize: 11, color: 'var(--pb-fg-muted)', background: 'var(--pb-surface-alt)' }}>
            {lang === 'zh' ? `共 ${t.rows.length} 行，展示前 ${max}。` : `${t.rows.length} rows, showing ${max}.`}
          </div>
        )}
      </div>
    );
  };
  const ChipList = ({ items, max = 40, bg = 'var(--pb-surface-alt)', color = 'var(--pb-fg)' }) => {
    const all = (items || []).filter(Boolean);
    const [showAll, setShowAll] = React.useState(false);
    const arr = showAll ? all : all.slice(0, max);
    if (!arr.length) return null;
    const hidden = all.length - arr.length;
    return (
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
        {arr.map((x, i) => (
          <span key={i} style={{ padding: '3px 9px', borderRadius: 5, fontSize: 12, background: bg, color, border: '1px solid var(--pb-border)' }}>{x}</span>
        ))}
        {hidden > 0 && (
          <button
            type="button"
            onClick={() => setShowAll(true)}
            style={{ padding: '3px 9px', borderRadius: 5, fontSize: 12, background: 'transparent', color: 'var(--pb-fg-muted)', border: '1px dashed var(--pb-border-strong)', cursor: 'pointer' }}
          >
            {lang === 'zh' ? `+${hidden} 更多` : `+${hidden} more`}
          </button>
        )}
      </div>
    );
  };

  // ── Trend bar mini component ─────────────────────────────────────────────────
  const TrendBar = ({ data }) => {
    if (!data || data.length < 2) return (
      <div style={{ fontSize: 12, color: 'var(--pb-fg-muted)' }}>
        {lang === 'zh' ? '数据不足' : 'Insufficient data'}
      </div>
    );
    const scores = data.map(d => d.score);
    const minS = Math.min(...scores);
    const maxS = Math.max(...scores);
    const range = maxS - minS || 1;
    return (
      <div style={{ display: 'flex', alignItems: 'flex-end', gap: 6, height: 40 }}>
        {data.map((d, i) => {
          const pct = ((d.score - minS) / range) * 70 + 30; // 30-100%
          const isLast = i === data.length - 1;
          return (
            <div key={d.year} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2 }}>
              <div style={{ fontSize: 10, color: 'var(--pb-fg-muted)' }}>{d.score}</div>
              <div style={{
                width: 22,
                height: `${pct}%`,
                minHeight: 12,
                background: isLast ? 'var(--pb-gold-500)' : 'var(--pb-border)',
                borderRadius: '3px 3px 0 0',
                transition: 'height 0.3s',
              }} />
              <div style={{ fontSize: 9, color: 'var(--pb-fg-muted)' }}>{String(d.year).slice(2)}</div>
            </div>
          );
        })}
      </div>
    );
  };

  return (
    <div className="pb-scroll" data-testid="school-detail-page">
      <PBNav active="gaokao" />

      {/* ── Hero ──────────────────────────────────────────────────────────────── */}
      <section style={{
        padding: '48px 48px 32px',
        background: 'var(--pb-navy-900)',
        color: '#fff',
      }}>
        <div style={{ maxWidth: 1100, margin: '0 auto' }}>
          {/* Breadcrumb */}
          <div style={{ fontSize: 13, color: 'rgba(255,255,255,0.55)', marginBottom: 20, display: 'flex', gap: 8, alignItems: 'center' }}>
            <span
              style={{ cursor: 'pointer', textDecoration: 'underline', textUnderlineOffset: 3 }}
              onClick={() => { window.location.hash = '#/gaokao'; }}
            >
              {lang === 'zh' ? '高考测算' : 'Gaokao Calculator'}
            </span>
            <span>›</span>
            <span
              style={{ cursor: 'pointer', textDecoration: 'underline', textUnderlineOffset: 3 }}
              onClick={() => { window.location.hash = '#/gaokao/search'; }}
            >
              {lang === 'zh' ? '查校' : 'Search Schools'}
            </span>
            <span>›</span>
            <span style={{ color: '#fff' }}>{schoolName}</span>
          </div>

          {/* School identity */}
          <div style={{ display: 'flex', gap: 24, alignItems: 'flex-start' }}>
            {/* Logo placeholder */}
            <div style={{
              width: 72, height: 72, borderRadius: 12,
              background: 'rgba(255,255,255,0.12)',
              border: '1px solid rgba(255,255,255,0.2)',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              flexShrink: 0,
              fontFamily: 'var(--pb-serif)', fontSize: 22, fontWeight: 700,
              color: 'var(--pb-gold-500)',
            }}>
              {(school.name.zh || '').slice(0, 2)}
            </div>

            <div style={{ flex: 1 }}>
              <div style={{ display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap', marginBottom: 8 }}>
                <h1
                  data-testid="school-detail-name"
                  style={{ fontFamily: 'var(--pb-serif)', fontSize: 30, fontWeight: 700, margin: 0, lineHeight: 1.2 }}
                >
                  {schoolName}
                </h1>
                <span style={{
                  padding: '3px 10px', borderRadius: 5,
                  background: tierStyle.bg, color: tierStyle.color,
                  fontSize: 12, fontWeight: 700, letterSpacing: '0.04em',
                }}>
                  {school.tier}
                </span>
              </div>

              <div style={{ fontSize: 14, color: 'rgba(255,255,255,0.65)', display: 'flex', gap: 16, flexWrap: 'wrap' }}>
                <span>📍 {cityName}</span>
                <span>·</span>
                <span>{schoolTagline()}</span>
              </div>
            </div>
          </div>
        </div>
      </section>

      {/* ── Overview grid ─────────────────────────────────────────────────────── */}
      <section style={{ background: 'var(--pb-surface-alt)', borderBottom: '1px solid var(--pb-border)' }}>
        <div className="pb-gk-school-statsec">
          <div className="pb-gk-school-stats">
            {/* Cut score */}
            <div style={cardStyle}>
              <div style={labelStyle}>{lang === 'zh' ? `${params.year} 投档分` : `${params.year} Cut Score`}</div>
              <div style={bigNumStyle}>
                {hiRange
                  ? <span style={{ fontSize: 24 }}>{hiRange.minScore}–{hiRange.maxScore}</span>
                  : (effectiveCutoff
                    ? effectiveCutoff[0]
                    : <span style={{ fontSize: 18 }}>{lang === 'zh' ? '暂无数据' : 'No data'}</span>)}
              </div>
              <div style={{ fontSize: 12, color: 'var(--pb-fg-muted)', marginTop: 4 }}>
                {hiRange
                  ? (lang === 'zh' ? `${hiRange.numGroups || ''} 个专业组 · 最低–最高` : `${hiRange.numGroups || ''} groups · min–max`)
                  : activeProvinceName}
              </div>
              {/* M4 HIPAGE-1: HI uses the 900-档标准分; label it so the figure is not
                  mistaken for a 750-scale 原始分. HI-only (gated on hiRange). */}
              {hiRange && (
                <div data-testid="school-detail-hi-std-score-note" style={{ fontSize: 11, color: 'var(--pb-fg-muted)', marginTop: 2 }}>
                  {lang === 'zh' ? '标准分（满分900）' : 'Standard score (max 900)'}
                </div>
              )}
            </div>

            {/* Cut rank */}
            <div style={cardStyle}>
              <div style={labelStyle}>{lang === 'zh' ? `${params.year} 投档位次` : `${params.year} Cut Rank`}</div>
              <div style={bigNumStyle}>
                {hiRange
                  ? <span style={{ fontSize: 20 }}>{hiRange.maxRank.toLocaleString()}–{hiRange.minRank.toLocaleString()}</span>
                  : (effectiveCutoff
                    ? effectiveCutoff[1].toLocaleString()
                    : <span style={{ fontSize: 18 }}>{lang === 'zh' ? '暂无数据' : 'No data'}</span>)}
              </div>
              <div style={{ fontSize: 12, color: 'var(--pb-fg-muted)', marginTop: 4 }}>
                {lang === 'zh' ? '全省位次' : 'Province rank'}
              </div>
            </div>

            {/* Trend */}
            <div style={cardStyle}>
              <div style={labelStyle}>{lang === 'zh' ? '3年分数趋势' : '3-Year Score Trend'}</div>
              <TrendBar data={effectiveTrend} />
            </div>

            {/* Probability */}
            <div style={cardStyle}>
              <div style={labelStyle}>{lang === 'zh' ? '录取概率估算' : 'Est. Admit Prob.'}</div>
              {effectiveProbResult ? (() => {
                const fk = fineKeyFor(effectiveProbResult.prob);
                const fc = fineColorMap[fk] || 'var(--pb-fg-muted)';
                return (
                <>
                  <div style={{ ...bigNumStyle, color: fc }}>
                    {effectiveProbResult.prob}%
                  </div>
                  <div style={{
                    display: 'inline-block', marginTop: 4,
                    fontSize: 11, fontWeight: 600, padding: '2px 8px',
                    borderRadius: 4, border: `1px solid ${fc}`,
                    color: fc,
                  }}>
                    {fineLabelMap[fk] || effectiveProbResult.bucket}
                  </div>
                </>
                );
              })() : (
                <div style={{ fontSize: 13, color: 'var(--pb-fg-muted)', paddingTop: 8, lineHeight: 1.5 }}>
                  {hiRange
                    ? (lang === 'zh' ? '本校多专业组 · 概率见测算页' : 'Multi-group · see calculator')
                    : (lang === 'zh' ? '请在测算页输入分数' : 'Enter score in calculator')}
                </div>
              )}
            </div>
          </div>
        </div>
      </section>

      {/* ── Main content ─────────────────────────────────────────────────────── */}
      <section className="pb-gk-school-section">
        <div className="pb-gk-school-layout">

          {/* Left: cutoffs table + similar schools */}
          <div>
            {/* Basic-info card — shown for national schools that have no full profile,
                so every university still surfaces 所在地/层次/性质/标签 instead of blanks. */}
            {resolved.card && !prof && (
              <div data-testid="school-detail-basic-card" style={{ ...cardStyle, marginBottom: 24, display: 'flex', flexWrap: 'wrap', gap: 28 }}>
                {(resolved.card.province || resolved.card.city) && (
                  <div>
                    <div style={labelStyle}>{lang === 'zh' ? '所在地' : 'Location'}</div>
                    <div style={{ fontSize: 14, marginTop: 2 }}>{[resolved.card.province, resolved.card.city].filter(Boolean).join(' · ')}</div>
                  </div>
                )}
                {resolved.card.level && (
                  <div>
                    <div style={labelStyle}>{lang === 'zh' ? '办学层次' : 'Level'}</div>
                    <div style={{ fontSize: 14, marginTop: 2 }}>{resolved.card.level}</div>
                  </div>
                )}
                {resolved.card.nature && (
                  <div>
                    <div style={labelStyle}>{lang === 'zh' ? '办学性质' : 'Type'}</div>
                    <div style={{ fontSize: 14, marginTop: 2 }}>{resolved.card.nature}</div>
                  </div>
                )}
                {(resolved.card.tags || []).length > 0 && (
                  <div>
                    <div style={labelStyle}>{lang === 'zh' ? '院校标签' : 'Tags'}</div>
                    <div style={{ marginTop: 4 }}><ChipList items={resolved.card.tags} bg="#ede9fe" color="#5b21b6" /></div>
                  </div>
                )}
              </div>
            )}

            {/* Cutoffs table */}
            <h2 style={{ fontFamily: 'var(--pb-serif)', fontSize: 20, fontWeight: 600, marginBottom: 16, marginTop: 0 }}>
              {lang === 'zh' ? '历年投档数据' : 'Historical Cutoff Data'}
            </h2>
            {activeCutoffMissing && (
              <div
                data-testid="school-detail-cutoffs-empty"
                style={{
                  padding: '12px 14px',
                  marginBottom: 12,
                  border: '1px solid var(--pb-border)',
                  borderRadius: 8,
                  background: 'var(--pb-surface-alt)',
                  color: 'var(--pb-fg-muted)',
                  fontSize: 13,
                }}
              >
                {lang === 'zh'
                  ? `暂无${activeProvinceName} ${params.year} 年投档数据`
                  : `No ${activeProvinceName} cutoff data for ${params.year}`}
              </div>
            )}
            <div
              data-testid="school-detail-cutoffs-table"
              className="pb-gk-table-scroll"
              style={{
                border: '1px solid var(--pb-border)',
                borderRadius: 10,
                marginBottom: 40,
              }}
            >
              <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
                <thead>
                  <tr style={{ background: 'var(--pb-navy-900)', color: '#fff' }}>
                    <th style={{ padding: '12px 16px', textAlign: 'left', fontWeight: 600 }}>
                      {lang === 'zh' ? '省份' : 'Province'}
                    </th>
                    {YEARS.map(y => (
                      <th key={y} colSpan={2} style={{ padding: '12px 16px', textAlign: 'center', fontWeight: 600 }}>
                        {y}
                      </th>
                    ))}
                  </tr>
                  <tr style={{ background: 'var(--pb-surface-alt)', borderBottom: '1px solid var(--pb-border)' }}>
                    <th style={{ padding: '8px 16px', textAlign: 'left', fontWeight: 500, fontSize: 11, color: 'var(--pb-fg-muted)' }}></th>
                    {YEARS.map(y => (
                      <React.Fragment key={y}>
                        <th style={{ padding: '8px 10px', textAlign: 'center', fontWeight: 500, fontSize: 11, color: 'var(--pb-fg-muted)' }}>
                          {lang === 'zh' ? '分数' : 'Score'}
                        </th>
                        <th style={{ padding: '8px 10px', textAlign: 'center', fontWeight: 500, fontSize: 11, color: 'var(--pb-fg-muted)' }}>
                          {lang === 'zh' ? '位次' : 'Rank'}
                        </th>
                      </React.Fragment>
                    ))}
                  </tr>
                </thead>
                <tbody>
                  {/* Real HI row (issue #296 item 3): name-resolved HI schools are
                      absent from PB_CUTOFFS, so the active 海南 row is built from
                      /data/admissions/HI/<year>-3plus3.json (minScore/minRank). */}
                  {params.province === 'HI' && hiAdmit.found && (
                    <tr
                      key="HI"
                      data-testid="school-detail-cutoffs-row-HI"
                      style={{
                        background: 'rgba(201,169,110,0.08)',
                        borderBottom: '1px solid var(--pb-border)',
                      }}
                    >
                      <td style={{ padding: '12px 16px', fontWeight: 600, color: 'var(--pb-gold-600)' }}>
                        {lang === 'zh' ? '海南' : 'Hainan'}
                        <span style={{ marginLeft: 6, fontSize: 10, color: 'var(--pb-gold-500)', fontWeight: 700 }}>★</span>
                        {/* M4 HIPAGE-1: HI 投档分 is the 900-档标准分, not a 750 原始分. */}
                        <div data-testid="school-detail-cutoffs-hi-std-note" style={{ fontWeight: 400, fontSize: 10, color: 'var(--pb-fg-muted)', marginTop: 2 }}>
                          {lang === 'zh' ? '分=标准分(满分900)' : 'Score = std (max 900)'}
                        </div>
                      </td>
                      {YEARS.map(y => {
                        const d = hiAdmit.byYear[y];
                        return (
                          <React.Fragment key={y}>
                            <td style={{ padding: '12px 8px', textAlign: 'center', fontFamily: 'var(--pb-mono)', fontWeight: d ? 600 : 400, color: 'var(--pb-fg)', fontSize: 12, whiteSpace: 'nowrap' }}>
                              {d ? `${d.minScore}–${d.maxScore}` : '—'}
                            </td>
                            <td style={{ padding: '12px 8px', textAlign: 'center', fontFamily: 'var(--pb-mono)', color: 'var(--pb-fg-muted)', fontSize: 11, whiteSpace: 'nowrap' }}>
                              {d ? `${d.maxRank.toLocaleString()}–${d.minRank.toLocaleString()}` : '—'}
                            </td>
                          </React.Fragment>
                        );
                      })}
                    </tr>
                  )}
                  {PROVINCE_LIST.map((prov, pi) => {
                    const provData = (CUTOFFS[prov.code] || {})[school.id] || {};
                    const hasAny = YEARS.some(y => provData[String(y)]);
                    if (!hasAny) return null;
                    const isActive = prov.code === params.province;
                    return (
                      <tr
                        key={prov.code}
                        style={{
                          background: isActive
                            ? 'rgba(201,169,110,0.08)'
                            : (pi % 2 === 0 ? 'var(--pb-surface)' : 'var(--pb-surface-alt)'),
                          borderBottom: '1px solid var(--pb-border)',
                        }}
                      >
                        <td style={{
                          padding: '12px 16px', fontWeight: isActive ? 600 : 400,
                          color: isActive ? 'var(--pb-gold-600)' : 'var(--pb-fg)',
                        }}>
                          {lang === 'zh' ? prov.zh : prov.en}
                          {isActive && (
                            <span style={{ marginLeft: 6, fontSize: 10, color: 'var(--pb-gold-500)', fontWeight: 700 }}>
                              ★
                            </span>
                          )}
                        </td>
                        {YEARS.map(y => {
                          const d = provData[String(y)];
                          return (
                            <React.Fragment key={y}>
                              <td style={{ padding: '12px 10px', textAlign: 'center', fontFamily: 'var(--pb-mono)', fontWeight: d ? 600 : 400, color: d ? 'var(--pb-fg)' : 'var(--pb-fg-muted)' }}>
                                {d ? d[0] : '—'}
                              </td>
                              <td style={{ padding: '12px 10px', textAlign: 'center', fontFamily: 'var(--pb-mono)', color: d ? 'var(--pb-fg-muted)' : 'var(--pb-fg-muted)', fontSize: 12 }}>
                                {d ? d[1].toLocaleString() : '—'}
                              </td>
                            </React.Fragment>
                          );
                        })}
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>

            {/* M4 HIPAGE-2: surface the official HI cutoff source already fetched into
                hiAdmit.source (mirrors the majors source line below). HI-only. */}
            {params.province === 'HI' && hiAdmit.found && hiAdmit.source && (
              <div
                data-testid="school-detail-cutoffs-source"
                style={{ marginTop: -16, marginBottom: 24, fontSize: 11, color: 'var(--pb-fg-muted)', lineHeight: 1.6 }}
              >
                {lang === 'zh' ? '数据来源' : 'Source'}: {hiAdmit.source.title || hiAdmit.source.mirror || hiAdmit.source.url || hiAdmit.source.type}
              </div>
            )}

            {/* Majors section — H3-C HI 50-school sample */}
            <div data-testid="school-detail-majors-section" style={{ marginBottom: 40 }}>
              <h2 style={{ fontFamily: 'var(--pb-serif)', fontSize: 20, fontWeight: 600, marginBottom: 16, marginTop: 0 }}>
                {lang === 'zh' ? '专业录取分数' : 'Major-Level Admission Cutoffs'}
                {majorsData.status === 'ready' && majorsData.data && (
                  <span style={{ marginLeft: 10, fontSize: 13, fontWeight: 400, color: 'var(--pb-fg-muted)' }}>
                    · {majorsData.data.year} · {(majorsData.data.counts && majorsData.data.counts.majors)
                      || (Array.isArray(majorsData.data.majors) ? majorsData.data.majors.length : '—')} {lang === 'zh' ? '个专业' : 'majors'}
                  </span>
                )}
              </h2>

              {majorsData.status === 'loading' && (
                <div data-testid="school-detail-majors-loading" style={{ padding: 24, textAlign: 'center', color: 'var(--pb-fg-muted)', fontSize: 13 }}>
                  {lang === 'zh' ? '正在加载专业数据…' : 'Loading major data…'}
                </div>
              )}

              {majorsData.status === 'empty' && (
                <div data-testid="school-detail-majors-empty" style={{
                  padding: '8px 12px', background: 'var(--pb-surface-alt)', borderRadius: 8,
                  fontSize: 12, color: 'var(--pb-fg-muted)', lineHeight: 1.6,
                }}>
                  {lang === 'zh'
                    ? `该院校在 ${params.province === 'HI' ? '海南' : params.province} ${params.year} 年的专业录取数据正在补充中，敬请期待。`
                    : `Major-level cutoff data for this school in ${params.province} ${params.year} is being collected.`}
                </div>
              )}

              {/* M4 HIPAGE-3: honest per-major labeling. ~793 HI schools' per-major
                  rows are DERIVED (anchored on the 院校专业组 投档线, not official
                  per-major publications). Show an honest caveat for those; tag the
                  genuinely official (national-code) ones. Helper keeps raw machine
                  source.type strings (e.g. "real-from-admissions-2025") off-screen. */}
              {majorsData.status === 'ready' && majorsData.data && (() => {
                const _msl = (typeof window !== 'undefined' && window.PBMajorSourceLabel) || null;
                const _src = majorsData.data.source || {};
                const _meta = majorsData.data._meta || {};
                const srcLabel = _msl
                  ? _msl.majorSourceLabel(_src.type, _meta.majorCodeStatus, lang)
                  : { isDerived: false, label: (_src.title || _src.url || ''), caveat: '' };
                return (
                <div
                  data-testid="school-detail-majors-table"
                  className="pb-gk-table-scroll"
                  style={{ border: '1px solid var(--pb-border)', borderRadius: 10 }}
                >
                  {srcLabel.isDerived ? (
                    <div
                      data-testid="school-detail-major-caveat"
                      style={{
                        padding: '8px 14px', fontSize: 12, lineHeight: 1.6,
                        color: 'var(--pb-fg-muted)', background: 'var(--pb-surface-alt)',
                        borderBottom: '1px solid var(--pb-border)',
                      }}
                    >
                      <span aria-hidden="true">⚠️ </span>{srcLabel.caveat}
                    </div>
                  ) : srcLabel.isOfficial ? (
                    <div
                      data-testid="school-detail-major-official-tag"
                      style={{
                        padding: '8px 14px', fontSize: 12, lineHeight: 1.6,
                        color: 'var(--pb-gold-700)', background: 'var(--pb-surface-alt)',
                        borderBottom: '1px solid var(--pb-border)', fontWeight: 600,
                      }}
                    >
                      {lang === 'zh' ? '✓ 官方逐专业数据' : '✓ Official per-major data'}
                    </div>
                  ) : null}
                  <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
                    <thead>
                      <tr style={{ background: 'var(--pb-navy-900)', color: '#fff' }}>
                        <th style={{ padding: '10px 14px', textAlign: 'left', fontWeight: 600 }}>
                          {lang === 'zh' ? '专业名称' : 'Major'}
                        </th>
                        <th style={{ padding: '10px 14px', textAlign: 'center', fontWeight: 600 }}>
                          {/* M4 HIPAGE-1: HI scores are 900-档标准分. */}
                          {params.province === 'HI'
                            ? (lang === 'zh' ? '最低分(标准分)' : 'Min Score (std)')
                            : (lang === 'zh' ? '最低分' : 'Min Score')}
                        </th>
                        <th style={{ padding: '10px 14px', textAlign: 'center', fontWeight: 600 }}>
                          {lang === 'zh' ? '最低位次' : 'Min Rank'}
                        </th>
                        <th style={{ padding: '10px 14px', textAlign: 'center', fontWeight: 600 }}>
                          {lang === 'zh' ? '平均分' : 'Avg Score'}
                        </th>
                        <th style={{ padding: '10px 14px', textAlign: 'center', fontWeight: 600 }}>
                          {lang === 'zh' ? '录取人数' : 'Admitted'}
                        </th>
                        <th style={{ padding: '10px 14px', textAlign: 'left', fontWeight: 600 }}>
                          {lang === 'zh' ? '选考' : 'Subj. Req.'}
                        </th>
                      </tr>
                    </thead>
                    <tbody>
                      {majorsData.data.majors.slice(0, 30).map((m, i) => {
                        // Highlight rows the user can plausibly hit
                        const userOk = params.score > 0 && typeof m.minScore === 'number' && params.score >= m.minScore;
                        // Link to major wiki page only when a MAJOR_META entry matches by nameZh
                        const _pbMeta = (typeof window !== 'undefined' && window.PBMajorMeta) || {};
                        const _allMajors = (_pbMeta.listAllMajors || (() => []))();
                        const _metaEntry = _allMajors.find((mm) => mm.nameZh === m.name);
                        return (
                          <tr
                            key={`${m.name}-${i}`}
                            data-testid={`school-detail-major-row-${i}`}
                            style={{
                              background: userOk ? 'rgba(60,140,90,0.06)' : (i % 2 === 0 ? 'var(--pb-surface)' : 'var(--pb-surface-alt)'),
                              borderBottom: '1px solid var(--pb-border)',
                            }}
                          >
                            <td style={{ padding: '10px 14px', fontWeight: 500 }}>
                              {_metaEntry ? (
                                <a
                                  href={`#/gaokao/major/${_metaEntry.code}`}
                                  data-testid={`school-detail-major-wiki-link-${i}`}
                                  style={{ color: 'var(--pb-navy-700)', textDecoration: 'underline', textUnderlineOffset: 2 }}
                                >
                                  {m.name}
                                </a>
                              ) : m.name}
                            </td>
                            <td style={{ padding: '10px 14px', textAlign: 'center', fontFamily: 'var(--pb-mono)', fontWeight: 600 }}>
                              {typeof m.minScore === 'number' ? m.minScore : '—'}
                            </td>
                            <td style={{ padding: '10px 14px', textAlign: 'center', fontFamily: 'var(--pb-mono)', color: 'var(--pb-fg-muted)' }}>
                              {typeof m.minRank === 'number' ? m.minRank.toLocaleString() : '—'}
                            </td>
                            <td style={{ padding: '10px 14px', textAlign: 'center', fontFamily: 'var(--pb-mono)' }}>
                              {typeof m.avgScore === 'number' ? m.avgScore : '—'}
                            </td>
                            <td style={{ padding: '10px 14px', textAlign: 'center' }}>
                              {typeof m.admittedCount === 'number' ? m.admittedCount : '—'}
                            </td>
                            <td style={{ padding: '10px 14px', fontSize: 12, color: 'var(--pb-fg-muted)' }}>
                              {m.subjectReq || '—'}
                            </td>
                          </tr>
                        );
                      })}
                    </tbody>
                  </table>
                  {majorsData.data.majors.length > 30 && (
                    <div style={{ padding: '10px 14px', fontSize: 12, color: 'var(--pb-fg-muted)', borderTop: '1px solid var(--pb-border)', background: 'var(--pb-surface-alt)' }}>
                      {lang === 'zh'
                        ? `共 ${majorsData.data.majors.length} 个专业，仅展示前 30。`
                        : `${majorsData.data.majors.length} majors total, showing top 30.`}
                    </div>
                  )}
                  {/* M4 HIPAGE-3: honest, human-readable source line. NEVER print the
                      raw machine source.type (e.g. "real-from-admissions-2025"). */}
                  {(srcLabel.label || (majorsData.data.source && (majorsData.data.source.title || majorsData.data.source.url))) && (
                    <div data-testid="school-detail-major-source" style={{ padding: '8px 14px', fontSize: 11, color: 'var(--pb-fg-muted)', background: 'var(--pb-surface-alt)' }}>
                      {lang === 'zh' ? '数据来源' : 'Source'}: {srcLabel.label}
                      {majorsData.data.source && (majorsData.data.source.title || majorsData.data.source.url)
                        ? ` · ${majorsData.data.source.title || majorsData.data.source.url}`
                        : ''}
                    </div>
                  )}
                </div>
                );
              })()}
            </div>

            {/* ── Wave 10-C Section 1: 院校元数据 (meta + discipline rank) ─────── */}
            <div data-testid="school-detail-meta-section" style={{ marginBottom: 40 }}>
              <h2 style={{ fontFamily: 'var(--pb-serif)', fontSize: 20, fontWeight: 600, marginBottom: 16, marginTop: 0 }}>
                {lang === 'zh' ? '院校信息' : 'School Information'}
              </h2>
              {metaData.status === 'loading' ? (
                <div style={{ padding: 20, fontSize: 13, color: 'var(--pb-fg-muted)' }}>
                  {lang === 'zh' ? '加载中…' : 'Loading…'}
                </div>
              ) : (
                <div
                  data-testid="school-detail-meta-panel"
                  style={{ ...cardStyle, display: 'flex', flexDirection: 'column', gap: 16 }}
                >
                  {/* Tier chips */}
                  <div>
                    <div style={labelStyle}>{lang === 'zh' ? '院校层次' : 'Tier'}</div>
                    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 4 }}>
                      {(metaData.meta ? metaData.meta.tags : (school.tags || [])).map(tag => {
                        const tagColors = {
                          '985': { bg: '#dbeafe', color: '#1e40af' },
                          '211': { bg: '#d1fae5', color: '#065f46' },
                          '双一流': { bg: '#ede9fe', color: '#5b21b6' },
                          'C9': { bg: '#fef3c7', color: '#92400e' },
                        };
                        const tc = tagColors[tag] || { bg: 'var(--pb-surface-alt)', color: 'var(--pb-fg-muted)' };
                        return (
                          <span key={tag} style={{
                            padding: '3px 10px', borderRadius: 5, fontSize: 12, fontWeight: 700,
                            background: tc.bg, color: tc.color,
                          }}>{tag}</span>
                        );
                      })}
                      {/* A5c: 双一流 badge from shuangyiliu.json — only show if not already in tags */}
                      {isSyl && !existingTagsSyl && (
                        <span
                          data-testid="school-detail-syl-badge"
                          style={{ padding: '3px 10px', borderRadius: 5, fontSize: 12, fontWeight: 700, background: '#ede9fe', color: '#5b21b6' }}
                        >
                          {lang === 'zh' ? '双一流' : 'Double First-Class'}
                        </span>
                      )}
                      {(!metaData.meta || !(metaData.meta.tags || []).length) && !(school.tags || []).length && !isSyl && (
                        <span style={{ fontSize: 13, color: 'var(--pb-fg-muted)' }}>—</span>
                      )}
                    </div>
                    {/* A5c: provenance note when shuangyiliu badge is shown */}
                    {isSyl && (
                      <div
                        data-testid="school-detail-syl-provenance"
                        style={{ fontSize: 11, color: 'var(--pb-fg-muted)', marginTop: 5 }}
                      >
                        {lang === 'zh'
                          ? '双一流名单依据公开政策整理，以教育部官方为准'
                          : 'Double First-Class list compiled from public policy; refer to MoE official list for accuracy'}
                      </div>
                    )}
                  </div>

                  {/* Location + schoolType */}
                  <div style={{ display: 'flex', gap: 32, flexWrap: 'wrap' }}>
                    <div>
                      <div style={labelStyle}>{lang === 'zh' ? '所在地' : 'Location'}</div>
                      <div style={{ fontSize: 14, marginTop: 2 }}>
                        {metaData.meta ? metaData.meta.location : (lang === 'zh' ? school.location.zh : school.location.en)}
                      </div>
                    </div>
                    {(metaData.meta && metaData.meta.schoolType) && (
                      <div>
                        <div style={labelStyle}>{lang === 'zh' ? '办学类型' : 'Type'}</div>
                        <div style={{ fontSize: 14, marginTop: 2 }}>{metaData.meta.schoolType}</div>
                      </div>
                    )}
                    {(metaData.meta && metaData.meta.website) && (
                      <div>
                        <div style={labelStyle}>{lang === 'zh' ? '官网' : 'Website'}</div>
                        <a
                          href={metaData.meta.website}
                          target="_blank"
                          rel="noopener noreferrer"
                          style={{ fontSize: 13, color: 'var(--pb-gold-600)', marginTop: 2, display: 'block' }}
                        >
                          {metaData.meta.website.replace(/^https?:\/\//, '')}
                        </a>
                      </div>
                    )}
                  </div>

                  {/* Discipline rank top-5 */}
                  {(() => {
                    const ranks = metaData.ranks || (metaData.meta && metaData.meta.disciplineRank) || [];
                    const topRanks = ranks.filter(r => r.rank === 'A+' || r.rank === 'A').slice(0, 5);
                    if (!topRanks.length) return null;
                    return (
                      <div data-testid="school-detail-discipline-ranks">
                        <div style={labelStyle}>{lang === 'zh' ? '学科评估 (A+/A 级)' : 'Discipline Eval (A+/A)'}</div>
                        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 6 }}>
                          {topRanks.map(r => (
                            <span key={r.subject} style={{
                              padding: '3px 10px', borderRadius: 5, fontSize: 12, fontWeight: 600,
                              background: r.rank === 'A+' ? '#fef3c7' : '#dbeafe',
                              color: r.rank === 'A+' ? '#92400e' : '#1e40af',
                            }}>
                              {r.subject} · {r.rank}
                            </span>
                          ))}
                        </div>
                      </div>
                    );
                  })()}
                </div>
              )}
            </div>

            {/* ── M8: consolidated 补充中 notice (non-HI / no secondary data) ──────
                Shown instead of 5-6 individual empty-body boxes when all secondary
                sections are done loading and none returned real data.              */}
            {secondarySectionsAllEmpty && (
              <div
                data-testid="school-detail-secondary-placeholder"
                style={{
                  marginBottom: 40,
                  padding: '16px 20px',
                  background: 'var(--pb-surface-alt)',
                  border: '1px solid var(--pb-border)',
                  borderRadius: 10,
                  fontSize: 13,
                  color: 'var(--pb-fg-muted)',
                  lineHeight: 1.75,
                }}
              >
                <div style={{ fontWeight: 600, marginBottom: 6, color: 'var(--pb-fg)' }}>
                  {lang === 'zh' ? '更多资料（数据建设中）' : 'More Data (Coming Soon)'}
                </div>
                {lang === 'zh'
                  ? '以下维度暂无该省份真实核验数据，不显示占位内容（宁缺勿错）：就业数据 · 招生计划 · 招生章程 · 校园生活 · 奖学金 · 升学与转专业。目前已收录海南省数据，其他省份陆续接入中。'
                  : 'The following sections have no real-verified data for this province and are withheld to avoid misinformation: Employment · Enrollment Plan · Admission Charter · Campus Life · Scholarships · Grad Promotion Policy. Data is currently verified for Hainan; other provinces are being added.'}
              </div>
            )}

            {/* ── Wave 10-C Section 2: 就业数据 ─────────────────────────────────── */}
            <div data-testid="school-detail-employment-section" style={{ marginBottom: 40 }}>
              <h2 style={{ fontFamily: 'var(--pb-serif)', fontSize: 20, fontWeight: 600, marginBottom: 16, marginTop: 0 }}>
                {lang === 'zh' ? '就业数据' : 'Employment Data'}
              </h2>
              {employData.status === 'loading' ? (
                <div style={{ padding: 20, fontSize: 13, color: 'var(--pb-fg-muted)' }}>
                  {lang === 'zh' ? '加载中…' : 'Loading…'}
                </div>
              ) : employData.data ? (() => {
                // M4 EMP-1 (诚实优先): honor FIELD-LEVEL verification. A row can carry
                // officially-sourced metrics (e.g. employmentRate from a 教学质量报告)
                // even when sourceVerified===false because another field (avgSalary)
                // is still a legacy estimate. Per-field flags decide the 估算 marker so
                // real data is no longer under-claimed as an estimate.
                const _ed = employData.data;
                const _fieldVerified = (flag) => _ed[flag] === true;
                // A metric is shown as an estimate when its own field flag is not true.
                // (avgSalary has no official source anywhere, so it is always an estimate.)
                const _estTag = () => (
                  <span style={{ marginLeft: 6, fontSize: 10, fontWeight: 600, color: 'var(--pb-fg-muted)', verticalAlign: 'middle' }}>
                    {lang === 'zh' ? '· 估算' : '· est.'}
                  </span>
                );
                const erEst = typeof _ed.employmentRate === 'number' && !_fieldVerified('employmentRateSourceVerified');
                const asEst = typeof _ed.avgSalary === 'number' && !_fieldVerified('avgSalarySourceVerified');
                const gsEst = typeof _ed.gradSchoolRate === 'number' && !_fieldVerified('gradSchoolRateSourceVerified');
                const anyVerified = _fieldVerified('employmentRateSourceVerified')
                  || _fieldVerified('avgSalarySourceVerified')
                  || _fieldVerified('gradSchoolRateSourceVerified');
                const anyEstimate = erEst || asEst || gsEst;
                return (
                <div
                  data-testid="school-detail-employment-panel"
                  style={{ ...cardStyle }}
                >
                  <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 20 }}>
                    {typeof _ed.employmentRate === 'number' && (
                      <div>
                        <div style={labelStyle}>{lang === 'zh' ? '就业率' : 'Employment Rate'}{erEst && _estTag()}</div>
                        <div style={{ ...bigNumStyle, fontSize: 24 }}>{_ed.employmentRate}%</div>
                      </div>
                    )}
                    {typeof _ed.avgSalary === 'number' && (
                      <div>
                        <div style={labelStyle}>{lang === 'zh' ? '平均月薪' : 'Avg Monthly Salary'}{asEst && _estTag()}</div>
                        <div style={{ ...bigNumStyle, fontSize: 24 }}>
                          {lang === 'zh'
                            ? `¥${(_ed.avgSalary / 1000).toFixed(0)}K`
                            : `¥${_ed.avgSalary.toLocaleString()}`}
                        </div>
                      </div>
                    )}
                    {typeof _ed.gradSchoolRate === 'number' && (
                      <div>
                        <div style={labelStyle}>{lang === 'zh' ? '考研深造率' : 'Grad School Rate'}{gsEst && _estTag()}</div>
                        <div style={{ ...bigNumStyle, fontSize: 24 }}>{_ed.gradSchoolRate}%</div>
                      </div>
                    )}
                    {Array.isArray(_ed.topDestinations) && _ed.topDestinations.length > 0 && (
                      <div>
                        <div style={labelStyle}>{lang === 'zh' ? '主要去向' : 'Top Destinations'}</div>
                        <div style={{ fontSize: 13, lineHeight: 1.7, marginTop: 4 }}>
                          {_ed.topDestinations.slice(0, 5).join(' · ')}
                        </div>
                      </div>
                    )}
                  </div>
                  {_ed.year && (
                    <div data-testid="school-detail-employment-source-note" style={{ marginTop: 12, fontSize: 11, color: 'var(--pb-fg-muted)' }}>
                      {lang === 'zh'
                        ? `数据年份：${_ed.year}${
                            anyVerified && anyEstimate ? ' · 部分字段为估算，其余为官方核验值'
                            : anyEstimate ? ' · 估算，非官方核验值'
                            : ' · 官方核验值'}`
                        : `Data year: ${_ed.year}${
                            anyVerified && anyEstimate ? ' · some fields estimated, others source-verified'
                            : anyEstimate ? ' · estimate, not source-verified'
                            : ' · source-verified'}`}
                    </div>
                  )}
                </div>
                );
              })() : (
                /* M8: suppress individual placeholder when consolidated notice is shown */
                !secondarySectionsAllEmpty && (
                <div style={{
                  padding: '8px 12px', background: 'var(--pb-surface-alt)', borderRadius: 8,
                  fontSize: 12, color: 'var(--pb-fg-muted)',
                }}>
                  {lang === 'zh' ? '该院校就业数据正在补充中，敬请期待。' : 'Employment data for this school is being collected.'}
                </div>
                )
              )}
            </div>

            {/* ── Wave 10-C Section 3: 招生计划 ─────────────────────────────────── */}
            <div data-testid="school-detail-plan-section" style={{ marginBottom: 40 }}>
              <h2 style={{ fontFamily: 'var(--pb-serif)', fontSize: 20, fontWeight: 600, marginBottom: 16, marginTop: 0 }}>
                {lang === 'zh' ? '招生计划' : 'Enrollment Plan'}
              </h2>
              {planData.status === 'loading' ? (
                <div style={{ padding: 20, fontSize: 13, color: 'var(--pb-fg-muted)' }}>
                  {lang === 'zh' ? '加载中…' : 'Loading…'}
                </div>
              ) : planData.total != null ? (
                <div
                  data-testid="school-detail-plan-panel"
                  style={{ ...cardStyle }}
                >
                  <div style={labelStyle}>
                    {lang === 'zh'
                      ? `${params.year} 年 ${(() => { const p = [{ code: 'HI', zh: '海南' }, { code: 'BJ', zh: '北京' }, { code: 'SH', zh: '上海' }, { code: 'GD', zh: '广东' }, { code: 'JS', zh: '江苏' }, { code: 'ZJ', zh: '浙江' }, { code: 'SD', zh: '山东' }].find(x => x.code === params.province); return p ? p.zh : params.province; })() } 招生人数`
                      : `${params.year} Enrollment in ${params.province}`}
                  </div>
                  <div style={{ ...bigNumStyle, marginTop: 6 }}>
                    {planData.total} {lang === 'zh' ? '人' : 'students'}
                  </div>
                  <div style={{ fontSize: 12, color: 'var(--pb-fg-muted)', marginTop: 4 }}>
                    {lang === 'zh' ? '（各专业组合计）' : '(sum across all major groups)'}
                  </div>
                </div>
              ) : (
                !secondarySectionsAllEmpty && (
                <div style={{
                  padding: '8px 12px', background: 'var(--pb-surface-alt)', borderRadius: 8,
                  fontSize: 12, color: 'var(--pb-fg-muted)',
                }}>
                  {lang === 'zh' ? '该省份招生计划数据正在补充中，敬请期待。' : 'Enrollment plan data for this province is being collected.'}
                </div>
                )
              )}
            </div>

            {/* ── Wave 10-C Section 4: 招生章程 + 限制 ─────────────────────────── */}
            <div data-testid="school-detail-rules-section" style={{ marginBottom: 40 }}>
              <h2 style={{ fontFamily: 'var(--pb-serif)', fontSize: 20, fontWeight: 600, marginBottom: 16, marginTop: 0 }}>
                {lang === 'zh' ? '招生章程与限制' : 'Admission Charter & Restrictions'}
              </h2>
              {rulesData.status === 'loading' ? (
                <div style={{ padding: 20, fontSize: 13, color: 'var(--pb-fg-muted)' }}>
                  {lang === 'zh' ? '加载中…' : 'Loading…'}
                </div>
              ) : rulesData.entry ? (
                <div
                  data-testid="school-detail-rules-panel"
                  style={{ ...cardStyle, display: 'flex', flexDirection: 'column', gap: 16 }}
                >
                  {/* Charter URL */}
                  {rulesData.entry.charterUrl && (
                    <div>
                      <div style={labelStyle}>{lang === 'zh' ? '招生章程' : 'Admission Charter'}</div>
                      <a
                        href={rulesData.entry.charterUrl}
                        target="_blank"
                        rel="noopener noreferrer"
                        style={{ fontSize: 14, color: 'var(--pb-gold-600)', marginTop: 4, display: 'inline-block' }}
                      >
                        {lang === 'zh' ? '查看招生章程 →' : 'View Admission Charter →'}
                      </a>
                    </div>
                  )}

                  {/* Restrictions list */}
                  {Array.isArray(rulesData.entry.restrictions) && rulesData.entry.restrictions.length > 0 && (
                    <div data-testid="school-detail-restrictions">
                      <div style={labelStyle}>{lang === 'zh' ? '报考限制' : 'Restrictions'}</div>
                      <div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginTop: 8 }}>
                        {rulesData.entry.restrictions.map((r, i) => {
                          const isHard = r.severity === 'hard';
                          return (
                            <div key={i} style={{
                              padding: '8px 12px', borderRadius: 6, fontSize: 13,
                              background: isHard ? '#fff1f2' : '#fffbeb',
                              border: `1px solid ${isHard ? '#fecdd3' : '#fde68a'}`,
                              display: 'flex', gap: 8, alignItems: 'flex-start',
                            }}>
                              <span style={{
                                flexShrink: 0,
                                padding: '1px 7px', borderRadius: 4, fontSize: 11, fontWeight: 700,
                                background: isHard ? '#fee2e2' : '#fef3c7',
                                color: isHard ? '#b91c1c' : '#92400e',
                              }}>
                                {isHard ? (lang === 'zh' ? '硬限制' : 'Hard') : (lang === 'zh' ? '软限制' : 'Soft')}
                              </span>
                              <div>
                                <div style={{ fontWeight: 600, color: isHard ? '#b91c1c' : '#92400e' }}>
                                  {r.detail}
                                </div>
                                {r.majorPattern && (
                                  <div style={{ fontSize: 12, color: 'var(--pb-fg-muted)', marginTop: 2 }}>
                                    {lang === 'zh' ? '适用专业' : 'Applies to'}: {r.majorPattern.replace(/\|/g, ' / ')}
                                  </div>
                                )}
                              </div>
                            </div>
                          );
                        })}
                      </div>
                    </div>
                  )}
                </div>
              ) : (
                !secondarySectionsAllEmpty && (
                <div style={{
                  padding: '8px 12px', background: 'var(--pb-surface-alt)', borderRadius: 8,
                  fontSize: 12, color: 'var(--pb-fg-muted)',
                }}>
                  {lang === 'zh' ? '该院校招生章程数据正在补充中，敬请期待。' : 'Admission charter data for this school is being collected.'}
                </div>
                )
              )}
            </div>

            {/* ── Wave 11-D Section 5: 校园生活 ──────────────────────────────── */}
            <div data-testid="school-detail-campus-section" style={{ marginBottom: 40 }}>
              <h2 style={{ fontFamily: 'var(--pb-serif)', fontSize: 20, fontWeight: 600, marginBottom: 16, marginTop: 0 }}>
                {lang === 'zh' ? '校园生活' : 'Campus Life'}
              </h2>
              {campusLifeData.status === 'loading' ? (
                <div style={{ padding: 20, fontSize: 13, color: 'var(--pb-fg-muted)' }}>
                  {lang === 'zh' ? '加载中…' : 'Loading…'}
                </div>
              ) : campusLifeData.entry ? (
                <div
                  data-testid="school-detail-campus-panel"
                  style={{ ...cardStyle, display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 20 }}
                >
                  {campusLifeData.entry.campusInfo.dormCondition && (
                    <div>
                      <div style={labelStyle}>{lang === 'zh' ? '宿舍条件' : 'Dorm'}</div>
                      <div style={{ fontSize: 14, marginTop: 4 }}>{campusLifeData.entry.campusInfo.dormCondition}</div>
                    </div>
                  )}
                  {campusLifeData.entry.campusInfo.cafeteria && (
                    <div>
                      <div style={labelStyle}>{lang === 'zh' ? '食堂' : 'Cafeteria'}</div>
                      <div style={{ fontSize: 14, marginTop: 4 }}>{campusLifeData.entry.campusInfo.cafeteria}</div>
                    </div>
                  )}
                  {campusLifeData.entry.campusInfo.sports && (
                    <div>
                      <div style={labelStyle}>{lang === 'zh' ? '体育设施' : 'Sports Facilities'}</div>
                      <div style={{ fontSize: 14, marginTop: 4 }}>{campusLifeData.entry.campusInfo.sports}</div>
                    </div>
                  )}
                  {campusLifeData.entry.campusInfo.genderRatio && (
                    <div>
                      <div style={labelStyle}>{lang === 'zh' ? '男女比例' : 'Gender Ratio'}</div>
                      <div style={{ fontSize: 14, marginTop: 4 }}>{campusLifeData.entry.campusInfo.genderRatio}</div>
                    </div>
                  )}
                  {campusLifeData.entry.campusInfo.cityCenter && (
                    <div>
                      <div style={labelStyle}>{lang === 'zh' ? '距市中心' : 'Distance to City Center'}</div>
                      <div style={{ fontSize: 14, marginTop: 4 }}>{campusLifeData.entry.campusInfo.cityCenter}</div>
                    </div>
                  )}
                </div>
              ) : (
                !secondarySectionsAllEmpty && (
                <div style={{
                  padding: '8px 12px', background: 'var(--pb-surface-alt)', borderRadius: 8,
                  fontSize: 12, color: 'var(--pb-fg-muted)',
                }}>
                  {lang === 'zh' ? '该院校校园生活数据正在补充中，敬请期待。' : 'Campus life data for this school is being collected.'}
                </div>
                )
              )}
            </div>

            {/* ── Wave 11-D Section 6: 奖学金 ────────────────────────────────── */}
            <div data-testid="school-detail-awards-section" style={{ marginBottom: 40 }}>
              <h2 style={{ fontFamily: 'var(--pb-serif)', fontSize: 20, fontWeight: 600, marginBottom: 16, marginTop: 0 }}>
                {lang === 'zh' ? '奖学金' : 'Scholarships'}
              </h2>
              {awardsData.status === 'loading' ? (
                <div style={{ padding: 20, fontSize: 13, color: 'var(--pb-fg-muted)' }}>
                  {lang === 'zh' ? '加载中…' : 'Loading…'}
                </div>
              ) : awardsData.entry ? (
                <div
                  data-testid="school-detail-awards-panel"
                  style={{ ...cardStyle, display: 'flex', flexDirection: 'column', gap: 16 }}
                >
                  {/* National award */}
                  {awardsData.entry.awards.national && (
                    <div>
                      <div style={labelStyle}>{lang === 'zh' ? '国家奖学金' : 'National Scholarship'}</div>
                      <div style={{
                        marginTop: 6, display: 'inline-block',
                        padding: '4px 12px', borderRadius: 6, fontSize: 13, fontWeight: 600,
                        background: '#fef3c7', color: '#92400e',
                        border: '1px solid #fde68a',
                      }}>
                        {awardsData.entry.awards.national}
                      </div>
                    </div>
                  )}

                  {/* School awards */}
                  {Array.isArray(awardsData.entry.awards.school) && awardsData.entry.awards.school.length > 0 && (
                    <div>
                      <div style={labelStyle}>{lang === 'zh' ? '校级奖学金' : 'School Scholarships'}</div>
                      <div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginTop: 6 }}>
                        {awardsData.entry.awards.school.map((aw, i) => (
                          <div key={i} style={{
                            padding: '6px 12px', borderRadius: 6, fontSize: 13,
                            background: '#dbeafe', color: '#1e40af',
                            border: '1px solid #bfdbfe',
                          }}>
                            {aw}
                          </div>
                        ))}
                      </div>
                    </div>
                  )}

                  {/* Private / social awards */}
                  {Array.isArray(awardsData.entry.awards.private) && awardsData.entry.awards.private.length > 0 && (
                    <div>
                      <div style={labelStyle}>{lang === 'zh' ? '社会捐赠奖学金' : 'Private Scholarships'}</div>
                      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 6 }}>
                        {awardsData.entry.awards.private.map((aw, i) => (
                          <span key={i} style={{
                            padding: '3px 10px', borderRadius: 5, fontSize: 12, fontWeight: 600,
                            background: '#d1fae5', color: '#065f46',
                            border: '1px solid #a7f3d0',
                          }}>
                            {aw}
                          </span>
                        ))}
                      </div>
                    </div>
                  )}
                </div>
              ) : (
                !secondarySectionsAllEmpty && (
                <div style={{
                  padding: '8px 12px', background: 'var(--pb-surface-alt)', borderRadius: 8,
                  fontSize: 12, color: 'var(--pb-fg-muted)',
                }}>
                  {lang === 'zh' ? '该院校奖学金数据正在补充中，敬请期待。' : 'Scholarship data for this school is being collected.'}
                </div>
                )
              )}
            </div>

            {/* ── Wave 11-D Section 7: 保研率 + 转专业政策 ───────────────────── */}
            <div data-testid="school-detail-academic-section" style={{ marginBottom: 40 }}>
              <h2 style={{ fontFamily: 'var(--pb-serif)', fontSize: 20, fontWeight: 600, marginBottom: 16, marginTop: 0 }}>
                {lang === 'zh' ? '升学与转专业' : 'Grad Promotion & Transfer Policy'}
              </h2>
              {academicPolicyData.status === 'loading' ? (
                <div style={{ padding: 20, fontSize: 13, color: 'var(--pb-fg-muted)' }}>
                  {lang === 'zh' ? '加载中…' : 'Loading…'}
                </div>
              ) : academicPolicyData.entry ? (
                <div
                  data-testid="school-detail-academic-panel"
                  style={{ ...cardStyle, display: 'flex', flexDirection: 'column', gap: 20 }}
                >
                  {/* Grad promotion rate */}
                  {typeof academicPolicyData.entry.gradPromotionRate === 'number' && (
                    <div>
                      <div style={labelStyle}>{lang === 'zh' ? '保研率（推免比例）' : 'Grad Promotion Rate'}</div>
                      <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginTop: 4 }}>
                        <div style={{ ...bigNumStyle, fontSize: 28, color: 'var(--pb-gold-600)' }}>
                          {(academicPolicyData.entry.gradPromotionRate * 100).toFixed(0)}%
                        </div>
                        <div style={{ fontSize: 12, color: 'var(--pb-fg-muted)' }}>
                          {lang === 'zh' ? '（本科生推免读研比例）' : 'of undergrads promoted to grad programs'}
                        </div>
                      </div>
                      {/* Progress bar */}
                      <div style={{ marginTop: 8, height: 6, borderRadius: 3, background: 'var(--pb-border)', overflow: 'hidden' }}>
                        <div style={{
                          height: '100%',
                          width: `${Math.min(100, academicPolicyData.entry.gradPromotionRate * 100)}%`,
                          background: 'var(--pb-gold-500)',
                          borderRadius: 3,
                          transition: 'width 0.4s ease',
                        }} />
                      </div>
                    </div>
                  )}

                  {/* Transfer policy */}
                  {academicPolicyData.entry.transferPolicy && (
                    <div>
                      <div style={labelStyle}>{lang === 'zh' ? '转专业政策' : 'Transfer Policy'}</div>
                      <div style={{
                        marginTop: 6, padding: '10px 14px', borderRadius: 8,
                        background: 'var(--pb-surface-alt)', border: '1px solid var(--pb-border)',
                        fontSize: 13, lineHeight: 1.7, color: 'var(--pb-fg)',
                      }}>
                        {academicPolicyData.entry.transferPolicy}
                      </div>
                    </div>
                  )}
                </div>
              ) : (
                !secondarySectionsAllEmpty && (
                <div style={{
                  padding: '8px 12px', background: 'var(--pb-surface-alt)', borderRadius: 8,
                  fontSize: 12, color: 'var(--pb-fg-muted)',
                }}>
                  {lang === 'zh' ? '该院校升学与转专业数据正在补充中，敬请期待。' : 'Academic policy data for this school is being collected.'}
                </div>
                )
              )}
            </div>

            {/* ── Wave 15-C Section 8: 历年警示案例 — hidden until real data verified (#data-authenticity) ── */}
            {MISTAKES_SECTION_ENABLED && (
            <div data-testid="school-detail-mistakes-section" style={{ marginBottom: 40 }}>
              <h2 style={{ fontFamily: 'var(--pb-serif)', fontSize: 20, fontWeight: 600, marginBottom: 4, marginTop: 0, display: 'flex', alignItems: 'center', gap: 8 }}>
                <span style={{ fontSize: 18 }}>⚠️</span>
                {lang === 'zh' ? '历年志愿失误警示' : 'Historical Application Mistakes'}
              </h2>
              <div style={{ fontSize: 13, color: 'var(--pb-fg-muted)', marginBottom: 16 }}>
                {lang === 'zh'
                  ? '以下为公开渠道整理的历年志愿翻车案例，仅供参考，请结合自身情况理性分析。'
                  : 'The following cases are compiled from public sources for reference only.'}
              </div>
              {mistakesData.status === 'loading' ? (
                <div style={{ padding: 20, fontSize: 13, color: 'var(--pb-fg-muted)' }}>
                  {lang === 'zh' ? '加载中…' : 'Loading…'}
                </div>
              ) : mistakesData.cases.length > 0 ? (
                <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
                  {mistakesData.cases.map((c) => {
                    const typeColorMap = {
                      gradient_too_steep: { bg: '#fef3c7', color: '#92400e', border: '#fde68a', label: lang === 'zh' ? '梯度过大' : 'Gradient Too Steep' },
                      subject_mismatch:   { bg: '#fee2e2', color: '#991b1b', border: '#fca5a5', label: lang === 'zh' ? '选科不符' : 'Subject Mismatch' },
                      rank_overestimate:  { bg: '#fce7f3', color: '#9d174d', border: '#f9a8d4', label: lang === 'zh' ? '位次高估' : 'Rank Overestimate' },
                      major_misalign:     { bg: '#ede9fe', color: '#5b21b6', border: '#c4b5fd', label: lang === 'zh' ? '专业错位' : 'Major Misalign' },
                      low_safety_buffer:  { bg: '#fff7ed', color: '#9a3412', border: '#fdba74', label: lang === 'zh' ? '保底不足' : 'Low Safety Buffer' },
                      no_match:           { bg: '#f1f5f9', color: '#334155', border: '#cbd5e1', label: lang === 'zh' ? '全无匹配' : 'No Match' },
                    };
                    const tc = typeColorMap[c.mistakeType] || typeColorMap['no_match'];
                    return (
                      <div
                        key={c.caseId}
                        data-testid={`mistake-card-${c.caseId}`}
                        style={{
                          border: '1px solid var(--pb-border)',
                          borderLeft: `4px solid ${tc.border}`,
                          borderRadius: 10,
                          padding: '16px 20px',
                          background: 'var(--pb-surface)',
                        }}
                      >
                        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 10, flexWrap: 'wrap', gap: 8 }}>
                          <div style={{ fontWeight: 600, fontSize: 14, lineHeight: 1.4 }}>
                            {c.target}
                          </div>
                          <span style={{
                            padding: '2px 10px', borderRadius: 5, fontSize: 11, fontWeight: 700,
                            background: tc.bg, color: tc.color, border: `1px solid ${tc.border}`,
                            whiteSpace: 'nowrap',
                          }}>
                            {tc.label}
                          </span>
                        </div>
                        <div style={{ display: 'flex', gap: 20, marginBottom: 10, flexWrap: 'wrap' }}>
                          <div>
                            <div style={{ fontSize: 11, color: 'var(--pb-fg-muted)', marginBottom: 2 }}>
                              {lang === 'zh' ? '考生分数' : 'Score'}
                            </div>
                            <div style={{ fontFamily: 'var(--pb-mono)', fontWeight: 700, fontSize: 18, color: 'var(--pb-fg)' }}>
                              {c.studentScore}{lang === 'zh' ? '分' : ' pts'}
                            </div>
                          </div>
                          {c.studentRank > 0 && (
                            <div>
                              <div style={{ fontSize: 11, color: 'var(--pb-fg-muted)', marginBottom: 2 }}>
                                {lang === 'zh' ? '省内位次' : 'Rank'}
                              </div>
                              <div style={{ fontFamily: 'var(--pb-mono)', fontWeight: 700, fontSize: 18, color: 'var(--pb-fg)' }}>
                                #{c.studentRank.toLocaleString()}
                              </div>
                            </div>
                          )}
                          <div style={{ flex: 1 }}>
                            <div style={{ fontSize: 11, color: 'var(--pb-fg-muted)', marginBottom: 2 }}>
                              {lang === 'zh' ? '最终结果' : 'Outcome'}
                            </div>
                            <div style={{ fontSize: 13, fontWeight: 600, color: '#dc2626' }}>
                              {c.actualOutcome}
                            </div>
                          </div>
                        </div>
                        <div style={{
                          padding: '10px 14px', borderRadius: 8,
                          background: 'var(--pb-surface-alt)', border: '1px solid var(--pb-border)',
                          fontSize: 13, lineHeight: 1.7, color: 'var(--pb-fg)',
                        }}>
                          <span style={{ fontWeight: 600, marginRight: 4 }}>
                            {lang === 'zh' ? '教训：' : 'Lesson: '}
                          </span>
                          {c.lessonLearned}
                        </div>
                        {c.recovery && (
                          <div
                            data-testid={`mistake-recovery-${c.caseId}`}
                            style={{
                              marginTop: 10,
                              padding: '10px 14px', borderRadius: 8,
                              background: '#f0fdf4', border: '1px solid #bbf7d0',
                              fontSize: 13, lineHeight: 1.7, color: '#14532d',
                            }}
                          >
                            <span style={{ fontWeight: 600, marginRight: 4 }}>
                              {lang === 'zh' ? '💡 启示：' : '💡 Recovery: '}
                            </span>
                            {c.recovery}
                          </div>
                        )}
                        {Array.isArray(c.tags) && c.tags.length > 0 && (
                          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 10 }}>
                            {c.tags.map((tag, i) => (
                              <span key={i} style={{
                                padding: '2px 8px', borderRadius: 4, fontSize: 11,
                                background: 'var(--pb-surface-alt)', color: 'var(--pb-fg-muted)',
                                border: '1px solid var(--pb-border)',
                              }}>
                                #{tag}
                              </span>
                            ))}
                          </div>
                        )}
                      </div>
                    );
                  })}
                </div>
              ) : (
                <div style={{
                  padding: '8px 12px', background: 'var(--pb-surface-alt)', borderRadius: 8,
                  fontSize: 12, color: 'var(--pb-fg-muted)',
                }}>
                  {lang === 'zh' ? '该院校暂无收录警示案例，填报时仍请保持合理梯度与保底策略。' : 'No recorded cases for this school. Always maintain a safe gradient and buffer strategy.'}
                </div>
              )}
            </div>
            )}

            {/* ── 院校详情页 overhaul: 院校档案 (cn-school-ref profile-detail) ────── */}
            {prof && (
              <div data-testid="school-detail-profile" style={{ marginBottom: 8 }}>
                <h2 style={{ fontFamily: 'var(--pb-serif)', fontSize: 20, fontWeight: 600, marginBottom: 6, marginTop: 0 }}>
                  {lang === 'zh' ? '院校档案（全国）' : 'School Profile (National)'}
                </h2>
                <div style={{ fontSize: 12, color: 'var(--pb-fg-muted)', marginBottom: 16 }}>
                  {lang === 'zh'
                    ? '综合招生章程、学科评估、专业与就业等公开资料整理，供参考。'
                    : 'Compiled from public admission charters, discipline evaluations, majors and employment data.'}
                </div>

                {/* 院校介绍 */}
                {profIntro && Array.isArray(profIntro.paragraphs) && profIntro.paragraphs.length > 0 && (
                  <div data-testid="school-detail-intro" style={{ ...cardStyle, marginBottom: 16 }}>
                    <div style={labelStyle}>{lang === 'zh' ? '院校介绍' : 'Overview'}</div>
                    <div style={{ fontSize: 13.5, lineHeight: 1.8, marginTop: 8, color: 'var(--pb-fg)' }}>
                      {profIntro.paragraphs.slice(0, 3).map((p, i) => (<p key={i} style={{ margin: '0 0 8px' }}>{p}</p>))}
                    </div>
                  </div>
                )}

                {/* 王牌 / 一流 / 特色专业 */}
                {(profStarMajors.length + profFirstClass.length + profFeatureMajors.length) > 0 && (
                  <div data-testid="school-detail-majors-profile" style={{ ...cardStyle, marginBottom: 16, display: 'flex', flexDirection: 'column', gap: 14 }}>
                    {profFirstClass.length > 0 && (
                      <div>
                        <div style={labelStyle}>{lang === 'zh' ? '一流本科专业建设点' : 'First-Class Majors'}</div>
                        <div style={{ marginTop: 6 }}><ChipList items={profFirstClass} bg="#ede9fe" color="#5b21b6" /></div>
                      </div>
                    )}
                    {profStarMajors.length > 0 && (
                      <div>
                        <div style={labelStyle}>{lang === 'zh' ? '王牌专业' : 'Flagship Majors'}</div>
                        <div style={{ marginTop: 6 }}><ChipList items={profStarMajors} bg="#fef3c7" color="#92400e" /></div>
                      </div>
                    )}
                    {profFeatureMajors.length > 0 && (
                      <div>
                        <div style={labelStyle}>{lang === 'zh' ? '特色专业' : 'Featured Majors'}</div>
                        <div style={{ marginTop: 6 }}><ChipList items={profFeatureMajors} bg="#d1fae5" color="#065f46" /></div>
                      </div>
                    )}
                  </div>
                )}

                {/* 学科评估 (profile) — fallback when the meta panel had no A/A+ ranks */}
                {profDisc.length > 0 && !(metaData.ranks && metaData.ranks.length) && (
                  <div data-testid="school-detail-profile-disc" style={{ ...cardStyle, marginBottom: 16 }}>
                    <div style={labelStyle}>{lang === 'zh' ? '学科评估' : 'Discipline Evaluation'}</div>
                    <div style={{ marginTop: 8 }}>
                      <ChipList
                        items={(profDisc.some((d) => /^A/.test(d.grade)) ? profDisc.filter((d) => /^A/.test(d.grade)) : profDisc.slice(0, 24)).map((d) => `${d.name} · ${d.grade}`)}
                        bg="#dbeafe"
                        color="#1e40af"
                        max={24}
                      />
                    </div>
                  </div>
                )}

                {/* 各省录取分数 (全国) */}
                {profScoreTbls.length > 0 && (
                  <div data-testid="school-detail-profile-scores" style={{ marginBottom: 16 }}>
                    <div style={{ ...labelStyle, marginBottom: 8 }}>
                      {lang === 'zh' ? `各省录取分数${prof['录取分数'].year ? `（${prof['录取分数'].year}）` : ''}` : 'Provincial Admission Scores'}
                    </div>
                    {renderProfTable(profScoreTbls[0], 16)}
                  </div>
                )}

                {/* 招生计划 (profile tables) */}
                {profPlanTbls.length > 0 && (
                  <div data-testid="school-detail-profile-plan" style={{ marginBottom: 16 }}>
                    <div style={{ ...labelStyle, marginBottom: 8 }}>
                      {lang === 'zh' ? `招生计划明细${prof['招生计划'].year ? `（${prof['招生计划'].year}）` : ''}` : 'Enrollment Plan (detail)'}
                    </div>
                    {renderProfTable(profPlanTbls[0], 14)}
                  </div>
                )}

                {/* 录取规则 (第X条) — collapsed by default: full charter clauses are
                    reference material, not first-read content (they used to dump
                    8 regulation paragraphs straight into the page flow) */}
                {profRules.length > 0 && (
                  <details className="pb-gk-details" data-testid="school-detail-profile-rules" style={{ marginBottom: 16 }}>
                    <summary>{lang === 'zh' ? `录取规则（${Math.min(profRules.length, 8)} 条）` : `Admission Rules (${Math.min(profRules.length, 8)})`}</summary>
                    <div className="pb-gk-details-body" style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                      {profRules.slice(0, 8).map((s, i) => (
                        <div key={i} style={{ fontSize: 13, lineHeight: 1.7 }}>
                          {s.title && <span style={{ fontWeight: 600, marginRight: 6 }}>{s.title}</span>}
                          <span style={{ color: 'var(--pb-fg)' }}>{s.text}</span>
                        </div>
                      ))}
                    </div>
                  </details>
                )}

                {/* 收费 + 奖助学金 */}
                {(profFees || profScholar) && (
                  <div data-testid="school-detail-profile-fees" style={{ ...cardStyle, marginBottom: 16, display: 'flex', flexDirection: 'column', gap: 14 }}>
                    {profFees && Array.isArray(profFees.tuitionRanges) && profFees.tuitionRanges.length > 0 && (
                      <div>
                        <div style={labelStyle}>{lang === 'zh' ? '收费标准' : 'Tuition'}</div>
                        <div style={{ fontSize: 13, lineHeight: 1.8, marginTop: 4 }}>{profFees.tuitionRanges.slice(0, 6).join('；')}</div>
                        {profFees.dormFee && <div style={{ fontSize: 13, marginTop: 4, color: 'var(--pb-fg-muted)' }}>{profFees.dormFee}</div>}
                      </div>
                    )}
                    {profScholar && Array.isArray(profScholar.paragraphs) && profScholar.paragraphs.length > 0 && (
                      <div>
                        <div style={labelStyle}>{lang === 'zh' ? '奖助学金' : 'Scholarships & Aid'}</div>
                        <div style={{ fontSize: 13, lineHeight: 1.8, marginTop: 4 }}>
                          {profScholar.paragraphs.slice(0, 4).map((p, i) => (<p key={i} style={{ margin: '0 0 6px' }}>{p}</p>))}
                        </div>
                      </div>
                    )}
                  </div>
                )}

                {/* 就业概况 (profile prose) — fallback when province employ panel empty */}
                {profEmployment && !employData.data && Array.isArray(profEmployment.paragraphs) && profEmployment.paragraphs.length > 0 && (
                  <div data-testid="school-detail-profile-employment" style={{ ...cardStyle, marginBottom: 16 }}>
                    <div style={labelStyle}>{lang === 'zh' ? '就业概况' : 'Employment Overview'}</div>
                    <div style={{ fontSize: 13, lineHeight: 1.8, marginTop: 6 }}>
                      {profEmployment.paragraphs.slice(0, 4).map((p, i) => (<p key={i} style={{ margin: '0 0 6px' }}>{p}</p>))}
                    </div>
                  </div>
                )}

                {/* 双一流建设学科 — prefer profile-detail data; fall back to shuangyiliu.json */}
                {(() => {
                  const profDiscs = (profSyl && Array.isArray(profSyl.buildingDisciplines)) ? profSyl.buildingDisciplines : [];
                  // sylDisciplines: [] means 整体建设 (一流大学建设高校, no separate discipline list)
                  // Only show shuangyiliu disciplines when profSyl is absent AND list is non-empty
                  const showSylDiscs = isSyl && sylDisciplines.length > 0 && profDiscs.length === 0;
                  if (profDiscs.length > 0) {
                    return (
                      <div data-testid="school-detail-profile-syl" style={{ ...cardStyle, marginBottom: 16 }}>
                        <div style={labelStyle}>{lang === 'zh' ? '双一流建设学科' : 'Double First-Class Disciplines'}</div>
                        <div style={{ marginTop: 8 }}><ChipList items={profDiscs} bg="#ede9fe" color="#5b21b6" /></div>
                      </div>
                    );
                  }
                  if (showSylDiscs) {
                    return (
                      <div data-testid="school-detail-syl-disciplines" style={{ ...cardStyle, marginBottom: 16 }}>
                        <div style={labelStyle}>{lang === 'zh' ? '双一流建设学科' : 'Double First-Class Disciplines'}</div>
                        <div style={{ marginTop: 8 }}><ChipList items={sylDisciplines} bg="#ede9fe" color="#5b21b6" /></div>
                      </div>
                    );
                  }
                  return null;
                })()}
              </div>
            )}

            {/* Similar schools */}
            {similarSchools.length > 0 && (
              <>
                <h2 style={{ fontFamily: 'var(--pb-serif)', fontSize: 20, fontWeight: 600, marginBottom: 16, marginTop: 0 }}>
                  {lang === 'zh' ? '同位次院校' : 'Schools at Similar Rank'}
                </h2>
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 14, marginBottom: 24 }}>
                  {similarSchools.map(sim => {
                    const simName = lang === 'zh' ? sim.name.zh : sim.name.en;
                    const simCity = lang === 'zh' ? sim.location.zh : sim.location.en;
                    const simTier = tierColors[sim.tier] || { bg: 'var(--pb-surface-alt)', color: 'var(--pb-fg-muted)' };
                    const simProb = params.score > 0
                      ? getProb(sim.id, params.province, params.year, params.score, params.rank)
                      : null;
                    return (
                      <div
                        key={sim.id}
                        data-testid={`school-detail-similar-card-${sim.id}`}
                        onClick={() => {
                          const p = new URLSearchParams();
                          p.set('province', params.province);
                          p.set('year', String(params.year));
                          if (params.score) p.set('score', String(params.score));
                          if (params.rank)  p.set('rank',  String(params.rank));
                          window.location.hash = `#/gaokao/school/${sim.id}?${p.toString()}`;
                        }}
                        style={{
                          ...cardStyle,
                          cursor: 'pointer',
                          transition: 'box-shadow 0.15s, transform 0.15s',
                        }}
                        onMouseEnter={e => {
                          e.currentTarget.style.boxShadow = '0 6px 20px -6px rgba(11,31,58,0.18)';
                          e.currentTarget.style.transform = 'translateY(-2px)';
                        }}
                        onMouseLeave={e => {
                          e.currentTarget.style.boxShadow = 'none';
                          e.currentTarget.style.transform = 'none';
                        }}
                      >
                        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 6 }}>
                          <div style={{ fontFamily: 'var(--pb-serif)', fontSize: 15, fontWeight: 600, lineHeight: 1.3 }}>
                            {simName}
                          </div>
                          <span style={{ padding: '2px 7px', fontSize: 10, fontWeight: 600, borderRadius: 4, background: simTier.bg, color: simTier.color, flexShrink: 0, marginLeft: 8 }}>
                            {sim.tier}
                          </span>
                        </div>
                        <div style={{ fontSize: 12, color: 'var(--pb-fg-muted)', marginBottom: 8 }}>📍 {simCity}</div>
                        <div style={{ display: 'flex', gap: 12, fontSize: 12 }}>
                          <span style={{ fontFamily: 'var(--pb-mono)', fontWeight: 600 }}>
                            {lang === 'zh' ? '投档分' : 'Cut'}: {sim.cutoffScore}
                          </span>
                          {simProb && (
                            <span style={{ color: fineColorMap[fineKeyFor(simProb.prob)] || 'var(--pb-fg-muted)', fontWeight: 600 }}>
                              {simProb.prob}%
                            </span>
                          )}
                        </div>
                      </div>
                    );
                  })}
                </div>
              </>
            )}
          </div>

          {/* Right: action sidebar */}
          <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
            {/* Your score context */}
            {params.score > 0 && (
              <div style={{ ...cardStyle, background: 'var(--pb-navy-900)', color: '#fff', border: 'none' }}>
                <div style={{ fontSize: 12, color: 'rgba(255,255,255,0.6)', marginBottom: 8, fontWeight: 500, letterSpacing: '0.04em', textTransform: 'uppercase' }}>
                  {lang === 'zh' ? '你的信息' : 'Your Profile'}
                </div>
                <div style={{ display: 'flex', gap: 20 }}>
                  <div>
                    <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.5)' }}>{lang === 'zh' ? '分数' : 'Score'}</div>
                    <div style={{ fontFamily: 'var(--pb-mono)', fontSize: 22, fontWeight: 700, color: 'var(--pb-gold-500)' }}>
                      {params.score}
                    </div>
                  </div>
                  {params.rank > 0 && (
                    <div>
                      <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.5)' }}>{lang === 'zh' ? '位次' : 'Rank'}</div>
                      <div style={{ fontFamily: 'var(--pb-mono)', fontSize: 22, fontWeight: 700, color: 'var(--pb-gold-500)' }}>
                        {params.rank.toLocaleString()}
                      </div>
                    </div>
                  )}
                </div>
              </div>
            )}

            {/* Add to compare */}
            <button
              data-testid="school-detail-add-compare"
              className={inCompare ? 'pb-btn pb-btn-secondary' : 'pb-btn pb-btn-primary'}
              onClick={toggleCompare}
              style={{ width: '100%', padding: '12px 16px', fontSize: 14, fontWeight: 600 }}
            >
              {inCompare
                ? (lang === 'zh' ? '✓ 已加入对比' : '✓ In Compare List')
                : (lang === 'zh' ? '+ 加入对比' : '+ Add to Compare')}
            </button>

            {/* Go to compare page */}
            <button
              className="pb-btn pb-btn-secondary"
              onClick={() => {
                const p = new URLSearchParams();
                p.set('ids', school.id);
                p.set('province', params.province);
                p.set('year', String(params.year));
                if (params.score) p.set('score', String(params.score));
                window.location.hash = '#/gaokao/compare?' + p.toString();
              }}
              style={{ width: '100%', padding: '12px 16px', fontSize: 14 }}
            >
              {lang === 'zh' ? '查看院校对比' : 'Compare Schools'}
            </button>

            {/* Go to similar rank search */}
            <button
              className="pb-btn pb-btn-secondary"
              onClick={() => {
                const p = new URLSearchParams();
                p.set('province', params.province);
                p.set('year', String(params.year));
                if (effectiveCutoff) {
                  p.set('minScore', String(effectiveCutoff[0] - 20));
                  p.set('maxScore', String(effectiveCutoff[0] + 20));
                }
                window.location.hash = '#/gaokao/search?' + p.toString();
              }}
              style={{ width: '100%', padding: '12px 16px', fontSize: 14 }}
            >
              {lang === 'zh' ? '查看同位次校' : 'View Similar-Rank Schools'}
            </button>

            {/* Province selector */}
            <div style={{ ...cardStyle, marginTop: 8 }}>
              <div style={{ fontSize: 12, color: 'var(--pb-fg-muted)', marginBottom: 8, fontWeight: 500 }}>
                {lang === 'zh' ? '切换省份' : 'Switch Province'}
              </div>
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
                {PROVINCE_LIST.map(prov => {
                  const isActive = prov.code === params.province;
                  const hasData = prov.code === 'HI'
                    ? true
                    : !!(CUTOFFS[prov.code] || {})[school.id];
                  return (
                    <button
                      key={prov.code}
                      onClick={() => {
                        if (!hasData) return;
                        const p = new URLSearchParams();
                        p.set('province', prov.code);
                        p.set('year', String(params.year));
                        if (params.score) p.set('score', String(params.score));
                        if (params.rank)  p.set('rank',  String(params.rank));
                        window.location.hash = `#/gaokao/school/${school.id}?${p.toString()}`;
                      }}
                      style={{
                        padding: '4px 10px', fontSize: 12, borderRadius: 6,
                        border: `1px solid ${isActive ? 'var(--pb-gold-500)' : 'var(--pb-border)'}`,
                        background: isActive ? 'var(--pb-gold-500)' : (hasData ? 'var(--pb-surface)' : 'var(--pb-surface-alt)'),
                        color: isActive ? '#fff' : (hasData ? 'var(--pb-fg)' : 'var(--pb-fg-muted)'),
                        cursor: hasData ? 'pointer' : 'default',
                        opacity: hasData ? 1 : 0.5,
                      }}
                    >
                      {lang === 'zh' ? prov.zh : prov.en}
                    </button>
                  );
                })}
              </div>
            </div>
          </div>
        </div>
      </section>

      <PBFooter />
    </div>
  );
};
