-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
579 lines (518 loc) · 36.4 KB
/
index.html
File metadata and controls
579 lines (518 loc) · 36.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<meta id="themeColor" name="theme-color" content="#0f131c" />
<title>App Survival: Android Release Night</title>
</head>
<body>
<a href="#sideBody" class="skip-link">Skip to dashboard</a>
<div id="app" class="wrap">
<div class="canvasWrap" aria-label="Architecture canvas">
<canvas id="c" aria-label="Component dependency graph. Use the dashboard to add and manage components."></canvas>
<div class="canvasHud" role="group" aria-label="Canvas controls">
<button id="btnZoomOut" class="btn hud" data-i18n-title="canvas.zoomOut" title="Zoom out" aria-label="Zoom out">−</button>
<button id="btnZoomIn" class="btn hud" data-i18n-title="canvas.zoomIn" title="Zoom in" aria-label="Zoom in">+</button>
<button id="btnZoomFit" class="btn hud" data-i18n-title="canvas.fit" title="Fit" aria-label="Fit to view">⤢</button>
</div>
</div>
<aside class="side" aria-label="Dashboard">
<div class="sideHeader">
<h1><span data-i18n="app.title">App Survival: Android Release Night</span> <span class="badge" data-i18n="app.badge">M3 • TS + Vite</span></h1>
<div class="row">
<button id="btnStart" class="btn filled" data-i18n-title="btn.start" title="Start">
<svg class="btn-icon" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="6 3 20 12 6 21 6 3"/></svg>
<span class="btn-label" data-i18n="btn.start">Start</span>
</button>
<button id="btnPause" class="btn tonal" data-i18n-title="btn.pause" title="Pause">
<svg class="btn-icon" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="4" width="4" height="16" rx="1"/><rect x="14" y="4" width="4" height="16" rx="1"/></svg>
<span class="btn-label" data-i18n="btn.pause">Pause</span>
</button>
<button id="btnReset" class="btn outlined" data-i18n-title="btn.reset" title="Reset">
<svg class="btn-icon" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 3-6.7L3 8"/><path d="M3 3v5h5"/></svg>
<span class="btn-label" data-i18n="btn.reset">Reset</span>
</button>
<button id="btnProfile" class="btn outlined" data-i18n-title="btn.profileTitle" title="Profile & achievements">
<svg class="btn-icon" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="4"/><path d="M4 21a8 8 0 0 1 16 0"/></svg>
<span class="btn-label" data-i18n="btn.profile">Profile</span>
</button>
</div>
<div class="row" style="margin-top:10px; align-items:center;">
<span class="pill" id="modePill"><span data-i18n="mode.label">Mode:</span> <b data-i18n="mode.select">Select</b></span>
<div class="row segmented" role="group" aria-label="Mode" style="margin-left:auto;">
<button id="btnSelect" class="btn text is-selected" data-i18n-title="mode.select" title="Select">
<svg class="btn-icon" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4l7 16 2-7 7-2-16-7z"/></svg>
<span class="btn-label" data-i18n="mode.select">Select</span>
</button>
<button id="btnLink" class="btn text" data-i18n-title="mode.link" title="Link">
<svg class="btn-icon" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 17H7a5 5 0 0 1 0-10h2"/><path d="M15 7h2a5 5 0 0 1 0 10h-2"/><line x1="8" y1="12" x2="16" y2="12"/></svg>
<span class="btn-label" data-i18n="mode.link">Link</span>
</button>
<button id="btnUnlink" class="btn text" data-i18n-title="mode.unlink" title="Unlink">
<svg class="btn-icon" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 17H7a5 5 0 0 1 0-10h2"/><path d="M15 7h2a5 5 0 0 1 0 10h-2"/><line x1="4" y1="4" x2="20" y2="20"/></svg>
<span class="btn-label" data-i18n="mode.unlink">Unlink</span>
</button>
</div>
</div>
<div class="tabBar" role="tablist" aria-label="Sections">
<button class="tabBtn is-selected" id="tabBtnOverview" role="tab" aria-selected="true" aria-controls="tabOverview" data-tab="overview" data-i18n="nav.overview">Overview</button>
<button class="tabBtn" id="tabBtnBacklog" role="tab" aria-selected="false" aria-controls="tabBacklog" data-tab="backlog" data-i18n="nav.backlog">Backlog</button>
<button class="tabBtn" id="tabBtnSignals" role="tab" aria-selected="false" aria-controls="tabSignals" data-tab="signals" data-i18n="nav.signals">Signals</button>
<button class="tabBtn" id="tabBtnHistory" role="tab" aria-selected="false" aria-controls="tabHistory" data-tab="history" data-i18n="nav.history">History</button>
</div>
</div>
<div id="sideBody" class="sideBody">
<section id="tabOverview" class="tabPanel" role="tabpanel" aria-labelledby="tabBtnOverview">
<!-- Keep "Selected" high in the panel for fast diagnosis/repair cycles -->
<section class="card" id="selCard">
<div class="k" data-i18n="sel.title">Selected</div>
<div class="v mono" id="selName" data-i18n="sel.none">None</div>
<pre class="small mono" id="selStats">-</pre>
<div class="row" style="margin-top:10px;">
<button id="btnUpgrade" class="btn filled" disabled data-i18n-title="btn.upgrade" title="Upgrade">
<svg class="btn-icon" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5"/><path d="M5 12l7-7 7 7"/></svg>
<span class="btn-label" data-i18n="btn.upgrade">Upgrade</span>
</button>
<button id="btnRepair" class="btn outlined" disabled data-i18n-title="btn.repair" title="Repair">
<svg class="btn-icon" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 7l3-3 4 4-3 3"/><path d="M13 8l-9 9v4h4l9-9"/></svg>
<span class="btn-label" data-i18n="btn.repair">Repair</span>
</button>
<button id="btnDelete" class="btn error" disabled data-i18n-title="btn.delete" title="Delete">
<svg class="btn-icon" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6M14 11v6"/><path d="M9 6V4a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2"/></svg>
<span class="btn-label" data-i18n="btn.delete">Delete</span>
</button>
</div>
<div class="small" style="margin-top:8px;" data-i18n="sel.hint">Upgrades raise capacity & reliability. Repairs restore health. Both cost budget</div>
</section>
<!-- Tier 1: Primary metrics (always visible) -->
<section class="metricsGrid metricsGrid--primary" aria-label="Primary metrics" aria-live="polite" aria-atomic="false">
<div class="card">
<div class="k" data-i18n="lbl.budget">Budget</div>
<div class="v v-lg mono" id="budget">$500</div>
</div>
<div class="card">
<div class="k" data-i18n="lbl.rating">Rating</div>
<div class="v v-lg mono" id="rating">5.0 ★</div>
<svg id="sparkRating" class="sparkline" aria-hidden="true"></svg>
</div>
<div class="card">
<div class="k" data-i18n="lbl.shift">Shift</div>
<div class="v mono" id="shift">0:00 / 9:00</div>
</div>
<div class="card">
<div class="k" data-i18n="lbl.score">Score</div>
<div class="v v-lg mono" id="score">0</div>
<span id="comboIndicator" class="pill pill-combo" hidden>COMBO x1</span>
</div>
</section>
<!-- Tier 2: System health (collapsible) -->
<details class="metricsPanel" open>
<summary class="metricsPanel-header">System Health</summary>
<section class="metricsGrid" aria-label="System health metrics">
<div class="card">
<div class="k" data-i18n="lbl.failures">Failures</div>
<div class="v mono" id="fail">0.0%</div>
<svg id="sparkFail" class="sparkline" aria-hidden="true"></svg>
</div>
<div class="card">
<div class="k" data-i18n="lbl.anr">ANR Risk</div>
<div class="v mono" id="anr">0.0%</div>
</div>
<div class="card">
<div class="k" data-i18n="lbl.latency">p95 Latency</div>
<div class="v mono" id="lat">0 ms</div>
</div>
<div class="card">
<div class="k" data-i18n="lbl.coverage">Coverage</div>
<div class="v mono" id="coverage">78%</div>
<div class="small" id="coverageHint">Target 70%+</div>
</div>
<div class="card">
<div class="k" data-i18n="lbl.arch">Architecture</div>
<div class="v mono" id="archDebt">0</div>
</div>
<div class="card">
<div class="k" data-i18n="lbl.battery">Battery</div>
<div class="v mono" id="bat">100</div>
</div>
</section>
</details>
<!-- Tier 3: Android internals (collapsed by default) -->
<details class="metricsPanel">
<summary class="metricsPanel-header">Android Internals</summary>
<section class="metricsGrid" aria-label="Android internal metrics">
<div class="card">
<div class="k" data-i18n="lbl.time">Time</div>
<div class="v mono" id="time">0s</div>
</div>
<div class="card">
<div class="k" data-i18n="lbl.jank">Jank</div>
<div class="v mono" id="jank">0%</div>
<svg id="sparkJank" class="sparkline" aria-hidden="true"></svg>
</div>
<div class="card">
<div class="k" data-i18n="lbl.heap">Heap</div>
<div class="v mono" id="heap">64 MB</div>
<svg id="sparkHeap" class="sparkline" aria-hidden="true"></svg>
</div>
<div class="card">
<div class="k"><span data-i18n="heapwatch.gc">GC</span> / <span data-i18n="heapwatch.msOom">OOM</span></div>
<div class="v mono"><span id="gc">0</span> ms / <span id="oom">0</span></div>
</div>
</section>
</details>
<!-- Seed controls (required by src/main.ts). Kept lightweight so runs can be shared/replayed. -->
<section class="card">
<div class="k" data-i18n="card.seed">Seed</div>
<div class="small" style="margin-top:8px;">
<span data-i18n="seed.active">Active:</span> <span id="seedVal" class="mono">0</span>
</div>
<div class="row" style="margin-top:8px; gap:8px;">
<input id="seedInput" class="input" style="flex:1;" type="number" step="1" data-i18n-placeholder="seed.placeholder" placeholder="Seed (optional)" aria-label="Seed value" />
<button id="btnDailySeed" class="btn text" title="Use a deterministic daily seed" data-i18n="seed.daily" data-i18n-title="seed.dailyTitle">Daily</button>
</div>
<div class="small muted" style="margin-top:8px;" data-i18n="seed.note">Reset uses the seed above (if set).</div>
</section>
<section class="card">
<div class="k">Challenges</div>
<div class="small" style="margin-top:8px;">
<b>Daily:</b> <span id="challengeDaily" class="mono">-</span>
</div>
<div class="small" style="margin-top:4px;">
<b>Weekly:</b> <span id="challengeWeekly" class="mono">-</span>
</div>
<div class="row" style="margin-top:10px;">
<button id="btnStartDaily" class="btn tonal">Start daily</button>
<button id="btnStartWeekly" class="btn outlined">Start weekly</button>
</div>
<pre class="small" id="challengeResults" style="margin-top:8px;"></pre>
</section>
<section class="card" id="scenariosCard">
<div class="k">Release Trains</div>
<div class="small muted" style="margin-top:6px;">Scripted shifts with pinned seeds and incident timelines. End-of-run postmortem earns a grade letter (S → D).</div>
<div id="scenarioList" style="margin-top:10px; display:flex; flex-direction:column; gap:10px;"></div>
</section>
<section class="card">
<div class="k" data-i18n="card.achievements">Achievements</div>
<div class="small" style="margin-top:8px;">
<span data-i18n="ach.preset">Preset:</span> <span id="achPreset" class="mono">SENIOR</span> •
<span data-i18n="ach.unlocked">Unlocked:</span> <span id="achUnlocked" class="mono">0</span>/<span id="achTotal" class="mono">0</span>
</div>
<div class="row" style="margin-top:10px;">
<button id="btnOpenProfile" class="btn tonal" data-i18n="ach.openProfile">Open profile</button>
</div>
</section>
<section class="card">
<div class="k" data-i18n="card.trust">Trust & accessibility</div>
<div class="small" style="margin-top:8px;">
<span data-i18n="trust.a11y">A11y</span>: <span class="mono" id="a11yScore">100</span>/100 •
<span data-i18n="trust.privacy">Privacy</span>: <span class="mono" id="privacyTrust">100</span>/100 •
<span data-i18n="trust.security">Security</span>: <span class="mono" id="securityPosture">100</span>/100 •
<span data-i18n="trust.supportLoad">Support load</span>: <span class="mono" id="supportLoad">0</span>/100
</div>
</section>
<!-- Setup & controls: kept in overview because it's a primary interaction surface -->
<div class="sideGroup">
<section class="card">
<label for="presetSelect" class="k" data-i18n="setup.evalPreset">Evaluation preset</label>
<div class="row" style="margin-top:8px;">
<select id="presetSelect">
<option value="JUNIOR_MID" data-i18n="preset.juniorMid">Junior-Mid</option>
<option value="SENIOR" data-i18n="preset.senior">Senior</option>
<option value="STAFF" data-i18n="preset.staff">Staff</option>
<option value="PRINCIPAL" data-i18n="preset.principal">Principal</option>
</select>
</div>
<div class="small muted" style="margin-top:8px;" data-i18n="setup.evalPresetHint">CoverageGate and enforcement weight</div>
</section>
<section class="card">
<label for="componentType" class="k" data-i18n="setup.placeComponent">Place component</label>
<div class="row" style="margin-top:8px;">
<select id="componentType">
<option value="UI" data-i18n="component.UI">UI</option>
<option value="VM" data-i18n="component.VM">ViewModel</option>
<option value="DOMAIN" data-i18n="component.DOMAIN">Domain</option>
<option value="REPO" data-i18n="component.REPO">Repository</option>
<option value="CACHE" data-i18n="component.CACHE">Cache</option>
<option value="DB" data-i18n="component.DB">Room DB</option>
<option value="NET" data-i18n="component.NET">Network</option>
<option value="WORK" data-i18n="component.WORK">WorkManager</option>
<option value="OBS" data-i18n="component.OBS">Observability</option>
<option value="FLAGS" data-i18n="component.FLAGS">Feature Flags</option>
<option value="AUTH" data-i18n="component.AUTH">Auth / Sessions</option>
<option value="PINNING" data-i18n="component.PINNING">TLS Pinning</option>
<option value="KEYSTORE" data-i18n="component.KEYSTORE">Keystore / Crypto</option>
<option value="SANITIZER" data-i18n="component.SANITIZER">Input Sanitizer</option>
<option value="ABUSE" data-i18n="component.ABUSE">Abuse Protection</option>
<option value="A11Y" data-i18n="component.A11Y">Accessibility Layer</option>
<option value="BILLING" data-i18n="component.BILLING">Billing / IAP</option>
<option value="PUSH" data-i18n="component.PUSH">Push / FCM</option>
<option value="DEEPLINK" data-i18n="component.DEEPLINK">Deep Links</option>
</select>
<button id="btnAdd" class="btn tonal" data-i18n-title="btn.add" title="Add">
<svg class="btn-icon" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
<span class="btn-label" data-i18n="btn.add">Add</span>
</button>
</div>
<div class="small" style="margin-top:8px;">
<span data-i18n="setup.dragHintPrefix">Drag components to rearrange. Link mode: click</span> <span class="mono" data-i18n="setup.dragHintSource">source</span> → <span data-i18n="setup.dragHintThen">click</span> <span class="mono" data-i18n="setup.dragHintDest">destination</span>
</div>
</section>
</div>
</section> <!-- /tabOverview -->
<section id="tabBacklog" class="tabPanel" role="tabpanel" aria-labelledby="tabBtnBacklog">
<section class="card" id="backlogCard">
<div class="k" data-i18n="nav.backlog">Backlog</div>
<div class="capRow">
<div class="capBar" role="progressbar" aria-labelledby="capBarLabel" aria-valuemin="0" aria-valuemax="100" aria-valuenow="100">
<div id="capBarLabel" class="capBarLabel"><span data-i18n="backlog.capacity">Capacity</span> <span id="capVal" class="mono">0/0</span> <span id="capRegenHint" class="small muted mono">+0/min</span></div>
<div class="capBarTrack"><div id="capBarFill" class="capBarFill"></div></div>
</div>
<div class="capBtns">
<button id="btnCapRefill" class="btn tonal" data-i18n-title="btn.refill" title="Refill">
<svg class="btn-icon" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="7" width="16" height="10" rx="2"/><line x1="22" y1="11" x2="22" y2="13"/><polyline points="11 9 8 13 12 13 9 17"/></svg>
<span class="btn-label" data-i18n="btn.refill">Refill</span>
</button>
<button id="btnCapBoost" class="btn tonal" data-i18n-title="btn.boostRegen" title="Boost regen">
<svg class="btn-icon" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="13" r="8"/><path d="M12 9v4l2 2"/><path d="M10 3h4"/></svg>
<span class="btn-label" data-i18n="btn.boostRegen">Boost regen</span>
</button>
<button id="btnCapDrink" class="btn tonal" hidden data-i18n-title="btn.energyDrinkTitle" title="Temporary regen booster (unlocks via achievements)">
<svg class="btn-icon" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
<span class="btn-label" data-i18n="btn.energyDrink">Energy drink</span>
</button>
<button id="btnCapShield" class="btn tonal" hidden data-i18n-title="btn.incidentShieldTitle" title="Blocks the next incident penalty once (unlocks via achievements)">
<svg class="btn-icon" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2l8 3v6c0 5-3.5 9-8 11-4.5-2-8-6-8-11V5l8-3z"/></svg>
<span class="btn-label" data-i18n="btn.incidentShield">Incident shield</span>
</button>
<button id="btnCapHire" class="btn tonal" data-i18n-title="btn.hire" title="Hire">
<svg class="btn-icon" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><line x1="19" y1="8" x2="19" y2="14"/><line x1="22" y1="11" x2="16" y2="11"/></svg>
<span class="btn-label" data-i18n="btn.hire">Hire</span>
</button>
</div>
<div class="small muted capHint" data-i18n="backlog.fixOrDefer">Fix tickets or defer</div>
</div>
<div id="ticketList" class="ticketList" style="margin-top:10px;"></div>
</section>
<section class="card">
<div class="row" style="align-items:center;">
<div class="k" data-i18n="card.roadmap">Refactor Roadmap</div>
<div style="margin-left:auto;">
<button id="btnApplyNextRoadmap" class="btn text" data-i18n="btn.applyNext">Apply next</button>
</div>
</div>
<div class="small muted" style="margin-top:6px;" data-i18n="history.roadmapHint">Suggested sequence to pay down architecture debt (Staff/Principal-style).</div>
<pre class="small" id="roadmap" style="margin-top:8px;" data-i18n="history.noRoadmap">No roadmap yet.</pre>
</section>
</section> <!-- /tabBacklog -->
<section id="tabSignals" class="tabPanel" role="tabpanel" aria-labelledby="tabBtnSignals">
<div class="sideGroup">
<section class="card">
<div class="k" data-i18n="card.platform">Platform</div>
<div class="small" style="margin-top:8px;">
<span data-i18n="platform.latestApi">Latest API</span> <span id="apiLatest" class="mono">0</span> • <span data-i18n="platform.minApi">Min API</span> <span id="apiMin" class="mono">0</span>
</div>
<div class="small muted" style="margin-top:4px;">
<span data-i18n="platform.oldDevices">Old devices</span> <span id="oldShare" class="mono">0</span>% • <span data-i18n="platform.lowRam">Low RAM</span> <span id="lowRamShare" class="mono">0</span>%
</div>
<div id="advisoryText" class="small" style="margin-top:8px;"></div>
</section>
<section class="card">
<div class="k" data-i18n="card.regions">Regions</div>
<div class="row" style="margin-top:8px; align-items:center;">
<div class="pill"><span data-i18n="regions.regPressure">Reg pressure</span> <span id="regPressure" class="mono">0</span></div>
<div class="small muted" style="margin-left:auto;" data-i18n="regions.hint">Compliance and rollout gates</div>
</div>
<div id="regionList" class="regionList" style="margin-top:10px;"></div>
</section>
</div>
<section class="card">
<div class="k" data-i18n="card.votes">User votes (live sentiment)</div>
<div class="small" style="margin-top:8px;">
<span data-i18n="votes.perf">Perf</span>: <span class="mono" id="votesPerf">0</span> •
<span data-i18n="votes.reliability">Reliability</span>: <span class="mono" id="votesReliability">0</span> •
<span data-i18n="votes.privacy">Privacy</span>: <span class="mono" id="votesPrivacy">0</span> •
<span data-i18n="votes.a11y">A11y</span>: <span class="mono" id="votesA11y">0</span> •
<span data-i18n="votes.battery">Battery</span>: <span class="mono" id="votesBattery">0</span>
</div>
<pre class="small" id="reviewLog" style="margin-top:8px;" data-i18n="signals.noReviews">No reviews yet.</pre>
</section>
<section class="card" id="incidentsCard">
<div class="k" data-i18n="card.incidents">Incidents</div>
<pre class="small" id="eventLog" data-i18n="signals.noIncidents">No incidents… yet.</pre>
</section>
</section> <!-- /tabSignals -->
<section id="tabHistory" class="tabPanel" role="tabpanel" aria-labelledby="tabBtnHistory">
<section class="card">
<div class="row" style="align-items:center;">
<div class="k" data-i18n="card.postmortem">Postmortem</div>
<div style="margin-left:auto;">
<button id="btnCopyRun" class="btn text" data-i18n="btn.copyRunJson">Copy run JSON</button>
</div>
</div>
<pre class="small" id="postmortem" data-i18n="history.noRun">No run yet.</pre>
</section>
<section class="card">
<div class="row" style="align-items:center;">
<div class="k" data-i18n="card.scoreboard">Scoreboard (local)</div>
<span id="integrityBadge" class="pill pill-tamper" hidden style="margin-left:8px;">⚠ Tampered</span>
<div style="margin-left:auto;">
<button id="btnClearScoreboard" class="btn text" data-i18n="btn.clear">Clear</button>
</div>
</div>
<div class="small muted" style="margin-top:6px;" data-i18n="history.scoreboardHint">Top runs stored in localStorage.</div>
<pre class="small" id="scoreboardList" style="margin-top:8px;" data-i18n="history.noScores">No scores yet.</pre>
</section>
<section class="card">
<div class="k" data-i18n="history.realismTitle">Android realism features (implemented)</div>
<ol class="small" style="margin:8px 0 0; padding-left:18px;">
<li><b>FrameGuard</b> <span data-i18n="history.realism.frameguard">frame budget and jank meter (16ms model)</span></li>
<li><b>MainThreadGuard</b> <span data-i18n="history.realism.mainthread">IO on main strictness (adds ANR and jank)</span></li>
<li><b>HeapWatch</b> <span data-i18n="history.realism.heapwatch">heap meter with GC pauses and OOM crashes</span></li>
</ol>
</section>
<p class="small">
<span data-i18n="history.recoPrefix">Recommended starter graph:</span> <span class="mono">UI → VM → Domain → Repo → (Cache → DB)</span> <span data-i18n="history.recoAnd">and</span> <span class="mono">Repo → Net</span>.
<span data-i18n="history.recoAdd">Add</span> <span class="mono">Observability</span> + <span class="mono">Feature Flags</span> <span data-i18n="history.recoSuffix">to reduce blast radius</span>
</p>
<div id="buildInfo" class="small mono" style="opacity:.75; margin-top:10px;"></div>
</section> <!-- /tabHistory -->
</div> <!-- /sideBody -->
</aside>
</div>
<!-- Incident response: shown briefly when a new incident hits -->
<div id="incidentOverlay" class="incidentOverlay" hidden aria-live="polite" aria-atomic="true">
<div class="card incidentOverlayInner">
<div class="row" style="align-items:center;">
<div class="pill" style="border-color: color-mix(in oklab, var(--md-sys-color-error) 70%, transparent);" data-i18n="incident.label">Incident</div>
<div id="incidentOverlayTitle" class="small mono" style="margin-left:10px; flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">-</div>
<button id="incidentOverlayDismiss" class="btn text" aria-label="Dismiss incident" data-i18n-aria-label="incident.dismiss">✕</button>
</div>
<div id="incidentOverlayHint" class="small muted" style="margin-top:8px;" data-i18n="incident.hint">Respond fast: fix or defer the top ticket to stop the bleed.</div>
<div class="row" style="margin-top:10px; justify-content:flex-end; flex-wrap:wrap;">
<button id="incidentOverlayBacklog" class="btn outlined" data-i18n="btn.openBacklog">Open backlog</button>
<button id="incidentOverlayRefill" class="btn outlined" data-i18n="btn.refill">Refill</button>
<button id="incidentOverlayTriage" class="btn filled" data-i18n="btn.quickTriage">Quick triage</button>
</div>
</div>
</div>
<!-- Welcome / onboarding modal (first visit only) -->
<div id="welcomeModal" class="modal" hidden role="dialog" aria-modal="true" aria-labelledby="welcomeTitle">
<div class="modalBackdrop" id="welcomeBackdrop"></div>
<div class="modalPanel card" style="max-width:480px; text-align:center;">
<h2 id="welcomeTitle" style="margin:0 0 var(--space-4); font-size:var(--type-hero-size); font-weight:600;">Welcome to App Survival</h2>
<p class="small" style="line-height:1.6; margin:0 0 var(--space-4);">
You are the on-call engineer for an Android app on release night.
Keep the app alive by managing architecture, fixing incidents, and balancing competing pressures.
</p>
<ol class="small" style="text-align:left; line-height:1.8; margin:0 0 var(--space-4); padding-left:20px;">
<li><b>Place components</b> (UI, Network, Database...) on the canvas</li>
<li><b>Link them together</b> to form your architecture</li>
<li><b>Press Start</b> and respond to incidents as they arise</li>
<li><b>Survive the shift</b> with a good rating and low debt</li>
</ol>
<div class="row" style="justify-content:center;">
<button id="welcomeDismiss" class="btn filled">Got it, let's go</button>
</div>
<div class="small muted" style="margin-top:var(--space-3);">
Full guide: <a href="./docs/GAMEPLAY.md" style="color:var(--md-sys-color-primary);">GAMEPLAY.md</a>
</div>
</div>
</div>
<!-- End-of-run celebration modal -->
<div id="endRunModal" class="modal" hidden role="dialog" aria-modal="true" aria-labelledby="endRunTitle">
<div class="modalBackdrop" id="endRunBackdrop"></div>
<div class="modalPanel card endRunPanel">
<h2 id="endRunTitle" class="endRun-title">Run Complete</h2>
<div class="endRun-score" style="display:flex; gap:16px; align-items:center; justify-content:center;">
<div>
<div class="k">Final Score</div>
<div id="endRunScore" class="endRun-hero mono">0</div>
</div>
<div>
<div class="k">Postmortem</div>
<div id="endRunGrade" class="endRun-hero mono">-</div>
<div id="endRunGradeCallouts" class="small" style="margin-top:6px;"></div>
</div>
</div>
<div id="endRunBreakdown" class="small mono" style="margin-top:var(--space-3);"></div>
<div class="metricsGrid" style="margin-top:var(--space-4); grid-template-columns: repeat(4, 1fr);">
<div><div class="k">Rating</div><div class="mono" id="endRunRating">-</div></div>
<div><div class="k">Duration</div><div class="mono" id="endRunDuration">-</div></div>
<div><div class="k">Debt</div><div class="mono" id="endRunDebt">-</div></div>
<div><div class="k">Tickets</div><div class="mono" id="endRunTickets">-</div></div>
</div>
<div id="endRunBonuses" class="small" style="margin-top:var(--space-3);"></div>
<div id="endRunVerified" class="small mono" style="margin-top:var(--space-3);"></div>
<div class="row" style="margin-top:var(--space-4); justify-content:center;">
<button id="endRunPlayAgain" class="btn filled">Play Again</button>
<button id="endRunReplay" class="btn outlined">Replay Seed</button>
<button id="endRunDismiss" class="btn text">Close</button>
</div>
</div>
</div>
<!-- Profile + achievements (per preset) -->
<div id="profileModal" class="modal" hidden role="dialog" aria-modal="true" aria-labelledby="profileTitle">
<div class="modalBackdrop" id="profileBackdrop"></div>
<div class="modalPanel card">
<div class="row" style="align-items:center; gap:10px;">
<h2 id="profileTitle" style="margin:0; font-size:16px;" data-i18n="profile.title">Profile</h2>
<div class="pill" style="margin-left:auto;" data-i18n="profile.preset">Preset</div>
<select id="profilePresetSelect">
<option value="JUNIOR_MID" data-i18n="preset.juniorMid">Junior-Mid</option>
<option value="SENIOR" data-i18n="preset.senior">Senior</option>
<option value="STAFF" data-i18n="preset.staff">Staff</option>
<option value="PRINCIPAL" data-i18n="preset.principal">Principal</option>
</select>
<button id="btnCloseProfile" class="btn text" aria-label="Close profile" data-i18n-aria-label="profile.close">✕</button>
</div>
<div class="small" style="margin-top:10px;">
<span data-i18n="profile.unlocked">Unlocked:</span> <span id="profileUnlocked" class="mono">0</span>/<span id="profileTotal" class="mono">0</span>
• <span data-i18n="profile.best">Best survival:</span> <span id="profileBest" class="mono">0s</span>
</div>
<div id="profileAchList" class="achList" style="margin-top:12px;"></div>
<div class="small muted" style="margin-top:12px;" data-i18n="profile.hiddenNote">
Hidden achievements show up as “Hidden achievement” until you unlock them.
</div>
<hr class="sep" />
<div class="k" data-i18n="settings.title">Settings</div>
<div class="row" style="margin-top:10px; align-items:center; gap:10px; flex-wrap:wrap;">
<label for="langSelect" class="pill" data-i18n="settings.language">Language</label>
<select id="langSelect" style="margin-left:auto; min-width:180px;">
<!-- options populated by src/i18n.ts (production locale list) -->
</select>
</div>
<div class="row" style="margin-top:10px; align-items:center; gap:10px; flex-wrap:wrap;">
<label for="themeSelect" class="pill" data-i18n="settings.theme">Theme</label>
<select id="themeSelect" style="margin-left:auto; min-width:180px;">
<option value="system" data-i18n="theme.system">System</option>
<option value="light" data-i18n="theme.light">Light</option>
<option value="dark" data-i18n="theme.dark">Dark</option>
</select>
</div>
<!-- Glass mode removed: low-transparency UI read as "disabled" and caused confusion. -->
</div>
</div>
<!-- Refactor options (architecture debt) -->
<div id="refactorModal" class="modal" hidden role="dialog" aria-modal="true" aria-labelledby="refactorTitle">
<div class="modalBackdrop" id="refactorBackdrop"></div>
<div class="modalPanel card">
<div class="row" style="align-items:center; gap:10px;">
<h2 id="refactorTitle" style="margin:0; font-size:16px;" data-i18n="ticket.refactorOptions">Refactor options</h2>
<button id="btnCloseRefactor" class="btn text" aria-label="Close refactor options" data-i18n-aria-label="ticket.closeRefactor" style="margin-left:auto;">✕</button>
</div>
<div id="refactorTicketTitle" class="small" style="margin-top:10px;"></div>
<div class="row" style="margin-top:12px; align-items:center; gap:10px; flex-wrap:wrap;">
<div class="pill" data-i18n="ticket.target">Target</div>
<select id="refactorTargetSelect" class="input mono" style="margin-left:auto; min-width:220px;"></select>
</div>
<div id="refactorOptions" class="refactorList" style="margin-top:12px;"></div>
<div class="small muted" style="margin-top:12px;" data-i18n="ticket.refactorHint">
Refactors spend budget and reduce architecture debt. Pick the cheapest thing that fixes the worst violation.
</div>
</div>
</div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>