- Real-world mode is first person: the view stands at the GPS position at eye height and only real walking moves it; the phone's orientation aims it, with the camera picture (AR) or over the 3D city when the camera is off. Without an orientation sensor it follows the compass or walking direction and can be dragged. The family, car and run buttons are hidden in this mode - Orientation sensors are now separate from the camera stream in ar.js - On phones, dialogue, the buy prompt, banners, points and GPS status sit in the middle of the screen; the prompt can be tapped; the toolbar is one scrollable row and no longer covers the budget and clock Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
356 lines
18 KiB
CSS
356 lines
18 KiB
CSS
:root {
|
|
--hud: rgba(236, 242, 238, .92);
|
|
--dim: rgba(200, 214, 207, .65);
|
|
--panel: rgba(10, 16, 14, .62);
|
|
--panel-solid: rgba(10, 16, 14, .9);
|
|
--line: rgba(220, 235, 228, .2);
|
|
--gold: #f2c94c;
|
|
--teal: #3ee0c9;
|
|
--red: #ff6b5e;
|
|
--font: 'Vazirmatn', Tahoma, sans-serif;
|
|
--safe-top: env(safe-area-inset-top, 0px);
|
|
}
|
|
|
|
* { box-sizing: border-box; }
|
|
|
|
html, body {
|
|
margin: 0;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
background: #0d1412;
|
|
color: var(--hud);
|
|
font-family: var(--font);
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
}
|
|
|
|
button { font: inherit; cursor: pointer; }
|
|
|
|
#world { position: fixed; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
|
|
|
|
#loading {
|
|
position: fixed; inset: 0; display: grid; place-items: center; z-index: 50;
|
|
background: #0d1412; color: var(--teal); font-size: 18px; letter-spacing: .5px;
|
|
}
|
|
|
|
/* ---------- Briefing ---------- */
|
|
#briefing {
|
|
position: fixed; inset: 0; z-index: 40; display: grid; grid-template-columns: 1.3fr 1fr;
|
|
background: radial-gradient(circle at 30% 40%, #16231f 0%, #070b0a 75%);
|
|
}
|
|
#briefing[hidden] { display: none; }
|
|
.brief-map { position: relative; }
|
|
.brief-map::after {
|
|
content: ''; position: absolute; inset: 0; pointer-events: none;
|
|
background: repeating-linear-gradient(0deg, rgba(255, 255, 255, .025) 0 1px, transparent 1px 4px);
|
|
}
|
|
#iran { width: 100%; height: 100%; display: block; }
|
|
.brief-panel {
|
|
align-self: center; margin: 24px; padding: 28px; max-width: 520px;
|
|
background: var(--panel); border: 1px solid var(--line); border-inline-start: 3px solid var(--gold);
|
|
}
|
|
.tag { color: var(--gold); letter-spacing: 1px; font-weight: 600; }
|
|
.brief-panel h1 { margin: 6px 0 0; font-size: clamp(32px, 5vw, 52px); font-weight: 800; }
|
|
.sub { margin: 0 0 18px; color: var(--teal); }
|
|
#brief-text {
|
|
min-height: 190px; margin: 0 0 20px; white-space: pre-wrap; font-family: var(--font);
|
|
font-size: 15px; line-height: 1.9; color: var(--hud);
|
|
}
|
|
#start-mission {
|
|
width: 100%; padding: 14px; border: 0; background: var(--gold); color: #1b1406;
|
|
font-size: 18px; font-weight: 800; letter-spacing: .5px;
|
|
}
|
|
#start-mission:hover { filter: brightness(1.08); }
|
|
.keys { color: var(--dim); font-size: 12px; line-height: 1.8; margin-bottom: 0; }
|
|
|
|
/* ---------- HUD ---------- */
|
|
#hud[hidden] { display: none; }
|
|
#hud > * { position: fixed; z-index: 10; }
|
|
#compass {
|
|
top: 14px; left: 50%; transform: translateX(-50%); width: min(560px, 80vw); height: 44px;
|
|
pointer-events: none;
|
|
}
|
|
#minimap-button {
|
|
top: 18px; left: 18px; width: 220px; height: 220px; padding: 0; border: 0; border-radius: 50%;
|
|
background: transparent;
|
|
}
|
|
#minimap { width: 220px; height: 220px; border-radius: 50%; box-shadow: 0 0 0 4px rgba(0, 0, 0, .35); }
|
|
.map-hint {
|
|
position: absolute; bottom: -4px; left: 50%; transform: translateX(-50%); padding: 2px 10px;
|
|
background: var(--panel-solid); color: var(--dim); font-size: 11px; border: 1px solid var(--line);
|
|
}
|
|
#location { top: 252px; left: 18px; width: 220px; text-align: center; text-shadow: 0 1px 3px #000; }
|
|
#street { display: block; font-weight: 600; font-size: 14px; }
|
|
#mode-label { color: var(--teal); }
|
|
|
|
#objectives {
|
|
top: 18px; right: 18px; width: min(330px, 40vw); padding: 12px 14px;
|
|
background: linear-gradient(270deg, var(--panel) 60%, transparent);
|
|
border-inline-start: 3px solid var(--gold);
|
|
}
|
|
#objectives h2 { margin: 0 0 6px; font-size: 13px; color: var(--gold); letter-spacing: 1px; }
|
|
#mission-list { margin: 0; padding: 0; list-style: none; font-size: 13px; line-height: 1.7; }
|
|
#mission-list li { color: var(--dim); }
|
|
#mission-list li.active { color: var(--hud); font-weight: 600; }
|
|
#mission-list li.active::before { content: '◆ '; color: var(--gold); }
|
|
#mission-list li.done { text-decoration: line-through; opacity: .55; }
|
|
#mission-list li.done::before { content: '✓ '; color: var(--teal); }
|
|
#mission-list li.hidden { display: none; }
|
|
|
|
#waypoint {
|
|
transform: translate(-50%, -100%); display: grid; justify-items: center; gap: 2px;
|
|
pointer-events: none; text-shadow: 0 1px 3px #000; white-space: nowrap;
|
|
}
|
|
#waypoint[hidden] { display: none; }
|
|
#waypoint i { width: 16px; height: 16px; background: var(--gold); transform: rotate(45deg); box-shadow: 0 0 10px rgba(242, 201, 76, .8); }
|
|
#waypoint b { font-size: 12px; }
|
|
#waypoint span { font-size: 12px; color: var(--gold); }
|
|
|
|
#crosshair {
|
|
top: 50%; left: 50%; width: 4px; height: 4px; margin: -2px; border-radius: 50%;
|
|
background: rgba(255, 255, 255, .7); pointer-events: none;
|
|
}
|
|
|
|
#prompt {
|
|
bottom: 28%; left: 50%; transform: translateX(-50%); padding: 8px 18px;
|
|
background: var(--panel-solid); border: 1px solid var(--line); font-weight: 600; white-space: nowrap;
|
|
}
|
|
#prompt kbd {
|
|
display: inline-block; min-width: 26px; margin-inline-end: 8px; padding: 1px 6px; text-align: center;
|
|
background: var(--hud); color: #111; border-radius: 3px; font-family: var(--font); font-weight: 800;
|
|
}
|
|
#subtitle {
|
|
bottom: 12%; left: 50%; transform: translateX(-50%); max-width: min(720px, 90vw); padding: 8px 16px;
|
|
background: rgba(0, 0, 0, .55); text-align: center; font-size: 16px; line-height: 1.7;
|
|
}
|
|
#subtitle b { margin-inline-end: 8px; }
|
|
#subtitle { display: flex; align-items: center; gap: 4px; }
|
|
#subtitle[hidden] { display: none; }
|
|
#subtitle-face { width: 52px; height: 52px; object-fit: cover; border-radius: 50%; margin-inline-end: 10px; background: #f7fcfc; border: 2px solid var(--teal); }
|
|
#subtitle-face[hidden] { display: none; }
|
|
#subtitle b.robot { color: var(--teal); }
|
|
#subtitle b.father { color: var(--gold); }
|
|
#subtitle b.child { color: #ff9f7a; }
|
|
|
|
#awards {
|
|
top: 36%; left: 50%; transform: translateX(-50%); display: grid; justify-items: center; gap: 2px;
|
|
pointer-events: none; text-shadow: 0 2px 4px #000;
|
|
}
|
|
#awards .total { font-size: 34px; font-weight: 800; color: var(--gold); animation: pop .35s ease-out; }
|
|
#awards .line { font-size: 14px; font-weight: 600; animation: slide .4s ease-out backwards; }
|
|
#awards .line b { color: var(--gold); margin-inline-start: 6px; }
|
|
@keyframes pop { from { transform: scale(1.6); opacity: 0; } }
|
|
@keyframes slide { from { transform: translateY(8px); opacity: 0; } }
|
|
|
|
#banner {
|
|
top: 22%; left: 0; right: 0; padding: 14px; text-align: center; pointer-events: none;
|
|
background: linear-gradient(90deg, transparent, rgba(10, 16, 14, .82) 25%, rgba(10, 16, 14, .82) 75%, transparent);
|
|
animation: pop .4s ease-out;
|
|
}
|
|
#banner small { display: block; color: var(--gold); letter-spacing: 2px; font-weight: 600; }
|
|
#banner strong { font-size: 22px; }
|
|
#banner span { display: block; color: var(--teal); font-size: 14px; }
|
|
|
|
#status { bottom: 18px; right: 18px; display: flex; gap: 10px; }
|
|
.stat {
|
|
min-width: 96px; padding: 6px 12px; background: var(--panel); border-bottom: 2px solid var(--teal);
|
|
display: grid; line-height: 1.3;
|
|
}
|
|
.stat small { color: var(--dim); font-size: 11px; }
|
|
.stat strong { font-size: 22px; font-weight: 800; }
|
|
.stat span { font-size: 11px; color: var(--dim); }
|
|
.stat[hidden] { display: none; }
|
|
|
|
#rank { bottom: 18px; left: 50%; transform: translateX(-50%); width: min(380px, 50vw); padding: 4px 10px; text-align: center; font-size: 12px; background: var(--panel); }
|
|
#rank b { color: var(--gold); margin-inline-end: 8px; }
|
|
#rank span { color: var(--dim); }
|
|
#rank .bar { height: 4px; margin-top: 4px; background: rgba(255, 255, 255, .15); }
|
|
#rank .bar i { display: block; height: 100%; width: 0; background: var(--gold); transition: width .6s; }
|
|
|
|
#toolbar { bottom: 18px; left: 18px; display: flex; gap: 6px; }
|
|
#toolbar button, .ghost {
|
|
padding: 6px 12px; background: var(--panel); color: var(--hud); border: 1px solid var(--line); font-size: 12px;
|
|
}
|
|
#attribution { bottom: 2px; left: 50%; transform: translateX(-50%); font-size: 10px; color: var(--dim); }
|
|
|
|
/* ---------- Payment ---------- */
|
|
#payment {
|
|
position: fixed; z-index: 20; bottom: 18%; right: 50%; transform: translateX(50%); width: min(380px, 92vw);
|
|
padding: 16px 18px; background: var(--panel-solid); border: 1px solid var(--line); border-top: 3px solid var(--teal);
|
|
}
|
|
#payment[hidden] { display: none; }
|
|
#payment small { color: var(--teal); }
|
|
#payment h2 { margin: 4px 0; font-size: 18px; }
|
|
#payment p { margin: 0 0 12px; color: var(--dim); }
|
|
.actions { display: flex; gap: 8px; }
|
|
#swipe, #again, #route-button {
|
|
flex: 1; padding: 10px; border: 0; background: var(--teal); color: #04201b; font-weight: 800;
|
|
}
|
|
#swipe:disabled { opacity: .45; cursor: wait; }
|
|
|
|
/* ---------- Dialogs ---------- */
|
|
dialog {
|
|
color: var(--hud); background: var(--panel-solid); border: 1px solid var(--line); padding: 0;
|
|
font-family: var(--font); backdrop-filter: blur(6px);
|
|
}
|
|
dialog::backdrop { background: rgba(0, 0, 0, .45); }
|
|
dialog header { display: flex; justify-content: space-between; align-items: start; gap: 12px; padding: 14px 18px; border-bottom: 1px solid var(--line); }
|
|
.close { width: 34px; height: 34px; background: transparent; color: var(--hud); border: 1px solid var(--line); }
|
|
|
|
#map-dialog { width: min(1180px, 96vw); height: min(760px, 92vh); }
|
|
#map-dialog[open] { display: flex; flex-direction: column; }
|
|
.tabs { display: flex; gap: 4px; }
|
|
.tabs button { padding: 6px 14px; background: transparent; color: var(--dim); border: 1px solid var(--line); }
|
|
.tabs button[aria-selected='true'] { color: #1b1406; background: var(--gold); border-color: var(--gold); font-weight: 600; }
|
|
.map-body { flex: 1; display: grid; grid-template-columns: 1fr 320px; min-height: 0; }
|
|
.map-view { position: relative; min-height: 0; }
|
|
#fullmap, #iran-map { position: absolute; inset: 0; width: 100%; height: 100%; cursor: grab; touch-action: none; }
|
|
#fullmap[hidden], #iran-map[hidden] { display: none; }
|
|
.map-help { position: absolute; bottom: 6px; right: 10px; margin: 0; font-size: 11px; color: var(--dim); pointer-events: none; }
|
|
.map-side { overflow: auto; padding: 14px; border-inline-start: 1px solid var(--line); }
|
|
.map-side h3 { margin: 0 0 10px; font-size: 14px; color: var(--gold); }
|
|
#machine-list { list-style: none; margin: 0 0 12px; padding: 0; display: grid; gap: 6px; }
|
|
#machine-list button {
|
|
width: 100%; display: grid; grid-template-columns: 28px 1fr auto; align-items: center; gap: 8px; text-align: right;
|
|
padding: 8px; background: rgba(255, 255, 255, .04); color: var(--hud); border: 1px solid transparent;
|
|
}
|
|
#machine-list button.selected { border-color: var(--gold); }
|
|
#machine-list button.locked { opacity: .45; cursor: default; }
|
|
#machine-list .meta { display: block; color: var(--dim); font-size: 11px; }
|
|
#machine-list .dist { color: var(--gold); font-size: 12px; white-space: nowrap; }
|
|
#machine-detail { margin-bottom: 12px; padding: 12px; background: rgba(255, 255, 255, .05); border-top: 2px solid var(--gold); }
|
|
#machine-detail h4 { margin: 0 0 6px; }
|
|
#machine-detail p { margin: 0 0 8px; font-size: 13px; color: var(--dim); line-height: 1.7; }
|
|
#route-button { width: 100%; }
|
|
|
|
#shop { width: min(620px, 94vw); }
|
|
#shop small { color: var(--teal); }
|
|
#shop h2 { margin: 2px 0; }
|
|
#shop header p { margin: 0; color: var(--dim); font-size: 13px; }
|
|
#shop-note { margin: 10px 18px 0; color: var(--gold); font-size: 13px; }
|
|
#products { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 8px; padding: 14px 18px; }
|
|
#products button {
|
|
display: grid; gap: 2px; justify-items: center; padding: 12px 8px; color: var(--hud);
|
|
background: rgba(255, 255, 255, .05); border: 1px solid var(--line);
|
|
}
|
|
#products button:hover:not(:disabled) { border-color: var(--teal); background: rgba(62, 224, 201, .1); }
|
|
#products button:disabled { opacity: .4; cursor: not-allowed; }
|
|
#products .icon { font-size: 30px; }
|
|
#products .price { color: var(--gold); font-weight: 600; }
|
|
#products s { color: var(--dim); font-size: 11px; }
|
|
.rate { margin: 0; padding: 0 18px 14px; font-size: 11px; color: var(--dim); }
|
|
|
|
#end { width: min(480px, 92vw); padding: 24px; text-align: center; border-top: 3px solid var(--gold); }
|
|
#end small { color: var(--gold); letter-spacing: 1px; }
|
|
#end h2 { margin: 6px 0; font-size: 28px; }
|
|
#end-stats { list-style: none; padding: 0; margin: 14px 0; display: grid; gap: 4px; }
|
|
#end-stats li { display: flex; justify-content: space-between; padding: 6px 10px; background: rgba(255, 255, 255, .05); }
|
|
|
|
/* ---------- Touch ---------- */
|
|
#touch[hidden] { display: none; }
|
|
#touch { inset: auto 0 90px 0; display: flex; justify-content: space-between; padding: 0 24px; pointer-events: none; }
|
|
#stick {
|
|
width: 130px; height: 130px; border-radius: 50%; border: 2px solid var(--line); background: rgba(0, 0, 0, .25);
|
|
position: relative; pointer-events: auto; touch-action: none; order: 2;
|
|
}
|
|
#stick i { position: absolute; left: 40px; top: 40px; width: 50px; height: 50px; border-radius: 50%; background: rgba(255, 255, 255, .5); }
|
|
.touch-buttons { display: grid; gap: 10px; pointer-events: auto; align-content: end; }
|
|
.touch-buttons button {
|
|
width: 64px; height: 64px; border-radius: 50%; background: var(--panel); color: var(--hud); border: 1px solid var(--line); font-weight: 800;
|
|
}
|
|
.touch-buttons button.on { background: var(--teal); color: #04201b; }
|
|
|
|
@media (max-width: 760px) {
|
|
#briefing { grid-template-columns: 1fr; grid-template-rows: 38vh 1fr; }
|
|
.brief-panel { margin: 0 16px 16px; padding: 18px; align-self: start; }
|
|
#brief-text { min-height: 150px; font-size: 13px; }
|
|
#minimap-button, #minimap { width: 130px; height: 130px; }
|
|
#minimap-button { top: 62px; left: 12px; }
|
|
#location { top: 198px; left: 12px; width: 130px; }
|
|
#objectives { top: 62px; right: 12px; width: 52vw; padding: 8px; }
|
|
#mission-list li:not(.active) { display: none; }
|
|
#status { right: 12px; bottom: 12px; }
|
|
.stat { min-width: 70px; padding: 4px 8px; }
|
|
.stat strong { font-size: 16px; }
|
|
#rank { bottom: auto; top: 6px; width: 70vw; }
|
|
#compass { top: 30px; }
|
|
#toolbar { left: 12px; bottom: 12px; }
|
|
#toolbar button { padding: 4px 8px; font-size: 11px; }
|
|
#crosshair { display: none; }
|
|
.map-body { grid-template-columns: 1fr; grid-template-rows: 1fr 40%; }
|
|
.map-side { border-inline-start: 0; border-top: 1px solid var(--line); }
|
|
}
|
|
|
|
/* ---------- PWA: install and update ---------- */
|
|
.brief-robot { float: left; width: 110px; height: auto; margin: -8px 0 0 8px; filter: drop-shadow(0 8px 18px rgba(62, 224, 201, .25)); }
|
|
.brief-actions { display: flex; gap: 8px; margin-top: 8px; }
|
|
.brief-actions button { flex: 1; padding: 10px; font-size: 13px; }
|
|
.brief-actions button[hidden] { display: none; }
|
|
#ios-install { width: min(420px, 92vw); padding: 22px; border-top: 3px solid var(--teal); }
|
|
#ios-install h2 { margin: 0 0 8px; }
|
|
#ios-install ol { line-height: 2; padding-inline-start: 20px; }
|
|
#ios-install button { width: 100%; padding: 10px; border: 0; background: var(--teal); color: #04201b; font-weight: 800; }
|
|
#update-toast {
|
|
position: fixed; z-index: 60; top: calc(12px + var(--safe-top)); left: 50%; transform: translateX(-50%);
|
|
padding: 8px 10px 8px 14px; background: var(--panel-solid); border: 1px solid var(--teal); display: flex; gap: 10px; align-items: center;
|
|
}
|
|
#update-toast[hidden] { display: none; }
|
|
#update-toast button { padding: 4px 12px; border: 0; background: var(--teal); color: #04201b; font-weight: 800; }
|
|
@media (max-width: 760px) { .brief-robot { width: 70px; } }
|
|
|
|
/* ---------- Real-world mode ---------- */
|
|
#gps-status { display: block; margin-top: 2px; color: var(--gold); font-size: 12px; }
|
|
#gps-status[hidden] { display: none; }
|
|
#gps-toggle.on { background: var(--teal); color: #04201b; border-color: var(--teal); }
|
|
#real-intro { width: min(460px, 92vw); padding: 22px; border-top: 3px solid var(--gold); }
|
|
#real-intro img { float: left; border-radius: 50%; background: #f7fcfc; }
|
|
#real-intro h2 { margin: 0 0 8px; }
|
|
#real-intro p, #real-intro li { line-height: 1.9; font-size: 14px; }
|
|
#real-intro ul { padding-inline-start: 18px; color: var(--dim); }
|
|
#real-go { flex: 1; padding: 10px; border: 0; background: var(--gold); color: #1b1406; font-weight: 800; }
|
|
|
|
/* ---------- Camera AR view ---------- */
|
|
#camera { position: fixed; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; background: #000; }
|
|
#camera[hidden] { display: none; }
|
|
#world { z-index: 1; }
|
|
body.ar { background: #000; }
|
|
body.ar #crosshair { display: none; }
|
|
#ar-toggle[hidden], #stable-toggle[hidden] { display: none; }
|
|
#ar-toggle.on, #stable-toggle.on { background: var(--teal); color: #04201b; border-color: var(--teal); }
|
|
|
|
/* ---------- Real-world first person, and notifications in the middle on phones ---------- */
|
|
body.real #crosshair, body.real #stick { display: none; }
|
|
#prompt { cursor: pointer; }
|
|
@media (max-width: 760px), (max-height: 520px) {
|
|
#banner { top: 24%; padding: 10px; }
|
|
#banner strong { font-size: 18px; }
|
|
#awards { top: 36%; }
|
|
#subtitle {
|
|
top: 50%; bottom: auto; transform: translate(-50%, -50%); width: min(92vw, 520px);
|
|
font-size: 15px; background: rgba(0, 0, 0, .7); border-radius: 10px;
|
|
}
|
|
#prompt { top: 64%; bottom: auto; font-size: 16px; padding: 12px 20px; border-radius: 10px; white-space: normal; text-align: center; max-width: 90vw; }
|
|
#gps-status {
|
|
position: fixed; top: 76px; left: 50%; transform: translateX(-50%); width: max-content; max-width: 92vw;
|
|
padding: 4px 12px; background: var(--panel-solid); border: 1px solid var(--line); border-radius: 999px; text-align: center;
|
|
}
|
|
#payment { bottom: auto; top: 50%; transform: translate(50%, -50%); }
|
|
}
|
|
/* Phone layout: one scrollable toolbar row, stats above it, compact rank bar above the compass,
|
|
and no car or run buttons while the GPS moves the player. */
|
|
body.real .touch-buttons [data-key='f'], body.real .touch-buttons [data-key='shift'] { display: none; }
|
|
@media (max-width: 760px) {
|
|
#rank { top: 4px; padding: 2px 8px; font-size: 11px; }
|
|
#rank .bar { height: 3px; margin-top: 2px; }
|
|
#compass { top: 36px; }
|
|
/* Between the mission banner and the dialogue, clear of the minimap and mission list. */
|
|
#gps-status { top: 42%; }
|
|
#toolbar {
|
|
left: 8px; right: 8px; bottom: 8px; overflow-x: auto; flex-wrap: nowrap; scrollbar-width: none;
|
|
padding-bottom: env(safe-area-inset-bottom, 0px);
|
|
}
|
|
#toolbar button { flex: 0 0 auto; white-space: nowrap; }
|
|
#status { bottom: 52px; }
|
|
#touch { bottom: 150px; }
|
|
}
|