galikon/index.html

845 lines
52 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<title>Галикон — спорт будущего</title>
<link rel="manifest" href="manifest.json">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="Галикон">
<meta name="theme-color" content="#0F1218">
<style>
:root{--ink:#0F1218;--cyan:#00E5FF;--cyan-50:#E8FCFF;--white:#fff;--gray-500:#5B6573;--gray-100:#F2F4F7;--red:#FF6B6B;--green:#4CAF50;--gold:#FFD700}
*{box-sizing:border-box;margin:0;padding:0}
body{font:15px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;color:var(--ink);background:var(--ink);overflow-x:hidden}
input,select,textarea{width:100%;padding:14px 16px;border:2px solid #2a3342;border-radius:12px;font:inherit;font-size:16px;background:#1a2332;color:var(--white);transition:border .2s}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--cyan)}
input::placeholder{color:#5b6573}
.btn{display:inline-block;background:var(--cyan);color:var(--ink);padding:14px 28px;border-radius:14px;font-weight:700;border:none;font-size:16px;cursor:pointer;transition:all .2s;text-align:center;width:100%}
.btn:active{transform:scale(.97)}
.btn.outline{background:transparent;border:2px solid var(--cyan);color:var(--cyan)}
.btn.small{padding:8px 16px;font-size:13px;width:auto}
.btn.danger{background:var(--red);color:#fff}
.toast{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:var(--cyan);color:var(--ink);padding:12px 24px;border-radius:30px;font-weight:700;font-size:14px;z-index:999;opacity:0;transition:opacity .3s;pointer-events:none}
.toast.show{opacity:1}
/* SCREENS */
.screen{position:fixed;top:0;left:0;right:0;bottom:0;display:none;flex-direction:column;background:var(--ink);color:var(--white);overflow-y:auto;z-index:10}
.screen.active{display:flex}
/* LOGIN */
.login-box{max-width:400px;margin:auto;padding:24px;width:100%}
.login-box h1{text-align:center;font-size:32px;margin-bottom:8px}
.login-box h1 span{color:var(--cyan)}
.login-box .sub{text-align:center;color:var(--gray-500);margin-bottom:24px;font-size:15px}
.login-box .error{color:var(--red);font-size:13px;margin-bottom:8px;display:none}
/* REGISTRATION STEPS */
.reg-step{display:none;flex-direction:column;gap:8px;max-width:400px;margin:auto;padding:24px;width:100%}
.reg-step.active{display:flex}
.reg-step h2{text-align:center;font-size:22px;margin-bottom:4px}
.reg-step .hint{text-align:center;color:var(--gray-500);font-size:13px;margin-bottom:16px}
.step-indicator{display:flex;justify-content:center;gap:6px;margin-bottom:16px}
.step-dot{width:8px;height:8px;border-radius:50%;background:#2a3342;transition:all .3s}
.step-dot.done{background:var(--cyan)}
.step-dot.current{background:var(--cyan);box-shadow:0 0 8px var(--cyan)}
.reg-nav{display:flex;gap:8px;margin-top:8px}
.reg-nav button{flex:1}
/* AVATAR PICKER */
.avatar-picker{display:flex;gap:8px;flex-wrap:wrap;justify-content:center;margin:12px 0}
.avatar-opt{width:56px;height:56px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:28px;cursor:pointer;border:3px solid transparent;transition:all .2s;background:#1a2332}
.avatar-opt.selected{border-color:var(--cyan);transform:scale(1.1)}
.avatar-opt img{width:100%;height:100%;border-radius:50%;object-fit:cover}
/* BOTTOM NAV */
.bottom-nav{display:flex;justify-content:space-around;padding:8px;background:#151c28;border-top:1px solid #2a3342;position:sticky;bottom:0;z-index:50}
.nav-item{display:flex;flex-direction:column;align-items:center;gap:2px;padding:6px 12px;border-radius:12px;cursor:pointer;color:var(--gray-500);font-size:11px;transition:all .2s;border:none;background:none}
.nav-item.active{color:var(--cyan)}
.nav-item .icon{font-size:22px}
/* CONTENT */
.content{flex:1;overflow-y:auto;padding:16px;max-width:700px;margin:0 auto;width:100%}
.card{background:#151c28;border-radius:16px;padding:20px;margin-bottom:12px;border:1px solid #2a3342}
.card h3{font-size:17px;font-weight:700;margin-bottom:10px}
.card .muted{color:var(--gray-500);font-size:13px}
.profile-header{text-align:center;padding:24px 16px}
.profile-header .avatar{width:80px;height:80px;border-radius:50%;background:var(--cyan);color:var(--ink);display:flex;align-items:center;justify-content:center;font-size:36px;font-weight:800;margin:0 auto 12px;overflow:hidden}
.profile-header .avatar img{width:100%;height:100%;object-fit:cover}
.profile-header h2{font-size:22px}
.profile-header .tag{display:inline-block;background:var(--cyan);color:var(--ink);padding:3px 12px;border-radius:12px;font-size:12px;font-weight:700;margin-top:4px}
.achievement-row{display:flex;gap:8px;flex-wrap:wrap}
.achievement-badge{background:#1a2332;border-radius:12px;padding:10px 14px;font-size:13px;text-align:center}
.achievement-badge .val{font-size:20px;font-weight:800;color:var(--cyan)}
table{width:100%;border-collapse:collapse;font-size:13px}
th,td{padding:8px;text-align:left;border-bottom:1px solid #2a3342}
th{color:var(--gray-500);font-size:11px;text-transform:uppercase}
.badge{display:inline-block;padding:2px 10px;border-radius:10px;font-size:11px;font-weight:600}
.badge.gold{background:#FFF3CD;color:#856404}
.badge.blue{background:var(--cyan-50);color:var(--ink)}
.badge.green{background:#D4EDDA;color:#155724}
/* CHAT */
.chat-list-item{display:flex;align-items:center;gap:12px;padding:12px;background:#1a2332;border-radius:12px;margin-bottom:6px;cursor:pointer;transition:background .2s}
.chat-list-item:active{background:#2a3342}
.chat-list-item .av{width:40px;height:40px;border-radius:50%;background:var(--cyan);color:var(--ink);display:flex;align-items:center;justify-content:center;font-weight:800;font-size:18px;flex-shrink:0}
.chat-list-item .info{flex:1;min-width:0}
.chat-list-item .name{font-weight:600;font-size:14px}
.chat-list-item .last{font-size:12px;color:var(--gray-500);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.chat-list-item .time{font-size:11px;color:var(--gray-500)}
.chat-list-item .unread{background:var(--cyan);color:var(--ink);border-radius:10px;padding:1px 7px;font-size:11px;font-weight:700}
.chat-messages{display:flex;flex-direction:column;gap:8px;padding:8px 0;flex:1;overflow-y:auto}
.chat-msg{max-width:80%;padding:10px 14px;border-radius:16px;font-size:14px;line-height:1.4;word-break:break-word}
.chat-msg.mine{background:var(--cyan);color:var(--ink);align-self:flex-end;border-bottom-right-radius:4px}
.chat-msg.theirs{background:#1a2332;color:var(--white);align-self:flex-start;border-bottom-left-radius:4px}
.chat-msg .sender{font-size:11px;color:var(--gray-500);margin-bottom:2px}
.chat-input-row{display:flex;gap:8px;padding:8px 0}
.chat-input-row input{flex:1;margin-bottom:0}
.chat-tabs{display:flex;gap:4px;margin-bottom:12px}
.chat-tab{flex:1;padding:8px;border-radius:10px;border:none;font-size:13px;font-weight:600;cursor:pointer;background:#1a2332;color:var(--gray-500);transition:all .2s}
.chat-tab.active{background:var(--cyan);color:var(--ink)}
.game-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:4px;width:180px;margin:12px auto}
.game-cell{aspect-ratio:1;background:#1a2332;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:32px;font-weight:800;cursor:pointer;transition:background .2s;border:2px solid #2a3342}
.game-cell:active{background:#2a3342}
.game-cell.x{color:var(--cyan)}
.game-cell.o{color:var(--red)}
.game-cell.disabled{pointer-events:none}
.game-status{text-align:center;font-size:14px;font-weight:600;margin:8px 0}
.quiz-option{display:block;width:100%;padding:12px;margin:6px 0;background:#1a2332;border:2px solid #2a3342;border-radius:10px;color:var(--white);font-size:14px;cursor:pointer;text-align:left;transition:all .2s}
.quiz-option:active{background:#2a3342}
.quiz-option.right{background:var(--green);border-color:var(--green);color:#fff}
.quiz-option.wrong{background:var(--red);border-color:var(--red);color:#fff}
.empty-state{text-align:center;padding:40px 20px;color:var(--gray-500)}
.empty-state .big{font-size:48px;margin-bottom:8px}
.progress-bar{height:6px;background:#1a2332;border-radius:3px;overflow:hidden;margin-top:6px}
.progress-bar .fill{height:100%;background:var(--cyan);border-radius:3px}
.lightbox{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.95);z-index:200;display:flex;align-items:center;justify-content:center;cursor:pointer}
.lightbox img,.lightbox video{max-width:95%;max-height:90vh;border-radius:8px}
label.file-btn{display:inline-block;background:#1a2332;padding:12px 20px;border-radius:12px;font-weight:600;cursor:pointer;font-size:14px;margin:8px 0}
label.file-btn:active{background:#2a3342}
input[type=file]{display:none}
</style>
</head>
<body>
<!-- LOGIN SCREEN -->
<div class="screen active" id="loginScreen">
<div class="login-box">
<h1>&#x1F3CA; <span>Галикон</span></h1>
<p class="sub">Спорт будущего</p>
<input type="text" id="lUser" placeholder="Логин">
<input type="password" id="lPass" placeholder="Пароль">
<div class="error" id="loginErr"></div>
<button class="btn" onclick="doLogin()">&#x1F512; Войти</button>
<div style="text-align:center;margin-top:12px">
<button class="btn outline" onclick="startReg()" style="width:auto;padding:12px 40px">&#x270F; Регистрация</button>
</div>
<p style="text-align:center;color:var(--gray-500);font-size:12px;margin-top:16px">Нет аккаунта? Создай за 1 минуту</p>
</div>
</div>
<!-- REGISTRATION SCREEN -->
<div class="screen" id="regScreen">
<div style="padding:16px;display:flex;align-items:center;justify-content:space-between">
<button class="btn small outline" onclick="backToLogin()">&#x2190; Назад</button>
<span style="font-weight:700;color:var(--cyan)">Шаг <span id="stepNum">1</span>/7</span>
</div>
<div class="step-indicator" id="stepDots"></div>
<!-- Step 1: Name -->
<div class="reg-step active" data-step="1">
<h2>&#x270F; Как тебя зовут?</h2>
<p class="hint">Введи свои Фамилию, Имя и Отчество</p>
<input type="text" id="rName" placeholder="Например: Кайрат Гали Аскарович" autofocus>
<div class="reg-nav"><button class="btn" onclick="nextStep()">Дальше &#x2192;</button></div>
</div>
<!-- Step 2: Login -->
<div class="reg-step" data-step="2">
<h2>&#x1F511; Придумай логин и пароль</h2>
<p class="hint">Логин — твоё имя в приложении. Пароль — секрет.</p>
<input type="text" id="rLogin" placeholder="Логин (латиница, без пробелов)">
<input type="password" id="rPass" placeholder="Пароль (минимум 3 символа)">
<div class="reg-nav">
<button class="btn outline" onclick="prevStep()">&#x2190; Назад</button>
<button class="btn" onclick="nextStep()">Дальше &#x2192;</button>
</div>
</div>
<!-- Step 3: Sport -->
<div class="reg-step" data-step="3">
<h2>&#x1F3CA; Твой вид спорта</h2>
<p class="hint">Выбери из списка олимпийских видов</p>
<select id="rSport"><option value="">Выбери вид спорта</option><option>Академическая гребля</option><option>Бадминтон</option><option>Баскетбол</option><option>Бокс</option><option>Борьба вольная</option><option>Борьба греко-римская</option><option>Велоспорт</option><option>Водное поло</option><option>Волейбол</option><option>Гандбол</option><option>Гимнастика спортивная</option><option>Гимнастика художественная</option><option>Гольф</option><option>Гребля на байдарках и каноэ</option><option>Дзюдо</option><option>Карате</option><option>Конный спорт</option><option>Лёгкая атлетика</option><option>Настольный теннис</option><option>Парусный спорт</option><option>Плавание</option><option>Прыжки в воду</option><option>Прыжки на батуте</option><option>Регби</option><option>Сёрфинг</option><option>Синхронное плавание</option><option>Скейтбординг</option><option>Современное пятиборье</option><option>Спортивное скалолазание</option><option>Стрельба из лука</option><option>Стрельба пулевая</option><option>Теннис</option><option>Триатлон</option><option>Тхэквондо</option><option>Тяжёлая атлетика</option><option>Фехтование</option><option>Футбол</option><option>Хоккей на траве</option></select>
<div class="reg-nav">
<button class="btn outline" onclick="prevStep()">&#x2190; Назад</button>
<button class="btn" onclick="nextStep()">Дальше &#x2192;</button>
</div>
</div>
<!-- Step 4: Birth & Avatar -->
<div class="reg-step" data-step="4">
<h2>&#x1F382; Дата рождения</h2>
<p class="hint">Возраст посчитается автоматически</p>
<input type="date" id="rBirth" onchange="autoAge()">
<input type="text" id="rAge" placeholder="Возраст (авто)" readonly style="background:#0F1218;border-color:#1a2332">
<p style="text-align:center;color:var(--gray-500);font-size:13px;margin-top:8px">&#x1F4F7; Выбери аватарку:</p>
<div class="avatar-picker" id="avatarPicker">
<div class="avatar-opt selected" data-emoji="&#x1F3CA;">&#x1F3CA;</div>
<div class="avatar-opt" data-emoji="&#x1F3C3;">&#x1F3C3;</div>
<div class="avatar-opt" data-emoji="&#x1F3CB;">&#x1F3CB;</div>
<div class="avatar-opt" data-emoji="&#x26BD;">&#x26BD;</div>
<div class="avatar-opt" data-emoji="&#x1F3C0;">&#x1F3C0;</div>
<div class="avatar-opt" data-emoji="&#x1F94A;">&#x1F94A;</div>
<div class="avatar-opt" data-emoji="&#x1F3C8;">&#x1F3C8;</div>
<div class="avatar-opt" data-emoji="&#x1F3BE;">&#x1F3BE;</div>
</div>
<label class="file-btn" style="text-align:center;width:100%">&#x1F4F7; Или загрузи своё фото<input type="file" accept="image/*" id="rPhotoFile" onchange="previewRegPhoto()"></label>
<img id="rPhotoPreview" style="width:64px;height:64px;border-radius:50%;object-fit:cover;display:none;margin:0 auto">
<div class="reg-nav">
<button class="btn outline" onclick="prevStep()">&#x2190; Назад</button>
<button class="btn" onclick="nextStep()">Дальше &#x2192;</button>
</div>
</div>
<!-- Step 5: Location -->
<div class="reg-step" data-step="5">
<h2>&#x1F30D; Где ты?</h2>
<p class="hint">Страна и город</p>
<select id="rCountry" onchange="updateCities()"><option value="">Страна</option><option>Австралия</option><option>Австрия</option><option>Азербайджан</option><option>Армения</option><option>Беларусь</option><option>Бельгия</option><option>Болгария</option><option>Бразилия</option><option>Великобритания</option><option>Венгрия</option><option>Германия</option><option>Греция</option><option>Грузия</option><option>Дания</option><option>Египет</option><option>Израиль</option><option>Индия</option><option>Индонезия</option><option>Испания</option><option>Италия</option><option>Казахстан</option><option>Канада</option><option>Катар</option><option>Китай</option><option>Корея Южная</option><option>Куба</option><option>Кыргызстан</option><option>Латвия</option><option>Литва</option><option>Малайзия</option><option>Мексика</option><option>Молдова</option><option>Монголия</option><option>Нидерланды</option><option>Новая Зеландия</option><option>Норвегия</option><option>ОАЭ</option><option>Польша</option><option>Португалия</option><option>Россия</option><option>Румыния</option><option>Саудовская Аравия</option><option>Сербия</option><option>Сингапур</option><option>США</option><option>Таджикистан</option><option>Таиланд</option><option>Туркменистан</option><option>Турция</option><option>Узбекистан</option><option>Украина</option><option>Финляндия</option><option>Франция</option><option>Хорватия</option><option>Чехия</option><option>Швейцария</option><option>Швеция</option><option>Эстония</option><option>ЮАР</option><option>Япония</option></select>
<input type="text" id="rCity" placeholder="Город" list="cityList" autocomplete="off">
<datalist id="cityList"></datalist>
<div class="reg-nav">
<button class="btn outline" onclick="prevStep()">&#x2190; Назад</button>
<button class="btn" onclick="nextStep()">Дальше &#x2192;</button>
</div>
</div>
<!-- Step 6: Club & Coach -->
<div class="reg-step" data-step="6">
<h2>&#x1F3EB; Клуб и тренер</h2>
<p class="hint">Где и с кем ты тренируешься</p>
<input type="text" id="rClub" placeholder="Клуб / спортивная школа">
<input type="text" id="rCoach" placeholder="Тренер (ФИО)">
<input type="text" id="rRank" placeholder="Разряд / звание (например: 1 юн, 3 взр)">
<input type="text" id="rGoal" placeholder="Твоя главная цель (например: 50 м в/с за 23″)">
<div class="reg-nav">
<button class="btn outline" onclick="prevStep()">&#x2190; Назад</button>
<button class="btn" onclick="nextStep()">Дальше &#x2192;</button>
</div>
</div>
<!-- Step 7: Contacts -->
<div class="reg-step" data-step="7">
<h2>&#x1F4F1; Контакты</h2>
<p class="hint">Телефон и email — чтобы тренер и родители могли связаться</p>
<input type="tel" id="rPhone" placeholder="Телефон (WhatsApp)">
<input type="email" id="rEmail" placeholder="Email">
<div class="reg-nav">
<button class="btn outline" onclick="prevStep()">&#x2190; Назад</button>
<button class="btn" onclick="finishReg()">&#x2705; Завершить!</button>
</div>
</div>
</div>
<!-- MAIN APP SCREEN -->
<div class="screen" id="appScreen">
<div class="content" id="mainContent"></div>
<div class="bottom-nav">
<button class="nav-item active" data-page="profile" onclick="showPage('profile')"><span class="icon">&#x1F464;</span>Профиль</button>
<button class="nav-item" data-page="diary" onclick="showPage('diary')"><span class="icon">&#x1F4D6;</span>Дневник</button>
<button class="nav-item" data-page="chat" onclick="showPage('chat')"><span class="icon">&#x1F4AC;</span>Чаты</button>
<button class="nav-item" data-page="tools" onclick="showPage('tools')"><span class="icon">&#x2699;</span>Инструменты</button>
</div>
</div>
<div class="toast" id="toast"></div>
<div class="lightbox" id="lightbox" style="display:none" onclick="this.style.display='none'"></div>
<script>
// === DATA ===
const LS=(k)=>{try{return JSON.parse(localStorage.getItem('g_'+k))}catch{return null}};
const SS=(k,v)=>{try{localStorage.setItem('g_'+k,JSON.stringify(v))}catch{toast('Память полна!')}};
let currentUser=null, currentPage='profile', currentChat=null, chatFilter='all';
function toast(m){const t=document.getElementById('toast');t.textContent=m;t.classList.add('show');setTimeout(()=>t.classList.remove('show'),2000)}
// === LOGIN ===
function doLogin(){
const u=document.getElementById('lUser').value.trim().toLowerCase();
const p=document.getElementById('lPass').value;
if(!u||!p){showErr('Введи логин и пароль');return}
const users=LS('users')||[];
const user=users.find(x=>x.login===u&&x.pass===p);
if(!user){showErr('Неверный логин или пароль');return}
currentUser=user;
document.getElementById('loginScreen').classList.remove('active');
document.getElementById('appScreen').classList.add('active');
hideErr();
renderAll();
}
function showErr(m){const e=document.getElementById('loginErr');e.textContent=m;e.style.display='block'}
function hideErr(){document.getElementById('loginErr').style.display='none'}
// === REGISTRATION ===
let regStep=1, regPhoto=null, regAvatar='&#x1F3CA;';
function startReg(){
document.getElementById('loginScreen').classList.remove('active');
document.getElementById('regScreen').classList.add('active');
regStep=1; showRegStep(1);
buildStepDots();
}
function backToLogin(){
document.getElementById('regScreen').classList.remove('active');
document.getElementById('loginScreen').classList.add('active');
}
function showRegStep(n){
regStep=n;
document.querySelectorAll('.reg-step').forEach(s=>s.classList.remove('active'));
const step=document.querySelector(`.reg-step[data-step="${n}"]`);
if(step){step.classList.add('active');step.querySelector('input,select')?.focus()}
document.getElementById('stepNum').textContent=n;
updateStepDots();
}
function buildStepDots(){
let h=''; for(let i=1;i<=7;i++)h+=`<div class="step-dot${i<=regStep?' done':''}${i===regStep?' current':''}"></div>`;
document.getElementById('stepDots').innerHTML=h;
}
function updateStepDots(){
document.querySelectorAll('#stepDots .step-dot').forEach((d,i)=>{
d.classList.toggle('done',i+1<=regStep);
d.classList.toggle('current',i+1===regStep);
});
}
function nextStep(){
if(regStep===1&&!document.getElementById('rName').value.trim()){toast('Введи ФИО!');return}
if(regStep===2){const l=document.getElementById('rLogin').value.trim();const p=document.getElementById('rPass').value;if(!l){toast('Придумай логин!');return}if(!p||p.length<3){toast('Пароль — минимум 3 символа!');return}const users=LS('users')||[];if(users.find(x=>x.login===l.toLowerCase())){toast('Такой логин уже занят!');return}}
if(regStep===3&&!document.getElementById('rSport').value){toast('Выбери вид спорта!');return}
if(regStep<7){buildStepDots();showRegStep(regStep+1)}else finishReg()
}
function prevStep(){if(regStep>1)showRegStep(regStep-1);buildStepDots()}
function autoAge(){const b=document.getElementById('rBirth').value;if(!b)return;const bd=new Date(b),td=new Date();let a=td.getFullYear()-bd.getFullYear();const m=td.getMonth()-bd.getMonth();if(m<0||(m===0&&td.getDate()<bd.getDate()))a--;document.getElementById('rAge').value=a}
function previewRegPhoto(){const f=document.getElementById('rPhotoFile').files[0];if(!f)return;const r=new FileReader();r.onload=e=>{regPhoto=e.target.result;const p=document.getElementById('rPhotoPreview');p.src=e.target.result;p.style.display='block'};r.readAsDataURL(f)}
document.querySelectorAll('.avatar-opt').forEach(a=>{a.addEventListener('click',()=>{document.querySelectorAll('.avatar-opt').forEach(x=>x.classList.remove('selected'));a.classList.add('selected');regAvatar=a.dataset.emoji;regPhoto=null;document.getElementById('rPhotoPreview').style.display='none'})});
function finishReg(){
const name=document.getElementById('rName').value.trim();
const login=document.getElementById('rLogin').value.trim().toLowerCase();
const pass=document.getElementById('rPass').value;
const sport=document.getElementById('rSport').value;
const birth=document.getElementById('rBirth').value;
autoAge();
const age=document.getElementById('rAge').value;
const country=document.getElementById('rCountry').value;
const city=document.getElementById('rCity').value.trim();
const club=document.getElementById('rClub').value.trim();
const coach=document.getElementById('rCoach').value.trim();
const rank=document.getElementById('rRank').value.trim();
const goal=document.getElementById('rGoal').value.trim();
const phone=document.getElementById('rPhone').value.trim();
const email=document.getElementById('rEmail').value.trim();
if(!name||!login||!pass||!sport){toast('Заполни обязательные поля!');return}
const user={
id:Date.now(),name,login,pass,sport,birth,age,country,city,club,coach,rank,goal,phone,email,
avatar:regPhoto||regAvatar,photo:regPhoto||null,
created:new Date().toISOString(),
achievements:[]
};
const users=LS('users')||[];
if(users.find(x=>x.login===login)){toast('Логин занят!');return}
users.push(user);
SS('users',users);
currentUser=user;
document.getElementById('regScreen').classList.remove('active');
document.getElementById('appScreen').classList.add('active');
// reset form
regStep=1;regPhoto=null;regAvatar='&#x1F3CA;';
['rName','rLogin','rPass','rSport','rBirth','rAge','rCountry','rCity','rClub','rCoach','rRank','rGoal','rPhone','rEmail'].forEach(id=>{const el=document.getElementById(id);if(el){if(el.tagName==='SELECT')el.selectedIndex=0;else el.value=''}});
document.getElementById('rPhotoPreview').style.display='none';
renderAll();
toast('&#x1F389; Профиль создан! Добро пожаловать в Галикон!');
}
// === USER STORAGE ===
function uid(){return currentUser?currentUser.id:'_'}
function getMy(k){const all=LS(k)||{};return all[uid()]||null}
function setMy(k,v){const all=LS(k)||{};all[uid()]=v;SS(k,all)}
function getMyArr(k){const all=LS(k)||{};return all[uid()]||[]}
function setMyArr(k,v){const all=LS(k)||{};all[uid()]=v;SS(k,all)}
// === PAGES ===
function showPage(page){
currentPage=page;
document.querySelectorAll('.nav-item').forEach(n=>n.classList.toggle('active',n.dataset.page===page));
renderPage();
}
function renderPage(){
const c=document.getElementById('mainContent');
switch(currentPage){
case 'profile':c.innerHTML=renderProfile();break;
case 'diary':c.innerHTML=renderDiaryPage();break;
case 'chat':c.innerHTML=renderChatPage();currentChat=null;break;
case 'tools':c.innerHTML=renderToolsPage();break;
}
}
// === PROFILE ===
function renderProfile(){
const u=currentUser;
const av=u.photo?(u.photo.startsWith('data:')?u.photo:u.avatar):u.avatar;
const h=`
<div class="profile-header">
<div class="avatar">${u.photo?`<img src="${u.photo}">`:av}</div>
<h2>${u.name}</h2>
<div class="tag">${u.sport}</div>
${u.rank?`<div style="margin-top:4px;font-size:13px;color:var(--gray-500)">${u.rank}</div>`:''}
${u.goal?`<div style="margin-top:4px;font-size:14px;color:var(--cyan)">&#x1F3AF; ${u.goal}</div>`:''}
</div>
<div class="card">
<h3>&#x1F4CB; Информация</h3>
<div style="font-size:14px;line-height:2">
${u.birth?`&#x1F382; ${u.birth} (${u.age||'?'} лет)<br>`:''}
${u.country?`&#x1F30D; ${u.country}${u.city?', '+u.city:''}<br>`:''}
${u.club?`&#x1F3EB; ${u.club}<br>`:''}
${u.coach?`&#x1F468;&#x200D;&#x1F3EB; Тренер: ${u.coach}<br>`:''}
${u.phone?`&#x1F4F1; ${u.phone}<br>`:''}
${u.email?`&#x2709;&#xFE0F; ${u.email}<br>`:''}
</div>
</div>
<div class="card">
<h3>&#x1F3C6; Достижения</h3>
${u.achievements&&u.achievements.length?u.achievements.map(a=>`<div style="padding:8px 0;border-bottom:1px solid #2a3342;font-size:14px"><strong>${a.title}</strong><br><span style="color:var(--gray-500);font-size:13px">${a.date} · ${a.desc||''}</span></div>`).join(''):'<p class="muted">Пока нет достижений. Добавь первое!</p>'}
<button class="btn small outline" onclick="addAchievement()" style="margin-top:8px">+ Добавить</button>
<div id="achForm" style="display:none;margin-top:8px">
<input type="text" id="achTitle" placeholder="Название (например: 1 место на Весёлый Дельфин)">
<input type="date" id="achDate">
<input type="text" id="achDesc" placeholder="Описание (результат, дистанция...)">
<button class="btn small" onclick="saveAchievement()">Сохранить</button>
</div>
</div>
<button class="btn danger" onclick="doLogout()" style="margin-top:8px">&#x1F6AA; Выйти</button>
`;return h;
}
function addAchievement(){document.getElementById('achForm').style.display='block'}
function saveAchievement(){
const t=document.getElementById('achTitle').value.trim();
const d=document.getElementById('achDate').value;
const desc=document.getElementById('achDesc').value.trim();
if(!t||!d){toast('Введи название и дату!');return}
const u=currentUser;
if(!u.achievements)u.achievements=[];
u.achievements.unshift({title:t,date:d,desc});
const users=LS('users')||[];
const idx=users.findIndex(x=>x.id===u.id);
if(idx>=0){users[idx]=u;SS('users',users);currentUser=u}
document.getElementById('achTitle').value='';document.getElementById('achDate').value='';document.getElementById('achDesc').value='';
document.getElementById('achForm').style.display='none';
renderPage();toast('Достижение добавлено!');
}
// === DIARY ===
function renderDiaryPage(){
const diary=getMyArr('diary');
const today=new Date().toISOString().slice(0,10);
let h=`
<div class="card">
<h3>&#x270F; Новая запись</h3>
<input type="date" id="dDate" value="${today}">
<select id="dType"><option>Скорость</option><option>Техника</option><option>Выносливость</option><option>ОФП</option><option>Соревнование</option></select>
<input type="number" id="dKm" placeholder="Километраж" step="0.1">
<input type="text" id="dTime" placeholder="Лучшее время">
<input type="number" id="dFeel" placeholder="Самочувствие (1-5)" min="1" max="5">
<input type="text" id="dNote" placeholder="Заметка">
<button class="btn" onclick="addDiary()">Сохранить</button>
</div>`;
if(!diary.length){h+='<div class="empty-state"><div class="big">&#x1F4AD;</div>Пока нет записей</div>'}
else{
h+=diary.slice(0,20).map(e=>`
<div class="card" style="padding:14px">
<div style="display:flex;justify-content:space-between"><strong>${e.date}</strong><span class="badge blue">${e.type}</span></div>
<div class="muted">&#x1F4CF; ${e.km} км | &#x23F1; ${e.time} | &#x1F31F; ${e.feel}/5${e.note!=='—'?'<br>'+e.note:''}</div>
<button class="btn danger small" style="margin-top:4px" onclick="delDiary(${e.id})">Удалить</button>
</div>`).join('');
}
return h;
}
function addDiary(){
const e={id:Date.now(),date:document.getElementById('dDate').value,type:document.getElementById('dType').value,km:document.getElementById('dKm').value||'0',time:document.getElementById('dTime').value||'—',feel:document.getElementById('dFeel').value||'—',note:document.getElementById('dNote').value||'—'};
const d=getMyArr('diary');d.unshift(e);setMyArr('diary',d);
renderPage();toast('Записано!');
}
function delDiary(id){const d=getMyArr('diary').filter(x=>x.id!==id);setMyArr('diary',d);renderPage();toast('Удалено')}
// === CHAT ===
function renderChatPage(){
if(currentChat)return renderChatView();
return renderChatList();
}
function renderChatList(){
const users=LS('users')||[];
const other=users.filter(u=>u.id!==uid());
const msgs=LS('messages')||{};
let h='<div class="card"><div class="chat-tabs"><button class="chat-tab active" onclick="chatFilter=\'all\';renderPage()">&#x1F4AC; Все</button><button class="chat-tab" onclick="chatFilter=\'athlete\';renderPage()">&#x1F3CA; Спортсмены</button><button class="chat-tab" onclick="chatFilter=\'coach\';renderPage()">&#x1F3CB; Тренеры</button><button class="chat-tab" onclick="chatFilter=\'parent\';renderPage()">&#x1F468;&#x200D;&#x1F466; Родители</button></div>';
// Games button
h+='<button class="btn outline" onclick="renderGames()" style="width:100%;margin-bottom:8px">&#x1F3AE; Игры</button>';
Show create group chat button
h+='<button class="btn small outline" onclick="createGroupChat()" style="width:100%;margin-bottom:8px">+ Создать групповой чат</button>';
if(!other.length){h+='<div class="empty-state"><div class="big">&#x1F4AC;</div>Нет других пользователей</div></div>';return h}
h+='<div style="font-size:13px;color:var(--gray-500);margin-bottom:6px">Выбери с кем общаться:</div>';
other.forEach(u=>{
const chatKey=[uid(),u.id].sort().join('_');
const chatMsgs=msgs[chatKey]||[];
const last=chatMsgs[chatMsgs.length-1];
const unread=chatMsgs.filter(m=>m.to===uid()&&!m.read).length;
h+=`<div class="chat-list-item" onclick="openChat(${u.id})">
<div class="av">${u.photo?`<img src="${u.photo}" style="width:100%;height:100%;border-radius:50%;object-fit:cover">`:u.avatar||u.name.charAt(0)}</div>
<div class="info"><div class="name">${u.name}${u.role?` <span class="badge blue" style="font-size:9px">${u.role}</span>`:''}</div><div class="last">${last?last.text:'Начни общение'}</div></div>
${unread?`<span class="unread">${unread}</span>`:''}
</div>`;
});
h+='</div>';return h;
}
function openChat(id){
currentChat=id;
// Mark messages as read
const msgs=LS('messages')||{};
const chatKey=[uid(),id].sort().join('_');
if(msgs[chatKey])msgs[chatKey].forEach(m=>{if(m.to===uid())m.read=true});
SS('messages',msgs);
renderPage();
}
function renderChatView(){
const other=((LS('users')||[]).find(u=>u.id===currentChat));
const chatKey=[uid(),currentChat].sort().join('_');
const msgs=(LS('messages')||{})[chatKey]||[];
let h=`<div style="display:flex;align-items:center;gap:8px;padding:8px 0">
<button class="btn small outline" onclick="currentChat=null;renderPage()">&#x2190;</button>
<strong>${other?other.name:'Чат'}</strong>
</div>
<div class="chat-messages" id="chatMsgs">`;
if(!msgs.length)h+='<div class="empty-state"><div class="big">&#x1F4AC;</div>Начните общение!</div>';
else msgs.forEach(m=>{h+=`<div class="chat-msg ${m.from===uid()?'mine':'theirs'}">${m.from!==uid()?`<div class="sender">${other?other.name:'Пользователь'}</div>`:''}${m.text}<div style="font-size:10px;color:${m.from===uid()?'var(--ink)':'var(--gray-500)'};margin-top:2px">${m.time||''}</div></div>`});
h+='</div><div class="chat-input-row"><input type="text" id="msgInput" placeholder="Сообщение..." onkeydown="if(event.key===\'Enter\')sendMsg()"><button class="btn small" onclick="sendMsg()">&#x27A4;</button></div>';
return h;
}
function sendMsg(){
const text=document.getElementById('msgInput')?.value.trim();
if(!text||!currentChat)return;
const msgs=LS('messages')||{};
const chatKey=[uid(),currentChat].sort().join('_');
if(!msgs[chatKey])msgs[chatKey]=[];
msgs[chatKey].push({from:uid(),to:currentChat,text,time:new Date().toLocaleTimeString('ru-RU',{hour:'2-digit',minute:'2-digit'}),read:false});
SS('messages',msgs);
document.getElementById('msgInput').value='';
renderPage();
// scroll to bottom
setTimeout(()=>{const el=document.getElementById('chatMsgs');if(el)el.scrollTop=el.scrollHeight},100);
}
let gameState = null;
function renderGames(){
const c = document.getElementById('mainContent');
c.innerHTML = `
<div class="card" style="text-align:center">
<h3>&#x1F3AE; Игры в чате</h3>
<p class="muted">Выбери игру и играй с друзьями!</p>
<button class="quiz-option" onclick="startTicTacToe()">&#x274C;&#x2B55; Крестики-нолики</button>
<button class="quiz-option" onclick="startGuessNumber()">&#x1F522; Угадай число (1-100)</button>
<button class="quiz-option" onclick="startReaction()">&#x26A1; Реакция — кто быстрее</button>
<button class="quiz-option" onclick="startQuiz()">&#x1F3C6; Спорт-викторина</button>
<button class="btn small outline" onclick="showPage('chat')" style="margin-top:8px">&#x2190; Назад в чаты</button>
</div>
<div id="gameArea"></div>
`;
}
function startTicTacToe(){
gameState = { type: 'tictactoe', board: Array(9).fill(''), turn: 'X', over: false };
renderTicTacToe();
}
function renderTicTacToe(){
const g = gameState;
let h = '<div class="card"><h3 style="text-align:center">&#x274C;&#x2B55; Крестики-нолики</h3><div class="game-status">';
if(g.over){
h += g.winner === 'draw' ? 'Ничья!' : g.winner + ' победил!';
h += '<br><button class="btn small outline" onclick="startTicTacToe()" style="margin-top:8px">Играть ещё</button>';
} else {
h += 'Ход: ' + (g.turn === 'X' ? '&#x274C; Крестики' : '&#x2B55; Нолики');
}
h += '</div><div class="game-grid">';
g.board.forEach((cell, i) => {
h += `<div class="game-cell${cell?' '+ (cell==='X'?'x':'o')+(g.over||cell?' disabled':'') :''}" onclick="ticMove(${i})">${cell}</div>`;
});
h += '</div><button class="btn small outline" onclick="startTicTacToe()" style="width:100%;margin-top:8px">Новая игра</button></div>';
document.getElementById('gameArea').innerHTML = h;
}
function ticMove(i){
if(!gameState||gameState.over||gameState.board[i])return;
gameState.board[i] = gameState.turn;
const lines = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]];
for(const [a,b,c] of lines){
if(gameState.board[a]&&gameState.board[a]===gameState.board[b]&&gameState.board[a]===gameState.board[c]){
gameState.over = true; gameState.winner = gameState.board[a]; break;
}
}
if(!gameState.over && gameState.board.every(c=>c)){ gameState.over = true; gameState.winner = 'draw'; }
if(!gameState.over) gameState.turn = gameState.turn === 'X' ? 'O' : 'X';
renderTicTacToe();
}
function startGuessNumber(){
gameState = { type: 'guess', number: Math.floor(Math.random()*100)+1, attempts: 0, max: 7 };
renderGuessNumber();
}
function renderGuessNumber(){
const g = gameState;
let h = `<div class="card" style="text-align:center"><h3>&#x1F522; Угадай число (1-100)</h3>
<p class="muted">Осталось попыток: <strong>${g.max - g.attempts}</strong></p>`;
if(g.won){
h += `<p style="color:var(--green);font-size:20px;font-weight:800">&#x1F389; Угадал! Это ${g.number}!</p><p class="muted">За ${g.attempts} попыток</p>`;
h += '<button class="btn small outline" onclick="startGuessNumber()">Играть ещё</button>';
} else if(g.attempts >= g.max){
h += `<p style="color:var(--red);font-size:18px;font-weight:800">Не угадал! Это было ${g.number}</p>`;
h += '<button class="btn small outline" onclick="startGuessNumber()">Играть ещё</button>';
} else {
h += '<input type="number" id="guessInput" placeholder="Твоё число" min="1" max="100" onkeydown="if(event.key==='Enter')doGuess()"><button class="btn" onclick="doGuess()" style="margin-top:8px">Проверить</button>';
}
h += '</div><button class="btn small outline" onclick="renderGames()" style="width:100%">&#x2190; К играм</button>';
document.getElementById('gameArea').innerHTML = h;
}
function doGuess(){
if(!gameState||gameState.won||gameState.attempts>=gameState.max)return;
const n = +document.getElementById('guessInput').value;
if(!n||n<1||n>100)return;
gameState.attempts++;
if(n === gameState.number){ gameState.won = true; renderGuessNumber(); return }
const hint = n < gameState.number ? '&#x1F4C8; Больше!' : '&#x1F4C9; Меньше!';
const tempDiv = document.createElement('div'); tempDiv.innerHTML = `<div class="card" style="text-align:center;padding:12px;margin-bottom:8px"><p>${n} &mdash; ${hint}</p></div>`;
document.getElementById('gameArea').insertBefore(tempDiv, document.getElementById('gameArea').firstChild);
if(gameState.attempts >= gameState.max) renderGuessNumber();
else { document.getElementById('guessInput').value=''; document.getElementById('guessInput').focus(); }
}
function startReaction(){
gameState = { type: 'reaction', state: 'waiting', startTime: 0, best: getMy('reactionBest') || 9999 };
renderReaction();
}
function renderReaction(){
const g = gameState;
let bg = '#151c28', txt = '';
if(g.state === 'waiting'){ bg = '#FF6B6B'; txt = '&#x1F534; Жди зелёный!'; }
else if(g.state === 'ready'){ bg = '#4CAF50'; txt = '&#x1F7E2; ЖМИ СЕЙЧАС!'; }
else if(g.state === 'early'){ bg = '#FFD700'; txt = '&#x26A0; Слишком рано! Жми "Начать"'; }
else if(g.state === 'done'){ txt = `&#x23F1; Твоё время: <strong>${g.reaction} мс</strong><br>Рекорд: ${g.best !== 9999 ? g.best+' мс' : '—'}`; }
let h = `<div class="card" style="text-align:center">
<h3>&#x26A1; Реакция</h3>
<div style="width:200px;height:200px;border-radius:50%;background:${bg};display:flex;align-items:center;justify-content:center;margin:16px auto;cursor:${g.state==='ready'?'pointer':'default'};font-size:18px;font-weight:800;transition:background .1s" onclick="reactClick()">${txt||'&#x1F3AF; Нажми "Начать"'}</div>`;
if(g.state === 'done'){
h += '<button class="btn small outline" onclick="startReaction()">Ещё раз</button>';
} else if(g.state === 'waiting' || g.state === 'early'){
h += '<button class="btn" onclick="reactStart()">Начать</button>';
}
h += '</div><button class="btn small outline" onclick="renderGames()" style="width:100%">&#x2190; К играм</button>';
document.getElementById('gameArea').innerHTML = h;
}
function reactStart(){
if(!gameState||gameState.state==='ready')return;
gameState.state = 'waiting'; renderReaction();
const delay = 1500 + Math.random() * 3000;
setTimeout(() => {
if(gameState.state !== 'waiting') return;
gameState.state = 'ready'; gameState.startTime = Date.now(); renderReaction();
}, delay);
}
function reactClick(){
if(!gameState)return;
if(gameState.state === 'waiting'){ gameState.state = 'early'; renderReaction(); return; }
if(gameState.state === 'ready'){ gameState.reaction = Date.now() - gameState.startTime; gameState.state = 'done';
if(gameState.reaction < gameState.best){ gameState.best = gameState.reaction; setMy('reactionBest', gameState.reaction); }
renderReaction(); }
}
function startQuiz(){
const questions = [
{q:'Сколько метров в олимпийском бассейне?',a:['25 м','50 м','100 м','33 м'],r:1},
{q:'Какой стиль плавания самый быстрый?',a:['Брасс','Баттерфляй','Кроль','На спине'],r:2},
{q:'Сколько золотых медалей у Майкла Фелпса?',a:['8','15','23','28'],r:2},
{q:'Как зовут лучшего спринтера мира (50 м в/с)?',a:['Майкл Фелпс','Калеб Дрессел','Райан Лохте','Адам Пити'],r:1},
{q:'Где пройдёт Олимпиада-2032?',a:['Лос-Анджелес','Париж','Брисбен','Токио'],r:2},
{q:'Сколько длится олимпийский цикл?',a:['2 года','3 года','4 года','5 лет'],r:2},
{q:'Какая страна выиграла больше всех медалей в плавании?',a:['Китай','Австралия','Россия','США'],r:3},
{q:'Как называется поворот в кроле?',a:['Сальто','Кувырок','Разворот','Маятник'],r:1}
];
gameState = { type: 'quiz', questions, current: 0, score: 0, answered: false };
renderQuiz();
}
function renderQuiz(){
const g = gameState;
if(g.current >= g.questions.length){
document.getElementById('gameArea').innerHTML = `<div class="card" style="text-align:center"><h3>&#x1F3C6; Викторина завершена!</h3><p style="font-size:24px;font-weight:800;color:var(--cyan)">${g.score} / ${g.questions.length}</p><p class="muted">${g.score>=6?'&#x1F451; Ты знаток спорта!':g.score>=4?'&#x1F44D; Неплохо!':'&#x1F4DA; Учи матчасть!'}</p><button class="btn" onclick="startQuiz()">Ещё раз</button></div>`;
return;
}
const q = g.questions[g.current];
let h = `<div class="card"><h3>&#x2753; Вопрос ${g.current+1}/${g.questions.length}</h3><p style="font-size:17px;font-weight:600;margin:12px 0">${q.q}</p>`;
q.a.forEach((ans,i)=>{
let cls = 'quiz-option';
if(g.answered){
if(i === q.r) cls += ' right';
else if(g.selected === i) cls += ' wrong';
}
h += `<button class="${cls}" onclick="answerQuiz(${i})" ${g.answered?'disabled':''}>${ans}</button>`;
});
h += `<div style="margin-top:8px;font-size:13px;color:var(--gray-500)">Счёт: ${g.score}</div>`;
if(g.answered){
h += '<button class="btn small" onclick="nextQuiz()" style="margin-top:8px">Дальше &#x2192;</button>';
}
h += '</div>';
document.getElementById('gameArea').innerHTML = h;
}
function answerQuiz(i){
if(!gameState||gameState.answered)return;
gameState.answered = true; gameState.selected = i;
if(i === gameState.questions[gameState.current].r) gameState.score++;
renderQuiz();
}
function nextQuiz(){
gameState.current++; gameState.answered = false; gameState.selected = null; renderQuiz();
}
function createGroupChat(){
const name=prompt('Название группового чата:');
if(!name)return;
const groups=LS('groups')||[];
groups.push({id:Date.now(),name,members:[uid()],messages:[]});
SS('groups',groups);
toast('Групповой чат создан!');
renderPage();
}
// === TOOLS PAGE ===
function renderToolsPage(){
return `
<div class="card">
<h3>&#x1F4CA; Нормативы (плавание, 50 м бассейн)</h3>
<table><tr><th>Разряд</th><th>50 м в/с</th><th>100 м в/с</th><th>400 м в/с</th></tr>
<tr><td><span class="badge gold">МСМК</span></td><td>21.50</td><td>47.00</td><td>3:48</td></tr>
<tr><td><span class="badge blue">МС</span></td><td>23.00</td><td>50.50</td><td>4:05</td></tr>
<tr><td><span class="badge green">КМС</span></td><td>25.00</td><td>54.50</td><td>4:25</td></tr>
<tr><td>1 взр.</td><td>28.00</td><td>1:01</td><td>5:00</td></tr>
<tr><td>2 взр.</td><td>31.50</td><td>1:09</td><td>5:40</td></tr>
<tr><td>3 взр.</td><td>35.00</td><td>1:17</td><td>6:20</td></tr>
<tr><td>1 юн.</td><td>40.00</td><td>1:29</td><td>7:10</td></tr>
<tr><td>2 юн.</td><td>47.00</td><td>1:45</td><td>8:10</td></tr></table>
</div>
<div class="card">
<h3>&#x1F48A; Здоровье</h3>
<p class="muted" style="margin-bottom:8px">Отмечай принятые витамины:</p>
<div id="vitCheck"></div>
<button class="btn small outline" onclick="addSleepEntry()" style="margin-top:8px">+ Записать сон и пульс</button>
<div id="vitHistory" class="muted" style="margin-top:8px"></div>
</div>
<div class="card">
<h3>&#x1F4DA; Видеоуроки</h3>
<p class="muted">Найди на YouTube:</p>
${['swimming start technique','flip turn tutorial','freestyle stroke technique','underwater dolphin kick','breaststroke technique','Caeleb Dressel 50m analysis','swimming stretching routine','dryland training swimming','swimming race psychology','swimmer nutrition meal plan'].map(q=>`<div style="padding:6px 0;font-size:13px;border-bottom:1px solid #2a3342"><em>${q}</em></div>`).join('')}
</div>
<div class="card">
<h3>&#x2B50; Рейтинг</h3>
<p class="muted">Топ спортсменов (по голосам):</p>
<div id="rankList"></div>
<button class="btn small outline" onclick="voteRandom()" style="margin-top:8px">&#x2B50; Проголосовать</button>
</div>
`;
}
// === VITAMINS ===
function renderVitamins(){
const el=document.getElementById('vitCheck');
if(!el)return;
const today=new Date().toISOString().slice(0,10);
const taken=getMy('vtaken')||{};
const vits=[{id:'d3',n:'Витамин D3'},{id:'omega',n:'Омега-3'},{id:'mg',n:'Магний'},{id:'zn',n:'Цинк'},{id:'bcaa',n:'BCAA'}];
el.innerHTML=vits.map(v=>{
const ok=taken[today]&&taken[today][v.id];
return `<label style="display:flex;align-items:center;gap:8px;padding:6px 0;cursor:pointer;font-size:14px"><input type="checkbox" ${ok?'checked':''} onchange="toggleVit('${v.id}')">${v.n}</label>`;
}).join('');
}
function toggleVit(id){
const today=new Date().toISOString().slice(0,10);
let taken=getMy('vtaken')||{};
if(!taken[today])taken[today]={};
taken[today][id]=!taken[today][id];
setMy('vtaken',taken);
renderVitamins();
}
function addSleepEntry(){
const h=prompt('Часов сна:');const p=prompt('Пульс утром:');
if(!h&&!p)return;
const s=getMyArr('sleep');s.unshift({date:new Date().toISOString().slice(0,10),hours:+h||0,pulse:+p||0});
setMyArr('sleep',s);toast('Сохранено!');
}
// === RANKING ===
function renderRanking(){
const el=document.getElementById('rankList');if(!el)return;
const users=LS('users')||[];
const sorted=[...users].filter(u=>u.stars>0).sort((a,b)=>b.stars-a.stars).slice(0,5);
if(!sorted.length){el.innerHTML='<p class="muted">Пока никто не проголосовал</p>';return}
el.innerHTML=sorted.map((u,i)=>`<div style="display:flex;align-items:center;gap:8px;padding:8px 0;border-bottom:1px solid #2a3342"><span style="color:${i===0?'#FFD700':i===1?'#C0C0C0':i===2?'#CD7F32':'var(--gray-500)'}">${i+1}.</span><span>${u.name}</span><span style="margin-left:auto;color:var(--cyan);font-size:13px">&#x2B50; ${u.stars}</span></div>`).join('');
}
function voteRandom(){
const users=LS('users')||[];
const others=users.filter(u=>u.id!==uid());
if(!others.length){toast('Нет других пользователей!');return}
const target=others[Math.floor(Math.random()*others.length)];
const voterKey='voted_'+target.id;
if(localStorage.getItem('g_'+voterKey)){toast('Ты уже голосовал за '+target.name+'!');return}
target.stars=(target.stars||0)+1;
const idx=users.findIndex(u=>u.id===target.id);
if(idx>=0){users[idx]=target;SS('users',users)}
localStorage.setItem('g_'+voterKey,'1');
renderRanking();
toast('Голос за '+target.name+' учтён!');
}
// === LOGOUT ===
function doLogout(){
currentUser=null;currentChat=null;
document.getElementById('appScreen').classList.remove('active');
document.getElementById('loginScreen').classList.add('active');
document.getElementById('lUser').value='';document.getElementById('lPass').value='';
}
// === CITIES ===
const cityData={'Казахстан':['Астана','Алматы','Шымкент','Актобе','Караганда','Тараз','Павлодар','Семей','Атырау','Костанай'],'Россия':['Москва','Санкт-Петербург','Новосибирск','Екатеринбург','Казань','Омск','Уфа'],'США':['Нью-Йорк','Лос-Анджелес','Чикаго','Хьюстон','Майами','Бостон'],'Турция':['Стамбул','Анкара','Измир','Анталья','Бурса'],'Узбекистан':['Ташкент','Самарканд','Бухара'],'Кыргызстан':['Бишкек','Ош'],'Украина':['Киев','Харьков','Одесса','Львов'],'Беларусь':['Минск','Гомель','Брест'],'Германия':['Берлин','Мюнхен','Гамбург'],'Франция':['Париж','Марсель','Лион'],'Великобритания':['Лондон','Манчестер'],'Италия':['Рим','Милан'],'Испания':['Мадрид','Барселона'],'Япония':['Токио','Осака'],'Китай':['Пекин','Шанхай'],
'ОАЭ':['Дубай','Абу-Даби'],'Азербайджан':['Баку','Гянджа'],'ЮАР':['Йоханнесбург','Кейптаун'],'Канада':['Торонто','Ванкувер','Монреаль'],'Австралия':['Сидней','Мельбурн'],'Бразилия':['Сан-Паулу','Рио']};
function updateCities(){
const c=document.getElementById('rCountry').value;
const dl=document.getElementById('cityList');dl.innerHTML='';
(cityData[c]||[]).forEach(city=>{const o=document.createElement('option');o.value=city;dl.appendChild(o)});
}
// === RENDER ALL ===
function renderAll(){
renderPage();
setTimeout(()=>{renderVitamins();renderRanking()},200);
}
// === INIT ===
if('serviceWorker'in navigator)navigator.serviceWorker.register('sw.js').catch(()=>{});
document.getElementById('loginScreen').classList.add('active');
</script>
</body>
</html>