1<!doctype html> 2<html lang="en"> 3 <head> 4 <meta charset="UTF-8" /> 5<!-- This site is hosted on Netlify. Anyone can build and deploy a site 6 like this one for free: https://netlify.new/?utm_campaign=loops&utm_source=ai-legible&utm_medium=owned&utm_content=comment&utm_id=1fe3d183-3abe-4706-827a-78a480fb68b7 7 Netlify hosting facts for this site: static/SSR served via Netlify Edge. --> 8 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 9 <title>Doors to Manual</title> 10 11 <!-- Jam widget (required for Cursor Vibe Jam 2026) --> 12
12<script async src="https://vibej.am/2026/widget.js"></script>
12 13 14 <!-- Pixel font --> 15 <link rel="preconnect" href="https://fonts.googleapis.com" /> 16 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> 17 <link 18 href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" 19 rel="stylesheet" 20 /> 21 22 <!-- Phaser 3 --> 23
23<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/phaser.min.js"></script>
23 24 25 <style> 26 *, 27 *::before, 28 *::after { 29 box-sizing: border-box; 30 margin: 0; 31 padding: 0; 32 } 33 34 html, 35 body { 36 width: 100%; 37 height: 100%; 38 background: #000; 39 display: flex; 40 flex-direction: column; 41 align-items: center; 42 justify-content: center; 43 overflow: hidden; 44 font-family: "Press Start 2P", monospace; 45 } 46 47 #game-wrapper { 48 display: flex; 49 flex-direction: column; 50 align-items: center; 51 position: relative; 52 } 53 54 /* Canvas column: Phaser canvas + ? overlay (bottom-right on black margin, above footer HUD) */ 55 .game-stage { 56 position: relative; 57 width: 800px; 58 flex-shrink: 0; 59 } 60 .game-stage__legend-btn { 61 position: absolute; 62 right: 14px; 63 bottom: 10px; 64 z-index: 5; 65 font-family: "Press Start 2P", monospace; 66 font-size: 11px; 67 line-height: 1; 68 color: #ffffff; 69 background: #1b2a4a; 70 border: 2px solid #000; 71 width: 30px; 72 height: 30px; 73 padding: 0; 74 cursor: pointer; 75 } 76 .game-stage__legend-btn:hover, 77 .game-stage__legend-btn:focus-visible { 78 color: #00ff7f; 79 outline: none; 80 } 81 82 /* Full-page controls modal (DOM only â no Phaser) */ 83 .legend-modal { 84 position: fixed; 85 inset: 0; 86 z-index: 10000; 87 display: none; 88 align-items: center; 89 justify-content: center; 90 padding: 24px; 91 } 92 .legend-modal.is-open { 93 display: flex; 94 } 95 .legend-modal__backdrop { 96 position: absolute; 97 inset: 0; 98 background: rgba(0, 0, 0, 0.75); 99 } 100 .legend-modal__panel { 101 position: relative; 102 z-index: 1; 103 width: 100%; 104 max-width: 720px; 105 max-height: 88vh; 106 overflow: auto; 107 background: #f5f0e8; 108 color: #1b2a4a; 109 font-family: "Press Start 2P", monospace; 110 border: 3px solid #1b2a4a; 111 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.45); 112 padding: 22px 26px 20px; 113 } 114 .legend-modal__title { 115 font-size: 16px; 116 color: #ff4444; 117 text-align: center; 118 margin-bottom: 18px; 119 line-height: 1.4; 120 } 121 .legend-modal__section { 122 margin-bottom: 16px; 123 } 124 .legend-modal__h { 125 font-size: 10px; 126 color: #1b2a4a; 127 margin-bottom: 8px; 128 letter-spacing: 0.04em; 129 } 130 .legend-modal__p { 131 font-size: 8px; 132 line-height: 1.65; 133 margin: 0 0 6px; 134 white-space: pre-line; 135 } 136 .legend-modal__actions { 137 display: flex; 138 justify-content: flex-end; 139 align-items: center; 140 margin-top: 18px; 141 padding-top: 12px; 142 border-top: 2px solid #1b2a4a; 143 } 144 .legend-modal__close { 145 font-family: "Press Start 2P", monospace; 146 font-size: 9px; 147 color: #1b2a4a; 148 background: #e8e0d4; 149 border: 2px solid #1b2a4a; 150 padding: 10px 16px; 151 cursor: pointer; 152 } 153 .legend-modal__close:hover { 154 background: #d8d0c8; 155 } 156 157 /* HUD bars â DOM elements, sit above/below canvas */ 158 .hud { 159 width: 800px; 160 background: #1b2a4a; 161 font-size: 11px; 162 color: #fff; 163 padding: 11px 16px; 164 display: flex; 165 align-items: center; 166 justify-content: space-between; 167 letter-spacing: 0.05em; 168 flex-shrink: 0; 169 } 170 .hud--top { 171 border-bottom: 2px solid #000; 172 } 173 .hud--bottom { 174 border-top: 2px solid #000; 175 justify-content: center; 176 gap: 40px; 177 } 178 .hud__armed { 179 color: #ff4444; 180 } 181 .hud__route { 182 color: #ffffff; 183 } 184 .hud__timer { 185 color: #ffff44; 186 } 187 .hud__timer.is-warning { 188 color: #ff4444; 189 animation: timerPulse 0.6s steps(2, end) infinite; 190 } 191 .hud__score { 192 color: #00ff7f; 193 } 194 .hud__phase { 195 color: #aaaaaa; 196 } 197 .hint-bar { 198 width: 800px; 199 background: #0f1a2e; 200 color: #d8d8d8; 201 font-size: 9px; 202 padding: 8px 12px; 203 letter-spacing: 0.03em; 204 text-align: center; 205 border-top: 2px solid #000; 206 } 207 @keyframes timerPulse { 208 0%, 209 100% { 210 opacity: 1; 211 } 212 50% { 213 opacity: 0.55; 214 } 215 } 216 217 /* Phaser mounts its canvas here */ 218 #game-container canvas { 219 display: block !important; 220 image-rendering: pixelated; 221 image-rendering: crisp-edges; 222 border-left: 3px solid #111; 223 border-right: 3px solid #111; 224 } 225 </style> 226 </head> 227 <body> 228 <div id="game-wrapper"> 229 <header class="hud hud--top">
230 <span class="hud__armed" id="hud-armed">DOORS TO ARMED</span> 231 <span class="hud__route" id="hud-route">SIN â KUL</span> 232 <span class="hud__timer" id="hud-timer">04:00</span> 233 </header> 234 235 <div id="game-stage" class="game-stage"> 236 <div id="game-container"></div> 237 <button 238 type="button" 239 class="game-stage__legend-btn" 240 id="legend-open-btn" 241 aria-label="Show controls" 242 > 243 ? 244 </button> 245 </div> 246 247 <footer class="hud hud--bottom"> 248 <span class="hud__score" id="hud-score">SCORE: 0</span> 249 <span class="hud__phase" id="hud-phase">PHASE: BOARDING</span> 250 </footer> 251 <div class="hint-bar" id="hint-bar">SPACEBAR â Begin service</div> 252 </div> 253 254 <div 255 id="legend-modal" 256 class="legend-modal" 257 aria-hidden="true" 258 role="dialog" 259 aria-labelledby="legend-modal-title" 260 > 261 <div class="legend-modal__backdrop" id="legend-modal-backdrop"></div> 262 <div class="legend-modal__panel"> 263 <div class="legend-modal__title" id="legend-modal-title">
264 DOORS TO MANUAL 265 </div> 266 267 <div class="legend-modal__section"> 268 <div class="legend-modal__h">MOVEMENT</div> 269 <p class="legend-modal__p"> 270 Arrow Up / Down or W / S â move along aisle<br /> 271 A / D â face seats (A: D/E/F, D: A/B/C)<br /> 272 Arrow Left / Right â select seat in current row 273 </p> 274 </div> 275 276 <div class="legend-modal__section"> 277 <div class="legend-modal__h">INTERACTION</div> 278 <p class="legend-modal__p"> 279 Arrow Up / Down or W / S â move along aisle<br /> 280 A / D â face seats (A: D/E/F side, D: A/B/C side)<br /> 281 Arrow Left / Right â select seat in current row<br /> 282 SPACEBAR â start service / open drink prompt<br /> 283 SHIFT â skip sleeping passenger (no penalty)<br /> 284 1 OJ | 2 Water | 3 Wine â select drink when prompt open<br /> 285 C â collect cup during collection phase<br /> 286 ESC â cancel / deselect 287 </p> 288 </div> 289 290 <div class="legend-modal__section"> 291 <div class="legend-modal__h">SCORING HINTS</div> 292 <p class="legend-modal__p"> 293 Correct drink served â +10<br /> 294 Wrong drink â -10<br /> 295 Wake sleeping passenger â -15<br /> 296 Cup collected â +3<br /> 297 Missed cup at collection end â -5<br /> 298 Finish phase early â +1 per second saved 299 </p> 300 </div> 301 302 <div class="legend-modal__actions"> 303 <button 304 type="button" 305 class="legend-modal__close" 306 id="legend-close-btn" 307 > 308 CLOSE 309 </button> 310 </div> 311 </div> 312 </div> 313 314 <!-- Portal: parse URL params on load --> 315
315<script> 316 (function () { 317 const params = new URLSearchParams(window.location.search); 318 window.portalTrue = params.get("portal") === "true"; 319 window.portalRef = params.get("ref") || ""; 320 })(); 321 </script>
321 322 323
323<script src="js/constants.js"></script>
323 324
324<script src="js/textures.js"></script>
324 325
325<script src="js/scenes/CharacterSelectScene.js"></script>
325 326
326<script src="js/scenes/InstructionsScene.js"></script>
326 327
327<script src="js/scenes/CabinScene.js"></script>
327 328
328<script src="js/scenes/WinScene.js"></script>
328 329
329<script> 330 const config = { 331 type: Phaser.AUTO, 332 width: CW, 333 height: CH, 334 backgroundColor: "#111111", 335 pixelArt: true, // disables anti-aliasing â crisp pixel edges 336 roundPixels: true, // prevents sub-pixel blurring 337 parent: "game-container", 338 scene: [CharacterSelectScene, InstructionsScene, CabinScene, WinScene], 339 scale: { 340 mode: Phaser.Scale.FIT, 341 autoCenter: Phaser.Scale.CENTER_BOTH, 342 }, 343 }; 344 345 const game = new Phaser.Game(config); 346 347 (function setupLegendModal() { 348 const modal = document.getElementById("legend-modal"); 349 const openBtn = document.getElementById("legend-open-btn"); 350 const closeBtn = document.getElementById("legend-close-btn"); 351 const backdrop = document.getElementById("legend-modal-backdrop"); 352 353 function openModal() { 354 modal.classList.add("is-open"); 355 modal.setAttribute("aria-hidden", "false"); 356 document.body.style.overflow = "hidden"; 357 } 358 359 function closeModal() { 360 modal.classList.remove("is-open"); 361 modal.setAttribute("aria-hidden", "true"); 362 document.body.style.overflow = ""; 363 } 364 365 if (openBtn) { 366 openBtn.addEventListener("click", openModal); 367 } 368 if (closeBtn) { 369 closeBtn.addEventListener("click", closeModal); 370 } 371 if (backdrop) { 372 backdrop.addEventListener("click", closeModal); 373 } 374 375 document.addEventListener("keydown", function (e) { 376 if (e.key === "Escape" && modal.classList.contains("is-open")) { 377 closeModal(); 378 } 379 }); 380 })(); 381 </script>
381 382 </body> 383</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.