feat: risks and sticky notes
@ -7,6 +7,7 @@
 | 
				
			|||||||
		x?: number;
 | 
							x?: number;
 | 
				
			||||||
		y?: number;
 | 
							y?: number;
 | 
				
			||||||
		zIndex?: number;
 | 
							zIndex?: number;
 | 
				
			||||||
 | 
							canvasOffset?: { x: number; y: number };
 | 
				
			||||||
		onMove?: (id: number, x: number, y: number) => void;
 | 
							onMove?: (id: number, x: number, y: number) => void;
 | 
				
			||||||
		onClick?: (id: number) => void;
 | 
							onClick?: (id: number) => void;
 | 
				
			||||||
		onDrop?: (id: number, x: number, y: number) => void;
 | 
							onDrop?: (id: number, x: number, y: number) => void;
 | 
				
			||||||
@ -20,6 +21,7 @@
 | 
				
			|||||||
		x = $bindable(0),
 | 
							x = $bindable(0),
 | 
				
			||||||
		y = $bindable(0),
 | 
							y = $bindable(0),
 | 
				
			||||||
		zIndex = $bindable(0),
 | 
							zIndex = $bindable(0),
 | 
				
			||||||
 | 
							canvasOffset = { x: 0, y: 0 },
 | 
				
			||||||
		onMove,
 | 
							onMove,
 | 
				
			||||||
		onClick,
 | 
							onClick,
 | 
				
			||||||
		onDrop
 | 
							onDrop
 | 
				
			||||||
