Echo / index.html
mlmihjaz
Footer: credit "Concept and developed by SriAI"
044add3
Raw
History Blame Contribute Delete
10.8 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ECHO 3D — listen, type, level up</title>
<link rel="stylesheet" href="/src/styles/main.css">
<style>
/* Boot loader — shown immediately on first paint, before any JS runs. */
#bootLoader{position:fixed;inset:0;z-index:100000;display:flex;flex-direction:column;
align-items:center;justify-content:center;gap:24px;
background:radial-gradient(ellipse at 50% 30%,#1a2440 0%,#070912 70%);color:#f4f6fb;
font-family:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
transition:opacity .55s ease;}
#bootLoader.gone{opacity:0;pointer-events:none}
#bootLoader .logo{font-family:ui-monospace,"SF Mono","JetBrains Mono",Menlo,Consolas,monospace;
font-weight:900;font-size:46px;letter-spacing:.32em;color:#ffe08a;
text-shadow:0 2px 0 #5a3a08,0 3px 0 rgba(0,0,0,.55),0 0 22px rgba(240,168,48,.7),0 0 40px rgba(240,168,48,.35)}
#bootLoader .spinner{width:58px;height:58px;border-radius:50%;
border:3px solid rgba(255,255,255,.1);border-top-color:#ffd166;
animation:bootSpin 1.1s linear infinite}
@keyframes bootSpin{to{transform:rotate(360deg)}}
#bootLoader .bar{width:min(360px,72vw);height:6px;background:rgba(255,255,255,.08);
border-radius:99px;overflow:hidden}
#bootLoader .bar i{display:block;height:100%;width:0%;
background:linear-gradient(90deg,#46d39a,#ffd166,#f0a830);
transition:width .35s ease;border-radius:99px}
#bootLoader .msg{font-size:13.5px;color:#cfd5e3;min-height:18px;text-align:center;
letter-spacing:.02em;padding:0 16px}
#bootLoader .hint{font-size:11px;color:#7a8298;margin-top:-12px}
</style>
</head>
<body>
<!-- Full-screen boot loader: visible until first frame after dictionary loads -->
<div id="bootLoader">
<div class="logo">ECHO</div>
<div class="hint">listen · type · level up</div>
<div class="spinner"></div>
<div class="bar"><i id="bootBar"></i></div>
<div class="msg" id="bootMsg">Initialising…</div>
</div>
<!-- Real 3D scene canvas -->
<canvas id="bg3d"></canvas>
<!-- Auth screen -->
<div class="auth-screen" id="authScreen">
<div class="auth-card">
<h2>ECHO</h2>
<div class="sub" id="authSub">Sign in to track your stars and climb the leaderboard.</div>
<div class="auth-tabs">
<button id="tabLogin" class="active" type="button">Login</button>
<button id="tabRegister" type="button">Register</button>
</div>
<form id="authForm" autocomplete="off">
<div class="auth-field"><label>Email</label><input type="email" id="fEmail" required placeholder="you@example.com"></div>
<div class="auth-field" id="fNameRow" style="display:none"><label>Display name (optional)</label><input type="text" id="fName" placeholder="e.g. Aisha"></div>
<div class="auth-field"><label>Password</label><input type="password" id="fPwd" required placeholder="at least 6 characters"></div>
<div class="auth-field" id="fPwd2Row" style="display:none"><label>Confirm password</label><input type="password" id="fPwd2"></div>
<div class="auth-field err" id="fErr"></div>
<button class="primary" id="authBtn" type="submit">Login</button>
<button class="auth-link" id="forgotBtn" type="button">Forgot password?</button>
</form>
<div class="guest"><button id="guestBtn" type="button">Continue as guest →</button></div>
</div>
</div>
<!-- Main app -->
<div class="wrap" id="appWrap" style="display:none">
<header>
<div class="brand">
<div class="logo">ECHO</div>
<div class="tagline">type what you hear · UK voice · සිංහල · தமிழ்</div>
</div>
<div class="hgroup">
<div class="total"><div class="n" id="t-mastered">0</div><div class="l">mastered</div></div>
<div class="total"><div class="n" id="t-stars" style="color:var(--amber)">0</div><div class="l">⭐ stars</div></div>
<button class="admin-chip" id="adminBtn" style="display:none" title="Open admin dashboard">⚙ ADMIN</button>
<div class="user-chip" id="userChip">
<div class="avatar" id="userAvatar">?</div>
<div><div class="name" id="userName">Guest</div><div class="stars" id="userRank">Rank —</div></div>
<button id="signOutBtn" title="Sign out"></button>
</div>
</div>
</header>
<div class="track-hint">🎮 Pick a grade — Sprout → Legendary · scroll →</div>
<div class="track" id="track"></div>
<div class="grid">
<div class="card" id="gameCard">
<div class="overlay" id="overlay">
<div>
<h2>Ready to listen?</h2>
<p>Each grade is a word length. Hear the word, see the Sinhala &amp; Tamil meaning, type what you hear, press <b>Enter</b>.</p>
<button class="btn" id="startBtn" style="background:var(--amber);color:#1a1404" disabled>Loading dictionary…</button>
<div id="bootStatus" style="margin-top:14px;font-size:12px;color:var(--fog)">Downloading 370,000+ English words…</div>
</div>
</div>
<div class="speaker-row">
<div class="speaker" id="speaker" role="button" tabindex="0" aria-label="Play the word again">
<span class="ring"></span>
<svg viewBox="0 0 24 24"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3a4.5 4.5 0 00-2.5-4.03v8.05A4.5 4.5 0 0016.5 12zM14 3.23v2.06a7 7 0 010 13.42v2.06a9 9 0 000-17.54z"/></svg>
</div>
<div class="replay-hint">Tap or press <b>Tab</b> to replay · <b>UK female voice</b></div>
</div>
<div class="meaning empty" id="meaning">
<div class="col si"><div class="lab">සිංහල (meaning)</div><div class="val" id="m-si"></div></div>
<div class="col ta"><div class="lab">தமிழ் (பொருள்)</div><div class="val" id="m-ta"></div></div>
</div>
<div class="tiles" id="tiles"></div>
<input id="typed" type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" placeholder="type the word and press Enter" disabled>
<div class="feedback" id="feedback"></div>
<div class="controls">
<button class="btn ghostbtn" id="revealBtn">Show answer</button>
<button class="btn ghostbtn" id="skipBtn">Skip →</button>
<button class="btn ghostbtn" id="hearAgainBtn">🔊 Slow</button>
</div>
<div class="levelup" id="levelup" style="display:none"><span></span></div>
<div class="burst" id="burst"></div>
</div>
<div class="side">
<div class="pcard">
<div class="eyebrow">Current grade</div>
<div class="lvlnum" id="p-lvlnum">🌱 Sprout</div>
<div class="lvldesc" id="p-lvldesc">1-letter words</div>
<div class="bar"><i id="p-bar"></i></div>
<div class="barlabel"><span><b id="p-done">0</b> / <b id="p-total">0</b> mastered</span><span id="p-pct">0%</span></div>
<div class="ministats">
<div class="ministat rem"><div class="n" id="p-remaining">0</div><div class="l">words left</div></div>
<div class="ministat strk"><div class="n" id="p-streak">0</div><div class="l">streak</div></div>
</div>
</div>
<div class="leaderboard">
<h3>Leaderboard <span class="live"><span id="onlineCount">1</span> online</span></h3>
<div id="lbList"></div>
<div class="lb-stats">
<div><b id="lb-total">1</b> Players</div>
<div><b class="green" id="lb-globalStars">0</b> Total ⭐</div>
</div>
</div>
<div class="pcard medals">
<h3>Journey <span id="medals-count">0/7</span></h3>
<div class="medal-row" id="medalRow"></div>
<div class="medal-cap">Sunny → Cloudy → Drizzle → Mist → Thunder → Snow → <b>Ice Age</b></div>
</div>
<div class="pcard fam">
<h3>Familiarised <span id="fam-count">0</span></h3>
<div class="famlist" id="famlist"><div class="famempty">Mastered words collect here.</div></div>
</div>
<details class="settings">
<summary>Settings</summary>
<div class="set-row">
<label>Voice</label>
<select id="engineSel">
<optgroup label="🎲 Surprise me">
<option value="stream:random">🎲 Random — different voice every word</option>
</optgroup>
<optgroup label="★ Premium natural (Polly via StreamElements · free)">
<option value="stream:Brian">★ Brian — UK male</option>
<option value="stream:Amy">★ Amy — UK female</option>
<option value="stream:Emma">★ Emma — UK female</option>
<option value="stream:Joanna">★ Joanna — US female</option>
<option value="stream:Matthew">★ Matthew — US male</option>
<option value="stream:Salli">★ Salli — US female</option>
<option value="stream:Russell">★ Russell — AU male</option>
<option value="stream:Nicole">★ Nicole — AU female</option>
</optgroup>
<optgroup label="Other">
<option value="google">Google Translate — UK</option>
<option value="browser">Browser native (offline)</option>
</optgroup>
</select>
</div>
<div class="set-row">
<label>Speed — <span class="pct" id="rateVal">0.95×</span></label>
<input type="range" id="rate" min="0.5" max="1.3" step="0.05" value="0.95">
</div>
<div class="set-row">
<label>Correct answers to master — <span class="pct" id="thrVal">3</span></label>
<input type="range" id="thr" min="1" max="6" step="1" value="3">
</div>
<div class="set-row"><label class="toggle"><input type="checkbox" id="showMeaning" checked> Show Sinhala &amp; Tamil meanings</label></div>
<div class="set-row"><label class="toggle"><input type="checkbox" id="cheerSound" checked> Cheering sound</label></div>
<div class="set-row"><label class="toggle"><input type="checkbox" id="ambientAudio" checked> Birds, crickets &amp; ambient nature sound</label></div>
<div class="set-row"><label class="toggle"><input type="checkbox" id="ambient3D" checked> 3D ambient scene (sky, sun, grass, goats)</label></div>
<div class="set-row"><label class="toggle"><input type="checkbox" id="fullDict" checked> Full English dictionary (370,000+ words)</label></div>
<div class="set-row"><button class="btn danger" id="resetBtn">Reset progress</button></div>
</details>
</div>
</div>
<footer>Concept and developed by SriAI</footer>
</div>
<audio id="audio" preload="auto"></audio>
<script type="module" src="/src/main.js"></script>
</body>
</html>