﻿/* subpage-components.jsx - shared parts for Group F and team pages */
const { useState: useSubState } = React;
function playerPositionType(p) {
  const pos = String((p && p.pos) || "").toUpperCase();
  const role = String((p && p.role) || "").toUpperCase();
  if (pos.includes("GK") || role.includes("GK")) return "GK";
  if (/RB|LB|WB|SB/.test(role) || /WB|SB/.test(pos)) return "SB";
  if (role.includes("CB") || pos.includes("DF")) return "CB";
  if (/DM|CM/.test(role)) return "MF";
  if (/ST|CF|WG|RW|LW|AM|FW/.test(role) || pos.includes("FW")) return "FW";
  return "MF";
}

function PageShell({ active, children }) {
  const nav = [
    ["/worldcup2026/", "トップ"],
    ["/worldcup2026/groups/group-f/", "Group F分析"],
    ["/worldcup2026/teams/japan/", "日本代表"],
    ["/worldcup2026/teams/netherlands/", "オランダ"],
    ["/worldcup2026/teams/sweden/", "スウェーデン"],
    ["/worldcup2026/teams/tunisia/", "チュニジア"],
    ["/worldcup2026/broadcast/", "放送・配信"],
    ["/worldcup2026/ratings/", "Rating"],
    ["/worldcup2026/schedule/", "日程"],
    ["/worldcup2026/goods/", "観戦準備"],
    ["/worldcup2026/predictions/", "順位予想"],
    ["/worldcup2026/predictions/group-f/", "勝敗予想"],
    ["/worldcup2026/players/", "注目選手"],
    ["/worldcup2026/news/", "ニュース"]
  ];
  return <React.Fragment><nav className="sub-nav wc-section-dark"><div className="wrap sub-nav__inner"><a className="brand" href="/worldcup2026/"><span className="brand__mark"><BallMark size={20} /></span><span>AI二刀流 W杯2026<small>Data Watch Guide</small></span></a><div className="sub-nav__links">{nav.map(([href,label]) => <a key={label} className={active === label ? "is-active" : ""} href={href}>{label}</a>)}</div></div></nav>{children}</React.Fragment>;
}

function HeroSection({ eyebrow, title, subtitle, team, children }) {
  return <header className="sub-hero wc-section-dark"><div className="wrap sub-hero__inner"><div><p className="eyebrow">{eyebrow}</p><h1>{title}</h1>{subtitle && <p className="sub-hero__lead">{subtitle}</p>}{children}</div>{team && <div className="hero-stat-card wc-card-dark"><span className="flag flag--large"><Flag team={team.key} width="54" height="36" /></span><b>{team.displayName}</b><small>{team.group}</small><div className="rating-with-rank rating-with-rank--hero"><strong>{Number(team.rating).toFixed(1)}</strong><RankBadge item={team} /></div><em>{team.label}</em><span>最終更新日：{team.updated}</span></div>}</div></header>;
}

function RankBadge({ item }) {
  if (!item || !item.worldRank || !item.tournamentTeams) return null;
  return <span className="rank-badge">{item.worldRank}位／{item.tournamentTeams}チーム</span>;
}

function SectionHead({ eyebrow, title, lead, center = false }) {
  return <div className={`section-head${center ? " section-head--center" : ""}`}><p className="eyebrow">{eyebrow}</p><h2>{title}</h2>{lead && <p>{lead}</p>}</div>;
}

function scoreTone(value) {
  const v = Number(value) || 0;
  if (v >= 93) return "#EF3340";
  if (v >= 85) return "#F47B20";
  if (v >= 75) return "#F2C94C";
  if (v >= 65) return "#37C871";
  if (v >= 50) return "#2F80ED";
  return "#8A94A6";
}
function displayRating(value) {
  const n = Number(value);
  return value === null || value === undefined || value === "" || !Number.isFinite(n) ? "参考" : n.toFixed(1);
}

