Lukeetah commited on
Commit
aaffe69
·
verified ·
1 Parent(s): a773eff

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.js +846 -0
  2. index.html +273 -19
  3. style.css +1277 -15
app.js ADDED
@@ -0,0 +1,846 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Resonancia Rioplatense - Game Engine
2
+ // Rewritten with robust event handling and debugging
3
+
4
+ let game;
5
+
6
+ // Initialize when DOM is loaded
7
+ document.addEventListener('DOMContentLoaded', function() {
8
+ console.log('DOM loaded, initializing game...');
9
+ game = new GameEngine();
10
+ });
11
+
12
+ class GameEngine {
13
+ constructor() {
14
+ this.gameState = 'landing';
15
+ this.player = null;
16
+ this.resonanceSystem = new ResonanceSystem();
17
+ this.narrativeEngine = new NarrativeEngine();
18
+ this.eventSystem = new EventSystem();
19
+
20
+ console.log('GameEngine constructor called');
21
+ this.init();
22
+ }
23
+
24
+ init() {
25
+ console.log('Initializing game...');
26
+ this.setupEventListeners();
27
+ this.loadGameData();
28
+ this.startGameLoop();
29
+ console.log('Game initialized');
30
+ }
31
+
32
+ setupEventListeners() {
33
+ console.log('Setting up event listeners...');
34
+
35
+ // Use setTimeout to ensure DOM is ready
36
+ setTimeout(() => {
37
+ // Landing page button
38
+ const startButton = document.getElementById('start-game');
39
+ console.log('Start button:', startButton);
40
+ if (startButton) {
41
+ startButton.onclick = () => {
42
+ console.log('Start button clicked');
43
+ this.transitionToScreen('character-creation');
44
+ };
45
+ }
46
+
47
+ // Character creation button
48
+ const createButton = document.getElementById('create-character');
49
+ console.log('Create button:', createButton);
50
+ if (createButton) {
51
+ createButton.onclick = () => {
52
+ console.log('Create character button clicked');
53
+ this.createCharacter();
54
+ };
55
+ }
56
+
57
+ // Decision buttons - use event delegation
58
+ document.onclick = (e) => {
59
+ if (e.target.classList.contains('decision-btn')) {
60
+ console.log('Decision button clicked:', e.target.dataset.decision);
61
+ const decision = e.target.dataset.decision;
62
+ this.processDecision(decision);
63
+ }
64
+
65
+ // Event joining
66
+ if (e.target.classList.contains('event-join')) {
67
+ console.log('Event join button clicked');
68
+ const eventElement = e.target.closest('.event-item');
69
+ const eventName = eventElement.querySelector('h5').textContent;
70
+ this.joinEvent(eventName);
71
+ }
72
+
73
+ // Modal controls
74
+ if (e.target.classList.contains('modal-close')) {
75
+ console.log('Modal close clicked');
76
+ this.closeModal();
77
+ }
78
+
79
+ if (e.target.id === 'continue-game') {
80
+ console.log('Continue game clicked');
81
+ this.closeModal();
82
+ this.generateNextScenario();
83
+ }
84
+
85
+ // NPC interactions
86
+ if (e.target.closest('.npc-item')) {
87
+ console.log('NPC clicked');
88
+ const npcElement = e.target.closest('.npc-item');
89
+ const npcId = npcElement.dataset.npc;
90
+ this.showNPCInfo(npcId);
91
+ }
92
+ };
93
+ }, 100);
94
+ }
95
+
96
+ transitionToScreen(screenId) {
97
+ console.log('Transitioning to screen:', screenId);
98
+
99
+ // Hide all screens
100
+ const screens = document.querySelectorAll('.screen');
101
+ screens.forEach(screen => {
102
+ screen.classList.remove('active');
103
+ });
104
+
105
+ // Show target screen
106
+ const targetScreen = document.getElementById(screenId);
107
+ if (targetScreen) {
108
+ targetScreen.classList.add('active');
109
+ this.gameState = screenId;
110
+ console.log('Successfully transitioned to:', screenId);
111
+ } else {
112
+ console.error('Screen not found:', screenId);
113
+ }
114
+ }
115
+
116
+ createCharacter() {
117
+ console.log('Creating character...');
118
+
119
+ const nameInput = document.getElementById('player-name');
120
+ const techSelect = document.getElementById('tech-background');
121
+ const culturalRadio = document.querySelector('input[name="cultural-preference"]:checked');
122
+ const socialSelect = document.getElementById('social-style');
123
+
124
+ const name = nameInput ? nameInput.value || 'Jugador' : 'Jugador';
125
+ const techBackground = techSelect ? techSelect.value : 'frontend';
126
+ const culturalPreference = culturalRadio ? culturalRadio.value : 'mixed';
127
+ const socialStyle = socialSelect ? socialSelect.value : 'adaptable';
128
+
129
+ console.log('Character data:', { name, techBackground, culturalPreference, socialStyle });
130
+
131
+ this.player = new Player({
132
+ name,
133
+ techBackground,
134
+ culturalPreference,
135
+ socialStyle
136
+ });
137
+
138
+ this.updatePlayerDisplay();
139
+ this.transitionToScreen('game-interface');
140
+
141
+ // Give some time for the transition, then generate scenario
142
+ setTimeout(() => {
143
+ this.generateNextScenario();
144
+ }, 500);
145
+ }
146
+
147
+ updatePlayerDisplay() {
148
+ if (!this.player) return;
149
+
150
+ console.log('Updating player display');
151
+
152
+ const nameElement = document.getElementById('player-display-name');
153
+ const roleElement = document.getElementById('player-role');
154
+
155
+ if (nameElement) nameElement.textContent = this.player.name;
156
+ if (roleElement) roleElement.textContent = this.getRoleDescription(this.player.techBackground);
157
+
158
+ this.updateStats();
159
+ this.updateNPCRelationships();
160
+ this.updateResonanceDisplay();
161
+ }
162
+
163
+ updateStats() {
164
+ if (!this.player) return;
165
+
166
+ const stats = ['carrera', 'cultura', 'social', 'bienestar'];
167
+ stats.forEach(stat => {
168
+ const value = this.player.stats[stat];
169
+ const fillElement = document.getElementById(`${stat}-fill`);
170
+ const valueElement = document.getElementById(`${stat}-value`);
171
+
172
+ if (fillElement && valueElement) {
173
+ fillElement.style.width = `${value}%`;
174
+ valueElement.textContent = value;
175
+ }
176
+ });
177
+ }
178
+
179
+ updateNPCRelationships() {
180
+ if (!this.player) return;
181
+
182
+ Object.entries(this.player.relationships).forEach(([npcId, relationship]) => {
183
+ const npcElement = document.querySelector(`[data-npc="${npcId}"]`);
184
+ if (npcElement) {
185
+ const fillElement = npcElement.querySelector('.relationship-fill');
186
+ if (fillElement) {
187
+ fillElement.style.width = `${relationship}%`;
188
+ }
189
+ }
190
+ });
191
+ }
192
+
193
+ updateResonanceDisplay() {
194
+ const resonanceWaves = document.querySelectorAll('.resonance-wave');
195
+ const activeWaves = Math.min(this.resonanceSystem.getResonanceLevel(), resonanceWaves.length);
196
+
197
+ resonanceWaves.forEach((wave, index) => {
198
+ if (index < activeWaves) {
199
+ wave.classList.add('active');
200
+ } else {
201
+ wave.classList.remove('active');
202
+ }
203
+ });
204
+
205
+ const resonanceText = document.querySelector('.resonance-text');
206
+ if (resonanceText) {
207
+ resonanceText.textContent = this.resonanceSystem.getResonanceDescription();
208
+ }
209
+ }
210
+
211
+ processDecision(decisionKey) {
212
+ console.log('Processing decision:', decisionKey);
213
+
214
+ const currentScenario = this.narrativeEngine.getCurrentScenario();
215
+ if (!currentScenario) {
216
+ console.log('No current scenario');
217
+ return;
218
+ }
219
+
220
+ const decision = currentScenario.decisions.find(d => d.key === decisionKey);
221
+
222
+ if (!decision) {
223
+ console.log('Decision not found:', decisionKey);
224
+ return;
225
+ }
226
+
227
+ console.log('Found decision:', decision);
228
+
229
+ // Apply stat changes
230
+ if (decision.effects) {
231
+ Object.entries(decision.effects).forEach(([stat, change]) => {
232
+ this.player.changeStat(stat, change);
233
+ });
234
+ }
235
+
236
+ // Update NPC relationships
237
+ if (decision.relationshipChanges) {
238
+ Object.entries(decision.relationshipChanges).forEach(([npcId, change]) => {
239
+ this.player.changeRelationship(npcId, change);
240
+ });
241
+ }
242
+
243
+ // Add to resonance system
244
+ this.resonanceSystem.addDecision(decision);
245
+
246
+ // Show resonance visualization
247
+ this.showResonanceEffect(decision);
248
+
249
+ // Update displays
250
+ this.updatePlayerDisplay();
251
+
252
+ // Generate next scenario after a delay
253
+ setTimeout(() => {
254
+ this.generateNextScenario();
255
+ }, 3000);
256
+ }
257
+
258
+ showResonanceEffect(decision) {
259
+ console.log('Showing resonance effect');
260
+ const modal = document.getElementById('resonance-modal');
261
+ if (modal) {
262
+ modal.classList.add('active');
263
+ this.drawResonanceVisualization(decision);
264
+
265
+ // Auto-close after 2 seconds
266
+ setTimeout(() => {
267
+ this.closeModal();
268
+ }, 2000);
269
+ }
270
+ }
271
+
272
+ drawResonanceVisualization(decision) {
273
+ const canvas = document.getElementById('resonance-canvas');
274
+ if (!canvas) return;
275
+
276
+ const ctx = canvas.getContext('2d');
277
+
278
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
279
+
280
+ const centerX = canvas.width / 2;
281
+ const centerY = canvas.height / 2;
282
+
283
+ // Draw decision impact as expanding circles
284
+ let radius = 10;
285
+ const maxRadius = 150;
286
+
287
+ const animate = () => {
288
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
289
+
290
+ // Draw multiple resonance waves
291
+ for (let i = 0; i < 3; i++) {
292
+ const waveRadius = radius - (i * 30);
293
+ if (waveRadius > 0) {
294
+ ctx.beginPath();
295
+ ctx.arc(centerX, centerY, waveRadius, 0, 2 * Math.PI);
296
+ ctx.strokeStyle = `rgba(33, 128, 141, ${0.8 - (waveRadius / maxRadius)})`;
297
+ ctx.lineWidth = 2;
298
+ ctx.stroke();
299
+ }
300
+ }
301
+
302
+ radius += 2;
303
+ if (radius < maxRadius) {
304
+ requestAnimationFrame(animate);
305
+ }
306
+ };
307
+
308
+ animate();
309
+ }
310
+
311
+ generateNextScenario() {
312
+ console.log('Generating next scenario');
313
+ const scenario = this.narrativeEngine.generateScenario(this.player, this.resonanceSystem);
314
+ this.displayScenario(scenario);
315
+ this.updateAvailableEvents();
316
+ }
317
+
318
+ displayScenario(scenario) {
319
+ console.log('Displaying scenario:', scenario);
320
+
321
+ const locationElement = document.getElementById('current-location');
322
+ const descriptionElement = document.getElementById('location-description');
323
+ const storyElement = document.getElementById('story-content');
324
+ const decisionsContainer = document.getElementById('decisions-container');
325
+
326
+ if (locationElement) locationElement.textContent = scenario.location.name;
327
+ if (descriptionElement) descriptionElement.textContent = scenario.location.description;
328
+ if (storyElement) storyElement.innerHTML = `<p>${scenario.narrative}</p>`;
329
+
330
+ if (decisionsContainer) {
331
+ decisionsContainer.innerHTML = `
332
+ <div class="decision-prompt">
333
+ <p>${scenario.prompt}</p>
334
+ </div>
335
+ <div class="decisions-list">
336
+ ${scenario.decisions.map(decision => `
337
+ <button class="decision-btn" data-decision="${decision.key}">
338
+ <span class="decision-text">${decision.text}</span>
339
+ <span class="decision-impact">${this.formatDecisionImpact(decision.effects)}</span>
340
+ </button>
341
+ `).join('')}
342
+ </div>
343
+ `;
344
+ }
345
+ }
346
+
347
+ formatDecisionImpact(effects) {
348
+ if (!effects) return '';
349
+
350
+ return Object.entries(effects)
351
+ .map(([stat, change]) => {
352
+ const prefix = change > 0 ? '+' : '';
353
+ const statName = stat.charAt(0).toUpperCase() + stat.slice(1);
354
+ return `${prefix}${statName}`;
355
+ })
356
+ .join(' ');
357
+ }
358
+
359
+ updateAvailableEvents() {
360
+ const eventsList = document.getElementById('events-list');
361
+ if (!eventsList || !this.player) return;
362
+
363
+ const availableEvents = this.eventSystem.getAvailableEvents(this.player);
364
+
365
+ eventsList.innerHTML = availableEvents.map(event => `
366
+ <div class="event-item ${event.available ? 'available' : 'locked'}">
367
+ <h5>${event.name}</h5>
368
+ <p>${event.description}</p>
369
+ ${event.available
370
+ ? `<span class="event-date">${event.date}</span>
371
+ <button class="btn btn--sm btn--secondary event-join">Asistir</button>`
372
+ : `<span class="event-requirement">${event.requirement}</span>`
373
+ }
374
+ </div>
375
+ `).join('');
376
+ }
377
+
378
+ joinEvent(eventName) {
379
+ console.log('Joining event:', eventName);
380
+ const result = this.eventSystem.joinEvent(eventName, this.player);
381
+ if (result) {
382
+ this.showEventResult(result);
383
+ this.resonanceSystem.addEvent(result);
384
+ this.updatePlayerDisplay();
385
+ }
386
+ }
387
+
388
+ showEventResult(result) {
389
+ const modal = document.getElementById('event-modal');
390
+ const title = document.getElementById('event-title');
391
+ const content = document.getElementById('event-result-content');
392
+
393
+ if (title) title.textContent = result.eventName;
394
+ if (content) {
395
+ content.innerHTML = `
396
+ <div class="event-result">
397
+ <p>${result.narrative}</p>
398
+ <div class="event-outcomes">
399
+ <h4>Resultados:</h4>
400
+ <ul>
401
+ ${result.outcomes.map(outcome => `<li>${outcome}</li>`).join('')}
402
+ </ul>
403
+ </div>
404
+ ${result.newConnections ? `
405
+ <div class="new-connections">
406
+ <h4>Nuevas Conexiones:</h4>
407
+ <p>${result.newConnections}</p>
408
+ </div>
409
+ ` : ''}
410
+ </div>
411
+ `;
412
+ }
413
+
414
+ if (modal) modal.classList.add('active');
415
+ }
416
+
417
+ closeModal() {
418
+ document.querySelectorAll('.modal').forEach(modal => {
419
+ modal.classList.remove('active');
420
+ });
421
+ }
422
+
423
+ showNPCInfo(npcId) {
424
+ const npcData = {
425
+ sofia: "Sofía es una frontend developer especializada en React. Viene de trabajar en San Francisco y lidera el tech team.",
426
+ mateo: "Mateo es product manager, ex-consultor de BCG obsesionado con métricas y user acquisition.",
427
+ luna: "Luna es artista digital freelance que organiza eventos culturales alternativos en la escena underground."
428
+ };
429
+
430
+ const info = npcData[npcId] || "Información no disponible";
431
+ alert(info);
432
+ }
433
+
434
+ getRoleDescription(techBackground) {
435
+ const roles = {
436
+ frontend: 'Frontend Developer',
437
+ backend: 'Backend Developer',
438
+ fullstack: 'Full Stack Developer',
439
+ product: 'Product Manager',
440
+ design: 'UX/UI Designer',
441
+ data: 'Data Scientist'
442
+ };
443
+ return roles[techBackground] || 'Desarrollador';
444
+ }
445
+
446
+ loadGameData() {
447
+ this.gameData = {
448
+ locations: [
449
+ {
450
+ name: "Vicente López",
451
+ description: "Hub tech moderno con startups y coworking spaces",
452
+ events: ["North Valley Meetup", "Startup Showcase", "After Office Tech"]
453
+ },
454
+ {
455
+ name: "San Isidro",
456
+ description: "Zona cultural con venues indies y espacios de arte",
457
+ events: ["Showcase Indie", "Exhibición Arte Digital", "Centro Cultural"]
458
+ },
459
+ {
460
+ name: "Olivos",
461
+ description: "Barrio residencial con cafeterías y espacios de networking",
462
+ events: ["Coffee Networking", "Encuentro Freelancers", "Workshop Design"]
463
+ }
464
+ ],
465
+ culturalPhrases: [
466
+ "¿Todo bien?", "Dale, joya", "Un golazo ese proyecto", "Re copado el evento",
467
+ "¿Qué tal?", "Está bárbaro", "Me parece genial", "Súper interesante"
468
+ ]
469
+ };
470
+ }
471
+
472
+ startGameLoop() {
473
+ setInterval(() => {
474
+ if (this.gameState === 'game-interface' && this.player) {
475
+ this.resonanceSystem.update();
476
+ this.updateResonanceDisplay();
477
+ }
478
+ }, 1000);
479
+ }
480
+ }
481
+
482
+ class Player {
483
+ constructor({ name, techBackground, culturalPreference, socialStyle }) {
484
+ this.name = name;
485
+ this.techBackground = techBackground;
486
+ this.culturalPreference = culturalPreference;
487
+ this.socialStyle = socialStyle;
488
+
489
+ this.stats = {
490
+ carrera: this.getInitialStat('carrera'),
491
+ cultura: this.getInitialStat('cultura'),
492
+ social: this.getInitialStat('social'),
493
+ bienestar: this.getInitialStat('bienestar')
494
+ };
495
+
496
+ this.relationships = {
497
+ sofia: 50,
498
+ mateo: 40,
499
+ luna: 30
500
+ };
501
+
502
+ this.memory = [];
503
+ this.achievements = [];
504
+
505
+ console.log('Player created:', this);
506
+ }
507
+
508
+ getInitialStat(statName) {
509
+ const baseStats = { carrera: 50, cultura: 30, social: 40, bienestar: 60 };
510
+ let value = baseStats[statName];
511
+
512
+ if (statName === 'carrera') {
513
+ value += ['frontend', 'backend', 'fullstack'].includes(this.techBackground) ? 10 : 0;
514
+ value += this.techBackground === 'product' ? 15 : 0;
515
+ }
516
+
517
+ if (statName === 'cultura') {
518
+ value += this.culturalPreference === 'music' ? 20 : 0;
519
+ value += this.culturalPreference === 'art' ? 15 : 0;
520
+ }
521
+
522
+ if (statName === 'social') {
523
+ value += this.socialStyle === 'networking' ? 20 : 0;
524
+ value += this.socialStyle === 'introvert' ? -10 : 0;
525
+ }
526
+
527
+ return Math.max(0, Math.min(100, value));
528
+ }
529
+
530
+ changeStat(statName, change) {
531
+ if (this.stats[statName] !== undefined) {
532
+ this.stats[statName] = Math.max(0, Math.min(100, this.stats[statName] + change));
533
+
534
+ const fillElement = document.getElementById(`${statName}-fill`);
535
+ if (fillElement) {
536
+ fillElement.classList.add(change > 0 ? 'increase' : 'decrease');
537
+ setTimeout(() => {
538
+ fillElement.classList.remove('increase', 'decrease');
539
+ }, 500);
540
+ }
541
+ }
542
+ }
543
+
544
+ changeRelationship(npcId, change) {
545
+ if (this.relationships[npcId] !== undefined) {
546
+ this.relationships[npcId] = Math.max(0, Math.min(100, this.relationships[npcId] + change));
547
+ }
548
+ }
549
+
550
+ addMemory(event) {
551
+ this.memory.push({
552
+ event,
553
+ timestamp: Date.now(),
554
+ impact: event.impact || 'minor'
555
+ });
556
+ }
557
+ }
558
+
559
+ class ResonanceSystem {
560
+ constructor() {
561
+ this.decisions = [];
562
+ this.resonanceLevel = 0;
563
+ this.activeWaves = [];
564
+ }
565
+
566
+ addDecision(decision) {
567
+ const resonanceWave = {
568
+ decision,
569
+ timestamp: Date.now(),
570
+ strength: this.calculateDecisionStrength(decision),
571
+ decayRate: 0.1
572
+ };
573
+
574
+ this.activeWaves.push(resonanceWave);
575
+ this.updateResonanceLevel();
576
+ }
577
+
578
+ addEvent(event) {
579
+ const eventWave = {
580
+ event,
581
+ timestamp: Date.now(),
582
+ strength: event.resonanceImpact || 0.5,
583
+ decayRate: 0.05
584
+ };
585
+
586
+ this.activeWaves.push(eventWave);
587
+ this.updateResonanceLevel();
588
+ }
589
+
590
+ calculateDecisionStrength(decision) {
591
+ const weights = { minor: 0.3, moderate: 0.6, major: 1.0, life_changing: 1.5 };
592
+ return weights[decision.weight] || 0.6;
593
+ }
594
+
595
+ update() {
596
+ const now = Date.now();
597
+
598
+ this.activeWaves = this.activeWaves.filter(wave => {
599
+ const age = (now - wave.timestamp) / 1000;
600
+ wave.strength *= Math.exp(-wave.decayRate * age);
601
+ return wave.strength > 0.01;
602
+ });
603
+
604
+ this.updateResonanceLevel();
605
+ }
606
+
607
+ updateResonanceLevel() {
608
+ this.resonanceLevel = this.activeWaves.reduce((total, wave) => total + wave.strength, 0);
609
+ }
610
+
611
+ getResonanceLevel() {
612
+ return Math.min(3, Math.floor(this.resonanceLevel));
613
+ }
614
+
615
+ getResonanceDescription() {
616
+ const level = this.getResonanceLevel();
617
+ const descriptions = [
618
+ "Calma total, sin ondas activas",
619
+ "Ligeras ondas de resonancia",
620
+ "Resonancia moderada en progreso",
621
+ "Intensa actividad de resonancia"
622
+ ];
623
+ return descriptions[level] || descriptions[0];
624
+ }
625
+
626
+ getResonanceEffects(player) {
627
+ const effects = [];
628
+
629
+ if (this.resonanceLevel > 1.5) {
630
+ effects.push("unexpected_opportunity");
631
+ }
632
+
633
+ if (this.resonanceLevel > 2.0) {
634
+ effects.push("personality_echo");
635
+ }
636
+
637
+ return effects;
638
+ }
639
+ }
640
+
641
+ class NarrativeEngine {
642
+ constructor() {
643
+ this.currentScenario = null;
644
+ this.scenarioHistory = [];
645
+ this.scenarioTemplates = this.initializeScenarios();
646
+ }
647
+
648
+ initializeScenarios() {
649
+ return [
650
+ {
651
+ id: 'startup_first_day',
652
+ location: { name: 'Vicente López', description: 'Hub tech moderno con startups y coworking spaces' },
653
+ narrative: 'Llegás a tu primer día en la nueva startup en Vicente López. El coworking space está lleno de energía, pantallas con código y el aroma de café de especialidad.',
654
+ prompt: 'Sofía, la frontend lead, se acerca durante el coffee break. Te comenta sobre un proyecto React que está armando y menciona que buscan alguien para el equipo.',
655
+ decisions: [
656
+ {
657
+ key: 'collaborate',
658
+ text: '"Me copa la propuesta, ¿cuándo arrancamos?"',
659
+ effects: { carrera: 10, social: 5 },
660
+ relationshipChanges: { sofia: 15 },
661
+ weight: 'moderate'
662
+ },
663
+ {
664
+ key: 'cautious',
665
+ text: '"Suena interesante, ¿me contás más detalles?"',
666
+ effects: { bienestar: 5 },
667
+ relationshipChanges: { sofia: 5 },
668
+ weight: 'minor'
669
+ },
670
+ {
671
+ key: 'independent',
672
+ text: '"Estoy enfocado en mi proyecto actual, pero gracias"',
673
+ effects: { carrera: 5, social: -5 },
674
+ relationshipChanges: { sofia: -10 },
675
+ weight: 'moderate'
676
+ }
677
+ ]
678
+ },
679
+ {
680
+ id: 'cultural_invitation',
681
+ location: { name: 'San Isidro', description: 'Zona cultural con venues indies y espacios de arte' },
682
+ narrative: 'Luna te escribe por Slack sobre un showcase de El Mató a un Policía Motorizado en un venue íntimo de San Isidro.',
683
+ prompt: 'Es viernes por la tarde y tenés que elegir entre quedarte terminando un sprint o ir al show.',
684
+ decisions: [
685
+ {
686
+ key: 'show',
687
+ text: '"Dale, me re copa. ¿Nos vemos ahí?"',
688
+ effects: { cultura: 15, bienestar: 10, carrera: -5 },
689
+ relationshipChanges: { luna: 20 },
690
+ weight: 'moderate'
691
+ },
692
+ {
693
+ key: 'work_first',
694
+ text: '"Me encantaría, pero tengo que cerrar este sprint"',
695
+ effects: { carrera: 10, bienestar: -5 },
696
+ relationshipChanges: { luna: -5 },
697
+ weight: 'minor'
698
+ },
699
+ {
700
+ key: 'compromise',
701
+ text: '"Si termino temprano, me sumo al after"',
702
+ effects: { carrera: 5, cultura: 5 },
703
+ relationshipChanges: { luna: 5 },
704
+ weight: 'minor'
705
+ }
706
+ ]
707
+ }
708
+ ];
709
+ }
710
+
711
+ generateScenario(player, resonanceSystem) {
712
+ const availableScenarios = this.scenarioTemplates.filter(scenario =>
713
+ !this.scenarioHistory.includes(scenario.id)
714
+ );
715
+
716
+ let selectedScenario;
717
+ if (availableScenarios.length === 0) {
718
+ selectedScenario = this.generateProceduralScenario(player, resonanceSystem);
719
+ } else {
720
+ selectedScenario = availableScenarios[0]; // Simple selection for now
721
+ }
722
+
723
+ this.currentScenario = selectedScenario;
724
+ this.scenarioHistory.push(selectedScenario.id);
725
+
726
+ return selectedScenario;
727
+ }
728
+
729
+ generateProceduralScenario(player, resonanceSystem) {
730
+ const locations = [
731
+ { name: 'Vicente López', description: 'Hub tech moderno con startups y coworking spaces' },
732
+ { name: 'San Isidro', description: 'Zona cultural con venues indies y espacios de arte' },
733
+ { name: 'Olivos', description: 'Barrio residencial con cafeterías y espacios de networking' }
734
+ ];
735
+
736
+ return {
737
+ id: `procedural_${Date.now()}`,
738
+ location: locations[Math.floor(Math.random() * locations.length)],
739
+ narrative: `Una nueva oportunidad aparece en tu camino. Las ondas de resonancia de tus decisiones pasadas están convergiendo...`,
740
+ prompt: "¿Cómo vas a responder a esta nueva situación?",
741
+ decisions: [
742
+ {
743
+ key: 'bold',
744
+ text: '"Voy con todo, sin dudas"',
745
+ effects: { carrera: 10, social: 5, bienestar: -5 },
746
+ weight: 'moderate'
747
+ },
748
+ {
749
+ key: 'balanced',
750
+ text: '"Analizo bien antes de decidir"',
751
+ effects: { carrera: 5, bienestar: 5 },
752
+ weight: 'minor'
753
+ },
754
+ {
755
+ key: 'creative',
756
+ text: '"Busco una solución creativa"',
757
+ effects: { cultura: 10, social: 5 },
758
+ weight: 'moderate'
759
+ }
760
+ ]
761
+ };
762
+ }
763
+
764
+ getCurrentScenario() {
765
+ return this.currentScenario;
766
+ }
767
+ }
768
+
769
+ class EventSystem {
770
+ constructor() {
771
+ this.events = [
772
+ {
773
+ name: "North Valley Meetup",
774
+ description: "Networking tech en Vicente López",
775
+ date: "Viernes 18:30",
776
+ requirements: { carrera: 20, social: 15 },
777
+ outcomes: ["networking_boost", "tech_knowledge", "startup_contacts"],
778
+ resonanceImpact: 0.7
779
+ },
780
+ {
781
+ name: "Innovation Tech Week",
782
+ description: "Semana tech más importante de BA",
783
+ date: "Próxima semana",
784
+ requirements: { carrera: 40, social: 30 },
785
+ outcomes: ["major_networking", "tech_skills_gain", "startup_opportunity"],
786
+ resonanceImpact: 1.2
787
+ },
788
+ {
789
+ name: "Showcase Indie",
790
+ description: "Show en venue de San Isidro",
791
+ date: "Sábado 21:00",
792
+ requirements: { cultura: 25, social: 20 },
793
+ outcomes: ["cultural_inspiration", "creative_boost", "underground_contacts"],
794
+ resonanceImpact: 0.8
795
+ }
796
+ ];
797
+ }
798
+
799
+ getAvailableEvents(player) {
800
+ return this.events.map(event => {
801
+ const available = this.checkRequirements(event.requirements, player);
802
+ return {
803
+ ...event,
804
+ available,
805
+ requirement: available ? null : this.formatRequirements(event.requirements, player)
806
+ };
807
+ });
808
+ }
809
+
810
+ checkRequirements(requirements, player) {
811
+ return Object.entries(requirements).every(([stat, required]) =>
812
+ player.stats[stat] >= required
813
+ );
814
+ }
815
+
816
+ formatRequirements(requirements, player) {
817
+ const unmet = Object.entries(requirements).find(([stat, required]) =>
818
+ player.stats[stat] < required
819
+ );
820
+
821
+ if (unmet) {
822
+ const [stat, required] = unmet;
823
+ const statName = stat.charAt(0).toUpperCase() + stat.slice(1);
824
+ return `Requiere ${statName}: ${required}`;
825
+ }
826
+
827
+ return "";
828
+ }
829
+
830
+ joinEvent(eventName, player) {
831
+ const event = this.events.find(e => e.name === eventName);
832
+ if (!event || !this.checkRequirements(event.requirements, player)) {
833
+ return null;
834
+ }
835
+
836
+ return {
837
+ eventName: event.name,
838
+ narrative: `Participás en ${event.name} y la experiencia es increíble. Conectás con gente nueva y aprendés un montón.`,
839
+ outcomes: ["Expandiste tu red de contactos", "Aprendiste sobre nuevas tecnologías"],
840
+ statChanges: { carrera: 10, social: 8 },
841
+ relationshipChanges: {},
842
+ newConnections: "Conociste a un founder de una startup prometedora",
843
+ resonanceImpact: event.resonanceImpact
844
+ };
845
+ }
846
+ }
index.html CHANGED
@@ -1,19 +1,273 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="es">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Resonancia Rioplatense - Simulador de Vida</title>
7
+ <link rel="stylesheet" href="style.css">
8
+ <meta name="description" content="Un simulador de vida revolucionario ambientado en la zona norte de Buenos Aires">
9
+ </head>
10
+ <body>
11
+ <!-- Landing Page -->
12
+ <div id="landing-page" class="screen active">
13
+ <div class="intro-container">
14
+ <div class="resonance-visual">
15
+ <div class="wave wave1"></div>
16
+ <div class="wave wave2"></div>
17
+ <div class="wave wave3"></div>
18
+ </div>
19
+ <h1 class="game-title">Resonancia Rioplatense</h1>
20
+ <p class="game-subtitle">Cada decisión genera ondas que transforman tu futuro</p>
21
+ <div class="intro-text">
22
+ <p>En la zona norte de Buenos Aires, donde la innovación tech se encuentra con la cultura indie, cada elección que tomás crea ondas de resonancia que se propagan en el tiempo.</p>
23
+ <p>¿Estás listo para descubrir cómo tus decisiones moldean tu destino?</p>
24
+ </div>
25
+ <button class="btn btn--primary btn--lg" id="start-game">Comenzar Experiencia</button>
26
+ </div>
27
+ </div>
28
+
29
+ <!-- Character Creation -->
30
+ <div id="character-creation" class="screen">
31
+ <div class="container">
32
+ <h2>Creá tu Perfil</h2>
33
+ <p>Definí tu trasfondo tech y preferencias culturales en la zona norte de Buenos Aires</p>
34
+
35
+ <div class="creation-form">
36
+ <div class="form-group">
37
+ <label class="form-label">Nombre</label>
38
+ <input type="text" class="form-control" id="player-name" placeholder="Tu nombre">
39
+ </div>
40
+
41
+ <div class="form-group">
42
+ <label class="form-label">Especialización Tech</label>
43
+ <select class="form-control" id="tech-background">
44
+ <option value="frontend">Frontend Developer - React/Vue specialist</option>
45
+ <option value="backend">Backend Developer - Node.js/Python expert</option>
46
+ <option value="fullstack">Full Stack Developer - Versatilidad total</option>
47
+ <option value="product">Product Manager - Strategy & growth</option>
48
+ <option value="design">UX/UI Designer - User experience focus</option>
49
+ <option value="data">Data Scientist - AI & analytics</option>
50
+ </select>
51
+ </div>
52
+
53
+ <div class="form-group">
54
+ <label class="form-label">¿Qué te mueve más en la cultura rioplatense?</label>
55
+ <div class="preference-grid">
56
+ <label class="preference-item">
57
+ <input type="radio" name="cultural-preference" value="music">
58
+ <span>Música indie (Mi Amigo Invencible, El Mató)</span>
59
+ </label>
60
+ <label class="preference-item">
61
+ <input type="radio" name="cultural-preference" value="art">
62
+ <span>Arte digital y nuevos medios</span>
63
+ </label>
64
+ <label class="preference-item">
65
+ <input type="radio" name="cultural-preference" value="literature">
66
+ <span>Literatura contemporánea</span>
67
+ </label>
68
+ <label class="preference-item">
69
+ <input type="radio" name="cultural-preference" value="mixed">
70
+ <span>Un mix de todo</span>
71
+ </label>
72
+ </div>
73
+ </div>
74
+
75
+ <div class="form-group">
76
+ <label class="form-label">Tu approach social</label>
77
+ <select class="form-control" id="social-style">
78
+ <option value="networking">Super networker - Conocés a todo el mundo</option>
79
+ <option value="selective">Selectivo - Círculo íntimo de confianza</option>
80
+ <option value="adaptable">Adaptable - Según el contexto</option>
81
+ <option value="introvert">Introvertido - Más observador que protagonista</option>
82
+ </select>
83
+ </div>
84
+
85
+ <button class="btn btn--primary btn--lg btn--full-width" id="create-character">Comenzar Aventura</button>
86
+ </div>
87
+ </div>
88
+ </div>
89
+
90
+ <!-- Game Interface -->
91
+ <div id="game-interface" class="screen">
92
+ <div class="game-layout">
93
+ <!-- Stats Panel -->
94
+ <div class="stats-panel">
95
+ <div class="player-info">
96
+ <h3 id="player-display-name">Jugador</h3>
97
+ <p id="player-role">Desarrollador</p>
98
+ </div>
99
+
100
+ <div class="stats-container">
101
+ <div class="stat-item">
102
+ <label>Carrera</label>
103
+ <div class="stat-bar">
104
+ <div class="stat-fill" id="carrera-fill"></div>
105
+ <span class="stat-value" id="carrera-value">50</span>
106
+ </div>
107
+ </div>
108
+ <div class="stat-item">
109
+ <label>Cultura</label>
110
+ <div class="stat-bar">
111
+ <div class="stat-fill" id="cultura-fill"></div>
112
+ <span class="stat-value" id="cultura-value">30</span>
113
+ </div>
114
+ </div>
115
+ <div class="stat-item">
116
+ <label>Social</label>
117
+ <div class="stat-bar">
118
+ <div class="stat-fill" id="social-fill"></div>
119
+ <span class="stat-value" id="social-value">40</span>
120
+ </div>
121
+ </div>
122
+ <div class="stat-item">
123
+ <label>Bienestar</label>
124
+ <div class="stat-bar">
125
+ <div class="stat-fill" id="bienestar-fill"></div>
126
+ <span class="stat-value" id="bienestar-value">60</span>
127
+ </div>
128
+ </div>
129
+ </div>
130
+
131
+ <div class="resonance-indicator">
132
+ <h4>Ondas de Resonancia</h4>
133
+ <div class="resonance-waves" id="resonance-display">
134
+ <div class="resonance-wave active"></div>
135
+ <div class="resonance-wave"></div>
136
+ <div class="resonance-wave"></div>
137
+ </div>
138
+ <p class="resonance-text">Tus decisiones están creando ondas...</p>
139
+ </div>
140
+ </div>
141
+
142
+ <!-- Main Game Area -->
143
+ <div class="game-content">
144
+ <div class="location-header">
145
+ <h2 id="current-location">Vicente López</h2>
146
+ <p id="location-description">Hub tech moderno con startups y coworking spaces</p>
147
+ </div>
148
+
149
+ <div class="narrative-section">
150
+ <div class="story-text" id="story-content">
151
+ <p>Llegás a tu primer día en la nueva startup en Vicente López. El coworking space está lleno de energía, pantallas con código y el aroma de café de especialidad. Tu decisión de mudarte a la zona norte ya está generando las primeras ondas de resonancia...</p>
152
+ </div>
153
+ </div>
154
+
155
+ <div class="decisions-container" id="decisions-container">
156
+ <div class="decision-prompt">
157
+ <p>Sofía, la frontend lead, se acerca durante el coffee break. Te comenta sobre un proyecto React que está armando y menciona que buscan alguien para el equipo.</p>
158
+ </div>
159
+ <div class="decisions-list">
160
+ <button class="decision-btn" data-decision="collaborate">
161
+ <span class="decision-text">"Me copa la propuesta, ¿cuándo arrancamos?"</span>
162
+ <span class="decision-impact">+Carrera +Social</span>
163
+ </button>
164
+ <button class="decision-btn" data-decision="cautious">
165
+ <span class="decision-text">"Suena interesante, ¿me contás más detalles?"</span>
166
+ <span class="decision-impact">+Bienestar</span>
167
+ </button>
168
+ <button class="decision-btn" data-decision="independent">
169
+ <span class="decision-text">"Estoy enfocado en mi proyecto actual, pero gracias"</span>
170
+ <span class="decision-impact">+Carrera -Social</span>
171
+ </button>
172
+ </div>
173
+ </div>
174
+
175
+ <div class="npc-panel">
176
+ <h4>Personas Importantes</h4>
177
+ <div class="npc-list">
178
+ <div class="npc-item" data-npc="sofia">
179
+ <div class="npc-avatar">S</div>
180
+ <div class="npc-info">
181
+ <span class="npc-name">Sofía</span>
182
+ <span class="npc-role">Frontend Developer</span>
183
+ <div class="relationship-bar">
184
+ <div class="relationship-fill" style="width: 50%"></div>
185
+ </div>
186
+ </div>
187
+ </div>
188
+ <div class="npc-item" data-npc="mateo">
189
+ <div class="npc-avatar">M</div>
190
+ <div class="npc-info">
191
+ <span class="npc-name">Mateo</span>
192
+ <span class="npc-role">Product Manager</span>
193
+ <div class="relationship-bar">
194
+ <div class="relationship-fill" style="width: 40%"></div>
195
+ </div>
196
+ </div>
197
+ </div>
198
+ <div class="npc-item" data-npc="luna">
199
+ <div class="npc-avatar">L</div>
200
+ <div class="npc-info">
201
+ <span class="npc-name">Luna</span>
202
+ <span class="npc-role">Digital Artist</span>
203
+ <div class="relationship-bar">
204
+ <div class="relationship-fill" style="width: 30%"></div>
205
+ </div>
206
+ </div>
207
+ </div>
208
+ </div>
209
+ </div>
210
+ </div>
211
+
212
+ <!-- Event Panel -->
213
+ <div class="events-panel">
214
+ <h4>Próximos Eventos</h4>
215
+ <div class="events-list" id="events-list">
216
+ <div class="event-item available">
217
+ <h5>North Valley Meetup</h5>
218
+ <p>Networking tech en Vicente López</p>
219
+ <span class="event-date">Viernes 18:30</span>
220
+ <button class="btn btn--sm btn--secondary event-join">Asistir</button>
221
+ </div>
222
+ <div class="event-item locked">
223
+ <h5>Innovation Tech Week</h5>
224
+ <p>Semana tech más importante de BA</p>
225
+ <span class="event-requirement">Requiere Carrera: 40</span>
226
+ </div>
227
+ <div class="event-item available">
228
+ <h5>Showcase Indie</h5>
229
+ <p>Show en venue de San Isidro</p>
230
+ <span class="event-date">Sábado 21:00</span>
231
+ <button class="btn btn--sm btn--secondary event-join">Asistir</button>
232
+ </div>
233
+ </div>
234
+ </div>
235
+ </div>
236
+ </div>
237
+
238
+ <!-- Resonance Visualization Modal -->
239
+ <div id="resonance-modal" class="modal">
240
+ <div class="modal-content">
241
+ <div class="modal-header">
242
+ <h3>Ondas de Resonancia</h3>
243
+ <button class="modal-close">&times;</button>
244
+ </div>
245
+ <div class="modal-body">
246
+ <div class="resonance-visualization">
247
+ <canvas id="resonance-canvas" width="400" height="300"></canvas>
248
+ </div>
249
+ <div class="resonance-explanation">
250
+ <p>Tu decisión ha generado ondas que se propagan en el tiempo. Observá cómo pueden afectar eventos futuros de maneras inesperadas.</p>
251
+ </div>
252
+ </div>
253
+ </div>
254
+ </div>
255
+
256
+ <!-- Event Result Modal -->
257
+ <div id="event-modal" class="modal">
258
+ <div class="modal-content">
259
+ <div class="modal-header">
260
+ <h3 id="event-title">Resultado del Evento</h3>
261
+ </div>
262
+ <div class="modal-body">
263
+ <div id="event-result-content">
264
+ <!-- Event results will be populated here -->
265
+ </div>
266
+ <button class="btn btn--primary" id="continue-game">Continuar</button>
267
+ </div>
268
+ </div>
269
+ </div>
270
+
271
+ <script src="app.js"></script>
272
+ </body>
273
+ </html>
style.css CHANGED
@@ -1,28 +1,1290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  }
5
 
