/* home.jsx — phase-aware home (before / during / after) */

function PhaseBar({ phase, subPhase, ord, clockLabel, goTab }) {
  const meta = {
    before: { name: "PRE-EVENT", text: "大会前 — お気に入りのチームを追加しよう" },
    during: subPhase === "interday"
      ? { name: "DAY 1 終了", text: "初日終了 — 明日の進出クルーを表示" }
      : { name: "LIVE", text: "大会中 — 進行中・速報を優先表示" },
    after: { name: "FINISHED", text: "大会後 — 結果・順位・強豪を表示" },
  }[phase];
  const toTeams = phase === "before" && goTab ? () => goTab("teams") : undefined;
  return (
    <div className={"phasebar" + (toTeams ? " clk" : "")} onClick={toTeams} style={toTeams ? { cursor: "pointer" } : undefined}>
      <span className="pdot" />
      <span className="pname">{meta.name}</span>
      <span className="ptext">{meta.text}</span>
      <span className="pclock">{clockLabel}</span>
    </div>
  );
}

function Countdown({ ord }) {
  // real countdown to race day (7/4 2026 08:30 JST) — 秒まで生きて動く
  const target = new Date("2026-07-04T08:30:00+09:00").getTime();
  const [now, setNow] = React.useState(() => Date.now());
  React.useEffect(() => {
    const id = setInterval(() => setNow(Date.now()), 1000);
    return () => clearInterval(id);
  }, []);
  const ms = Math.max(0, target - now);
  const d = Math.floor(ms / 86400000);
  const h = Math.floor((ms % 86400000) / 3600000);
  const m = Math.floor((ms % 3600000) / 60000);
  const s = Math.floor((ms % 60000) / 1000);
  const units = [[d, "DAYS"], [h, "HRS"], [m, "MIN"], [s, "SEC"]];
  return (
    <div className="countcard">
      <span className="lanes" />
      <span className="cglow" aria-hidden="true" />
      <div className="ci">
        <div className="cl"><span className="cl-spark" aria-hidden="true" />Road to Hamadera<span className="cl-spark r" aria-hidden="true" /></div>
        <div className="cbig">決戦まで</div>
        <div className="cnums">
          {units.map(([v, l], i) => (
            <React.Fragment key={l}>
              {i > 0 && <span className="ccolon" aria-hidden="true">:</span>}
              <div className={"cu" + (l === "SEC" ? " sec" : "")}>
                {/* key=値 で毎回マウントし直し、切替の瞬間にフリップが走る */}
                <b key={v}>{String(v).padStart(2, "0")}</b>
                <span>{l}</span>
              </div>
            </React.Fragment>
          ))}
        </div>
        <div className="csub"><i className="cdot" aria-hidden="true" />7月4日(土) 8:30 第1レース発艇　／　浜寺水路</div>
      </div>
    </div>
  );
}

function HighlightCard({ ev, onOpen, sub, rail }) {
  return (
    <section className="hcard" onClick={() => onOpen(ev.key)}>
      <span className="acc" />
      <div className="hc-top">
        <Code ev={ev} /><DivChip div={ev.div} />
        <span className="hc-name">{ev.name}</span>
        <span className="hc-cnt">{ev.entries.length}</span>
      </div>
      {sub && <div className="hc-body">{sub}</div>}
      {rail && rail.length > 0 && (
        <div className="hc-rail">
          {rail.map((r, i) => <span className={"hc-pill" + (r.star ? " star" : "")} key={i}>{r.label}</span>)}
        </div>
      )}
    </section>
  );
}

function etaLabel(race, ord) {
  const diff = race.ord - ord;
  if (diff <= 0) return null;
  if (diff < 60) return `あと${diff}分`;
  if (race.dayIndex * 1440 > ord + 60 && diff > 240) return `${race.date}(${race.dow})`;
  return `あと${Math.floor(diff / 60)}h${String(diff % 60).padStart(2, "0")}m`;
}

function NextList({ races, ord, favTeams, onOpenRace }) {
  const RW = window.RW;
  return (
    <div className="nextlist">
      {races.map((r) => {
        const favHit = r.ev.entries.some((e) => favTeams[RW.baseTeam(e.team)]);
        return (
          <div className="nextrow" key={r.id} style={{ cursor: "pointer" }} onClick={() => onOpenRace(r.id)}>
            <div className="nt">{r.t}</div>
            <div className="nx">
              <div className="l1"><Code ev={r.ev} /><DivChip div={r.ev.div} />{r.isFinalA && <span className="finchip">A決勝</span>}</div>
              <div className="nm">{r.ev.name} · {r.round}{r.grp ? " " + r.grp : ""}</div>
            </div>
            {favHit && <span style={{ color: "var(--red)", flex: "none", display: "inline-flex" }}><Star size={14} /></span>}
            <span className="eta">{etaLabel(r, ord)}</span>
            <IconArrow size={15} />
          </div>
        );
      })}
    </div>
  );
}

