/* history-view.jsx — 「栄冠」タブ。全日本新人選手権の歴代優勝（2000–2025・24大会）。
   出典：JARA公式 大会結果（2016年のみ「ローイングのデータ集」）。事実のみ（創作しない）。 */

// 種目コード（歴代表記 M8+ / W4X+ …）→ 今大会の EVENTS.key。無い種目は null。
const HIST_EV2KEY = { "M1X": "m1x", "W1X": "w1x", "M2X": "m2x", "W2X": "w2x",
  "M4+": "m4c", "W4X+": "w4xc", "M8+": "m8", "W4+": null };
// 種目タブの並び（小さい艇→大きい艇、男子→女子、最後に現在は行われていない種目）
const HIST_EV_ORDER = ["M1X", "M2X", "M4+", "M8+", "W1X", "W2X", "W4X+", "W4+"];   // W4+＝2001年まで（2000・2001年を収録）
const HIST_EV_NAME = { "M1X": "男子シングルスカル", "M2X": "男子ダブルスカル", "M4+": "男子舵手つきフォア",
  "M8+": "男子エイト", "W1X": "女子シングルスカル", "W2X": "女子ダブルスカル",
  "W4X+": "女子舵手つきクォドルプル", "W4+": "女子舵手つきフォア" };
// 新人戦は「優勝なし」の年が無い（中止年は年表の備考で示す）。
const HIST_EV_1974 = [];

function HistBar({ rows, unit, max, onTeam, openKey, renderDetail }) {
  return (
    <div className="mbt-sec"><div className="mbt-chart">
      {rows.map((r, i) => {
        const tie = i > 0 && r.n === rows[i - 1].n;
        const open = openKey != null && r.team === openKey;
        return (
          <div className={"mbt-item" + (open ? " open" : "")} key={r.team + i}>
            <div className="mbt-row entval-row" style={{ cursor: onTeam ? "pointer" : "default" }}
                 onClick={onTeam ? () => onTeam(r) : undefined}>
              <span className="mbt-rk">{tie ? "=" : (i + 1)}</span>
              <span className="mbt-tm">{r.disp || r.team}</span>
              <span className="mbt-barwrap">
                <span className="mbt-bar"><span className="mbt-fill hist" style={{ width: (r.n / max * 100) + "%" }} /></span>
                <span className="mbt-val entval">
                  <span className="entcell tot"><b>{r.n}</b><span className="entu">{unit}</span></span>
                </span>
              </span>
            </div>
            {open && renderDetail && renderDetail(r)}
          </div>
        );
      })}
    </div></div>
  );
}

// 団体の規模推移（出漕一覧の22年分）。SIZE データが無い団体は null を返して何も描かない。
// 年ごとの男女積み上げ棒（データタブ「出場の推移」／歴史タブ「優勝の推移」で共用）。
// series = { aM,aW,bM,bW }（a=人数系 b=クルー系）。内訳不明分（total>m+w）は無彩色で上積み。
/* ── 記録の推移（新人戦の年間最速 × U23世界一 × シニア世界一）───────────────
   狙い：国内の勝ち負けの先にある「世界との距離」を毎年の実測タイムで見せる。
   タイムは気象で大きく振れるため、単年の差ではなく年代平均の差も併記する。 */