function AbilityBars({ axes, values, title, compact = false }) {
  return <div className={`ability-bars${compact ? " ability-bars--compact" : ""}`}>{title && <h3>{title}</h3>}{axes.map((axis, i) => { const value = Math.round(Number(values[i] || 0)); const color = scoreTone(value); return <div className="ability-bar" key={axis}><div className="ability-bar__head"><span>{axis}</span><b>{value}</b></div><div className="ability-bar__track"><i style={{ width: `${Math.max(0, Math.min(100, value))}%`, background: color }} /></div></div>; })}</div>;
}
function RatingBar({ label, value, max = 100, color }) {
  const pct = Math.max(0, Math.min(100, (value / max) * 100));
  const barColor = color || scoreTone(pct);
  return <div className="sub-ratingbar"><div><b>{label}</b><span>{Number(value).toFixed(Number.isInteger(value) ? 0 : 1)} / {max}</span></div><i><span style={{ width: `${pct}%`, background: barColor }} /></i></div>;
}

function RadarScoreList({ axes, values, color, compact = false }) {
  return <ul className={`radar-score-list${compact ? " radar-score-list--compact" : ""}`}>{axes.map((axis, i) => <li key={axis}><span><i style={{ background: color }} />{axis}</span><b>{Math.round(Number(values[i] || 0))}</b></li>)}</ul>;
}

function TeamRadar({ values, color = "#1A6FC4", size = 300, compact = false, series, labels }) {
  const radarSeries = series || [{ values, color, fill: .18 }];
  const showLabels = labels ?? !compact;
  return <div className={`sub-radar${compact ? " sub-radar--compact" : ""}${series ? " sub-radar--multi" : ""}`}><HexRadar axes={TEAM_PAGE_AXES} series={radarSeries} size={size} labels={showLabels} />{series ? <div className="radar-score-groups">{series.map((s) => <div className="radar-score-group" key={s.name}><h3><i style={{ background: s.color }} />{s.name}</h3><RadarScoreList axes={TEAM_PAGE_AXES} values={s.values} color={s.color} compact /></div>)}</div> : <RadarScoreList axes={TEAM_PAGE_AXES} values={values} color={color} compact={compact} />}</div>;
}

function PlayerRadar({ player, values, color }) {
  const type = playerPositionType(player);
  const fallbackAxes = {
    GK: ["セーブ", "配球", "ハイボール", "守備範囲", "経験", "安定"],
    CB: ["対人守備", "空中戦", "カバー", "ビルドアップ", "経験", "安定"],
    SB: ["守備対応", "推進力", "クロス創出", "運動量", "経験", "安定"],
    MF: ["回収力", "前進力", "配球", "対人守備", "運動量", "安定"],
    FW: ["得点", "創出", "推進力", "守備貢献", "経験", "安定"]
  };
  return <AbilityBars axes={fallbackAxes[type] || fallbackAxes.FW} values={values || []} compact />;
}

function AnalysisNote({ title = "私の見立て", children }) {
  return <article className="sub-analysis"><p className="eyebrow">My View</p><h2>{title}</h2><p>{children}</p></article>;
}

function StrengthWeaknessCards({ strengths, concerns }) {
  return <div className="strength-grid"><article><h3>強み</h3><ul>{strengths.map((x) => <li key={x}>{x}</li>)}</ul></article><article><h3>不安</h3><ul>{concerns.map((x) => <li key={x}>{x}</li>)}</ul></article></div>;
}

function BroadcastBadge({ text = "放送局発表待ち" }) {
  return <span className="broadcast-badge">{I.tv}{text}</span>;
}

function SourceBadge({ text = "公式発表・信頼できる公開情報を確認しながら更新" }) {
  return <span className="source-badge source-badge--dark">{I.info}{text}</span>;
}

function DisclaimerBox() {
  return <section className="sub-disclaimer wc-card-dark"><h2>{I.info} 非公式注意書き</h2><p>{OFFICIAL_NOTICE}</p></section>;
}

function StandingsTable() {
  return <div className="table-wrap"><table className="sub-table"><thead><tr><th>予想</th><th>チーム</th><th>総合Rating</th><th>独自指標順位</th><th>評価</th><th>見どころ</th></tr></thead><tbody>{GROUPF_STANDINGS.map((r) => <tr key={r.key}><td className="rank">{r.rank}</td><td><span className="flag"><Flag team={r.key} width="30" height="20" /></span>{r.team}</td><td><b>{r.rating.toFixed(1)}</b></td><td><RankBadge item={r} /></td><td>{r.label}</td><td>{r.note}</td></tr>)}</tbody></table></div>;
}