6
  h1 {
7
- font-size: 16px;
8
- margin-top: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  }
10
 
11
  p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  }
17
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
 
 
28
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ :root {
3
+ /* Colors */
4
+ --color-background: rgba(252, 252, 249, 1);
5
+ --color-surface: rgba(255, 255, 253, 1);
6
+ --color-text: rgba(19, 52, 59, 1);
7
+ --color-text-secondary: rgba(98, 108, 113, 1);
8
+ --color-primary: rgba(33, 128, 141, 1);
9
+ --color-primary-hover: rgba(29, 116, 128, 1);
10
+ --color-primary-active: rgba(26, 104, 115, 1);
11
+ --color-secondary: rgba(94, 82, 64, 0.12);
12
+ --color-secondary-hover: rgba(94, 82, 64, 0.2);
13
+ --color-secondary-active: rgba(94, 82, 64, 0.25);
14
+ --color-border: rgba(94, 82, 64, 0.2);
15
+ --color-btn-primary-text: rgba(252, 252, 249, 1);
16
+ --color-card-border: rgba(94, 82, 64, 0.12);
17
+ --color-card-border-inner: rgba(94, 82, 64, 0.12);
18
+ --color-error: rgba(192, 21, 47, 1);
19
+ --color-success: rgba(33, 128, 141, 1);
20
+ --color-warning: rgba(168, 75, 47, 1);
21
+ --color-info: rgba(98, 108, 113, 1);
22
+ --color-focus-ring: rgba(33, 128, 141, 0.4);
23
+ --color-select-caret: rgba(19, 52, 59, 0.8);
24
+
25
+ /* Common style patterns */
26
+ --focus-ring: 0 0 0 3px var(--color-focus-ring);
27
+ --focus-outline: 2px solid var(--color-primary);
28
+ --status-bg-opacity: 0.15;
29
+ --status-border-opacity: 0.25;
30
+ --select-caret-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23134252' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
31
+ --select-caret-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23f5f5f5' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
32
+
33
+ /* RGB versions for opacity control */
34
+ --color-success-rgb: 33, 128, 141;
35
+ --color-error-rgb: 192, 21, 47;
36
+ --color-warning-rgb: 168, 75, 47;
37
+ --color-info-rgb: 98, 108, 113;
38
+
39
+ /* Typography */
40
+ --font-family-base: "FKGroteskNeue", "Geist", "Inter", -apple-system,
41
+ BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
42
+ --font-family-mono: "Berkeley Mono", ui-monospace, SFMono-Regular, Menlo,
43
+ Monaco, Consolas, monospace;
44
+ --font-size-xs: 11px;
45
+ --font-size-sm: 12px;
46
+ --font-size-base: 14px;
47
+ --font-size-md: 14px;
48
+ --font-size-lg: 16px;
49
+ --font-size-xl: 18px;
50
+ --font-size-2xl: 20px;
51
+ --font-size-3xl: 24px;
52
+ --font-size-4xl: 30px;
53
+ --font-weight-normal: 400;
54
+ --font-weight-medium: 500;
55
+ --font-weight-semibold: 550;
56
+ --font-weight-bold: 600;
57
+ --line-height-tight: 1.2;
58
+ --line-height-normal: 1.5;
59
+ --letter-spacing-tight: -0.01em;
60
+
61
+ /* Spacing */
62
+ --space-0: 0;
63
+ --space-1: 1px;
64
+ --space-2: 2px;
65
+ --space-4: 4px;
66
+ --space-6: 6px;
67
+ --space-8: 8px;
68
+ --space-10: 10px;
69
+ --space-12: 12px;
70
+ --space-16: 16px;
71
+ --space-20: 20px;
72
+ --space-24: 24px;
73
+ --space-32: 32px;
74
+
75
+ /* Border Radius */
76
+ --radius-sm: 6px;
77
+ --radius-base: 8px;
78
+ --radius-md: 10px;
79
+ --radius-lg: 12px;
80
+ --radius-full: 9999px;
81
+
82
+ /* Shadows */
83
+ --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.02);
84
+ --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.02);
85
+ --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.04),
86
+ 0 2px 4px -1px rgba(0, 0, 0, 0.02);
87
+ --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.04),
88
+ 0 4px 6px -2px rgba(0, 0, 0, 0.02);
89
+ --shadow-inset-sm: inset 0 1px 0 rgba(255, 255, 255, 0.15),
90
+ inset 0 -1px 0 rgba(0, 0, 0, 0.03);
91
+
92
+ /* Animation */
93
+ --duration-fast: 150ms;
94
+ --duration-normal: 250ms;
95
+ --ease-standard: cubic-bezier(0.16, 1, 0.3, 1);
96
+
97
+ /* Layout */
98
+ --container-sm: 640px;
99
+ --container-md: 768px;
100
+ --container-lg: 1024px;
101
+ --container-xl: 1280px;
102
+ }
103
+
104
+ /* Dark mode colors */
105
+ @media (prefers-color-scheme: dark) {
106
+ :root {
107
+ --color-background: rgba(31, 33, 33, 1);
108
+ --color-surface: rgba(38, 40, 40, 1);
109
+ --color-text: rgba(245, 245, 245, 1);
110
+ --color-text-secondary: rgba(167, 169, 169, 0.7);
111
+ --color-primary: rgba(50, 184, 198, 1);
112
+ --color-primary-hover: rgba(45, 166, 178, 1);
113
+ --color-primary-active: rgba(41, 150, 161, 1);
114
+ --color-secondary: rgba(119, 124, 124, 0.15);
115
+ --color-secondary-hover: rgba(119, 124, 124, 0.25);
116
+ --color-secondary-active: rgba(119, 124, 124, 0.3);
117
+ --color-border: rgba(119, 124, 124, 0.3);
118
+ --color-error: rgba(255, 84, 89, 1);
119
+ --color-success: rgba(50, 184, 198, 1);
120
+ --color-warning: rgba(230, 129, 97, 1);
121
+ --color-info: rgba(167, 169, 169, 1);
122
+ --color-focus-ring: rgba(50, 184, 198, 0.4);
123
+ --color-btn-primary-text: rgba(19, 52, 59, 1);
124
+ --color-card-border: rgba(119, 124, 124, 0.2);
125
+ --color-card-border-inner: rgba(119, 124, 124, 0.15);
126
+ --shadow-inset-sm: inset 0 1px 0 rgba(255, 255, 255, 0.1),
127
+ inset 0 -1px 0 rgba(0, 0, 0, 0.15);
128
+ --button-border-secondary: rgba(119, 124, 124, 0.2);
129
+ --color-border-secondary: rgba(119, 124, 124, 0.2);
130
+ --color-select-caret: rgba(245, 245, 245, 0.8);
131
+
132
+ /* Common style patterns - updated for dark mode */
133
+ --focus-ring: 0 0 0 3px var(--color-focus-ring);
134
+ --focus-outline: 2px solid var(--color-primary);
135
+ --status-bg-opacity: 0.15;
136
+ --status-border-opacity: 0.25;
137
+ --select-caret-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23134252' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
138
+ --select-caret-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23f5f5f5' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
139
+
140
+ /* RGB versions for dark mode */
141
+ --color-success-rgb: 50, 184, 198;
142
+ --color-error-rgb: 255, 84, 89;
143
+ --color-warning-rgb: 230, 129, 97;
144
+ --color-info-rgb: 167, 169, 169;
145
+ }
146
+ }
147
+
148
+ /* Data attribute for manual theme switching */
149
+ [data-color-scheme="dark"] {
150
+ --color-background: rgba(31, 33, 33, 1);
151
+ --color-surface: rgba(38, 40, 40, 1);
152
+ --color-text: rgba(245, 245, 245, 1);
153
+ --color-text-secondary: rgba(167, 169, 169, 0.7);
154
+ --color-primary: rgba(50, 184, 198, 1);
155
+ --color-primary-hover: rgba(45, 166, 178, 1);
156
+ --color-primary-active: rgba(41, 150, 161, 1);
157
+ --color-secondary: rgba(119, 124, 124, 0.15);
158
+ --color-secondary-hover: rgba(119, 124, 124, 0.25);
159
+ --color-secondary-active: rgba(119, 124, 124, 0.3);
160
+ --color-border: rgba(119, 124, 124, 0.3);
161
+ --color-error: rgba(255, 84, 89, 1);
162
+ --color-success: rgba(50, 184, 198, 1);
163
+ --color-warning: rgba(230, 129, 97, 1);
164
+ --color-info: rgba(167, 169, 169, 1);
165
+ --color-focus-ring: rgba(50, 184, 198, 0.4);
166
+ --color-btn-primary-text: rgba(19, 52, 59, 1);
167
+ --color-card-border: rgba(119, 124, 124, 0.15);
168
+ --color-card-border-inner: rgba(119, 124, 124, 0.15);
169
+ --shadow-inset-sm: inset 0 1px 0 rgba(255, 255, 255, 0.1),
170
+ inset 0 -1px 0 rgba(0, 0, 0, 0.15);
171
+ --color-border-secondary: rgba(119, 124, 124, 0.2);
172
+ --color-select-caret: rgba(245, 245, 245, 0.8);
173
+
174
+ /* Common style patterns - updated for dark mode */
175
+ --focus-ring: 0 0 0 3px var(--color-focus-ring);
176
+ --focus-outline: 2px solid var(--color-primary);
177
+ --status-bg-opacity: 0.15;
178
+ --status-border-opacity: 0.25;
179
+ --select-caret-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23134252' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
180
+ --select-caret-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23f5f5f5' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
181
+
182
+ /* RGB versions for dark mode */
183
+ --color-success-rgb: 50, 184, 198;
184
+ --color-error-rgb: 255, 84, 89;
185
+ --color-warning-rgb: 230, 129, 97;
186
+ --color-info-rgb: 167, 169, 169;
187
+ }
188
+
189
+ [data-color-scheme="light"] {
190
+ --color-background: rgba(252, 252, 249, 1);
191
+ --color-surface: rgba(255, 255, 253, 1);
192
+ --color-text: rgba(19, 52, 59, 1);
193
+ --color-text-secondary: rgba(98, 108, 113, 1);
194
+ --color-primary: rgba(33, 128, 141, 1);
195
+ --color-primary-hover: rgba(29, 116, 128, 1);
196
+ --color-primary-active: rgba(26, 104, 115, 1);
197
+ --color-secondary: rgba(94, 82, 64, 0.12);
198
+ --color-secondary-hover: rgba(94, 82, 64, 0.2);
199
+ --color-secondary-active: rgba(94, 82, 64, 0.25);
200
+ --color-border: rgba(94, 82, 64, 0.2);
201
+ --color-btn-primary-text: rgba(252, 252, 249, 1);
202
+ --color-card-border: rgba(94, 82, 64, 0.12);
203
+ --color-card-border-inner: rgba(94, 82, 64, 0.12);
204
+ --color-error: rgba(192, 21, 47, 1);
205
+ --color-success: rgba(33, 128, 141, 1);
206
+ --color-warning: rgba(168, 75, 47, 1);
207
+ --color-info: rgba(98, 108, 113, 1);
208
+ --color-focus-ring: rgba(33, 128, 141, 0.4);
209
+
210
+ /* RGB versions for light mode */
211
+ --color-success-rgb: 33, 128, 141;
212
+ --color-error-rgb: 192, 21, 47;
213
+ --color-warning-rgb: 168, 75, 47;
214
+ --color-info-rgb: 98, 108, 113;
215
+ }
216
+
217
+ /* Base styles */
218
+ html {
219
+ font-size: var(--font-size-base);
220
+ font-family: var(--font-family-base);
221
+ line-height: var(--line-height-normal);
222
+ color: var(--color-text);
223
+ background-color: var(--color-background);
224
+ -webkit-font-smoothing: antialiased;
225
+ box-sizing: border-box;
226
+ }
227
+
228
  body {
229
+ margin: 0;
230
+ padding: 0;
231
+ }
232
+
233
+ *,
234
+ *::before,
235
+ *::after {
236
+ box-sizing: inherit;
237
+ }
238
+
239
+ /* Typography */
240
+ h1,
241
+ h2,
242
+ h3,
243
+ h4,
244
+ h5,
245
+ h6 {
246
+ margin: 0;
247
+ font-weight: var(--font-weight-semibold);
248
+ line-height: var(--line-height-tight);
249
+ color: var(--color-text);
250
+ letter-spacing: var(--letter-spacing-tight);
251
  }
