diff --git a/pyramid.css b/pyramid.css index 9315bd5..10da44d 100644 --- a/pyramid.css +++ b/pyramid.css @@ -1,4 +1,4 @@ -.pyramid-board { +#pyramid-board { display: flex; flex-direction: column; align-items: center; @@ -15,23 +15,33 @@ max-width: 1000px; } +.pyramid-layout .card { + position: absolute; /* Essential for correct placement in layout */ + transition: all 0.4s cubic-bezier(0.2, 0.8, 0.2, 1); + z-index: 100; +} + .pyramid-bottom-piles { display: flex; width: 100%; justify-content: center; - gap: 100px; + gap: 80px; align-items: flex-start; + margin-top: 20px; } -.discard-pile-container { - display: flex; - flex-direction: column; - align-items: center; -} - -.pyramid-layout .card { - transition: all 0.4s cubic-bezier(0.2, 0.8, 0.2, 1); - z-index: 100; +/* Make stock look like a stack */ +#pyramid-stock::after { + content: ''; + position: absolute; + top: 4px; + left: 4px; + width: 100%; + height: 100%; + border: 2px solid var(--line); + border-radius: var(--card-radius); + z-index: -1; + background: var(--card-bg); } .card.is-selected { diff --git a/pyramid.js b/pyramid.js index 4dac11a..03aef9c 100644 --- a/pyramid.js +++ b/pyramid.js @@ -144,47 +144,50 @@ const boardRect = gameBoard.getBoundingClientRect(); const layoutRect = pyramidLayout.getBoundingClientRect(); - // If the board isn't visible yet, wait for the next frame - if (boardRect.width === 0) { + // If the board isn't visible yet or dimensions aren't ready, wait for the next frame + if (boardRect.width === 0 || layoutRect.width === 0) { requestAnimationFrame(renderBoard); return; } const rootStyle = getComputedStyle(document.documentElement); let cardWidth = parseFloat(rootStyle.getPropertyValue('--card-width')); - - // Fallback for cardWidth if parsing fails - if (isNaN(cardWidth) || cardWidth === 0) { - cardWidth = 90; - } + if (isNaN(cardWidth) || cardWidth === 0) cardWidth = 80; const cardHeight = cardWidth * 1.4; const rowOverlap = 0.45; - const horizontalGap = 10; + const horizontalGap = cardWidth * 0.15; // Proportional gap - // Position Pyramid Cards - let index = 0; const boardTop = boardRect.top; const boardLeft = boardRect.left; + // Position Pyramid Cards + let index = 0; for (let row = 0; row < 7; row++) { - const rowWidth = (row + 1) * cardWidth + row * horizontalGap; - const startX = (layoutRect.width - rowWidth) / 2; + // Calculate total width of this row to center it + const numCards = row + 1; + const totalRowWidth = numCards * cardWidth + (numCards - 1) * horizontalGap; + const startX = (layoutRect.width - totalRowWidth) / 2; const y = row * (cardHeight * rowOverlap); for (let i = 0; i <= row; i++) { const card = pyramid[index]; if (card) { const el = cardElements[card.id]; - // Calculate final positions relative to the board + // Absolute position relative to the gameBoard const finalTop = y + (layoutRect.top - boardTop); const finalLeft = startX + i * (cardWidth + horizontalGap) + (layoutRect.left - boardLeft); el.style.top = `${finalTop}px`; el.style.left = `${finalLeft}px`; - el.style.zIndex = 100 + row; + el.style.zIndex = 100 + index; el.classList.add('is-flipped'); - el.draggable = false; + + // Mark if it's currently playable for visual feedback + const exposed = isExposed(card.id); + el.classList.toggle('is-exposed', exposed); + el.style.opacity = exposed ? '1' : '0.85'; + // el.style.filter = exposed ? 'none' : 'brightness(0.8)'; // Optional: dim unexposed cards } index++; } @@ -194,32 +197,36 @@ const sRect = stockEl.getBoundingClientRect(); stock.forEach((card, i) => { const el = cardElements[card.id]; - el.style.top = `${sRect.top - boardRect.top + PILE_BORDER_WIDTH}px`; - el.style.left = `${sRect.left - boardRect.left + PILE_BORDER_WIDTH}px`; + el.style.top = `${sRect.top - boardTop + PILE_BORDER_WIDTH}px`; + el.style.left = `${sRect.left - boardLeft + PILE_BORDER_WIDTH}px`; el.style.zIndex = 10 + i; el.classList.remove('is-flipped'); - el.classList.remove('is-selected'); // Selection only on top of waste/pyramid + el.classList.remove('is-selected'); + el.classList.remove('is-exposed'); }); // Position Waste const wRect = wasteEl.getBoundingClientRect(); waste.forEach((card, i) => { const el = cardElements[card.id]; - el.style.top = `${wRect.top - boardRect.top + PILE_BORDER_WIDTH}px`; - el.style.left = `${wRect.left - boardRect.left + PILE_BORDER_WIDTH}px`; + const isTop = (i === waste.length - 1); + el.style.top = `${wRect.top - boardTop + PILE_BORDER_WIDTH}px`; + el.style.left = `${wRect.left - boardLeft + PILE_BORDER_WIDTH}px`; el.style.zIndex = 50 + i; el.classList.add('is-flipped'); + el.classList.toggle('is-exposed', isTop); }); // Position Discard const dRect = discardEl.getBoundingClientRect(); discard.forEach((card, i) => { const el = cardElements[card.id]; - el.style.top = `${dRect.top - boardRect.top + PILE_BORDER_WIDTH}px`; - el.style.left = `${dRect.left - boardRect.left + PILE_BORDER_WIDTH}px`; + el.style.top = `${dRect.top - boardTop + PILE_BORDER_WIDTH}px`; + el.style.left = `${dRect.left - boardLeft + PILE_BORDER_WIDTH}px`; el.style.zIndex = 10 + i; el.classList.add('is-flipped'); el.classList.remove('is-selected'); + el.classList.remove('is-exposed'); }); checkWin(); @@ -228,12 +235,12 @@ function handleCardClick(cardId) { if (!isActive) return; - // Find card in pyramid or waste + // Find card let card = null; let source = ''; - let index = pyramid.findIndex(c => c && c.id === cardId); - if (index !== -1) { - card = pyramid[index]; + let pIdx = pyramid.findIndex(c => c && c.id === cardId); + if (pIdx !== -1) { + card = pyramid[pIdx]; source = 'pyramid'; } else if (waste.length > 0 && waste[waste.length - 1].id === cardId) { card = waste[waste.length - 1]; @@ -245,10 +252,13 @@ const el = cardElements[card.id]; - // King is special (13) + // King is special (13) - remove immediately if (getValueRank(card.value) === 13) { + if (selectedCard) { + cardElements[selectedCard.id].classList.remove('is-selected'); + selectedCard = null; + } removeCards([card]); - selectedCard = null; return; } @@ -257,15 +267,16 @@ el.classList.add('is-selected'); } else { if (selectedCard.id === card.id) { - // Deselect + // Deselect if clicking the same card el.classList.remove('is-selected'); selectedCard = null; } else { + // Try to match if (getValueRank(selectedCard.value) + getValueRank(card.value) === 13) { removeCards([selectedCard, card]); selectedCard = null; } else { - // Invalid pair - switch selection to new card + // Invalid pair: switch selection to the new card cardElements[selectedCard.id].classList.remove('is-selected'); selectedCard = card; el.classList.add('is-selected');