function FixturesTable({ fixtures }) {
  return <div className="table-wrap"><table className="sub-table fixtures"><thead><tr><th>節</th><th>対戦カード</th><th>日本時間</th><th>放送・配信</th><th>状態</th><th>出典</th><th>詳細</th></tr></thead><tbody>{fixtures.map((m, idx) => <tr key={`${m.card}-${idx}`}><td>{m.round}</td><td className="fixture-card"><span className="flag"><Flag team={m.teams[0]} width="28" height="19" /></span>{m.card}<span className="flag"><Flag team={m.teams[1]} width="28" height="19" /></span></td><td>{m.jst}</td><td><BroadcastBadge text={m.broadcast} /></td><td>{m.status}</td><td>{m.source || "公式発表があれば更新"}</td><td><a href={m.href}>見る</a></td></tr>)}</tbody></table></div>;
}

function MatchScheduleCard({ fixtures }) {
  return <div className="match-list">{fixtures.map((m) => <article key={m.card}><b>{m.round}</b><h3>{m.card}</h3><p>{m.jst}</p><BroadcastBadge text={m.broadcast} /><small>{m.status}</small><small>出典：{m.source || "公式発表があれば更新"} / 最終更新日：{m.updated || PAGE_UPDATED}</small></article>)}</div>;
}

function TeamCard({ team, compact = false }) {
  return <article className={`sub-team-card${compact ? " sub-team-card--compact" : ""}`}><div className="team-card__bar" style={{ background: `linear-gradient(90deg, ${team.color}, ${team.accent})` }} /><div className="sub-team-card__body"><div className="sub-team-card__head"><span className="flag flag--large"><Flag team={team.key} width="44" height="30" /></span><div><h3>{team.displayName}</h3><p>{team.en}</p></div><em>{team.label}</em></div><div className="rating-with-rank"><strong>{team.rating.toFixed(1)}</strong><RankBadge item={team} /></div><p className="sub-team-card__summary">{team.summary}</p>{!compact && <TeamRadar values={team.radar} color={team.color} size={205} compact />}<a href={TEAM_URLS[team.key]}>詳細ページへ</a></div></article>;
}

function normalizePlayerCard(player) {
  if (!Array.isArray(player)) return player;
  return { name: player[0], pos: player[1], club: player[2], rating: player[3], teamRole: player[4], mainStat: player[5], radar: player[6], strength: player[7], concern: player[8], comment: player[9] };
}

function normalizeName(name = "") {
  return String(name).replace(/\s+/g, "");
}

function playerCardDisplayRecord(player) {
  const name = normalizeName(player && player.name);
  const patch = typeof GROUP_F_PLAYER_CARD_DISPLAY_PATCH !== "undefined" ? GROUP_F_PLAYER_CARD_DISPLAY_PATCH : window.GROUP_F_PLAYER_CARD_DISPLAY_PATCH;
  const cards = patch && Array.isArray(patch.cards) ? patch.cards : [];
  return cards.find((card) => normalizeName(card.player_name) === name || normalizeName(card.player_name_en) === name) || null;
}

function playerDetailRecord(player) {
  const name = normalizeName(player && player.name);
  const japan = typeof TEAM_DETAILS !== "undefined" && TEAM_DETAILS.jpn;
  const pools = [japan && japan.keyPlayers, japan && japan.squad && Object.values(japan.squad).flat()].filter(Boolean);
  for (const pool of pools) {
    const hit = pool.find((item) => normalizeName(item.name) === name);
    if (hit) return hit;
  }
  return {};
}

function formatMetricValue(value, suffix = "") {
  const n = Number(value);
  if (!Number.isFinite(n)) return "参考";
  return `${n.toFixed(Number.isInteger(n) ? 0 : 1)}${suffix}`;
}

function playerMinutesText(p) {
  if (p.mainStat) return p.mainStat;
  if (p.minutes) return `${Number(p.minutes).toLocaleString("ja-JP")}分`;
  return "出場分・試合数を中心に評価";
}