function RecordTrend({ ev }) {
  const P = window.KANSAI.PROG;
  if (!P) return null;
  const ic = (P.ic || {})[ev], wch = (P.wch || {})[ev], u23 = (P.u23 || {})[ev];
  const pct = (P.pct || {})[ev] || null;
  if (!ic || Object.keys(ic).length < 2) return null;   // W8+（2023年〜）も世界との距離は見せる
  // ％ビュー用のミニ系列（w=対シニア世界一 u=対U23世界一）。secキーで揃えて平滑化関数を共用する
  const pW = {}, pU = {};
  if (pct) Object.keys(pct).forEach((y) => {
    if (pct[y].w != null) pW[y] = { sec: pct[y].w };
    if (pct[y].u != null) pU[y] = { sec: pct[y].u };
  });
  const hasPct = Object.keys(pW).length >= 3;
  const [mode, setMode] = React.useState(hasPct ? "p" : "t");   // p=世界一比％ t=タイム
  const [sel, setSel] = React.useState(null);                    // タップした年
  const [howOpen, setHowOpen] = React.useState(false);           // 「どうやって？」の開閉
  const Y0 = 2000, Y1 = 2025;
  const years = []; for (let y = Y0; y <= Y1; y++) years.push(y);
  const val = (m, y) => (m && m[y] ? m[y].sec : null);
  // 実ピクセル幅で描く（viewBox縮小で文字が潰れないように）。回転は稀なので初回計測でよい
  const wrapRef = React.useRef(null);
  const [W, setW] = React.useState(680);
  React.useEffect(() => {
    if (wrapRef.current) { const w = wrapRef.current.clientWidth - 8; if (w > 200 && Math.abs(w - W) > 4) setW(w); }
  });
  const H = 210, L = 44, R = 14, T = 12, B = 26;
  const x = (y) => L + (W - L - R) * (y - Y0) / (Y1 - Y0);
  // 太線＝5年移動平均（風・水温の年差をならした「実力の傾向」）。点＝各年の実測。
  // 1〜2年の欠測（2020中止・2021欠落）は平均線としては橋渡しして良い。
  const smoothPts = (m) => {
    const have = years.filter((y) => val(m, y) != null);
    if (!have.length) return [];
    const out = [];
    for (let y = have[0]; y <= have[have.length - 1]; y++) {
      const vs = [];
      for (let k = y - 2; k <= y + 2; k++) { const v = val(m, k); if (v != null) vs.push(v); }
      if (vs.length >= 2 || val(m, y) != null)
        out.push({ y, v: vs.reduce((a, b) => a + b, 0) / vs.length });
    }
    return out;
  };
  const isP = mode === "p" && hasPct;
  const S1 = isP ? pW : ic;                       // 主役（％では対世界一、タイムでは新人戦）
  const sm = isP
    ? { a: smoothPts(pW), b: smoothPts(pU), c: [] }
    : { a: smoothPts(ic), b: wch ? smoothPts(wch) : [], c: u23 ? smoothPts(u23) : [] };
  // 縦軸レンジは傾向線から。％ビューは100（＝世界一と同速）を必ず含める
  const smAll = sm.a.concat(sm.b, sm.c).map((p) => p.v);
  if (!smAll.length) return null;
  let lo = Math.min.apply(null, smAll), hi = Math.max.apply(null, smAll);
  if (isP) { hi = Math.max(hi, 100); lo = Math.min(lo, 88); }
  const pad = isP ? 1.2 : Math.max(6, (hi - lo) * 0.14);
  // タイム＝速いほど上（値が小さいほど上）／％＝高いほど上（値が大きいほど上）
  const yy = isP
    ? (v) => T + (H - T - B) * (1 - (v - (lo - pad)) / ((hi + pad) - (lo - pad)))
    : (v) => T + (H - T - B) * (v - (lo - pad)) / ((hi + pad) - (lo - pad));
  const curve = (pts) => {
    if (pts.length < 2) return "";
    const Pp = pts.map((p) => [x(p.y), yy(p.v)]);
    let d = "M" + Pp[0][0].toFixed(1) + " " + Pp[0][1].toFixed(1);
    for (let i = 0; i < Pp.length - 1; i++) {
      const p0 = Pp[i - 1] || Pp[i], p1 = Pp[i], p2 = Pp[i + 1], p3 = Pp[i + 2] || p2;
      const c1 = [p1[0] + (p2[0] - p0[0]) / 6, p1[1] + (p2[1] - p0[1]) / 6];
      const c2 = [p2[0] - (p3[0] - p1[0]) / 6, p2[1] - (p3[1] - p1[1]) / 6];
      d += "C" + c1[0].toFixed(1) + " " + c1[1].toFixed(1) + " " + c2[0].toFixed(1) + " " + c2[1].toFixed(1) + " " + p2[0].toFixed(1) + " " + p2[1].toFixed(1);
    }
    return d;
  };
  // 「世界との距離」の帯：％＝100の線と対世界線の間／タイム＝世界一の傾向線と新人戦の傾向線の間
  const gapArea = (() => {
    if (isP) {
      if (sm.a.length < 2) return "";
      const up = sm.a.map((p) => x(p.y).toFixed(1) + " " + yy(p.v).toFixed(1)).join(" L");
      const yl = yy(100).toFixed(1);
      return "M" + x(sm.a[0].y).toFixed(1) + " " + yl + " L" + up.replace(/^/, "") +
        " L" + x(sm.a[sm.a.length - 1].y).toFixed(1) + " " + yl + " Z";
    }
    if (!sm.b.length || !sm.a.length) return "";
    const w0 = Math.max(sm.b[0].y, sm.a[0].y), w1 = Math.min(sm.b[sm.b.length - 1].y, sm.a[sm.a.length - 1].y);
    const wPts = sm.b.filter((p) => p.y >= w0 && p.y <= w1);
    const iPts = sm.a.filter((p) => p.y >= w0 && p.y <= w1);
    if (wPts.length < 2 || iPts.length < 2) return "";
    const up = wPts.map((p) => x(p.y).toFixed(1) + " " + yy(p.v).toFixed(1)).join(" L");
    const dn = iPts.slice().reverse().map((p) => x(p.y).toFixed(1) + " " + yy(p.v).toFixed(1)).join(" L");
    return "M" + up + " L" + dn + " Z";
  })();
  const dots = (m) => years.filter((y) => val(m, y) != null);
  const fmtT = (s) => { const mm = Math.floor(s / 60), r = s - mm * 60; return mm + ":" + (r < 10 ? "0" : "") + r.toFixed(2); };
  const fmtY = isP ? ((v) => v.toFixed(0) + "%") : ((v) => fmtT(v).replace(/\.\d+$/, ""));
  // 年代平均（％は世界一比、タイムは秒差）
  const eras = [[2000, 2009, "2000年代"], [2010, 2019, "2010年代"], [2020, 2025, "2020年代"]];
  const eraRows = eras.map(([a, b, lb]) => {
    const vs = [];
    for (let y = a; y <= b; y++) {
      if (isP) { const v = val(pW, y); if (v != null) vs.push(v); }
      else if (val(ic, y) != null && val(wch, y) != null) vs.push(val(ic, y) - val(wch, y));
    }
    return vs.length ? { lb, g: vs.reduce((s, v) => s + v, 0) / vs.length } : { lb, g: null };
  });
  const showEra = isP ? true : !!wch;
  const grid = [];
  if (isP) { for (let v = Math.ceil((lo - pad) / 2) * 2; v <= hi + pad; v += 2) grid.push(v); }
  else { const step = (hi - lo + 2 * pad) > 120 ? 30 : (hi - lo + 2 * pad) > 50 ? 15 : 10;
    for (let v = Math.ceil((lo - pad) / step) * step; v <= hi + pad; v += step) grid.push(v); }
  const selData = sel && {
    ic: ic && ic[sel], wch: wch && wch[sel], u23: u23 && u23[sel], p: pct && pct[sel],
  };
  const gapTxt = (a, b) => (a && b) ? "＋" + (a.sec - b.sec).toFixed(2) + "秒" : null;
  return (
    <div className="trendbox">
      <div className="subhead" style={{ marginTop: 18 }}><IconChart className="ic" size={16} />
        世界との距離
        {hasPct && (
          <span className="trendmode">
            <button className={"tm" + (isP ? " on" : "")} onClick={() => setMode("p")}>世界一比</button>
            <button className={"tm" + (!isP ? " on" : "")} onClick={() => setMode("t")}>タイム</button>
          </span>
        )}
      </div>
      <div className="trendlegend">
        {isP ? (<>
          <span className="tl w100">世界一 ＝ 100</span>
          <span className="tl ic">対シニア世界一</span>
          {Object.keys(pU).length > 0 && <span className="tl u23">対U23世界一</span>}
        </>) : (<>
          {wch && Object.keys(wch).length > 0 && <span className="tl wch">シニア世界一</span>}
          {u23 && Object.keys(u23).length > 0 && <span className="tl u23">U23世界一</span>}
          <span className="tl ic">新人戦最速</span>
        </>)}
      </div>
      <div className="trendwrap" ref={wrapRef}>
        <svg viewBox={"0 0 " + W + " " + H} width={W} height={H} className="trendsvg" preserveAspectRatio="xMidYMid meet">
          {grid.map((v) => (
            <g key={v}>
              <line x1={L} x2={W - R} y1={yy(v)} y2={yy(v)} className="tr-grid" />
              <text x={L - 6} y={yy(v) + 3} className="tr-ylabel">{fmtY(v)}</text>
            </g>
          ))}
          {years.filter((y) => y % 5 === 0 || y === Y1).map((y) => (
            <text key={y} x={x(y)} y={H - 8} className="tr-xlabel">{y}</text>
          ))}
          {sel && <line x1={x(sel)} x2={x(sel)} y1={T} y2={H - B} className="tr-sel" />}
          {gapArea && <path d={gapArea} className="tr-gaparea" />}
          {isP && <line x1={L} x2={W - R} y1={yy(100)} y2={yy(100)} className="tr-w100" />}
          {isP ? (<>
            {dots(pW).map((y) => <circle key={"pw" + y} cx={x(y)} cy={yy(val(pW, y))} r="2" className="tr-dot raw ic" />)}
            {dots(pU).map((y) => <circle key={"pu" + y} cx={x(y)} cy={yy(val(pU, y))} r="1.8" className="tr-dot raw u23" />)}
            {sm.b.length > 1 && <path d={curve(sm.b)} className="tr-line u23" />}
            {sm.a.length > 1 && <path d={curve(sm.a)} className="tr-line ic" />}
            {sel && val(pW, sel) != null && <circle cx={x(sel)} cy={yy(val(pW, sel))} r="3.6" className="tr-dot sel ic" />}
            {sel && val(pU, sel) != null && <circle cx={x(sel)} cy={yy(val(pU, sel))} r="3.6" className="tr-dot sel u23" />}
          </>) : (<>
            {wch && dots(wch).map((y) => <circle key={"w" + y} cx={x(y)} cy={yy(val(wch, y))} r={val(wch, y) && wch[y].og ? 3 : 1.8} className={"tr-dot raw wch" + (wch[y].og ? " og" : "")} />)}
            {u23 && dots(u23).map((y) => <circle key={"u" + y} cx={x(y)} cy={yy(val(u23, y))} r="1.8" className="tr-dot raw u23" />)}
            {dots(ic).map((y) => <circle key={"i" + y} cx={x(y)} cy={yy(val(ic, y))} r="2" className="tr-dot raw ic" />)}
            {sm.b.length > 0 && <path d={curve(sm.b)} className="tr-line wch" />}
            {sm.c.length > 0 && <path d={curve(sm.c)} className="tr-line u23" />}
            {sm.a.length > 1 ? <path d={curve(sm.a)} className="tr-line ic" />
              : dots(ic).map((y) => <circle key={"is" + y} cx={x(y)} cy={yy(val(ic, y))} r="3" className="tr-dot ic" />)}
            {sel && [ic, wch, u23].map((m, i) => (m && val(m, sel) != null &&
              <circle key={"s" + i} cx={x(sel)} cy={yy(val(m, sel))} r="3.6" className={"tr-dot sel " + (m === ic ? "ic" : m === wch ? "wch" : "u23")} />))}
          </>)}
          {years.map((y) => (
            <rect key={"t" + y} x={x(y) - (W - L - R) / (Y1 - Y0) / 2} y={0} width={(W - L - R) / (Y1 - Y0)} height={H}
              fill="transparent" onClick={() => setSel(sel === y ? null : y)} style={{ cursor: "pointer" }} />
          ))}
        </svg>
      </div>
      {selData && (
        <div className="trendsel">
          <b className="ts-y">{sel}年</b>
          {selData.wch && <span className="ts-row wch">世界一 <b>{selData.wch.t}</b> {selData.wch.crew}{selData.wch.og ? "（五輪）" : ""}</span>}
          {selData.u23 && <span className="ts-row u23">U23 <b>{selData.u23.t}</b> {selData.u23.crew}</span>}
          {selData.ic && <span className="ts-row ic">新人戦 <b>{selData.ic.t}</b> {selData.ic.crew}</span>}
          {selData.p && selData.p.w != null && <span className="ts-gap">世界一比 {selData.p.w.toFixed(1)}%（条件補正後）</span>}
          {!selData.p && selData.ic && selData.wch && <span className="ts-gap">世界との差 {gapTxt(selData.ic, selData.wch)}</span>}
          {!selData.ic && <span className="ts-gap">この年の新人戦記録なし</span>}
        </div>
      )}
      {showEra && eraRows.some((e) => e.g != null) && (
        <div className="trendgaps">
          <span className="tg-lb">{isP ? "世界一比（年代平均）" : "世界一との差（年代平均）"}</span>
          {eraRows.map((e, i) => {
            const prev = eraRows.slice(0, i).reverse().find((p) => p.g != null);
            let dir = null;
            if (e.g != null && prev) {
              const worse = isP ? e.g < prev.g - 0.3 : e.g > prev.g + 1;
              const better = isP ? e.g > prev.g + 0.3 : e.g < prev.g - 1;
              dir = worse ? "up" : better ? "down" : "flat";
            }
            return e.g == null ? null : (
              <span className={"tg " + (dir || "")} key={e.lb}>
                {e.lb} <b>{isP ? e.g.toFixed(1) + "%" : "＋" + e.g.toFixed(1) + "秒"}</b>
                {dir === "up" ? "▼" : dir === "down" ? "▲" : ""}
              </span>
            );
          })}
        </div>
      )}
      {isP ? (
        <p className="sizenote" style={{ margin: "6px 2px 0" }}>
          縦軸の「世界一比」は、<b>その年の世界一のスピードを100%としたときの、新人戦最速クルーのスピード</b>です。
          ボートのタイムは風だけで数秒から十数秒も変わるので、追い風の大会と逆風の大会のタイムをそのまま比べても意味がありません。
          そこで、風が強い日は全種目がそろって速くなったり遅くなったりすることを手がかりに、
          大会全体の速さから風の分を割り出して取り除いてあります。
          <br /><b>見るのは、毎年の上下をならした太い線</b>です。100%に近いほど、世界一のスピードに近いことを表します。
          うすい点には風の影響が残っているので、まれに100%を超える年もありますが、
          本当に世界一より速かったわけではありません。
        </p>
      ) : (
        <p className="sizenote" style={{ margin: "6px 2px 0" }}>
          <b>太線＝5年移動平均</b>（風・水温による年ごとの振れをならした傾向）、<b>点＝各年の実測</b>。
          薄い赤の帯が「世界一との距離」です。年代平均は両方の記録がある年だけで算出。
          世界一＝世界選手権A決勝優勝（五輪年は五輪◎、非五輪種目は世界選手権）。新人戦は全ラウンドの年間最速
          （2016年は公式記録の未掲載、2020・2021年は中止により無し）。
        </p>
      )}
      {isP && (
        <>
        <button type="button" className="howbtn" onClick={() => setHowOpen(!howOpen)}>
          {howOpen ? "▾" : "▸"} 風の分をどうやって割り出したの？
        </button>
        {howOpen && (
          <div className="howbox">
            <p>考え方は、コーチが結果表を見て「今日は全体に速い水だな」と読むのと同じです。それを数字でやっています。</p>
            <ol>
              <li>種目ごとに「その頃のふつうの記録」を決めます。その年をはさむ<b>前後3年ずつ・あわせて7年分</b>の記録を
                速い順にならべた、ちょうどまんなかの値（中央値）です。全期間の平均と比べないのは、艇の進化や世界全体の
                レベル向上まで「速い水」あつかいになって、時代をまたぐ比較が壊れてしまうからです。</li>
              <li>ひとつの大会について、全種目それぞれ「ふつうより何%速いか・遅いか」を出します。</li>
              <li>全種目ぶんの「何%」をならべて、<b>そのまんなかの値を、その大会の「水の速さ」とみなします</b>。
                まんなかの値を使うので、1種目だけ飛び抜けた記録——それは実力かもしれません——には引きずられません。
                逆に、全種目がそろって3%速いなら、それは実力ではなく、その日の風や水と考えるのが自然です。</li>
              <li>その分だけタイムを直します。速い水の大会はその分おそく、遅い水の大会はその分速く直します。
                新人戦側・世界側（五輪と世界選手権は別の大会として扱います）で同じことをして、
                直したタイム同士でスピードを比べたのが「世界一比」です。</li>
            </ol>
            <p><b>実例</b>：2012年ロンドン五輪は全種目がふつうより2.5%ほど遅い、重いコンディションでした。
              一方、同じ年の新人戦（戸田）は全種目がふつうより2.4%ほど速い、追い風寄りの水でした。
              この分を直さないと、2012年の新人戦だけが実力以上に世界へ近づいて見えてしまいます。</p>
            <p>消せるのは「大会ぜんたいに共通する風・水」だけです。同じ日でも朝と夕方で風は違うので、
              レース1本ごとの風までは消せません。たまたま追い風で漕いだ記録は実際より速いまま残るため、
              グラフのうすい点（1年ごとの値）がまれに100%を超えます。</p>
            <p>データ：新人戦はJARA公式の記録（2001〜2025年・約1万3千本のタイム）、世界はWorld Rowing公式の記録。
              記録が少なすぎる種目・年は計算から外しています。</p>
          </div>
        )}
        </>
      )}
    </div>
  );
}

