| <!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> |
|
|
| |
| <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) |
| } |
| |
| |
| 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-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); |
| } |
| |
| |
| #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); |
| } |
| |
| |
| .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-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 { |
| 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; |
| } |
| |
| |
| .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); |
| } |
| |
| |
| .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; |
| } |
| |
| |
| @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); |
| } |
| } |
| |
| |
| .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); |
| } |
| |
| |
| .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; |
| } |
| |
| |
| .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; |
| } |
| |
| |
| .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); |
| } |
| |
| |
| #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); |
| |
| 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; |
| } |
| |
| |
| @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; } |
| } |
| |
| |
| #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; |
| } |
| } |
| |
| |
| #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.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); |
| } |
| |
| |
| #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; |
| } |
| |
| |
| #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-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; |
| } |
| |
| |
| } |
| |
| |
| .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); |
| } |
| } |
| |
| |
| .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); |
| } |
| } |
| |
| |
| .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); |
| } |
| } |
| |
| |
| .btn:hover { |
| transform: translateY(-2px) scale(1.02); |
| } |
| |
| .btn:active { |
| transform: translateY(0) scale(0.98); |
| } |
| |
| |
| #toast { |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
| } |
| |
| |
| #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); |
| } |
| |
| |
| #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); |
| } |
| |
| |
| |
| |
| |
| @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); |
| } |
| |
| |
| |
| |
| .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; |
| } |
| |
| |
| .lv-track { |
| grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr)); |
| } |
| |
| |
| @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; |
| } |
| } |
| |
| |
| |
| |
| |
| @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; |
| } |
| |
| |
| #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; |
| } |
| |
| |
| #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; |
| } |
| } |
| |
| |
| #cert-wrapper { |
| width: 100%; |
| display: flex; |
| justify-content: center; |
| overflow-x: auto; |
| flex-shrink: 0; |
| } |
| |
| .crt { |
| |
| } |
| |
| |
| @media (max-width: 1400px), |
| (max-height: 800px), |
| (pointer: coarse) { |
| |
| |
| #gc { |
| position: fixed; |
| inset: 0; |
| width: 100vw; |
| height: 100vh; |
| z-index: var(--z-canvas); |
| background: #000; |
| } |
| |
| |
| #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; |
| } |
| |
| |
| #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; |
| } |
| |
| |
| #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 { |
| bottom: 6px; |
| padding: 0 10px; |
| background: none; |
| } |
| |
| |
| #steer-wheel-container { |
| width: 86px; |
| height: 86px; |
| border-width: 2px; |
| background: rgba(0, 0, 0, 0.25); |
| margin-left: 24px; |
| } |
| |
| |
| .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-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.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; |
| } |
| } |
| |
| |
| @media (max-width: 400px) { |
| #sig-ind { |
| display: none !important; |
| } |
| } |
| |
| |
| @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 { |
| 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-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-hide { |
| display: none !important; |
| } |
| |
| |
| .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-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-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-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 { |
| 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-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-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: 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-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); |
| } |
| |
| |
| body.kid-lvl-low #mmc { |
| display: none !important; |
| } |
| |
| body.kid-lvl-low #phone-gps { |
| display: none !important; |
| } |
| |
| |
| #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 { |
| 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 { |
| 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-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); |
| } |
| |
| |
| #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-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 { |
| 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-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-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-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 #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; |
| } |
| |
| |
| .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; |
| } |
| |
| |
| .kid-mode #screen-briefing .tb .btn-g { |
| display: none !important; |
| } |
| |
| |
| .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; |
| } |
| } |
| |
| |
| |
| [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; |
| } |
| |
| |
| [data-age-tier="teen"] #kid-hearts { |
| font-size: 2.8rem; |
| } |
| |
| [data-age-tier="teen"] #kid-task-bar .task-text { |
| font-size: 1.4rem; |
| } |
| |
| |
| [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; |
| } |
| |
| |
| |
| |
| @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; |
| } |
| } |
| |
| |
| @media (max-width: 600px) { |
| * { |
| -webkit-backdrop-filter: none !important; |
| backdrop-filter: none !important; |
| } |
| } |
| |
| |
| 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; |
| } |
| |
| |
| .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; } |
| |
| .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 { |
| 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; } |
| |
| |
| .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); } |
| } |
| |
| |
| .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; } |
| } |
| |
| |
| .civic-icon { width: 16px; height: 16px; flex-shrink: 0; } |
| .civic-label { white-space: nowrap; line-height: 1; } |
| |
| |
| #btn-seatbelt .civic-label.off { color: #ff3b30; } |
| #btn-seatbelt .civic-label.on { color: #27ae60; } |
| |
| |
| @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; } |
| } |
| |
| |
| |
| |
| |
| @media (prefers-reduced-motion: reduce) { |
| *, *::before, *::after { |
| animation-duration: 0.01ms !important; |
| animation-iteration-count: 1 !important; |
| transition-duration: 0.01ms !important; |
| } |
| } |
| |
| |
| |
| @media (hover: none) { |
| .syl-item:hover, |
| .lcard:hover, |
| .bcard:not(.lk):hover { |
| transform: none; |
| box-shadow: inherit; |
| } |
| } |
| |
| |
| .btn:active, .btn-g:active, .back-btn:active { |
| transform: scale(0.98); |
| transition-duration: 0.1s; |
| } |
| |
| |
| |
| |
| html, body { overflow-x: hidden; } |
| |
| h1, h2, h3 { text-wrap: balance; } |
| p, .ltg, .syl-sub { text-wrap: pretty; } |
| |
| |
| #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"> |
| |
|
|
| |
| <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> |
|
|
| |
| <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> |
|
|
| |
| <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> |
|
|
| |
| <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> |
|
|
| |
| <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 → |
| </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> |
|
|
| |
|
|
| <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> |
| |
| (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); |
| })(); |
| |
| (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> |
|
|
| |
| <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> |
| |
| window.KID_MODE = true; |
| |
| (function () { |
| if (!window.KID_MODE) return; |
| |
| const $ = id => document.getElementById(id); |
| |
| window._kidLastUsed = { go: 0, stop: 0, horn: 0, indicator: 0, headlight: 0 }; |
| |
| |
| document.body.classList.add('kid-mode'); |
| |
| |
| const hideSelectors = [ |
| '#gp', |
| '#spgauge', |
| '#boostgauge', |
| '#boost-vignette', |
| '#speed-lines', |
| '#boost-ready', |
| '#civic-controls', |
| '#sig-ind', |
| '#da', |
| '#phone-gps', |
| '#phone-gps-btn', |
| '#mmc', |
| '#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')); |
| }); |
| |
| |
| document.querySelectorAll('#ss .pls .btn-p').forEach(btn => { |
| btn.textContent = '🎮 PLAY!'; |
| }); |
| |
| |
| const mc = $('mobile-controls'); |
| if (mc) mc.classList.add('kid-hide'); |
| const tc = $('tc'); |
| if (tc) tc.classList.add('kid-hide'); |
| |
| |
| $('kid-hearts').style.display = 'flex'; |
| $('kid-level-name').style.display = 'block'; |
| $('kid-task-bar').style.display = 'block'; |
| requestAnimationFrame(() => { $('kid-task-bar').classList.add('on'); }); |
| |
| |
| 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'; |
| |
| 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); |
| } |
| |
| 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'); |
| |
| 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; |
| |
| window.game.keys['a'] = false; |
| window.game.keys['d'] = false; |
| |
| 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; |
| }); |
| |
| |
| |
| 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; |
| }); |
| |
| |
| 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; |
| }); |
| |
| |
| $('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 }); |
| |
| |
| |
| $('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 }); |
| |
| $('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 }); |
| |
| $('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 }); |
| |
| |
| 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; |
| }; |
| |
| |
| window._kidUpdateLevelName = function (name) { |
| $('kid-level-name').textContent = '🏁 ' + name; |
| }; |
| |
| |
| window._kidUpdateTask = function (text, progress, emoji) { |
| $('kid-task-text').textContent = text; |
| if (emoji) $('kid-task-emoji').textContent = emoji; |
| |
| 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; |
| }; |
| |
| |
| 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'; |
| |
| 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(); |
| }; |
| |
| |
| 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) { |
| |
| 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 { |
| |
| 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); |
| } |
| }; |
| |
| |
| 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(); |
| }; |
| |
| |
| 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(); |
| }; |
| |
| |
| 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); |
| }; |
| |
| |
| 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); |
| }; |
| |
| |
| 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); |
| } |
| } |
| |
| |
| |
| let _kidOrigUh = null; |
| let _kidOrigComplete = null; |
| let _kidOrigCheckTasks = null; |
| |
| function _kidHookGame() { |
| if (!window.game) { setTimeout(_kidHookGame, 200); return; } |
| |
| |
| const origGo = window.game._go.bind(window.game); |
| window.game._go = function (reason) { |
| origGo(reason); |
| |
| setTimeout(() => { |
| const kidFriendly = reason || 'Be careful next time!'; |
| window._kidShowCrash(kidFriendly); |
| }, 600); |
| }; |
| |
| |
| if (!_kidOrigComplete) { |
| _kidOrigComplete = window.game.completeLevel.bind(window.game); |
| } |
| |
| window.game.completeLevel = function () { |
| if (window.KID_MODE) { |
| |
| _kidOrigComplete(); |
| setTimeout(() => { |
| |
| const overlays = document.querySelectorAll('div[style*="z-index:9998"]'); |
| overlays.forEach(el => el.remove()); |
| |
| window._kidShowCelebrate('⭐⭐⭐', 'Amazing!', 'You did it!'); |
| }, 100); |
| } else { |
| _kidOrigComplete(); |
| } |
| }; |
| |
| |
| if (!_kidOrigUh) { |
| _kidOrigUh = window.game._uh.bind(window.game); |
| } |
| |
| |
| 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); |
| |
| window._kidUpdateHearts(100, 100); |
| |
| window.game._uh = function () { |
| _kidOrigUh(); |
| window._kidUpdateHearts(window.game.hp, 100); |
| }; |
| |
| |
| window._kidShowIntro(lvNum, lv.name || 'Level ' + lvNum, isFirstPlay, function () { |
| |
| if (isFirstPlay && lvNum === 1) { |
| localStorage.setItem('kid_tutorial_done', '1'); |
| |
| 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); |
| |
| 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)); |
| } |
| } |
| } |
| }); |
| |
| |
| 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'); |
| |
| [stopBtn, hornBtn, leftBtn, rightBtn, headBtn].forEach(b => { if (b) b.classList.remove('kid-locked'); }); |
| |
| if (lvNum <= 3) { |
| [stopBtn, hornBtn, leftBtn, rightBtn, headBtn].forEach(b => { if (b) b.classList.add('kid-locked'); }); |
| } |
| |
| else if (lvNum <= 6) { |
| [hornBtn, leftBtn, rightBtn, headBtn].forEach(b => { if (b) b.classList.add('kid-locked'); }); |
| } |
| |
| else if (lvNum <= 9) { |
| [leftBtn, rightBtn, headBtn].forEach(b => { if (b) b.classList.add('kid-locked'); }); |
| } |
| |
| |
| 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()); |
| |
| 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); |
| |
| 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); |
| |
| setTimeout(() => { window._kidDismissTutorial(); }, 4000); |
| } |
| |
| |
| 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; |
| _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; |
| |
| 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); |
| } |
| 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, '🎉'); |
| } |
| }; |
| |
| 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'); |
| |
| |
| 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; |
| |
| 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; |
| |
| 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); |
| })(); |
| |
| |
| 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> |