function playerCommonMetrics(player, detail) {
  const wc = player.wcRating ?? player.rating ?? detail.wcRating ?? detail.rating;
  const club = player.clubScore ?? detail.clubScore;
  const league = player.leagueStrength ?? detail.leagueStrength;
  const hasClub = Number.isFinite(Number(club));
  const hasLeague = Number.isFinite(Number(league));
  return [
    { label: "総合評価", value: formatMetricValue(wc), bar: wc },
    { label: "クラブ評価", value: hasClub ? formatMetricValue(club) : "成績参照", bar: club, textOnly: !hasClub },
    { label: "リーグ強度", value: hasLeague ? formatMetricValue(league) : "環境参照", bar: league, textOnly: !hasLeague },
    { label: "稼働", value: playerMinutesText({ ...detail, ...player }), textOnly: true }
  ];
}

function playerPositionNotes(player, detail) {
  const p = { ...detail, ...player };
  const type = playerPositionType(p);
  const ga = [Number.isFinite(Number(p.goals)) ? `${p.goals}G` : null, Number.isFinite(Number(p.assists)) ? `${p.assists}A` : null].filter(Boolean).join("");
  const gaText = p.mainStat || (ga ? `${ga}${Number.isFinite(Number(p.ga90)) ? ` / G+A90 ${Number(p.ga90).toFixed(3)}` : ""}` : "役割と稼働を中心に評価");
  if (type === "GK") return [
    { label: "セーブ", text: p.memo || p.strength || "セーブ率、Saves、失点阻止系の指標を補助的に確認します。" },
    { label: "配球/安定", text: p.strength || p.comment || "Pass成功率、Long ball、安定感、ミスの少なさを確認します。" }
  ];
  if (type === "CB") return [
    { label: "守備評価", text: p.memo || p.strength || "守備メモ、クラブ成績評価、CBとしての役割を確認します。" },
    { label: "ビルドアップ/空中戦", text: `${gaText}。空中戦、対人、つなぎの役割をテキストで見ます。` }
  ];
  if (type === "SB") return [
    { label: "攻撃参加", text: `${gaText}。クロス、前進、供給源としての役割を確認します。` },
    { label: "守備対応", text: p.caution && p.caution !== "大きな注意点なし" ? p.caution : (p.memo || p.strength || "守備対応と上下動のバランスを確認します。") }
  ];
  if (type === "MF") return [
    { label: "展開/前進", text: p.memo || p.strength || "パス、持ち運び、前進、試合を安定させる役割を確認します。" },
    { label: "回収/守備貢献", text: p.caution && p.caution !== "大きな注意点なし" ? p.caution : "回収、対人、運動量、守備強度を補助的に見ます。" }
  ];
  return [
    { label: p.role && /ST|CF/.test(String(p.role).toUpperCase()) ? "得点力" : "得点関与", text: gaText },
    { label: p.role && /ST|CF/.test(String(p.role).toUpperCase()) ? "役割/プレス" : "創出/推進", text: p.memo || p.strength || "チャンスメイク、ドリブル、前進、守備貢献を確認します。" }
  ];
}

function PlayerMetricBars({ player }) {
  const cardRecord = playerCardDisplayRecord(player);
  if (cardRecord) {
    return <div className="player-metrics"><div className="player-metrics__bars">{cardRecord.metrics.map((metric) => {
      const hasBar = metric.bar_value !== null && metric.bar_value !== undefined && Number.isFinite(Number(metric.bar_value));
      const value = Number(metric.bar_value);
      return <div className={`player-metric${hasBar ? "" : " player-metric--text"}`} key={`${metric.label}-${metric.value_text}`}><div className="player-metric__head"><span>{metric.label}</span><b>{metric.value_text}</b></div>{hasBar && <div className="player-metric__track"><i style={{ width: `${Math.max(0, Math.min(100, value))}%`, background: scoreTone(value) }} /></div>}</div>;
    })}</div></div>;
  }
  const detail = playerDetailRecord(player);
  const metrics = playerCommonMetrics(player, detail);
  const notes = playerPositionNotes(player, detail);
  return <div className="player-metrics"><div className="player-metrics__bars">{metrics.map((metric) => <div className={`player-metric${metric.textOnly ? " player-metric--text" : ""}`} key={metric.label}><div className="player-metric__head"><span>{metric.label}</span><b>{metric.value}</b></div>{!metric.textOnly && <div className="player-metric__track"><i style={{ width: `${Math.max(0, Math.min(100, Number(metric.bar) || 0))}%`, background: scoreTone(metric.bar) }} /></div>}</div>)}</div><div className="player-position-notes">{notes.map((note) => <article key={note.label}><b>{note.label}</b><p>{note.text}</p></article>)}</div></div>;
}