function YearStackChart({ years, series, labelA, labelB, unitA, unitB, totalsA, totalsB, note, zoomable, renderYear }) {
  const [mode, setMode] = React.useState("a");
  const [selY, setSelY] = React.useState(null);   // タップで選んだ年（renderYear があるときだけ使う）
  const [scale, setScale] = React.useState(zoomable ? "zoom" : "full");
  const m = mode === "a" ? series.aM : series.bM;
  const w = mode === "a" ? series.aW : series.bW;
  const totals = mode === "a" ? (totalsA || null) : (totalsB || null);
  const unit = mode === "a" ? unitA : unitB;
  const tot = years.map((y, i) => {
    const t = totals ? totals[i] : null;
    if (t != null) return t;
    if (m[i] == null && w[i] == null) return null;
    return (m[i] || 0) + (w[i] || 0);
  });
  const vals = tot.filter((v) => v != null && v > 0);
  const hi = Math.max(1, ...vals), lo = Math.min(...vals);
  const floor = scale === "zoom" ? Math.max(0, lo - Math.max(1, Math.round((hi - lo) * 0.35))) : 0;
  const span = Math.max(1, hi - floor);
  const dense = years.length > 30;               // 51年チャートは数値を縦書き＋横スクロール
  const step = 1;                                // 年ラベルは毎年表記
  const scrollRef = React.useRef(null);
  React.useEffect(() => {                        // 初期表示は右端（直近の年）。左へスワイプで過去へ
    if (dense && scrollRef.current) scrollRef.current.scrollLeft = scrollRef.current.scrollWidth;
  }, [dense, mode, scale]);
  return (
    <div className="sizebox" style={{ borderTop: 0, paddingTop: 0, marginTop: 6 }}>
      <div className="sizehd">
        <span className="sizeh">{labelA}／{labelB}<i>{years[0]}–{years[years.length - 1]}</i></span>
        <span className="mtoggle sizetg">
          <button type="button" className={mode === "a" ? "on" : ""} onClick={() => setMode("a")}>{labelA}</button>
          <button type="button" className={mode === "b" ? "on" : ""} onClick={() => setMode("b")}>{labelB}</button>
        </span>
      </div>
      <div className="sizehd" style={{ marginTop: -2 }}>
        <span className="sizelegend" style={{ margin: 0 }}>
          <span><i className="sw m" />男子</span>
          <span><i className="sw w" />女子</span>
        </span>
        {zoomable && (
          <span className="mtoggle sizetg" style={{ marginLeft: "auto" }}>
            <button type="button" className={scale === "zoom" ? "on" : ""} onClick={() => setScale("zoom")}>変化を強調</button>
            <button type="button" className={scale === "full" ? "on" : ""} onClick={() => setScale("full")}>0から</button>
            <button type="button" className={scale === "ratio" ? "on" : ""} onClick={() => setScale("ratio")}>男女比</button>
          </span>
        )}
      </div>
      {scale === "zoom" && (
        <p className="sizeaxis">縦軸は<b>{floor}{unit}</b>から（増減を見やすくするため）。</p>
      )}
      {scale === "ratio" && (
        <p className="sizeaxis">各年を100%として<b>男女の比率</b>を表示（棒の高さは揃います）。</p>
      )}
      {dense && <p className="sizeaxis" style={{ marginBottom: 2 }}>← 左へスワイプすると過去の年が見られます（初期表示は直近）。</p>}
      <div className={dense ? "sizescroll" : undefined} ref={scrollRef}>
      <div className={"sizechart" + (dense ? " dense" : "")}
           style={{ height: scale === "ratio" ? 108 : (dense ? 138 : 126), width: dense ? (years.length * 26) + "px" : undefined }}>
        {years.map((y, i) => {
          const v = tot[i];
          const mm = m[i] || 0, ww = w[i] || 0, known = mm + ww;
          let h = 0;
          if (v != null && v > 0) {
            h = scale === "ratio" ? 100 : Math.max(4, Math.round((v - floor) / span * 100));
          }
          const wr = scale === "ratio"
            ? (known > 0 ? Math.round(ww / known * 100) : 0)
            : (v > 0 && known > 0 ? Math.round(ww / v * 100) : 0);
          const gr = scale !== "ratio" && v > 0 && known < v ? Math.round((v - known) / v * 100) : 0;
          const shownVal = scale === "ratio" ? (known > 0 ? Math.round(ww / known * 100) + "%" : "") : v;
          return (
            <div className={"sizecol" + (v == null || v === 0 ? " none" : "") + (selY === y ? " sel" : "")} key={y}
                 onClick={renderYear ? () => setSelY(selY === y ? null : y) : undefined}
                 style={renderYear ? { cursor: "pointer" } : undefined}
                 title={y + "年 " + (v == null ? "—" : v + unit + (known > 0 ? "（男" + mm + "・女" + ww + "）" : ""))}>
              <span className="sizev">{v == null ? "" : shownVal}</span>
              <span className="sizebar split" style={{ height: h + "%" }}>
                {gr > 0 && <i className="sw-g" style={{ height: gr + "%" }} />}
                {wr > 0 && <i className="sw-w" style={{ height: wr + "%" }} />}
              </span>
              <span className="sizey" style={{ opacity: (i % step === 0 || i === years.length - 1) ? 1 : 0 }}>{String(y).slice(2)}</span>
            </div>
          );
        })}
      </div>
      </div>
      {renderYear && selY != null && (
        <div className="yeardetail">{renderYear(selY)}</div>
      )}
      {note && <p className="sizenote">{note}</p>}
    </div>
  );
}
Object.assign(window, { YearStackChart });

