// StarCourt landing - main app // Composes hero, problem/solution, USP, features, roles, geography, // how-it-works, screenshots, FAQ, CTA, footer + Tweaks panel. const { useState, useEffect, useRef } = React; const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "palette": "ocean", "heroVariant": "split", "featureDensity": "regular", "showTweaks": true }/*EDITMODE-END*/; // Palette presets const PALETTES = { ocean: { ocean:'#0B4FE0', oceanDeep:'#072E8C', sand:'#F2E6CC', sandSoft:'#FBF3DE', sandLine:'#E6D5B0', depth:'#0B1729' }, midnight: { ocean:'#1166FF', oceanDeep:'#0B47B5', sand:'#EAD9B0', sandSoft:'#F6EAC9', sandLine:'#D9C28E', depth:'#08111F' }, warm: { ocean:'#1453C6', oceanDeep:'#0B3690', sand:'#F5D9A8', sandSoft:'#FCEDCC', sandLine:'#E6C682', depth:'#1A1208' } }; function applyPalette(name){ const p = PALETTES[name] || PALETTES.ocean; const root = document.documentElement; root.style.setProperty('--ocean', p.ocean); root.style.setProperty('--ocean-deep', p.oceanDeep); root.style.setProperty('--sand', p.sand); root.style.setProperty('--sand-soft', p.sandSoft); root.style.setProperty('--sand-line', p.sandLine); root.style.setProperty('--depth', p.depth); } // ───── Nav ───── function Nav(){ const [scrolled, setScrolled] = useState(false); useEffect(() => { const on = () => setScrolled(window.scrollY > 8); on(); window.addEventListener('scroll', on, { passive: true }); return () => window.removeEventListener('scroll', on); }, []); return ( ); } // ───── Hero ───── function Hero(){ return (
Падел · пляжный теннис · сообщество

Открой свою игру,
найди своих людей.

StarCourt - приложение для сообщества падел-тенниса и пляжного тенниса. Игры, турниры, тренеры, рейтинг - в одном месте.

Открыть веб-приложение Узнать больше
Работает в браузере Можно установить на телефон App Store и Google Play скоро
1 место свободно сегодня
ELO 1000 · старт
Beach Club · 16:00
); } // ───── Problem / Solution ───── function ProblemSolution(){ const pains = [ 'Десятки чатов в Telegram и WhatsApp по каждому клубу', 'Непонятно где играть, с кем играть, какой у тебя реальный уровень', 'Турниры собираются хаотично, результаты теряются', 'Тренировки и оплата - голосом, переводами, скриншотами', ]; const gains = [ 'Единая лента игр в твоём городе', 'Объективный рейтинг ELO, который не врёт', 'Турниры с сеткой и live-scoring - не таблица в Google Sheets', 'Карта всех площадок города с фильтрами', ]; return (
Зачем

Падел растёт быстрее,
чем инструменты для него.

Игроки собираются в чатах, организаторы считают результаты в таблицах, а площадки - в записных книжках. StarCourt собирает всё в одном месте.

Сейчас

Боль
    {pains.map((p, i) => (
  • 0{i+1} {p}
  • ))}

В StarCourt

Решение
    {gains.map((p, i) => (
  • 0{i+1} {p}
  • ))}
); } // ───── USP ───── function USP(){ return (
Отличие

Это не приложение одного клуба.
Любая площадка.{' '} Любой организатор.{' '} Любой город.

StarCourt - открытая платформа сообщества, а не корпоративный софт одной сети кортов.

01
Площадка

Любой клуб или корт добавляет себя на карту и собирает заявки.

02
Организатор

Турнир, лига, кубок выходного дня - сетка и регистрация в пару кликов.

03
Город

Игроки, тренеры и события из всех клубов в одной ленте.

); } // ───── Features ───── const FEATURES = [ { I: IconBall, title:'События и игры', sub:'Создавай и находи игры - уличные бесплатные или с арендой корта.', tag:'core', cls:'feat-1', variant:'ocean' }, { I: IconBracket, title:'Турниры', sub:'Лиговая система, сетка, live-scoring, история матчей.', tag:'pro', cls:'feat-2' }, { I: IconChart, title:'Рейтинг ELO', sub:'Раздельно для падела и пляжного. Честно и прозрачно.', tag:'fair', cls:'feat-3', variant:'dark' }, { I: IconMap, title:'Карта площадок', sub:'Фильтры по покрытию, крытым кортам, городу.', tag:'find', cls:'feat-4' }, { I: IconCoach, title:'Тренеры', sub:'Профили, расписание, запись и оплата онлайн.', tag:'book', cls:'feat-5' }, { I: IconChat, title:'Чаты', sub:'Личные, групповые, чат события - без отдельных мессенджеров.', tag:'talk', cls:'feat-6' }, { I: IconBell, title:'Уведомления', sub:'Push, email, in-app. Не пропустишь свою игру.', tag:'ping', cls:'feat-7' }, ]; function Features({ density }){ return (
Возможности

