/* 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 ( {p[name]} ); } 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 });