// 特定レース(race)における特定チーム(team)の実結果1件を返す（realRacesForはラウンド単位で複数組を返すため、
// お気に入りチームのレーンを探して着順・qualifyだけ取り出す。無ければnull）
function teamResultInRace(race, team) {
  const RW = window.RW;
  const krs = RW.realRacesFor(race);
  if (!krs.length) return null;
  const norm = (s) => String(s || "").replace(/[\s　]/g, "");
  const crews = (RW.TEAMS[team] ? RW.TEAMS[team].crews : []).filter((c) => c.evKey === race.evKey);
  const multi = crews.length > 1;
  for (const kr of krs) {
    for (const l of kr.lanes) {
      const hit = multi ? crews.some((c) => norm(l.team) === norm(c.label)) : RW.baseTeam(l.team) === team;
      if (hit) return { rank: l.rank, qualify: l.qualify || null };
    }
  }
  return null;
}

// マイチーム・タイムライン（大会中: 本日／初日終了後: 明日）。お気に入りチームが出る全レースを時刻順に1本で表示
function MyTeamDaySheet({ ord, subPhase, favTeams, onOpenRace }) {
  const RW = window.RW;
  const favTeamNames = Object.keys(favTeams).filter((t) => favTeams[t]);
  if (!favTeamNames.length) return null;
  const targetDay = subPhase === "interday" ? Math.floor(ord / 1440) + 1 : Math.floor(ord / 1440);
  const races = RW.FLAT.filter((r) => r.dayIndex === targetDay && r.ev.entries.some((e) => favTeamNames.includes(RW.baseTeam(e.team))))
    .sort((a, b) => a.ord - b.ord);
  if (!races.length) return null;

  // 掛け持ち検出：同一選手（氏名一致）が60分以内に2レース以上に出ている行に警告バッジを出す
  const ROS = window.KANSAI.ROSTERS || {};
  const norm = (s) => String(s || "").replace(/[\s　]/g, "");
  const namesByRace = races.map((r) => {
    const set = new Set();
    r.ev.entries.forEach((en) => {
      const ms = (ROS[r.evKey] || {})[en.n];
      if (ms) ms.forEach((m) => set.add(norm(m[1])));
    });
    return set;
  });
  const doubleBooked = races.map((r, i) => races.some((r2, j) => {
    // 同一種目のB決勝／A決勝など「同じ種目の別ラウンド」はエントリー全体が重複して当然なので対象外にする
    if (i === j || r2.evKey === r.evKey || Math.abs(r2.ord - r.ord) > 60) return false;
    for (const nm of namesByRace[i]) if (namesByRace[j].has(nm)) return true;
    return false;
  }));

  return (
    <>
      <div className="subhead"><IconClock className="ic" size={16} />マイチーム・タイムライン
        <span className="subnote">{subPhase === "interday" ? "明日" : "本日"}の出場を時刻順に</span></div>
      <div className="nextlist">
        {races.map((r, i) => {
          const teams = favTeamNames.filter((t) => r.ev.entries.some((e) => RW.baseTeam(e.team) === t));
          const done = RW.statusOf(r, ord) === "done";
          const results = done ? teams.map((t) => ({ team: t, res: teamResultInRace(r, t) })).filter((x) => x.res) : [];
          return (
            <div className="nextrow" key={r.id} style={{ cursor: "pointer" }} onClick={() => onOpenRace(r.id)}>
              <div className="nt">{r.t}</div>
              <div className="nx">
                <div className="l1">
                  <Code ev={r.ev} /><DivChip div={r.ev.div} />
                  {r.isFinalA && <span className="finchip">A決勝</span>}
                  {doubleBooked[i] && <span className="dbchip">⚠ 掛け持ち</span>}
                </div>
                <div className="nm">{r.ev.name} · {r.round}{r.grp ? " " + r.grp : ""} · {teams.join("・")}</div>
                {results.length > 0 && (
                  <div style={{ marginTop: 4, display: "flex", flexWrap: "wrap", gap: 5 }}>
                    {results.map(({ team, res }, k) => (
                      <span className={"progchip" + (res.qualify ? " go" : "")} key={k}>
                        {team}{res.rank != null ? " " + res.rank + "位" : ""}{res.qualify ? " " + res.qualify : ""}
                      </span>
                    ))}
                  </div>
                )}
              </div>
              {!done && <span className="eta">{etaLabel(r, ord)}</span>}
            </div>
          );
        })}
      </div>
    </>
  );
}