// 団体別の出場推移（男女別・年ごと）。データタブの行タップで開く。
function TeamEntryTrend({ team }) {
  const S = window.KANSAI.SIZE;
  if (!S) return null;
  const key = S.alias[team] || team;
  const t = S.byTeam[key];
  if (!t) {
    return <p className="sizenote" style={{ margin: "8px 2px 0" }}>
      この団体は{S.years[0]}年以降の新人戦に出走の記録が見つかりませんでした（初出場の団体など）。
    </p>;
  }
  return (
    <YearStackChart years={S.years}
      series={{ aM: t.mAth, aW: t.wAth, bM: t.mCrews, bW: t.wCrews }}
      totalsA={t.athletes} totalsB={t.crews}
      labelA="人数" labelB="クルー数" unitA="名" unitB="クルー"
      note={"出典：JARA公式 大会結果（各年の予選に出走したクルー。" + S.years[0] + "〜2025）と2026年エントリー表。人数は艇の定員×クルー数（舵手を含む）。棒が無い年は出場なし。2016年は結果未掲載、2020・2021年は中止。"} />
  );
}
Object.assign(window, { TeamEntryTrend });

// 団体別の優勝推移（男女別・年ごと）。歴史タブの行タップで開く。
function TeamWinTrend({ team }) {
  const H = window.KANSAI.HISTORY;
  if (!H) return null;
  const SEAT = (ev) => {
    const m = String(ev).match(/(4X\+|4X|2X|1X|2\+|2-|4\+|4-|8\+)/);
    return m ? ({ "1X": 1, "2X": 2, "2-": 2, "2+": 3, "4-": 4, "4+": 5, "4X": 4, "4X+": 5, "8+": 9 })[m[1]] : 0;
  };
  const ys = []; for (let y = H.stats.from; y <= H.stats.latest; y++) if (H.stats.off.indexOf(y) < 0) ys.push(y);
  const aM = ys.map(() => 0), aW = ys.map(() => 0), bM = ys.map(() => 0), bW = ys.map(() => 0);
  // 歴代優勝は団体名（「日本」）で記録されている。正式名（「日本大学」）で呼ばれても照合できるように解決
  const short = H.NAME_MAP[team] !== undefined ? team
    : (Object.keys(H.NAME_MAP).find((k) => H.NAME_MAP[k] === team) || team);
  let any = false;
  H.rows.forEach((r) => {
    if (r.team !== short) return;
    const i = ys.indexOf(r.year); if (i < 0) return;
    any = true;
    const isW = String(r.ev).charAt(0) === "W";
    const seats = SEAT(r.ev);
    if (isW) { bW[i]++; aW[i] += seats; } else { bM[i]++; aM[i] += seats; }
  });
  if (!any) return null;
  return (
    <YearStackChart years={ys} series={{ aM, aW, bM, bW }}
      labelA="優勝人数" labelB="優勝クルー数" unitA="名" unitB="クルー"
      renderYear={(y) => {
        const rows = H.rows.filter((r) => r.year === y && r.team === short)
          .sort((a, b) => HIST_EV_ORDER.indexOf(a.ev) - HIST_EV_ORDER.indexOf(b.ev));
        if (!rows.length) return <p className="yd-none">{y}年の優勝はありません。</p>;
        return (<React.Fragment>
          <div className="yd-hd"><b>{y}年</b>の優勝種目<span className="yd-n">{rows.length}冠</span></div>
          <div className="yd-list">
            {rows.map((r, i) => (
              <span className={"yd-item " + (/^L?W/.test(r.ev) ? "gw" : "gm")} key={i}>
                <b>{r.ev}</b>{r.athlete || ""}
              </span>
            ))}
          </div>
        </React.Fragment>);
      }}
      note="出典：歴代優勝データ。人数＝優勝クルーの定員合計（舵手を含む）。棒が無い年は優勝なし。棒をタップすると、その年の優勝種目が出ます。" />
  );
}
Object.assign(window, { TeamWinTrend });