252
 
253
  h1 {
254
+ font-size: var(--font-size-4xl);
255
+ }
256
+ h2 {
257
+ font-size: var(--font-size-3xl);
258
+ }
259
+ h3 {
260
+ font-size: var(--font-size-2xl);
261
+ }
262
+ h4 {
263
+ font-size: var(--font-size-xl);
264
+ }
265
+ h5 {
266
+ font-size: var(--font-size-lg);
267
+ }
268
+ h6 {
269
+ font-size: var(--font-size-md);
270
  }
271
 
272
  p {
273
+ margin: 0 0 var(--space-16) 0;
274
+ }
275
+
276
+ a {
277
+ color: var(--color-primary);
278
+ text-decoration: none;
279
+ transition: color var(--duration-fast) var(--ease-standard);
280
+ }
281
+
282
+ a:hover {
283
+ color: var(--color-primary-hover);
284
+ }
285
+
286
+ code,
287
+ pre {
288
+ font-family: var(--font-family-mono);
289
+ font-size: calc(var(--font-size-base) * 0.95);
290
+ background-color: var(--color-secondary);
291
+ border-radius: var(--radius-sm);
292
+ }
293
+
294
+ code {
295
+ padding: var(--space-1) var(--space-4);
296
+ }
297
+
298
+ pre {
299
+ padding: var(--space-16);
300
+ margin: var(--space-16) 0;
301
+ overflow: auto;
302
+ border: 1px solid var(--color-border);
303
+ }
304
+
305
+ pre code {
306
+ background: none;
307
+ padding: 0;
308
+ }
309
+
310
+ /* Buttons */
311
+ .btn {
312
+ display: inline-flex;
313
+ align-items: center;
314
+ justify-content: center;
315
+ padding: var(--space-8) var(--space-16);
316
+ border-radius: var(--radius-base);
317
+ font-size: var(--font-size-base);
318
+ font-weight: 500;
319
+ line-height: 1.5;
320
+ cursor: pointer;
321
+ transition: all var(--duration-normal) var(--ease-standard);
322
+ border: none;
323
+ text-decoration: none;
324
+ position: relative;
325
+ }
326
+
327
+ .btn:focus-visible {
328
+ outline: none;
329
+ box-shadow: var(--focus-ring);
330
+ }
331
+
332
+ .btn--primary {
333
+ background: var(--color-primary);
334
+ color: var(--color-btn-primary-text);
335
+ }
336
+
337
+ .btn--primary:hover {
338
+ background: var(--color-primary-hover);
339
+ }
340
+
341
+ .btn--primary:active {
342
+ background: var(--color-primary-active);
343
+ }
344
+
345
+ .btn--secondary {
346
+ background: var(--color-secondary);
347
+ color: var(--color-text);
348
+ }
349
+
350
+ .btn--secondary:hover {
351
+ background: var(--color-secondary-hover);
352
+ }
353
+
354
+ .btn--secondary:active {
355
+ background: var(--color-secondary-active);
356
+ }
357
+
358
+ .btn--outline {
359
+ background: transparent;
360
+ border: 1px solid var(--color-border);
361
+ color: var(--color-text);
362
+ }
363
+
364
+ .btn--outline:hover {
365
+ background: var(--color-secondary);
366
+ }
367
+
368
+ .btn--sm {
369
+ padding: var(--space-4) var(--space-12);
370
+ font-size: var(--font-size-sm);
371
+ border-radius: var(--radius-sm);
372
+ }
373
+
374
+ .btn--lg {
375
+ padding: var(--space-10) var(--space-20);
376
+ font-size: var(--font-size-lg);
377
+ border-radius: var(--radius-md);
378
+ }
379
+
380
+ .btn--full-width {
381
+ width: 100%;
382
+ }
383
+
384
+ .btn:disabled {
385
+ opacity: 0.5;
386
+ cursor: not-allowed;
387
+ }
388
+
389
+ /* Form elements */
390
+ .form-control {
391
+ display: block;
392
+ width: 100%;
393
+ padding: var(--space-8) var(--space-12);
394
+ font-size: var(--font-size-md);
395
+ line-height: 1.5;
396
+ color: var(--color-text);
397
+ background-color: var(--color-surface);
398
+ border: 1px solid var(--color-border);
399
+ border-radius: var(--radius-base);
400
+ transition: border-color var(--duration-fast) var(--ease-standard),
401
+ box-shadow var(--duration-fast) var(--ease-standard);
402
+ }
403
+
404
+ textarea.form-control {
405
+ font-family: var(--font-family-base);
406
+ font-size: var(--font-size-base);
407
+ }
408
+
409
+ select.form-control {
410
+ padding: var(--space-8) var(--space-12);
411
+ -webkit-appearance: none;
412
+ -moz-appearance: none;
413
+ appearance: none;
414
+ background-image: var(--select-caret-light);
415
+ background-repeat: no-repeat;
416
+ background-position: right var(--space-12) center;
417
+ background-size: 16px;
418
+ padding-right: var(--space-32);
419
+ }
420
+
421
+ /* Add a dark mode specific caret */
422
+ @media (prefers-color-scheme: dark) {
423
+ select.form-control {
424
+ background-image: var(--select-caret-dark);
425
+ }
426
+ }
427
+
428
+ /* Also handle data-color-scheme */
429
+ [data-color-scheme="dark"] select.form-control {
430
+ background-image: var(--select-caret-dark);
431
+ }
432
+
433
+ [data-color-scheme="light"] select.form-control {
434
+ background-image: var(--select-caret-light);
435
+ }
436
+
437
+ .form-control:focus {
438
+ border-color: var(--color-primary);
439
+ outline: var(--focus-outline);
440
  }
