pre-mortem 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1005 @@
1
+ var m = Object.defineProperty;
2
+ var b = (r, t, e) => t in r ? m(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e;
3
+ var l = (r, t, e) => b(r, typeof t != "symbol" ? t + "" : t, e);
4
+ import c from "matter-js";
5
+ var u = /* @__PURE__ */ ((r) => (r.BOOTSTRAP = "bootstrap", r.VC = "vc", r.SLACKER = "slacker", r.CONTROL_FREAK = "control_freak", r.PRIMA_DONNA = "prima_donna", r.QUIET_QUITTER = "quiet_quitter", r.SLACK_SPAMMER = "slack_spammer", r))(u || {});
6
+ const p = {
7
+ bootstrap: {
8
+ w: 132,
9
+ h: 44,
10
+ color: "#555555",
11
+ // Concrete/Rust
12
+ density: 5e-3,
13
+ // High relative density (Matter.js default is 0.001)
14
+ friction: 0.8,
15
+ restitution: 0,
16
+ label: "Refactoring",
17
+ possibleLabels: [
18
+ // The Work (Old & New)
19
+ "Refactoring",
20
+ "Unit Tests",
21
+ "Bug Fixes",
22
+ "User Support",
23
+ "Documentation",
24
+ "Security Audit",
25
+ "Compliance",
26
+ "Cleanup",
27
+ "Optimization",
28
+ "CI/CD Pipeline",
29
+ "Code Review",
30
+ "DB Tuning",
31
+ "API Design",
32
+ "Accessibility",
33
+ "Localization",
34
+ "Error Logs",
35
+ "Backups",
36
+ "Legacy Code",
37
+ "Hiring",
38
+ "Customer Love",
39
+ "Bossware",
40
+ "YAML Hell",
41
+ "DNS Propagation",
42
+ "Regex",
43
+ "npm audit",
44
+ "GDPR / SOC2",
45
+ "Migration",
46
+ "Certificate Expiry",
47
+ "On-Call",
48
+ "Technical Debt",
49
+ "Works on my machine",
50
+ "Yak Shaving"
51
+ ]
52
+ },
53
+ vc: {
54
+ w: 120,
55
+ h: 40,
56
+ color: "#FF00FF",
57
+ // Neon Pink
58
+ density: 5e-4,
59
+ // Slightly heavier (was 0.0001)
60
+ friction: 0.3,
61
+ // Less slippery (was 0.1)
62
+ restitution: 0.6,
63
+ // Less bouncy (was 0.9)
64
+ label: "AI Blockchain",
65
+ possibleLabels: [
66
+ // The Hype (Old & New)
67
+ "GenAI",
68
+ "Blockchain",
69
+ "Web3",
70
+ "Metaverse",
71
+ "NFTs",
72
+ "Viral Growth",
73
+ "Pivot",
74
+ "Synergy",
75
+ "Disruption",
76
+ "Thought Leader",
77
+ "Influencers",
78
+ "Hyper-Scale",
79
+ "Quantum",
80
+ "Big Data",
81
+ "Growth Hack",
82
+ "Paradigm Shift",
83
+ "10x Engineer",
84
+ "Visionary",
85
+ "Series B Pitch",
86
+ "Exit Strategy",
87
+ "Vibe Coding",
88
+ "Founder Mode",
89
+ "Prompt Engineering",
90
+ "AGI",
91
+ "AI Wrapper",
92
+ "Unlimited PTO",
93
+ "Radical Candor",
94
+ "Community Led",
95
+ "Pre-Revenue",
96
+ "Fractional CxO"
97
+ ]
98
+ },
99
+ slacker: {
100
+ w: 64,
101
+ // 80 * 0.8
102
+ h: 64,
103
+ // 80 * 0.8
104
+ color: "#FFFFFF",
105
+ density: 0.01,
106
+ friction: 1,
107
+ restitution: 0.1,
108
+ label: "The Slacker",
109
+ possibleLabels: ["The Slacker"]
110
+ },
111
+ control_freak: {
112
+ w: 64,
113
+ // Square to match image aspect ratio
114
+ h: 64,
115
+ color: "#FF0000",
116
+ density: 0.012,
117
+ friction: 0.5,
118
+ restitution: 0.2,
119
+ label: "Control Freak",
120
+ possibleLabels: ["Control Freak"]
121
+ },
122
+ prima_donna: {
123
+ w: 56,
124
+ h: 56,
125
+ // 70 * 0.8
126
+ color: "#FFFF00",
127
+ density: 8e-3,
128
+ friction: 0.1,
129
+ restitution: 1.2,
130
+ label: "Prima Donna",
131
+ possibleLabels: ["Prima Donna"]
132
+ },
133
+ quiet_quitter: {
134
+ w: 48,
135
+ h: 48,
136
+ // 60 * 0.8
137
+ color: "#00FFFF",
138
+ density: 5e-3,
139
+ friction: 0.01,
140
+ restitution: 0.9,
141
+ label: "Quiet Quitter",
142
+ possibleLabels: ["Quiet Quitter"]
143
+ },
144
+ slack_spammer: {
145
+ w: 60,
146
+ h: 60,
147
+ // 75 * 0.8
148
+ color: "#00FF00",
149
+ density: 0.01,
150
+ friction: 0.4,
151
+ restitution: 0.4,
152
+ label: "Slack Spammer",
153
+ possibleLabels: ["Slack Spammer"]
154
+ }
155
+ }, x = (r) => {
156
+ const t = p[r].possibleLabels || [];
157
+ return t[Math.floor(Math.random() * t.length)];
158
+ }, y = (r, t, e) => {
159
+ const i = p[e], s = x(e), o = {
160
+ density: i.density,
161
+ friction: i.friction,
162
+ restitution: i.restitution,
163
+ render: {
164
+ fillStyle: i.color,
165
+ strokeStyle: "#000",
166
+ lineWidth: 2
167
+ },
168
+ label: s
169
+ };
170
+ if (e === "vc") {
171
+ const n = i.w, d = i.h, g = Math.random() * 15, h = Math.random() * 15, a = (Math.random() - 0.5) * 20, f = [
172
+ { x: 0 + a, y: g },
173
+ // TL
174
+ { x: n + a, y: h },
175
+ // TR
176
+ { x: n, y: d },
177
+ // BR
178
+ { x: 0, y: d }
179
+ // BL
180
+ ];
181
+ return c.Bodies.fromVertices(r, t, [f], o);
182
+ } else if (e === "slacker" || e === "control_freak" || e === "prima_donna" || e === "quiet_quitter" || e === "slack_spammer") {
183
+ const d = {
184
+ ...o,
185
+ render: {
186
+ sprite: {
187
+ texture: {
188
+ slacker: "./slacker.png",
189
+ control_freak: "./control-freak.png",
190
+ prima_donna: "./prima-donna.png",
191
+ quiet_quitter: "./quet-quitter.png",
192
+ // User typo kept
193
+ slack_spammer: "./slack-spammer.png"
194
+ }[e],
195
+ xScale: i.w / 512 * 2,
196
+ // Approximate
197
+ yScale: i.h / 512 * 2
198
+ },
199
+ lineWidth: 3,
200
+ strokeStyle: "#ffffff"
201
+ }
202
+ };
203
+ return e === "quiet_quitter" ? c.Bodies.circle(r, t, i.w / 2, d) : e === "control_freak" ? c.Bodies.trapezoid(r, t, i.w, i.h, 0.5, d) : e === "prima_donna" ? c.Bodies.polygon(r, t, 5, i.w / 2, d) : e === "slack_spammer" ? c.Bodies.polygon(r, t, 6, i.w / 2, d) : c.Bodies.rectangle(r, t, i.w, i.h, d);
204
+ } else
205
+ return c.Bodies.rectangle(r, t, i.w, i.h, o);
206
+ };
207
+ class v {
208
+ constructor(t) {
209
+ l(this, "runway");
210
+ l(this, "valuation", 0);
211
+ l(this, "stage");
212
+ // Dynamic name
213
+ l(this, "status", "playing");
214
+ l(this, "lastPenaltyReason", "");
215
+ l(this, "config");
216
+ l(this, "currentStageIndex", 0);
217
+ l(this, "listeners", []);
218
+ if (this.config = t, this.runway = t.initialRunway, !this.config.stages || this.config.stages.length === 0)
219
+ throw new Error("Game must have at least one stage defined.");
220
+ this.config.stages.sort((e, i) => e.threshold - i.threshold), this.currentStageIndex = 0, this.stage = this.config.stages[0].name, this.checkStage();
221
+ }
222
+ setPaused(t) {
223
+ t ? this.status === "playing" && (this.status = "paused") : this.status === "paused" && (this.status = "playing"), this.notify();
224
+ }
225
+ togglePause() {
226
+ this.setPaused(
227
+ this.status === "playing"
228
+ /* PLAYING */
229
+ );
230
+ }
231
+ tick(t) {
232
+ if (this.status !== "playing") return;
233
+ const e = Math.min(t, 0.1), i = this.getDynamicBurnRate();
234
+ this.runway -= i * e, this.runway <= 0 && (this.runway = 0, this.status = "bankrupt");
235
+ const s = this.config.stages[this.config.stages.length - 1];
236
+ this.valuation >= s.threshold && this.status === "playing" && (this.status = "exited"), this.checkStage(), this.notify();
237
+ }
238
+ getDynamicBurnRate() {
239
+ return this.config.stages[this.currentStageIndex].burnRate;
240
+ }
241
+ addValuation(t) {
242
+ this.valuation += t, this.checkStage(), this.notify();
243
+ }
244
+ spendMoney(t) {
245
+ this.runway -= t, this.notify();
246
+ }
247
+ addMoney(t) {
248
+ this.runway += t, this.notify();
249
+ }
250
+ setCollapsed() {
251
+ this.status === "playing" && (this.status = "collapsed", this.notify());
252
+ }
253
+ getNextStageThreshold() {
254
+ return this.currentStageIndex < this.config.stages.length - 1 ? this.config.stages[this.currentStageIndex + 1].threshold : this.config.stages[this.config.stages.length - 1].threshold;
255
+ }
256
+ penalty(t, e) {
257
+ this.runway -= t, this.lastPenaltyReason = e, this.runway < 0 && (this.runway = 0, this.status = "bankrupt"), this.notify("penalty");
258
+ }
259
+ checkStage() {
260
+ let t = 0;
261
+ for (let e = 0; e < this.config.stages.length && this.valuation >= this.config.stages[e].threshold; e++)
262
+ t = e;
263
+ if (t !== this.currentStageIndex) {
264
+ if (t > this.currentStageIndex) {
265
+ const e = this.config.stages[t].fundingBonus || 0;
266
+ e > 0 && (this.addMoney(e), this.notify("funding:" + e));
267
+ }
268
+ this.currentStageIndex = t, this.stage = this.config.stages[t].name;
269
+ }
270
+ }
271
+ subscribe(t) {
272
+ this.listeners.push(t);
273
+ }
274
+ notify(t) {
275
+ this.listeners.forEach((e) => e(t));
276
+ }
277
+ isPlaying() {
278
+ return this.status === "playing";
279
+ }
280
+ isBankrupt() {
281
+ return this.status === "bankrupt";
282
+ }
283
+ }
284
+ class w {
285
+ constructor(t, e = {}) {
286
+ l(this, "engine");
287
+ l(this, "render");
288
+ l(this, "runner");
289
+ l(this, "container");
290
+ l(this, "width");
291
+ l(this, "height");
292
+ l(this, "mouseConstraint");
293
+ l(this, "currentQuote", null);
294
+ l(this, "chaosTimer", 1e4);
295
+ // Debug: Initial event after 10s
296
+ l(this, "chaosInterval", 9e4);
297
+ // Default 1.5 mins
298
+ l(this, "onCrisis");
299
+ l(this, "gameState");
300
+ l(this, "spawnVertical", !1);
301
+ this.container = t, this.width = e.width || t.clientWidth || 800, this.height = e.height || t.clientHeight || 600, this.onCrisis = e.onCrisis, this.chaosInterval = e.chaosInterval || 9e4, this.chaosTimer = e.chaosInterval ? e.chaosInterval : 1e4;
302
+ const i = [
303
+ { name: "Seed Round", threshold: 0, burnRate: 2e3 },
304
+ { name: "Series A", threshold: 1e8, burnRate: 1e4, fundingBonus: 25e4 },
305
+ { name: "Series B", threshold: 25e7, burnRate: 25e3, fundingBonus: 5e5 },
306
+ { name: "Series C", threshold: 5e8, burnRate: 5e4, fundingBonus: 1e6 },
307
+ { name: "Series D", threshold: 7e8, burnRate: 75e3, fundingBonus: 2e6 },
308
+ { name: "Series E", threshold: 85e7, burnRate: 1e5, fundingBonus: 5e6 },
309
+ { name: "Unicorn Status", threshold: 1e9, burnRate: 15e4, fundingBonus: 1e7 }
310
+ ];
311
+ this.gameState = new v({
312
+ initialRunway: e.initialRunway || 2e5,
313
+ stages: e.stages || i
314
+ }), this.engine = c.Engine.create(), this.render = c.Render.create({
315
+ element: this.container,
316
+ engine: this.engine,
317
+ options: {
318
+ width: this.width,
319
+ height: this.height,
320
+ wireframes: !1,
321
+ background: "#1a1a1a"
322
+ }
323
+ }), this.runner = c.Runner.create(), this.initWorld(), this.initMouse(), this.initRenderLoop(), this.initGameLoop(), this.initControls(), this.gameState.subscribe(() => {
324
+ this.gameState.status === "paused" ? this.runner.enabled = !1 : this.gameState.status === "playing" && (this.runner.enabled = !0);
325
+ });
326
+ }
327
+ initControls() {
328
+ window.addEventListener("keydown", (t) => {
329
+ (t.key === "r" || t.key === "R") && this.gameState.isPlaying() && this.rotateHeldBody();
330
+ });
331
+ }
332
+ rotateHeldBody() {
333
+ const t = this.mouseConstraint.body;
334
+ t && c.Body.rotate(t, 45 * (Math.PI / 180));
335
+ }
336
+ initWorld() {
337
+ const { width: t, height: e } = this, i = 60, s = c.Bodies.rectangle(t / 2, e - i / 2, t, i, {
338
+ isStatic: !0,
339
+ render: { fillStyle: "#333" }
340
+ });
341
+ c.Composite.add(this.engine.world, [s]);
342
+ }
343
+ initMouse() {
344
+ const t = c.Mouse.create(this.render.canvas);
345
+ this.mouseConstraint = c.MouseConstraint.create(this.engine, {
346
+ mouse: t,
347
+ constraint: {
348
+ stiffness: 0.2,
349
+ render: { visible: !1 }
350
+ }
351
+ }), c.Composite.add(this.engine.world, this.mouseConstraint), this.render.mouse = t;
352
+ }
353
+ start() {
354
+ c.Render.run(this.render), c.Runner.run(this.runner, this.engine);
355
+ }
356
+ stop() {
357
+ c.Render.stop(this.render), c.Runner.stop(this.runner);
358
+ }
359
+ initRenderLoop() {
360
+ c.Events.on(this.render, "afterRender", () => {
361
+ const t = this.render.context, e = this.width / 2, i = 400, s = 700, o = 100, n = { x: e - i / 2, y: o, w: i, h: this.height - o }, d = { x: e - s / 2, y: o - 50, w: s, h: this.height - o + 100 };
362
+ if (t.beginPath(), t.setLineDash([10, 10]), t.lineWidth = 2, t.strokeStyle = "rgba(255, 255, 255, 0.3)", t.strokeRect(n.x, n.y, n.w, n.h), t.font = '10px "Inter", sans-serif', t.fillStyle = "rgba(255, 255, 255, 0.3)", t.textAlign = "center", t.fillText("ACCURATE VALUATION ZONE", e, n.y - 15), !this.currentQuote) {
363
+ const h = [
364
+ { text: `WE ARE A
365
+ FAMILY`, author: "- CEO" },
366
+ { text: `CHANGE THE
367
+ WORLD`, author: "- Founder" },
368
+ { text: `DISRUPT
369
+ EVERYTHING`, author: "- VC" },
370
+ { text: `MOVE FAST
371
+ BREAK THINGS`, author: "- Tech Lead" },
372
+ { text: `RADICAL
373
+ CANDOR`, author: "- HR" },
374
+ { text: `UNLIMITED
375
+ PTO`, author: "- Recruiter" },
376
+ { text: `VISIONARY
377
+ LEADER`, author: "- LinkedIn" },
378
+ { text: `10X
379
+ GROWTH`, author: "- Investor" },
380
+ { text: `PIVOT
381
+ TO AI`, author: "- Board" }
382
+ ];
383
+ this.currentQuote = h[Math.floor(Math.random() * h.length)];
384
+ }
385
+ t.save(), t.translate(e, n.y + n.h / 3), t.rotate(-Math.PI / 12), t.textAlign = "center", t.textBaseline = "middle", t.font = '900 48px "Inter", sans-serif', t.fillStyle = "rgba(255, 255, 255, 0.05)";
386
+ const g = this.currentQuote.text.split(`
387
+ `);
388
+ g.forEach((h, a) => t.fillText(h, 0, a * 50)), t.font = 'italic 700 24px "Inter", sans-serif', t.fillStyle = "rgba(255, 255, 255, 0.04)", t.fillText(this.currentQuote.author, 0, g.length * 50 + 10), t.restore(), t.beginPath(), t.setLineDash([]), t.lineWidth = 1, t.strokeStyle = "rgba(255, 50, 50, 0.2)", t.strokeRect(d.x, d.y, d.w, d.h), t.font = 'bold 13px "Inter", sans-serif', t.textAlign = "center", t.textBaseline = "middle", t.fillStyle = "#FFFFFF", this.engine.world.bodies.forEach((h) => {
389
+ if (!h.label || h.label.includes("Body") || h.isStatic) return;
390
+ const a = h.gameData, f = a && a.vested;
391
+ t.save(), t.translate(h.position.x, h.position.y), t.rotate(h.angle), f ? (t.shadowColor = "#00ff7f", t.shadowBlur = 10, t.fillStyle = "#00ff7f", t.fillText(h.label + " ✓", 0, 0)) : (t.shadowColor = "black", t.shadowBlur = 4, t.fillStyle = "#FFFFFF", t.fillText(h.label, 0, 0)), t.restore();
392
+ });
393
+ });
394
+ }
395
+ initGameLoop() {
396
+ c.Events.on(this.runner, "afterUpdate", () => {
397
+ if (!this.gameState.isPlaying()) return;
398
+ const t = this.runner.delta;
399
+ this.gameState.tick(t / 1e3), this.gameState.valuation >= 1e6 && (this.chaosTimer -= t, this.chaosTimer <= 0 && (this.triggerChaosEvent(), this.chaosTimer = this.chaosInterval));
400
+ const e = this.width / 2, i = 400, s = 700;
401
+ this.engine.world.bodies.forEach((o) => {
402
+ if (o.isStatic) return;
403
+ const n = o.gameData;
404
+ if (Math.abs(o.position.x - e) > s / 2 || o.position.y > this.height + 50) {
405
+ c.Composite.remove(this.engine.world, o);
406
+ const g = ["VC Scolding!", "Server Outage!", "GDPR Violation!", "TechCrunch Hit Piece!", "IP Lawsuit!"];
407
+ this.gameState.penalty(5e4, g[Math.floor(Math.random() * g.length)]);
408
+ return;
409
+ }
410
+ if (n && !n.vested) {
411
+ const g = o.speed < 0.15 && Math.abs(o.angularVelocity) < 0.05, h = Math.abs(o.position.x - e) < i / 2;
412
+ g && h ? (n.vestTimer += this.runner.delta, n.vestTimer > 1e3 && (n.vested = !0, this.gameState.addValuation(n.value))) : n.vestTimer = 0;
413
+ }
414
+ });
415
+ });
416
+ }
417
+ triggerChaosEvent() {
418
+ var e, i, s, o, n;
419
+ switch (Math.floor(Math.random() * 5)) {
420
+ case 0:
421
+ (e = this.onCrisis) == null || e.call(this, "STRATEGIC PIVOT", "CEO: 'We are pivoting to AI!' (Gravity Shift)"), this.engine.world.gravity.x = 0.2, setTimeout(() => {
422
+ this.engine.world.gravity.x = 0;
423
+ }, 5e3);
424
+ break;
425
+ case 1:
426
+ (i = this.onCrisis) == null || i.call(this, "THE BIG REORG", "HR: 'New Org Chart Announced!' (No Friction)"), this.engine.world.bodies.forEach((a) => {
427
+ a.isStatic || (a.oldFriction = a.friction, a.friction = 0, a.frictionStatic = 0);
428
+ }), setTimeout(() => {
429
+ this.engine.world.bodies.forEach((a) => {
430
+ a.isStatic || (a.friction = a.oldFriction || 0.1, a.frictionStatic = 0.5);
431
+ });
432
+ }, 5e3);
433
+ break;
434
+ case 2:
435
+ (s = this.onCrisis) == null || s.call(this, "LEGACY DEPRECATION", "CTO: 'Deprecating v1 API!' (Ghost Blocks)");
436
+ const d = this.engine.world.bodies.filter((a) => !a.isStatic);
437
+ if (d.length > 3) {
438
+ const a = d[Math.floor(Math.random() * d.length)];
439
+ a.isSensor = !0, setTimeout(() => {
440
+ a.isSensor = !1;
441
+ }, 4e3);
442
+ }
443
+ break;
444
+ case 3:
445
+ (o = this.onCrisis) == null || o.call(this, "FEATURE CREEP", "PM: 'Just one more feature...' (1.2x Scaling)"), this.engine.world.bodies.forEach((a) => {
446
+ a.isStatic || c.Body.scale(a, 1.2, 1.2);
447
+ });
448
+ break;
449
+ case 4:
450
+ (n = this.onCrisis) == null || n.call(this, "VIRAL SPIKE", "DevOps: 'Traffic Spike!' (Earthquake)");
451
+ let g = 0;
452
+ const h = setInterval(() => {
453
+ this.engine.world.bodies.forEach((a) => {
454
+ a.isStatic || c.Body.applyForce(a, a.position, { x: (Math.random() - 0.5) * 0.05, y: (Math.random() - 0.5) * 0.05 });
455
+ }), ++g > 20 && clearInterval(h);
456
+ }, 100);
457
+ break;
458
+ }
459
+ }
460
+ toggleRotation() {
461
+ this.spawnVertical = !this.spawnVertical;
462
+ }
463
+ spawnBlock(t) {
464
+ if (!this.gameState.isPlaying()) return;
465
+ if (Math.random() < 1 / 15) {
466
+ const o = [u.SLACKER, u.CONTROL_FREAK, u.PRIMA_DONNA, u.QUIET_QUITTER, u.SLACK_SPAMMER];
467
+ t = o[Math.floor(Math.random() * o.length)];
468
+ }
469
+ t === u.BOOTSTRAP ? this.gameState.spendMoney(5e3) : t === u.VC && this.gameState.addMoney(75e3);
470
+ const e = this.width / 2 + (Math.random() - 0.5) * 40, i = y(e, S, t);
471
+ let s = this.spawnVertical ? 90 * (Math.PI / 180) : 0;
472
+ t === u.VC && (s += (Math.random() - 0.5) * (30 * (Math.PI / 180))), c.Body.setAngle(i, s), i.gameData = {
473
+ type: t,
474
+ value: t === u.VC ? 5e7 : t === u.BOOTSTRAP ? 5e6 : 0,
475
+ vested: !1,
476
+ vestTimer: 0
477
+ }, c.Composite.add(this.engine.world, i);
478
+ }
479
+ }
480
+ const S = 100;
481
+ class k {
482
+ constructor(t, e) {
483
+ l(this, "container");
484
+ l(this, "options");
485
+ l(this, "hudElement");
486
+ this.container = t, this.options = e, this.initStyles(), this.createHUD(), this.createControls(), this.createLegend(), this.createSystemButtons(), this.options.gameState.subscribe((i) => {
487
+ if (this.updateHUD(), i === "penalty")
488
+ this.showToast(`-${this.options.gameState.lastPenaltyReason}`, "danger");
489
+ else if (i != null && i.startsWith("funding:")) {
490
+ const s = i.split(":")[1];
491
+ this.showToast(`+$${parseInt(s).toLocaleString()} Series Funding!`, "info");
492
+ }
493
+ }), this.updateHUD();
494
+ }
495
+ initStyles() {
496
+ const t = document.createElement("style");
497
+ t.textContent = `
498
+ .uj-hud {
499
+ position: absolute;
500
+ top: 20px; /* Restored to top */
501
+ right: 20px;
502
+ color: #fff;
503
+ font-family: 'Inter', sans-serif;
504
+ text-align: right;
505
+ pointer-events: none;
506
+ z-index: 100;
507
+ }
508
+ /* ... existing styles ... */
509
+ .uj-sys-btn {
510
+ background: rgba(255,255,255,0.1);
511
+ border: 1px solid rgba(255,255,255,0.3);
512
+ color: #fff;
513
+ padding: 6px 10px;
514
+ border-radius: 4px;
515
+ cursor: pointer;
516
+ font-size: 11px; /* Slightly smaller to be discreet */
517
+ font-family: 'Inter', sans-serif;
518
+ transition: background 0.2s;
519
+ pointer-events: auto;
520
+ }
521
+ .uj-sys-btn:hover { background: rgba(255,255,255,0.2); }
522
+ .uj-sys-container {
523
+ position: absolute;
524
+ bottom: 20px; /* Moved to bottom */
525
+ left: 20px; /* Moved to left */
526
+ display: flex;
527
+ gap: 10px;
528
+ z-index: 200;
529
+ opacity: 0.6; /* Make them even less conspicuous */
530
+ transition: opacity 0.2s;
531
+ }
532
+ .uj-sys-container:hover { opacity: 1; }
533
+ .uj-stat-label {
534
+ font-size: 12px;
535
+ color: #888;
536
+ text-transform: uppercase;
537
+ }
538
+ .uj-stat-value {
539
+ font-size: 20px;
540
+ font-weight: bold;
541
+ margin-bottom: 10px;
542
+ }
543
+ .uj-stat-value.danger {
544
+ color: #ff4444;
545
+ }
546
+ .uj-controls {
547
+ position: absolute;
548
+ bottom: 20px;
549
+ right: 20px; /* Aligned with HUD */
550
+ display: flex;
551
+ flex-direction: column; /* Stack vertically for cleaner look on right side? Or Keep horizontal? */
552
+ /* User said "move right". Sidebar layout might be better. */
553
+ gap: 20px;
554
+ z-index: 100;
555
+ align-items: flex-end; /* Align right */
556
+ }
557
+ .uj-btn {
558
+ padding: 16px 0; /* Changed padding to vertical only, width handles horizontal */
559
+ width: 220px; /* Fixed width for equality */
560
+ font-family: 'Inter', sans-serif;
561
+ font-weight: bold;
562
+ border: none;
563
+ border-radius: 8px;
564
+ cursor: pointer;
565
+ text-transform: uppercase;
566
+ font-size: 16px;
567
+ box-shadow: 0 4px 6px rgba(0,0,0,0.3);
568
+ transition: transform 0.1s;
569
+ text-align: center; /* Ensure centered text */
570
+ }
571
+ .uj-btn:active {
572
+ transform: scale(0.95);
573
+ }
574
+ .uj-btn-solid {
575
+ background: #555;
576
+ color: #fff;
577
+ border-bottom: 4px solid #333;
578
+ }
579
+ .uj-btn-hype {
580
+ background: #FF00FF;
581
+ color: #fff;
582
+ border-bottom: 4px solid #cc00cc;
583
+ animation: pulse 2s infinite;
584
+ }
585
+ .uj-btn-rotate {
586
+ background: #0077ff;
587
+ color: #fff;
588
+ border-bottom: 4px solid #0055aa;
589
+ padding: 16px 20px;
590
+ }
591
+ .uj-btn-rotate.vertical {
592
+ background: #00AAFF;
593
+ transform: translateY(-2px);
594
+ }
595
+ @keyframes pulse {
596
+ 0% { box-shadow: 0 0 0 0 rgba(255, 0, 255, 0.7); }
597
+ 70% { box-shadow: 0 0 0 10px rgba(255, 0, 255, 0); }
598
+ 100% { box-shadow: 0 0 0 0 rgba(255, 0, 255, 0); }
599
+ }
600
+ .uj-overlay-logo {
601
+ position: absolute;
602
+ top: 20px;
603
+ left: 20px;
604
+ max-width: 150px;
605
+ z-index: 100;
606
+ pointer-events: none;
607
+ }
608
+ .uj-legend {
609
+ position: absolute;
610
+ top: 80px;
611
+ left: 20px;
612
+ width: 240px;
613
+ z-index: 90;
614
+ font-family: 'Inter', sans-serif;
615
+ color: white;
616
+ background: rgba(0,0,0,0.6);
617
+ padding: 12px;
618
+ border-radius: 8px;
619
+ backdrop-filter: blur(6px);
620
+ border: 1px solid rgba(255,255,255,0.1);
621
+ }
622
+ .uj-legend-item {
623
+ display: flex;
624
+ align-items: center;
625
+ margin-bottom: 12px;
626
+ }
627
+ .uj-swatch {
628
+ width: 20px;
629
+ height: 20px;
630
+ margin-right: 10px;
631
+ border: 1px solid white;
632
+ }
633
+ .uj-swatch.solid { background: #555; }
634
+ .uj-swatch.hype { background: #FF00FF; }
635
+ .uj-legend-text { font-size: 12px; line-height: 1.4; }
636
+ .uj-legend-note { margin-top: 10px; opacity: 0.7; font-size: 11px; }
637
+ .uj-legend-mini {
638
+ display: flex;
639
+ align-items: center;
640
+ gap: 6px;
641
+ font-size: 10px;
642
+ color: #ddd;
643
+ }
644
+ .uj-crisis-overlay {
645
+ position: absolute;
646
+ top: 0; left: 0; width: 100%; height: 100%;
647
+ pointer-events: none;
648
+ display: flex;
649
+ flex-direction: column;
650
+ align-items: center;
651
+ justify-content: center;
652
+ z-index: 400;
653
+ font-family: 'Inter', sans-serif;
654
+ background: rgba(255, 0, 0, 0);
655
+ transition: background 0.3s;
656
+ }
657
+ .uj-crisis-overlay.active {
658
+ background: rgba(255, 0, 0, 0.2);
659
+ animation: flash-red 1s infinite;
660
+ }
661
+ @keyframes flash-red {
662
+ 0%, 100% { background: rgba(255,0,0,0.1); }
663
+ 50% { background: rgba(255,0,0,0.3); }
664
+ }
665
+ .uj-crisis-box {
666
+ background: #ff4444;
667
+ color: white;
668
+ padding: 20px 40px;
669
+ border-radius: 12px;
670
+ box-shadow: 0 0 20px rgba(0,0,0,0.5);
671
+ text-align: center;
672
+ transform: scale(0.8);
673
+ opacity: 0;
674
+ transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
675
+ }
676
+ .uj-crisis-box.visible {
677
+ transform: scale(1);
678
+ opacity: 1;
679
+ }
680
+ .uj-crisis-shout {
681
+ font-size: 32px;
682
+ font-weight: 900;
683
+ margin-bottom: 5px;
684
+ text-transform: uppercase;
685
+ }
686
+ .uj-crisis-desc {
687
+ font-size: 18px;
688
+ opacity: 0.9;
689
+ }
690
+ `, document.head.appendChild(t);
691
+ }
692
+ createControls() {
693
+ const t = document.createElement("div");
694
+ t.className = "uj-controls";
695
+ const e = document.createElement("button");
696
+ e.className = "uj-btn uj-btn-solid", e.innerText = `Build Solid
697
+ (Costs $5k)`, e.onclick = () => this.options.onSpawn(u.BOOTSTRAP);
698
+ const i = document.createElement("button");
699
+ i.className = "uj-btn uj-btn-hype", i.innerText = `Hype Up
700
+ (Grants $75k)`, i.onclick = () => this.options.onSpawn(u.VC), t.appendChild(e), t.appendChild(i), this.container.appendChild(t);
701
+ }
702
+ createSystemButtons() {
703
+ const t = document.createElement("div");
704
+ t.className = "uj-sys-container";
705
+ const e = document.createElement("button");
706
+ e.className = "uj-sys-btn", e.innerText = "⏸ PAUSE", e.id = "uj-btn-pause", e.onclick = () => {
707
+ const s = this.options.gameState.isPlaying();
708
+ this.options.gameState.togglePause(), e.innerText = s ? "▶ RESUME" : "⏸ PAUSE";
709
+ };
710
+ const i = document.createElement("button");
711
+ i.className = "uj-sys-btn", i.innerText = "↻ RESET", i.onclick = () => window.location.reload(), t.appendChild(e), t.appendChild(i), this.container.appendChild(t);
712
+ }
713
+ createLegend() {
714
+ var e;
715
+ const t = document.createElement("div");
716
+ t.className = "uj-legend", t.innerHTML = `
717
+ <div style="margin-bottom: 8px; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 6px;">
718
+ <strong style="color: #00ff7f; font-size: 14px;">MISSION</strong>
719
+ <div class="uj-legend-text">
720
+ 1. Reach <strong>$1 Billion</strong> Valuation.<br>
721
+ 2. Don't run out of <strong>Runway</strong>.<br>
722
+ <small style="opacity:0.7">Burn Rate increases with Stage!</small>
723
+ </div>
724
+ </div>
725
+
726
+ <div style="margin-bottom: 12px;">
727
+ <div class="uj-legend-item">
728
+ <div class="uj-swatch solid"></div>
729
+ <div class="uj-legend-text">
730
+ <strong>Build Solid</strong><br>
731
+ High Friction. Good Foundation.<br>
732
+ <span style="color:#ffaaaa">Costs $5k. Low Value.</span>
733
+ </div>
734
+ </div>
735
+ <div class="uj-legend-item">
736
+ <div class="uj-swatch hype"></div>
737
+ <div class="uj-legend-text">
738
+ <strong>Hype Up</strong><br>
739
+ Distorted Shape. Unstable.<br>
740
+ <span style="color:#aaffaa">Grants $75k. High Value.</span>
741
+ </div>
742
+ </div>
743
+ </div>
744
+
745
+ <!-- Toxic Employees Section -->
746
+ <div style="margin-bottom: 8px; border-top: 1px solid rgba(255,255,255,0.1); padding-top: 6px;">
747
+ <strong style="color: #ff4444; font-size: 13px;">TOXIC (Risk: 1/15)</strong>
748
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 5px;">
749
+ <div class="uj-legend-mini">
750
+ <img src="./slacker.png" style="width: 30px; height: auto;">
751
+ <span>Slacker</span>
752
+ </div>
753
+ <div class="uj-legend-mini">
754
+ <img src="./control-freak.png" style="width: 30px; height: auto;">
755
+ <span>Control Freak</span>
756
+ </div>
757
+ <div class="uj-legend-mini">
758
+ <img src="./prima-donna.png" style="width: 30px; height: auto;">
759
+ <span>Prima Donna</span>
760
+ </div>
761
+ <div class="uj-legend-mini">
762
+ <img src="./quet-quitter.png" style="width: 30px; height: auto;">
763
+ <span>Quiet Quitter</span>
764
+ </div>
765
+ <div class="uj-legend-mini">
766
+ <img src="./slack-spammer.png" style="width: 30px; height: auto;">
767
+ <span>Spammer</span>
768
+ </div>
769
+ </div>
770
+ <div class="uj-legend-note" style="color: #ffaaaa; margin-top: 5px;">
771
+ $0 Val. Heavy. Annoying.
772
+ </div>
773
+ </div>
774
+ <div class="uj-legend-note" style="font-size: 10px; line-height: 1.3;">
775
+ <strong style="font-size: 11px;">STRATEGY:</strong><br>
776
+ • <strong>Hype-Hole</strong>: High Hype = Zero stability. Organization & code collapse.<br>
777
+ • <strong>Solid-Sink</strong>: Pure solid = Runway burn. No growth velocity.<br>
778
+ <span style="color: #00ff7f; opacity: 0.9;"><em>Balance foundation with noise.</em></span>
779
+ </div>
780
+
781
+ <div class="uj-legend-note" style="margin-top: 8px;">
782
+ <strong>RULES:</strong><br>
783
+ - Blocks only vest inside <strong>Dashed Zone</strong>.<br>
784
+ - Crossing <strong>Red Line</strong> = Penalty.<br>
785
+ - Press <strong>'R'</strong> to rotate.
786
+ </div>
787
+
788
+ <!-- Chaos Events Description -->
789
+ <div style="margin-top: 8px; border-top: 1px solid rgba(255,255,255,0.1); padding-top: 6px;">
790
+ <strong style="color: #ffaa00; font-size: 13px;">CHAOS EVENTS</strong>
791
+ <div style="font-size: 11px; color: #ccc; line-height: 1.4; margin-top: 4px;">
792
+ • <strong>Strategic Pivot</strong>: Gravity shifts.<br>
793
+ • <strong>The Big Reorg</strong>: Zero friction.<br>
794
+ • <strong>Legacy Deprecation</strong>: Ghost API.<br>
795
+ • <strong>Feature Creep</strong>: Codescale 1.2x.<br>
796
+ • <strong>Viral Spike</strong>: Trafficequake.
797
+ </div>
798
+ </div>
799
+
800
+ <button id="uj-view-labels" style="
801
+ margin-top: 15px;
802
+ width: 100%;
803
+ background: rgba(255,255,255,0.1);
804
+ border: 1px solid rgba(255,255,255,0.3);
805
+ color: #ccc;
806
+ border-radius: 4px;
807
+ padding: 6px 8px;
808
+ cursor: pointer;
809
+ font-size: 11px;
810
+ transition: background 0.2s;">
811
+ View Label Dictionary
812
+ </button>
813
+ `, this.container.appendChild(t), (e = document.getElementById("uj-view-labels")) == null || e.addEventListener("click", () => {
814
+ this.showGlossary();
815
+ });
816
+ }
817
+ showGlossary() {
818
+ if (document.getElementById("uj-glossary")) return;
819
+ const t = this.options.gameState.isPlaying();
820
+ if (t) {
821
+ this.options.gameState.setPaused(!0);
822
+ const s = document.getElementById("uj-btn-pause");
823
+ s && (s.innerText = "▶ RESUME");
824
+ }
825
+ const e = document.createElement("div");
826
+ e.id = "uj-glossary", e.style.cssText = `
827
+ position: absolute;
828
+ top: 0; left: 0; width: 100%; height: 100%;
829
+ background: rgba(0,0,0,0.9);
830
+ color: white;
831
+ display: flex;
832
+ flex-direction: column;
833
+ align-items: center;
834
+ justify-content: center;
835
+ z-index: 300;
836
+ font-family: 'Inter', sans-serif;
837
+ `, e.innerHTML = `
838
+ <div style="background: #222; padding: 40px; border-radius: 12px; max-width: 800px; width: 90%; max-height: 80vh; overflow-y: auto; position: relative;">
839
+ <button id="uj-glossary-close" style="position: absolute; top: 10px; right: 10px; background: none; border: none; color: white; font-size: 24px; cursor: pointer;">&times;</button>
840
+ <h2 style="text-align: center; margin-bottom: 30px;">Startup Lingo Dictionary</h2>
841
+ <div style="display: flex; gap: 40px;">
842
+ <div style="flex: 1;">
843
+ <h3 style="color: #aaa; border-bottom: 2px solid #555; padding-bottom: 10px;">Build Solid (The Work)</h3>
844
+ <ul id="uj-solid-list" style="list-style: none; padding: 0; color: #ccc; line-height: 1.6;"></ul>
845
+ </div>
846
+ <div style="flex: 1;">
847
+ <h3 style="color: #FF00FF; border-bottom: 2px solid #cc00cc; padding-bottom: 10px;">Hype Up (The Noise)</h3>
848
+ <ul id="uj-hype-list" style="list-style: none; padding: 0; color: #ffccff; line-height: 1.6;"></ul>
849
+ </div>
850
+ </div>
851
+ </div>
852
+ `, this.container.appendChild(e), this.populateGlossaryLists();
853
+ const i = () => {
854
+ if (e.remove(), t) {
855
+ this.options.gameState.setPaused(!1);
856
+ const s = document.getElementById("uj-btn-pause");
857
+ s && (s.innerText = "⏸ PAUSE");
858
+ }
859
+ };
860
+ document.getElementById("uj-glossary-close").onclick = i, e.onclick = (s) => {
861
+ s.target === e && i();
862
+ };
863
+ }
864
+ populateGlossaryLists() {
865
+ var i, s;
866
+ const t = document.getElementById("uj-solid-list"), e = document.getElementById("uj-hype-list");
867
+ t && e && ((i = p[u.BOOTSTRAP].possibleLabels) == null || i.forEach((o) => {
868
+ const n = document.createElement("li");
869
+ n.textContent = o, t.appendChild(n);
870
+ }), (s = p[u.VC].possibleLabels) == null || s.forEach((o) => {
871
+ const n = document.createElement("li");
872
+ n.textContent = o, e.appendChild(n);
873
+ }));
874
+ }
875
+ createHUD() {
876
+ this.hudElement = document.createElement("div"), this.hudElement.className = "uj-hud", this.container.appendChild(this.hudElement);
877
+ }
878
+ updateHUD() {
879
+ const t = this.options.gameState, e = new Intl.NumberFormat("en-US", {
880
+ style: "currency",
881
+ currency: "USD",
882
+ maximumFractionDigits: 0
883
+ }), i = new Intl.NumberFormat("en-US", {
884
+ style: "currency",
885
+ currency: "USD",
886
+ notation: "compact",
887
+ maximumFractionDigits: 1
888
+ }), o = t.isBankrupt() ? "uj-stat-value danger" : "uj-stat-value", n = t.getNextStageThreshold(), d = n > 0 ? `<small style="font-size:12px; opacity:0.7">Next: ${i.format(n)}</small>` : "";
889
+ this.hudElement.innerHTML = `
890
+ <div class="uj-stat-label">Stage</div>
891
+ <div class="uj-stat-value">${t.stage}</div>
892
+
893
+ <div class="uj-stat-label">Valuation</div>
894
+ <div class="uj-stat-value" style="color: #00ff7f">
895
+ ${e.format(t.valuation)}<br>
896
+ ${d}
897
+ </div>
898
+
899
+ <div class="uj-stat-label">Runway</div>
900
+ <div class="${o}">${e.format(t.runway)}</div>
901
+ `, t.status !== "playing" && this.showGameOver(t.status, t.valuation);
902
+ }
903
+ showToast(t, e = "info") {
904
+ const i = document.createElement("div");
905
+ i.innerText = t, i.style.cssText = `
906
+ position: absolute;
907
+ top: 100px;
908
+ left: 50%;
909
+ transform: translateX(-50%);
910
+ background: ${e === "danger" ? "rgba(255, 68, 68, 0.9)" : "rgba(0, 0, 0, 0.8)"};
911
+ color: white;
912
+ padding: 10px 20px;
913
+ border-radius: 20px;
914
+ font-family: 'Inter', sans-serif;
915
+ font-weight: bold;
916
+ z-index: 500;
917
+ animation: fadeUp 2s forwards;
918
+ white-space: nowrap;
919
+ `, this.container.appendChild(i), setTimeout(() => {
920
+ i.style.transition = "opacity 0.5s", i.style.opacity = "0", setTimeout(() => i.remove(), 500);
921
+ }, 2e3);
922
+ }
923
+ showGameOver(t, e) {
924
+ if (document.getElementById("uj-gameover")) return;
925
+ const i = document.createElement("div");
926
+ i.id = "uj-gameover", i.style.cssText = `
927
+ position: absolute;
928
+ top: 0; left: 0; width: 100%; height: 100%;
929
+ background: rgba(0,0,0,0.85);
930
+ color: white;
931
+ display: flex;
932
+ flex-direction: column;
933
+ align-items: center;
934
+ justify-content: center;
935
+ z-index: 200;
936
+ font-family: 'Inter', sans-serif;
937
+ `;
938
+ let s = "", o = "", n = "#fff";
939
+ switch (t) {
940
+ case "bankrupt":
941
+ s = "HONEST POOR", o = "You ran out of runway. The market did not love you.", n = "#ff4444";
942
+ break;
943
+ case "collapsed":
944
+ s = "SCANDAL!", o = "Your tower of lies has collapsed. TechCrunch is mocking you.", n = "#ffaa00";
945
+ break;
946
+ case "exited":
947
+ s = "VISIONARY FRAUD", o = `You exited with $${(e / 1e6).toFixed(1)}M! You are a genius.`, n = "#00ff7f";
948
+ break;
949
+ }
950
+ i.innerHTML = `
951
+ <h1 style="font-size: 48px; color: ${n}; margin-bottom: 10px;">${s}</h1>
952
+ <p style="font-size: 18px; color #ccc; max-width: 400px; text-align: center;">${o}</p>
953
+ <button onclick="window.location.reload()" style="
954
+ margin-top: 30px;
955
+ padding: 15px 30px;
956
+ font-size: 20px;
957
+ background: white;
958
+ border: none;
959
+ cursor: pointer;
960
+ border-radius: 8px;
961
+ font-weight: bold;
962
+ ">TRY AGAIN</button>
963
+ `, this.container.appendChild(i);
964
+ }
965
+ showLogo(t) {
966
+ if (!t) return;
967
+ const e = document.createElement("img");
968
+ e.src = t, e.className = "uj-overlay-logo", this.container.appendChild(e);
969
+ }
970
+ showCrisisAlert(t, e) {
971
+ const i = document.createElement("div");
972
+ i.className = "uj-crisis-overlay active";
973
+ const s = document.createElement("div");
974
+ s.className = "uj-crisis-box", s.innerHTML = `
975
+ <div class="uj-crisis-shout">${t}</div>
976
+ <div class="uj-crisis-desc">${e}</div>
977
+ `, i.appendChild(s), this.container.appendChild(i), setTimeout(() => s.classList.add("visible"), 100), setTimeout(() => {
978
+ s.classList.remove("visible"), i.classList.remove("active"), setTimeout(() => {
979
+ i.remove();
980
+ }, 500);
981
+ }, 4e3);
982
+ }
983
+ }
984
+ class T {
985
+ constructor(t, e = {}) {
986
+ l(this, "game");
987
+ l(this, "ui");
988
+ this.game = new w(t, {
989
+ ...e,
990
+ onCrisis: (i, s) => this.ui.showCrisisAlert(i, s)
991
+ }), this.ui = new k(t, {
992
+ onSpawn: (i) => this.game.spawnBlock(i),
993
+ onRotate: () => this.game.toggleRotation(),
994
+ gameState: this.game.gameState
995
+ }), e.logoUrl && this.ui.showLogo(e.logoUrl);
996
+ }
997
+ start() {
998
+ this.game.start();
999
+ }
1000
+ }
1001
+ export {
1002
+ T as PreMortem,
1003
+ w as PreMortemGame,
1004
+ k as UIManager
1005
+ };