function TeamSizeChart({ team }) {
  const S = window.KANSAI.SIZE;
  const H = window.KANSAI.HISTORY;
  const [mode, setMode] = React.useState("athletes");   // athletes=人数 / crews=クルー数
  if (!S) return null;
  const key = S.alias[team] || team;
  const series = S.byTeam[key];
  if (!series) return null;
  const tot = series[mode === "athletes" ? "athletes" : "crews"];
  const mArr = series[mode === "athletes" ? "mAth" : "mCrews"];
  const wArr = series[mode === "athletes" ? "wAth" : "wCrews"];
  const max = Math.max(1, ...tot.filter((v) => v != null));
  const unit = mode === "athletes" ? "名" : "クルー";
  // 優勝した年（歴代優勝データ・短縮名で照合）
  const short = H ? (H.NAME_MAP[team] !== undefined ? team
    : (Object.keys(H.NAME_MAP).find((k) => H.NAME_MAP[k] === team) || team)) : team;
  const wins = {};
  if (H) H.rows.forEach((r) => { if (r.team === short) wins[r.year] = (wins[r.year] || 0) + 1; });
  // 直近3大会 vs 10年前3大会
  const avg = (arr) => { const x = arr.filter((v) => v != null); return x.length ? x.reduce((p, q) => p + q, 0) / x.length : null; };
  const n = S.years.length;
  const recent = avg(tot.slice(n - 3));
  const past = avg(tot.slice(Math.max(0, n - 13), Math.max(3, n - 10)));
  let trend = null;
  if (recent != null && past != null && past > 0) {
    const r = (recent - past) / past;
    trend = { pct: Math.round(r * 100),
      label: r >= 0.15 ? "拡大" : r <= -0.15 ? "縮小" : "ほぼ横ばい",
      cls: r >= 0.15 ? "up" : r <= -0.15 ? "down" : "flat" };
  }
  const first = tot.findIndex((v) => v != null);
  const last = tot.length - 1 - tot.slice().reverse().findIndex((v) => v != null);
  return (
    <div className="sizebox">
      <div className="sizehd">
        <span className="sizeh">出場規模の推移<i>{S.years[0]}–{S.years[n - 1]}</i></span>
        <span className="mtoggle sizetg">
          <button type="button" className={mode === "athletes" ? "on" : ""} onClick={() => setMode("athletes")}>人数</button>
          <button type="button" className={mode === "crews" ? "on" : ""} onClick={() => setMode("crews")}>クルー数</button>
        </span>
      </div>
      {trend && (
        <div className={"sizetrend " + trend.cls}>
          <b>{trend.label}</b>
          <span>直近3大会は10年前より{trend.pct >= 0 ? "＋" : "−"}{Math.abs(trend.pct)}%</span>
        </div>
      )}
      <div className="sizelegend">
        <span><i className="sw m" />男子</span>
        <span><i className="sw w" />女子</span>
        {Object.keys(wins).length > 0 && <span><i className="sw g">★</i>優勝した年（数字は冠数）</span>}
      </div>
      <div className="sizechart">
        {S.years.map((y, i) => {
          const v = tot[i];
          const h = v == null ? 0 : Math.max(3, Math.round(v / max * 100));
          const m = mArr[i], w = wArr[i];
          const hasSplit = v != null && m != null && w != null && (m + w) === v;
          const wr = hasSplit && v > 0 ? Math.round(w / v * 100) : 0;
          const won = wins[y] || 0;
          return (
            <div className={"sizecol" + (v == null ? " none" : "")} key={y}
                 title={y + "年 " + (v == null ? "出場なし" : v + unit + (hasSplit ? "（男" + m + "・女" + w + "）" : "")) + (won ? " ／ 優勝" + won + "冠" : "")}>
              <span className="sizev">{v == null ? "" : v}</span>
              {won > 0 && <span className="sizewin">★{won > 1 ? won : ""}</span>}
              <span className={"sizebar" + (hasSplit ? " split" : "")} style={{ height: h + "%" }}>
                {hasSplit && w > 0 && <i className="sw-w" style={{ height: wr + "%" }} />}
              </span>
              <span className="sizey">{String(y).slice(2)}</span>
            </div>
          );
        })}
      </div>
      <p className="sizenote">
        出典：JARA公式 大会結果（各年の予選に出走したクルー）／2026年はエントリー表。
        「名」は艇の定員×クルー数（舵手を含む）。棒が無い年は出場なし（2016年は結果未掲載、2020・2021年は中止）。
        {first >= 0 && <> 初出場 {S.years[first]}年 ・ 直近 {S.years[last]}年。</>}
      </p>
    </div>
  );
}