function PodiumRows({ podium, onOpenTeam }) {
  return podium.map((p) => (
    <div className="prow" key={p.rank}>
      <MedalDisc rank={p.rank} />
      <span className="pt" onClick={() => onOpenTeam && onOpenTeam(p.team)} style={{ cursor: onOpenTeam ? "pointer" : "default" }}>{p.label}</span>
      <span className="pn">{p.time}</span>
    </div>
  ));
}

// 浜寺水路の天気カード（前日・大会中のみ表示。取得できていない間は何も出さない）
const WIND_DIRS = ["北", "北北東", "北東", "東北東", "東", "東南東", "南東", "南南東", "南", "南南西", "南西", "西南西", "西", "西北西", "北西", "北北西"];
function windDirLabel(deg) { return WIND_DIRS[Math.round(((deg % 360) + 360) % 360 / 22.5) % 16]; }
function heatLevel(tempC) {
  if (tempC == null) return null;
  if (tempC >= 31) return { label: "厳重警戒", cls: "danger" };
  if (tempC >= 28) return { label: "警戒", cls: "warn" };
  return { label: "注意", cls: "ok" };
}
function WeatherCard({ weather }) {
  if (!weather || !weather.current) return null;
  const cur = weather.current;
  const temp = cur.temperature_2m, rh = cur.relative_humidity_2m, wind = cur.wind_speed_10m, windDir = cur.wind_direction_10m;
  let popMax = null;
  const hourly = weather.hourly;
  if (hourly && hourly.time && hourly.precipitation_probability) {
    const start = Math.max(0, hourly.time.indexOf(cur.time));
    const slice = hourly.precipitation_probability.slice(start, start + 3);
    if (slice.length) popMax = Math.max(...slice);
  }
  const heat = heatLevel(temp);
  return (
    <div className="wxcard">
      <div className="wxcard-hd">浜寺水路 今のコンディション</div>
      <div className="wxcard-row">
        <div className="wx-item">
          <span className="wx-arrow" style={{ transform: `rotate(${(windDir || 0) + 180}deg)` }}>↑</span>
          <b>{wind != null ? wind.toFixed(1) : "—"} m/秒</b>
          <span>{windDir != null ? windDirLabel(windDir) + "の風" : "風"}</span>
        </div>
        <div className="wx-item"><b>{temp != null ? Math.round(temp) : "—"}℃</b><span>気温</span></div>
        <div className="wx-item"><b>{rh != null ? Math.round(rh) : "—"}%</b><span>湿度</span></div>
        <div className="wx-item"><b>{popMax != null ? popMax : "—"}%</b><span>降水確率(3h)</span></div>
      </div>
      {heat && <div className={"wx-heat " + heat.cls}>暑さ目安：{heat.label}</div>}
      <div className="wx-note">簡易目安です。実際の対応は運営の指示に従ってください。</div>
    </div>
  );
}