const PLAYER_RATING_EVIDENCE = {
  "上田綺世": "得点力・ボックス内関与",
  "久保建英": "創出力・チャンス演出",
  "佐野海舟": "回収力・守備強度",
  "堂安律": "得点関与・右サイド適性",
  "鈴木彩艶": "セーブ力・配球",
  "中村敬斗": "仕掛け・得点関与",
  "菅原由勢": "クロス・運動量",
  "渡辺剛": "空中戦・対人守備"
};

function PlayerRatingGuide({ compact = false }) {
  const cards = [
    {
      kicker: "Step 1",
      title: "選手Ratingを作る",
      formula: "選手Rating = クラブ成績評価 70% + リーグ・クラブ環境評価 30%",
      body: "クラブでの出場実績、成績、パフォーマンスを中心に、所属リーグやクラブ環境の強度を加味した独自指標です。",
      points: ["クラブ成績評価：出場分、先発率、得点・アシスト、守備貢献、ポジション別の詳細指標", "リーグ・クラブ環境評価：主要欧州リーグ、エールディビジ、Jリーグなどのリーグ強度と所属クラブでの役割", "詳細指標ありの選手は、xG、xA、回収、デュエル、空中戦、GK指標なども補助的に反映"]
    },
    {
      kicker: "Step 2",
      title: "選手戦力評価へ集計する",
      formula: "選手戦力評価60 = 攻撃陣18 + 中盤14 + 守備陣14 + GK6 + 控え層8",
      body: "選手Ratingをポジション別に分け、チームの土台となる60点満点の評価へ換算します。",
      points: ["攻撃陣18：CF、WG、AMの得点力、xG、npxG、xA、シュート、チャンス創出、ドリブル、ボックス内関与", "中盤14：配球、前進、回収、インターセプト、タックル、パス成功率、デュエル、運動量、試合支配", "守備陣14：対人守備、空中戦、カバー、ブロック、ビルドアップ、攻撃参加、守備安定性", "GK6：正GK候補のRating、セーブ率、失点、被シュート、ゴール阻止、ハイボール対応、配球、安定感", "控え層8：交代カードの強さ、複数ポジション対応、怪我や累積警告への耐性、連戦でのローテーション力"]
    },
    {
      kicker: "Step 3",
      title: "チームRatingへ換算する",
      formula: "チームRating = 選手戦力評価60 + 代表実績25 + 戦術・組織力10 + 大会適性5",
      body: "選手戦力評価に、代表としての現在地、戦術の完成度、大会で効きやすい要素を加えて100点満点に整理します。",
      points: ["代表実績25：FIFA/Elo系の現在地10、予選成績6、直近10試合5、強豪相手の内容4", "戦術・組織力10：守備ブロック3、プレス・回収2、ビルドアップ2、カウンター1、監督・継続性2", "大会適性5：セットプレー1.5、怪我・コンディション1.5、選手層・連戦耐性1、PK・終盤対応1"]
    },
    {
      kicker: "Note",
      title: "数字の見方",
      formula: "絶対評価ではなく、W杯での戦力反映度を見るための独自分析",
      body: "このRatingは、選手能力やチーム力を完全に数式だけで決めたものではありません。共通して取れる出場実績、クラブ成績、リーグ強度を土台にしながら、詳細指標が確認できる選手やチームは補助的に反映しています。",
      points: ["正式メンバー、怪我、直近の出場状況、大会直前のコンディションによって数値は変動します", "詳細データが限られる選手は、出場分、クラブ成績、リーグ強度、代表での役割を中心に評価します"]
    }
  ];
  return <div className={`rating-method-grid${compact ? " rating-method-grid--compact" : ""}`}>{cards.map((card) => <article className="extra-card rating-method-card" key={card.title}><span>{card.kicker}</span><h3>{card.title}</h3><p className="rating-method-formula">{card.formula}</p><p>{card.body}</p><ul>{card.points.map((point) => <li key={point}>{point}</li>)}</ul></article>)}</div>;
}
function PlayerCard({ player, color }) {
  const p = normalizePlayerCard(player);
  const cardRecord = playerCardDisplayRecord(p);
  const ratingMetric = cardRecord && cardRecord.metrics.find((metric) => metric.label === "総合評価");
  const availabilityMetric = cardRecord && cardRecord.metrics.find((metric) => metric.label === "稼働");
  const rating = ratingMetric ? ratingMetric.value_text : displayRating(p.wcRating ?? p.rating);
  const evidence = PLAYER_RATING_EVIDENCE[p.name];
  const displayGroup = cardRecord ? cardRecord.display_group : `${p.pos}${p.role ? ` / ${p.role}` : ""}`;
  const displayName = cardRecord ? cardRecord.player_name : p.name;
  const club = cardRecord ? cardRecord.club : p.club;
  const league = cardRecord ? cardRecord.league : p.league;
  const rankText = cardRecord && cardRecord.rank ? `独自Rank ${cardRecord.rank}` : (p.teamRole || p.grade || "注目選手");
  const mainStat = availabilityMetric ? availabilityMetric.value_text : (p.mainStat || "参考情報");
  const memo = cardRecord ? cardRecord.memo : (p.strength || p.memo || "役割に応じた貢献");
  return <article className="player-card player-card--metrics"><div className="player-card__intro"><span>{displayGroup}</span><h3>{displayName}</h3><p>{club}{league ? ` / ${league}` : ""}</p><strong>{rating}</strong><em>{rankText}</em></div><PlayerMetricBars player={p} /><ul><li><b>主な数字</b>{mainStat}</li>{evidence && <li><b>評価の見どころ</b>{evidence}</li>}<li><b>強み</b>{memo}</li><li><b>注意点</b>{p.caution || p.concern || "大きな注意点なし"}</li><li><b>私の一言</b>{p.comment || "代表での役割再現が見どころです。"}</li></ul></article>;
}

