diff --git a/Ludo-Game/READEM.md b/Ludo-Game/READEM.md
new file mode 100644
index 000000000..e69de29bb
diff --git a/Ludo-Game/index.html b/Ludo-Game/index.html
new file mode 100644
index 000000000..13b8afcb2
--- /dev/null
+++ b/Ludo-Game/index.html
@@ -0,0 +1,245 @@
+
+
+
+
+
+
+
+
+
+ Ludo Game | Gauravi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Choose Players
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Red
+
+
+
+
+ Green
+
+
+
+
+ Yellow
+
+
+
+
+ Blue
+
+
+
+
+ FINISH
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
๐
+
+
+ We Have a Winner!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Ludo-Game/script.js b/Ludo-Game/script.js
new file mode 100644
index 000000000..38ade5f9d
--- /dev/null
+++ b/Ludo-Game/script.js
@@ -0,0 +1,1023 @@
+"use strict";
+
+/* -------------------------------------------------
+ Player and game configuration
+------------------------------------------------- */
+
+const PLAYER_DATA = [
+ {
+ name: "Red",
+ color: "red",
+ hex: "#e63946",
+ start: 0
+ },
+ {
+ name: "Green",
+ color: "green",
+ hex: "#2a9d62",
+ start: 13
+ },
+ {
+ name: "Yellow",
+ color: "yellow",
+ hex: "#f4b942",
+ start: 26
+ },
+ {
+ name: "Blue",
+ color: "blue",
+ hex: "#2878d0",
+ start: 39
+ }
+];
+
+const TRACK_LENGTH = 52;
+
+/* Tokens cannot be captured on these cells. */
+const SAFE_CELLS = new Set([
+ 0, 8, 13, 21, 26, 34, 39, 47
+]);
+
+const DICE_FACES = [
+ "โ", "โ", "โ", "โ", "โ", "โ
"
+];
+
+
+/* -------------------------------------------------
+ Select HTML elements
+------------------------------------------------- */
+
+const setupCard =
+ document.querySelector("#setupCard");
+
+const gameSection =
+ document.querySelector("#game");
+
+const track =
+ document.querySelector("#track");
+
+const tokensLayer =
+ document.querySelector("#tokensLayer");
+
+const turnBanner =
+ document.querySelector("#turnBanner");
+
+const diceElement =
+ document.querySelector("#dice");
+
+const rollButton =
+ document.querySelector("#rollButton");
+
+const message =
+ document.querySelector("#message");
+
+const scoreboard =
+ document.querySelector("#scoreboard");
+
+const winnerModal =
+ document.querySelector("#winnerModal");
+
+const winnerText =
+ document.querySelector("#winnerText");
+
+
+/* -------------------------------------------------
+ Game variables
+------------------------------------------------- */
+
+let playerCount = 2;
+let players = [];
+let currentPlayer = 0;
+let diceValue = 0;
+let waitingForToken = false;
+let gameOver = false;
+let trackPositions = [];
+
+
+/* -------------------------------------------------
+ Player-selection buttons
+------------------------------------------------- */
+
+document
+ .querySelectorAll(".player-option")
+ .forEach(function (button) {
+
+ button.addEventListener("click", function () {
+
+ document
+ .querySelectorAll(".player-option")
+ .forEach(function (item) {
+ item.classList.remove("selected");
+ });
+
+ button.classList.add("selected");
+
+ playerCount =
+ Number(button.dataset.count);
+ });
+ });
+
+
+/* -------------------------------------------------
+ Main button events
+------------------------------------------------- */
+
+document
+ .querySelector("#startButton")
+ .addEventListener("click", startGame);
+
+document
+ .querySelector("#newGameButton")
+ .addEventListener("click", showSetup);
+
+document
+ .querySelector("#playAgainButton")
+ .addEventListener("click", function () {
+
+ winnerModal.classList.add("hidden");
+
+ startGame();
+ });
+
+rollButton.addEventListener(
+ "click",
+ rollDice
+);
+
+window.addEventListener(
+ "resize",
+ renderTokens
+);
+
+
+/* -------------------------------------------------
+ Create the 52-cell Ludo track
+------------------------------------------------- */
+
+function createTrack() {
+
+ track.innerHTML = "";
+
+ trackPositions = [];
+
+ const centre = 50;
+ const radius = 43;
+
+ for (
+ let index = 0;
+ index < TRACK_LENGTH;
+ index++
+ ) {
+ const angle =
+ -Math.PI / 2 +
+ (index / TRACK_LENGTH) *
+ Math.PI *
+ 2;
+
+ const x =
+ centre +
+ Math.cos(angle) * radius;
+
+ const y =
+ centre +
+ Math.sin(angle) * radius;
+
+ trackPositions.push({
+ x: x,
+ y: y
+ });
+
+ const cell =
+ document.createElement("div");
+
+ cell.className = "cell";
+
+ cell.dataset.index = index;
+
+ cell.style.left = x + "%";
+ cell.style.top = y + "%";
+
+ if (SAFE_CELLS.has(index)) {
+ cell.classList.add("safe");
+ cell.textContent = "โ";
+ }
+
+ const startOwner =
+ PLAYER_DATA.find(function (player) {
+ return player.start === index;
+ });
+
+ if (startOwner) {
+ cell.classList.add(
+ "start-" + startOwner.color
+ );
+
+ cell.textContent = "โ
";
+ }
+
+ track.appendChild(cell);
+ }
+}
+
+
+/* -------------------------------------------------
+ Start or restart the game
+------------------------------------------------- */
+
+function startGame() {
+
+ players =
+ PLAYER_DATA
+ .slice(0, playerCount)
+ .map(function (playerData, playerIndex) {
+
+ return {
+ name: playerData.name,
+ color: playerData.color,
+ hex: playerData.hex,
+ start: playerData.start,
+
+ tokens: Array
+ .from(
+ { length: 4 },
+ function (_, tokenIndex) {
+
+ return {
+ id:
+ playerIndex +
+ "-" +
+ tokenIndex,
+
+ /*
+ -1 means the token is at home.
+ 0โ51 means it is on the track.
+ 52 means it has finished.
+ */
+ progress: -1
+ };
+ }
+ )
+ };
+ });
+
+ currentPlayer = 0;
+ diceValue = 0;
+ waitingForToken = false;
+ gameOver = false;
+
+ diceElement.textContent = "โ";
+
+ diceElement.setAttribute(
+ "aria-label",
+ "Dice has not been rolled"
+ );
+
+ rollButton.disabled = false;
+
+ winnerModal.classList.add("hidden");
+ setupCard.classList.add("hidden");
+ gameSection.classList.remove("hidden");
+
+ PLAYER_DATA.forEach(
+ function (_, index) {
+
+ const home =
+ document.querySelector(
+ "#home-" + index
+ );
+
+ home.classList.toggle(
+ "inactive-home",
+ index >= playerCount
+ );
+ }
+ );
+
+ createTrack();
+ renderTokens();
+
+ updateInterface(
+ "Roll the dice to begin."
+ );
+}
+
+
+/* -------------------------------------------------
+ Return to the player-selection screen
+------------------------------------------------- */
+
+function showSetup() {
+
+ gameOver = true;
+
+ winnerModal.classList.add("hidden");
+ gameSection.classList.add("hidden");
+ setupCard.classList.remove("hidden");
+}
+
+
+/* -------------------------------------------------
+ Roll the dice
+------------------------------------------------- */
+
+function rollDice() {
+
+ if (waitingForToken || gameOver) {
+ return;
+ }
+
+ rollButton.disabled = true;
+
+ diceElement.classList.add("rolling");
+
+ diceElement.textContent = "?";
+
+ window.setTimeout(
+ function () {
+
+ diceValue =
+ Math.floor(
+ Math.random() * 6
+ ) + 1;
+
+ diceElement.textContent =
+ DICE_FACES[diceValue - 1];
+
+ diceElement.setAttribute(
+ "aria-label",
+ "Dice rolled " + diceValue
+ );
+
+ diceElement.classList.remove(
+ "rolling"
+ );
+
+ handleRoll();
+ },
+ 480
+ );
+}
+
+
+/* -------------------------------------------------
+ Process the dice result
+------------------------------------------------- */
+
+function handleRoll() {
+
+ const player =
+ players[currentPlayer];
+
+ const movableTokens =
+ getMovableTokens(
+ player,
+ diceValue
+ );
+
+ /*
+ If no token can move, either give another roll
+ after a six or move to the next player.
+ */
+ if (movableTokens.length === 0) {
+
+ updateInterface(
+ player.name +
+ " rolled " +
+ diceValue +
+ ", but no token can move."
+ );
+
+ window.setTimeout(
+ function () {
+
+ if (diceValue === 6) {
+
+ updateInterface(
+ "You rolled a six. Roll again!"
+ );
+
+ rollButton.disabled = false;
+ } else {
+ nextTurn();
+ }
+ },
+ 900
+ );
+
+ return;
+ }
+
+ /*
+ If only one token can move, move it automatically.
+ */
+ if (movableTokens.length === 1) {
+
+ const tokenIndex =
+ player.tokens.indexOf(
+ movableTokens[0]
+ );
+
+ moveToken(
+ currentPlayer,
+ tokenIndex
+ );
+
+ return;
+ }
+
+ /*
+ If several tokens can move, allow the player
+ to select one of the glowing tokens.
+ */
+ waitingForToken = true;
+
+ updateInterface(
+ "You rolled " +
+ diceValue +
+ ". Select a glowing token."
+ );
+
+ renderTokens();
+}
+
+
+/* -------------------------------------------------
+ Find tokens that can legally move
+------------------------------------------------- */
+
+function getMovableTokens(player, roll) {
+
+ return player.tokens.filter(
+ function (token) {
+
+ /*
+ A finished token cannot move again.
+ */
+ if (token.progress === 52) {
+ return false;
+ }
+
+ /*
+ A token can leave home only after rolling 6.
+ */
+ if (token.progress === -1) {
+ return roll === 6;
+ }
+
+ /*
+ The roll must not move the token
+ beyond the finishing position.
+ */
+ return (
+ token.progress + roll <= 52
+ );
+ }
+ );
+}
+
+
+/* -------------------------------------------------
+ Move a selected token
+------------------------------------------------- */
+
+function moveToken(
+ playerIndex,
+ tokenIndex
+) {
+
+ if (gameOver) {
+ return;
+ }
+
+ const player =
+ players[playerIndex];
+
+ const token =
+ player.tokens[tokenIndex];
+
+ const oldProgress =
+ token.progress;
+
+ /*
+ Bring the token onto the starting cell
+ when the player rolls a six.
+ */
+ if (token.progress === -1) {
+ token.progress = 0;
+ } else {
+ token.progress += diceValue;
+ }
+
+ waitingForToken = false;
+
+ let captured = false;
+
+ /*
+ Check captures only when the token is
+ currently on the main track.
+ */
+ if (
+ token.progress >= 0 &&
+ token.progress < 52
+ ) {
+ captured =
+ captureOpponent(
+ playerIndex,
+ token
+ );
+ }
+
+ renderTokens();
+ updateScoreboard();
+
+ /*
+ Check whether all four tokens finished.
+ */
+ const playerWon =
+ player.tokens.every(
+ function (item) {
+ return item.progress === 52;
+ }
+ );
+
+ if (playerWon) {
+ finishGame(player);
+ return;
+ }
+
+ /*
+ Display the result of the move.
+ */
+ if (token.progress === 52) {
+
+ updateInterface(
+ player.name +
+ "'s token reached the finish!"
+ );
+
+ } else if (oldProgress === -1) {
+
+ updateInterface(
+ player.name +
+ " brought a token onto the board."
+ );
+
+ } else if (captured) {
+
+ updateInterface(
+ player.name +
+ " captured an opponent's token!"
+ );
+
+ } else {
+
+ updateInterface(
+ player.name +
+ " moved " +
+ diceValue +
+ " spaces."
+ );
+ }
+
+ /*
+ The player gets another turn after rolling
+ six or capturing another token.
+ */
+ window.setTimeout(
+ function () {
+
+ if (
+ diceValue === 6 ||
+ captured
+ ) {
+ updateInterface(
+ player.name +
+ " gets another roll!"
+ );
+
+ rollButton.disabled = false;
+ } else {
+ nextTurn();
+ }
+ },
+ 650
+ );
+}
+
+
+/* -------------------------------------------------
+ Capture opponent tokens
+------------------------------------------------- */
+
+function captureOpponent(
+ activePlayerIndex,
+ movedToken
+) {
+
+ const cellIndex =
+ getBoardIndex(
+ activePlayerIndex,
+ movedToken.progress
+ );
+
+ /*
+ No capture is allowed on a safe cell.
+ */
+ if (SAFE_CELLS.has(cellIndex)) {
+ return false;
+ }
+
+ let captured = false;
+
+ players.forEach(
+ function (player, playerIndex) {
+
+ if (
+ playerIndex ===
+ activePlayerIndex
+ ) {
+ return;
+ }
+
+ player.tokens.forEach(
+ function (token) {
+
+ const tokenIsOnTrack =
+ token.progress >= 0 &&
+ token.progress < 52;
+
+ if (!tokenIsOnTrack) {
+ return;
+ }
+
+ const opponentCell =
+ getBoardIndex(
+ playerIndex,
+ token.progress
+ );
+
+ if (
+ opponentCell ===
+ cellIndex
+ ) {
+ /*
+ Send the captured token home.
+ */
+ token.progress = -1;
+ captured = true;
+ }
+ }
+ );
+ }
+ );
+
+ return captured;
+}
+
+
+/* -------------------------------------------------
+ Convert token progress into a board-cell index
+------------------------------------------------- */
+
+function getBoardIndex(
+ playerIndex,
+ progress
+) {
+
+ return (
+ players[playerIndex].start +
+ progress
+ ) % TRACK_LENGTH;
+}
+
+
+/* -------------------------------------------------
+ Change to the next player's turn
+------------------------------------------------- */
+
+function nextTurn() {
+
+ currentPlayer =
+ (currentPlayer + 1) %
+ players.length;
+
+ diceValue = 0;
+ waitingForToken = false;
+
+ rollButton.disabled = false;
+
+ updateInterface(
+ players[currentPlayer].name +
+ "'s turn. Roll the dice."
+ );
+
+ renderTokens();
+}
+
+
+/* -------------------------------------------------
+ Display every player's tokens
+------------------------------------------------- */
+
+function renderTokens() {
+
+ tokensLayer.innerHTML = "";
+
+ if (
+ players.length === 0 ||
+ trackPositions.length === 0
+ ) {
+ return;
+ }
+
+ players.forEach(
+ function (player, playerIndex) {
+
+ player.tokens.forEach(
+ function (token, tokenIndex) {
+
+ const tokenElement =
+ document.createElement(
+ "button"
+ );
+
+ tokenElement.type =
+ "button";
+
+ tokenElement.className =
+ "token " +
+ player.color;
+
+ tokenElement.setAttribute(
+ "aria-label",
+ player.name +
+ " token " +
+ (tokenIndex + 1)
+ );
+
+ const position =
+ getTokenPosition(
+ playerIndex,
+ tokenIndex,
+ token
+ );
+
+ tokenElement.style.left =
+ position.x + "%";
+
+ tokenElement.style.top =
+ position.y + "%";
+
+ const movableTokens =
+ getMovableTokens(
+ player,
+ diceValue
+ );
+
+ const canMove =
+ waitingForToken &&
+ playerIndex ===
+ currentPlayer &&
+ movableTokens.includes(
+ token
+ );
+
+ if (canMove) {
+
+ tokenElement.classList.add(
+ "movable"
+ );
+
+ tokenElement.addEventListener(
+ "click",
+ function () {
+
+ moveToken(
+ playerIndex,
+ tokenIndex
+ );
+ }
+ );
+
+ } else {
+
+ tokenElement.disabled = true;
+ }
+
+ if (
+ token.progress === 52
+ ) {
+ tokenElement.classList.add(
+ "finished"
+ );
+ }
+
+ tokensLayer.appendChild(
+ tokenElement
+ );
+ }
+ );
+ }
+ );
+}
+
+
+/* -------------------------------------------------
+ Calculate each token's visual position
+------------------------------------------------- */
+
+function getTokenPosition(
+ playerIndex,
+ tokenIndex,
+ token
+) {
+
+ /*
+ Positions of tokens while inside their homes.
+ */
+ if (token.progress === -1) {
+
+ const homes = [
+ { x: 19, y: 19 },
+ { x: 81, y: 19 },
+ { x: 81, y: 81 },
+ { x: 19, y: 81 }
+ ];
+
+ const offsets = [
+ { x: -4, y: -4 },
+ { x: 4, y: -4 },
+ { x: -4, y: 4 },
+ { x: 4, y: 4 }
+ ];
+
+ return {
+ x:
+ homes[playerIndex].x +
+ offsets[tokenIndex].x,
+
+ y:
+ homes[playerIndex].y +
+ offsets[tokenIndex].y
+ };
+ }
+
+ /*
+ Positions of tokens after reaching the finish.
+ */
+ if (token.progress === 52) {
+
+ const finishPositions = [
+ { x: 46, y: 46 },
+ { x: 54, y: 46 },
+ { x: 54, y: 54 },
+ { x: 46, y: 54 }
+ ];
+
+ const smallOffsets = [
+ { x: -1.6, y: -1.6 },
+ { x: 1.6, y: -1.6 },
+ { x: -1.6, y: 1.6 },
+ { x: 1.6, y: 1.6 }
+ ];
+
+ const finish =
+ finishPositions[playerIndex];
+
+ return {
+ x:
+ finish.x +
+ smallOffsets[tokenIndex].x,
+
+ y:
+ finish.y +
+ smallOffsets[tokenIndex].y
+ };
+ }
+
+ /*
+ Position of a token on the track.
+ */
+ const boardIndex =
+ getBoardIndex(
+ playerIndex,
+ token.progress
+ );
+
+ const cell =
+ trackPositions[boardIndex];
+
+ /*
+ Small offsets ensure that several tokens on
+ one cell remain visible.
+ */
+ const stackOffsets = [
+ { x: -1.2, y: -1.2 },
+ { x: 1.2, y: -1.2 },
+ { x: -1.2, y: 1.2 },
+ { x: 1.2, y: 1.2 }
+ ];
+
+ return {
+ x:
+ cell.x +
+ stackOffsets[tokenIndex].x,
+
+ y:
+ cell.y +
+ stackOffsets[tokenIndex].y
+ };
+}
+
+
+/* -------------------------------------------------
+ Update turn, message and scoreboard
+------------------------------------------------- */
+
+function updateInterface(text) {
+
+ if (players.length === 0) {
+ return;
+ }
+
+ const player =
+ players[currentPlayer];
+
+ message.textContent = text;
+
+ turnBanner.textContent =
+ player.name + "'s turn";
+
+ turnBanner.style.background =
+ player.hex;
+
+ updateScoreboard();
+}
+
+
+/* -------------------------------------------------
+ Update player progress
+------------------------------------------------- */
+
+function updateScoreboard() {
+
+ scoreboard.innerHTML =
+ players
+ .map(function (player) {
+
+ const finished =
+ player.tokens.filter(
+ function (token) {
+ return (
+ token.progress ===
+ 52
+ );
+ }
+ ).length;
+
+ const active =
+ player.tokens.filter(
+ function (token) {
+ return (
+ token.progress >= 0 &&
+ token.progress < 52
+ );
+ }
+ ).length;
+
+ return `
+
+
+ ${player.name}
+
+
+
+ ${active} active ยท
+ ${finished}/4 home
+
+
+ `;
+ })
+ .join("");
+}
+
+
+/* -------------------------------------------------
+ Finish the game
+------------------------------------------------- */
+
+function finishGame(player) {
+
+ gameOver = true;
+
+ rollButton.disabled = true;
+
+ winnerText.textContent =
+ player.name +
+ " brought all four tokens home and won the game!";
+
+ winnerModal.classList.remove("hidden");
+}
\ No newline at end of file
diff --git a/Ludo-Game/style.css b/Ludo-Game/style.css
new file mode 100644
index 000000000..2ac41683e
--- /dev/null
+++ b/Ludo-Game/style.css
@@ -0,0 +1,704 @@
+/* ---------- Colour variables ---------- */
+
+:root {
+ --red: #e63946;
+ --green: #2a9d62;
+ --yellow: #f4b942;
+ --blue: #2878d0;
+
+ --ink: #1e293b;
+ --muted: #64748b;
+ --paper: #ffffff;
+ --surface: #f8fafc;
+ --line: #cbd5e1;
+
+ --shadow: 0 18px 45px rgba(15, 23, 42, 0.15);
+}
+
+
+/* ---------- Basic page styling ---------- */
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ min-height: 100vh;
+
+ font-family:
+ Inter,
+ system-ui,
+ -apple-system,
+ BlinkMacSystemFont,
+ "Segoe UI",
+ sans-serif;
+
+ color: var(--ink);
+
+ background:
+ radial-gradient(
+ circle at 15% 10%,
+ rgba(40, 120, 208, 0.16),
+ transparent 30%
+ ),
+ radial-gradient(
+ circle at 85% 90%,
+ rgba(230, 57, 70, 0.14),
+ transparent 30%
+ ),
+ #eef2f7;
+}
+
+button {
+ font: inherit;
+}
+
+button:focus-visible {
+ outline: 4px solid rgba(37, 99, 235, 0.3);
+ outline-offset: 3px;
+}
+
+.hidden {
+ display: none !important;
+}
+
+
+/* ---------- Main page container ---------- */
+
+.app-shell {
+ width: min(1180px, 94vw);
+ margin: 0 auto;
+ padding: 32px 0 48px;
+}
+
+
+/* ---------- Heading ---------- */
+
+header {
+ margin-bottom: 24px;
+ text-align: center;
+}
+
+.eyebrow {
+ margin: 0 0 4px;
+
+ color: #2563eb;
+
+ font-size: 0.78rem;
+ font-weight: 800;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+}
+
+h1 {
+ margin: 0;
+
+ font-size: clamp(2.2rem, 7vw, 4.5rem);
+ line-height: 1;
+}
+
+.subtitle {
+ margin: 10px 0 0;
+ color: var(--muted);
+}
+
+
+/* ---------- Cards ---------- */
+
+.setup-card,
+.control-panel {
+ background: rgba(255, 255, 255, 0.92);
+
+ border: 1px solid rgba(255, 255, 255, 0.8);
+ border-radius: 24px;
+
+ box-shadow: var(--shadow);
+}
+
+
+/* ---------- Player setup card ---------- */
+
+.setup-card {
+ width: min(540px, 100%);
+ margin: 48px auto;
+ padding: 32px;
+ text-align: center;
+}
+
+.setup-card h2 {
+ margin-top: 0;
+}
+
+.player-options {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+
+ gap: 10px;
+ margin: 22px 0;
+}
+
+.player-option,
+.secondary-button {
+ padding: 12px;
+
+ color: var(--ink);
+ background: var(--paper);
+
+ border: 1px solid var(--line);
+ border-radius: 12px;
+
+ font-weight: 700;
+ cursor: pointer;
+}
+
+.player-option.selected {
+ color: #ffffff;
+ background: #2563eb;
+ border-color: #2563eb;
+}
+
+
+/* ---------- Main buttons ---------- */
+
+.primary-button {
+ width: 100%;
+ padding: 14px 18px;
+
+ color: #ffffff;
+
+ background: linear-gradient(
+ 135deg,
+ #2563eb,
+ #4f46e5
+ );
+
+ border: none;
+ border-radius: 14px;
+
+ box-shadow:
+ 0 10px 20px rgba(37, 99, 235, 0.23);
+
+ font-weight: 800;
+ cursor: pointer;
+
+ transition:
+ transform 0.2s ease,
+ opacity 0.2s ease;
+}
+
+.primary-button:hover {
+ transform: translateY(-1px);
+}
+
+.primary-button:disabled {
+ opacity: 0.55;
+ cursor: not-allowed;
+ transform: none;
+}
+
+
+/* ---------- Game layout ---------- */
+
+.game-layout {
+ display: grid;
+
+ grid-template-columns:
+ minmax(300px, 760px)
+ minmax(240px, 320px);
+
+ gap: 24px;
+
+ align-items: center;
+ justify-content: center;
+}
+
+.board-wrap {
+ width: 100%;
+}
+
+
+/* ---------- Ludo board ---------- */
+
+.board {
+ position: relative;
+
+ width: min(76vw, 720px);
+ aspect-ratio: 1;
+
+ margin: auto;
+ overflow: hidden;
+
+ background: #f1f5f9;
+
+ border: 8px solid #ffffff;
+ border-radius: 26px;
+
+ box-shadow: var(--shadow);
+}
+
+
+/* ---------- Player home areas ---------- */
+
+.home {
+ position: absolute;
+
+ width: 31%;
+ height: 31%;
+
+ display: grid;
+ place-items: center;
+
+ color: #ffffff;
+
+ border-radius: 22px;
+
+ font-weight: 900;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.home::after {
+ content: "";
+
+ position: absolute;
+
+ width: 58%;
+ height: 58%;
+
+ border: 4px solid rgba(255, 255, 255, 0.75);
+ border-radius: 50%;
+}
+
+.home span {
+ position: relative;
+ z-index: 1;
+}
+
+.red-home {
+ top: 8%;
+ left: 8%;
+ background: var(--red);
+}
+
+.green-home {
+ top: 8%;
+ right: 8%;
+ background: var(--green);
+}
+
+.yellow-home {
+ right: 8%;
+ bottom: 8%;
+ background: var(--yellow);
+}
+
+.blue-home {
+ bottom: 8%;
+ left: 8%;
+ background: var(--blue);
+}
+
+.inactive-home {
+ opacity: 0.28;
+ filter: grayscale(1);
+}
+
+
+/* ---------- Finishing area ---------- */
+
+.centre-finish {
+ position: absolute;
+
+ top: 41%;
+ left: 41%;
+
+ width: 18%;
+ height: 18%;
+
+ display: grid;
+ place-items: center;
+
+ color: #475569;
+
+ background: conic-gradient(
+ var(--red) 0 25%,
+ var(--green) 0 50%,
+ var(--yellow) 0 75%,
+ var(--blue) 0
+ );
+
+ border: 7px solid #ffffff;
+ border-radius: 50%;
+
+ font-size: clamp(0.55rem, 1.7vw, 0.9rem);
+ font-weight: 950;
+
+ z-index: 1;
+}
+
+
+/* ---------- Track cells ---------- */
+
+.cell {
+ position: absolute;
+
+ width: 5.4%;
+ aspect-ratio: 1;
+
+ display: grid;
+ place-items: center;
+
+ color: #475569;
+ background: #ffffff;
+
+ border: 1px solid #94a3b8;
+ border-radius: 7px;
+
+ font-size: clamp(0.45rem, 1.25vw, 0.7rem);
+ font-weight: 800;
+
+ transform: translate(-50%, -50%);
+}
+
+.cell.safe {
+ background: #e2e8f0;
+}
+
+.cell.start-red {
+ background: #fecdd3;
+ border-color: var(--red);
+}
+
+.cell.start-green {
+ background: #bbf7d0;
+ border-color: var(--green);
+}
+
+.cell.start-yellow {
+ background: #fef08a;
+ border-color: var(--yellow);
+}
+
+.cell.start-blue {
+ background: #bfdbfe;
+ border-color: var(--blue);
+}
+
+
+/* ---------- Player tokens ---------- */
+
+.token {
+ position: absolute;
+
+ width: 4.4%;
+ aspect-ratio: 1;
+
+ border: 3px solid #ffffff;
+
+ border-radius:
+ 50%
+ 50%
+ 50%
+ 12%;
+
+ box-shadow:
+ 0 3px 7px rgba(15, 23, 42, 0.45);
+
+ transform: translate(-50%, -50%);
+
+ cursor: default;
+ z-index: 5;
+
+ transition:
+ left 0.25s ease,
+ top 0.25s ease,
+ transform 0.15s ease;
+}
+
+.token::after {
+ content: "";
+
+ position: absolute;
+
+ top: 24%;
+ left: 24%;
+
+ width: 32%;
+ height: 32%;
+
+ background: rgba(255, 255, 255, 0.75);
+ border-radius: 50%;
+}
+
+.token.red {
+ background: var(--red);
+}
+
+.token.green {
+ background: var(--green);
+}
+
+.token.yellow {
+ background: var(--yellow);
+}
+
+.token.blue {
+ background: var(--blue);
+}
+
+.token.movable {
+ cursor: pointer;
+ animation: pulse 1s infinite;
+}
+
+.token.movable:hover {
+ transform:
+ translate(-50%, -50%)
+ scale(1.2);
+}
+
+.token.finished {
+ border-radius: 50%;
+
+ box-shadow:
+ 0 0 0 4px rgba(255, 255, 255, 0.6);
+}
+
+
+/* ---------- Token animation ---------- */
+
+@keyframes pulse {
+ 50% {
+ filter: brightness(1.25);
+
+ box-shadow:
+ 0 0 0 7px rgba(255, 255, 255, 0.65);
+ }
+}
+
+
+/* ---------- Control panel ---------- */
+
+.control-panel {
+ padding: 24px;
+ text-align: center;
+}
+
+.turn-banner {
+ padding: 10px;
+
+ color: #ffffff;
+ background: var(--red);
+
+ border-radius: 12px;
+
+ font-weight: 900;
+}
+
+
+/* ---------- Dice ---------- */
+
+.dice {
+ width: 92px;
+ height: 92px;
+
+ display: grid;
+ place-items: center;
+
+ margin: 24px auto;
+
+ color: #1d4ed8;
+ background: #ffffff;
+
+ border: 4px solid #dbeafe;
+ border-radius: 20px;
+
+ box-shadow:
+ inset 0 0 18px rgba(37, 99, 235, 0.1);
+
+ font-size: 3rem;
+ font-weight: 950;
+}
+
+.dice.rolling {
+ animation: shake 0.45s linear;
+}
+
+@keyframes shake {
+ 25% {
+ transform:
+ rotate(12deg)
+ scale(0.92);
+ }
+
+ 50% {
+ transform:
+ rotate(-12deg)
+ scale(1.06);
+ }
+
+ 75% {
+ transform: rotate(8deg);
+ }
+}
+
+
+/* ---------- Status message ---------- */
+
+.message {
+ min-height: 48px;
+
+ color: var(--muted);
+ font-weight: 650;
+}
+
+
+/* ---------- Scoreboard ---------- */
+
+.scoreboard {
+ display: grid;
+ gap: 8px;
+
+ margin: 18px 0;
+
+ text-align: left;
+}
+
+.score-row {
+ display: flex;
+ justify-content: space-between;
+
+ padding: 8px 10px;
+
+ background: var(--surface);
+
+ border-left: 8px solid;
+ border-radius: 8px;
+}
+
+.score-row strong {
+ text-transform: capitalize;
+}
+
+
+/* ---------- Board legend ---------- */
+
+.legend {
+ display: flex;
+ justify-content: center;
+
+ gap: 14px;
+ margin: 16px 0;
+
+ color: var(--muted);
+ font-size: 0.8rem;
+}
+
+.safe-dot {
+ display: inline-block;
+
+ width: 10px;
+ height: 10px;
+
+ background: #cbd5e1;
+ border-radius: 50%;
+}
+
+.secondary-button {
+ width: 100%;
+}
+
+
+/* ---------- Winner popup ---------- */
+
+.modal {
+ position: fixed;
+ inset: 0;
+
+ z-index: 20;
+
+ display: grid;
+ place-items: center;
+
+ padding: 20px;
+
+ background: rgba(15, 23, 42, 0.72);
+}
+
+.modal-card {
+ width: min(400px, 100%);
+
+ padding: 34px;
+
+ text-align: center;
+ background: #ffffff;
+
+ border-radius: 24px;
+ box-shadow: var(--shadow);
+}
+
+.modal-card h2 {
+ margin: 8px 0;
+}
+
+.modal-card p {
+ color: var(--muted);
+}
+
+.trophy {
+ font-size: 4rem;
+}
+
+
+/* ---------- Tablet screens ---------- */
+
+@media (max-width: 860px) {
+ .game-layout {
+ grid-template-columns: 1fr;
+ }
+
+ .board {
+ width: min(94vw, 650px);
+ }
+
+ .control-panel {
+ width: min(520px, 100%);
+ margin: auto;
+ }
+}
+
+
+/* ---------- Mobile screens ---------- */
+
+@media (max-width: 520px) {
+ .app-shell {
+ padding-top: 20px;
+ }
+
+ .setup-card {
+ padding: 22px 16px;
+ }
+
+ .player-options {
+ grid-template-columns: 1fr;
+ }
+
+ .board {
+ border-width: 4px;
+ border-radius: 16px;
+ }
+
+ .home {
+ border-radius: 12px;
+ font-size: 0.65rem;
+ }
+
+ .cell {
+ border-radius: 4px;
+ }
+
+ .token {
+ border-width: 2px;
+ }
+}
\ No newline at end of file