Всё, что нужно сообществу,
а не одному клубу.

Семь модулей, которые закрывают типичный путь игрока: от поиска партнёра на сегодня до участия в лиге.

{FEATURES.map((f, i) => (

{f.title}

{f.sub}

/ {f.tag}
))}
); } // ───── Roles ───── const ROLES = [ { id:'player', label:'Игрокам', Icon: IconUser, title:'Найти партнёров и события за 30 секунд', body:'Открыл приложение - увидел игры на сегодня в твоём городе. Выбрал уровень и время, записался одной кнопкой.', perks:[ 'Лента событий на сегодня и неделю', 'Объективный ELO, прозрачная история матчей', 'Чат события и напоминания о записи', 'Карта всех кортов города с фильтрами' ], illu:'PLAYER' }, { id:'coach', label:'Тренерам', Icon: IconWhistle, title:'Расписание и онлайн-запись - без чатов', body:'Один профиль, одно расписание. Ученики записываются сами, оплата проходит онлайн, отмены и переносы - в один клик.', perks:[ 'Публичный профиль с расписанием', 'Запись и оплата через приложение', 'История тренировок и учеников', 'Уведомления о записях и отменах' ], illu:'COACH' }, { id:'org', label:'Организаторам', Icon: IconBracket, title:'Турниры в одном инструменте', body:'Сетка, регистрация, оплата, результаты и live-scoring - без таблиц и переписок. История турниров остаётся у сообщества.', perks:[ 'Создание турнира за 5 минут', 'Регистрация и оплата онлайн', 'Live-scoring и автоматическая сетка', 'История и публикация результатов' ], illu:'ORG' }, { id:'club', label:'Площадкам', Icon: IconClub, title:'Корты на карте города', body:'Добавь свои корты, получай заявки на бронь и организуй события прямо в приложении. Без отдельного сайта и менеджера в Telegram.', perks:[ 'Корты на общей карте сообщества', 'Бронь и оплата через приложение', 'Своя страница площадки', 'События и абонементы' ], illu:'CLUB' } ]; function Roles(){ const [active, setActive] = useState(ROLES[0].id); const r = ROLES.find(r => r.id === active); return (
Для кого

Четыре роли,
одно приложение.

Каждая роль получает свой инструмент. Профили могут совмещаться: игрок может быть и организатором, тренер - игроком.

{ROLES.map((r, i) => ( ))}

{r.title}

{r.body}

    {r.perks.map((p, i) => (
  • {p}
  • ))}
); } function RoleIllu({ kind }){ // Brand-friendly geometric illustrations if (kind === 'PLAYER') { return ( {/* Avatar: head + shoulders */} ИГРОК ); } if (kind === 'COACH') { return ( ТРЕНЕР ); } if (kind === 'ORG') { return ( {/* Bracket */} ТУРНИР ); } // CLUB return ( ПЛОЩАДКА ); } // ───── Pricing ───── function Pricing(){ return (
Стоимость

Прозрачные цены, без скрытых платежей

Подписка для активных игроков и взносы за участие в турнирах. Бронирование корта остаётся за клубами-партнёрами.

Подписка

Premium для игроков

  • 1 месяц 299 ₽
  • 3 месяца 799 ₽ −11%
  • 12 месяцев 2 490 ₽ −30%

Безлимит на создание турниров, полная история игр и динамика ELO, расширенные фильтры поиска, приоритетная поддержка, без рекламы.

Разово

Взнос за турнир

  • Любительский 500 ₽
  • Стандартный 1 000 ₽
  • Рейтинговый 1 500 ₽

Включает: организационный сбор, аренду корта, инвентарь, призовой фонд (для рейтинговых турниров).

Все тарифы и реквизиты

Оплата через ЮKassa · чеки по 54-ФЗ · условия в оферте

); } // ───── Geography ───── function Geography(){ return (
География

Стартуем с Поволжья.
Растём по стране.

Запускаемся там, где уже есть живое сообщество. По мере подключения новых клубов и площадок открываем приложение в новых городах.

  • Саратов ~500 активных игроков Старт
  • Энгельс пляжный теннис, лето Старт
  • Самара подключение клубов Q3
  • Другие города по заявкам сообщества Скоро
{/* Volga river */} {/* Samara (soon) */} Самара {/* Saratov - active */} Саратов {/* Engels - active */} Энгельс {/* Future ? */} ?
Активные На подходе река Волга
); } // ───── How it works ───── function How(){ return (
3 шага

От установки -
до первой игры.

Регистрация по email с одноразовым кодом - без паролей. Можно играть, даже если ты ещё не выбрал клуб.

/ 01

Зарегистрируйся
по email

Код приходит за 30 секунд. Пароль не нужен - только почта.

3 4 7 9
/ 02

Заполни
профиль