function playerMainStat(p) {
  if (p.mainStat) return p.mainStat;
  return p.pos === "GK" ? "セーブ / 配球" : p.pos === "DF" ? "対人 / 空中戦" : p.pos === "MF" ? "回収 / 前進" : "得点 / 推進";
}

function PlayerTable({ players }) {
  return <div className="table-wrap"><table className="sub-table player-data-table"><thead><tr><th>選手名</th><th>Pos</th><th>所属クラブ</th><th>所属リーグ</th><th>Rating</th><th>役割</th><th>主な数字</th><th>一言コメント</th><th>注意点</th></tr></thead><tbody>{players.map((raw) => { const p = normalizePlayerCard(raw); const cardRecord = playerCardDisplayRecord(p); const ratingMetric = cardRecord && cardRecord.metrics.find((metric) => metric.label === "総合評価" || metric.label === "評価"); const availabilityMetric = cardRecord && cardRecord.metrics.find((metric) => metric.label === "稼働"); return <tr key={`${cardRecord ? cardRecord.player_name : p.name}-${cardRecord ? cardRecord.display_group : p.pos}`}><td><b>{cardRecord ? cardRecord.player_name : p.name}</b></td><td>{cardRecord ? cardRecord.display_group : p.pos}</td><td>{cardRecord ? cardRecord.club : p.club}</td><td>{cardRecord ? cardRecord.league : (p.league || "参考情報")}</td><td><b>{ratingMetric ? ratingMetric.value_text : displayRating(p.wcRating ?? p.rating)}</b></td><td>{cardRecord ? cardRecord.display_group : (p.teamRole || p.memo)}</td><td>{availabilityMetric ? availabilityMetric.value_text : playerMainStat(p)}</td><td>{cardRecord ? cardRecord.memo : (p.memo || "代表での役割に注目")}</td><td>{p.caution || "大きな注意点なし"}</td></tr>; })}</tbody></table></div>;
}