// YouTubeライブ配信カード（ページ内埋め込み再生・時間差表示）
// 敗者戦の配信は 7/4 15:00 以降に表示（それまでは予選のみ）
// 配信一覧は config.js（window.KANSAI.ytStreams）に集約。fromISO→表示開始のタイムスタンプに変換。
const YT_STREAMS = ((window.KANSAI_CONFIG && window.KANSAI_CONFIG.ytStreams) || [
  { key: "heats", id: "WpbQ1G1AIzk", label: "予選", sub: "8:30–", day: "7/4（土）", fromISO: null },
  { key: "cr", id: "69o6zJgIisw", label: "敗者戦（CR）", sub: "15:50–", day: "7/4（土）", fromISO: "2026-07-04T15:00:00+09:00" },
  { key: "finals", id: "xxUKNG9DQ_I", label: "準決・決勝", sub: "8:30–", day: "7/5（日）", fromISO: "2026-07-05T00:00:00+09:00" },
]).map((s) => ({ ...s, from: s.fromISO ? new Date(s.fromISO).getTime() : 0 }));
function YouTubeLive() {
  const [now, setNow] = React.useState(() => Date.now());
  React.useEffect(() => { const t = setInterval(() => setNow(Date.now()), 60000); return () => clearInterval(t); }, []);
  const vis = YT_STREAMS.filter((s) => now >= s.from);
  const [sel, setSel] = React.useState(null);
  const [playing, setPlaying] = React.useState(false);
  if (!vis.length) return null;
  const cur = vis.find((s) => s.key === sel) || vis[vis.length - 1];  // 既定＝今の時間帯の配信
  return (
    <div className="ytlive">
      <div className="ytlive-hd">
        <svg width="22" height="16" viewBox="0 0 28 20" aria-hidden="true"><path d="M27.4 3.1a3.5 3.5 0 0 0-2.46-2.48C22.77 0 14 0 14 0S5.23 0 3.06.62A3.5 3.5 0 0 0 .6 3.1 36.6 36.6 0 0 0 0 10c0 2.33.2 4.63.6 6.9a3.5 3.5 0 0 0 2.46 2.48C5.23 20 14 20 14 20s8.77 0 10.94-.62a3.5 3.5 0 0 0 2.46-2.48c.4-2.27.6-4.57.6-6.9 0-2.33-.2-4.63-.6-6.9Z" fill="#FF0033" /><path d="M11.2 14.29 18.48 10 11.2 5.71v8.58Z" fill="#fff" /></svg>
        <span className="ytlive-ttl">ライブ配信</span>
        <span className="ytlive-badge"><span className="ytlive-dot" />LIVE</span>
      </div>
      {vis.length > 1 && (
        <div className="ytlive-chips">
          {vis.map((s) => (
            <button type="button" key={s.key} className={"ytlive-chip" + (s.key === cur.key ? " on" : "")}
                    onClick={() => { setSel(s.key); setPlaying(false); }}>
              {s.label}<i>{s.sub}</i>
            </button>
          ))}
        </div>
      )}
      <div className="ytlive-frame">
        {playing ? (
          <iframe src={"https://www.youtube.com/embed/" + cur.id + "?autoplay=1&playsinline=1"} title="YouTube live"
                  allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowFullScreen />
        ) : (
          <div className="ytlive-face" style={{ backgroundImage: "url(https://i.ytimg.com/vi/" + cur.id + "/hqdefault.jpg)" }}
               onClick={() => setPlaying(true)} role="button" aria-label="再生">
            <span className="ytlive-shade" />
            <span className="ytlive-bigplay"><svg width="18" height="20" viewBox="0 0 12 14" aria-hidden="true"><path d="M0 0l12 7-12 7z" fill="#fff" /></svg></span>
            <span className="ytlive-cap"><b>{cur.day}{cur.label}</b>{" "}{cur.sub}{" "}タップで再生</span>
          </div>
        )}
      </div>
    </div>
  );
}

// サブフェーズ eve（開幕まで24時間以内）：カウントダウンの上に、お気に入りチームの初戦時刻を並べる
function EveCard({ favTeams, onOpenRace }) {
  const RW = window.RW;
  const favTeamNames = Object.keys(favTeams).filter((t) => favTeams[t]);
  const rows = favTeamNames.map((t) => {
    const race = RW.FLAT.filter((r) => r.ev.entries.some((e) => RW.baseTeam(e.team) === t)).sort((a, b) => a.ord - b.ord)[0];
    return race ? { team: t, race } : null;
  }).filter(Boolean).sort((a, b) => a.race.ord - b.race.ord);
  if (!rows.length) return null;
  return (
    <div className="evecard">
      <div className="evecard-hd"><IconClock size={14} />明日はいよいよ開幕 — お気に入りの初戦</div>
      <div className="nextlist">
        {rows.map(({ team, race }) => (
          <div className="nextrow" key={team} style={{ cursor: "pointer" }} onClick={() => onOpenRace(race.id)}>
            <div className="nt" style={{ width: 56 }}>{race.dow} {race.t}</div>
            <div className="nx"><div style={{ fontWeight: 800, fontSize: 13 }}>{team}</div><div className="nm">{race.ev.code} {race.ev.name} · {race.round}</div></div>
            <IconArrow size={16} />
          </div>
        ))}
      </div>
    </div>
  );
}