@ -59,8 +61,8 @@
 | 
				
			|||||||
			hasMoved = true;
 | 
								hasMoved = true;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		const newX = event.clientX - dragOffset.x;
 | 
							const newX = event.clientX - dragOffset.x - canvasOffset.x;
 | 
				
			||||||
		const newY = event.clientY - dragOffset.y;
 | 
							const newY = event.clientY - dragOffset.y - canvasOffset.y;
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		x = newX;
 | 
							x = newX;
 | 
				
			||||||
		y = newY;
 | 
							y = newY;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										223
									
								
								src/lib/StickyNote.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,223 @@
 | 
				
			|||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
						interface Props {
 | 
				
			||||||
 | 
							id: number;
 | 
				
			||||||
 | 
							text: string;
 | 
				
			||||||
 | 
							x: number;
 | 
				
			||||||
 | 
							y: number;
 | 
				
			||||||
 | 
							zIndex: number;
 | 
				
			||||||
 | 
							color: string;
 | 
				
			||||||
 | 
							canvasOffset?: { x: number; y: number };
 | 
				
			||||||
 | 
							onMove?: (id: number, x: number, y: number) => void;
 | 
				
			||||||
 | 
							onUpdate?: (id: number, text: string) => void;
 | 
				
			||||||
 | 
							onDelete?: (id: number) => void;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let {
 | 
				
			||||||
 | 
							id,
 | 
				
			||||||
 | 
							text = $bindable(''),
 | 
				
			||||||
 | 
							x = $bindable(0),
 | 
				
			||||||
 | 
							y = $bindable(0),
 | 
				
			||||||
 | 
							zIndex = $bindable(0),
 | 
				
			||||||
 | 
							color,
 | 
				
			||||||
 | 
							canvasOffset = { x: 0, y: 0 },
 | 
				
			||||||
 | 
							onMove,
 | 
				
			||||||
 | 
							onUpdate,
 | 
				
			||||||
 | 
							onDelete
 | 
				
			||||||
 | 
						}: Props = $props();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let isDragging = $state(false);
 | 
				
			||||||
 | 
						let dragOffset = $state({ x: 0, y: 0 });
 | 
				
			||||||
 | 
						let dragStartPos = $state({ x: 0, y: 0 });
 | 
				
			||||||
 | 
						let hasMoved = $state(false);
 | 
				
			||||||
 | 
						let noteElement: HTMLDivElement;
 | 
				
			||||||
 | 
						let textareaElement: HTMLTextAreaElement;
 | 
				
			||||||
 | 
						let isEditing = $state(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function handleMouseDown(event: MouseEvent) {
 | 
				
			||||||
 | 
							if (event.button !== 0) return;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							isDragging = true;
 | 
				
			||||||
 | 
							hasMoved = false;
 | 
				
			||||||
 | 
							const rect = noteElement.getBoundingClientRect();
 | 
				
			||||||
 | 
							dragOffset = {
 | 
				
			||||||
 | 
								x: event.clientX - rect.left,
 | 
				
			||||||
 | 
								y: event.clientY - rect.top
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							dragStartPos = {
 | 
				
			||||||
 | 
								x: event.clientX,
 | 
				
			||||||
 | 
								y: event.clientY
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							event.preventDefault();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function handleMouseMove(event: MouseEvent) {
 | 
				
			||||||
 | 
							if (!isDragging) return;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							const deltaX = Math.abs(event.clientX - dragStartPos.x);
 | 
				
			||||||
 | 
							const deltaY = Math.abs(event.clientY - dragStartPos.y);
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							if (deltaX > 5 || deltaY > 5) {
 | 
				
			||||||
 | 
								hasMoved = true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							const newX = event.clientX - dragOffset.x - canvasOffset.x;
 | 
				
			||||||
 | 
							const newY = event.clientY - dragOffset.y - canvasOffset.y;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							x = newX;
 | 
				
			||||||
 | 
							y = newY;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							onMove?.(id, newX, newY);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function handleMouseUp() {
 | 
				
			||||||
 | 
							isDragging = false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function handleClick() {
 | 
				
			||||||
 | 
							if (!hasMoved) {
 | 
				
			||||||
 | 
								isEditing = true;
 | 
				
			||||||
 | 
								setTimeout(() => textareaElement?.focus(), 0);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function handleTextBlur() {
 | 
				
			||||||
 | 
							isEditing = false;
 | 
				
			||||||
 | 
							onUpdate?.(id, text);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function handleKeyDown(event: KeyboardEvent) {
 | 
				
			||||||
 | 
							if (event.key === 'Enter' && !event.shiftKey) {
 | 
				
			||||||
 | 
								event.preventDefault();
 | 
				
			||||||
 | 
								isEditing = false;
 | 
				
			||||||
 | 
								onUpdate?.(id, text);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (event.key === 'Escape') {
 | 
				
			||||||
 | 
								isEditing = false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function handleDelete(event: MouseEvent) {
 | 
				
			||||||
 | 
							event.stopPropagation();
 | 
				
			||||||
 | 
							onDelete?.(id);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						$effect(() => {
 | 
				
			||||||
 | 
							if (isDragging) {
 | 
				
			||||||
 | 
								document.addEventListener('mousemove', handleMouseMove);
 | 
				
			||||||
 | 
								document.addEventListener('mouseup', handleMouseUp);
 | 
				
			||||||
 | 
								return () => {
 | 
				
			||||||
 | 
									document.removeEventListener('mousemove', handleMouseMove);
 | 
				
			||||||
 | 
									document.removeEventListener('mouseup', handleMouseUp);
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div
 | 
				
			||||||
 | 
						bind:this={noteElement}
 | 
				
			||||||
 | 
						class="sticky-note"
 | 
				
			||||||
 | 
						class:dragging={isDragging}
 | 
				
			||||||
 | 
						class:editing={isEditing}
 | 
				
			||||||
 | 
						style="left: {x}px; top: {y}px; z-index: {zIndex}; background-color: {color};"
 | 
				
			||||||
 | 
						onmousedown={handleMouseDown}
 | 
				
			||||||
 | 
						onclick={handleClick}
 | 
				
			||||||
 | 
						role="button"
 | 
				
			||||||
 | 
						tabindex="0"
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
						<button class="delete-btn" onclick={handleDelete}>×</button>
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						{#if isEditing}
 | 
				
			||||||
 | 
							<textarea
 | 
				
			||||||
 | 
								bind:this={textareaElement}
 | 
				
			||||||
 | 
								bind:value={text}
 | 
				
			||||||
 | 
								onblur={handleTextBlur}
 | 
				
			||||||
 | 
								onkeydown={handleKeyDown}
 | 
				
			||||||
 | 
								class="note-textarea"
 | 
				
			||||||
 | 
								placeholder="Enter your note..."
 | 
				
			||||||
 | 
							></textarea>
 | 
				
			||||||
 | 
						{:else}
 | 
				
			||||||
 | 
							<div class="note-text">{text || 'Click to edit'}</div>
 | 
				
			||||||
 | 
						{/if}
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style>
 | 
				
			||||||
 | 
						.sticky-note {
 | 
				
			||||||
 | 
							position: absolute;
 | 
				
			||||||
 | 
							width: 200px;
 | 
				
			||||||
 | 
							min-height: 120px;
 | 
				
			||||||
 | 
							padding: 15px;
 | 
				
			||||||
 | 
							border-radius: 8px;
 | 
				
			||||||
 | 
							cursor: pointer;
 | 
				
			||||||
 | 
							user-select: none;
 | 
				
			||||||
 | 
							box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
 | 
				
			||||||
 | 
							font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
 | 
				
			||||||
 | 
							font-size: 14px;
 | 
				
			||||||
 | 
							line-height: 1.4;
 | 
				
			||||||
 | 
							transition: transform 0.1s ease;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.sticky-note:hover {
 | 
				
			||||||
 | 
							transform: translateY(-2px);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.sticky-note.dragging {
 | 
				
			||||||
 | 
							cursor: grabbing;
 | 
				
			||||||
 | 
							z-index: 1000 !important;
 | 
				
			||||||
 | 
							transform: rotate(2deg);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.sticky-note.dragging:hover {
 | 
				
			||||||
 | 
							transform: rotate(2deg);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.delete-btn {
 | 
				
			||||||
 | 
							position: absolute;
 | 
				
			||||||
 | 
							top: 5px;
 | 
				
			||||||
 | 
							right: 5px;
 | 
				
			||||||
 | 
							width: 20px;
 | 
				
			||||||
 | 
							height: 20px;
 | 
				
			||||||
 | 
							border: none;
 | 
				
			||||||
 | 
							background: rgba(0, 0, 0, 0.1);
 | 
				
			||||||
 | 
							border-radius: 50%;
 | 
				
			||||||
 | 
							cursor: pointer;
 | 
				
			||||||
 | 
							font-size: 14px;
 | 
				
			||||||
 | 
							font-weight: bold;
 | 
				
			||||||
 | 
							display: flex;
 | 
				
			||||||
 | 
							align-items: center;
 | 
				
			||||||
 | 
							justify-content: center;
 | 
				
			||||||
 | 
							color: rgba(0, 0, 0, 0.6);
 | 
				
			||||||
 | 
							transition: all 0.2s ease;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.delete-btn:hover {
 | 
				
			||||||
 | 
							background: rgba(255, 0, 0, 0.2);
 | 
				
			||||||
 | 
							color: #ff0000;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.note-textarea {
 | 
				
			||||||
 | 
							width: 100%;
 | 
				
			||||||
 | 
							min-height: 80px;
 | 
				
			||||||
 | 
							border: none;
 | 
				
			||||||
 | 
							background: transparent;
 | 
				
			||||||
 | 
							resize: none;
 | 
				
			||||||
 | 
							font-family: inherit;
 | 
				
			||||||
 | 
							font-size: inherit;
 | 
				
			||||||
 | 
							line-height: inherit;
 | 
				
			||||||
 | 
							outline: none;
 | 
				
			||||||
 | 
							padding: 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.note-text {
 | 
				
			||||||
 | 
							min-height: 80px;
 | 
				
			||||||
 | 
							word-wrap: break-word;
 | 
				
			||||||
 | 
							white-space: pre-wrap;
 | 
				
			||||||
 | 
							color: rgba(0, 0, 0, 0.8);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.note-text:empty::before {
 | 
				
			||||||
 | 
							content: 'Click to edit';
 | 
				
			||||||
 | 
							color: rgba(0, 0, 0, 0.4);
 | 
				
			||||||
 | 
							font-style: italic;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
@ -1,5 +1,7 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
	import Card from '$lib/Card.svelte';
 | 
						import Card from '$lib/Card.svelte';
 | 
				
			||||||
 | 
						import StickyNote from '$lib/StickyNote.svelte';
 | 
				
			||||||
 | 
						import { onMount } from 'svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	interface CardData {
 | 
						interface CardData {
 | 
				
			||||||
		id: number;
 | 
							id: number;
 | 
				
			||||||
@ -9,25 +11,128 @@
 | 
				
			|||||||
		y: number;
 | 
							y: number;
 | 
				
			||||||
		zIndex: number;
 | 
							zIndex: number;
 | 
				
			||||||
		inDeck: boolean;
 | 
							inDeck: boolean;
 | 
				
			||||||
 | 
							isRiskCategory?: boolean;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const cardImages = Array.from({ length: 14 }, (_, i) => `front${i + 1}.png`);
 | 
						interface DeckConfig {
 | 
				
			||||||
	const backImage = 'back.png';
 | 
							name: string;
 | 
				
			||||||
 | 
							backImage: string;
 | 
				
			||||||
 | 
							cards: string[];
 | 
				
			||||||
 | 
							riskCategories?: string[];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						interface StickyNote {
 | 
				
			||||||
 | 
							id: number;
 | 
				
			||||||
 | 
							text: string;
 | 
				
			||||||
 | 
							x: number;
 | 
				
			||||||
 | 
							y: number;
 | 
				
			||||||
 | 
							zIndex: number;
 | 
				
			||||||
 | 
							color: string;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const deckConfigs: DeckConfig[] = [
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: 'Default',
 | 
				
			||||||
 | 
								backImage: 'back.png',
 | 
				
			||||||
 | 
								cards: Array.from({ length: 14 }, (_, i) => `front${i + 1}.png`)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: 'Risk Cards',
 | 
				
			||||||
 | 
								backImage: 'risks/risiko_back.png',
 | 
				
			||||||
 | 
								cards: [
 | 
				
			||||||
 | 
									'risks/altsysteme_und_altlasten1.png',
 | 
				
			||||||
 | 
									'risks/altsysteme_und_altlasten2.png',
 | 
				
			||||||
 | 
									'risks/altsysteme_und_altlasten3.png',
 | 
				
			||||||
 | 
									'risks/altsysteme_und_altlasten4.png',
 | 
				
			||||||
 | 
									'risks/altsysteme_und_altlasten5.png',
 | 
				
			||||||
 | 
									'risks/betrieb_und_deployment1.png',
 | 
				
			||||||
 | 
									'risks/betrieb_und_deployment2.png',
 | 
				
			||||||
 | 
									'risks/betrieb_und_deployment3.png',
 | 
				
			||||||
 | 
									'risks/betrieb_und_deployment4.png',
 | 
				
			||||||
 | 
									'risks/betrieb_und_deployment5.png',
 | 
				
			||||||
 | 
									'risks/fremdsysteme_und_plattformen1.png',
 | 
				
			||||||
 | 
									'risks/fremdsysteme_und_plattformen2.png',
 | 
				
			||||||
 | 
									'risks/fremdsysteme_und_plattformen3.png',
 | 
				
			||||||
 | 
									'risks/fremdsysteme_und_plattformen4.png',
 | 
				
			||||||
 | 
									'risks/fremdsysteme_und_plattformen5.png',
 | 
				
			||||||
 | 
									'risks/kompetenz_und_erfahrung1.png',
 | 
				
			||||||
 | 
									'risks/kompetenz_und_erfahrung2.png',
 | 
				
			||||||
 | 
									'risks/kompetenz_und_erfahrung3.png',
 | 
				
			||||||
 | 
									'risks/kompetenz_und_erfahrung4.png',
 | 
				
			||||||
 | 
									'risks/kompetenz_und_erfahrung5.png',
 | 
				
			||||||
 | 
									'risks/orga_und_prozesse1.png',
 | 
				
			||||||
 | 
									'risks/orga_und_prozesse2.png',
 | 
				
			||||||
 | 
									'risks/orga_und_prozesse3.png',
 | 
				
			||||||
 | 
									'risks/orga_und_prozesse4.png',
 | 
				
			||||||
 | 
									'risks/orga_und_prozesse5.png',
 | 
				
			||||||
 | 
									'risks/softwareloesung1.png',
 | 
				
			||||||
 | 
									'risks/softwareloesung2.png',
 | 
				
			||||||
 | 
									'risks/softwareloesung3.png',
 | 
				
			||||||
 | 
									'risks/softwareloesung4.png',
 | 
				
			||||||
 | 
									'risks/softwareloesung5.png',
 | 
				
			||||||
 | 
									'risks/weiche_faktoren1.png',
 | 
				
			||||||
 | 
									'risks/weiche_faktoren2.png',
 | 
				
			||||||
 | 
									'risks/weiche_faktoren3.png',
 | 
				
			||||||
 | 
									'risks/weiche_faktoren4.png',
 | 
				
			||||||
 | 
									'risks/weiche_faktoren5.png',
 | 
				
			||||||
 | 
									'risks/zielsetzung1.png',
 | 
				
			||||||
 | 
									'risks/zielsetzung2.png',
 | 
				
			||||||
 | 
									'risks/zielsetzung3.png',
 | 
				
			||||||
 | 
									'risks/zielsetzung4.png',
 | 
				
			||||||
 | 
									'risks/zielsetzung5.png'
 | 
				
			||||||
 | 
								],
 | 
				
			||||||
 | 
								riskCategories: [
 | 
				
			||||||
 | 
									'altsysteme_und_altlasten',
 | 
				
			||||||
 | 
									'betrieb_und_deployment',
 | 
				
			||||||
 | 
									'fremdsysteme_und_plattformen',
 | 
				
			||||||
 | 
									'kompetenz_und_erfahrung',
 | 
				
			||||||
 | 
									'orga_und_prozesse',
 | 
				
			||||||
 | 
									'softwareloesung',
 | 
				
			||||||
 | 
									'weiche_faktoren',
 | 
				
			||||||
 | 
									'zielsetzung'
 | 
				
			||||||
 | 
								]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let selectedDeck = $state(0);
 | 
				
			||||||
 | 
						let shuffleMode = $state('normal');
 | 
				
			||||||
 | 
						let currentDeckConfig = $derived(deckConfigs[selectedDeck]);
 | 
				
			||||||
 | 
						let backImage = $derived(currentDeckConfig.backImage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let deckPosition = $state({ x: 50, y: 300 });
 | 
						let deckPosition = $state({ x: 50, y: 300 });
 | 
				
			||||||
 | 
						let canvasOffset = $state({ x: 0, y: 0 });
 | 
				
			||||||
 | 
						let isDragging = $state(false);
 | 
				
			||||||
 | 
						let dragStart = $state({ x: 0, y: 0 });
 | 
				
			||||||
 | 
						let dragStartOffset = $state({ x: 0, y: 0 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let cards = $state<CardData[]>(
 | 
						let cards = $state<CardData[]>([]);
 | 
				
			||||||
		cardImages.map((image, index) => ({
 | 
						let maxZIndex = $state(0);
 | 
				
			||||||
 | 
						let stickyNotes = $state<StickyNote[]>([]);
 | 
				
			||||||
 | 
						let nextNoteId = $state(1);
 | 
				
			||||||
 | 
						let noteColors = ['#ffeb3b', '#4caf50', '#2196f3', '#ff9800', '#e91e63', '#9c27b0'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function initializeDeck() {
 | 
				
			||||||
 | 
							const config = deckConfigs[selectedDeck];
 | 
				
			||||||
 | 
							cards = config.cards.map((image, index) => ({
 | 
				
			||||||
			id: index + 1,
 | 
								id: index + 1,
 | 
				
			||||||
			frontImage: image,
 | 
								frontImage: image,
 | 
				
			||||||
			flipped: true,
 | 
								flipped: true,
 | 
				
			||||||
			x: deckPosition.x + index * 2,
 | 
								x: deckPosition.x + index * 2,
 | 
				
			||||||
			y: deckPosition.y + index * 2,
 | 
								y: deckPosition.y + index * 2,
 | 
				
			||||||
			zIndex: index,
 | 
								zIndex: index,
 | 
				
			||||||
			inDeck: true
 | 
								inDeck: true,
 | 
				
			||||||
		}))
 | 
								isRiskCategory: selectedDeck === 1 && image.includes('1.png')
 | 
				
			||||||
	);
 | 
							}));
 | 
				
			||||||
	let maxZIndex = $state(cards.length);
 | 
							maxZIndex = cards.length;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						onMount(() => {
 | 
				
			||||||
 | 
							initializeDeck();
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function handleDeckChange() {
 | 
				
			||||||
 | 
							initializeDeck();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function handleCardMove(id: number, x: number, y: number) {
 | 
						function handleCardMove(id: number, x: number, y: number) {
 | 
				
			||||||
		const card = cards.find(c => c.id === id);
 | 
							const card = cards.find(c => c.id === id);
 | 
				
			||||||
@ -41,12 +146,16 @@
 | 
				
			|||||||
	function handleCardDrop(id: number, x: number, y: number) {
 | 
						function handleCardDrop(id: number, x: number, y: number) {
 | 
				
			||||||
		const card = cards.find(c => c.id === id);
 | 
							const card = cards.find(c => c.id === id);
 | 
				
			||||||
		if (card) {
 | 
							if (card) {
 | 
				
			||||||
			// Check if card is dropped on deck area
 | 
								// Calculate card center position (card is 220x300px)
 | 
				
			||||||
 | 
								const cardCenterX = x + 110;
 | 
				
			||||||
 | 
								const cardCenterY = y + 150;
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								// Check if card center is dropped on deck area
 | 
				
			||||||
			if (
 | 
								if (
 | 
				
			||||||
				x >= deckPosition.x && 
 | 
									cardCenterX >= deckPosition.x && 
 | 
				
			||||||
				x <= deckPosition.x + 220 && 
 | 
									cardCenterX <= deckPosition.x + 220 && 
 | 
				
			||||||
				y >= deckPosition.y && 
 | 
									cardCenterY >= deckPosition.y && 
 | 
				
			||||||
				y <= deckPosition.y + 300 &&
 | 
									cardCenterY <= deckPosition.y + 300 &&
 | 
				
			||||||
				!card.inDeck
 | 
									!card.inDeck
 | 
				
			||||||
			) {
 | 
								) {
 | 
				
			||||||
				// Move card to top of deck
 | 
									// Move card to top of deck
 | 
				
			||||||
@ -76,6 +185,20 @@
 | 
				
			|||||||
		const deckCards = cards.filter(card => card.inDeck);
 | 
							const deckCards = cards.filter(card => card.inDeck);
 | 
				
			||||||
		if (deckCards.length === 0) return;
 | 
							if (deckCards.length === 0) return;
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
 | 
							if (selectedDeck === 1) {
 | 
				
			||||||
 | 
								if (shuffleMode === 'shuffle-only-risks') {
 | 
				
			||||||
 | 
									shuffleOnlyRisks();
 | 
				
			||||||
 | 
								} else if (shuffleMode === 'shuffle-per-category') {
 | 
				
			||||||
 | 
									shufflePerCategory();
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									normalShuffle(deckCards);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								normalShuffle(deckCards);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function normalShuffle(deckCards: CardData[]) {
 | 
				
			||||||
		for (let i = deckCards.length - 1; i > 0; i--) {
 | 
							for (let i = deckCards.length - 1; i > 0; i--) {
 | 
				
			||||||
			const j = Math.floor(Math.random() * (i + 1));
 | 
								const j = Math.floor(Math.random() * (i + 1));
 | 
				
			||||||
			[deckCards[i], deckCards[j]] = [deckCards[j], deckCards[i]];
 | 
								[deckCards[i], deckCards[j]] = [deckCards[j], deckCards[i]];
 | 
				
			||||||
@ -89,6 +212,70 @@
 | 
				
			|||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function shuffleOnlyRisks() {
 | 
				
			||||||
 | 
							const deckCards = cards.filter(card => card.inDeck);
 | 
				
			||||||
 | 
							const riskCategories = deckCards.filter(card => card.isRiskCategory);
 | 
				
			||||||
 | 
							const riskCards = deckCards.filter(card => !card.isRiskCategory);
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							for (let i = riskCards.length - 1; i > 0; i--) {
 | 
				
			||||||
 | 
								const j = Math.floor(Math.random() * (i + 1));
 | 
				
			||||||
 | 
								[riskCards[i], riskCards[j]] = [riskCards[j], riskCards[i]];
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							const shuffled = [...riskCategories, ...riskCards];
 | 
				
			||||||
 | 
							shuffled.reverse();
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							const numCategories = riskCategories.length;
 | 
				
			||||||
 | 
							const totalCards = shuffled.length;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							shuffled.forEach((card, index) => {
 | 
				
			||||||
 | 
								if (index >= totalCards - numCategories) {
 | 
				
			||||||
 | 
									const categoryIndex = index - (totalCards - numCategories);
 | 
				
			||||||
 | 
									card.x = 50 + 330 + (categoryIndex % 6) * 240;
 | 
				
			||||||
 | 
									card.y = 50 + Math.floor(categoryIndex / 6) * 320;
 | 
				
			||||||
 | 
									card.zIndex = index;
 | 
				
			||||||
 | 
									card.flipped = false;
 | 
				
			||||||
 | 
									card.inDeck = false;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									card.x = deckPosition.x + index * 2;
 | 
				
			||||||
 | 
									card.y = deckPosition.y + index * 2;
 | 
				
			||||||
 | 
									card.zIndex = index;
 | 
				
			||||||
 | 
									card.flipped = true;
 | 
				
			||||||
 | 
									card.inDeck = true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function shufflePerCategory() {
 | 
				
			||||||
 | 
							const deckCards = cards.filter(card => card.inDeck);
 | 
				
			||||||
 | 
							const categories = currentDeckConfig.riskCategories || [];
 | 
				
			||||||
 | 
							const shuffledDeck: CardData[] = [];
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							for (const category of categories) {
 | 
				
			||||||
 | 
								const categoryCard = deckCards.find(card => card.frontImage.includes(`${category}1.png`));
 | 
				
			||||||
 | 
								const categoryRisks = deckCards.filter(card => 
 | 
				
			||||||
 | 
									card.frontImage.includes(category) && !card.frontImage.includes('1.png')
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								for (let i = categoryRisks.length - 1; i > 0; i--) {
 | 
				
			||||||
 | 
									const j = Math.floor(Math.random() * (i + 1));
 | 
				
			||||||
 | 
									[categoryRisks[i], categoryRisks[j]] = [categoryRisks[j], categoryRisks[i]];
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								if (categoryCard) shuffledDeck.push(categoryCard);
 | 
				
			||||||
 | 
								shuffledDeck.push(...categoryRisks);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							shuffledDeck.reverse();
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							shuffledDeck.forEach((card, index) => {
 | 
				
			||||||
 | 
								card.x = deckPosition.x + index * 2;
 | 
				
			||||||
 | 
								card.y = deckPosition.y + index * 2;
 | 
				
			||||||
 | 
								card.zIndex = index;
 | 
				
			||||||
 | 
								card.flipped = true;
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function collectToDeck() {
 | 
						function collectToDeck() {
 | 
				
			||||||
		cards.forEach((card, index) => {
 | 
							cards.forEach((card, index) => {
 | 
				
			||||||
			card.x = deckPosition.x + index * 2;
 | 
								card.x = deckPosition.x + index * 2;
 | 
				
			||||||
@ -101,7 +288,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	function dealCards() {
 | 
						function dealCards() {
 | 
				
			||||||
		cards.forEach((card, index) => {
 | 
							cards.forEach((card, index) => {
 | 
				
			||||||
			card.x = 50 + (index % 6) * 240;
 | 
								card.x = 50 + 330 + (index % 6) * 240;
 | 
				
			||||||
			card.y = 50 + Math.floor(index / 6) * 320;
 | 
								card.y = 50 + Math.floor(index / 6) * 320;
 | 
				
			||||||
			card.zIndex = index;
 | 
								card.zIndex = index;
 | 
				
			||||||
			card.flipped = false;
 | 
								card.flipped = false;
 | 
				
			||||||
@ -114,21 +301,132 @@
 | 
				
			|||||||
			card.flipped = !card.flipped;
 | 
								card.flipped = !card.flipped;
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function handleCanvasWheel(event: WheelEvent) {
 | 
				
			||||||
 | 
							event.preventDefault();
 | 
				
			||||||
 | 
							const scrollSpeed = 1;
 | 
				
			||||||
 | 
							canvasOffset.x -= event.deltaX * scrollSpeed;
 | 
				
			||||||
 | 
							canvasOffset.y -= event.deltaY * scrollSpeed;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							canvasOffset.x = Math.max(-2000, Math.min(2000, canvasOffset.x));
 | 
				
			||||||
 | 
							canvasOffset.y = Math.max(-2000, Math.min(2000, canvasOffset.y));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function handleCanvasMouseDown(event: MouseEvent) {
 | 
				
			||||||
 | 
							if (event.button === 1 || event.button === 2) {
 | 
				
			||||||
 | 
								event.preventDefault();
 | 
				
			||||||
 | 
								isDragging = true;
 | 
				
			||||||
 | 
								dragStart.x = event.clientX;
 | 
				
			||||||
 | 
								dragStart.y = event.clientY;
 | 
				
			||||||
 | 
								dragStartOffset.x = canvasOffset.x;
 | 
				
			||||||
 | 
								dragStartOffset.y = canvasOffset.y;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function handleCanvasMouseMove(event: MouseEvent) {
 | 
				
			||||||
 | 
							if (isDragging) {
 | 
				
			||||||
 | 
								event.preventDefault();
 | 
				
			||||||
 | 
								const deltaX = event.clientX - dragStart.x;
 | 
				
			||||||
 | 
								const deltaY = event.clientY - dragStart.y;
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								canvasOffset.x = Math.max(-2000, Math.min(2000, dragStartOffset.x + deltaX));
 | 
				
			||||||
 | 
								canvasOffset.y = Math.max(-2000, Math.min(2000, dragStartOffset.y + deltaY));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function handleCanvasMouseUp(event: MouseEvent) {
 | 
				
			||||||
 | 
							if (event.button === 1 || event.button === 2) {
 | 
				
			||||||
 | 
								isDragging = false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function handleCanvasContextMenu(event: MouseEvent) {
 | 
				
			||||||
 | 
							event.preventDefault();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function addStickyNote() {
 | 
				
			||||||
 | 
							const randomColor = noteColors[Math.floor(Math.random() * noteColors.length)];
 | 
				
			||||||
 | 
							const newNote: StickyNote = {
 | 
				
			||||||
 | 
								id: nextNoteId++,
 | 
				
			||||||
 | 
								text: 'New note',
 | 
				
			||||||
 | 
								x: 400 - canvasOffset.x,
 | 
				
			||||||
 | 
								y: 200 - canvasOffset.y,
 | 
				
			||||||
 | 
								zIndex: ++maxZIndex,
 | 
				
			||||||
 | 
								color: randomColor
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							stickyNotes.push(newNote);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function updateStickyNote(id: number, text: string) {
 | 
				
			||||||
 | 
							const note = stickyNotes.find(n => n.id === id);
 | 
				
			||||||
 | 
							if (note) {
 | 
				
			||||||
 | 
								note.text = text;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function moveStickyNote(id: number, x: number, y: number) {
 | 
				
			||||||
 | 
							const note = stickyNotes.find(n => n.id === id);
 | 
				
			||||||
 | 
							if (note) {
 | 
				
			||||||
 | 
								note.x = x;
 | 
				
			||||||
 | 
								note.y = y;
 | 
				
			||||||
 | 
								note.zIndex = ++maxZIndex;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function deleteStickyNote(id: number) {
 | 
				
			||||||
 | 
							const index = stickyNotes.findIndex(n => n.id === id);
 | 
				
			||||||
 | 
							if (index >= 0) {
 | 
				
			||||||
 | 
								stickyNotes.splice(index, 1);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="game-container">
 | 
					<div class="game-container">
 | 
				
			||||||
	<div class="controls">
 | 
						<div class="controls">
 | 
				
			||||||
 | 
							<div class="control-group">
 | 
				
			||||||
 | 
								<label for="deck-select">Deck:</label>
 | 
				
			||||||
 | 
								<select id="deck-select" bind:value={selectedDeck} onchange={handleDeckChange}>
 | 
				
			||||||
 | 
									{#each deckConfigs as config, index}
 | 
				
			||||||
 | 
										<option value={index}>{config.name}</option>
 | 
				
			||||||
 | 
									{/each}
 | 
				
			||||||
 | 
								</select>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							{#if selectedDeck === 1}
 | 
				
			||||||
 | 
								<div class="control-group">
 | 
				
			||||||
 | 
									<label for="shuffle-mode">Shuffle:</label>
 | 
				
			||||||
 | 
									<select id="shuffle-mode" bind:value={shuffleMode}>
 | 
				
			||||||
 | 
										<option value="normal">Normal</option>
 | 
				
			||||||
 | 
										<option value="shuffle-only-risks">Only Risks</option>
 | 
				
			||||||
 | 
										<option value="shuffle-per-category">Per Category</option>
 | 
				
			||||||
 | 
									</select>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							{/if}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
		<button onclick={collectToDeck}>Collect to Deck</button>
 | 
							<button onclick={collectToDeck}>Collect to Deck</button>
 | 
				
			||||||
		<button onclick={shuffleDeck}>Shuffle Deck</button>
 | 
							<button onclick={shuffleDeck}>Shuffle Deck</button>
 | 
				
			||||||
		<button onclick={dealCards}>Deal Cards</button>
 | 
							<button onclick={dealCards}>Deal Cards</button>
 | 
				
			||||||
		<button onclick={flipAllCards}>Flip All</button>
 | 
							<button onclick={flipAllCards}>Flip All</button>
 | 
				
			||||||
 | 
							<button onclick={addStickyNote}>Add Note</button>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<div class="game-area">
 | 
						<div 
 | 
				
			||||||
 | 
							class="game-area"
 | 
				
			||||||
 | 
							onwheel={handleCanvasWheel}
 | 
				
			||||||
 | 
							onmousedown={handleCanvasMouseDown}
 | 
				
			||||||
 | 
							onmousemove={handleCanvasMouseMove}
 | 
				
			||||||
 | 
							onmouseup={handleCanvasMouseUp}
 | 
				
			||||||
 | 
							oncontextmenu={handleCanvasContextMenu}
 | 
				
			||||||
 | 
						>
 | 
				
			||||||
 | 
							<div 
 | 
				
			||||||
 | 
								class="canvas-content" 
 | 
				
			||||||
 | 
								style="transform: translate({canvasOffset.x}px, {canvasOffset.y}px);"
 | 
				
			||||||
 | 
							>
 | 
				
			||||||
			{#each cards as card (card.id)}
 | 
								{#each cards as card (card.id)}
 | 
				
			||||||
				<Card
 | 
									<Card
 | 
				
			||||||
					{...card}
 | 
										{...card}
 | 
				
			||||||
				backImage={backImage}
 | 
										backImage={card.isRiskCategory ? 'risks/risikocat_back.png' : backImage}
 | 
				
			||||||
 | 
										canvasOffset={canvasOffset}
 | 
				
			||||||
					bind:flipped={card.flipped}
 | 
										bind:flipped={card.flipped}
 | 
				
			||||||
					bind:x={card.x}
 | 
										bind:x={card.x}
 | 
				
			||||||
					bind:y={card.y}
 | 
										bind:y={card.y}
 | 
				
			||||||
@ -139,6 +437,20 @@
 | 
				
			|||||||
				/>
 | 
									/>
 | 
				
			||||||
			{/each}
 | 
								{/each}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								{#each stickyNotes as note (note.id)}
 | 
				
			||||||
 | 
									<StickyNote
 | 
				
			||||||
 | 
										{...note}
 | 
				
			||||||
 | 
										canvasOffset={canvasOffset}
 | 
				
			||||||
 | 
										bind:text={note.text}
 | 
				
			||||||
 | 
										bind:x={note.x}
 | 
				
			||||||
 | 
										bind:y={note.y}
 | 
				
			||||||
 | 
										bind:zIndex={note.zIndex}
 | 
				
			||||||
 | 
										onMove={moveStickyNote}
 | 
				
			||||||
 | 
										onUpdate={updateStickyNote}
 | 
				
			||||||
 | 
										onDelete={deleteStickyNote}
 | 
				
			||||||
 | 
									/>
 | 
				
			||||||
 | 
								{/each}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			<div 
 | 
								<div 
 | 
				
			||||||
				class="deck-area" 
 | 
									class="deck-area" 
 | 
				
			||||||
				style="left: {deckPosition.x}px; top: {deckPosition.y}px;"
 | 
									style="left: {deckPosition.x}px; top: {deckPosition.y}px;"
 | 
				
			||||||
@ -146,6 +458,7 @@
 | 
				
			|||||||
				Deck
 | 
									Deck
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style>
 | 
					<style>
 | 
				
			||||||
@ -164,6 +477,33 @@
 | 
				
			|||||||
		display: flex;
 | 
							display: flex;
 | 
				
			||||||
		gap: 10px;
 | 
							gap: 10px;
 | 
				
			||||||
		z-index: 2000;
 | 
							z-index: 2000;
 | 
				
			||||||
 | 
							align-items: center;
 | 
				
			||||||
 | 
							flex-wrap: wrap;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						.control-group {
 | 
				
			||||||
 | 
							display: flex;
 | 
				
			||||||
 | 
							align-items: center;
 | 
				
			||||||
 | 
							gap: 5px;
 | 
				
			||||||
 | 
							background: rgba(255, 255, 255, 0.9);
 | 
				
			||||||
 | 
							padding: 8px 12px;
 | 
				
			||||||
 | 
							border-radius: 27px;
 | 
				
			||||||
 | 
							box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						.control-group label {
 | 
				
			||||||
 | 
							font-weight: 600;
 | 
				
			||||||
 | 
							font-size: 14px;
 | 
				
			||||||
 | 
							color: #333;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						.control-group select {
 | 
				
			||||||
 | 
							padding: 4px 8px;
 | 
				
			||||||
 | 
							border: 1px solid #ccc;
 | 
				
			||||||
 | 
							border-radius: 4px;
 | 
				
			||||||
 | 
							background: white;
 | 
				
			||||||
 | 
							font-size: 14px;
 | 
				
			||||||
 | 
							cursor: pointer;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	.controls button {
 | 
						.controls button {
 | 
				
			||||||
@ -187,6 +527,20 @@
 | 
				
			|||||||
		position: relative;
 | 
							position: relative;
 | 
				
			||||||
		width: 100%;
 | 
							width: 100%;
 | 
				
			||||||
		height: 100%;
 | 
							height: 100%;
 | 
				
			||||||
 | 
							overflow: hidden;
 | 
				
			||||||
 | 
							cursor: grab;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.game-area:active {
 | 
				
			||||||
 | 
							cursor: grabbing;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.canvas-content {
 | 
				
			||||||
 | 
							position: relative;
 | 
				
			||||||
 | 
							width: 500vw;
 | 
				
			||||||
 | 
							height: 500vh;
 | 
				
			||||||
 | 
							min-width: 500vw;
 | 
				
			||||||
 | 
							min-height: 500vh;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	.deck-area {
 | 
						.deck-area {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								static/risks/altsysteme_und_altlasten1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 100 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/altsysteme_und_altlasten2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 232 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/altsysteme_und_altlasten3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 358 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/altsysteme_und_altlasten4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 316 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/altsysteme_und_altlasten5.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 351 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/betrieb_und_deployment1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 115 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/betrieb_und_deployment2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 265 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/betrieb_und_deployment3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 488 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/betrieb_und_deployment4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 242 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/betrieb_und_deployment5.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 387 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/fremdsysteme_und_plattformen1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 189 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/fremdsysteme_und_plattformen2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 237 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/fremdsysteme_und_plattformen3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 381 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/fremdsysteme_und_plattformen4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 371 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/fremdsysteme_und_plattformen5.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 296 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/kompetenz_und_erfahrung1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 178 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/kompetenz_und_erfahrung2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 290 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/kompetenz_und_erfahrung3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 327 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/kompetenz_und_erfahrung4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 378 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/kompetenz_und_erfahrung5.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 318 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/orga_und_prozesse1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 159 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/orga_und_prozesse2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 310 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/orga_und_prozesse3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 321 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/orga_und_prozesse4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 575 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/orga_und_prozesse5.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 366 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/risiko_back.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 474 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/risikocat_back.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 295 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/softwareloesung1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 165 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/softwareloesung2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 492 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/softwareloesung3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 368 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/softwareloesung4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 287 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/softwareloesung5.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 276 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/weiche_faktoren1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 126 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/weiche_faktoren2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 265 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/weiche_faktoren3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 291 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/weiche_faktoren4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 293 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/weiche_faktoren5.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 280 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/zielsetzung1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 174 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/zielsetzung2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 342 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/zielsetzung3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 262 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/zielsetzung4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 310 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/risks/zielsetzung5.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 288 KiB  |