Class_Of_Learners / Traffic /Driving.html
NeelAniGamer's picture
Updated favicons, dark mode logo, and completely resolved large files via LFS
a4fb0f8
Raw
History Blame Contribute Delete
186 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover, interactive-widget=resizes-content" />
<meta http-equiv="Content-Security-Policy"
content="default-src 'self' 'unsafe-inline' 'unsafe-eval' https: data: blob:; img-src 'self' https: data: blob:; media-src 'self' https: data: blob:; connect-src 'self' https: wss:; font-src 'self' https: data:;" />
<title>Mumbai Traffic Hero Academy - Simulation</title>
<!-- Cloud Auth & Sync Logic -->
<script>
const _oldFetch = window.fetch
window.fetch = function () {
if (arguments[0] && typeof arguments[0] === 'string' && arguments[0].startsWith('config.json')) {
arguments[0] = '../' + arguments[0]
}
return _oldFetch.apply(this, arguments)
}
// Silence GLTFLoader spam for KHR_texture_transform
const _oldWarn = console.warn
console.warn = function (...args) {
if (args[0] && typeof args[0] === 'string' && (args[0].includes('Custom UV sets in "KHR_texture_transform"') || args[0].includes('Custom UV set') || args[0].includes('not yet supported')))
return
_oldWarn.apply(console, args)
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/utils/BufferGeometryUtils.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<script src="traffic-charts.js"></script>
<link rel="stylesheet" href="traffic-charts.css" />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Lora:ital,wght@0,400;0,500;0,600;1,400&display=swap"
rel="stylesheet" />
<style>
:root {
--bg: #070a14;
--card: rgba(17, 24, 39, 0.7);
--text: #e8e3d8;
--border: rgba(255, 255, 255, 0.1);
--hover: rgba(255, 255, 255, 0.15);
--muted: #8891aa;
--muted2: #525046;
--accent: #f2b84b;
--green: #34d399;
--red: #ef4444;
--yellow: #f2b84b;
--signal: #5ed4f5;
--r: 16px;
/* Z-index scale — 10 semantic layers, everything fits here */
--z-canvas: 1;
--z-effects: 19;
--z-vehicle: 20;
--z-minimap: 24;
--z-controls: 100;
--z-hud: 9998;
--z-kid-toast: 9999;
--z-overlay: 100000;
--z-modal: 100001;
--z-loading: 999999;
}
.mobile-dl {
display: none !important;
}
@media (max-width: 768px) {
.mobile-dl {
display: inline-flex !important;
}
.lw {
padding: 30px 24px;
}
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Inter', sans-serif;
}
#3c {
display: block;
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
height: 100dvh;
}
body {
background: var(--bg);
color: var(--text);
padding-top: env(safe-area-inset-top, 0px);
padding-bottom: env(safe-area-inset-bottom, 0px);
padding-left: env(safe-area-inset-left, 0px);
padding-right: env(safe-area-inset-right, 0px);
}
/* New Dedicated Loading Screen */
#loading-screen {
position: fixed;
inset: 0;
background: #070a14;
z-index: var(--z-loading);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #34d399;
font-family: 'Space Mono', monospace;
transition:
opacity 0.8s ease,
transform 0.8s ease;
}
#loading-screen h1 {
font-family: 'Instrument Serif', serif;
font-style: italic;
font-size: clamp(2rem, 5vw, 4rem);
color: #f2b84b;
margin-bottom: 20px;
}
#loading-bar-container {
width: clamp(250px, 50vw, 400px);
height: 12px;
background: rgba(255, 255, 255, 0.1);
border-radius: 6px;
overflow: hidden;
margin-bottom: 15px;
}
#loading-bar {
width: 0%;
height: 100%;
background: #34d399;
transition: width 0.1s linear;
}
#loading-pct {
font-size: 1.5rem;
font-weight: bold;
}
#loading-status {
margin-top: 10px;
font-size: 0.9rem;
color: #8891aa;
}
.screen {
position: fixed;
inset: 0;
z-index: 50;
background: var(--bg);
overflow-y: auto;
display: none;
flex-direction: column;
}
.screen.active {
display: flex;
}
h1,
h2,
h3 {
font-family: 'Lora', serif;
color: var(--text);
font-weight: 600;
}
.btn {
background: var(--text);
color: #fff;
border: none;
padding: clamp(12px, 1.5vw, 16px) clamp(20px, 2.5vw, 32px);
border-radius: 30px;
font-weight: 600;
font-size: clamp(0.95rem, 1.2vw, 1.1rem);
cursor: pointer;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 8px;
justify-content: center;
}
.btn:hover {
background: var(--muted2);
transform: translateY(-1px);
}
.btn-g {
background: var(--card);
color: var(--text);
border: 1px solid var(--border);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
}
.btn-g:hover {
background: var(--hover);
}
.btn-danger {
background: var(--red);
color: #fff;
border: none;
padding: 12px 20px;
border-radius: 30px;
font-weight: 600;
font-size: 0.95rem;
cursor: pointer;
transition: all 0.2s;
}
.btn-danger:hover {
filter: brightness(0.9);
transform: translateY(-1px);
}
/* Start Screen */
.brand-header {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 40px;
}
.sc {
width: 100%;
min-height: 100vh;
min-height: 100dvh;
margin: 0 auto;
padding: clamp(40px, 8vw, 120px) 20px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
justify-content: center;
position: relative;
z-index: 10;
box-sizing: border-box;
}
.st {
font-size: clamp(2.5rem, 6vw, 5.5rem);
line-height: 1.1;
letter-spacing: -0.02em;
margin-bottom: 24px;
font-family: 'Lora', serif;
font-weight: 700;
}
.su {
font-size: clamp(1rem, 2vw, 1.4rem);
color: var(--muted2);
line-height: 1.6;
max-width: 800px;
margin: 0 auto 40px auto;
font-weight: 400;
}
.pls {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 16px;
margin-bottom: 40px;
}
#rl {
position: absolute;
inset: 0;
overflow: hidden;
pointer-events: none;
z-index: var(--z-canvas);
}
.rd {
position: absolute;
top: -100px;
width: 2px;
background: rgba(44, 43, 37, 0.1);
animation: fall linear infinite;
}
@keyframes fall {
to {
transform: translateY(120vh);
}
}
/* Name Dialog */
#name-dlg {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 90;
display: none;
align-items: center;
justify-content: center;
backdrop-filter: blur(4px);
}
#name-dlg.on {
display: flex;
}
.nd-c {
background: var(--card);
padding: 32px;
border-radius: 16px;
max-width: 400px;
width: 90%;
text-align: center;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.1);
}
.nd-i {
width: 100%;
padding: 12px 16px;
border: 1px solid var(--border);
border-radius: 8px;
font-size: 1.1rem;
margin: 16px 0 24px;
text-align: center;
}
/* Toast */
#toast {
position: fixed;
top: 20px;
left: 50%;
max-width: calc(100vw - 32px);
transform: translateX(-50%) translateY(-100px);
z-index: 100;
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
#toast.on {
transform: translateX(-50%) translateY(0);
}
#ti {
padding: 12px 24px;
border-radius: 30px;
font-weight: 600;
color: #2c2b25;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
font-size: 0.95rem;
display: flex;
align-items: center;
gap: 8px;
}
/* Levels Screen */
.tb {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px 40px;
padding-top: max(20px, env(safe-area-inset-top));
border-bottom: 1px solid var(--border);
background: var(--card);
position: sticky;
top: 0;
z-index: 10;
}
.back-btn {
background: var(--card);
border: 1px solid var(--border);
min-height: 48px;
border-radius: 999px;
color: var(--text);
font-weight: 600;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 0.95rem;
padding: 8px 20px;
transition: all 0.2s ease;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
.back-btn:hover {
background: var(--hover);
border-color: var(--muted);
transform: translateY(-1px);
}
.lw {
max-width: 100%;
margin: 0 auto;
padding: 40px 60px;
width: 100%;
}
.sec-hdr {
font-family: 'Lora', serif;
font-size: 1.8rem;
margin: 32px 0 16px;
color: var(--text);
}
.lv-track {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 24px;
margin-bottom: 40px;
}
.lcard {
background: var(--card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 24px;
cursor: pointer;
transition: all 0.2s;
position: relative;
overflow: hidden;
display: flex;
flex-direction: column;
}
.lcard:hover {
border-color: var(--muted);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.04);
transform: translateY(-2px);
}
.lcard.lk {
opacity: 0.6;
cursor: not-allowed;
background: var(--bg);
border-style: dashed;
}
.lcard.lk:hover {
transform: none;
box-shadow: none;
border-color: var(--border);
}
.lbar {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
}
.lct {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 16px;
}
.lico {
width: 48px;
height: 48px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
color: #fff;
}
.lst {
font-size: 0.8rem;
font-weight: 600;
padding: 4px 12px;
border-radius: 20px;
}
.sdk {
background: rgba(59, 140, 102, 0.1);
color: var(--green);
}
.sip {
background: rgba(217, 119, 87, 0.1);
color: var(--accent);
}
.sns {
background: var(--hover);
color: var(--muted);
}
.lnum {
font-size: 0.85rem;
font-weight: 600;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.04em;
margin-bottom: 4px;
}
.lnm {
font-size: 1.4rem;
font-family: 'Lora', serif;
font-weight: 600;
margin-bottom: 8px;
line-height: 1.2;
}
.ltg {
font-size: 0.95rem;
color: var(--muted2);
line-height: 1.5;
flex-grow: 1;
}
.lmt {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid var(--hover);
font-size: 0.85rem;
font-weight: 500;
color: var(--muted);
}
/* Briefing Screen */
.br-w {
display: flex;
max-width: 100%;
margin: 40px auto;
gap: 40px;
width: 100%;
padding: 0 40px;
}
.br-l {
flex: 0 0 400px;
}
.br-r {
flex: 1;
position: sticky;
top: 120px;
margin-top: 0px;
align-self: flex-start;
display: flex;
flex-direction: column;
align-items: center;
}
/* Tablet: shrink left sidebar */
@media (max-width: 1280px) {
.br-l {
flex: 0 0 320px;
}
}
.br-prog {
background: var(--hover);
height: 6px;
border-radius: 3px;
margin: 24px 0 8px;
overflow: hidden;
}
.br-prog-fill {
background: var(--text);
height: 100%;
width: 0%;
transition: width 0.3s;
}
.syl-item {
border: 1px solid var(--border);
border-radius: 12px;
margin-bottom: 16px;
background: var(--card);
overflow: hidden;
cursor: pointer;
padding: 20px;
display: flex;
gap: 16px;
align-items: flex-start;
transition: all 0.2s ease;
}
.syl-item:hover {
border-color: var(--muted);
transform: translateX(2px);
}
.syl-item.syl-active {
border-color: var(--text);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.06);
}
.syl-ck {
flex-shrink: 0;
width: 32px;
height: 32px;
border-radius: 50%;
border: 3px solid var(--border);
margin-top: 2px;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
.syl-done .syl-ck {
background: linear-gradient(135deg, #00c851, #007e33);
border-color: #00c851;
box-shadow: 0 0 20px rgba(0, 200, 81, 0.5);
position: relative;
animation: pulse-ck 2s ease-in-out infinite;
}
.syl-done .syl-ck::after {
content: '';
position: absolute;
top: 5px;
left: 9px;
width: 6px;
height: 13px;
border: solid #fff;
border-width: 0 3px 3px 0;
transform: rotate(45deg);
}
.syl-active .syl-ck {
border-color: var(--yellow);
box-shadow: 0 0 0 3px rgba(255, 213, 74, 0.15);
}
@keyframes pulse-ck {
0%,
100% {
box-shadow: 0 0 20px rgba(0, 200, 81, 0.5);
}
50% {
box-shadow:
0 0 30px rgba(0, 200, 81, 0.8),
0 0 40px rgba(0, 200, 81, 0.3);
}
}
/* Badge Pill */
.syl-badge {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 12px;
border-radius: 20px;
font-size: 0.7rem;
font-family: 'Space Mono', monospace;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
margin-top: 4px;
width: fit-content;
background: rgba(184, 155, 255, 0.1);
border: 1px solid rgba(184, 155, 255, 0.2);
color: rgba(184, 155, 255, 0.7);
transition: all 0.3s ease;
}
.syl-done .syl-badge {
background: rgba(0, 200, 81, 0.12);
border-color: rgba(0, 200, 81, 0.3);
color: #00c851;
}
.syl-active .syl-badge {
background: rgba(242, 184, 75, 0.12);
border-color: rgba(242, 184, 75, 0.3);
color: var(--yellow);
}
.syl-info {
display: flex;
flex-direction: column;
gap: 6px;
}
.syl-lbl {
font-weight: 600;
font-size: 1.1rem;
color: var(--text);
}
.syl-sub {
font-size: 0.95rem;
color: var(--muted2);
line-height: 1.5;
display: none;
}
.syl-active .syl-sub {
display: block;
}
.bc-card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 40px;
min-height: 400px;
max-width: 700px;
width: 100%;
margin: 0 auto;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.04);
}
.bc-ttl {
font-size: 0.9rem;
font-weight: 600;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 24px;
}
.bc-next-btn {
margin-top: 40px;
padding-top: 24px;
border-top: 1px solid var(--border);
text-align: right;
}
.bc-next-btn button {
background: var(--text);
color: #fff;
border: none;
padding: 12px 24px;
border-radius: 30px;
font-weight: 600;
cursor: pointer;
}
.stat-row {
display: flex;
flex-wrap: wrap;
gap: 16px;
margin-top: 24px;
}
.stat-box {
flex: 1;
background: var(--bg);
border: 1px solid var(--border);
border-radius: 16px;
padding: 20px;
text-align: center;
}
.stat-val {
font-size: 2rem;
font-family: 'Lora', serif;
font-weight: 600;
color: var(--text);
}
.stat-lbl {
font-size: 0.85rem;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-top: 4px;
}
.bc-rule-pill {
display: inline-block;
background: rgba(217, 119, 87, 0.1);
color: var(--accent);
font-weight: 600;
font-size: 0.85rem;
padding: 4px 12px;
border-radius: 20px;
margin-bottom: 16px;
}
.bc-rule-txt {
font-size: 1.4rem;
font-family: 'Lora', serif;
line-height: 1.5;
color: var(--text);
}
.lb {
background: var(--bg);
border: 1px solid var(--border);
border-left: 4px solid var(--text);
padding: 24px;
border-radius: 0 16px 16px 0;
margin-bottom: 24px;
}
.ls {
font-weight: 600;
font-size: 0.9rem;
color: var(--muted);
margin-bottom: 8px;
}
.lt {
font-size: 1.1rem;
line-height: 1.5;
color: var(--text);
}
.fr {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 24px;
background: var(--card);
border: 1px solid var(--border);
border-radius: 16px;
}
.fl {
font-weight: 600;
color: var(--muted);
}
.fa {
font-size: 1.5rem;
font-family: 'Lora', serif;
font-weight: 600;
color: var(--red);
}
/* Quiz Screen */
.qw {
max-width: 100%;
margin: 60px auto;
width: 100%;
padding: 0 24px;
}
.qd-cnt {
display: flex;
gap: 8px;
justify-content: center;
margin-bottom: 40px;
}
.qdt {
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--border);
}
.qdt.dn {
background: var(--text);
}
.qdt.cu {
background: var(--accent);
box-shadow: 0 0 0 4px rgba(217, 119, 87, 0.2);
}
.qcard {
background: var(--card);
border: 1px solid var(--border);
border-radius: 16px;
padding: 40px;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.06);
}
.qq {
font-size: 1.4rem;
font-family: 'Lora', serif;
line-height: 1.5;
margin-bottom: 32px;
display: flex;
gap: 16px;
align-items: flex-start;
}
.qq span {
font-family: 'Inter', sans-serif;
font-size: 0.9rem;
font-weight: 700;
color: var(--accent);
background: rgba(217, 119, 87, 0.1);
padding: 4px 10px;
border-radius: 8px;
margin-top: 4px;
}
.qopts {
display: flex;
flex-direction: column;
gap: 12px;
}
.qo {
text-align: left;
padding: 16px 24px;
background: var(--bg);
border: 1px solid var(--border);
border-radius: 14px;
font-size: 1rem;
color: var(--text);
cursor: pointer;
transition: all 0.2s;
font-weight: 500;
}
.qo:hover:not(:disabled) {
background: var(--hover);
border-color: var(--muted);
}
.qo.selected {
background: rgba(217, 119, 87, 0.12);
border-color: var(--accent);
color: var(--accent);
font-weight: 600;
box-shadow: 0 0 0 3px rgba(217, 119, 87, 0.15);
}
#qconfirm {
display: none;
margin-top: 16px;
text-align: center;
}
#qconfirm.show {
display: block;
animation: screenFadeIn 0.25s ease-out;
}
.qo.ok {
background: rgba(59, 140, 102, 0.1);
border-color: var(--green);
color: var(--green);
}
.qo.no {
background: rgba(201, 79, 79, 0.1);
border-color: var(--red);
color: var(--red);
}
.qo.rv {
background: rgba(59, 140, 102, 0.1);
border-color: var(--green);
color: var(--green);
border-style: dashed;
}
.qfb {
margin-top: 24px;
font-weight: 600;
font-size: 1.1rem;
text-align: center;
min-height: 24px;
}
.qfb.ok {
color: var(--green);
}
.qfb.no {
color: var(--red);
}
.qbot {
display: flex;
justify-content: flex-end;
margin-top: 32px;
}
/* Results Screen */
.res-w {
max-width: 100%;
margin: 60px auto;
width: 100%;
text-align: center;
padding: 0 24px;
}
.rico {
font-size: 5rem;
margin-bottom: 24px;
}
.rtit {
font-family: 'Lora', serif;
font-size: 2.5rem;
margin-bottom: 8px;
}
.rsub {
font-size: 1.1rem;
color: var(--muted2);
margin-bottom: 40px;
}
.rcard {
background: var(--card);
border: 1px solid var(--border);
border-radius: 16px;
padding: 32px;
text-align: left;
margin-bottom: 24px;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.04);
}
.rr {
display: flex;
justify-content: space-between;
padding: 16px 0;
border-bottom: 1px solid var(--hover);
font-size: 1.05rem;
}
.rr:last-child {
border-bottom: none;
padding-bottom: 0;
}
.rl {
color: var(--muted);
font-weight: 500;
}
.rv {
font-weight: 700;
color: var(--text);
}
.bdx {
background: var(--card);
border: 1px solid var(--accent);
border-radius: 12px;
padding: 24px;
margin-bottom: 32px;
display: none;
text-align: left;
background: rgba(217, 119, 87, 0.05);
}
.bdx.on {
display: block;
}
.bi {
font-size: 2.5rem;
margin-bottom: 12px;
}
.bn {
font-weight: 700;
font-size: 1.1rem;
color: var(--accent);
margin-bottom: 8px;
}
.bd {
font-size: 0.95rem;
color: var(--muted2);
line-height: 1.5;
}
.rbts {
display: flex;
gap: 16px;
justify-content: center;
}
/* Badges Screen */
.bgrid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 24px;
margin-top: 32px;
}
.bcard {
background: #fff;
border: 1px solid var(--border);
border-radius: 16px;
padding: 40px 24px;
text-align: center;
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.03);
}
.bcard.lk {
opacity: 0.5;
filter: grayscale(1);
background: #fdfdfd;
border: 2px dashed #ddd;
box-shadow: none;
}
.bcard:not(.lk):hover {
transform: translateY(-8px) scale(1.02);
box-shadow: 0 16px 32px rgba(0, 0, 0, 0.08);
border-color: #ddd;
}
.bgi {
font-size: 3.5rem;
margin-bottom: 16px;
}
.bgn {
font-weight: 600;
font-size: 1.1rem;
margin-bottom: 8px;
color: var(--text);
}
.bgd {
font-size: 0.9rem;
color: var(--muted2);
line-height: 1.5;
}
.stats-body {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-top: 40px;
}
.stat-card {
background: #fff;
border: 1px solid var(--border);
border-radius: 16px;
padding: 24px;
display: flex;
flex-direction: column;
gap: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.03);
}
.stat-card span {
color: var(--muted2);
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.05em;
font-weight: 600;
}
.stat-card strong {
font-size: 2rem;
color: var(--text);
}
/* Certificate */
#cert-wrapper {
display: flex;
justify-content: center;
margin: 40px 0;
}
.crt {
background: #fff;
padding: 60px;
color: #2c2b25;
position: relative;
font-family: 'Inter', sans-serif;
text-align: center;
width: 1056px;
min-height: 756px;
height: auto;
display: flex;
flex-direction: column;
align-items: center;
flex-shrink: 0;
border: 10px solid #c8a45c;
outline: 4px solid #e8c96d;
outline-offset: -14px;
border-radius: 2px;
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.08);
transform-origin: top center;
transform: scale(0.8);
}
.crt::after {
content: '';
position: absolute;
inset: 20px;
border: 2px solid rgba(0, 0, 0, 0);
/* disabled old border */
pointer-events: none;
}
.crtn {
font-family: 'Lora', serif;
font-size: 4.5rem;
color: #2c2b25;
font-weight: 400;
margin: 24px 0;
line-height: 1.1;
letter-spacing: -0.01em;
}
.crt-course {
font-family: 'Lora', serif;
font-weight: 500;
font-size: 3.5rem;
color: #d97757;
margin: 16px 0 60px;
letter-spacing: -0.01em;
}
.crt-body {
font-size: 1.4rem;
color: #525046;
font-weight: 400;
max-width: 100%;
line-height: 1.6;
}
.crt-meta {
font-size: 1.1rem;
color: #525046;
line-height: 1.6;
}
/* === Certificate Print Styles - Fixed Single Page === */
@media print {
body { background: white !important; }
#cert-wrapper {
display: block !important;
}
.crt {
width: 100% !important;
max-width: 700px !important;
margin: 0 auto !important;
padding: 20px !important;
border-width: 6px !important;
font-size: 12pt !important;
page-break-inside: avoid !important;
transform: scale(0.95) !important;
transform-origin: top center !important;
min-height: auto !important;
height: auto !important;
}
.crtn { font-size: 18pt !important; }
.crt-course { font-size: 24pt !important; }
.crt-body { font-size: 12pt !important; }
.crt-meta { font-size: 10pt !important; }
}
/* Challan & In-game overlays */
#cov {
position: fixed;
inset: 0;
background: rgba(201, 79, 79, 0.9);
z-index: 80;
display: none;
align-items: center;
justify-content: center;
backdrop-filter: blur(8px);
}
#cov.on {
display: flex;
}
.cvc {
background: var(--card);
padding: 40px;
border-radius: 16px;
max-width: 500px;
width: 90%;
text-align: center;
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.2);
}
.c-ttl {
font-size: 2rem;
font-family: 'Lora', serif;
font-weight: 700;
color: var(--red);
margin-bottom: 8px;
}
.c-sub {
font-weight: 600;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 32px;
}
.c-val {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 16px;
font-size: 1.2rem;
font-weight: 600;
color: var(--text);
margin-bottom: 16px;
}
.c-amt {
font-size: 3rem;
font-family: 'Lora', serif;
font-weight: 700;
color: var(--red);
margin: 32px 0;
}
#vflash {
position: fixed;
inset: 0;
background: var(--red);
z-index: 79;
opacity: 0;
pointer-events: none;
}
#vflash.flash {
animation: vfl 0.4s ease-out;
}
@keyframes vfl {
0% {
opacity: 0.8;
}
100% {
opacity: 0;
}
}
/* HUD overrides */
#gc {
position: fixed;
inset: 0;
width: 100vw;
height: 100vh;
z-index: var(--z-canvas);
display: none;
overflow: hidden;
}
#gc.on {
display: block;
}
#hud {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 20;
display: none;
padding: 16px 20px;
align-items: flex-start;
justify-content: space-between;
pointer-events: none;
}
#hud.on {
display: flex;
}
.hud-panel {
background: var(--card);
backdrop-filter: blur(12px);
border: 1px solid var(--border);
border-radius: 8px;
padding: clamp(4px, 1vw, 8px) clamp(8px, 2vw, 12px);
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
font-size: clamp(0.7rem, 1.2vw, 0.9rem);
color: var(--text);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.2);
}
.hp {
background: var(--card);
border: 1px solid var(--border);
border-radius: 8px;
padding: clamp(4px, 1vw, 8px) clamp(12px, 2.5vw, 16px);
font-size: clamp(0.65rem, 1.1vw, 0.85rem);
font-weight: 500;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
pointer-events: auto;
}
.hv {
font-weight: 700;
color: var(--accent);
margin-left: 4px;
}
#hudbar {
position: fixed;
top: 70px;
left: 20px;
right: 20px;
z-index: 21;
display: none;
justify-content: space-between;
gap: 12px;
pointer-events: none;
}
#hudbar.on {
display: flex;
}
/* objective-overlay — fluid version defined below under "Fluid HUD Elements" */
#objective-overlay.on {
transform: translateX(0);
}
#objective-title {
font-size: clamp(0.65rem, 1vw, 0.8rem);
text-transform: uppercase;
letter-spacing: 2px;
color: var(--yellow);
font-weight: 800;
}
#objective-desc {
font-size: clamp(0.75rem, 1.3vw, 1rem);
line-height: 1.4;
font-weight: 500;
}
.hb {
background: var(--card);
border: 1px solid var(--border);
border-radius: 8px;
padding: 8px 16px;
font-size: 0.85rem;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
pointer-events: auto;
}
#hwrap {
position: fixed;
top: 120px;
left: 20px;
z-index: 23;
display: none;
flex-direction: column;
gap: 6px;
background: var(--card);
padding: 12px;
border: 1px solid var(--border);
border-radius: 8px;
}
#hwrap.on {
display: flex;
}
.modal {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0.95);
background: var(--card);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
padding: 32px;
border-radius: var(--r);
border: 1px solid var(--border);
display: none;
flex-direction: column;
gap: 16px;
z-index: var(--z-controls);
width: 90%;
max-width: 420px;
opacity: 0;
transition:
opacity 0.3s ease,
transform 0.3s ease;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
}
.modal.on {
display: flex;
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
.hl {
font-size: 0.75rem;
font-weight: 600;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.ht {
width: 120px;
height: 8px;
background: var(--hover);
border-radius: 4px;
overflow: hidden;
}
.hf {
height: 100%;
background: var(--green);
border-radius: 4px;
transition: width 0.3s ease;
}
#tc {
position: fixed;
bottom: 40px;
left: 0;
right: 0;
display: none;
z-index: 29;
pointer-events: none;
padding: 0 32px;
justify-content: space-between;
align-items: flex-end;
}
#tc.on {
display: flex;
}
.db {
width: 64px;
height: 64px;
border-radius: 50%;
background: var(--card);
border: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.6rem;
pointer-events: auto;
user-select: none;
cursor: pointer;
transition: all 0.15s;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
}
.db:active {
background: var(--hover);
transform: scale(0.95);
border-color: var(--muted);
}
/* Gear selection overlay */
#gp {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
z-index: 25;
display: none;
gap: 4px;
background: var(--card);
padding: 8px;
border-radius: 30px;
border: 1px solid var(--border);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
}
#gp.on {
display: flex;
}
.gb {
width: 40px;
height: 40px;
border-radius: 50%;
border: none;
background: var(--bg);
font-weight: 700;
font-size: 0.9rem;
color: var(--muted);
cursor: pointer;
}
.gb.ag {
background: var(--text);
color: #fff;
}
#gread {
position: fixed;
bottom: 80px;
left: 50%;
transform: translateX(-50%);
font-weight: 700;
font-size: 1.1rem;
color: var(--text);
background: rgba(255, 255, 255, 0.8);
padding: 4px 12px;
border-radius: 12px;
z-index: var(--z-minimap);
display: none;
}
#gc.on~#gread {
display: block;
}
#ow {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1.4rem;
font-weight: 600;
background: rgba(217, 119, 87, 0.9);
color: #fff;
padding: 16px 32px;
border-radius: 30px;
z-index: 30;
display: none;
}
#ow.on {
display: block;
}
#rotate-device-overlay {
position: fixed;
inset: 0;
background: var(--bg);
z-index: var(--z-modal);
display: none;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
padding: 20px;
color: var(--text);
}
#rotate-device-overlay.on {
display: flex;
}
.rd-icon {
font-size: 3rem;
margin-bottom: 20px;
animation: rotatePhone 2s ease-in-out infinite;
}
@keyframes rotatePhone {
0%, 100% { transform: rotate(0deg); }
50% { transform: rotate(90deg); }
}
#da {
color: #111827 !important;
position: fixed;
top: 130px;
left: 50%;
transform: translateX(-50%);
display: none;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.95) 0%, rgba(240, 250, 255, 0.95) 100%);
padding: 14px 28px;
border-radius: 20px;
align-items: center;
gap: 16px;
font-weight: 700;
font-size: 1.15rem;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15), 0 0 0 2px rgba(0, 200, 100, 0.2);
z-index: 26;
backdrop-filter: blur(8px);
border: 1px solid rgba(0, 200, 100, 0.3);
}
#da.on {
display: flex;
animation: daPulse 2s ease-in-out infinite;
}
@keyframes daPulse {
0%,
100% {
transform: translateX(-50%) scale(1);
}
50% {
transform: translateX(-50%) scale(1.02);
}
}
.da-arrow-icon {
width: 44px;
height: 44px;
background: linear-gradient(135deg, #00c851 0%, #00a854 100%);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 12px rgba(0, 200, 81, 0.4);
}
.da-arrow-icon svg {
width: 26px;
height: 26px;
color: white;
}
.dal {
font-weight: 700;
letter-spacing: 0.5px;
color: #1a1a1a;
}
.da-dist {
font-size: 0.85rem;
color: #00a854;
font-weight: 800;
background: rgba(0, 200, 81, 0.1);
padding: 4px 10px;
border-radius: 8px;
}
#da.on {
display: flex;
}
/* Modals */
#mgo {
position: fixed;
inset: 0;
z-index: 60;
background: rgba(250, 249, 246, 0.8);
backdrop-filter: blur(4px);
display: none;
align-items: center;
justify-content: center;
padding: 24px;
}
#mgo.on {
display: flex;
}
.mgc {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--r);
padding: 40px;
max-width: 440px;
width: 100%;
text-align: center;
box-shadow: 0 24px 48px rgba(0, 0, 0, 0.1);
}
.mgt {
font-family: 'Lora', serif;
font-size: 1.8rem;
font-weight: 600;
margin-bottom: 12px;
}
.mgd {
color: var(--muted2);
font-size: 1rem;
line-height: 1.5;
margin-bottom: 24px;
}
.mgb {
display: flex;
gap: 12px;
justify-content: center;
flex-wrap: wrap;
margin-top: 16px;
}
/* MOBILE RESPONSIVE UI & ON-SCREEN CONTROLS */
#mobile-controls {
display: none;
position: fixed;
bottom: 20px;
left: 0;
right: 0;
padding: 0 20px;
justify-content: space-between;
z-index: var(--z-hud);
pointer-events: none;
}
@media (max-width: 900px) {
#mobile-controls.on {
display: flex;
}
}
@media (min-width: 901px) {
#mobile-controls.on {
display: none !important;
}
}
.mc-col {
display: flex;
gap: 10px;
pointer-events: auto;
}
.mc-btn {
background: rgba(0, 0, 0, 0.4);
color: white;
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
width: 70px;
height: 70px;
font-size: 24px;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
-webkit-user-select: none;
}
.mc-btn:active {
background: rgba(255, 255, 255, 0.4);
}
@media (max-width: 768px) {
.lv-track,
.bgrid,
.stats-body {
grid-template-columns: 1fr;
}
.br-w {
flex-direction: column;
padding: 0 16px;
margin: 20px auto;
gap: 20px;
}
.br-l,
.br-r {
flex: none;
width: 100%;
}
.br-l {
max-width: 100%;
}
.br-r {
position: static;
align-items: center;
}
.tb {
padding: 0 16px;
}
.grid {
grid-template-columns: 1fr;
gap: 12px;
}
.card {
padding: 16px;
margin: 10px;
}
h1 {
font-size: 1.8rem;
}
h2 {
font-size: 1.4rem;
}
.screen {
padding: 10px;
}
/* Show mobile controls when HUD is active (handled by JS) */
}
/* Screen transitions */
.screen {
animation: none;
}
.screen.active {
animation: screenFadeIn 0.35s ease-out forwards;
}
@keyframes screenFadeIn {
0% {
opacity: 0;
transform: translateY(12px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
/* Card entrance animations */
.lcard {
animation: cardSlideUp 0.4s ease-out backwards;
}
.lcard:nth-child(1) {
animation-delay: 0.05s;
}
.lcard:nth-child(2) {
animation-delay: 0.1s;
}
.lcard:nth-child(3) {
animation-delay: 0.15s;
}
.lcard:nth-child(4) {
animation-delay: 0.2s;
}
.lcard:nth-child(5) {
animation-delay: 0.25s;
}
@keyframes cardSlideUp {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
/* Syllabus item animations */
.syl-item {
animation: sylSlide 0.3s ease-out backwards;
}
.syl-item:nth-child(1) {
animation-delay: 0.05s;
}
.syl-item:nth-child(2) {
animation-delay: 0.1s;
}
.syl-item:nth-child(3) {
animation-delay: 0.15s;
}
.syl-item:nth-child(4) {
animation-delay: 0.2s;
}
.syl-item:nth-child(5) {
animation-delay: 0.25s;
}
@keyframes sylSlide {
0% {
opacity: 0;
transform: translateX(-12px);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
/* Button hover pulse */
.btn:hover {
transform: translateY(-2px) scale(1.02);
}
.btn:active {
transform: translateY(0) scale(0.98);
}
/* Toast animation improvement */
#toast {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Challan card stack */
#challan-stack {
position: fixed;
bottom: 100px;
right: 12px;
z-index: 28;
pointer-events: none;
display: none;
width: 100px;
}
#challan-stack.on {
display: block;
}
.challan-card {
pointer-events: none;
transition: all 0.3s;
animation: cardSlideUp 0.3s ease-out;
position: relative;
margin-top: -55px;
}
.challan-card:first-child {
margin-top: 0;
}
.challan-card .cc-off {
font-size: 0.75rem;
font-weight: 600;
color: var(--red);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.challan-card .cc-amt {
font-size: 0.85rem;
font-weight: 700;
color: var(--text);
}
/* Fluid HUD Elements */
#objective-overlay {
position: fixed;
top: 8vh;
right: 3vw;
left: auto;
transform: translateX(150%);
z-index: 23;
background: rgba(15, 23, 42, 0.6);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 16px;
padding: 2vh 3vw;
color: white;
max-width: 90vw;
width: clamp(280px, 30vw, 400px);
text-align: center;
transition: transform 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
display: flex;
flex-direction: column;
gap: 0.5vw;
pointer-events: none;
font-size: clamp(0.7rem, 1.2vw, 1rem);
}
#task-tracker {
position: fixed;
top: calc(3vw + 52px);
right: 3vw;
left: auto;
z-index: 23;
background: rgba(15, 23, 42, 0.6);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 16px;
padding: 2vh 3vw;
color: white;
width: clamp(240px, 30vw, 280px);
max-width: min(280px, 85vw);
max-height: 40vh;
overflow-y: auto;
display: none;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
font-size: clamp(0.7rem, 1.2vw, 0.85rem);
}
/* Ensure proper separation between overlapping elements —
#dn-clock sits at top:3vw/right:3vw when visible (night-cycle levels), so
#task-tracker is pushed down below it at every breakpoint where both stay
anchored to the top-right corner, instead of overlapping it. */
@media (max-width: 768px) {
#task-tracker {
top: calc(2vh + 52px);
}
#objective-overlay {
top: auto;
bottom: 120px;
}
}
#dn-clock {
position: fixed;
top: 3vw;
right: 3vw;
z-index: 25;
display: none;
align-items: center;
gap: 0.5vw;
background: rgba(255, 255, 255, 0.92);
backdrop-filter: blur(12px);
padding: 0.5vh 1.5vw;
border-radius: 20px;
border: 1px solid var(--border);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
font-family: 'Space Mono', monospace;
font-weight: 700;
font-size: clamp(0.7rem, 1.1vw, 0.9rem);
}
#spgauge {
position: fixed;
right: 3vw;
bottom: 3vw;
background: var(--card);
border: 1px solid var(--border);
border-radius: 50%;
padding: 0.5vw;
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.1);
z-index: 25;
display: none;
width: clamp(56px, 6vw, 100px);
height: clamp(56px, 6vw, 100px);
}
#boostgauge {
position: fixed;
right: 3vw;
bottom: 12vh;
background: var(--card);
border: 1px solid var(--border);
border-radius: 50%;
padding: 0.5vw;
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
z-index: 25;
display: none;
transition: box-shadow 0.3s;
width: clamp(48px, 5vw, 72px);
height: clamp(48px, 5vw, 72px);
}
/* --- Responsive UI Adjustments --- */
/* Make the root containers fluid and center them */
.sc {
width: 100%;
min-height: 100vh;
min-height: 100dvh;
margin: 0 auto;
padding: clamp(40px, 8vw, 120px) 20px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
justify-content: center;
position: relative;
z-index: 10;
box-sizing: border-box;
}
.st {
font-size: clamp(2.5rem, 6vw, 5.5rem);
line-height: 1.1;
letter-spacing: -0.02em;
margin-bottom: 24px;
font-family: 'Lora', serif;
font-weight: 700;
}
.su {
font-size: clamp(1rem, 2vw, 1.4rem);
color: var(--muted2);
line-height: 1.6;
max-width: 800px;
margin: 0 auto 40px auto;
font-weight: 400;
}
/* Level grid: ensure it doesn't overflow on tiny screens */
.lv-track {
grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr));
}
/* Briefing screen: stack on smaller screens */
@media (max-width: 900px) {
.br-w {
flex-direction: column;
padding: 0 20px;
margin: 20px auto;
gap: 20px;
}
.br-l {
flex: 1 1 auto;
}
.br-r {
position: static;
margin-top: 20px;
}
.bc-card {
padding: 20px;
min-height: auto;
}
.stat-row {
flex-direction: column;
gap: 12px;
}
.bc-rule-txt {
font-size: 1.2rem;
}
}
/* Global screen padding for mobile */
/* Certificate mobile/tablet scaling — own breakpoint, wider than the general 600px
mobile block below, since the certificate's fixed width starts clipping on
anything under ~1100px, well before phone-sized breakpoints kick in. */
@media (max-width: 1100px) {
#cert-wrapper {
overflow-x: auto;
padding-bottom: 20px;
}
.crt {
transform-origin: top left;
transform: scale(min(1, calc((100vw - 40px) / 1056)));
}
}
@media (max-width: 600px) {
.tb {
padding: 16px 20px;
flex-direction: row;
flex-wrap: wrap;
gap: 12px;
}
.lw {
padding: 20px;
}
.sec-hdr {
font-size: 1.4rem;
margin: 20px 0 12px;
}
.qw {
padding: 0 16px;
margin: 20px auto;
}
.qcard {
padding: 24px 16px;
}
.qq {
font-size: 1.1rem;
flex-direction: column;
gap: 8px;
}
.res-w {
padding: 0 16px;
margin: 20px auto;
}
/* Mobile direction box */
#da {
top: 100px;
padding: 10px 18px;
font-size: 0.95rem;
gap: 10px;
}
.da-arrow-icon {
width: 36px;
height: 36px;
border-radius: 10px;
}
.da-arrow-icon svg {
width: 20px;
height: 20px;
}
.da-dist {
font-size: 0.75rem;
padding: 3px 8px;
}
.dal {
font-size: 0.9rem;
}
/* Mobile objective overlay */
#objective-overlay {
top: auto;
bottom: 100px;
padding: 12px 16px;
font-size: 0.85rem;
max-width: 90vw;
}
#objective-title {
font-size: 0.7rem;
}
#objective-desc {
font-size: 0.9rem;
}
}
/* Certificate specific responsiveness */
#cert-wrapper {
width: 100%;
display: flex;
justify-content: center;
overflow-x: auto;
flex-shrink: 0;
}
.crt {
/* Removed min-width and max-width to allow transform scaling without text overflow */
}
/* === COMPLETE MOBILE DRIVING UI REDESIGN === */
@media (max-width: 1400px),
(max-height: 800px),
(pointer: coarse) {
/* Ensure canvas fills screen without distortion */
#gc {
position: fixed;
inset: 0;
width: 100vw;
height: 100vh;
z-index: var(--z-canvas);
background: #000;
}
/* HUD — compact top-left cluster */
#hud {
top: 6px;
left: 6px;
right: auto;
padding: 0;
gap: 4px;
}
.hp {
padding: 3px 7px;
font-size: 0.7rem;
border-radius: 5px;
}
#hudbar {
top: 40px;
left: 6px;
right: auto;
gap: 4px;
padding: 0;
}
.hb {
padding: 3px 8px;
font-size: 0.65rem;
border-radius: 5px;
}
#hwrap {
top: 48px;
left: 4px;
right: auto;
flex-direction: row;
align-items: center;
gap: 4px;
padding: 2px 5px;
border-radius: 4px;
z-index: 25;
max-width: 34vw;
overflow-x: auto;
}
#hwrap .hl {
margin: 0;
font-size: 0.48rem;
}
#hwallet {
font-size: 0.65rem;
}
.ht {
width: 36px;
height: 4px;
}
#objective-overlay {
top: 80px;
right: 4px;
left: auto;
width: auto;
max-width: 32vw;
padding: 3px 6px;
z-index: 25;
max-height: 24vh;
overflow-y: auto;
font-size: 0.6rem;
}
#task-tracker {
top: 140px;
bottom: auto;
left: 4px;
right: auto;
width: auto;
max-width: 32vw;
max-height: 20vh;
font-size: 0.6rem;
padding: 5px;
}
#sig-ind {
top: 4px;
left: 50%;
transform: translateX(-50%);
font-size: 0.6rem;
padding: 2px 8px;
gap: 4px;
}
#sind-state {
min-width: 30px;
font-size: 0.7rem;
}
#sind-timer {
font-size: 0.72rem;
min-width: 18px;
}
/* Touch Controls (Center Bottom) */
#tc {
bottom: 6px;
left: 50%;
transform: translateX(-50%);
padding: 0;
gap: 8px;
justify-content: center;
}
.db {
width: 44px;
height: 44px;
font-size: 0.85rem;
border-radius: 8px;
}
/* Gear Selector (Vertical on Left) */
#gp.on {
display: flex;
}
#gp {
bottom: 50%;
left: 6px;
transform: translateY(50%);
flex-direction: column;
padding: 3px;
gap: 3px;
background: rgba(255, 255, 255, 0.55);
backdrop-filter: blur(8px);
border-radius: 10px;
}
.gb {
width: 36px;
height: 36px;
font-size: 0.7rem;
border-radius: 7px;
}
#gread {
bottom: 48px;
top: auto;
left: 6px;
transform: none;
font-size: 0.6rem;
padding: 2px 5px;
background: rgba(255, 255, 255, 0.75);
}
/* Mobile Controls Wrapper */
#mobile-controls {
bottom: 6px;
padding: 0 10px;
background: none;
}
/* Steering Wheel (Bottom Left) */
#steer-wheel-container {
width: 86px;
height: 86px;
border-width: 2px;
background: rgba(0, 0, 0, 0.25);
margin-left: 24px;
}
/* Pedals (Bottom Right) */
.mc-col {
gap: 8px;
}
.mc-btn {
width: 48px;
height: 48px;
font-size: 15px;
border-width: 2px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
}
#da {
display: none !important;
}
#mmc {
top: 4px;
right: 4px;
width: 64px;
height: 64px;
border-width: 2px;
z-index: 18;
}
#spgauge {
right: auto;
left: 6px;
bottom: 80px;
width: 56px;
height: 56px;
}
#spgauge svg {
width: 56px;
height: 56px;
}
#boostgauge {
right: 6px;
bottom: 150px;
}
#boostgauge svg {
width: 38px;
height: 38px;
}
#civic-controls {
top: 80px;
right: 4px;
bottom: auto;
left: auto;
gap: 2px;
}
#civic-controls .civic-btn {
padding: 3px 6px;
font-size: 0.65rem;
border-radius: 12px;
}
/* Phone GPS Button mobile sizing */
#phone-gps-btn {
width: 46px !important;
height: 46px !important;
font-size: 18px !important;
}
}
@media (min-width: 1401px) and (min-height: 801px) and (pointer: fine) {
#mobile-controls {
display: none !important;
}
}
/* Pause overlay — show/hide */
#pause-overlay.on {
display: flex !important;
}
@media (min-width: 400px) and (max-width: 768px) {
#task-tracker {
top: calc(24px + 48px);
right: 24px;
width: 240px;
max-width: 75vw;
padding: 12px 14px;
font-size: 0.78rem;
}
#objective-overlay {
top: auto;
bottom: 120px;
right: 24px;
font-size: 0.7rem;
max-width: 70vw;
padding: 8px 12px;
}
#phone-gps {
width: min(280px, 80vw);
height: min(280px, 80vw);
}
#kid-steer {
width: 120px;
height: 120px;
bottom: 16px;
}
#kid-pedals .kid-btn.go-btn,
#kid-pedals .kid-btn.stop-btn {
width: 54px;
height: 54px;
font-size: 1.5rem;
}
#kid-pedals .kid-btn.horn-btn {
width: 44px;
height: 44px;
}
#kid-hearts {
top: 40px;
right: 8px;
}
#kid-hearts .heart {
font-size: 1.1rem;
}
#kid-msg {
font-size: 0.95rem;
bottom: 180px;
}
#steer-wheel-container {
width: 72px;
height: 72px;
margin-left: 24px;
}
#civic-controls .civic-btn {
padding: 3px 5px;
font-size: 0.55rem;
}
#spgauge {
bottom: 72px;
width: 52px;
height: 52px;
}
#spgauge svg {
width: 52px;
height: 52px;
}
#boostgauge svg {
width: 34px;
height: 34px;
}
}
/* Very small screens: hide signal indicator */
@media (max-width: 400px) {
#sig-ind {
display: none !important;
}
}
/* Portrait mode: adaptive layout for mobile */
@media (orientation: portrait) and (max-width: 600px) {
#hwrap {
top: 4px;
left: 50%;
transform: translateX(-50%);
right: auto;
max-width: 70vw;
}
#objective-overlay {
bottom: 120px;
right: 16px;
left: auto;
max-width: 45vw;
max-height: 16vh;
}
#task-tracker {
top: calc(16px + 48px);
right: 16px;
left: auto;
bottom: auto;
max-width: 45vw;
max-height: 14vh;
}
#sig-ind {
top: 4px;
left: 4px;
transform: none;
}
#steer-wheel-container {
width: 80px;
height: 80px;
margin-left: 16px;
bottom: 8px;
}
#tc {
bottom: 8px;
right: 16px;
left: auto;
transform: none;
}
#gp {
left: 4px;
bottom: 100px;
transform: none;
}
#spgauge {
bottom: 100px;
left: 4px;
}
#boostgauge {
bottom: 100px;
right: 4px;
}
#mmc {
top: 4px;
right: 4px;
width: 52px;
height: 52px;
}
#civic-controls {
top: 60px;
right: 4px;
}
#da {
display: none !important;
}
#phone-gps {
top: 50%;
right: 4px;
transform: translateY(-50%);
width: 140px;
height: 180px;
}
.mc-btn {
width: 44px;
height: 44px;
font-size: 14px;
}
}
/* Phone GPS Overlay */
#phone-gps {
display: none;
position: fixed;
top: 80px;
right: 12px;
width: 180px;
height: 240px;
background: rgba(10, 15, 30, 0.88);
backdrop-filter: blur(10px);
border: 1px solid rgba(94, 212, 245, 0.35);
border-radius: 20px;
z-index: 30;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.05);
color: white;
font-family: 'Inter', sans-serif;
user-select: none;
-webkit-user-select: none;
touch-action: none;
padding: 14px 10px;
}
#phone-gps.on {
display: flex;
}
#phone-gps-compass {
position: relative;
width: 100px;
height: 100px;
border-radius: 50%;
border: 2px solid rgba(94, 212, 245, 0.4);
background: rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
#phone-gps-compass::before {
content: '';
position: absolute;
inset: 4px;
border-radius: 50%;
border: 1px solid rgba(94, 212, 245, 0.15);
}
#phone-gps-arrow {
transition: transform 0.12s ease-out;
display: flex;
align-items: center;
justify-content: center;
}
#phone-gps-dist {
font-size: 1.1rem;
font-weight: 800;
color: #5ed4f5;
letter-spacing: 0.5px;
font-variant-numeric: tabular-nums;
}
#phone-gps-dir {
font-size: 0.7rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1.5px;
color: rgba(255, 255, 255, 0.65);
}
#phone-gps-obj {
font-size: 0.65rem;
color: rgba(255, 255, 255, 0.45);
text-align: center;
line-height: 1.3;
max-height: 36px;
overflow: hidden;
}
#phone-gps-close {
position: absolute;
top: 6px;
right: 8px;
width: 36px;
height: 36px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
border: none;
color: rgba(255, 255, 255, 0.5);
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
line-height: 1;
}
#phone-gps-close::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 44px;
height: 44px;
transform: translate(-50%, -50%);
}
/* Phone GPS Button (in mobile controls) */
#phone-gps-btn {
width: 56px;
height: 56px;
background: rgba(94, 212, 245, 0.25);
color: #5ed4f5;
border: 2px solid rgba(94, 212, 245, 0.4);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
-webkit-user-select: none;
font-size: 22px;
flex-shrink: 0;
align-self: flex-end;
box-shadow: 0 4px 12px rgba(94, 212, 245, 0.15);
transition: background 0.15s;
}
#phone-gps-btn:active {
background: rgba(94, 212, 245, 0.5);
}
@media (min-width: 901px) {
#phone-gps {
top: 100px;
right: 20px;
}
}
/* === KID MODE — Phase 2: Simplified UI for 4-5 year olds === */
.kid-hide {
display: none !important;
}
/* Big colorful kid buttons — min 60px touch target */
.kid-btn {
min-width: 64px;
min-height: 64px;
border-radius: 20px;
border: 3px solid rgba(255, 255, 255, 0.5);
font-size: 2rem;
font-weight: 800;
cursor: pointer;
user-select: none;
-webkit-user-select: none;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
gap: 2px;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
transition: transform 0.1s, box-shadow 0.1s;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
-webkit-tap-highlight-color: transparent;
}
.kid-btn:active {
transform: scale(0.92);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}
.kid-btn-label {
font-size: 0.6rem;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
text-shadow: none;
}
/* Kid steering wheel — bigger, centered */
#kid-steer {
display: none;
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
width: 180px;
height: 180px;
border-radius: 50%;
border: 6px solid rgba(255, 255, 255, 0.6);
background: radial-gradient(circle, rgba(59, 130, 246, 0.5) 0%, rgba(59, 130, 246, 0.2) 60%, transparent 100%);
z-index: var(--z-hud);
pointer-events: auto;
touch-action: none;
}
#kid-steer::after {
content: '🎯';
font-size: 3rem;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
opacity: 0.7;
}
#kid-steer.touching {
border-color: rgba(255, 255, 255, 0.9);
background: radial-gradient(circle, rgba(59, 130, 246, 0.7) 0%, rgba(59, 130, 246, 0.35) 60%, transparent 100%);
}
/* Kid pedal buttons — right side */
#kid-pedals {
display: none;
position: fixed;
bottom: 30px;
right: 24px;
z-index: var(--z-hud);
pointer-events: auto;
flex-direction: column;
gap: 16px;
align-items: center;
}
#kid-pedals .kid-btn.go-btn {
width: 80px;
height: 80px;
border-radius: 50%;
background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%);
color: white;
font-size: 2.2rem;
}
#kid-pedals .kid-btn.stop-btn {
width: 80px;
height: 80px;
border-radius: 50%;
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
color: white;
font-size: 2.2rem;
}
#kid-pedals .kid-btn.horn-btn {
width: 64px;
height: 64px;
border-radius: 50%;
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
color: white;
font-size: 2rem;
}
/* Kid indicator button — left side */
#kid-indicators {
display: none;
position: fixed;
right: 16px;
top: 50%;
transform: translateY(-50%);
z-index: var(--z-hud);
flex-direction: column;
gap: 10px;
transition: opacity 0.3s ease;
opacity: 0;
}
#kid-indicators.on {
opacity: 1;
}
#kid-indicators .kid-btn {
width: 64px;
height: 64px;
border-radius: 50%;
font-size: 1.8rem;
}
.kid-left-btn {
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
color: white;
}
.kid-right-btn {
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
color: white;
}
.kid-headlight-btn {
background: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%);
color: white;
}
/* Kid locked control — hidden with lock */
.kid-locked {
opacity: 0.25 !important;
pointer-events: none !important;
filter: grayscale(0.8);
position: relative;
}
.kid-locked::after {
content: '🔒';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1.4rem;
filter: none;
}
/* Kid unlock notification */
#kid-unlock-toast {
display: none;
position: fixed;
bottom: 280px;
left: 50%;
transform: translateX(-50%);
z-index: var(--z-kid-toast);
font-size: 1.3rem;
font-weight: 800;
color: white;
background: linear-gradient(135deg, #22c55e, #16a34a);
padding: 12px 32px;
border-radius: 40px;
backdrop-filter: blur(8px);
box-shadow: 0 4px 20px rgba(34, 197, 94, 0.5);
animation: kidUnlockPop 0.4s ease-out;
pointer-events: none;
white-space: nowrap;
}
@keyframes kidUnlockPop {
0% {
transform: translateX(-50%) scale(0.5);
opacity: 0;
}
60% {
transform: translateX(-50%) scale(1.1);
}
100% {
transform: translateX(-50%) scale(1);
opacity: 1;
}
}
/* Kid health hearts — big, prominent */
#kid-hearts {
display: none;
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
z-index: var(--z-hud);
font-size: 2.5rem;
gap: 8px;
text-shadow: 0 3px 8px rgba(0, 0, 0, 0.3);
pointer-events: none;
background: rgba(0, 0, 0, 0.3);
padding: 8px 20px;
border-radius: 40px;
backdrop-filter: blur(8px);
transition: opacity 0.3s ease;
}
/* Kid level name display */
#kid-level-name {
display: none;
position: fixed;
top: 80px;
left: 50%;
transform: translateX(-50%);
z-index: var(--z-hud);
font-size: 1.1rem;
font-weight: 800;
color: white;
text-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
background: rgba(59, 130, 246, 0.6);
padding: 6px 24px;
border-radius: 30px;
pointer-events: none;
backdrop-filter: blur(6px);
white-space: nowrap;
}
/* Kid task indicator — visual emoji card */
#kid-task-bar {
display: none;
position: fixed;
top: 120px;
left: 50%;
transform: translateX(-50%);
z-index: var(--z-hud);
background: rgba(0, 0, 0, 0.6);
padding: 14px 28px 12px;
border-radius: 28px;
pointer-events: none;
backdrop-filter: blur(10px);
max-width: 85vw;
border: 2px solid rgba(255, 255, 255, 0.15);
text-align: center;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
transition: opacity 0.35s ease, transform 0.35s ease;
opacity: 0;
}
#kid-task-bar.on {
opacity: 1;
}
#kid-task-bar .task-emoji {
font-size: 3rem;
line-height: 1.1;
filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.3));
animation: taskEmojiBounce 2s ease-in-out infinite;
}
@keyframes taskEmojiBounce {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-4px);
}
}
#kid-task-bar .task-text {
color: white;
font-size: 1.3rem;
font-weight: 800;
text-align: center;
margin-top: 4px;
margin-bottom: 6px;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
font-family: var(--sans);
}
#kid-task-bar .task-progress {
display: flex;
justify-content: center;
gap: 2px;
font-size: 1.1rem;
}
#kid-task-bar .task-progress .star {
opacity: 0.25;
transition: opacity 0.3s, transform 0.3s;
}
#kid-task-bar .task-progress .star.filled {
opacity: 1;
transform: scale(1.15);
}
/* Progressive HUD hide — applied to body.kid-lvl-N class */
body.kid-lvl-low #mmc {
display: none !important;
}
body.kid-lvl-low #phone-gps {
display: none !important;
}
/* Kid countdown — big, centered, colorful */
#kid-countdown {
display: none;
position: fixed;
inset: 0;
z-index: var(--z-overlay);
background: rgba(0, 0, 0, 0.6);
align-items: center;
justify-content: center;
backdrop-filter: blur(4px);
}
#kid-countdown .count-num {
font-size: 10rem;
font-weight: 900;
color: white;
text-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
animation: kidCountPulse 0.8s ease-in-out;
}
@keyframes kidCountPulse {
0% {
transform: scale(2);
opacity: 0;
}
50% {
transform: scale(0.9);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 1;
}
}
/* Kid GO! flash */
#kid-go-flash {
display: none;
position: fixed;
inset: 0;
z-index: var(--z-overlay);
background: rgba(0, 0, 0, 0.5);
align-items: center;
justify-content: center;
backdrop-filter: blur(4px);
}
#kid-go-flash .go-text {
font-size: 8rem;
font-weight: 900;
background: linear-gradient(135deg, #22c55e, #4ade80);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: none;
filter: drop-shadow(0 8px 24px rgba(34, 197, 94, 0.5));
animation: kidGoPulse 0.6s ease-out forwards;
}
@keyframes kidGoPulse {
0% {
transform: scale(0.5) rotate(-5deg);
opacity: 0;
}
60% {
transform: scale(1.2) rotate(2deg);
opacity: 1;
}
100% {
transform: scale(1) rotate(0deg);
opacity: 0;
}
}
/* Kid tutorial overlay — big arrows + simple text */
#kid-tutorial {
display: none;
position: fixed;
inset: 0;
z-index: var(--z-overlay);
background: rgba(0, 0, 0, 0.7);
align-items: center;
justify-content: center;
flex-direction: column;
gap: 24px;
padding: 40px;
backdrop-filter: blur(6px);
}
#kid-tutorial .tut-emoji {
font-size: 6rem;
animation: kidBounce 1s ease-in-out infinite alternate;
}
@keyframes kidBounce {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-20px);
}
}
#kid-tutorial .tut-text {
color: white;
font-size: 1.6rem;
font-weight: 800;
text-align: center;
max-width: 500px;
line-height: 1.4;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
}
#kid-tutorial .tut-arrow {
font-size: 3rem;
animation: kidArrowBounce 1.2s ease-in-out infinite alternate;
}
@keyframes kidArrowBounce {
0% {
transform: translateX(0);
}
100% {
transform: translateX(20px);
}
}
#kid-tutorial .kid-btn.tut-ok {
min-width: 120px;
min-height: 60px;
font-size: 1.4rem;
background: linear-gradient(135deg, #22c55e, #16a34a);
color: white;
margin-top: 16px;
}
/* Kid level intro card — 5s visual demo before gameplay */
#kid-intro {
display: none;
position: fixed;
inset: 0;
z-index: var(--z-overlay);
background: rgba(0, 0, 0, 0.75);
align-items: center;
justify-content: center;
flex-direction: column;
gap: 16px;
padding: 40px;
backdrop-filter: blur(6px);
opacity: 0;
transition: opacity 0.4s ease;
}
#kid-intro.on {
opacity: 1;
}
#kid-intro .intro-emoji {
font-size: 5rem;
animation: introFloat 1.5s ease-in-out infinite alternate;
}
@keyframes introFloat {
0% {
transform: translateY(0) scale(1);
}
100% {
transform: translateY(-15px) scale(1.05);
}
}
#kid-intro .intro-text {
color: white;
font-size: 1.4rem;
font-weight: 700;
text-align: center;
max-width: 440px;
line-height: 1.5;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
}
#kid-intro .intro-level {
color: rgba(255, 255, 255, 0.6);
font-size: 1rem;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
}
#kid-intro .intro-progress {
display: flex;
gap: 8px;
margin-top: 8px;
}
#kid-intro .intro-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.3);
transition: background 0.3s, transform 0.3s;
}
#kid-intro .intro-dot.active {
background: #22c55e;
transform: scale(1.3);
}
/* Animated hand pointer — points at the control being taught */
#kid-hand {
display: none;
position: fixed;
z-index: 100001;
font-size: 3.5rem;
filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.5));
pointer-events: none;
animation: kidHandPoint 0.8s ease-in-out infinite alternate;
transition: left 0.4s ease, top 0.4s ease;
}
@keyframes kidHandPoint {
0% {
transform: translate(-50%, 0) scale(1);
}
50% {
transform: translate(-50%, -12px) scale(1.15);
}
100% {
transform: translate(-50%, 0) scale(1);
}
}
/* Kid celebration — stars + confetti */
#kid-celebrate {
display: none;
position: fixed;
inset: 0;
z-index: var(--z-modal);
background: rgba(0, 0, 0, 0.5);
align-items: center;
justify-content: center;
flex-direction: column;
gap: 16px;
backdrop-filter: blur(4px);
}
#kid-celebrate .cel-stars {
font-size: 5rem;
animation: kidStars 0.8s ease-out;
}
@keyframes kidStars {
0% {
transform: scale(0) rotate(-30deg);
opacity: 0;
}
60% {
transform: scale(1.3) rotate(5deg);
opacity: 1;
}
100% {
transform: scale(1) rotate(0deg);
opacity: 1;
}
}
#kid-celebrate .cel-text {
font-size: 2.5rem;
font-weight: 900;
color: #fbbf24;
text-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
#kid-celebrate .cel-sub {
font-size: 1.1rem;
color: white;
font-weight: 600;
text-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}
#kid-celebrate .kid-btn.cel-ok {
min-width: 140px;
min-height: 60px;
font-size: 1.4rem;
background: linear-gradient(135deg, #3b82f6, #2563eb);
color: white;
margin-top: 20px;
}
/* Kid crash screen — friendly Oops! */
#kid-crash {
display: none;
position: fixed;
inset: 0;
z-index: var(--z-overlay);
background: rgba(0, 0, 0, 0.6);
align-items: center;
justify-content: center;
flex-direction: column;
gap: 16px;
padding: 40px;
backdrop-filter: blur(6px);
}
#kid-crash .crash-emoji {
font-size: 6rem;
}
#kid-crash .crash-text {
font-size: 2.5rem;
font-weight: 900;
color: #fbbf24;
text-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
#kid-crash .crash-sub {
font-size: 1.1rem;
color: white;
font-weight: 600;
text-align: center;
max-width: 400px;
line-height: 1.4;
text-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}
#kid-crash .kid-btn.crash-retry {
min-width: 160px;
min-height: 64px;
font-size: 1.5rem;
background: linear-gradient(135deg, #22c55e, #16a34a);
color: white;
margin-top: 12px;
}
#kid-crash .kid-btn.crash-home {
min-width: 120px;
min-height: 52px;
font-size: 1.1rem;
background: rgba(255, 255, 255, 0.15);
color: white;
border: 2px solid rgba(255, 255, 255, 0.3);
}
/* Kid off-road warning — simple shake */
#kid-offroad {
display: none;
position: fixed;
bottom: 240px;
left: 50%;
transform: translateX(-50%);
z-index: var(--z-kid-toast);
font-size: 1.2rem;
font-weight: 800;
color: white;
background: rgba(239, 68, 68, 0.8);
padding: 10px 28px;
border-radius: 30px;
pointer-events: none;
backdrop-filter: blur(6px);
animation: kidShake 0.3s ease-in-out;
}
@keyframes kidShake {
0%,
100% {
transform: translateX(-50%) rotate(0);
}
25% {
transform: translateX(-50%) rotate(-3deg);
}
75% {
transform: translateX(-50%) rotate(3deg);
}
}
/* Kid start screen overrides */
.kid-mode #ss .sc {
gap: 12px;
}
.kid-mode #ss .st {
font-size: clamp(2rem, 5vw, 3.5rem);
background: linear-gradient(135deg, #3b82f6, #8b5cf6, #ec4899);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.kid-mode #ss .su {
font-size: clamp(0.9rem, 1.8vw, 1.1rem);
max-width: 500px;
}
.kid-mode #ss .pls {
flex-direction: column;
align-items: center;
gap: 16px;
}
.kid-mode #ss .pls .btn {
min-width: 220px;
min-height: 56px;
font-size: 1.3rem;
border-radius: 20px;
}
.kid-mode #ss #btn-diff,
.kid-mode #ss #btn-weather,
.kid-mode #ss .nav-user-profile {
display: none !important;
}
/* Kid level cards — bigger, more colorful */
.kid-mode .lcard {
min-height: 120px;
border-radius: 16px;
padding: 20px;
}
.kid-mode .lcard .ln {
font-size: 1.3rem;
}
.kid-mode .lcard .ls {
font-size: 0.85rem;
}
.kid-mode .lcard.locked {
opacity: 0.4;
pointer-events: none;
}
.kid-mode .lcard.current {
border-color: #3b82f6;
box-shadow: 0 0 20px rgba(59, 130, 246, 0.3);
}
/* === KID-MODE: Start screen simplification === */
.kid-mode #ss .pls .btn-g,
.kid-mode #ss .btn-g[onclick*="hardReset"],
.kid-mode #ss .btn-s[onclick*="prompt"],
.kid-mode #ss .btn-g.mobile-dl {
display: none !important;
}
.kid-mode #ss .pls .btn-p {
min-width: 280px;
min-height: 64px;
font-size: 1.8rem;
border-radius: 24px;
background: linear-gradient(135deg, #3b82f6, #8b5cf6) !important;
color: #fff !important;
border: none !important;
box-shadow: 0 8px 32px rgba(59, 130, 246, 0.4);
animation: kidPulse 2s ease-in-out infinite;
letter-spacing: 0.05em;
}
@keyframes kidPulse {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
}
.kid-mode #ss .brand-header {
display: none !important;
}
.kid-mode #ss .su {
font-size: clamp(0.85rem, 1.6vw, 1rem);
max-width: 400px;
line-height: 1.5;
}
/* Level selection: hide profile + progress chip */
.kid-mode #screen-levels .tb .btn-g,
.kid-mode #screen-levels #pchip {
display: none !important;
}
.kid-mode #screen-levels .back-btn {
min-width: 48px;
min-height: 48px;
font-size: 1rem;
}
/* Briefing: hide profile */
.kid-mode #screen-briefing .tb .btn-g {
display: none !important;
}
/* Confetti particle */
.kid-confetti {
position: fixed;
width: 10px;
height: 10px;
border-radius: 2px;
z-index: var(--z-modal);
pointer-events: none;
animation: confettiFall 1.5s ease-out forwards;
}
@keyframes confettiFall {
0% {
transform: translateY(0) rotate(0deg);
opacity: 1;
}
100% {
transform: translateY(100vh) rotate(720deg);
opacity: 0;
}
}
/* --- Age-adaptive visuals --- */
/* Child tier: bigger HUD, simpler info, hide numeric speed */
[data-age-tier="child"] #kid-hearts {
font-size: 3.2rem;
padding: 10px 28px;
}
[data-age-tier="child"] #kid-task-bar .task-emoji {
font-size: 4rem;
}
[data-age-tier="child"] #kid-task-bar .task-text {
font-size: 1.6rem;
}
[data-age-tier="child"] #gspd {
display: none;
}
[data-age-tier="child"] #garc {
display: none;
}
[data-age-tier="child"] #htmr {
font-size: 1rem;
opacity: 0.6;
}
[data-age-tier="child"] #hfin {
display: none;
}
[data-age-tier="child"] #speed-lines {
display: none !important;
}
/* Teen tier: slightly larger, keep all info */
[data-age-tier="teen"] #kid-hearts {
font-size: 2.8rem;
}
[data-age-tier="teen"] #kid-task-bar .task-text {
font-size: 1.4rem;
}
/* Senior tier: larger text, higher contrast */
[data-age-tier="senior"] #kid-hearts {
font-size: 3rem;
}
[data-age-tier="senior"] #kid-task-bar .task-text {
font-size: 1.5rem;
}
[data-age-tier="senior"] #htmr {
font-size: 1.2rem;
font-weight: 800;
}
/* Adult and young: default sizing, all HUD visible (no overrides needed) */
/* Landscape phone: compact HUD for short viewports */
@media (max-height: 450px) and (orientation: landscape) {
#objective-overlay {
top: auto;
bottom: 8px;
right: 8px;
max-width: 35vw;
padding: 6px 10px;
font-size: clamp(0.55rem, 1vw, 0.7rem);
}
#task-tracker {
top: 4px;
right: 8px;
max-width: 35vw;
max-height: 30vh;
padding: 4px 8px;
font-size: clamp(0.5rem, 0.9vw, 0.65rem);
}
#dn-clock {
top: 4px;
left: 8px;
right: auto;
padding: 2px 8px;
font-size: clamp(0.55rem, 1vw, 0.7rem);
}
#spgauge {
bottom: 4px;
left: 8px;
right: auto;
width: clamp(40px, 5vw, 56px);
height: clamp(40px, 5vw, 56px);
padding: 3px;
}
#spgauge svg {
width: clamp(40px, 5vw, 56px);
height: clamp(40px, 5vw, 56px);
}
#boostgauge {
right: 8px;
bottom: auto;
top: 36px;
width: clamp(32px, 4vw, 48px);
height: clamp(32px, 4vw, 48px);
padding: 3px;
}
#boostgauge svg {
width: clamp(32px, 4vw, 48px);
height: clamp(32px, 4vw, 48px);
}
#hwrap {
top: 4px;
left: 4px;
padding: 4px 8px;
}
#sig-ind {
top: 2px;
padding: 2px 6px;
font-size: 0.55rem;
}
#steer-wheel-container {
width: 60px;
height: 60px;
}
#civic-controls {
top: 40px;
right: 4px;
}
#civic-controls .civic-btn {
padding: 2px 6px;
font-size: 0.55rem;
}
#tc {
bottom: 4px;
}
.db {
width: 30px;
height: 30px;
font-size: 0.7rem;
}
#gp {
bottom: auto;
top: 50%;
transform: translateY(-50%);
}
#hwallet {
font-size: 0.6rem;
}
#hwrap .hl {
font-size: 0.45rem;
}
.ht {
width: 28px;
height: 3px;
}
}
/* Perf: disable backdrop-filter on mobile (GPU compositing overhead) */
@media (max-width: 600px) {
* {
-webkit-backdrop-filter: none !important;
backdrop-filter: none !important;
}
}
/* ── Light mode overrides (matches parent site pattern) ── */
body.lm {
--bg: #f3f2eb;
--card: rgba(255, 255, 255, 0.45);
--text: #1a1a2e;
--border: rgba(0, 0, 0, 0.15);
--hover: rgba(0, 0, 0, 0.05);
--muted: #475569;
--muted2: #94a3b8;
--accent: #f2b84b;
--green: #10b981;
--red: #dc2626;
--yellow: #f2b84b;
--signal: #0369a1;
}
/* ── Horizon Toggle ── */
.hz-toggle {
position: relative;
width: 369px;
max-width: calc(100vw - 40px);
height: 145px;
border-radius: 72.5px;
cursor: pointer;
overflow: hidden;
flex-shrink: 0;
background: linear-gradient(180deg, #3B82F6 0%, #60A5FA 25%, #93C5FD 50%, #FDE68A 80%, #F59E0B 100%);
transition: background 0.7s cubic-bezier(0.4,0,0.2,1);
box-shadow: inset 0 2px 6px rgba(0,0,0,0.15), 0 2px 8px rgba(0,0,0,0.12);
}
.hz-toggle.night {
background: linear-gradient(180deg, #070B1A 0%, #0F172A 25%, #1E293B 50%, #334155 80%, #1E1B4B 100%);
}
.hz-toggle:active { transform: scale(0.97); }
.hz-stars {
position: absolute; inset: 0;
border-radius: inherit;
opacity: 0;
transition: opacity 0.7s ease;
pointer-events: none;
box-shadow:
60px 18px 0 1px #fff, 120px 35px 0 0px #fff, 185px 15px 0 0.5px #fff,
230px 40px 0 0px #fff, 290px 22px 0 1px #fff, 340px 45px 0 0px #fff,
85px 50px 0 0.5px #fff, 155px 8px 0 0px #fff, 210px 55px 0 0px #fff,
310px 12px 0 1px #fff, 355px 28px 0 0px #fff, 40px 42px 0 0px #fff,
100px 62px 0 0.5px #fff, 175px 72px 0 0px #fff, 250px 18px 0 0px #fff,
50px 28px 0 0.5px #fff, 130px 48px 0 0px #fff, 200px 32px 0 1px #fff,
275px 58px 0 0px #fff, 320px 38px 0 0px #fff;
}
.hz-toggle.night .hz-stars { opacity: 1; }
/* Clouds — day-mode counterpart to the stars (same as Academy.html) */
.hz-clouds {
position: absolute; inset: 0;
border-radius: inherit;
opacity: 1;
transition: opacity 0.7s ease;
pointer-events: none;
}
.hz-toggle.night .hz-clouds { opacity: 0; }
.hz-clouds::before, .hz-clouds::after {
content: '';
position: absolute;
top: 22px;
width: 46px; height: 18px;
background: rgba(255,255,255,0.85);
border-radius: 999px;
box-shadow:
16px -8px 0 -2px rgba(255,255,255,0.85),
-14px -4px 0 -3px rgba(255,255,255,0.75),
28px 2px 0 -4px rgba(255,255,255,0.7);
}
.hz-clouds::before { left: 40px; }
.hz-clouds::after { left: 230px; top: 46px; transform: scale(0.8); }
@keyframes hz-twinkle {
0%,100% { opacity: 1; }
50% { opacity: 0.4; }
}
.hz-stars::before {
content: '';
position: absolute;
width: 2px; height: 2px;
background: white;
border-radius: 50%;
top: 30px; left: 95px;
box-shadow: 0 0 4px 1px rgba(255,255,255,0.6);
animation: hz-twinkle 3s ease-in-out infinite;
}
.hz-stars::after {
content: '';
position: absolute;
width: 2px; height: 2px;
background: white;
border-radius: 50%;
top: 50px; left: 260px;
box-shadow: 0 0 4px 1px rgba(255,255,255,0.6);
animation: hz-twinkle 3s ease-in-out 1.5s infinite;
}
.hz-skyline {
position: absolute;
bottom: 0; left: 0; right: 0;
height: 38px;
background: #1E3A5F;
clip-path: polygon(
0% 100%, 0% 60%, 3% 60%, 3% 30%, 6% 30%, 6% 55%, 9% 55%,
9% 40%, 12% 40%, 12% 20%, 15% 20%, 15% 40%, 18% 40%,
18% 50%, 22% 50%, 22% 25%, 25% 25%, 25% 45%, 28% 45%,
28% 35%, 31% 35%, 31% 15%, 34% 15%, 34% 35%, 37% 35%,
37% 55%, 40% 55%, 40% 30%, 43% 30%, 43% 10%, 46% 10%,
46% 30%, 49% 30%, 49% 50%, 53% 50%, 53% 20%, 56% 20%,
56% 45%, 59% 45%, 59% 35%, 62% 35%, 62% 5%, 65% 5%,
65% 35%, 68% 35%, 68% 55%, 71% 55%, 71% 40%, 74% 40%,
74% 15%, 77% 15%, 77% 40%, 80% 40%, 80% 55%, 83% 55%,
83% 25%, 86% 25%, 86% 50%, 89% 50%, 89% 35%, 92% 35%,
92% 60%, 95% 60%, 95% 45%, 98% 45%, 98% 70%, 100% 70%, 100% 100%
);
transition: background 0.7s cubic-bezier(0.4,0,0.2,1);
}
.hz-toggle.night .hz-skyline { background: #0B1120; }
.hz-windows {
position: absolute;
bottom: 4px; left: 0; right: 0;
height: 30px;
opacity: 0;
transition: opacity 0.7s ease;
pointer-events: none;
box-shadow:
38px -8px 0 1px #FDE68A, 44px -8px 0 1px #FDE68A,
107px -16px 0 1px #FDE68A, 130px -16px 0 1px #FDE68A,
202px -12px 0 1px #FDE68A, 208px -12px 0 1px #FDE68A,
253px -8px 0 1px #FDE68A, 342px -4px 0 1px #FDE68A,
182px -2px 0 1px #FDE68A, 260px -2px 0 1px #FDE68A;
}
.hz-toggle.night .hz-windows { opacity: 1; }
.hz-orb {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 110px;
height: 110px;
border-radius: 50%;
left: 22px;
background: radial-gradient(circle at 40% 35%, #FFF7C2 0%, #FCD34D 35%, #F59E0B 70%, #D97706 100%);
box-shadow: 0 0 24px 8px rgba(252,211,77,0.35), 0 0 60px 16px rgba(252,211,77,0.12);
transition: left 0.7s cubic-bezier(0.4,0,0.2,1), background 0.7s ease, box-shadow 0.7s ease;
pointer-events: none;
}
.hz-toggle.night .hz-orb {
left: 262px;
background: radial-gradient(circle at 45% 40%, #E2E8F0 0%, #94A3B8 40%, #64748B 75%, #475569 100%);
box-shadow: 0 0 20px 6px rgba(148,163,184,0.2), 0 0 50px 12px rgba(148,163,184,0.08);
}
.hz-orb::after {
content: '';
position: absolute;
top: 8px; left: 15px;
width: 70px; height: 70px;
border-radius: 50%;
background: transparent;
transition: background 0.7s ease;
}
.hz-toggle.night .hz-orb::after {
background: #0F172A;
}
@keyframes hz-sun-pulse {
0%, 100% { box-shadow: 0 0 24px 8px rgba(252,211,77,0.35), 0 0 60px 16px rgba(252,211,77,0.12); }
50% { box-shadow: 0 0 32px 12px rgba(252,211,77,0.45), 0 0 80px 24px rgba(252,211,77,0.18); }
}
.hz-sun-pulse { animation: hz-sun-pulse 4s ease-in-out infinite; }
.hz-toggle:not(.night) .hz-orb { animation: hz-sun-pulse 4s ease-in-out infinite; }
@keyframes hz-moon-glow {
0%, 100% { box-shadow: 0 0 20px 6px rgba(148,163,184,0.2), 0 0 50px 12px rgba(148,163,184,0.08); }
50% { box-shadow: 0 0 28px 10px rgba(148,163,184,0.3), 0 0 65px 18px rgba(148,163,184,0.12); }
}
.hz-toggle.night .hz-orb { animation: hz-moon-glow 5s ease-in-out infinite; }
@media (max-width: 420px) {
.hz-toggle { width: 300px; height: 98px; border-radius: 49px; }
.hz-orb { width: 76px; height: 76px; left: 14px; }
.hz-toggle.night .hz-orb { left: 202px; }
.hz-orb::after { width: 48px; height: 48px; top: 6px; left: 10px; }
.hz-skyline { height: 26px; }
.hz-clouds { transform: scale(0.8); transform-origin: top left; }
}
/* ── Civic Controls (Seatbelt + Indicators + Phone) ── */
#civic-controls {
display: flex;
flex-direction: row;
gap: 6px;
align-items: center;
}
.civic-btn {
display: flex;
align-items: center;
gap: 5px;
background: rgba(30,30,40,0.85);
backdrop-filter: blur(6px);
border: 2px solid #555;
border-radius: 22px;
padding: 7px 12px;
cursor: pointer;
font-weight: 700;
font-size: 0.78rem;
color: var(--ink, #e8e3d8);
transition: all 0.2s;
user-select: none;
-webkit-user-select: none;
min-height: 34px;
}
.civic-btn:active { transform: scale(0.92); }
.civic-btn:hover { border-color: #777; }
/* Seatbelt icon */
.sb-icon-wrap {
width: 22px;
height: 22px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.3s, box-shadow 0.3s;
flex-shrink: 0;
}
.sb-icon { width: 16px; height: 16px; transition: color 0.3s; }
.sb-icon-wrap.off {
background: rgba(255,59,48,0.2);
box-shadow: 0 0 0 0 rgba(255,59,48,0);
animation: sb-pulse 1.2s ease-in-out infinite;
}
.sb-icon-wrap.off .sb-icon { color: #ff3b30; }
.sb-icon-wrap.on {
background: rgba(39,174,96,0.25);
}
.sb-icon-wrap.on .sb-icon { color: #27ae60; animation: none; }
@keyframes sb-pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(255,59,48,0.5); }
50% { box-shadow: 0 0 0 6px rgba(255,59,48,0); }
}
/* Indicator arrows */
.civic-ind {
padding: 7px 10px;
min-width: 34px;
justify-content: center;
}
.ind-arrow { width: 18px; height: 18px; transition: all 0.15s; color: #666; }
.civic-ind.active .ind-arrow {
color: #f39c12;
filter: drop-shadow(0 0 5px rgba(243,156,18,0.7));
}
.civic-ind.active {
border-color: #f39c12;
background: rgba(243,156,18,0.15);
}
.civic-ind.active {
animation: ind-blink 0.5s ease-in-out infinite;
}
@keyframes ind-blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.35; }
}
/* Phone icon */
.civic-icon { width: 16px; height: 16px; flex-shrink: 0; }
.civic-label { white-space: nowrap; line-height: 1; }
/* Seatbelt label color states */
#btn-seatbelt .civic-label.off { color: #ff3b30; }
#btn-seatbelt .civic-label.on { color: #27ae60; }
/* ── Responsive: civic-controls ── */
@media (max-width: 600px) {
#civic-controls {
gap: 3px;
bottom: 10px;
right: 6px;
}
.civic-btn { padding: 5px 8px; border-radius: 18px; font-size: 0.65rem; min-height: 44px; }
.sb-icon-wrap { width: 18px; height: 18px; }
.sb-icon { width: 13px; height: 13px; }
.ind-arrow { width: 14px; height: 14px; }
.civic-ind { padding: 5px 7px; min-width: 28px; }
.civic-label { font-size: 0.6rem; }
}
/* --- Global mobile-UX patterns --- */
/* Respect reduced-motion preference across menu/UI screens — not the in-game HUD
or 3D rendering itself, which is a separate performance concern. */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Touch devices don't have real :hover — guards against stuck/jittery hover
transforms on menu cards after a tap. */
@media (hover: none) {
.syl-item:hover,
.lcard:hover,
.bcard:not(.lk):hover {
transform: none;
box-shadow: inherit;
}
}
/* Visible press feedback on tap. */
.btn:active, .btn-g:active, .back-btn:active {
transform: scale(0.98);
transition-duration: 0.1s;
}
/* Safety net against sideways scroll on the menu screens. Deliberately not
touching the in-game canvas/HUD here, since that's already handled separately
and full-bleed by design. */
html, body { overflow-x: hidden; }
h1, h2, h3 { text-wrap: balance; }
p, .ltg, .syl-sub { text-wrap: pretty; }
/* --- Gyro calibration + rotate-device overlays --- */
#gyro-calibrate-overlay,
#rotate-device-overlay {
display: none;
position: fixed;
inset: 0;
z-index: 200000;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 16px;
background: rgba(10, 12, 20, 0.88);
backdrop-filter: blur(10px);
color: #fff;
text-align: center;
padding: 24px;
pointer-events: auto;
}
#gyro-calibrate-overlay.on,
#rotate-device-overlay.on {
display: flex;
}
.gc-ring {
width: 64px;
height: 64px;
border-radius: 50%;
border: 4px solid rgba(255, 255, 255, 0.2);
border-top-color: #34d399;
animation: gc-spin 1s linear infinite;
}
@keyframes gc-spin { to { transform: rotate(360deg); } }
.gc-title, .rd-title {
font-size: clamp(1.1rem, 4vw, 1.4rem);
font-weight: 700;
}
.gc-sub, .rd-sub {
font-size: clamp(0.85rem, 3vw, 1rem);
color: rgba(255, 255, 255, 0.7);
max-width: 320px;
}
.rd-icon {
font-size: 3rem;
animation: rd-rotate 1.6s ease-in-out infinite;
}
@keyframes rd-rotate {
0%, 100% { transform: rotate(0deg); }
50% { transform: rotate(90deg); }
}
</style>
<script>
window.addEventListener('error', function (e) {
const errDiv = document.createElement('div')
errDiv.style.position = 'fixed'
errDiv.style.top = '10px'
errDiv.style.left = '10px'
errDiv.style.zIndex = '999999'
errDiv.style.background = 'red'
errDiv.style.color = 'white'
errDiv.style.padding = '10px'
errDiv.style.fontFamily = 'monospace'
errDiv.innerText = 'JS Error: ' + e.message + ' at ' + e.filename + ':' + e.lineno
document.body.appendChild(errDiv)
})
window.addEventListener('unhandledrejection', function (e) {
const errDiv = document.createElement('div')
errDiv.style.position = 'fixed'
errDiv.style.top = '50px'
errDiv.style.left = '10px'
errDiv.style.zIndex = '999999'
errDiv.style.background = 'darkred'
errDiv.style.color = 'white'
errDiv.style.padding = '10px'
errDiv.style.fontFamily = 'monospace'
errDiv.innerText = 'Promise Rejection: ' + e.reason
document.body.appendChild(errDiv)
})
</script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/GLTFLoader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/libs/inflate.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/FBXLoader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/OBJLoader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/MTLLoader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/EffectComposer.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/RenderPass.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/ShaderPass.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/CopyShader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/LuminosityHighPassShader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/UnrealBloomPass.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/math/SimplexNoise.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/SSAOShader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/SSAOPass.js"></script>
<script src="vehicles.js"></script>
</head>
<body class="lm">
<!-- Script entry points -->
<!-- Portrait mode hint (non-blocking) -->
<div id="rotate-overlay"
style="display:none;position:fixed;top:4px;left:50%;transform:translateX(-50%);z-index:var(--z-hud);background:rgba(0,0,0,0.6);border-radius:8px;padding:4px 10px;color:#34D399;font-family:'Inter',sans-serif;font-size:0.65rem;text-align:center;backdrop-filter:blur(4px);pointer-events:none;transition:opacity 0.3s;">
Rotate for best view
</div>
<!-- Virtual Joystick for Mobile -->
<div id="virtual-joystick" style="
position: fixed;
bottom: clamp(40px, 12vh, 100px);
left: clamp(20px, 6vw, 40px);
width: clamp(120px, 18vw, 160px);
height: clamp(120px, 18vw, 160px);
border-radius: 50%;
background: rgba(255, 255, 255, 0.15);
border: 3px solid rgba(255, 255, 255, 0.35);
display: none;
justify-content: center;
align-items: center;
touch-action: none;
z-index: 10000;
backdrop-filter: blur(4px);
">
<div id="joystick-knob" style="
width: 55px;
height: 55px;
border-radius: 50%;
background: linear-gradient(145deg, #ffd54a, #ffb300);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4), inset 0 2px 4px rgba(255, 255, 255, 0.4);
transition: transform 0.05s ease-out;
"></div>
</div>
<!-- Right-side Camera Joystick for Mobile Look-Around -->
<div id="camera-joystick" style="
position: fixed;
bottom: clamp(40px, 12vh, 100px);
right: clamp(20px, 6vw, 40px);
width: clamp(100px, 15vw, 140px);
height: clamp(100px, 15vw, 140px);
border-radius: 50%;
background: rgba(100, 200, 255, 0.12);
border: 2px solid rgba(100, 200, 255, 0.3);
display: none;
justify-content: center;
align-items: center;
touch-action: none;
z-index: 10000;
backdrop-filter: blur(4px);
">
<div id="camera-joystick-knob" style="
width: 44px;
height: 44px;
border-radius: 50%;
background: linear-gradient(145deg, #5ed4f5, #3ba8d0);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4), inset 0 2px 4px rgba(255, 255, 255, 0.4);
transition: transform 0.05s ease-out;
opacity: 0.8;
"></div>
<div style="position:absolute;top:-18px;left:50%;transform:translateX(-50%);font-size:0.55rem;color:rgba(255,255,255,0.5);pointer-events:none;white-space:nowrap;font-family:Inter,sans-serif;">👁 LOOK</div>
</div>
<div id="mobile-controls" style="
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 16px 24px 28px;
justify-content: space-between;
z-index: 9999;
pointer-events: none;
background: linear-gradient(to top, rgba(0, 0, 0, 0.3) 0%, transparent 100%);
">
<div class="mc-col" style="display: flex; pointer-events: auto; align-items: flex-end">
<div id="steer-wheel-container" style="
position: relative;
width: clamp(120px, 18vw, 160px);
height: clamp(120px, 18vw, 160px);
border-radius: 50%;
background: rgba(0, 0, 0, 0.4);
border: 4px solid rgba(255, 255, 255, 0.4);
display: flex;
justify-content: center;
align-items: center;
touch-action: none;
backdrop-filter: blur(4px);
">
<div id="steer-wheel" style="
width: 100%;
height: 100%;
position: absolute;
border-radius: 50%;
background: url('data:image/svg+xml;utf8,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><circle cx=%2250%22 cy=%2250%22 r=%2245%22 fill=%22none%22 stroke=%22white%22 stroke-width=%228%22/><line x1=%2250%22 y1=%225%22 x2=%2250%22 y2=%2295%22 stroke=%22white%22 stroke-width=%228%22/><line x1=%225%22 y1=%2250%22 x2=%2295%22 y2=%2250%22 stroke=%22white%22 stroke-width=%228%22/><circle cx=%2250%22 cy=%2250%22 r=%2215%22 fill=%22%23ffd54a%22/><circle cx=%2250%22 cy=%2215%22 r=%228%22 fill=%22white%22/></svg>')
center/cover;
transition: transform 0.05s linear;
"></div>
</div>
</div>
<div class="mc-col" style="display: flex; gap: 10px; pointer-events: auto; align-items: flex-end">
<div id="phone-gps-btn" style="display: none;">📱</div>
<div class="mc-btn" id="mc-enter" style="
width: clamp(64px, 12vw, 84px);
height: clamp(64px, 12vw, 84px);
background: rgba(241, 196, 15, 0.5);
color: white;
border: 2px solid rgba(241, 196, 15, 0.7);
border-radius: 50%;
display: none;
align-items: center;
justify-content: center;
user-select: none;
-webkit-user-select: none;
box-shadow: 0 4px 12px rgba(241, 196, 15, 0.3);
font-size: 11px;
font-weight: 700;
font-family: 'Inter', sans-serif;
">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5"
stroke-linecap="round" stroke-linejoin="round">
<path d="M5 12h14" />
<path d="M12 5l7 7-7 7" />
</svg>
</div>
<div class="mc-btn" id="mc-boost" style="
width: clamp(64px, 12vw, 84px);
height: clamp(64px, 12vw, 84px);
background: rgba(94, 212, 245, 0.5);
color: white;
border: 2px solid rgba(94, 212, 245, 0.7);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
-webkit-user-select: none;
box-shadow: 0 4px 12px rgba(94, 212, 245, 0.3);
font-size: 11px;
font-weight: 700;
font-family: 'Inter', sans-serif;
letter-spacing: 0.5px;
">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5"
stroke-linecap="round" stroke-linejoin="round">
<polygon points="13,2 3,14 12,14 11,22 21,10 12,10" fill="rgba(255,255,255,0.15)" />
</svg>
</div>
<div class="mc-btn" id="mc-brake" style="
width: clamp(70px, 14vw, 96px);
height: clamp(70px, 14vw, 96px);
background: rgba(220, 40, 40, 0.6);
margin-top: 0;
color: white;
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
-webkit-user-select: none;
box-shadow: 0 4px 12px rgba(220, 40, 40, 0.3);
">
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5"
stroke-linecap="round" stroke-linejoin="round">
<path d="M4 20h16" />
<rect x="7" y="6" width="10" height="14" rx="2" fill="rgba(255,255,255,0.15)" />
<path d="M9 10h6" />
</svg>
</div>
<div class="mc-btn" id="mc-gas" style="
background: rgba(40, 180, 40, 0.6);
color: white;
border: 2px solid rgba(255, 255, 255, 0.4);
border-radius: 20px;
width: clamp(70px, 14vw, 96px);
height: clamp(70px, 14vw, 96px);
backdrop-filter: blur(8px);
display: flex;
align-items: center;
justify-content: center;
user-select: none;
-webkit-user-select: none;
box-shadow: 0 4px 12px rgba(40, 180, 40, 0.3);
">
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5"
stroke-linecap="round" stroke-linejoin="round">
<path d="M4 20h16" />
<polygon points="12,4 6,14 18,14" fill="rgba(255,255,255,0.15)" />
<path d="M12 7v5" />
</svg>
</div>
</div>
</div>
<!-- Dedicated Pre-Loader Screen -->
<div id="loading-screen">
<h1>Traffic Academy</h1>
<div style="font-size: 1.2rem; margin-bottom: 10px">Loading 3D Open World...</div>
<div id="loading-bar-container">
<div id="loading-bar"></div>
</div>
<div id="loading-pct">0%</div>
<div id="loading-status">Initializing assets...</div>
</div>
<div id="gc"><canvas id="3c" style="touch-action:none;"></canvas></div>
<div id="swipe-look-hint"
style="position:fixed; bottom:80px; left:50%; transform:translateX(-50%); background:rgba(0,0,0,0.6); color:#fff; padding:8px 16px; border-radius:20px; font-size:0.85rem; pointer-events:none; z-index:var(--z-hud); display:none; font-family:Inter,sans-serif; backdrop-filter:blur(4px); border:1px solid rgba(255,255,255,0.15); transition:opacity 0.5s;">
👆 Swipe empty area to look around
</div>
<div id="hud">
<div class="hp">Lv<span class="hv" id="hlv">1</span></div>
<div class="hp" id="hobj" style="font-size: 0.85rem; font-weight: 600; text-align: center"></div>
<div class="hp">Score<span class="hv" id="hsc">0</span></div>
</div>
<div id="hudbar">
<div class="hb"><span id="htmr">0:00</span></div>
<div class="hb">💸<span id="hfin">0</span></div>
<div class="hb"><span id="hcp">0/0</span></div>
</div>
<div id="objective-overlay">
<div id="objective-title">Current Objective</div>
<div id="objective-desc">Loading...</div>
</div>
<div id="gyro-calibrate-overlay">
<div class="gc-ring"></div>
<div class="gc-title">Hold your phone naturally</div>
<div class="gc-sub">Keep it still for a moment — calibrating steering…</div>
</div>
<div id="rotate-device-overlay">
<div class="rd-icon">📱</div>
<div class="rd-title">Turn your phone sideways</div>
<div class="rd-sub">Driving needs landscape mode</div>
</div>
<div id="pause-overlay"
style="display:none; position:fixed; inset:0; z-index:100000; background:rgba(0,0,0,0.7); backdrop-filter:blur(8px); flex-direction:column; align-items:center; justify-content:center; gap:18px; pointer-events:auto;">
<div
style="font-family:'Bebas Neue',sans-serif; font-size:3rem; color:#fff; text-shadow:0 4px 12px rgba(0,0,0,0.5);">
PAUSED</div>
<button id="pause-resume"
style="font-family:'Inter',sans-serif; font-size:1.1rem; font-weight:600; padding:12px 36px; border:none; border-radius:12px; background:rgba(52,152,219,0.85); color:#fff; cursor:pointer; transition:background 0.2s;">Resume
(Esc)</button>
<button id="pause-restart"
style="font-family:'Inter',sans-serif; font-size:1rem; font-weight:500; padding:10px 32px; border:none; border-radius:12px; background:rgba(255,149,0,0.8); color:#fff; cursor:pointer;">Restart
Level</button>
<button id="pause-quit"
style="font-family:'Inter',sans-serif; font-size:1rem; font-weight:500; padding:10px 32px; border:none; border-radius:12px; background:rgba(231,76,60,0.8); color:#fff; cursor:pointer;">Quit
to Menu</button>
</div>
<div id="task-tracker">
<div
style="font-size: 0.7rem; text-transform: uppercase; letter-spacing: 2px; color: var(--yellow); font-weight: 800; margin-bottom: 10px">
📋 Tasks</div>
<div id="task-list" style="display: flex; flex-direction: column; gap: 8px"></div>
</div>
<div id="civic-controls"
style="position: fixed; bottom: 16px; right: 20px; z-index: var(--z-controls); display: flex; flex-direction: row; gap: 6px; align-items: center">
<div id="btn-seatbelt" class="civic-btn" onclick="game.toggleSeatbelt(this)">
<div id="sb-icon-wrap" class="sb-icon-wrap">
<svg id="sb-icon" class="sb-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 2a4 4 0 0 0-4 4v2"/>
<path d="M16 6a4 4 0 0 0-4-4"/>
<circle cx="12" cy="16" r="5"/>
<path d="M12 11v3"/>
<path d="M10 14h4"/>
</svg>
</div>
<span id="sb-label" class="civic-label">Belt OFF</span>
</div>
<div id="btn-ind-left" class="civic-btn civic-ind" onclick="game.toggleTurnSignal(-1)">
<svg class="ind-arrow" viewBox="0 0 24 24" fill="currentColor"><path d="M15 4l-8 8 8 8V4z"/></svg>
</div>
<div id="btn-ind-right" class="civic-btn civic-ind" onclick="game.toggleTurnSignal(1)">
<svg class="ind-arrow" viewBox="0 0 24 24" fill="currentColor"><path d="M9 4l8 8-8 8V4z"/></svg>
</div>
<div id="btn-mobile" class="civic-btn" onclick="game.toggleMobile(this)">
<svg class="civic-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
<rect x="5" y="2" width="14" height="20" rx="2" ry="2"/>
<line x1="12" y1="18" x2="12.01" y2="18"/>
</svg>
<span class="civic-label">Phone</span>
</div>
<div id="btn-gyro" class="civic-btn" onclick="game.toggleGyro(this)">
<svg class="civic-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"/>
<path d="M12 2v20M2 12h20"/>
</svg>
<span class="civic-label">Gyro</span>
</div>
<div id="btn-kid" class="civic-btn" onclick="game.toggleKidMode(this)">
<svg class="civic-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 12m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0"/>
<path d="M12 2a10 10 0 0 0 -10 10c0 3 2 6 5 8v2h10v-2c3-2 5-5 5-8a10 10 0 0 0 -10 -10z"/>
</svg>
<span class="civic-label">Kid Mode</span>
</div>
</div>
<div id="sig-ind" style="
position: fixed;
top: 16px;
left: 50%;
transform: translateX(-50%);
z-index: 25;
display: none;
align-items: center;
gap: 12px;
background: rgba(255, 255, 255, 0.92);
backdrop-filter: blur(12px);
padding: 10px 20px;
border-radius: 30px;
border: 1px solid var(--border);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
font-weight: 700;
font-size: 0.95rem;
">
<div id="sind-lamp"
style="width: 20px; height: 20px; border-radius: 50%; background: #ff3b30; box-shadow: 0 0 14px #ff3b30; transition: all 0.3s">
</div>
<div id="sind-state" style="color: #ff3b30; min-width: 55px; text-align: center; transition: color 0.3s">RED</div>
<div style="width: 1px; height: 20px; background: var(--border)"></div>
<div id="sind-timer" style="color: var(--text); font-size: 1.1rem; min-width: 30px; text-align: center">0s</div>
<div style="width: 1px; height: 20px; background: var(--border)"></div>
<div id="sind-dist" style="color: var(--muted); font-size: 0.85rem">--m</div>
</div>
<div id="dn-clock">
>
<span id="dn-icon">☀️</span>
<span id="dn-time">12:00</span>
</div>
<div id="hwrap">
<div class="hl" style="font-size: 0.7rem; color: var(--muted); letter-spacing: 0.06em">💰 WALLET</div>
<div id="hwallet"
style="font-size: 1.05rem; font-weight: 900; color: #ffd54a; font-family: 'Bebas Neue', sans-serif; letter-spacing: 0.04em">
₹50,000</div>
<div class="hl" style="font-size: 0.7rem; color: var(--muted); letter-spacing: 0.06em; margin-top: 4px">🚗 HEALTH
</div>
<div class="ht">
<div class="hf" id="hfill"></div>
</div>
</div>
<div id="ow">⚠️ OFF ROAD - Return to road!</div>
<div id="da">
<div class="da-arrow-icon" id="da-arrow-container">
<div id="da-arrow" style="transition: transform 0.15s ease-out;">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"
stroke-linejoin="round">
<path d="M12 19V5M5 12l7-7 7 7" />
</svg>
</div>
</div>
<div style="display: flex; flex-direction: column; gap: 2px;">
<div class="dal" id="dal">GO STRAIGHT</div>
<div class="da-dist" id="da-dist">--m</div>
</div>
</div>
<div id="phone-gps">
<button id="phone-gps-close"></button>
<div id="phone-gps-compass">
<div id="phone-gps-arrow">
<svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#5ed4f5" stroke-width="2.5"
stroke-linecap="round" stroke-linejoin="round">
<path d="M12 19V5M5 12l7-7 7 7" />
</svg>
</div>
</div>
<div id="phone-gps-dist">--m</div>
<div id="phone-gps-dir">--</div>
<div id="phone-gps-obj">Objective</div>
</div>
<div id="gp">
<button class="gb ag" data-g="P">P</button>
<button class="gb" data-g="R">R</button>
<button class="gb" data-g="N">N</button>
<button class="gb" data-g="D">D</button>
</div>
<div id="gread">GEAR: P</div>
<div id="tc">
<div style="display: flex; gap: 16px">
<div class="db" id="tl">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"
stroke-linecap="round" stroke-linejoin="round">
<path d="M19 12H5M12 19l-7-7 7-7" />
</svg>
</div>
<div class="db" id="tr">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"
stroke-linecap="round" stroke-linejoin="round">
<path d="M5 12h14M12 5l7 7-7 7" />
</svg>
</div>
</div>
<div style="display: flex; gap: 16px">
<div class="db" id="abb">🛑</div>
<div class="db" id="tu">⬆️</div>
<div class="db" id="abh">📢</div>
</div>
</div>
<div id="crash-screen" style="
position: fixed;
inset: 0;
background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.15) 40%, rgba(180, 0, 0, 0.35) 100%);
z-index: 9999;
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
backdrop-filter: blur(10px);
">
<div class="cvc"
style="padding: 0; background: #fff; overflow: hidden; border-radius: 16px; max-width: 450px; width: 90%; box-shadow: 0 24px 60px rgba(255, 0, 0, 0.2)">
<div style="background: #b02a37; color: white; padding: 24px 20px; display: flex; align-items: center; gap: 16px">
<div
style="background: #fff; color: #b02a37; width: 48px; height: 48px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 1.6rem">
⚠️
</div>
<div style="text-align: left">
<div style="font-size: 1.2rem; font-weight: 700">Critical Accident Report</div>
<div style="font-size: 0.85rem; color: rgba(255, 255, 255, 0.8); margin-top: 2px">Simulation Terminated</div>
</div>
</div>
<div style="padding: 24px; text-align: left">
<div
style="display: flex; justify-content: space-between; margin-bottom: 16px; border-bottom: 1px dashed #ccc; padding-bottom: 12px">
<span style="color: #6c757d; font-weight: 500">Incident Type</span>
<span style="font-weight: 700; color: #b02a37; max-width: 60%; text-align: right" id="crash-reason">Collided
with Vehicle</span>
</div>
<div
style="background: #fff3cd; border-left: 4px solid #ffc107; padding: 12px 16px; border-radius: 4px; margin-bottom: 20px">
<div style="font-size: 0.8rem; font-weight: 700; color: #856404; margin-bottom: 6px">REAL LIFE CONSEQUENCES
</div>
<div id="crash-info" style="font-size: 0.95rem; color: #856404; line-height: 1.4">Dangerous driving can lead
to severe structural damage and potential injury.</div>
</div>
</div>
<div
style="background: #f8f9fa; padding: 20px; border-top: 1px solid #e9ecef; display: flex; flex-direction: column; gap: 12px">
<button class="btn btn-p"
style="background: #b02a37; font-size: 1.1rem; padding: 12px; border-radius: 30px; width: 100%; border: none"
onclick="game.retryLevel()">Try Again</button>
<button class="btn btn-g"
style="font-size: 1rem; padding: 12px; border-radius: 30px; width: 100%; background: transparent; border: 2px solid #ccc; color: #555"
onclick="
document.getElementById('crash-screen').style.display = 'none'
ui.showLevels()
setTimeout(() => ui._selSyl('theory'), 100)
">
Course Overview
</button>
</div>
</div>
</div>
<div id="mgo">
<div class="mgc" id="mgc">
<div class="mgt" id="mgt">Action</div>
<div class="mgd" id="mgd">Perform action.</div>
<div id="mgct"></div>
<div class="mgb" id="mgbt"></div>
</div>
</div>
<!-- GAME HUD RESTORATION -->
<div id="cdown" style="
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 5rem;
font-weight: 800;
color: var(--ink);
text-shadow: 0 8px 30px rgba(0, 0, 0, 0.5);
z-index: 90;
display: none;
animation: cdownPulse 0.5s ease;
">
READY
</div>
<div id="spgauge">
<svg viewBox="0 0 88 88" width="100" height="100">
<path d="M44,76 A32,32 0 1,1 76,44" fill="none" stroke="var(--border)" stroke-width="6" stroke-linecap="round" />
<path id="garc" fill="none" stroke="var(--yellow)" stroke-width="6" stroke-linecap="round" />
<text id="gspd" x="44" y="52" text-anchor="middle" font-size="28" font-weight="700" fill="var(--text)">0</text>
<text x="44" y="68" text-anchor="middle" font-size="10" font-weight="600" fill="var(--muted)">KM/H</text>
</svg>
</div>
<div id="boostgauge">
<svg viewBox="0 0 64 64" width="72" height="72">
<circle cx="32" cy="32" r="24" fill="none" stroke="var(--border)" stroke-width="5" />
<circle id="boost-arc" cx="32" cy="32" r="24" fill="none" stroke="#5ed4f5" stroke-width="5"
stroke-dasharray="150.8" stroke-dashoffset="0" stroke-linecap="round" transform="rotate(-90 32 32)" />
<text id="boost-pct" x="32" y="34" text-anchor="middle" font-size="16" font-weight="700" fill="#5ed4f5">100</text>
<text x="32" y="48" text-anchor="middle" font-size="7" font-weight="600" fill="var(--muted)">BOOST</text>
</svg>
</div>
<div id="boost-vignette"
style="position:fixed;inset:0;pointer-events:none;z-index:var(--z-effects);display:none;border:4px solid transparent;border-radius:0;box-shadow:inset 0 0 80px rgba(0,240,204,0.35),inset 0 0 160px rgba(0,240,204,0.15);transition:opacity 0.3s;opacity:0;">
</div>
<div id="speed-lines"
style="position:fixed;inset:0;pointer-events:none;z-index:var(--z-effects);display:none;opacity:0;transition:opacity 0.15s;background:radial-gradient(ellipse at center,transparent 40%,rgba(255,255,255,0.06) 60%,rgba(255,255,255,0.18) 100%);mix-blend-mode:screen;">
</div>
<div id="boost-ready"
style="position:fixed;right:32px;bottom:130px;z-index:26;font-size:0.7rem;font-family:var(--sans);color:#00f0cc;background:var(--card);border:1px solid #00f0cc;border-radius:12px;padding:4px 10px;display:none;opacity:0;transition:opacity 0.3s;box-shadow:0 0 10px rgba(0,240,204,0.3);">
Boost Ready</div>
<canvas id="mmc" width="160" height="160"
style="position: fixed; top: 32px; right: 32px; border-radius: 50%; border: 4px solid var(--card); box-shadow: 0 12px 30px rgba(0, 0, 0, 0.2); z-index: var(--z-effects); display: none"></canvas>
<div id="compass-strip" style="
position: fixed; top: 14px; left: 50%; transform: translateX(-50%);
z-index: var(--z-effects); display: none;
align-items: center; gap: 10px;
background: rgba(15, 23, 42, 0.65); backdrop-filter: blur(10px);
border: 1px solid rgba(255,255,255,0.15); border-radius: 999px;
padding: 6px 16px; color: #fff; font-family: var(--sans, sans-serif);
font-size: 0.8rem; font-weight: 700; white-space: nowrap;">
<span id="compass-heading">N</span>
<span id="compass-arrow" style="display:inline-block; transition: transform 0.2s;"></span>
<span id="compass-dist" style="opacity: 0.7; font-weight: 600;"></span>
</div>
<div id="challan-stack"></div>
<div id="toast">
<div id="ti"></div>
</div>
<div id="name-prompt-dlg" style="
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.85);
backdrop-filter: blur(10px);
z-index: 9999;
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
color: #ffffff;
">
<style>
@keyframes promptFloatY {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-15px);
}
}
</style>
<div style="
position: absolute;
inset: 0;
background: radial-gradient(circle at top right, rgba(255, 213, 74, 0.15), transparent 50%), radial-gradient(circle at bottom left, rgba(255, 213, 74, 0.1), transparent 50%);
pointer-events: none;
"></div>
<div
style="position: relative; z-index: 2; text-align: center; max-width: 500px; width: 90%; animation: promptFloatY 6s ease-in-out infinite">
<div
style="font-family: 'Lora', serif; font-size: 4.5rem; margin-bottom: 10px; text-shadow: 0 10px 30px rgba(0, 0, 0, 0.5)">
Traffic <i style="color: #ffd54a; font-style: normal">Hero</i>
</div>
<p style="color: var(--dim); font-size: 1.1rem; margin-bottom: 40px">Welcome to the Academy. Enter your name to
begin your journey.</p>
<div style="
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
padding: 40px;
border-radius: 24px;
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
backdrop-filter: blur(20px);
">
<input type="text" id="prompt-name" placeholder="Enter Driver Name" maxlength="30" style="
width: 100%;
box-sizing: border-box;
padding: 18px 24px;
border-radius: 12px;
border: 2px solid rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.3);
color: var(--ink);
font-size: 1.2rem;
margin-bottom: 24px;
outline: none;
text-align: center;
font-family: 'Inter', sans-serif;
transition: all 0.3s;
" onfocus="
this.style.borderColor = '#FFD54A'
this.style.boxShadow = '0 0 20px rgba(255,213,74,0.2)'
" onblur="
this.style.borderColor = 'rgba(255,255,255,0.1)'
this.style.boxShadow = 'none'
" onkeydown="if (event.key === 'Enter') ui.saveNamePrompt()" />
<button class="btn btn-p" onclick="ui.saveNamePrompt()" style="
width: 100%;
font-size: 1.2rem;
padding: 16px;
border-radius: 12px;
background: #ffd54a;
color: #000;
border: none;
cursor: pointer;
box-shadow: 0 10px 30px rgba(255, 213, 74, 0.3);
letter-spacing: 1px;
text-transform: uppercase;
font-weight: 700;
">
Start Engine
</button>
</div>
</div>
</div>
<div id="profile-dlg"
style="position: fixed; inset: 0; background: rgba(0, 0, 0, 0.6); z-index: var(--z-hud); display: none; flex-direction: column; align-items: center; justify-content: center; backdrop-filter: blur(10px)">
<div class="nd-c"
style="background: #fff; padding: 30px; border-radius: 16px; max-width: 400px; width: 90%; box-shadow: 0 24px 60px rgba(0, 0, 0, 0.2); text-align: center">
<h3 style="font-size: 1.6rem; margin-bottom: 8px">Driver Profile</h3>
<p style="color: var(--muted2); margin-top: 0px; font-size: 0.95rem; margin-bottom: 20px">Manage your academy
details</p>
<div style="text-align: left; margin-bottom: 12px">
<label style="font-weight: 600; font-size: 0.9rem; color: #555">Full Name</label>
<input type="text" id="prof-name" class="nd-i" placeholder="e.g. Rahul Sharma" maxlength="30"
style="margin-top: 6px; width: 100%; box-sizing: border-box" />
</div>
<div style="text-align: left; margin-bottom: 12px">
<label style="font-weight: 600; font-size: 0.9rem; color: #555">Age</label>
<input type="number" id="prof-age" class="nd-i" min="8" max="99" placeholder="E.g., 18"
style="margin-top: 6px; width: 100%; box-sizing: border-box" />
</div>
<div style="text-align: left; margin-bottom: 12px">
<label style="font-weight: 600; font-size: 0.9rem; color: #555">Preferred Language</label>
<select id="prof-lang" class="nd-i"
style="margin-top: 6px; width: 100%; box-sizing: border-box; cursor: pointer; padding: 12px; border-radius: 8px; border: 1px solid #ccc; font-size: 1rem">
<option value="en">English</option>
<option value="hi">हिन्दी (Hindi)</option>
</select>
</div>
<div style="text-align: left; margin-bottom: 24px">
<label style="font-weight: 600; font-size: 0.9rem; color: #555">Preferred Vehicle</label>
<select id="prof-veh" class="nd-i"
style="margin-top: 6px; width: 100%; box-sizing: border-box; cursor: pointer; padding: 12px; border-radius: 8px; border: 1px solid #ccc; font-size: 1rem">
<option value="Car">Car</option>
<option value="Auto">Auto Rickshaw</option>
<option value="Bike">Motorcycle</option>
<option value="SUV">SUV</option>
<option value="Bus">Bus</option>
<option value="Truck">Truck</option>
</select>
</div>
<button class="btn" onclick="
document.getElementById('profile-dlg').style.display = 'none'
ui.showBadges()
"
style="width: 100%; font-size: 1rem; padding: 12px; margin-bottom: 16px; background: #4a90e2; color: var(--ink); font-weight: 700">
🎖️ View Badges
</button>
<button class="btn" onclick="
document.getElementById('profile-dlg').style.display = 'none'
ui.showCert()
"
style="width: 100%; font-size: 1rem; padding: 12px; margin-bottom: 16px; background: #ffc107; color: #1f3b5e; font-weight: 700">
🎓 View Certificate
</button>
<div style="display: flex; gap: 12px">
<button class="btn btn-g" onclick="document.getElementById('profile-dlg').style.display = 'none'"
style="width: 50%; font-size: 1rem; padding: 12px">Cancel</button>
<button class="btn btn-p" onclick="ui.saveProfile()"
style="width: 50%; font-size: 1rem; padding: 12px">Save</button>
</div>
</div>
</div>
<div id="vflash"></div>
<div id="cov">
<div class="cvc" id="cvc-main"
style="padding: 0; background: #fff; overflow: hidden; border-radius: 16px; max-width: 400px; width: 90%; box-shadow: 0 24px 60px rgba(0, 0, 0, 0.3)">
<div style="background: #1f3b5e; color: white; padding: 24px 20px; display: flex; align-items: center; gap: 16px">
<div style="
background: #ffc107;
color: #1f3b5e;
width: 48px;
height: 48px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 1.4rem;
">
xa
</div>
<div style="text-align: left">
<div style="font-size: 1.2rem; font-weight: 700">Mumbai Traffic Police</div>
<div style="font-size: 0.85rem; color: rgba(255, 255, 255, 0.7); margin-top: 2px">Electronic Challan Notice
</div>
</div>
</div>
<div style="padding: 24px; text-align: left">
<div style="display: flex; justify-content: space-between; margin-bottom: 16px">
<span style="color: #6c757d; font-weight: 500">Challan No.</span>
<span style="font-weight: 700" id="cnum"></span>
</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 16px">
<span style="color: #6c757d; font-weight: 500">Offense</span>
<span style="font-weight: 700; color: #dc3545; text-align: right; max-width: 60%" id="coff"></span>
</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 16px">
<span style="color: #6c757d; font-weight: 500">Law Section</span>
<span style="font-weight: 700; text-align: right; max-width: 60%" id="claw"></span>
</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 24px">
<span style="color: #6c757d; font-weight: 500">Location</span>
<span style="font-weight: 700" id="cloc">📍 Andheri West</span>
</div>
<div style="border-top: 2px dashed #e9ecef; margin-bottom: 24px"></div>
<div
style="background: #fff3cd; border: 2px solid #ffc107; border-radius: 12px; padding: 16px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px">
<span style="color: #856404; font-weight: 600">Fine Amount</span>
<span
style="color: #dc3545; font-size: 2.2rem; font-weight: 700; font-family: 'Bebas Neue', sans-serif; line-height: 1"
id="camt"></span>
</div>
</div>
<div
style="background: #f8f9fa; padding: 20px; border-top: 1px solid #e9ecef; display: flex; flex-direction: column; align-items: center">
<button class="btn btn-p"
style="background: #b02a37; width: 100%; font-size: 1.2rem; padding: 14px; border-radius: 30px; margin-bottom: 16px"
onclick="ui.dismissChallan()">
Pay & Continue &rarr;
</button>
<div
style="font-size: 0.7rem; color: #888; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 6px; font-weight: 600">
Sponsored By</div>
<div style="display: flex; justify-content: center; gap: 16px; align-items: center">
<img loading="lazy" id="cert-logo-1" src="mumbai-police-logo.png" style="height: 24px; opacity: 0.7" />
<div style="width: 1px; height: 16px; background: #ccc"></div>
<img loading="lazy" id="cert-logo-2" src="sneh-logo.png" style="height: 24px; opacity: 0.7" />
</div>
</div>
</div>
</div>
<!-- SCREENS -->
<div id="ss" class="screen active">
<div id="top-bar-container"
style="position: absolute; top: 20px; right: 20px; z-index: var(--z-controls); display: flex; gap: 10px">
<div class="hz-toggle" onclick="hzToggle()">
<div class="hz-clouds"></div>
<div class="hz-stars"></div>
<div class="hz-skyline"></div>
<div class="hz-windows"></div>
<div class="hz-orb"></div>
</div>
<button class="nav-login-btn" onclick="
window.gameDifficulty = window.gameDifficulty === 'Hard' ? 'Easy' : 'Hard'
this.innerHTML = window.gameDifficulty === 'Hard' ? '🔴 Hard Mode' : '🟢 Easy Mode'
" style="
background: #fff;
border: 1px solid var(--border);
border-radius: 30px;
padding: 8px 16px;
font-weight: 600;
cursor: pointer;
font-size: 0.95rem;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
" id="btn-diff">
🟢 Easy Mode
</button>
<button class="nav-login-btn" onclick="
window.gameWeather = window.gameWeather === 'Rain' ? 'Clear' : 'Rain'
this.innerHTML = window.gameWeather === 'Rain' ? '🌧️ Rain' : '☀️ Clear'
" style="
background: #fff;
border: 1px solid var(--border);
border-radius: 30px;
padding: 8px 16px;
font-weight: 600;
cursor: pointer;
font-size: 0.95rem;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
" id="btn-weather">
☀️ Clear
</button>
<div class="nav-user-profile" id="navUserProfile" data-preserve-click="true" onclick="ui.showProfile()" style="
display: none;
align-items: center;
gap: 8px;
background: #fff;
border: 1px solid var(--border);
border-radius: 30px;
padding: 5px 15px 5px 5px;
cursor: pointer;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
">
<div class="nav-user-avatar" style="
width: 28px;
height: 28px;
border-radius: 50%;
background: var(--signal);
color: var(--ink);
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
overflow: hidden;
">
?
</div>
<span class="nun" style="font-weight: 600; font-size: 0.95rem; color: #111827">User</span>
</div>
</div>
<div id="rl"></div>
<div class="sc">
<div class="brand-header"
style="display: flex; flex-direction: column; align-items: center; gap: 8px; margin-bottom: 40px; width: 100%; align-self: center">
<div style="display: flex; gap: 32px; align-items: center; justify-content: center">
<div style="display: flex; align-items: center; gap: 12px">
<img loading="lazy" id="cert-logo-3" src="mumbai-police-logo.png"
style="height: 60px; width: auto; object-fit: contain" alt="Mumbai Police"
onerror="this.style.display = 'none'" />
<div
style="font-weight: 700; letter-spacing: 0.05em; color: var(--muted); text-align: left; line-height: 1.2">
MUMBAI<br />TRAFFIC POLICE</div>
</div>
<div style="width: 2px; height: 50px; background: var(--border)"></div>
<div style="display: flex; align-items: center; gap: 12px">
<img loading="lazy" id="cert-logo-4" src="sneh-logo.png"
style="height: 60px; width: auto; object-fit: contain" alt="Sneh Asha"
onerror="this.style.display = 'none'" />
<div
style="font-weight: 700; letter-spacing: 0.05em; color: var(--muted); text-align: left; line-height: 1.2">
SNEH ASHA</div>
</div>
</div>
<div
style="font-size: 0.85rem; font-weight: 600; color: var(--muted2); letter-spacing: 0.1em; text-transform: uppercase; margin-top: 8px">
Proudly Sponsored By</div>
</div>
<div style="text-align: center">
<div style="font-size: 6rem; margin-bottom: 20px; text-shadow: 0 12px 24px rgba(0, 0, 0, 0.1)">🚦🏙️🚗</div>
<div class="st" style="text-align: center">Mumbai Traffic Hero Academy</div>
<div class="su" style="max-width: 700px; margin-left: auto; margin-right: auto; text-align: center">
Welcome to the premier driving simulation! Complete 15 rigorous real-world scenarios designed by the Mumbai
Traffic Police to test your skills, patience, and compliance. Prove you have
what it takes to be a true Traffic Hero and earn your official certification.
</div>
<div class="pls" style="justify-content: center; margin-top: 40px">
<button class="btn btn-p" onclick="ui.showLevels()" style="font-size: 1.1rem; padding: 12px 32px">Enter
Academy</button>
<button class="btn btn-g" onclick="ui.hardReset()" style="font-size: 1.1rem; padding: 12px 32px">Reset
Progress</button>
<button class="btn btn-s"
style="background: var(--red); font-size: 1.1rem; padding: 12px 32px; color: var(--ink)" onclick="
const p = prompt('Developer Password:')
if (['Neel', 'Ansh', 'Sanjana'].includes(p)) {
ui.adminUnlock()
} else if (p) {
toast('❌ Access Denied', '#ff3b30')
}
">
🔓 Dev Unlock
</button>
<button class="btn btn-g mobile-dl"
style="border-color: var(--signal); color: var(--signal); font-size: 1.1rem; padding: 12px 32px"
onclick="window.location.href = '../COL.apk'">
📱 Download Android App
</button>
</div>
</div>
</div>
</div>
<div id="screen-levels" class="screen">
<div class="tb">
<button class="back-btn" onclick="ui.showStart()">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"
stroke-linecap="round" stroke-linejoin="round">
<path d="M19 12H5M12 19l-7-7 7-7" />
</svg>
Back
</button>
<div style="display: flex; gap: 12px; align-items: center">
<button class="btn btn-g" onclick="ui.showProfile()"
style="padding: 8px 20px; font-size: 0.95rem; white-space: nowrap; display: flex; align-items: center; justify-content: center; gap: 6px; white-space: nowrap">
👤 Profile
</button>
<div id="pchip" class="btn btn-g"
style="padding: 8px 20px; font-size: 0.95rem; white-space: nowrap; display: flex; align-items: center; justify-content: center; gap: 6px; white-space: nowrap">
0/52 ✅
</div>
</div>
</div>
<div class="lw" id="lvbody"></div>
</div>
<div id="screen-briefing" class="screen">
<div class="tb">
<button class="back-btn" onclick="ui.showLevels()">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"
stroke-linecap="round" stroke-linejoin="round">
<path d="M19 12H5M12 19l-7-7 7-7" />
</svg>
Home
</button>
<div style="font-weight: 700; font-size: 0.95rem; color: var(--text)" id="blt">Level 1</div>
<div style="display: flex; gap: 12px; align-items: center">
<button class="btn btn-g" onclick="ui.showProfile()"
style="padding: 8px 20px; font-size: 0.95rem; white-space: nowrap; display: flex; align-items: center; justify-content: center; gap: 6px">
👤 Profile
</button>
<div class="btn"
style="padding: 8px 20px; font-size: 0.95rem; white-space: nowrap; display: flex; align-items: center; justify-content: center; gap: 6px"
id="bvh"></div>
</div>
</div>
<div class="br-w">
<div class="br-l">
<h2 style="font-size: 1.8rem; margin-bottom: 8px">Course Overview</h2>
<div class="br-prog">
<div class="br-prog-fill" id="br-prog-fill"></div>
</div>
<div style="font-size: 0.85rem; color: var(--muted); text-align: right; margin-bottom: 24px" id="br-prog-label">
0%</div>
<div id="br-syllabus"></div>
</div>
<div class="br-r" id="br-content"></div>
</div>
</div>
<div id="screen-quiz" class="screen">
<div class="tb">
<button class="back-btn" onclick="ui.showLevels()">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"
stroke-linecap="round" stroke-linejoin="round">
<path d="M19 12H5M12 19l-7-7 7-7" />
</svg>
Abort
</button>
<button class="btn btn-g" onclick="ui.showProfile()"
style="padding: 8px 20px; font-size: 0.95rem; white-space: nowrap; display: flex; align-items: center; justify-content: center; gap: 6px">
👤 Profile
</button>
</div>
<div class="qw">
<h2 style="text-align: center; font-size: 2rem; margin-bottom: 24px">Compliance Check</h2>
<div class="qd-cnt" id="qd"></div>
<div id="qa"></div>
<div class="qbot"><button id="qnxt" class="btn btn-p" onclick="ui.nextQ()">Next →</button></div>
</div>
</div>
<div id="screen-results" class="screen">
<div class="res-w">
<div class="rico" id="rico"></div>
<div class="rtit" id="rtit"></div>
<div class="rsub" id="rsub"></div>
<div class="rcard" id="rcard"></div>
<div class="bdx" id="rbdg"></div>
<div class="rbts">
<button class="btn btn-g" onclick="ui.showProfile()">👤 Profile</button>
<button class="btn btn-g" onclick="ui.showLevels()">Modules</button>
<button class="btn btn-p" id="rnxt" onclick="ui.goNext()">Next Module →</button>
</div>
</div>
</div>
<div id="screen-badges" class="screen">
<div class="tb">
<button class="back-btn" onclick="ui.showLevels()">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"
stroke-linecap="round" stroke-linejoin="round">
<path d="M19 12H5M12 19l-7-7 7-7" />
</svg>
Back
</button>
<h2 style="font-size: 1.4rem; margin: 0">Driver Profile</h2>
<button class="btn btn-g" onclick="ui.showProfile()"
style="padding: 8px 20px; font-size: 0.95rem; white-space: nowrap; display: flex; align-items: center; justify-content: center; gap: 6px">
👤 Profile
</button>
</div>
<div class="lw">
<div class="stats-body" id="stats-body"></div>
<h3 style="margin-top: 40px; font-size: 1.5rem">Achievements</h3>
<div class="bgrid" id="bgrid"></div>
</div>
</div>
<div id="screen-certificate" class="screen">
<div class="tb">
<button class="back-btn" onclick="ui.showLevels()">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"
stroke-linecap="round" stroke-linejoin="round">
<path d="M19 12H5M12 19l-7-7 7-7" />
</svg>
Back
</button>
<div style="color: var(--accent); font-weight: 600; font-size: 0.95rem" id="cstat"></div>
<div style="display: flex; gap: 12px; align-items: center">
<button class="btn btn-g" onclick="ui.showProfile()"
style="padding: 8px 20px; font-size: 0.95rem; white-space: nowrap; display: flex; align-items: center; justify-content: center; gap: 6px">
👤 Profile
</button>
<button class="btn btn-p" id="cdownload" onclick="ui.dlCert()"
style="padding: 8px 20px; font-size: 0.95rem; display: flex; align-items: center; justify-content: center; gap: 6px">
📥 Download PDF
</button>
</div>
</div>
<div id="cert-wrapper">
<div class="crt" id="cert">
<div
style="margin-bottom: 30px; display: flex; align-items: center; justify-content: space-between; width: 100%">
<div style="display: flex; gap: 16px; align-items: center">
<div style="text-align: right; margin-right: 4px">
<div
style="font-size: 0.8rem; color: #7c7968; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase">
Partnered With</div>
</div>
<img loading="lazy" id="cert-logo-5" src="mumbai-police-logo.png" style="height: 75px; object-fit: contain"
alt="Logo 1" onerror="this.style.display = 'none'" />
<img loading="lazy" id="cert-logo-6" src="sneh-logo.png" style="height: 75px; object-fit: contain"
alt="Logo 2" onerror="this.style.display = 'none'" />
</div>
</div>
<div
style="font-weight: 600; letter-spacing: 0.1em; color: #7c7968; text-transform: uppercase; font-size: 1.2rem; margin-top: 20px">
Certificate of Completion</div>
<div class="crtn" id="cert-title">Traffic Hero Certification</div>
<div id="cert-icon" style="font-size: 5rem; text-align: center; display: none; margin: 20px 0"></div>
<div class="crt-body" id="cert-doc-text" style="margin-top: 32px">This document certifies that</div>
<div class="crt-course" id="cname">DRIVER</div>
<div class="crt-body" style="margin-bottom: 60px">
has successfully completed the comprehensive traffic regulations and virtual operations training program,
demonstrating mastery across 15 real-world compliance scenarios.
</div>
<div
style="display: flex; justify-content: space-between; align-items: flex-end; width: 100%; margin-top: auto; padding-top: 40px; border-top: 1px solid #e0dfd7; text-align: left">
<div class="crt-meta">
<div style="margin-bottom: 8px"><span
style="font-weight: 600; color: #2c2b25; width: 100px; display: inline-block">Date:</span><span
id="cdate"></span></div>
<div style="margin-bottom: 8px"><span
style="font-weight: 600; color: #2c2b25; width: 100px; display: inline-block">Score:</span><span
id="cscore"></span></div>
<div><span style="font-weight: 600; color: #2c2b25; width: 100px; display: inline-block">Ref ID:</span><span
id="cert-num"></span></div>
</div>
<div style="text-align: right">
<div
style="font-family: 'Lora', serif; font-size: 1.6rem; font-weight: 600; color: #2c2b25; border-bottom: 1px solid #e0dfd7; padding-bottom: 8px; margin-bottom: 8px; width: 200px">
Authorized
</div>
<div style="font-size: 0.95rem; color: #7c7968">Joint Commissioner of Police (Traffic)</div>
</div>
</div>
</div>
</div>
</div>
<script src="cert_assets.js"></script>
<script>
window.LVS = []
</script>
<script src="levels/level1.js"></script>
<script src="levels/level2.js"></script>
<script src="levels/level3.js"></script>
<script src="levels/level4.js"></script>
<script src="levels/level5.js"></script>
<script src="levels/level6.js"></script>
<script src="levels/level7.js"></script>
<script src="levels/level8.js"></script>
<script src="levels/level9.js"></script>
<script src="levels/level10.js"></script>
<script src="levels/level11.js"></script>
<script src="levels/level12.js"></script>
<script src="levels/level13.js"></script>
<script src="levels/level14.js"></script>
<script src="levels/level15.js"></script>
<script src="levels/level16.js"></script>
<script src="levels/level17.js"></script>
<script src="levels/level18.js"></script>
<script src="levels/level19.js"></script>
<script src="levels/level20.js"></script>
<script src="levels/level21.js"></script>
<script src="levels/level22.js"></script>
<script src="levels/level23.js"></script>
<script src="levels/level24.js"></script>
<script src="levels/level25.js"></script>
<script src="levels/level26.js"></script>
<script src="levels/level27.js"></script>
<script src="levels/level28.js"></script>
<script src="levels/level29.js"></script>
<script src="levels/level30.js"></script>
<script src="levels/level31.js"></script>
<script src="levels/level32.js"></script>
<script src="levels/level33.js"></script>
<script src="levels/level34.js"></script>
<script src="levels/level35.js"></script>
<script src="levels/level36.js"></script>
<script src="levels/level37.js"></script>
<script src="levels/level38.js"></script>
<script src="levels/level39.js"></script>
<script src="levels/level40.js"></script>
<script src="levels/level41.js"></script>
<script src="levels/level42.js"></script>
<script src="levels/level43.js"></script>
<script src="levels/level44.js"></script>
<script src="levels/level45.js"></script>
<script src="levels/level46.js"></script>
<script src="levels/level47.js"></script>
<script src="levels/level48.js"></script>
<script src="levels/level49.js"></script>
<script src="levels/level50.js"></script>
<script src="levels/level51.js"></script>
<script src="levels/level52.js"></script>
<script>
// Portrait mode hint (non-blocking)
(function () {
const ov = document.getElementById('rotate-overlay');
if (!ov) return;
function isMobile() { return /Android|iPhone|iPad|iPod/i.test(navigator.userAgent); }
function isPortrait() { return window.innerHeight > window.innerWidth; }
let hideTimer = null;
function update() {
if (isMobile() && isPortrait() && typeof game !== 'undefined' && game && game.playing) {
ov.style.display = 'block';
ov.style.opacity = '1';
clearTimeout(hideTimer);
hideTimer = setTimeout(function () { ov.style.opacity = '0'; }, 3000);
} else {
ov.style.display = 'none';
}
}
window.addEventListener('resize', update);
window.addEventListener('orientationchange', function () { setTimeout(update, 150); });
setInterval(update, 500);
})();
// Show swipe-to-look hint on mobile for 3 seconds after game starts
(function () {
const hint = document.getElementById('swipe-look-hint');
if (!hint) return;
let shown = false;
setInterval(function () {
if (typeof game === 'undefined' || !game || !game.playing || shown) return;
if (/Android|iPhone|iPad|iPod/i.test(navigator.userAgent)) {
hint.style.display = 'block';
shown = true;
setTimeout(function () { hint.style.opacity = '0'; }, 2000);
setTimeout(function () { hint.style.display = 'none'; }, 2500);
}
}, 500);
})();
</script>
<script src="start.js"></script>
<script src="render_core.js"></script>
<script src="pool.js"></script>
<script src="game_core.js"></script>
<script src="ui.js"></script>
<script src="course.js"></script>
<!-- KID MODE HTML OVERLAYS -->
<div id="kid-steer"></div>
<div id="kid-pedals">
<div class="kid-btn go-btn" id="kid-go-btn"></div>
<div class="kid-btn stop-btn" id="kid-stop-btn"></div>
<div class="kid-btn horn-btn" id="kid-horn-btn">📢</div>
</div>
<div id="kid-indicators">
<div class="kid-btn kid-left-btn" id="kid-left-btn"></div>
<div class="kid-btn kid-right-btn" id="kid-right-btn"></div>
<div class="kid-btn kid-headlight-btn" id="kid-headlight-btn">💡</div>
</div>
<div id="kid-hearts"></div>
<div id="kid-level-name"></div>
<div id="kid-task-bar">
<div class="task-emoji" id="kid-task-emoji">🚗</div>
<div class="task-text" id="kid-task-text">Driving...</div>
<div class="task-progress" id="kid-task-progress"></div>
</div>
<div id="kid-countdown">
<div class="count-num" id="kid-count-num">3</div>
</div>
<div id="kid-go-flash">
<div class="go-text">GO!</div>
</div>
<div id="kid-tutorial">
<div class="tut-emoji" id="kid-tut-emoji">🚗</div>
<div class="tut-text" id="kid-tut-text">Turn the wheel to drive!</div>
<div class="tut-arrow" id="kid-tut-arrow" style="display:none">👉</div>
<button class="kid-btn tut-ok" id="kid-tut-ok" onclick="window._kidDismissTutorial()">OK! 👍</button>
</div>
<div id="kid-hand">👆</div>
<div id="kid-intro">
<div class="intro-level" id="kid-intro-level">Level 1</div>
<div class="intro-emoji" id="kid-intro-emoji">🚗</div>
<div class="intro-text" id="kid-intro-text">Let's learn to drive!</div>
<div class="intro-progress" id="kid-intro-dots"></div>
</div>
<div id="kid-celebrate">
<div class="cel-stars" id="kid-cel-stars">⭐⭐⭐</div>
<div class="cel-text" id="kid-cel-text">Amazing!</div>
<div class="cel-sub" id="kid-cel-sub">You did it!</div>
<button class="kid-btn cel-ok" id="kid-cel-ok" onclick="window._kidDismissCelebrate()">Next Level! 🚀</button>
</div>
<div id="kid-crash">
<div class="crash-emoji">😮</div>
<div class="crash-text">Oops!</div>
<div class="crash-sub" id="kid-crash-sub">Be careful next time!</div>
<button class="kid-btn crash-retry" onclick="window._kidRetry()">Try Again! 💪</button>
<button class="kid-btn crash-home" onclick="window._kidGoHome()">Home 🏠</button>
</div>
<div id="kid-offroad">⚠️ Stay on the road!</div>
<div id="kid-unlock-toast">🆕 New control unlocked!</div>
<script>
/* ===== KID MODE ENGINE — Phase 2 ===== */
window.KID_MODE = true; // toggle to enable/disable kid mode
(function () {
if (!window.KID_MODE) return;
const $ = id => document.getElementById(id);
// Track last time each kid control was used (for reminder tooltips)
window._kidLastUsed = { go: 0, stop: 0, horn: 0, indicator: 0, headlight: 0 };
// --- Activate kid mode class on body ---
document.body.classList.add('kid-mode');
// --- Hide complex controls ---
const hideSelectors = [
'#gp', // gear selector
'#spgauge', // speed gauge
'#boostgauge', // boost gauge
'#boost-vignette',
'#speed-lines',
'#boost-ready',
'#civic-controls',
'#sig-ind', // traffic light
'#da', // direction arrow
'#phone-gps',
'#phone-gps-btn',
'#mmc', // minimap canvas
'#challan-stack',
'#challenge-overlay',
'#cov',
'#profile-dlg',
'.nav-user-profile',
'#top-bar-container',
'.swipe-look-hint-wrap'
];
hideSelectors.forEach(sel => {
document.querySelectorAll(sel).forEach(el => el.classList.add('kid-hide'));
});
// Kid start screen: rename "Enter Academy" to "PLAY!"
document.querySelectorAll('#ss .pls .btn-p').forEach(btn => {
btn.textContent = '🎮 PLAY!';
});
// Hide original mobile controls (steering wheel + pedals)
const mc = $('mobile-controls');
if (mc) mc.classList.add('kid-hide');
const tc = $('tc');
if (tc) tc.classList.add('kid-hide');
// Show kid controls
$('kid-hearts').style.display = 'flex';
$('kid-level-name').style.display = 'block';
$('kid-task-bar').style.display = 'block';
requestAnimationFrame(() => { $('kid-task-bar').classList.add('on'); });
// --- Kid steering wheel touch (left/right tap zones) ---
const steer = $('kid-steer');
let steerActive = false;
let steerTouchId = null;
let steerCenterX = 0;
function showSteerAndPedals() {
if (!window.game || !window.game.playing) return;
const lv = window.game._lv || 1;
steer.style.display = 'block';
$('kid-pedals').style.display = 'flex';
// Progressive HUD: indicators only after level 10
if (lv >= 11) {
$('kid-indicators').style.display = 'flex';
$('kid-indicators').classList.add('on');
} else {
$('kid-indicators').classList.remove('on');
setTimeout(() => { $('kid-indicators').style.display = 'none'; }, 350);
}
// Progressive: hide minimap/GPS for early levels
document.body.classList.remove('kid-lvl-low', 'kid-lvl-mid', 'kid-lvl-high');
if (lv <= 5) {
document.body.classList.add('kid-lvl-low');
} else if (lv <= 10) {
document.body.classList.add('kid-lvl-mid');
} else {
document.body.classList.add('kid-lvl-high');
}
}
steer.addEventListener('touchstart', function (e) {
e.preventDefault();
steerActive = true;
steerTouchId = e.changedTouches[0].identifier;
steerCenterX = steer.getBoundingClientRect().left + steer.offsetWidth / 2;
steer.classList.add('touching');
// Determine left or right side of wheel
const x = e.changedTouches[0].clientX;
if (x < steerCenterX) {
window.game.keys['a'] = true;
} else {
window.game.keys['d'] = true;
}
}, { passive: false });
steer.addEventListener('touchmove', function (e) {
e.preventDefault();
if (!steerActive) return;
for (let i = 0; i < e.changedTouches.length; i++) {
if (e.changedTouches[i].identifier === steerTouchId) {
const x = e.changedTouches[i].clientX;
// Release both first
window.game.keys['a'] = false;
window.game.keys['d'] = false;
// Set based on position relative to center
if (x < steerCenterX) {
window.game.keys['a'] = true;
} else {
window.game.keys['d'] = true;
}
}
}
}, { passive: false });
steer.addEventListener('touchend', function (e) {
for (let i = 0; i < e.changedTouches.length; i++) {
if (e.changedTouches[i].identifier === steerTouchId) {
steerActive = false;
steerTouchId = null;
steer.classList.remove('touching');
window.game.keys['a'] = false;
window.game.keys['d'] = false;
}
}
});
steer.addEventListener('touchcancel', function () {
steerActive = false;
steerTouchId = null;
steer.classList.remove('touching');
window.game.keys['a'] = false;
window.game.keys['d'] = false;
});
// --- Pedal buttons ---
// GO (accelerate) — hold (maps to W key)
const goBtn = $('kid-go-btn');
goBtn.addEventListener('touchstart', function (e) {
e.preventDefault();
if (window.game) window.game.keys['w'] = true;
window._kidLastUsed.go = Date.now();
}, { passive: false });
goBtn.addEventListener('touchend', function () {
if (window.game) window.game.keys['w'] = false;
});
goBtn.addEventListener('touchcancel', function () {
if (window.game) window.game.keys['w'] = false;
});
// STOP (brake) — hold (maps to S key)
const stopBtn = $('kid-stop-btn');
stopBtn.addEventListener('touchstart', function (e) {
e.preventDefault();
if (window.game) window.game.keys['s'] = true;
window._kidLastUsed.stop = Date.now();
}, { passive: false });
stopBtn.addEventListener('touchend', function () {
if (window.game) window.game.keys['s'] = false;
});
stopBtn.addEventListener('touchcancel', function () {
if (window.game) window.game.keys['s'] = false;
});
// HORN — tap (calls _horn() method)
$('kid-horn-btn').addEventListener('touchstart', function (e) {
e.preventDefault();
if (window.game && window.game._horn) window.game._horn();
window._kidLastUsed.horn = Date.now();
}, { passive: false });
// --- Indicator buttons ---
// Left indicator (calls toggleTurnSignal(-1))
$('kid-left-btn').addEventListener('touchstart', function (e) {
e.preventDefault();
if (window.game && window.game.toggleTurnSignal) window.game.toggleTurnSignal(-1);
window._kidLastUsed.indicator = Date.now();
}, { passive: false });
// Right indicator (calls toggleTurnSignal(1))
$('kid-right-btn').addEventListener('touchstart', function (e) {
e.preventDefault();
if (window.game && window.game.toggleTurnSignal) window.game.toggleTurnSignal(1);
window._kidLastUsed.indicator = Date.now();
}, { passive: false });
// Headlights (calls toggleHighBeam)
$('kid-headlight-btn').addEventListener('touchstart', function (e) {
e.preventDefault();
if (window.game && window.game.toggleHighBeam) window.game.toggleHighBeam();
window._kidLastUsed.headlight = Date.now();
}, { passive: false });
// --- Update hearts display (5 hearts based on HP%) ---
window._kidUpdateHearts = function (hp, maxHp) {
const hearts = $('kid-hearts');
const pct = maxHp > 0 ? hp / maxHp : 1;
const count = 5;
const full = Math.round(pct * count);
let html = '';
for (let i = 0; i < count; i++) {
html += i < full ? '❤️' : '🖤';
}
hearts.innerHTML = html;
};
// --- Update level name ---
window._kidUpdateLevelName = function (name) {
$('kid-level-name').textContent = '🏁 ' + name;
};
// --- Update task progress ---
window._kidUpdateTask = function (text, progress, emoji) {
$('kid-task-text').textContent = text;
if (emoji) $('kid-task-emoji').textContent = emoji;
// Star progress: show up to 5 stars, fill based on progress
var stars = '';
var total = 5;
var filled = Math.round((progress / 100) * total);
for (var i = 0; i < total; i++) {
stars += '<span class="star ' + (i < filled ? 'filled' : '') + '">⭐</span>';
}
$('kid-task-progress').innerHTML = stars;
};
// --- Show tutorial with animated hand pointer ---
window._kidShowTutorial = function (emoji, text, arrowDir, targetBtnId) {
$('kid-tut-emoji').textContent = emoji || '🚗';
$('kid-tut-text').textContent = text || 'Turn the wheel to drive!';
const arrow = $('kid-tut-arrow');
if (arrowDir === 'left') {
arrow.textContent = '👈';
arrow.style.display = 'block';
} else if (arrowDir === 'right') {
arrow.textContent = '👉';
arrow.style.display = 'block';
} else {
arrow.style.display = 'none';
}
$('kid-tutorial').style.display = 'flex';
// Position animated hand below the target button
const hand = $('kid-hand');
if (targetBtnId) {
const btn = $(targetBtnId);
if (btn) {
const rect = btn.getBoundingClientRect();
hand.style.left = (rect.left + rect.width / 2) + 'px';
hand.style.top = (rect.top - 50) + 'px';
hand.style.display = 'block';
}
} else {
hand.style.display = 'none';
}
};
window._kidDismissTutorial = function () {
$('kid-tutorial').style.display = 'none';
$('kid-hand').style.display = 'none';
if (window.game && window.game.onTutorialDismiss) window.game.onTutorialDismiss();
};
// --- Level intro card — multi-step for first play, single card for repeat ---
var _introSteps = [
{ emoji: '🖐️', text: 'Use left and right buttons to steer!' },
{ emoji: '⛽', text: 'Press the green pedal to go!' },
{ emoji: '🛑', text: 'Press red to stop — you\'ll need it!' },
{ emoji: '🏁', text: 'Follow checkpoints to the finish!' }
];
window._kidShowIntro = function (lvNum, lvName, isFirstPlay, cb) {
var intro = $('kid-intro');
var introEmoji = $('kid-intro-emoji');
var introText = $('kid-intro-text');
var introLevel = $('kid-intro-level');
var dots = $('kid-intro-dots');
if (isFirstPlay && lvNum === 1) {
// Multi-step walkthrough for Level 1 first play
var tier = (document.body.dataset.ageTier || 'adult');
var maxSteps = tier === 'child' ? 2 : tier === 'teen' ? 3 : _introSteps.length;
var steps = _introSteps.slice(0, maxSteps);
var step = 0;
introLevel.textContent = 'Welcome! 🎉';
dots.innerHTML = '';
for (var d = 0; d < steps.length; d++) {
var dot = document.createElement('div');
dot.className = 'intro-dot' + (d === 0 ? ' active' : '');
dots.appendChild(dot);
}
function showStep() {
if (step >= steps.length) {
intro.classList.remove('on');
setTimeout(function () { intro.style.display = 'none'; cb(); }, 400);
return;
}
introEmoji.textContent = steps[step].emoji;
introText.textContent = steps[step].text;
var allDots = dots.querySelectorAll('.intro-dot');
allDots.forEach(function (d, i) { d.classList.toggle('active', i === step); });
intro.style.display = 'flex';
requestAnimationFrame(function () { intro.classList.add('on'); });
setTimeout(function () { step++; showStep(); }, 2500);
}
showStep();
} else {
// Single card for all other levels
introLevel.textContent = 'Level ' + lvNum;
introEmoji.textContent = lvNum <= 3 ? '🚗' : lvNum <= 6 ? '🚦' : lvNum <= 9 ? '🏙️' : '🌙';
introText.textContent = lvName || 'Let\'s go!';
dots.innerHTML = '';
intro.style.display = 'flex';
requestAnimationFrame(function () { intro.classList.add('on'); });
setTimeout(function () {
intro.classList.remove('on');
setTimeout(function () { intro.style.display = 'none'; cb(); }, 400);
}, 2000);
}
};
// --- Show celebration ---
window._kidShowCelebrate = function (stars, text, sub) {
$('kid-cel-stars').textContent = stars || '⭐⭐⭐';
$('kid-cel-text').textContent = text || 'Amazing!';
$('kid-cel-sub').textContent = sub || 'You did it!';
$('kid-celebrate').style.display = 'flex';
spawnConfetti();
};
window._kidDismissCelebrate = function () {
$('kid-celebrate').style.display = 'none';
if (window.ui && window.ui.showStart) window.ui.showStart();
};
// --- Show crash (friendly) ---
window._kidShowCrash = function (msg) {
$('kid-crash-sub').textContent = msg || 'Be careful next time!';
$('kid-crash').style.display = 'flex';
};
window._kidDismissCrash = function () {
$('kid-crash').style.display = 'none';
};
window._kidRetry = function () {
window._kidDismissCrash();
if (window.game && window.game.retryLevel) window.game.retryLevel();
};
window._kidGoHome = function () {
window._kidDismissCrash();
if (window.ui && window.ui.showStart) window.ui.showStart();
};
// --- Show countdown ---
window._kidCountdown = function (callback) {
const cd = $('kid-countdown');
const num = $('kid-count-num');
cd.style.display = 'flex';
let count = 3;
num.textContent = count;
num.style.animation = 'none';
void num.offsetWidth;
num.style.animation = 'kidCountPulse 0.8s ease-in-out';
const iv = setInterval(() => {
count--;
if (count > 0) {
num.textContent = count;
num.style.animation = 'none';
void num.offsetWidth;
num.style.animation = 'kidCountPulse 0.8s ease-in-out';
} else {
num.textContent = 'GO!';
num.style.animation = 'none';
void num.offsetWidth;
num.style.animation = 'kidCountPulse 0.8s ease-in-out';
setTimeout(() => {
cd.style.display = 'none';
if (callback) callback();
}, 600);
clearInterval(iv);
}
}, 800);
};
// --- Show off-road warning ---
window._kidShowOffroad = function () {
const ow = $('kid-offroad');
ow.style.display = 'block';
ow.style.animation = 'none';
void ow.offsetWidth;
ow.style.animation = 'kidShake 0.3s ease-in-out';
clearTimeout(window._kidOffroadTimer);
window._kidOffroadTimer = setTimeout(() => {
ow.style.display = 'none';
}, 1500);
};
// --- Confetti ---
function spawnConfetti() {
const colors = ['#ef4444', '#f59e0b', '#22c55e', '#3b82f6', '#8b5cf6', '#ec4899'];
for (let i = 0; i < 40; i++) {
setTimeout(() => {
const el = document.createElement('div');
el.className = 'kid-confetti';
el.style.left = (Math.random() * 100) + 'vw';
el.style.top = '-10px';
el.style.background = colors[Math.floor(Math.random() * colors.length)];
el.style.width = (6 + Math.random() * 8) + 'px';
el.style.height = (6 + Math.random() * 8) + 'px';
el.style.animationDuration = (1 + Math.random() * 1.5) + 's';
document.body.appendChild(el);
setTimeout(() => el.remove(), 3000);
}, i * 30);
}
}
// --- Hook into game events ---
// Wait for game object, then override _go (crash) and completeLevel (win)
let _kidOrigUh = null; // save original _uh once
let _kidOrigComplete = null; // save original completeLevel once
let _kidOrigCheckTasks = null; // save original _checkTasks once
function _kidHookGame() {
if (!window.game) { setTimeout(_kidHookGame, 200); return; }
// Override crash screen — show kid-friendly "Oops!" instead of legal report
const origGo = window.game._go.bind(window.game);
window.game._go = function (reason) {
origGo(reason);
// After original crash screen appears, show kid overlay on top
setTimeout(() => {
const kidFriendly = reason || 'Be careful next time!';
window._kidShowCrash(kidFriendly);
}, 600);
};
// Save original completeLevel once
if (!_kidOrigComplete) {
_kidOrigComplete = window.game.completeLevel.bind(window.game);
}
// Override level completion — suppress original overlay, show kid celebration
window.game.completeLevel = function () {
if (window.KID_MODE) {
// Call original but remove its overlay after it appears
_kidOrigComplete();
setTimeout(() => {
// Remove the original "MISSION COMPLETE!" overlay
const overlays = document.querySelectorAll('div[style*="z-index:9998"]');
overlays.forEach(el => el.remove());
// Show kid-friendly celebration instead
window._kidShowCelebrate('⭐⭐⭐', 'Amazing!', 'You did it!');
}, 100);
} else {
_kidOrigComplete();
}
};
// Save original _uh once
if (!_kidOrigUh) {
_kidOrigUh = window.game._uh.bind(window.game);
}
// Show kid controls when game starts
const origActualStart = window.game._actualStart.bind(window.game);
window.game._actualStart = function (lv) {
origActualStart(lv);
const lvNum = lv.id || ui.cur || 1;
const isFirstPlay = !localStorage.getItem('kid_tutorial_done');
setTimeout(() => {
showSteerAndPedals();
window._kidUpdateLevelName(lv.name || 'Level ' + lv.id);
// Initialize hearts
window._kidUpdateHearts(100, 100);
// Hook hearts update into _uh (use saved original to avoid stacking)
window.game._uh = function () {
_kidOrigUh();
window._kidUpdateHearts(window.game.hp, 100);
};
// Show intro card (non-blocking overlay)
window._kidShowIntro(lvNum, lv.name || 'Level ' + lvNum, isFirstPlay, function () {
// After intro dismisses, show task tutorial if applicable
if (isFirstPlay && lvNum === 1) {
localStorage.setItem('kid_tutorial_done', '1');
// Show first task tutorial after intro
setTimeout(function () {
var firstTask = window.game.tasks && window.game.tasks.find(function (t) { return !t.done; });
if (firstTask) {
window._kidShowTutorial('🖐️', 'Tap the arrows to steer!', 'right', 'kid-steer');
setTimeout(function () { window._kidDismissTutorial(); }, 4000);
}
}, 300);
// ── E. Progressive tutorial tooltips (levels 2-5) ──
const tutStage = parseInt(localStorage.getItem('kid_tutorial_stage') || '1', 10);
if (lvNum >= 2 && lvNum <= 5 && lvNum > tutStage) {
const tips = {
2: { emoji: '💡', text: 'Use Q/E for turn signals!', dir: 'left', target: 'kid-left-btn' },
3: { emoji: '💡', text: 'Press H for high beam!', dir: 'right', target: 'kid-headlight-btn' },
4: { emoji: '💡', text: 'Press P to park & earn points!', dir: 'right', target: 'kid-stop-btn' },
5: { emoji: '💡', text: 'Follow the GPS arrows!', dir: 'right', target: null },
};
const tip = tips[lvNum];
if (tip) {
setTimeout(function () {
window._kidShowTutorial(tip.emoji, tip.text, tip.dir, tip.target || 'kid-steer');
setTimeout(function () { window._kidDismissTutorial(); }, 5000);
}, 2500);
localStorage.setItem('kid_tutorial_stage', String(lvNum));
}
}
}
});
// Progressive control unlock based on level
const stopBtn = $('kid-stop-btn');
const hornBtn = $('kid-horn-btn');
const leftBtn = $('kid-left-btn');
const rightBtn = $('kid-right-btn');
const headBtn = $('kid-headlight-btn');
// Reset all locks
[stopBtn, hornBtn, leftBtn, rightBtn, headBtn].forEach(b => { if (b) b.classList.remove('kid-locked'); });
// Tier 1 (1-3): steering + gas only
if (lvNum <= 3) {
[stopBtn, hornBtn, leftBtn, rightBtn, headBtn].forEach(b => { if (b) b.classList.add('kid-locked'); });
}
// Tier 2 (4-6): add brake
else if (lvNum <= 6) {
[hornBtn, leftBtn, rightBtn, headBtn].forEach(b => { if (b) b.classList.add('kid-locked'); });
}
// Tier 3 (7-9): add horn
else if (lvNum <= 9) {
[leftBtn, rightBtn, headBtn].forEach(b => { if (b) b.classList.add('kid-locked'); });
}
// Tier 4 (10+): all controls unlocked
// Show unlock toast on tier transitions
const prevTier = parseInt(localStorage.getItem('kid_tier') || '0', 10);
let curTier = 1;
if (lvNum > 9) curTier = 4;
else if (lvNum > 6) curTier = 3;
else if (lvNum > 3) curTier = 2;
if (curTier > prevTier && prevTier > 0) {
const unlockMsgs = { 2: '🆕 Brake unlocked!', 3: '🆕 Horn unlocked!', 4: '🆕 Indicators & lights unlocked!' };
const toast = $('kid-unlock-toast');
toast.textContent = unlockMsgs[curTier] || '🆕 New control!';
toast.style.display = 'block';
setTimeout(() => { toast.style.display = 'none'; }, 3000);
}
localStorage.setItem('kid_tier', curTier.toString());
// Auto-unlock controls needed by current tasks
const neededTargets = (window.game.tasks || []).map(t => t.target);
if (neededTargets.includes('indicator') || neededTargets.includes('hazards')) {
if (leftBtn) leftBtn.classList.remove('kid-locked');
if (rightBtn) rightBtn.classList.remove('kid-locked');
}
if (neededTargets.includes('headlights')) {
if (headBtn) headBtn.classList.remove('kid-locked');
}
const taskMap = {
'stop': { 'red_light': ['🚦', 'Wait for green light!', 'kid-stop-btn'], 'stationary': ['🛑', 'Stop the car!', 'kid-stop-btn'], 'walking_speed': ['🐢', 'Drive slowly!', 'kid-go-btn'], 'parking_zone': ['🅿️', 'Park here!', 'kid-go-btn'], 'parking_spot': ['🅿️', 'Park here!', 'kid-go-btn'] },
'reach': { 'destination': ['🏁', 'Drive to the end!', 'kid-go-btn'], 'green_light': ['🟢', 'Go when green!', 'kid-go-btn'], 'parking_spot': ['🅿️', 'Find parking!', 'kid-go-btn'], 'market_zone': ['🏪', 'Go to market!', 'kid-go-btn'], 'left_side': ['👈', 'Move left!', 'kid-steer'], 'left_lane': ['👈', 'Use left lane!', 'kid-steer'], 'main_road': ['🛣️', 'Get on main road!', 'kid-go-btn'], 'forward_space': ['⬆️', 'Keep going!', 'kid-go-btn'], 'away_gate': ['🚪', 'Drive to exit!', 'kid-go-btn'] },
'avoid': { 'honk': ['🤫', 'No honking!', ''], 'speed_zone': ['🐢', 'Drive slowly!', 'kid-go-btn'], 'speed_night': ['🌙', 'Slow at night!', 'kid-go-btn'], 'speed_puddle': ['💧', 'Avoid puddles!', 'kid-steer'], 'speed_hospital': ['🏥', 'Slow near hospital!', 'kid-go-btn'], 'pedestrian': ['🚶', 'Watch for people!', ''], 'collision': ['🚗', 'Don\'t crash!', ''], 'ambulance': ['🚑', 'Let ambulance pass!', ''] },
'toggle': { 'seatbelt': ['🔒', 'Buckle up!', ''], 'indicator': ['➡️', 'Use indicator!', 'kid-left-btn'], 'headlights': ['💡', 'Turn on lights!', 'kid-headlight-btn'], 'hazards': ['⚠️', 'Turn on hazard lights!', 'kid-left-btn'] }
};
const firstTask = window.game.tasks && window.game.tasks.find(t => !t.done);
// Skip task tutorial if intro already handled it (Level 1 first play)
if (firstTask && !(isFirstPlay && lvNum === 1)) {
const tm = taskMap[firstTask.type] && taskMap[firstTask.type][firstTask.target];
const emoji = tm ? tm[0] : '🚗';
const text = tm ? tm[1] : firstTask.text;
const btnId = tm ? tm[2] : '';
window._kidShowTutorial(emoji, text, '', btnId);
// Auto-dismiss after 4 seconds
setTimeout(() => { window._kidDismissTutorial(); }, 4000);
}
// Hook task completion for kid task bar updates (save original once)
if (!_kidOrigCheckTasks) {
_kidOrigCheckTasks = window.game._checkTasks.bind(window.game);
}
var _kidLastTaskUpdate = 0;
window.game._checkTasks = function () {
_kidOrigCheckTasks();
var now = performance.now();
if (now - _kidLastTaskUpdate < 500) return; // throttle to 2x/sec
_kidLastTaskUpdate = now;
var tasks = window.game.tasks || [];
var doneCount = tasks.filter(function (t) { return t.done; }).length;
var total = tasks.length;
var pct = total > 0 ? (doneCount / total) * 100 : 0;
// For reach destination, use checkpoint progress
var next = tasks.find(function (t) { return !t.done; });
if (next && next.type === 'reach' && next.target === 'destination' && window.game.cps && window.game.cps.length > 0) {
var cpPct = (window.game.hits / window.game.cps.length) * 100;
pct = Math.max(pct, cpPct); // at least the task-based pct
}
if (next) {
var tm = taskMap[next.type] && taskMap[next.type][next.target];
window._kidUpdateTask(tm ? tm[1] : next.text, pct, tm ? tm[0] : null);
} else {
window._kidUpdateTask('All done!', 100, '🎉');
}
};
// Initial task bar
if (firstTask) {
var tm0 = taskMap[firstTask.type] && taskMap[firstTask.type][firstTask.target];
window._kidUpdateTask(tm0 ? tm0[1] : firstTask.text, 0, tm0 ? tm0[0] : null);
}
}, 200);
};
console.log('[Kid Mode] Game hooks installed');
}
_kidHookGame();
console.log('[Kid Mode] Initialized — simplified UI for children');
// --- Reminder tooltips: pulse buttons when unused for 20s during relevant tasks ---
const REMIND_MS = 20000;
const _remindMap = {
'stop': 'stop',
'stationary': 'stop',
'walking_speed': 'stop',
'red_light': 'stop',
'parking_zone': 'stop',
'parking_spot': 'stop',
'indicator': 'indicator',
'hazards': 'indicator',
'headlights': 'headlight'
};
setInterval(() => {
if (!window.game || !window.game.tasks) return;
const nextTask = window.game.tasks.find(t => !t.done);
if (!nextTask) return;
const ctrl = _remindMap[nextTask.target];
if (!ctrl) return;
const last = window._kidLastUsed[ctrl] || 0;
if (Date.now() - last < REMIND_MS) return;
// Don't remind if control is locked
const btnMap = { stop: 'kid-stop-btn', indicator: 'kid-left-btn', headlight: 'kid-headlight-btn' };
const btn = $(btnMap[ctrl]);
if (!btn || btn.classList.contains('kid-locked')) return;
// Pulse the button briefly
btn.style.transition = 'transform 0.3s, box-shadow 0.3s';
btn.style.transform = 'scale(1.25)';
btn.style.boxShadow = '0 0 20px rgba(255,255,0,0.7)';
setTimeout(() => {
btn.style.transform = 'scale(1)';
btn.style.boxShadow = 'none';
}, 1200);
}, 5000);
})();
/* ── Horizon Toggle ── */
function hzInit() {
var t = localStorage.getItem('theme');
if (t === 'light') { document.body.classList.add('lm'); }
else { document.body.classList.remove('lm'); }
hzSync();
}
function hzSync() {
var el = document.querySelector('.hz-toggle');
if (!el) return;
if (document.body.classList.contains('lm')) { el.classList.remove('night'); }
else { el.classList.add('night'); }
}
function hzToggle() {
document.body.classList.toggle('lm');
localStorage.setItem('theme', document.body.classList.contains('lm') ? 'light' : 'dark');
hzSync();
}
hzInit();
</script>
</body>
</html>