441
 
442
+ .form-label {
443
+ display: block;
444
+ margin-bottom: var(--space-8);
445
+ font-weight: var(--font-weight-medium);
446
+ font-size: var(--font-size-sm);
447
+ }
448
+
449
+ .form-group {
450
+ margin-bottom: var(--space-16);
451
+ }
452
+
453
+ /* Card component */
454
  .card {
455
+ background-color: var(--color-surface);
456
+ border-radius: var(--radius-lg);
457
+ border: 1px solid var(--color-card-border);
458
+ box-shadow: var(--shadow-sm);
459
+ overflow: hidden;
460
+ transition: box-shadow var(--duration-normal) var(--ease-standard);
461
+ }
462
+
463
+ .card:hover {
464
+ box-shadow: var(--shadow-md);
465
+ }
466
+
467
+ .card__body {
468
+ padding: var(--space-16);
469
+ }
470
+
471
+ .card__header,
472
+ .card__footer {
473
+ padding: var(--space-16);
474
+ border-bottom: 1px solid var(--color-card-border-inner);
475
+ }
476
+
477
+ /* Status indicators - simplified with CSS variables */
478
+ .status {
479
+ display: inline-flex;
480
+ align-items: center;
481
+ padding: var(--space-6) var(--space-12);
482
+ border-radius: var(--radius-full);
483
+ font-weight: var(--font-weight-medium);
484
+ font-size: var(--font-size-sm);
485
+ }
486
+
487
+ .status--success {
488
+ background-color: rgba(
489
+ var(--color-success-rgb, 33, 128, 141),
490
+ var(--status-bg-opacity)
491
+ );
492
+ color: var(--color-success);
493
+ border: 1px solid
494
+ rgba(var(--color-success-rgb, 33, 128, 141), var(--status-border-opacity));
495
+ }
496
+
497
+ .status--error {
498
+ background-color: rgba(
499
+ var(--color-error-rgb, 192, 21, 47),
500
+ var(--status-bg-opacity)
501
+ );
502
+ color: var(--color-error);
503
+ border: 1px solid
504
+ rgba(var(--color-error-rgb, 192, 21, 47), var(--status-border-opacity));
505
+ }
506
+
507
+ .status--warning {
508
+ background-color: rgba(
509
+ var(--color-warning-rgb, 168, 75, 47),
510
+ var(--status-bg-opacity)
511
+ );
512
+ color: var(--color-warning);
513
+ border: 1px solid
514
+ rgba(var(--color-warning-rgb, 168, 75, 47), var(--status-border-opacity));
515
+ }
516
+
517
+ .status--info {
518
+ background-color: rgba(
519
+ var(--color-info-rgb, 98, 108, 113),
520
+ var(--status-bg-opacity)
521
+ );
522
+ color: var(--color-info);
523
+ border: 1px solid
524
+ rgba(var(--color-info-rgb, 98, 108, 113), var(--status-border-opacity));
525
+ }
526
+
527
+ /* Container layout */
528
+ .container {
529
+ width: 100%;
530
+ margin-right: auto;
531
+ margin-left: auto;
532
+ padding-right: var(--space-16);
533
+ padding-left: var(--space-16);
534
+ }
535
+
536
+ @media (min-width: 640px) {
537
+ .container {
538
+ max-width: var(--container-sm);
539
+ }
540
+ }
541
+ @media (min-width: 768px) {
542
+ .container {
543
+ max-width: var(--container-md);
544
+ }
545
+ }
546
+ @media (min-width: 1024px) {
547
+ .container {
548
+ max-width: var(--container-lg);
549
+ }
550
+ }
551
+ @media (min-width: 1280px) {
552
+ .container {
553
+ max-width: var(--container-xl);
554
+ }
555
+ }
556
+
557
+ /* Utility classes */
558
+ .flex {
559
+ display: flex;
560
+ }
561
+ .flex-col {
562
+ flex-direction: column;
563
+ }
564
+ .items-center {
565
+ align-items: center;
566
+ }
567
+ .justify-center {
568
+ justify-content: center;
569
+ }
570
+ .justify-between {
571
+ justify-content: space-between;
572
+ }
573
+ .gap-4 {
574
+ gap: var(--space-4);
575
+ }
576
+ .gap-8 {
577
+ gap: var(--space-8);
578
+ }
579
+ .gap-16 {
580
+ gap: var(--space-16);
581
+ }
582
+
583
+ .m-0 {
584
+ margin: 0;
585
+ }
586
+ .mt-8 {
587
+ margin-top: var(--space-8);
588
+ }
589
+ .mb-8 {
590
+ margin-bottom: var(--space-8);
591
+ }
592
+ .mx-8 {
593
+ margin-left: var(--space-8);
594
+ margin-right: var(--space-8);
595
+ }
596
+ .my-8 {
597
+ margin-top: var(--space-8);
598
+ margin-bottom: var(--space-8);
599
+ }
600
+
601
+ .p-0 {
602
+ padding: 0;
603
+ }
604
+ .py-8 {
605
+ padding-top: var(--space-8);
606
+ padding-bottom: var(--space-8);
607
+ }
608
+ .px-8 {
609
+ padding-left: var(--space-8);
610
+ padding-right: var(--space-8);
611
+ }
612
+ .py-16 {
613
+ padding-top: var(--space-16);
614
+ padding-bottom: var(--space-16);
615
+ }
616
+ .px-16 {
617
+ padding-left: var(--space-16);
618
+ padding-right: var(--space-16);
619
+ }
620
+
621
+ .block {
622
+ display: block;
623
+ }
624
+ .hidden {
625
+ display: none;
626
+ }
627
+
628
+ /* Accessibility */
629
+ .sr-only {
630
+ position: absolute;
631
+ width: 1px;
632
+ height: 1px;
633
+ padding: 0;
634
+ margin: -1px;
635
+ overflow: hidden;
636
+ clip: rect(0, 0, 0, 0);
637
+ white-space: nowrap;
638
+ border-width: 0;
639
+ }
640
+
641
+ :focus-visible {
642
+ outline: var(--focus-outline);
643
+ outline-offset: 2px;
644
+ }
645
+
646
+ /* Dark mode specifics */
647
+ [data-color-scheme="dark"] .btn--outline {
648
+ border: 1px solid var(--color-border-secondary);
649
+ }
650
+
651
+ @font-face {
652
+ font-family: 'FKGroteskNeue';
653
+ src: url('https://www.perplexity.ai/fonts/FKGroteskNeue.woff2')
654
+ format('woff2');
655
+ }
656
+
657
+ /* Game-specific styles extending the design system */
658
+
659
+ .screen {
660
+ display: none;
661
+ min-height: 100vh;
662
+ }
663
+
664
+ .screen.active {
665
+ display: block;
666
+ }
667
+
668
+ /* Landing Page Styles */
669
+ #landing-page {
670
+ background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-background) 100%);
671
+ display: flex;
672
+ align-items: center;
673
+ justify-content: center;
674
+ text-align: center;
675
+ position: relative;
676
+ overflow: hidden;
677
+ }
678
+
679
+ .intro-container {
680
+ max-width: 600px;
681
+ padding: var(--space-32);
682
+ position: relative;
683
+ z-index: 2;
684
+ }
685
+
686
+ .resonance-visual {
687
+ position: relative;
688
+ width: 200px;
689
+ height: 200px;
690
+ margin: 0 auto var(--space-32);
691
+ }
692
+
693
+ .wave {
694
+ position: absolute;
695
+ border: 2px solid var(--color-btn-primary-text);
696
+ border-radius: 50%;
697
+ opacity: 0.7;
698
+ animation: pulse 3s infinite;
699
+ }
700
+
701
+ .wave1 { width: 60px; height: 60px; top: 70px; left: 70px; animation-delay: 0s; }
702
+ .wave2 { width: 120px; height: 120px; top: 40px; left: 40px; animation-delay: 1s; }
703
+ .wave3 { width: 180px; height: 180px; top: 10px; left: 10px; animation-delay: 2s; }
704
+
705
+ @keyframes pulse {
706
+ 0% { transform: scale(0.8); opacity: 0.8; }
707
+ 50% { transform: scale(1.2); opacity: 0.4; }
708
+ 100% { transform: scale(0.8); opacity: 0.8; }
709
+ }
710
+
711
+ .game-title {
712
+ font-size: var(--font-size-4xl);
713
+ font-weight: var(--font-weight-bold);
714
+ color: var(--color-btn-primary-text);
715
+ margin-bottom: var(--space-16);
716
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
717
+ }
718
+
719
+ .game-subtitle {
720
+ font-size: var(--font-size-xl);
721
+ color: var(--color-btn-primary-text);
722
+ margin-bottom: var(--space-24);
723
+ opacity: 0.9;
724
+ }
725
+
726
+ .intro-text {
727
+ color: var(--color-btn-primary-text);
728
+ margin-bottom: var(--space-32);
729
+ opacity: 0.8;
730
+ line-height: 1.6;
731
  }