function HistoryView({ goTab, onOpenTeam }) {
  const H = window.KANSAI.HISTORY, RW = window.RW;
  const [tab, setTab] = React.useState("rank");
  const [evSel, setEvSel] = React.useState("M1X");   // 「各種目」タブ内で選んでいる種目
  const [openTeam, setOpenTeam] = React.useState(null);
  const evTabs = HIST_EV_ORDER.filter((e) => H.byEvent[e]);
  const TABS = [["rank", "歴代優勝回数"], ["streak", "連覇・記録"], ["chrono", "年表"],
    ["ev", "各種目"], ["trend", "大学・高校・社会人"]];
  const [scope, setScope] = React.useState("all");   // 通算優勝回数：all=歴代すべて / now=今年出場の団体
  const S = H.stats;

  // 今大会に出場する団体（正式名）
  const icTeams = React.useMemo(() => {
    const set = new Set();
    RW.EVENTS.forEach((e) => e.entries.forEach((en) => set.add(RW.baseTeam(en.team))));
    return [...set];
  }, []);
  const entrySet = new Set(icTeams);

  return (
    <section className="view">
      <div className="shead"><span className="tag"><span>GLORY</span></span><span className="jp">栄冠</span>
        <span className="sp" /><span className="meta">{S.editions}大会 · {S.titles}冠</span></div>

      <div className="histlead">
        第{S.from - 1959}回（{S.from}年）から第{S.latest - 1959}回（{S.latest}年）まで、<b>{S.editions}大会・{S.titles}冠</b>の記録。
        計<b>{S.teams}団体</b>が頂点に立ちました。新人戦は大学だけでなく<b>高校・社会人も優勝</b>する大会です
        （大学{H.byKind.univ}冠・高校{H.byKind.hs}冠・社会人/クラブ{H.byKind.club}冠）。
        <span className="histnote">※2000年 第41回（岐阜・長良川）はJARA公式に結果がないため、静岡県ボート協会が当時掲載した公式記録（ウェブアーカイブに保存）で補完。2016年はJARA未掲載のため「ローイングのデータ集」の記録で補完。2020年・2021年は新型コロナのため中止。高校生の選手名は載せていません。</span>
      </div>

      <div className="chips">
        {TABS.map(([k, l]) => (
          <button key={k} className={"chip hc-g" + (tab === k ? " on" : "")} onClick={() => setTab(k)}>{l}</button>
        ))}
      </div>
      {tab === "ev" && (
        <div className="chips" style={{ marginTop: -4 }}>
          {evTabs.map((e) => {
            const cat = e.charAt(0) === "W" || e.slice(0, 2) === "LW" ? "hc-w" : "hc-m";
            return (
              <button key={e} className={"chip " + cat + (evSel === e ? " on" : "")} onClick={() => setEvSel(e)}>{e}</button>
            );
          })}
        </div>
      )}

      {/* ── 通算優勝回数 ── */}
      {tab === "rank" && (() => {
        const nowRows = icTeams.map((t) => ({ t, h: H.forEntryTeam(t) })).filter((x) => x.h)
          .map((x) => ({ team: x.t, n: x.h.n, first: x.h.first, last: x.h.last, evs: x.h.evs }))
          .sort((a, b) => b.n - a.n);
        const none = icTeams.filter((t) => !H.forEntryTeam(t));
        const allRows = H.teamRank.map((t) => ({ team: t.team, n: t.n, first: t.first, last: t.last, evs: t.evs,
          disp: t.team + (entrySet.has(t.team) ? "" : "　·今年は不出場") }));
        const rows = scope === "all" ? allRows : nowRows;
        const sum = nowRows.reduce((n, x) => n + x.n, 0);
        return (<>
          <p className="histlead sm">
            今年の新人戦に出る{icTeams.length}団体のうち<b>{nowRows.length}団体</b>が優勝経験のある団体で、
            合計<b>{sum}冠</b>（歴代{H.stats.known}冠のうち）。残る<b>{none.length}団体</b>は初タイトルに挑みます。
          </p>
          <div className="mtoggle">
            <button type="button" className={scope === "all" ? "on" : ""} onClick={() => setScope("all")}>歴代すべての団体</button>
            <button type="button" className={scope === "now" ? "on" : ""} onClick={() => setScope("now")}>今年出場の団体だけ</button>
          </div>
          <div className="subhead"><IconTrophy className="ic" size={16} />通算優勝回数<span className="shsub">{S.from}–{S.latest}</span></div>
          <HistBar rows={rows} unit="冠" max={rows.length ? rows[0].n : 1}
            onTeam={(r) => setOpenTeam(openTeam === r.team ? null : r.team)}
            openKey={openTeam}
            renderDetail={(t) => {
              const evs = Object.keys(t.evs || {}).sort((a, b) => t.evs[b] - t.evs[a]);
              return (
                <div className="histdetail">
                  <b>{t.disp || t.team}</b>　{t.first}年 初優勝 ・ {t.last}年 最新優勝
                  {evs.length > 0 && <div className="histevs">{evs.map((e) => <span key={e} className="histev">{e} <b>{t.evs[e]}</b></span>)}</div>}
                  <TeamWinTrend team={t.team} />
                </div>
              );
            }} />
          {(() => {
            const none2 = icTeams.filter((t) => !H.forEntryTeam(t));
            const gaps = nowRows.slice().sort((a, b) => a.last - b.last).slice(0, 10);
            return (<>
              <div className="subhead" style={{ marginTop: 18 }}>初優勝を狙う{none2.length}団体</div>
              <div className="histchips">{none2.map((t) => <span className="histchip" key={t}>{t}</span>)}</div>
              <div className="subhead" style={{ marginTop: 18 }}>何年ぶりの栄冠か</div>
              <div className="histgap">
                {gaps.map((x) => (
                  <div className="histgaprow" key={x.team}>
                    <span className="gt">{x.team}</span><span className="gy">{x.last}年 以来</span>
                    <span className="gn"><b>{2026 - x.last}</b>年ぶり</span>
                  </div>
                ))}
              </div>
            </>);
          })()}
        </>);
      })()}

      {/* ── 連覇・記録 ── */}
      {tab === "streak" && (() => {
        const live = H.liveStreaks;                            // 2連覇中から表示
        const past = H.bestStreaks.filter((s) => !s.live);
        const firsts = {};
        H.known.forEach((r) => { if (!firsts[r.team] || r.year < firsts[r.team].year) firsts[r.team] = { team: r.team, year: r.year, ev: r.ev }; });
        const newest = Object.values(firsts).sort((a, b) => b.year - a.year).slice(0, 8);
        return (<>
          {live.length > 0 && (<>
            <div className="subhead"><IconFlame className="ic" size={16} />継続中の連覇<span className="shsub">2026年に記録が伸びるか</span></div>
            <div className="histstreaks">
              {live.map((s, i) => (
                <div className="hstk live" key={i}>
                  <span className="hstk-n"><b>{s.n}</b>連覇中</span>
                  <span className="hstk-b"><b>{H.disp(s.team)}</b>　{s.ev}</span>
                  <span className="hstk-y">{s.from}–{s.to}</span>
                </div>
              ))}
            </div>
          </>)}
          <div className="subhead" style={{ marginTop: 16 }}><IconTrophy className="ic" size={16} />歴代の連覇記録<span className="shsub">3連覇以上</span></div>
          <div className="histstreaks">
            {past.slice(0, 18).map((s, i) => (
              <div className="hstk" key={i}>
                <span className="hstk-n"><b>{s.n}</b>連覇</span>
                <span className="hstk-b"><b>{H.disp(s.team)}</b>　{s.ev}</span>
                <span className="hstk-y">{s.from}–{s.to}</span>
              </div>
            ))}
          </div>
          <div className="subhead" style={{ marginTop: 18 }}>新しく生まれた王者<span className="shsub">初優勝が新しい順</span></div>
          <div className="histgap">
            {newest.map((f) => (
              <div className="histgaprow" key={f.team}>
                <span className="gt">{H.disp(f.team)}</span><span className="gy">{f.ev}</span><span className="gn"><b>{f.year}</b>年 初優勝</span>
              </div>
            ))}
          </div>
        </>);
      })()}

      {/* ── 大学・高校・社会人：年ごとの優勝団体の内訳（新人戦は高校・社会人も勝つ） ── */}
      {tab === "trend" && (() => {
        const KL = { univ: "大学", hs: "高校", club: "社会人・クラブ" };
        const yrs = H.YEARS.filter((y) => y[3].length).map((y) => y[0]).reverse();
        const tot = H.byKind;
        return (<>
          <div className="subhead"><IconTrophy className="ic" size={16} />優勝団体の内訳<span className="shsub">{S.from}–{S.latest}</span></div>
          <p className="histlead sm">
            新人戦は大学1・2年生のほか、<b>中高生と高校卒業2年目までの社会人</b>も出場できます。
            歴代{S.titles}冠のうち、大学<b>{tot.univ}</b>冠・高校<b>{tot.hs}</b>冠・社会人/クラブ<b>{tot.club}</b>冠。
            1年ごとの7種目を、優勝した団体の種類で色分けしました。
          </p>
          <div className="kindleg">
            {["univ", "hs", "club"].map((k) => <span key={k}><i className={"kd " + k} />{KL[k]}</span>)}
          </div>
          <div className="kindyears">
            {yrs.map((y) => {
              const rows = H.rows.filter((r) => r.year === y).sort((a, b) => HIST_EV_ORDER.indexOf(a.ev) - HIST_EV_ORDER.indexOf(b.ev));
              return (
                <div className="kyr" key={y}>
                  <b className="ky">{y}</b>
                  <div className="kbar">
                    {rows.map((r, i) => (
                      <span key={i} className={"kc " + H.kindOf(r.team)} title={r.ev + " " + r.team}>
                        <i>{r.ev}</i>{H.disp(r.team)}
                      </span>
                    ))}
                  </div>
                </div>
              );
            })}
          </div>
        </>);
      })()}

      {/* ── 年表 ── */}
      {tab === "chrono" && (() => {
        const DEF = H.defending;
        return (<>
          <div className="subhead"><IconMedal className="ic" size={16} />前回大会の王者<span className="shsub">{S.latest}年 第{S.latest - 1959}回</span></div>
          <div className="histchips">
            {DEF.map((d, i) => (
              <span className="histchip def" key={i}>{d.ev} <b>{H.disp(d.team)}</b>{d.athlete ? "（" + d.athlete + "）" : ""}</span>
            ))}
          </div>
          <div className="subhead" style={{ marginTop: 18 }}><IconCal className="ic" size={16} />年表<span className="shsub">新しい順</span></div>
          <div className="histyears">
            {H.YEARS.slice().reverse().map(([year, wa, ed, wins, note]) => (
              <div className="hy" key={year}>
                <div className="hy-h"><b>{year}</b><span className="hy-wa">{wa}</span><span className="hy-ed">第{ed}回</span></div>
                {note && <div className="hy-note">{note}</div>}
                {wins.length > 0 && (
                  <div className="hy-wins">
                    {wins.map(([ev, team, ath], i) => (
                      <span className="hy-w" key={i}><i className={ev.charAt(0) === "W" ? "w" : ""}>{ev}</i>{H.disp(team)}{ath ? "（" + ath + "）" : ""}</span>
                    ))}
                  </div>
                )}
              </div>
            ))}
          </div>
        </>);
      })()}

      {tab === "ev" && (() => {
        const ev = evSel;
        const E = H.byEvent[ev];
        const list = H.rows.filter((r) => r.ev === ev).sort((a, b) => b.year - a.year);
        const held = !!HIST_EV2KEY[ev] && RW.EVENTS.some((x) => x.key === HIST_EV2KEY[ev]);
        const scull = list.some((r) => r.athlete);
        const streaks = H.bestStreaks.filter((x) => x.ev === ev)
          .concat(H.liveStreaks.filter((x) => x.ev === ev && x.n < 3));   // 2連覇中もその種目の欄には出す
        const yrs = list.map((r) => r.year);
        return (<>
          <div className="subhead"><IconTrophy className="ic" size={16} />{ev}　{HIST_EV_NAME[ev] || ""}
            <span className="shsub">{E.n}大会</span></div>
          <p className="histlead sm">
            {Math.min.apply(null, yrs)}年〜{Math.max.apply(null, yrs)}年に<b>{E.n}回</b>実施。
            最多優勝は<b>{H.disp(E.top[0].team)}</b>の{E.top[0].n}回。
            {held ? "　今大会も実施されます。" : (ev === "W4+" ? "　※2001年まで実施（翌年から舵手付きクォドルプル W4X+ に）。" : "　※この種目は現在は行われていません。")}
            {scull ? "　歴代の優勝者名が残っている種目です。" : ""}
          </p>
          <div className="subhead">優勝回数</div>
          <HistBar rows={E.top.map((t) => ({ ...t, disp: H.disp(t.team) }))} unit="回" max={E.top[0].n} />
          {streaks.length > 0 && (
            <div className="histstreaks" style={{ marginBottom: 12 }}>
              {streaks.slice(0, 3).map((x, i) => (
                <div className={"hstk" + (x.live ? " live" : "")} key={i}>
                  <span className="hstk-n"><b>{x.n}</b>{x.live ? "連覇中" : "連覇"}</span>
                  <span className="hstk-b"><b>{H.disp(x.team)}</b></span>
                  <span className="hstk-y">{x.from}–{x.to}</span>
                </div>
              ))}
            </div>
          )}
          <div className="subhead" style={{ marginTop: 18 }}>歴代優勝{scull ? "者" : "団体"}<span className="shsub">新しい順</span></div>
          <div className="histscull">
            {list.map((r, i) => (
              <div className="hsrow" key={i}>
                <span className="hsy">{r.year}</span>
                {scull
                  ? <><span className="hsn">{r.athlete || (r.minor ? "（高校生のため氏名は非掲載）" : "—")}</span><span className="hst">{H.disp(r.team)}</span></>
                  : <span className="hsn">{H.disp(r.team)}</span>}
              </div>
            ))}
            {HIST_EV_1974.indexOf(ev) >= 0 && (
              <div className="hsrow nowin">
                <span className="hsy">1974</span>
                <span className="hsn">第1回は荒天のため決勝中止／優勝校なし</span>
              </div>
            )}
          </div>
          <div className="subhead" style={{ marginTop: 18 }}><IconChart className="ic" size={16} />最速記録</div>
          {window.KANSAI.RECORDS && window.KANSAI.RECORDS[ev] && (() => {
            const R = window.KANSAI.RECORDS[ev];
            const U = (window.KANSAI.U23 || {})[ev];
            const W = (window.KANSAI.WBT || {})[ev];
            const toSec = (t) => { const m = String(t).match(/^(\d+):(\d+(?:\.\d+)?)$/); return m ? +m[1] * 60 + parseFloat(m[2]) : null; };
            const base = toSec(R.t);
            const gap = (t) => { const x = toSec(t); return (x == null || base == null) ? null : (base - x); };
            const row = (lb, cls, t, who, sub) => {
              const g = gap(t);
              return (
                <div className={"rec-row " + cls} key={lb}>
                  <span className="rec-lb">{lb}</span>
                  <b className="rec-t">{t}</b>
                  <span className="rec-who">{who}</span>
                  {g != null && g > 0 && <span className="rec-gap">＋{g.toFixed(2)}秒</span>}
                  <span className="rec-yr">{sub}</span>
                </div>
              );
            };
            return (
              <div className="recbox">
                {row("新人戦最速", "ic", R.t, R.crew, R.year + "年・" + R.round)}
                {U && U.t && row("U23世界最速", "u23", U.t, U.crew, U.year + "年・" + (U.venue || ""))}
                {W && row("世界最速", "wbt", W.t, W.crew, W.year + "年・" + (W.venue || ""))}
                {!W && <p className="rec-none">この種目はWorld Rowingのシニア種目ではないため、世界最速の記録がありません。</p>}
                {W && W.year < 2000 && (
                  <p className="rec-none">この種目は今は国際大会でほとんど行われていないため、記録が古いまま残っています。</p>
                )}
              </div>
            );
          })()}
          {window.KANSAI.RECORDS && window.KANSAI.RECORDS[ev] && (
            <p className="sizenote" style={{ margin: "-4px 2px 10px" }}>
              最速はJARA公式アーカイブ（2001〜2025年。2016年は結果未掲載、2020・2021年は中止）の全ラウンドから算出した<b>参考記録</b>。2000年（岐阜・長良川）の結果はアーカイブ外のため、この計算には含めていません。
              風などの条件で大きく変わるため、公式のコースレコードではありません。
              <br />世界最速は<b>ワールドカップ・世界選手権・五輪で出たタイムだけ</b>が対象です。
              新人戦を含む国内大会のタイムは、どれだけ速くても世界記録としては扱われません。
            </p>
          )}
          <RecordTrend ev={ev} />
        </>);
      })()}

      <p className="evnote histsrc">
        出典：<a href={H.SOURCE.url} target="_blank" rel="noopener noreferrer">{H.SOURCE.name}</a>（2016年は
        <a href={H.SOURCE2.url} target="_blank" rel="noopener noreferrer">{H.SOURCE2.name}</a>、2000年は
        <a href={H.SOURCE3.url} target="_blank" rel="noopener noreferrer">{H.SOURCE3.name}</a>）。
        当サイトは非公式のため、記録の正確性は出典元をご確認ください。
        団体名は当時の表記のまま（A/B等のクルー記号のみ除去）。高校生の選手名は掲載していません。
      </p>
    </section>
  );
}
Object.assign(window, { HistoryView });