function SquadSection({ squad }) {
  const [open, setOpen] = useSubState({ GK: true, DF: true, MF: true, FW: true });
  return <div className="squad-section">{["GK","DF","MF","FW"].map((pos) => <section key={pos} className="squad-group"><button type="button" onClick={() => setOpen({ ...open, [pos]: !open[pos] })}><b>{pos}</b><span>{(squad[pos] || []).length}人</span></button>{open[pos] && <div className="squad-list">{(squad[pos] || []).map((p) => <div key={p.name}><span>{p.name}<small> / {p.role && p.role !== p.pos ? `${p.pos} / ${p.role}` : p.pos}</small></span><small>{p.club}{p.league ? ` / ${p.league}` : ""}</small><b>{displayRating(p.wcRating ?? p.rating)}</b><em>{p.memo || p.teamRole || "参考情報"}</em></div>)}</div>}</section>)}</div>;
}

function RelatedLinks({ links }) {
  return <div className="related-links">{links.map((l) => <a key={l.href + l.label} href={l.href}>{l.flag && <span className="related-flag"><Flag team={l.flag} width="24" height="16" /></span>}{l.label}{I.arrow}</a>)}</div>;
}

function pctText(value) {
  const n = Number(value);
  if (!Number.isFinite(n)) return "参考";
  if (n >= 0 && n < 0.05) return "<0.1%";
  return `${n.toFixed(1)}%`;
}

function GroupProbabilityTable({ rows = window.GROUPF_PROBABILITIES || [] }) {
  return <div className="table-wrap"><table className="sub-table probability-table"><thead><tr><th>チーム</th><th>Rating</th><th>1位</th><th>2位</th><th>3位</th><th>4位</th><th>上位2</th><th>3位通過</th><th>R32進出</th><th>期待勝点</th></tr></thead><tbody>{rows.map((r) => <tr key={r.team}><td><span className="flag"><Flag team={r.key} width="28" height="19" /></span><b>{r.team}</b></td><td>{Number(r.rating).toFixed(1)}</td><td>{pctText(r.first)}</td><td>{pctText(r.second)}</td><td>{pctText(r.third)}</td><td>{pctText(r.fourth)}</td><td>{pctText(r.top2)}</td><td>{pctText(r.thirdAdvance)}</td><td><b>{pctText(r.advance)}</b></td><td>{Number(r.expectedPts).toFixed(2)}</td></tr>)}</tbody></table></div>;
}

function koPercentText(value, mode = "round") {
  const n = Number(value);
  if (!Number.isFinite(n)) return "参考";
  if (n >= 0 && n < 0.05) return "<0.1%";
  if (mode === "title" && n < 2) return `${(Math.round((n + Number.EPSILON) * 100) / 100).toFixed(2)}%`;
  return `${(Math.round((n + Number.EPSILON) * 10) / 10).toFixed(1)}%`;
}

function knockoutTone(value) {
  const n = Number(value) || 0;
  if (n >= 40) return "#F47B20";
  if (n >= 15) return "#F2C94C";
  if (n >= 5) return "#37C871";
  if (n >= 1) return "#2F80ED";
  return "#8A94A6";
}

function getKnockoutRow(teamKeyOrName) {
  const rows = window.KNOCKOUT_TITLE_RANKINGS || window.KNOCKOUT_PROBABILITIES || [];
  return rows.find((r) => r.key === teamKeyOrName || r.team === teamKeyOrName) || null;
}

function knockoutStageItems(row) {
  if (!row) return [];
  return [
    ["R32進出", row.r32, "round"],
    ["R16進出", row.r16, "round"],
    ["ベスト8", row.qf, "round"],
    ["ベスト4", row.sf, "round"],
    ["決勝", row.final, "round"],
    ["優勝", row.title, "title"]
  ];
}