732
 
733
+ /* Character Creation Styles */
734
+ #character-creation {
735
+ background: var(--color-background);
736
+ padding: var(--space-32) 0;
737
  }
738
+
739
+ #character-creation .container {
740
+ max-width: 600px;
741
+ text-align: center;
742
+ }
743
+
744
+ .creation-form {
745
+ text-align: left;
746
+ margin-top: var(--space-32);
747
+ }
748
+
749
+ .preference-grid {
750
+ display: grid;
751
+ grid-template-columns: 1fr 1fr;
752
+ gap: var(--space-12);
753
+ margin-top: var(--space-8);
754
+ }
755
+
756
+ .preference-item {
757
+ display: flex;
758
+ align-items: center;
759
+ padding: var(--space-12);
760
+ border: 1px solid var(--color-border);
761
+ border-radius: var(--radius-base);
762
+ cursor: pointer;
763
+ transition: all var(--duration-fast) var(--ease-standard);
764
+ }
765
+
766
+ .preference-item:hover {
767
+ background: var(--color-secondary);
768
+ border-color: var(--color-primary);
769
+ }
770
+
771
+ .preference-item input[type="radio"] {
772
+ margin-right: var(--space-8);
773
+ }
774
+
775
+ .preference-item input[type="radio"]:checked + span {
776
+ color: var(--color-primary);
777
+ font-weight: var(--font-weight-medium);
778
+ }
779
+
780
+ /* Game Interface Layout */
781
+ .game-layout {
782
+ display: grid;
783
+ grid-template-columns: 280px 1fr 280px;
784
+ gap: var(--space-16);
785
+ min-height: 100vh;
786
+ background: var(--color-background);
787
+ }
788
+
789
+ @media (max-width: 1024px) {
790
+ .game-layout {
791
+ grid-template-columns: 1fr;
792
+ grid-template-rows: auto 1fr auto;
793
+ }
794
+ }
795
+
796
+ /* Stats Panel */
797
+ .stats-panel {
798
+ background: var(--color-surface);
799
+ border-right: 1px solid var(--color-border);
800
+ padding: var(--space-20);
801
+ overflow-y: auto;
802
+ }
803
+
804
+ .player-info {
805
+ text-align: center;
806
+ margin-bottom: var(--space-24);
807
+ padding-bottom: var(--space-16);
808
+ border-bottom: 1px solid var(--color-border);
809
+ }
810
+
811
+ .player-info h3 {
812
+ margin-bottom: var(--space-4);
813
+ }
814
+
815
+ .player-info p {
816
+ color: var(--color-text-secondary);
817
+ margin: 0;
818
+ }
819
+
820
+ .stats-container {
821
+ margin-bottom: var(--space-24);
822
+ }
823
+
824
+ .stat-item {
825
+ margin-bottom: var(--space-16);
826
+ }
827
+
828
+ .stat-item label {
829
+ display: block;
830
+ font-weight: var(--font-weight-medium);
831
+ margin-bottom: var(--space-4);
832
+ font-size: var(--font-size-sm);
833
+ }
834
+
835
+ .stat-bar {
836
+ position: relative;
837
+ height: 24px;
838
+ background: var(--color-secondary);
839
+ border-radius: var(--radius-full);
840
+ overflow: hidden;
841
+ }
842
+
843
+ .stat-fill {
844
+ height: 100%;
845
+ background: linear-gradient(90deg, var(--color-primary), var(--color-primary-hover));
846
+ border-radius: var(--radius-full);
847
+ transition: width var(--duration-normal) var(--ease-standard);
848
+ width: 50%;
849
+ }
850
+
851
+ .stat-value {
852
+ position: absolute;
853
+ right: var(--space-8);
854
+ top: 50%;
855
+ transform: translateY(-50%);
856
+ font-size: var(--font-size-xs);
857
+ font-weight: var(--font-weight-medium);
858
+ color: var(--color-text);
859
+ }
860
+
861
+ .resonance-indicator {
862
+ background: var(--color-secondary);
863
+ padding: var(--space-16);
864
+ border-radius: var(--radius-base);
865
+ }
866
+
867
+ .resonance-indicator h4 {
868
+ margin-bottom: var(--space-12);
869
+ font-size: var(--font-size-sm);
870
+ }
871
+
872
+ .resonance-waves {
873
+ display: flex;
874
+ gap: var(--space-4);
875
+ margin-bottom: var(--space-8);
876
+ }
877
+
878
+ .resonance-wave {
879
+ width: 12px;
880
+ height: 12px;
881
+ border-radius: 50%;
882
+ background: var(--color-border);
883
+ transition: background var(--duration-fast) var(--ease-standard);
884
+ }
885
+
886
+ .resonance-wave.active {
887
+ background: var(--color-primary);
888
+ box-shadow: 0 0 8px var(--color-primary);
889
+ }
890
+
891
+ .resonance-text {
892
+ font-size: var(--font-size-xs);
893
+ color: var(--color-text-secondary);
894
+ margin: 0;
895
+ }
896
+
897
+ /* Game Content */
898
+ .game-content {
899
+ padding: var(--space-20);
900
+ overflow-y: auto;
901
+ }
902
+
903
+ .location-header {
904
+ margin-bottom: var(--space-24);
905
+ text-align: center;
906
+ }
907
+
908
+ .location-header h2 {
909
+ color: var(--color-primary);
910
+ margin-bottom: var(--space-8);
911
+ }
912
+
913
+ .location-header p {
914
+ color: var(--color-text-secondary);
915
+ margin: 0;
916
+ }
917
+
918
+ .narrative-section {
919
+ background: var(--color-surface);
920
+ padding: var(--space-20);
921
+ border-radius: var(--radius-lg);
922
+ margin-bottom: var(--space-24);
923
+ border: 1px solid var(--color-card-border);
924
+ }
925
+
926
+ .story-text {
927
+ line-height: 1.6;
928
+ }
929
+
930
+ .decisions-container {
931
+ margin-bottom: var(--space-24);
932
+ }
933
+
934
+ .decision-prompt {
935
+ background: var(--color-secondary);
936
+ padding: var(--space-16);
937
+ border-radius: var(--radius-base);
938
+ margin-bottom: var(--space-16);
939
+ font-style: italic;
940
+ }
941
+
942
+ .decision-prompt p {
943
+ margin: 0;
944
+ }
945
+
946
+ .decisions-list {
947
+ display: flex;
948
+ flex-direction: column;
949
+ gap: var(--space-12);
950
+ }
951
+
952
+ .decision-btn {
953
+ display: flex;
954
+ justify-content: space-between;
955
+ align-items: center;
956
+ padding: var(--space-16);
957
+ background: var(--color-surface);
958
+ border: 2px solid var(--color-border);
959
+ border-radius: var(--radius-base);
960
+ cursor: pointer;
961
+ transition: all var(--duration-fast) var(--ease-standard);
962
+ text-align: left;
963
+ }
964
+
965
+ .decision-btn:hover {
966
+ border-color: var(--color-primary);
967
+ background: var(--color-secondary);
968
+ transform: translateY(-1px);
969
+ }
970
+
971
+ .decision-btn:active {
972
+ transform: translateY(0);
973
+ }
974
+
975
+ .decision-text {
976
+ flex: 1;
977
+ font-weight: var(--font-weight-medium);
978
+ color: var(--color-text);
979
+ }
980
+
981
+ .decision-impact {
982
+ font-size: var(--font-size-xs);
983
+ color: var(--color-primary);
984
+ font-weight: var(--font-weight-medium);
985
+ background: var(--color-secondary);
986
+ padding: var(--space-4) var(--space-8);
987
+ border-radius: var(--radius-sm);
988
+ }
989
+
990
+ .npc-panel {
991
+ background: var(--color-surface);
992
+ padding: var(--space-20);
993
+ border-radius: var(--radius-lg);
994
+ border: 1px solid var(--color-card-border);
995
+ }
996
+
997
+ .npc-panel h4 {
998
+ margin-bottom: var(--space-16);
999
+ text-align: center;
1000
+ }
1001
+
1002
+ .npc-list {
1003
+ display: flex;
1004
+ flex-direction: column;
1005
+ gap: var(--space-12);
1006
+ }
1007
+
1008
+ .npc-item {
1009
+ display: flex;
1010
+ align-items: center;
1011
+ gap: var(--space-12);
1012
+ padding: var(--space-12);
1013
+ background: var(--color-secondary);
1014
+ border-radius: var(--radius-base);
1015
+ cursor: pointer;
1016
+ transition: background var(--duration-fast) var(--ease-standard);
1017
+ }
1018
+
1019
+ .npc-item:hover {
1020
+ background: var(--color-secondary-hover);
1021
+ }
1022
+
1023
+ .npc-avatar {
1024
+ width: 40px;
1025
+ height: 40px;
1026
+ border-radius: 50%;
1027
+ background: var(--color-primary);
1028
+ color: var(--color-btn-primary-text);
1029
+ display: flex;
1030
+ align-items: center;
1031
+ justify-content: center;
1032
+ font-weight: var(--font-weight-bold);
1033
+ }
1034
+
1035
+ .npc-info {
1036
+ flex: 1;
1037
+ }
1038
+
1039
+ .npc-name {
1040
+ display: block;
1041
+ font-weight: var(--font-weight-medium);
1042
+ margin-bottom: var(--space-2);
1043
+ }
1044
+
1045
+ .npc-role {
1046
+ display: block;
1047
+ font-size: var(--font-size-xs);
1048
+ color: var(--color-text-secondary);
1049
+ margin-bottom: var(--space-4);
1050
+ }
1051
+
1052
+ .relationship-bar {
1053
+ height: 4px;
1054
+ background: var(--color-border);
1055
+ border-radius: var(--radius-full);
1056
+ overflow: hidden;
1057
+ }
1058
+
1059
+ .relationship-fill {
1060
+ height: 100%;
1061
+ background: var(--color-primary);
1062
+ transition: width var(--duration-normal) var(--ease-standard);
1063
+ }
1064
+
1065
+ /* Events Panel */
1066
+ .events-panel {
1067
+ background: var(--color-surface);
1068
+ border-left: 1px solid var(--color-border);
1069
+ padding: var(--space-20);
1070
+ overflow-y: auto;
1071
+ }
1072
+
1073
+ .events-panel h4 {
1074
+ text-align: center;
1075
+ margin-bottom: var(--space-16);
1076
+ }
1077
+
1078
+ .events-list {
1079
+ display: flex;
1080
+ flex-direction: column;
1081
+ gap: var(--space-16);
1082
+ }
1083
+
1084
+ .event-item {
1085
+ padding: var(--space-16);
1086
+ border-radius: var(--radius-base);
1087
+ border: 1px solid var(--color-border);
1088
+ transition: all var(--duration-fast) var(--ease-standard);
1089
+ }
1090
+
1091
+ .event-item.available {
1092
+ background: var(--color-secondary);
1093
+ border-color: var(--color-primary);
1094
+ }
1095
+
1096
+ .event-item.locked {
1097
+ background: var(--color-border);
1098
+ opacity: 0.6;
1099
+ }
1100
+
1101
+ .event-item h5 {
1102
+ margin-bottom: var(--space-8);
1103
+ color: var(--color-primary);
1104
+ }
1105
+
1106
+ .event-item p {
1107
+ font-size: var(--font-size-sm);
1108
+ color: var(--color-text-secondary);
1109
+ margin: 0 0 var(--space-8) 0;
1110
+ }
1111
+
1112
+ .event-date {
1113
+ font-size: var(--font-size-xs);
1114
+ color: var(--color-primary);
1115
+ font-weight: var(--font-weight-medium);
1116
+ }
1117
+
1118
+ .event-requirement {
1119
+ font-size: var(--font-size-xs);
1120
+ color: var(--color-warning);
1121
+ font-weight: var(--font-weight-medium);
1122
+ }
1123
+
1124
+ .event-join {
1125
+ margin-top: var(--space-8);
1126
+ width: 100%;
1127
+ }
1128
+
1129
+ /* Modal Styles */
1130
+ .modal {
1131
+ display: none;
1132
+ position: fixed;
1133
+ z-index: 1000;
1134
+ left: 0;
1135
+ top: 0;
1136
+ width: 100%;
1137
+ height: 100%;
1138
+ background: rgba(0, 0, 0, 0.5);
1139
+ backdrop-filter: blur(4px);
1140
+ }
1141
+
1142
+ .modal.active {
1143
+ display: flex;
1144
+ align-items: center;
1145
+ justify-content: center;
1146
+ }
1147
+
1148
+ .modal-content {
1149
+ background: var(--color-surface);
1150
+ border-radius: var(--radius-lg);
1151
+ max-width: 600px;
1152
+ width: 90%;
1153
+ max-height: 80vh;
1154
+ overflow-y: auto;
1155
+ box-shadow: var(--shadow-lg);
1156
+ border: 1px solid var(--color-card-border);
1157
+ }
1158
+
1159
+ .modal-header {
1160
+ display: flex;
1161
+ justify-content: space-between;
1162
+ align-items: center;
1163
+ padding: var(--space-20);
1164
+ border-bottom: 1px solid var(--color-border);
1165
+ }
1166
+
1167
+ .modal-header h3 {
1168
+ margin: 0;
1169
+ }
1170
+
1171
+ .modal-close {
1172
+ background: none;
1173
+ border: none;
1174
+ font-size: var(--font-size-3xl);
1175
+ cursor: pointer;
1176
+ color: var(--color-text-secondary);
1177
+ transition: color var(--duration-fast) var(--ease-standard);
1178
+ }
1179
+
1180
+ .modal-close:hover {
1181
+ color: var(--color-text);
1182
+ }
1183
+
1184
+ .modal-body {
1185
+ padding: var(--space-20);
1186
+ }
1187
+
1188
+ .resonance-visualization {
1189
+ text-align: center;
1190
+ margin-bottom: var(--space-16);
1191
+ }
1192
+
1193
+ .resonance-explanation {
1194
+ text-align: center;
1195
+ color: var(--color-text-secondary);
1196
+ }
1197
+
1198
+ #resonance-canvas {
1199
+ border: 1px solid var(--color-border);
1200
+ border-radius: var(--radius-base);
1201
+ background: var(--color-background);
1202
+ }
1203
+
1204
+ /* Responsive Design */
1205
+ @media (max-width: 768px) {
1206
+ .game-layout {
1207
+ grid-template-columns: 1fr;
1208
+ gap: 0;
1209
+ }
1210
+
1211
+ .stats-panel,
1212
+ .events-panel {
1213
+ border: none;
1214
+ border-bottom: 1px solid var(--color-border);
1215
+ padding: var(--space-16);
1216
+ }
1217
+
1218
+ .game-content {
1219
+ padding: var(--space-16);
1220
+ }
1221
+
1222
+ .preference-grid {
1223
+ grid-template-columns: 1fr;
1224
+ }
1225
+
1226
+ .decisions-list {
1227
+ gap: var(--space-8);
1228
+ }
1229
+
1230
+ .decision-btn {
1231
+ flex-direction: column;
1232
+ align-items: stretch;
1233
+ gap: var(--space-8);
1234
+ }
1235
+
1236
+ .decision-impact {
1237
+ text-align: center;
1238
+ }
1239
+ }
1240
+
1241
+ /* Loading animations */
1242
+ @keyframes fadeIn {
1243
+ from { opacity: 0; transform: translateY(20px); }
1244
+ to { opacity: 1; transform: translateY(0); }
1245
+ }
1246
+
1247
+ .animate-in {
1248
+ animation: fadeIn var(--duration-normal) var(--ease-standard);
1249
+ }
1250
+
1251
+ /* Success/Error states */
1252
+ .stat-fill.increase {
1253
+ animation: statIncrease 0.5s ease-out;
1254
+ }
1255
+
1256
+ .stat-fill.decrease {
1257
+ animation: statDecrease 0.5s ease-out;
1258
+ }
1259
+
1260
+ @keyframes statIncrease {
1261
+ 0% { background: var(--color-success); }
1262
+ 100% { background: linear-gradient(90deg, var(--color-primary), var(--color-primary-hover)); }
1263
+ }
1264
+
1265
+ @keyframes statDecrease {
1266
+ 0% { background: var(--color-error); }
1267
+ 100% { background: linear-gradient(90deg, var(--color-primary), var(--color-primary-hover)); }
1268
+ }
1269
+
1270
+ /* Hover effects for interactivity */
1271
+ .npc-item:hover .npc-avatar {
1272
+ transform: scale(1.1);
1273
+ transition: transform var(--duration-fast) var(--ease-standard);
1274
+ }
1275
+
1276
+ .event-item.available:hover {
1277
+ transform: translateY(-2px);
1278
+ box-shadow: var(--shadow-md);
1279
+ }
1280
+
1281
+ /* Focus states for accessibility */
1282
+ .decision-btn:focus-visible {
1283
+ outline: var(--focus-outline);
1284
+ outline-offset: 2px;
1285
+ }
1286
+
1287
+ .event-join:focus-visible {
1288
+ outline: var(--focus-outline);
1289
+ outline-offset: 2px;
1290
+ }