// サブフェーズ interday（初日終了〜2日目開始前）：お気に入りの進出状況＋明日の初戦をホーム最上部にまとめる
function InterdaySummary({ favTeams, onOpenTeam, onOpenRace }) {
  const RW = window.RW;
  const favTeamNames = Object.keys(favTeams).filter((t) => favTeams[t]);
  const progRows = favTeamNames.map((team) => ({ team, prog: RW.favProgress(team) })).filter((r) => r.prog.length > 0);
  const tomorrow = favTeamNames.map((t) => {
    const race = RW.FLAT.filter((r) => r.dayIndex === 1 && r.ev.entries.some((e) => RW.baseTeam(e.team) === t)).sort((a, b) => a.ord - b.ord)[0];
    return race ? { team: t, race } : null;
  }).filter(Boolean).sort((a, b) => a.race.ord - b.race.ord);
  if (!progRows.length && !tomorrow.length) return null;
  return (
    <div className="interday">
      <div className="interday-hd">初日終了 — 明日の進出クルー</div>
      {progRows.map(({ team, prog }) => (
        <div className="interday-team" key={team}>
          <div className="interday-teamnm" onClick={() => onOpenTeam(team)}>{team}</div>
          <div className="interday-chips">
            {prog.map((p, i) => (
              <span className={"progchip" + (p.qualify ? " go" : "")} key={i}>
                {p.code} {p.crewLabel}　{p.lastRound}{p.grp ? " " + p.grp : ""}{p.rank != null ? " " + p.rank + "位" : ""}{p.qualify ? " " + p.qualify : ""}
              </span>
            ))}
          </div>
        </div>
      ))}
      {tomorrow.length > 0 && (
        <div className="interday-tmrw" onClick={() => onOpenRace(tomorrow[0].race.id)}>
          <span>明日の初戦：{tomorrow[0].race.t}　{tomorrow[0].race.ev.code} {tomorrow[0].team}</span>
          <IconArrow size={15} />
        </div>
      )}
    </div>
  );
}

// サブフェーズ justEnded（閉会から24時間以内）：直近のA決勝ハイライトをホーム最上部に
function JustEndedHighlights({ justDone, onOpenTeam }) {
  const RW = window.RW;
  if (!justDone.length) return null;
  return (
    <div className="jehl">
      <div className="jehl-hd"><IconTrophy size={15} />本日のA決勝ハイライト</div>
      {justDone.slice(0, 5).map((fb) => {
        const podium = RW.PODIUMS[fb.ev.key];
        const top = podium && podium[0];
        return (
          <div className="jehl-row" key={fb.ev.key}>
            <Code ev={fb.ev} />
            <span className="jehl-nm">{fb.ev.name}</span>
            {top && <span className="jehl-win" onClick={() => onOpenTeam(top.team)}>優勝 {top.label}</span>}
          </div>
        );
      })}
    </div>
  );
}

function FavEntryRow({ team, count, evs, onOpenEvent, toggleFav }) {
  const RW = window.RW;
  const ROS = window.KANSAI.ROSTERS || {};
  const t = RW.TEAMS[team];
  const byEvent = {};
  (t ? t.crews : []).forEach((c) => { (byEvent[c.evKey] = byEvent[c.evKey] || []).push(c); });
  const [open, setOpen] = React.useState(false);
  const [openEv, setOpenEv] = React.useState(null);
  return (
    <>
      <div className="nextrow" style={{ cursor: "pointer" }} onClick={() => setOpen((o) => !o)}>
        <div className="nt" style={{ width: 34 }}><StarBtn on={true} onClick={() => toggleFav(team)} size={23} /></div>
        <div className="nx">
          <div style={{ fontWeight: 800, fontSize: 13 }}>{team}</div>
          <div className="nm">{count}種目にエントリー · {evs.map((e) => e.code).slice(0, 5).join(" / ")}</div>
        </div>
        <IconChevron dir={open ? "up" : "down"} size={14} />
      </div>
      {open && (
        <div className="favteam-evs">
          {evs.map((ev) => {
            const crews = byEvent[ev.key] || [];
            const ros = ROS[ev.key] || {};
            const opEv = openEv === ev.key;
            return (
              <React.Fragment key={ev.key}>
                <div className="trow clk" onClick={() => setOpenEv(opEv ? null : ev.key)}>
                  <span className="tcode"><Code ev={ev} /></span>
                  <span className="tnm">{ev.name}{ev.div !== "general" ? " · " + RW.DIV[ev.div].label : ""}</span>
                  <IconChevron dir={opEv ? "up" : "down"} size={12} />
                </div>
                {opEv && (
                  <div className="trow-ros">
                    {crews.map((c) => {
                      const ms = ros[c.n] || [];
                      return (
                        <div className="trow-crew" key={c.n}>
                          {crews.length > 1 && <div className="trow-crewlbl">{c.label}</div>}
                          {ms.length ? ms.map((m, i) => (
                            <div className="trow-seat" key={i}>
                              {ev.type !== "scull" && <span className="trow-s">{m[0]}</span>}
                              <span className="trow-nm"><RowerName name={m[1]} kana={m[2]} /></span>
                            </div>
                          )) : <div className="trow-seat"><span className="trow-nm">{c.name || c.label}</span></div>}
                        </div>
                      );
                    })}
                    <button type="button" className="trow-evbtn" onClick={(e) => { e.stopPropagation(); onOpenEvent(ev.key); }}>種目の全クルーを見る →</button>
                  </div>
                )}
              </React.Fragment>
            );
          })}
        </div>
      )}
    </>
  );
}