function KnockoutBars({ row, compact = false }) {
  if (!row) return null;
  return <div className={`knockout-bars${compact ? " knockout-bars--compact" : ""}`}>{knockoutStageItems(row).map(([label, value, mode]) => { const n = Number(value) || 0; const width = n > 0 ? Math.max(3, Math.min(100, n)) : 3; return <div className="knockout-bar" key={label}><div className="knockout-bar__head"><span>{label}</span><b>{koPercentText(value, mode)}</b></div><div className="knockout-bar__track"><i style={{ width: `${width}%`, background: knockoutTone(n) }} /></div></div>; })}</div>;
}

function JapanKnockoutOutlook() {
  const row = getKnockoutRow("jpn");
  if (!row) return null;
  return <div className="knockout-outlook-grid"><article className="featured-card wc-card-dark knockout-main-card"><p className="eyebrow">Japan Knockout Path</p><h3>日本代表の勝ち上がり予測</h3><KnockoutBars row={row} /><p>日本はGroup F突破を十分に狙える位置にいます。決勝トーナメントでは、対戦相手や3位通過チームの配置によって道のりが大きく変わります。ベスト8以上は上振れラインですが、攻撃陣と中盤の質がかみ合えば、十分に挑戦できる余地があります。</p></article><article className="featured-card wc-card-dark"><p className="eyebrow">Position</p><h3>優勝確率ランキング</h3><strong className="knockout-rank">{row.rank}位</strong><p>{row.team}は全48チーム中{row.rank}位。優勝確率は{koPercentText(row.title, "title")}で、現時点では山次第のダークホースという位置づけです。</p></article></div>;
}

function GroupFKnockoutCards() {
  const rows = (window.GROUPF_KNOCKOUT_OUTLOOK || []).slice().sort((a, b) => a.rank - b.rank);
  return <div className="knockout-card-grid">{rows.map((row) => <article className="featured-card wc-card-dark knockout-team-card" key={row.team}>{row.key && <span className="flag flag--large"><Flag team={row.key} width="38" height="26" /></span>}<p className="eyebrow">{row.group} / {row.rank}位</p><h3>{row.team}</h3><p>R32 {koPercentText(row.r32)} / R16 {koPercentText(row.r16)} / ベスト8 {koPercentText(row.qf)} / 優勝 {koPercentText(row.title, "title")}</p><KnockoutBars row={row} compact /></article>)}</div>;
}

function TeamProjectionCard({ team }) {
  if (!team || !team.prob) return null;
  const ko = team.knockout || {};
  return <div className="projection-grid"><article className="featured-card wc-card-dark"><p className="eyebrow">Group Stage</p><h3>グループ突破確率</h3><strong>{pctText(team.prob.advance)}</strong><p>1位 {pctText(team.prob.first)} / 2位 {pctText(team.prob.second)} / 3位 {pctText(team.prob.third)} / 4位 {pctText(team.prob.fourth)}</p></article><article className="featured-card wc-card-dark"><p className="eyebrow">Knockout</p><h3>決勝T到達目安</h3><p>R16 {koPercentText(ko.r16)} / ベスト8 {koPercentText(ko.qf)} / ベスト4 {koPercentText(ko.sf)} / 決勝 {koPercentText(ko.final)} / 優勝 {koPercentText(ko.title, "title")}</p></article></div>;
}

function TeamHeaderStats({ team }) {
  return <div className="team-stats"><article><b>{team.group}</b><span>Group</span></article><article><b>{team.fifaRank}</b><span>FIFAランキング</span></article><article><b>{team.rating.toFixed(1)}</b><span>総合Rating</span><RankBadge item={team} /></article><article><b>{team.label}</b><span>評価</span></article></div>;
}

Object.assign(window, { PageShell, HeroSection, SectionHead, RatingBar, AbilityBars, RadarScoreList, TeamRadar, PlayerRadar, MatchScheduleCard, FixturesTable, StandingsTable, AnalysisNote, StrengthWeaknessCards, TeamCard, PlayerCard, PlayerTable, SquadSection, BroadcastBadge, DisclaimerBox, SourceBadge, RelatedLinks, PlayerRatingGuide, GroupProbabilityTable, koPercentText, getKnockoutRow, KnockoutBars, JapanKnockoutOutlook, GroupFKnockoutCards, TeamProjectionCard, TeamHeaderStats, RankBadge });





