BareGit

Initial commit: Web-based Scoundrel game implementation.

- Core Scoundrel game logic and rules implementation.
- Preact and HTM-based single-page application.
- Interactive room management and card resolution.
- Hover-based attack overlay on monster cards (Weapon vs. Bare-handed).
- Multi-level Undo system with state snapshots.
- Visual discard pile and defeated monsters tracking.
- Dynamic weapon durability and combat mechanics.
Author: MetroWind <chris.corsair@gmail.com>
Date: Sat Jan 3 13:59:31 2026 -0800
Commit: 8d101c611d7f7aca457d670e963c9f8e5c436dcf

Changes

diff --git a/design.md b/design.md
new file mode 100644
index 0000000..5c78a46
--- /dev/null
+++ b/design.md
@@ -0,0 +1,146 @@
+# Scoundrel Web Game Design Document
+
+## 1. Overview
+This document outlines the design for a web-based implementation of "Scoundrel," a solo dungeon crawler card game played with a standard poker deck. The application will be a static single-page application (SPA) requiring no server-side code or build steps. It will utilize **Preact** and **HTM** (Hyperscript Tagged Markup) via ES Modules to allow for modern component-based development directly in the browser.
+
+## 2. Game Rules Summary
+The game simulates a dungeon crawl. The goal is to traverse the entire dungeon (the deck) without dying.
+
+### 2.1 Setup
+-   **Deck**: Standard 52-card deck.
+-   **modifications**: Remove all Red Face cards (J, Q, K of Hearts and Diamonds) and Red Aces.
+-   **Start**: Player begins with 20 HP.
+
+### 2.2 Core Loop
+1.  **The Room**: A room is formed by dealing 4 cards face up.
+2.  **Action**: The player must resolve (interact with) at least 3 of the 4 cards to clear the room.
+3.  **Progression**: Once 3 cards are resolved, the remaining 1 card stays, and 3 new cards are dealt to refill the room to 4.
+4.  **Fleeing**: The player may choose to flee (skip) a room. The 4 cards are shuffled to the bottom of the deck. You cannot flee two rooms in a row.
+
+### 2.3 Card Types & Interactions
+-   **Monsters (Spades ♠ & Clubs ♣)**:
+    -   Damage = Card Value (2-10, J=11, Q=12, K=13, A=14).
+    -   If unarmed: Take full damage to HP.
+    -   If armed: Damage = (Monster Value - Weapon Value). If result is < 0, take 0 damage.
+-   **Weapons (Diamonds ♦)**:
+    -   Equipping a weapon replaces the current one.
+    -   Adds the card's value to attack power.
+    -   **Durability**: After a weapon is used to kill a monster, it can only kill monsters *strictly weaker* than the last one killed. (e.g., if you kill a 10, the next kill must be < 10).
+-   **Health Potions (Hearts ♥)**:
+    -   Heals HP equal to card value (2-10).
+    -   Max HP is 20.
+    -   You can only use **one** potion per turn (per room refill). Other hearts in the room are useless for healing until the room refreshes (or unless they are discarded to clear the room).
+
+### 2.4 Win/Loss
+-   **Win**: The deck is empty, and the final room is cleared.
+-   **Lose**: HP drops to 0 or below.
+
+## 3. Technical Architecture
+
+### 3.1 Stack
+-   **HTML5**: Semantic structure.
+-   **CSS3**: Custom styling, CSS Grid/Flexbox for layout, CSS patterns for card backs.
+-   **JavaScript (ES6+)**: Logic and State management.
+-   **Libraries**:
+    -   `preact`: Lightweight React alternative.
+    -   `htm`: JSX-like syntax using tagged templates, eliminating the need for Babel/transpilation.
+    -   *Import strategy*: ES Modules via `unpkg` or `esm.sh`.
+
+### 3.2 File Structure
+```text
+/
+├── index.html      # Entry point, imports main.js
+├── style.css       # All visual styles
+└── main.js         # Game logic, state management, and UI components
+```
+
+### 3.3 Coding Conventions
+-   **Functions**: `camelCase` (e.g., `calculateDamage`)
+-   **Variables**: `snake_case` (e.g., `current_hp`)
+-   **Global Constants**: `UPPER_CASE` (e.g., `MAX_HP`)
+-   **Comments**: JSDoc style for complex logic.
+
+## 4. Data Structures
+
+### 4.1 Card Representation
+```javascript
+const CARD_SUITS = {
+    HEARTS: '♥',
+    DIAMONDS: '♦',
+    CLUBS: '♣',
+    SPADES: '♠'
+};
+
+// Card Object
+{
+    suit: '♥', // One of CARD_SUITS
+    rank: 'K', // Display rank (2-10, J, Q, K, A)
+    value: 13, // Numerical value for logic
+    id: 'unique_id_string', // For React keys
+    type: 'POTION' // POTION, WEAPON, or MONSTER
+}
+```
+
+### 4.2 Game State
+```javascript
+{
+    current_hp: 20,
+    max_hp: 20,
+    deck: [], // Array of Card objects
+    room: [], // Array of Card objects (max 4)
+    discard_pile: [], // Array of Card objects (cleared cards)
+    
+    // Weapon State
+    equipped_weapon: null, // Card object or null
+    last_enemy_value: null, // For tracking weapon durability rule
+    
+    // Turn State
+    cards_played_this_turn: 0, // Need 3 to advance
+    potion_used_this_turn: false,
+    can_flee: true,
+    
+    // History
+    defeated_monsters: [] // Array of Card objects (visual appeal)
+}
+```
+
+## 5. UI Design
+
+### 5.1 Layout
+The screen will be divided into three main sections:
+1.  **Header/Stats**: Title, Current HP bar, Deck count.
+2.  **The Dungeon (Center)**:
+    -   **Room Area**: The 4 active cards laid out horizontally.
+    -   **Weapon Slot**: Shows current weapon and its "max killable value".
+    -   **Controls**: "Run Away" button (if applicable).
+3.  **History/Footer**: 
+    -   Pile of defeated monsters (stacked visually).
+    -   Rules summary / Help toggle.
+
+### 5.2 Card Visuals
+-   **Face**: White background with red (Hearts/Diamonds) or black (Clubs/Spades) text. Large unicode suit symbol in center. Corners show Rank + Suit.
+-   **Back**: CSS `repeating-linear-gradient` to create a cross-hatch or diagonal pattern.
+
+## 6. Implementation Logic
+
+### 6.1 Initialization (`initGame`)
+1.  Generate full deck.
+2.  Filter out Red Faces (J,Q,K) and Red Aces.
+3.  Shuffle.
+4.  Deal 4 cards to `room`.
+
+### 6.2 Interactions
+-   **Clicking a Card**:
+    -   *If Monster*: Calculate damage. `current_hp -= max(0, monster.value - (weapon ? weapon.value : 0))`. If weapon used, update `last_enemy_value`. Check validity (weapon durability). Move monster to `defeated_monsters`.
+    -   *If Weapon*: Move current weapon (if any) to discard. Set new weapon. Reset `last_enemy_value` to infinity (or max).
+    -   *If Potion*: If `!potion_used_this_turn`, `current_hp = min(20, current_hp + potion.value)`. Mark potion used. Move card to discard.
+-   **State Update**: Increment `cards_played_this_turn`.
+-   **Turn End**: If `cards_played_this_turn == 3`:
+    -   Deal 3 new cards.
+    -   Reset `cards_played_this_turn = 0`.
+    -   Reset `potion_used_this_turn = false`.
+    -   Enable `can_flee`.
+
+## 7. Resources
+-   [Rules Description](https://rpdillon.net/scoundrel.html)
+-   [Official PDF](http://www.stfj.net/art/2011/Scoundrel.pdf)
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..602c403
--- /dev/null
+++ b/index.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Scoundrel</title>
+    <link rel="stylesheet" href="style.css">
+    <style>
+        #error-log {
+            display: none;
+            position: fixed;
+            top: 0; left: 0; right: 0;
+            background: #e74c3c;
+            color: white;
+            padding: 20px;
+            z-index: 1000;
+            white-space: pre-wrap;
+            font-family: monospace;
+        }
+    </style>
+</head>
+<body>
+    <div id="error-log"></div>
+    <div id="app"></div>
+
+    <script>
+        window.onerror = function(message, source, lineno, colno, error) {
+            const errorLog = document.getElementById('error-log');
+            errorLog.style.display = 'block';
+            errorLog.textContent += `Error: ${message}\nAt: ${source}:${lineno}:${colno}\n\n`;
+        };
+    </script>
+
+    <!-- Main Game Script -->
+    <script type="module" src="./main.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/main.js b/main.js
new file mode 100644
index 0000000..c57029a
--- /dev/null
+++ b/main.js
@@ -0,0 +1,336 @@
+import { h, render, useState, useEffect, html } from 'https://unpkg.com/htm/preact/standalone.module.js';
+
+// --- Constants ---
+const MAX_HP = 20;
+const SUITS = {
+    HEARTS: { symbol: '♥', color: 'red', type: 'POTION' },
+    DIAMONDS: { symbol: '♦', color: 'red', type: 'WEAPON' },
+    CLUBS: { symbol: '♣', color: 'black', type: 'MONSTER' },
+    SPADES: { symbol: '♠', color: 'black', type: 'MONSTER' }
+};
+
+const RANKS = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'];
+
+// --- Helpers ---
+const getCardValue = (rank) => {
+    if (rank === 'A') return 14;
+    if (rank === 'K') return 13;
+    if (rank === 'Q') return 12;
+    if (rank === 'J') return 11;
+    return parseInt(rank, 10);
+};
+
+const createDeck = () => {
+    let deck = [];
+    let id_counter = 0;
+    for (const [suit_key, suit_data] of Object.entries(SUITS)) {
+        for (const rank of RANKS) {
+            if ((suit_key === 'HEARTS' || suit_key === 'DIAMONDS') && ['J', 'Q', 'K', 'A'].includes(rank)) continue;
+            deck.push({
+                id: `card-${id_counter++}`,
+                suit: suit_data.symbol,
+                color: suit_data.color,
+                type: suit_data.type,
+                rank: rank,
+                value: getCardValue(rank),
+                suit_key: suit_key
+            });
+        }
+    }
+    return deck;
+};
+
+const shuffleDeck = (deck) => {
+    const new_deck = [...deck];
+    for (let i = new_deck.length - 1; i > 0; i--) {
+        const j = Math.floor(Math.random() * (i + 1));
+        [new_deck[i], new_deck[j]] = [new_deck[j], new_deck[i]];
+    }
+    return new_deck;
+};
+
+// --- Components ---
+
+const Card = ({ card, onClick, is_disabled, onAttack, can_use_weapon }) => {
+    if (!card) return html`<div class="card-slot-empty"></div>`;
+
+    const handleWeaponClick = (e) => {
+        e.stopPropagation();
+        onAttack(card, true);
+    };
+
+    const handleBareHandClick = (e) => {
+        e.stopPropagation();
+        onAttack(card, false);
+    };
+
+    return html`
+        <div class="card ${card.color} ${is_disabled ? 'disabled' : ''}" onClick=${() => !is_disabled && onClick(card)}>
+            <div class="card-corner top-left">
+                <span>${card.rank}</span>
+                <span>${card.suit}</span>
+            </div>
+            <div class="card-center">
+                ${card.suit}
+            </div>
+            <div class="card-corner bottom-right">
+                <span>${card.rank}</span>
+                <span>${card.suit}</span>
+            </div>
+            ${card.type === 'MONSTER' && !is_disabled ? html`
+                <div class="card-overlay">
+                    <button class="overlay-btn" title="Attack with Weapon" disabled=${!can_use_weapon} onClick=${handleWeaponClick}>
+                        🗡️
+                    </button>
+                    <button class="overlay-btn" title="Bare-handed" onClick=${handleBareHandClick}>
+                        👊
+                    </button>
+                </div>
+            ` : ''}
+        </div>
+    `;
+};
+
+const WeaponSlot = ({ weapon, last_enemy_value }) => {
+    if (!weapon) {
+        return html`
+            <div class="weapon-slot"><span>No Weapon</span></div>
+            <div class="weapon-info">
+                <p><strong>Unarmed</strong></p>
+                <p>Attack Power: 0</p>
+            </div>
+        `;
+    }
+
+    const max_kill_msg = last_enemy_value === Infinity ? "Any" : `< ${last_enemy_value}`;
+    return html`
+        <div class="weapon-slot"><${Card} card=${weapon} is_disabled=${true} /></div>
+        <div class="weapon-info">
+            <p><strong>Equipped: ${weapon.rank}${weapon.suit}</strong></p>
+            <p>Power: ${weapon.value} | Max Kill: ${max_kill_msg}</p>
+        </div>
+    `;
+};
+
+const GameOver = ({ won, hp, reset_game }) => {
+    const title = won ? "VICTORY!" : "YOU DIED";
+    const css_class = won ? "win" : "lose";
+    return html`
+        <div class="game-over ${css_class}">
+            <h2>${title}</h2>
+            <p>Final Health: ${won ? hp : "DEAD"}</p>
+            <button onClick=${reset_game}>Play Again</button>
+        </div>
+    `;
+};
+
+// --- Main App ---
+
+const INITIAL_STATE = {
+    hp: 20,
+    max_hp: 20,
+    deck: [],
+    room: [null, null, null, null], // Fixed indices
+    discard_pile: [],
+    defeated_monsters: [],
+    weapon: null,
+    last_enemy_value: Infinity,
+    cards_played_this_turn: 0,
+    potion_used_this_turn: false,
+    can_flee: true,
+    game_over: false,
+    won: false,
+    fled_last_turn: false,
+    msg: "Welcome to the dungeon.",
+    history: []
+};
+
+const App = () => {
+    const [state, setState] = useState(INITIAL_STATE);
+
+    useEffect(() => { startGame(); }, []);
+
+    const startGame = () => {
+        const full_deck = shuffleDeck(createDeck());
+        const room = full_deck.splice(0, 4);
+        setState({ ...INITIAL_STATE, deck: full_deck, room, msg: "A new dungeon awaits." });
+    };
+
+    const saveHistory = (currentState) => {
+        // Deep copy state excluding history itself to avoid recursion/heavy memory usage
+        const { history, ...stateToSave } = currentState;
+        // Basic deep copy for this simple state structure (arrays/objects)
+        const snapshot = JSON.parse(JSON.stringify(stateToSave));
+        return [...history, snapshot];
+    };
+
+    const undoLastMove = () => {
+        if (state.history.length === 0) return;
+        
+        const previousState = state.history[state.history.length - 1];
+        const remainingHistory = state.history.slice(0, -1);
+        
+        // Fix JSON.stringify(Infinity) -> null issue
+        if (previousState.last_enemy_value === null) {
+            previousState.last_enemy_value = Infinity;
+        }
+
+        setState({
+            ...previousState,
+            history: remainingHistory,
+            msg: "Undid last move."
+        });
+    };
+
+    const resolveCard = (card, use_weapon = true) => {
+        let new_state = { 
+            ...state,
+            history: saveHistory(state)
+        };
+        let card_resolved = false;
+
+        if (card.type === 'MONSTER') {
+            let damage_taken = 0;
+            if (use_weapon && new_state.weapon && card.value < new_state.last_enemy_value) {
+                damage_taken = Math.max(0, card.value - new_state.weapon.value);
+                new_state.last_enemy_value = card.value;
+                new_state.msg = `Slayed ${card.rank}${card.suit} with weapon. Took ${damage_taken} damage.`;
+                new_state.defeated_monsters = [...new_state.defeated_monsters, card];
+            } else {
+                damage_taken = card.value;
+                new_state.msg = `Fought ${card.rank}${card.suit} bare-handed. Took ${damage_taken} damage.`;
+                new_state.discard_pile = [...new_state.discard_pile, card];
+            }
+            new_state.hp -= damage_taken;
+            card_resolved = true;
+        } else if (card.type === 'WEAPON') {
+            if (new_state.weapon) {
+                new_state.discard_pile = [
+                    ...new_state.discard_pile, 
+                    new_state.weapon,
+                    ...new_state.defeated_monsters
+                ];
+            }
+            new_state.defeated_monsters = [];
+            new_state.weapon = card;
+            new_state.last_enemy_value = Infinity;
+            new_state.msg = `Equipped ${card.rank}${card.suit}.`;
+            card_resolved = true;
+        } else if (card.type === 'POTION') {
+            if (!new_state.potion_used_this_turn) {
+                const old_hp = new_state.hp;
+                new_state.hp = Math.min(new_state.max_hp, new_state.hp + card.value);
+                new_state.potion_used_this_turn = true;
+                new_state.msg = `Healed ${new_state.hp - old_hp} HP.`;
+            } else {
+                new_state.msg = "Potion wasted!";
+            }
+            new_state.discard_pile = [...new_state.discard_pile, card];
+            card_resolved = true;
+        }
+
+        if (card_resolved) {
+            new_state.room = new_state.room.map(c => c && c.id === card.id ? null : c);
+            new_state.cards_played_this_turn += 1;
+            if (new_state.hp <= 0) { new_state.game_over = true; new_state.won = false; }
+            else if (new_state.room.filter(c => c !== null).length === 1 && new_state.deck.length > 0) {
+                const remaining_card = new_state.room.find(c => c !== null);
+                const next_cards = new_state.deck.splice(0, 3);
+                new_state.room = [remaining_card, ...next_cards];
+                new_state.cards_played_this_turn = 0;
+                new_state.potion_used_this_turn = false;
+                new_state.fled_last_turn = false;
+            } else if (new_state.room.every(c => c === null) && new_state.deck.length === 0) {
+                new_state.game_over = true; new_state.won = true;
+            }
+            setState(new_state);
+        }
+    };
+
+    const handleCardClick = (card) => {
+        if (state.game_over) return;
+        if (card.type === 'MONSTER') {
+            // Monsters are handled via overlay buttons
+            return;
+        } else {
+            resolveCard(card);
+        }
+    };
+
+    const fleeRoom = () => {
+        const active_cards = state.room.filter(c => c !== null);
+        const new_deck = [...state.deck, ...active_cards];
+        const new_room = new_deck.splice(0, 4);
+        setState({ 
+            ...state, 
+            deck: new_deck, 
+            room: new_room, 
+            fled_last_turn: true, 
+            cards_played_this_turn: 0, 
+            potion_used_this_turn: false, 
+            msg: "Fled the room.",
+            history: saveHistory(state)
+        });
+    };
+
+    const can_flee_check = !state.fled_last_turn && state.cards_played_this_turn === 0 && state.deck.length > 0;
+
+    return html`
+        <header>
+            <h1>SCOUNDREL</h1>
+            <div class="stats-bar">
+                <span class="hp-bar">HP: ${state.hp} / ${state.max_hp}</span>
+                <span class="deck-count">Deck: ${state.deck.length}</span>
+            </div>
+        </header>
+
+        <div class="game-board">
+            <div class="weapon-area">
+                <div style="display: flex; gap: 20px; align-items: center;">
+                    <${WeaponSlot} weapon=${state.weapon} last_enemy_value=${state.last_enemy_value} />
+                </div>
+                <div class="defeated-container">
+                    <strong>Defeated:</strong>
+                    <div class="defeated-pile">
+                        ${state.defeated_monsters.map(m => html`<div class="mini-card ${m.color}" title="${m.rank}${m.suit}">${m.rank}${m.suit}</div>`)}
+                    </div>
+                </div>
+            </div>
+
+            <div style="text-align: center; height: 1.2em; font-style: italic;">${state.msg}</div>
+
+            <div class="room-container">
+                <div class="room-cards">
+                    ${state.room.map((card, idx) => html`<${Card} 
+                        key=${card ? card.id : 'empty-'+idx} 
+                        card=${card} 
+                        onClick=${handleCardClick} 
+                        onAttack=${resolveCard}
+                        can_use_weapon=${card && state.weapon && card.value < state.last_enemy_value}
+                    />`)}
+                </div>
+            </div>
+
+            <div class="actions">
+                <button onClick=${undoLastMove} disabled=${state.history.length === 0}>Undo</button>
+                <button onClick=${fleeRoom} disabled=${!can_flee_check}>Run Away</button>
+            </div>
+        </div>
+
+        <div class="history-area">
+            <div class="discard-container">
+                <strong>Discard Pile (Newest First):</strong>
+                <div class="discard-pile">
+                    ${state.discard_pile.slice().reverse().map(c => html`<div class="mini-card ${c.color}" title="${c.rank}${c.suit}">${c.rank}${c.suit}</div>`)}
+                </div>
+            </div>
+            <div class="rules-container">
+                <p><strong>Rules:</strong> Standard 52-card deck (no red face cards/aces). Play 3 of 4 cards to advance. Hearts heal (1/turn). Diamonds are weapons. Weapons durability: next monster must be weaker than last. <a href="https://rpdillon.net/scoundrel.html" target="_blank">Full Rules</a></p>
+            </div>
+        </div>
+
+        ${state.game_over && html`<${GameOver} won=${state.won} hp=${state.hp} reset_game=${startGame} />`}
+    `;
+};
+
+render(html`<${App} />`, document.getElementById('app'));
\ No newline at end of file
diff --git a/prompts.md b/prompts.md
new file mode 100644
index 0000000..44c01cf
--- /dev/null
+++ b/prompts.md
@@ -0,0 +1,47 @@
+Create a design doc to `design.md` which describe an implement of the
+Scoundrel poker game as a web game. This is a solo dungeon crawler
+played with a standard deck of poker cards.
+
+The rules are documented at the following places:
+
+* https://rpdillon.net/scoundrel.html.
+* http://www.stfj.net/art/2011/Scoundrel.pdf
+
+Your goal is to create a game that can be hosted statically in a HTTP
+server. It should be purely run in browser. There will be not
+server-side code. For javascript libraries, use Preact if you think
+it’s appropriate. The code should be able to run as-is in browser
+without any building steps. In terms of coding style, functions should
+be in `camelCase`, and variables should be in `snake_case`. Global
+constants should be `UPPER_CASE`. Use text and unicode symbols for the
+card faces, and some kind of CSS pattern for the card back.
+
+In principle, the only cards the player is required to see are the 4
+cards that form the room. However, for visual appeal, the game should
+also show the current weapon card and all the monster cards it
+defeated.
+
+The game should contain a short description of the game rules, and
+links to the external documents I linked above.
+
+The design doc should include a section that describes the rule of the
+game.
+
+You should only create the design doc for now.
+
+----
+
+This project is web implementation of the Scoundrel poker game. See
+`design.md` for the overall design.
+
+Right now when the player pick a monster card, they have to then click
+a button to either attack using weapon or attack bare-handed. Change
+this so that when the player hover the mouse to the monster card,
+there will be an overlay on the card with two buttons: attach with
+weapon or attach bare-handed. Both buttons should be square and with
+an emoji on it.
+
+----
+
+
+TODO: show list of discarded cards
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..9f252c4
--- /dev/null
+++ b/style.css
@@ -0,0 +1,323 @@
+:root {
+    --bg-color: #2c3e50;
+    --text-color: #ecf0f1;
+    --card-width: 120px;
+    --card-height: 168px; /* 1.4 aspect ratio */
+    --card-radius: 10px;
+    --accent-red: #e74c3c;
+    --accent-black: #2c3e50;
+    --highlight: #f1c40f;
+}
+
+body {
+    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+    background-color: var(--bg-color);
+    color: var(--text-color);
+    margin: 0;
+    padding: 0;
+    display: flex;
+    justify-content: center;
+    min-height: 100vh;
+}
+
+#app {
+    width: 100%;
+    max-width: 800px;
+    padding: 20px;
+    box-sizing: border-box;
+    display: flex;
+    flex-direction: column;
+    gap: 20px;
+}
+
+/* Header & Stats */
+header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    border-bottom: 2px solid rgba(255,255,255,0.1);
+    padding-bottom: 10px;
+}
+
+h1 { margin: 0; font-size: 1.5rem; }
+
+.stats-bar {
+    display: flex;
+    gap: 20px;
+    font-size: 1.2rem;
+    font-weight: bold;
+}
+
+.hp-bar { color: var(--accent-red); }
+.deck-count { color: var(--highlight); }
+
+/* Game Area */
+.game-board {
+    display: flex;
+    flex-direction: column;
+    gap: 40px;
+    flex-grow: 1;
+}
+
+/* Weapon Slot */
+.weapon-area {
+    display: flex;
+    align-items: center;
+    gap: 20px;
+    background: rgba(0,0,0,0.2);
+    padding: 15px;
+    border-radius: var(--card-radius);
+}
+
+.weapon-slot {
+    width: var(--card-width);
+    height: var(--card-height);
+    border: 2px dashed rgba(255,255,255,0.3);
+    border-radius: var(--card-radius);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 0.9rem;
+    color: rgba(255,255,255,0.5);
+    position: relative;
+}
+
+.weapon-info {
+    flex-grow: 1;
+}
+
+/* Room (The 4 cards) */
+.room-container {
+    display: flex;
+    flex-direction: column;
+    gap: 10px;
+    min-height: calc(var(--card-height) + 20px);
+}
+
+.room-cards {
+    display: grid;
+    grid-template-columns: repeat(4, var(--card-width));
+    gap: 15px;
+    justify-content: center;
+}
+
+/* Actions */
+.actions {
+    display: flex;
+    justify-content: center;
+    gap: 10px;
+}
+
+.card-slot-empty {
+    width: var(--card-width);
+    height: var(--card-height);
+    border: 2px dashed rgba(255,255,255,0.1);
+    border-radius: var(--card-radius);
+}
+
+/* History / Footer */
+.history-area {
+    margin-top: auto;
+    border-top: 2px solid rgba(255,255,255,0.1);
+    padding-top: 20px;
+    display: flex;
+    flex-direction: column;
+    gap: 20px;
+}
+
+.defeated-container, .discard-container {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    gap: 5px;
+}
+
+.defeated-pile, .discard-pile {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 3px;
+    max-height: 120px;
+    overflow-y: auto;
+    background: rgba(0,0,0,0.1);
+    padding: 5px;
+    border-radius: 5px;
+}
+
+.mini-card {
+    width: 30px;
+    height: 42px;
+    background: white;
+    border-radius: 3px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 1rem;
+    color: black;
+    border: 1px solid #ccc;
+}
+
+.mini-card.red { color: var(--accent-red); }
+.mini-card.black { color: var(--accent-black); }
+
+
+/* Card Styles */
+.card {
+    width: var(--card-width);
+    height: var(--card-height);
+    background-color: white;
+    border-radius: var(--card-radius);
+    position: relative;
+    box-shadow: 0 4px 6px rgba(0,0,0,0.3);
+    cursor: pointer;
+    transition: transform 0.2s, box-shadow 0.2s;
+    user-select: none;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+}
+
+.card:hover {
+    transform: translateY(-5px);
+    box-shadow: 0 8px 12px rgba(0,0,0,0.4);
+}
+
+.card.red { color: var(--accent-red); }
+.card.black { color: var(--accent-black); }
+
+.card-corner {
+    position: absolute;
+    font-size: 1.2rem;
+    font-weight: bold;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    line-height: 1;
+}
+
+.top-left { top: 5px; left: 5px; }
+.bottom-right { bottom: 5px; right: 5px; transform: rotate(180deg); }
+
+.card-center {
+    font-size: 3.5rem;
+}
+
+/* Card Back Pattern */
+.card-back {
+    width: 100%;
+    height: 100%;
+    border-radius: var(--card-radius);
+    background-color: #34495e;
+    background-image: 
+        repeating-linear-gradient(
+            45deg,
+            #2c3e50,
+            #2c3e50 10px,
+            #34495e 10px,
+            #34495e 20px
+        );
+    border: 4px solid white;
+    box-sizing: border-box;
+}
+
+/* Overlay for Monster Cards */
+.card-overlay {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background: rgba(44, 62, 80, 0.9);
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    gap: 10px;
+    opacity: 0;
+    transition: opacity 0.2s;
+    border-radius: var(--card-radius);
+    pointer-events: none; /* Let clicks pass through if hidden */
+    z-index: 10;
+}
+
+.card:hover .card-overlay {
+    opacity: 1;
+    pointer-events: auto;
+}
+
+.overlay-btn {
+    width: 40px;
+    height: 40px;
+    font-size: 1.5rem;
+    cursor: pointer;
+    border: none;
+    border-radius: 5px;
+    background: #ecf0f1;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    transition: transform 0.1s, background-color 0.1s;
+    padding: 0;
+}
+
+.overlay-btn:hover {
+    transform: scale(1.1);
+    background: white;
+}
+
+.overlay-btn:active {
+    transform: scale(0.95);
+}
+
+.overlay-btn:disabled {
+    opacity: 0.3;
+    cursor: not-allowed;
+    filter: grayscale(1);
+    transform: none;
+}
+
+/* Modal / Rules */
+.rules-container {
+    background: rgba(0,0,0,0.3);
+    padding: 15px;
+    border-radius: 5px;
+    font-size: 0.9rem;
+    line-height: 1.4;
+}
+
+.rules-container a { color: var(--highlight); }
+
+.game-over {
+    position: fixed;
+    top: 0; left: 0; right: 0; bottom: 0;
+    background: rgba(0,0,0,0.85);
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    z-index: 100;
+    text-align: center;
+}
+
+.game-over h2 { font-size: 3rem; margin-bottom: 20px; }
+.game-over.win { color: var(--highlight); }
+.game-over.lose { color: var(--accent-red); }
+
+/* Utility */
+.shake {
+    animation: shake 0.5s;
+}
+
+@keyframes shake {
+    0% { transform: translate(1px, 1px) rotate(0deg); }
+    10% { transform: translate(-1px, -2px) rotate(-1deg); }
+    20% { transform: translate(-3px, 0px) rotate(1deg); }
+    30% { transform: translate(3px, 2px) rotate(0deg); }
+    40% { transform: translate(1px, -1px) rotate(1deg); }
+    50% { transform: translate(-1px, 2px) rotate(-1deg); }
+    60% { transform: translate(-3px, 1px) rotate(0deg); }
+    70% { transform: translate(3px, 1px) rotate(-1deg); }
+    80% { transform: translate(-1px, -1px) rotate(1deg); }
+    90% { transform: translate(1px, 2px) rotate(0deg); }
+    100% { transform: translate(1px, -2px) rotate(-1deg); }
+}
\ No newline at end of file