function HomeView({ ord, phase, subPhase, weather, clockLabel, favTeams, onOpenTeam, onOpenEvent, onOpenRace, goTab, teamQ, setTeamQ, toggleFav }) {
  const RW = window.RW;
  const { live, next, justDone } = RW.liveAndNext(ord);

  // ----- BEFORE -----
  if (phase === "before") {
    const favTeamNames = Object.keys(favTeams).filter((t) => favTeams[t]);
    const favEntries = favTeamNames.map((t) => {
      const evs = RW.EVENTS.filter((e) => e.entries.some((en) => RW.baseTeam(en.team) === t));
      return { team: t, count: evs.length, evs };
    }).filter((x) => x.count > 0);

    const q = teamQ.trim();
    const searchHits = q ? RW.TEAM_NAMES.filter((t) => t.includes(q)) : [];
    const athleteHits = q ? RW.searchAthletes(q).slice(0, 6) : [];

    return (
      <section className="view">
        {!q && <Countdown ord={ord} />}
        {!q && subPhase === "eve" && <WeatherCard weather={weather} />}
        {!q && <LiveStrip ord={ord} phase={phase} onOpen={() => goTab("home")} onOpenRace={onOpenRace} />}
        {!q && <YouTubeLive />}
        {!q && subPhase === "eve" && <EveCard favTeams={favTeams} onOpenRace={onOpenRace} />}

        <div className="search hsearch">
          <IconSearch size={20} />
          <input value={teamQ} onChange={(e) => setTeamQ(e.target.value)}
                 placeholder="お気に入りのチーム名は？（例：同志社、京都、…）" autoComplete="off" />
          {q && <button type="button" className="hsearch-clear" aria-label="検索をクリア" onClick={() => setTeamQ("")}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round"><path d="M6 6l12 12M18 6L6 18" /></svg>
          </button>}
        </div>

        {q && (
          <div className="nextlist" style={{ marginTop: 2 }}>
            {searchHits.length === 0 ? (
              <div className="empty">「{q}」に一致する団体はありません</div>
            ) : (
              <>
                {searchHits.slice(0, 8).map((t) => {
                  const isFav = !!favTeams[t];
                  return (
                    <div className="nextrow" key={t}>
                      <button type="button" className={"favaddbtn" + (isFav ? " on" : "")}
                              onClick={() => { toggleFav(t); setTeamQ(""); }}>
                        <Star size={14} color={isFav ? "#fff" : "var(--red)"} />
                        {isFav ? "登録済み" : "お気に入り"}
                      </button>
                      <div className="nx" style={{ cursor: "pointer", display: "flex", alignItems: "center", gap: 8 }} onClick={() => onOpenTeam(t)}>
                        <div style={{ flex: 1, minWidth: 0 }}>
                          <div style={{ fontWeight: 800, fontSize: 13 }}>{t}</div>
                          <div className="nm">{RW.TEAMS[t].crews.length}クルーがエントリー</div>
                        </div>
                        <IconArrow size={16} />
                      </div>
                    </div>
                  );
                })}
                {searchHits.length > 8 && <div className="empty" style={{ padding: "8px 0 0" }}>ほか{searchHits.length - 8}団体 · 続きは<span style={{ color: "var(--ph-ink)", fontWeight: 700, cursor: "pointer" }} onClick={() => goTab("teams")}>チームタブ</span>で</div>}
              </>
            )}
          </div>
        )}

        {q && athleteHits.length > 0 && (
          <>
            <div className="subhead">選手</div>
            <div className="nextlist" style={{ marginTop: 2 }}>
              {athleteHits.map((a, i) => (
                <div className="nextrow" key={i} style={{ cursor: "pointer" }} onClick={() => onOpenTeam(RW.baseTeam(a.team))}>
                  <div className="nx" style={{ display: "flex", alignItems: "center", gap: 8 }}>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontWeight: 800, fontSize: 13 }}>{a.name}{a.kana && <i className="rn-kana" style={{ marginLeft: 6 }}>{a.kana}</i>}</div>
                      <div className="nm">{a.team}・{a.evs.join(" / ")}</div>
                    </div>
                    <IconArrow size={16} />
                  </div>
                </div>
              ))}
            </div>
          </>
        )}

        {!q && favEntries.length > 0 && (
          <>
            <div className="subhead"><Star size={15} color="var(--red)" />お気に入りの出場</div>
            <div className="nextlist">
              {favEntries.map(({ team, count, evs }) => (
                <FavEntryRow key={team} team={team} count={count} evs={evs} onOpenEvent={onOpenEvent} toggleFav={toggleFav} />
              ))}
            </div>
          </>
        )}

        {!q && (<><div className="subhead"><IconOar className="ic" size={16} />種目一覧 <span className="subnote">部門別・男女</span></div>
        <EventDivGrid onOpenEvent={onOpenEvent} /></>)}

        {!q && (
          <div style={{ textAlign: "center", marginTop: 16, display: "flex", flexDirection: "column", alignItems: "center", gap: 8 }}>
            <button type="button" className="chip" onClick={() => goTab("rules")}>はじめての観戦はこちら ›</button>
            <button type="button" className="chip" onClick={() => { window.__openChampions = true; goTab("rules"); }}>歴代優勝者を見る ›</button>
          </div>
        )}
      </section>
    );
  }

  // ----- DURING -----
  if (phase === "during") {
    const cur = live[0], up = next[0];
    const favTeamNames = Object.keys(favTeams).filter((t) => favTeams[t]);
    const favNext = favTeamNames.map((t) => {
      const races = RW.FLAT.filter((r) => r.ord > ord && r.ev.entries.some((e) => RW.baseTeam(e.team) === t)).sort((a, b) => a.ord - b.ord);
      return races[0] ? { team: t, race: races[0] } : null;
    }).filter(Boolean).sort((a, b) => a.race.ord - b.race.ord);

    return (
      <section className="view">
        <PhaseBar phase={phase} subPhase={subPhase} ord={ord} clockLabel={clockLabel} goTab={goTab} />
        {subPhase === "interday" && <InterdaySummary favTeams={favTeams} onOpenTeam={onOpenTeam} onOpenRace={onOpenRace} />}
        {/* 進行中カードは上部のLiveStripと重複するため、レース進行中は非表示にする。
            空き時間だけ「次のレース／本日終了」を大きく出す。 */}
        {!cur && (
          <div className={"bignow" + (up ? "" : " empty")}>
            {up && <span className="lanes" />}
            <div className="bn-in">
              <div className="bn-h"><span className="dotlive" style={{ background: up ? "var(--cyan-2)" : "var(--mut)" }} /><span className="ls-lbl" style={{ color: up ? "var(--cyan-2)" : "var(--sub)" }}>Next · 次のレース</span>{up && <span className="bn-time">{up.date}({up.dow}) {up.t}</span>}</div>
              {up ? (<><div className="bn-code"><Code ev={up.ev} /><DivChip div={up.ev.div} />{up.isFinalA && <span className="finchip">A決勝</span>}</div><div className="bn-name">{up.ev.name}</div><div className="bn-round">{up.round} ／ {etaLabel(up, ord)}</div></>) : (<div className="bn-name" style={{ fontSize: 18 }}>本日のレースは終了しました</div>)}
            </div>
          </div>
        )}

        <YouTubeLive />

        <MyTeamDaySheet ord={ord} subPhase={subPhase} favTeams={favTeams} onOpenRace={onOpenRace} />

        {next.length > 0 && (<><div className="subhead"><IconClock className="ic" size={16} />これからのレース<span style={{ marginLeft: "auto", fontSize: 11, color: "var(--mut)", fontWeight: 700 }}>タップで組・エントリー</span></div><NextList races={next} ord={ord} favTeams={favTeams} onOpenRace={onOpenRace} /></>)}

        {favNext.length > 0 && (
          <><div className="subhead"><Star size={15} color="var(--red)" />お気に入りの予定</div>
          <div className="nextlist">{favNext.map(({ team, race }) => {
            const prog = RW.favProgress(team).find((p) => p.evKey === race.ev.key);
            return (
              <div className="nextrow" key={team} style={{ cursor: "pointer" }} onClick={() => onOpenTeam(team)}>
                <div className="nt" style={{ width: 56 }}>{race.dow} {race.t}</div>
                <div className="nx">
                  <div style={{ fontWeight: 800, fontSize: 13 }}>{team}</div>
                  <div className="nm">{race.ev.code} {race.ev.name} · {race.round}</div>
                  {prog && (
                    <div style={{ marginTop: 4 }}>
                      <span className={"progchip" + (prog.qualify ? " go" : "")}>
                        {prog.lastRound}{prog.grp ? " " + prog.grp : ""}{prog.rank != null ? " " + prog.rank + "位" : ""}{prog.qualify ? " " + prog.qualify : ""}
                      </span>
                    </div>
                  )}
                </div>
                <IconArrow size={16} />
              </div>
            );
          })}
          </div></>
        )}

        {justDone.length > 0 && (
          <><div className="subhead"><IconMedal className="ic" size={16} />直近のメダル<span style={{ marginLeft: "auto", fontSize: 11, color: "var(--mut)", fontWeight: 700 }}>{justDone.length}種目 確定</span></div>
          {justDone.slice(0, 4).map((fb) => (
            <div className="medalcard" key={fb.ev.key}>
              <div className="mh"><Code ev={fb.ev} /><DivChip div={fb.ev.div} /><span className="nm2">{fb.ev.name}</span><span className="when">{fb.dow} {fb.t}</span></div>
              <PodiumRows podium={RW.PODIUMS[fb.ev.key]} onOpenTeam={onOpenTeam} />
            </div>
          ))}
          <div style={{ textAlign: "center", marginTop: 10 }}><button className="chip" onClick={() => goTab("results")}>結果をすべて見る ›</button></div></>
        )}

        {(subPhase === "racing" || subPhase === "interday") && <WeatherCard weather={weather} />}
      </section>
    );
  }

  // ----- AFTER -----
  const table = RW.medalTable(ord).slice(0, 8);
  return (
    <section className="view">
      {/* 大会後：ゼッケン型の種目カードを復活。タイムテーブルの代わりに、種目から結果・選手へ辿る入口にする */}
      <div className="subhead"><IconOar className="ic" size={16} />種目からさがす<span style={{ marginLeft: "auto", fontSize: 11, color: "var(--mut)", fontWeight: 700 }}>カードをタップで結果・名簿へ</span></div>
      <EventDivGrid onOpenEvent={onOpenEvent} />

      <div className="subhead"><IconMedal className="ic" size={16} />メダル獲得ランキング</div>
      <div className="mtable">
        {table.map((t, i) => (
          <div className={"mtrow" + (i === 0 ? " top" : "")} key={t.team} onClick={() => onOpenTeam(t.team)} style={{ cursor: "pointer" }}>
            <span className="rk">{i + 1}</span>
            <span className="tm">{t.team}</span>
            <span className="medals">
              <span className={"mc g" + (t.g ? "" : " zero")}><i />{t.g}</span>
              <span className={"mc s" + (t.s ? "" : " zero")}><i />{t.s}</span>
              <span className={"mc b" + (t.b ? "" : " zero")}><i />{t.b}</span>
            </span>
          </div>
        ))}
      </div>

      <div className="subhead"><IconTrophy className="ic" size={16} />決勝の結果（全種目）</div>
      {/* 全A決勝を 一般→浜寺杯→マスターズ の順で。区分が変わる所に小見出しを挟む。 */}
      {RW.EVENTS.filter((ev) => ev.finalA && RW.realDecided(ev.key))
        .slice().sort((a, b) => RW.DIV[a.div].order - RW.DIV[b.div].order)
        .map((ev, i, arr) => (
          <React.Fragment key={ev.key}>
            {(i === 0 || arr[i - 1].div !== ev.div) && ev.div !== "general" && (
              <div className="fin-divhd">{RW.DIV[ev.div].label}</div>
            )}
            <div className="medalcard">
              <div className="mh"><Code ev={ev} /><span className="nm2">{ev.name}</span></div>
              <PodiumRows podium={RW.PODIUMS[ev.key]} onOpenTeam={onOpenTeam} />
            </div>
          </React.Fragment>
        ))}
      <div style={{ textAlign: "center", marginTop: 12 }}><button className="chip" onClick={() => goTab("results")}>結果タブで詳しく見る ›</button></div>
    </section>
  );
}

Object.assign(window, { HomeView, etaLabel, PodiumRows, YouTubeLive, WeatherCard });