Город, виды спорта, уровень. Это занимает меньше минуты.

/ 03

Играй
уже сегодня

Записывайся на события из ленты или создавай свои - открытые или для друзей.

{/* Calendar with checkmark */}
); } // ───── Screenshots ───── function Shots(){ return (
Экраны

Так выглядит приложение.

Пять ключевых экранов: лента, событие, карта, рейтинг и турнирная сетка. Простая навигация, никакой перегрузки.

{[ { label:'01 · Лента', b:'Home', Screen: ScreenHome }, { label:'02 · Событие', b:'Event', Screen: ScreenEvent }, { label:'03 · Карта', b:'Map', Screen: ScreenMap }, { label:'04 · Рейтинг', b:'Rating', Screen: ScreenRating }, ].map((s, i) => (
{s.label} {s.b}
))}
); } // ───── FAQ ───── const FAQ_ITEMS = [ { q: 'Сколько стоит приложение?', a: 'Бесплатно для игроков. Платными остаются только аренда кортов и тренировки - по тарифам площадки или тренера. С каждой брони/тренировки сервис берёт небольшую комиссию.' }, { q: 'Это только для профи?', a: 'Нет. Ядро аудитории - любители категорий B и C, 30–45 лет. Рейтинг строится так, чтобы новичок встретил соперника своего уровня, а не вылетел против профессионала с 1700+ ELO.' }, { q: 'Падел и пляжный теннис - это одно приложение?', a: 'Да, одно приложение. Рейтинг ELO считается раздельно: можно быть B-игроком в падел и C-новичком на пляже одновременно.' }, { q: 'Мой клуб может присоединиться?', a: 'Да. Любая площадка может добавить свои корты, создавать события и подключать тренеров. Это открытая платформа - не сеть, не франшиза.' }, { q: 'Когда релиз?', a: 'Backend в активной разработке, mobile-приложение подключается к API. Точную дату объявим ближе к выходу - оставь email ниже, чтобы узнать первым.' }, { q: 'А если у меня нет партнёра для игры?', a: 'Большинство событий в ленте - открытые. Записываешься один, остальных собирает приложение. Уровень подбирается по ELO, чтобы игра была интересной.' }, ]; function FAQ(){ const [open, setOpen] = useState(0); return (
FAQ

Частые
вопросы.

Не нашёл ответ? Напиши нам - мы добавим его сюда.

Написать команде
{FAQ_ITEMS.map((it, i) => { const isOpen = open === i; return (
{it.a}
); })}
); } // ───── CTA / Email capture ───── function CTA(){ const [email, setEmail] = useState(''); const [sent, setSent] = useState(false); const [err, setErr] = useState(''); const submit = (e) => { e.preventDefault(); if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) { setErr('Проверь адрес почты'); return; } setErr(''); setSent(true); }; return (
Релиз скоро

Узнай о запуске первым.

Оставь email - расскажем, когда приложение появится в сторах, и пришлём ранний доступ для сообщества Саратова и Энгельса.

{!sent ? ( <>
{ setEmail(e.target.value); setErr(''); }} />
{err ? err : 'Без спама. Письмо одно - когда релиз.'}
) : (
Готово. Напишем на {email}, когда стартуем.
)}
{/* Envelope with notification dot */} STARCOURT
); } // ───── Footer ───── function Footer(){ return ( ); } // ───── Orphan fix: glue Russian prepositions/conjunctions to next word ───── function useOrphanFix(){ useEffect(() => { const re = /(^|[\s («„—-])(во?|со?|у|на|по|для|ко?|о|об|обо|до|от|за|из|без|при|про|над|под|перед|через|и|а|но|или|что|как|если|не|ни|же|бы|ли)\s+(?=[А-Яа-яЁёA-Za-z0-9])/giu; const sels = 'h1, h2, h3, h4, .lead, .hero-sub, .usp-quote, .usp-tag, .feat p, .roles-perks li, .how-step p, .faq-a-in, .ps-list li, .hero-meta span, .cta-card p'; document.querySelectorAll(sels).forEach(el => { const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT); const nodes = []; let n; while ((n = walker.nextNode())) nodes.push(n); nodes.forEach(node => { const v = node.nodeValue; const fixed = v.replace(re, (_m, pre, w) => pre + w + '\u00A0'); if (fixed !== v) node.nodeValue = fixed; }); }); }); } // ───── Reveal observer ───── function useReveal(){ useEffect(() => { const els = document.querySelectorAll('.reveal'); const io = new IntersectionObserver((entries) => { entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); } }); }, { rootMargin: '0px 0px -80px 0px', threshold: 0.05 }); els.forEach(el => io.observe(el)); return () => io.disconnect(); }, []); } // ───── App ───── function App(){ const [t, setTweak] = useTweaks(TWEAK_DEFAULTS); useEffect(() => { applyPalette(t.palette); }, [t.palette]); useReveal(); useOrphanFix(); return ( <>