/* parts1.jsx — иконки, флаг, карточка матча, хедер, баннеры «сегодня/завтра» */
const { useState } = React;
const W = window.WC;
const U = window.U;
/* ── Иконки (простые контуры) ── */
function Icon({ name, size = 16, stroke = 2 }) {
const p = {
star: ,
pin: <>>,
phone: ,
check: ,
cal: <>>,
arrow: ,
close: ,
telegram: ,
};
const fill = name === 'star' ? 'currentColor' : 'none';
return (
);
}
function Flag({ code }) {
return ;
}
/* ── Карточка матча ── */
function MatchCard({ m, favs, toggleFav, dark }) {
const isFav = favs.has(m.home) || favs.has(m.away);
const tag = m.stage === 'group' ? 'Группа ' + m.group : m.name;
function TeamRow({ code }) {
const fav = favs.has(code);
return (
{U.teamName(code)}
{fav && }
);
}
return (
{m.time}{tag}
);
}
/* ── Хедер ── */
function Hero({ cur, onOpenBars, onOpenBridges }) {
const start = W.tournamentStart;
const d = U.daysUntil(start, cur);
const started = d <= 0;
let countLabel, countNum;
if (started) {
const left = U.daysUntil(W.finalDate, cur);
countLabel = left > 0 ? 'дней до финала' : 'турнир завершён';
countNum = left > 0 ? left : '—';
} else {
countLabel = 'дней до старта';
countNum = d;
}
return (
КОМИТЕТ
{started ? '● в эфире' : 'Канада · Мексика · США'}
ЧЕМПИОНАТ МИРА 2026
Транслируем все значимые матчи на больших экранахв гастрономических пабах Санкт-Петербурга и Москвы.
{countNum}{countLabel}
48сборных
7площадок
);
}
/* ── Баннер «Смотри у нас сегодня» ── */
function TodayBanner({ cur, favs, toggleFav }) {
let matches = U.matchesOn(cur);
let dateForLabel = cur;
let preTournament = false;
if (matches.length === 0) {
const nd = U.nextDayWithMatches(cur);
if (nd) { matches = U.matchesOn(nd); dateForLabel = nd; preTournament = true; }
}
matches = [...matches].sort((a, b) => a.time.localeCompare(b.time));
const d = U.daysUntil(W.tournamentStart, cur);
return (
Смотри у нас сегодня
{preTournament ? (
<>
Совсем скоро — старт!
{d > 0 ? 'До первого матча ' + d + ' дн. · ' : ''}Открытие {U.fmtLong(dateForLabel)}
>
) : (
<>
{matches.length} {matchWord(matches.length)} сегодня
{U.fmtLong(cur)}, {U.wdShort(cur)}
>
)}
{matches.map((m) => )}
);
}
/* ── Баннер «Смотри у нас завтра» ── */
function TomorrowBanner({ cur, favs, toggleFav }) {
const tomorrow = U.addDays(cur, 1);
let matches = U.matchesOn(tomorrow);
let label = U.fmtLong(tomorrow) + ', ' + U.wdShort(tomorrow);
let empty = false;
if (matches.length === 0) {
const nd = U.nextDayWithMatches(tomorrow);
if (nd) { matches = U.matchesOn(nd); label = 'Ближайшие — ' + U.fmtLong(nd); }
else empty = true;
}
matches = [...matches].sort((a, b) => a.time.localeCompare(b.time)).slice(0, 4);
return (
Смотри у нас завтра
{empty ? 'Матчей нет' : label}
{!empty &&
Бронируйте стол заранее — мест меньше, чем болельщиков
}
{empty ? (
Турнир завершён. Спасибо, что были с нами!
) : (
{matches.map((m) => )}
)}
);
}
function matchWord(n) {
const a = n % 10, b = n % 100;
if (a === 1 && b !== 11) return 'матч';
if (a >= 2 && a <= 4 && (b < 10 || b >= 20)) return 'матча';
return 'матчей';
}
Object.assign(window, { Icon, Flag, MatchCard, Hero, TodayBanner, TomorrowBanner, matchWord });