(function () {
  const protocol = "localchat.safe_fill_comedy.v1";
  const eventSessionCreated = "session.created";
  const eventPlayerJoined = "player.joined";
  const eventRoundStarted = "round.started";
  const eventSubmissionCast = "submission.cast";
  const eventSubmissionPicked = "submission.picked";
  const eventGameOver = "game.over";
  const storageKey = "safe-fill-comedy-last";
  const handSize = 7;
  const targetScore = 3;
  const maxRounds = 5;

  const prompts = [
    { id: "p-secret-talent", blanks: 1, en: "My secret talent is {blank1}.", zh: "我的隐藏技能是{blank1}。" },
    { id: "p-meeting-weird", blanks: 1, en: "The meeting got better when {blank1} appeared.", zh: "会议因为{blank1}突然变有趣。" },
    { id: "p-weekend-plan", blanks: 1, en: "My weekend plan: {blank1}.", zh: "我的周末计划：{blank1}。" },
    { id: "p-group-project", blanks: 2, en: "Our group project needed {blank1}, so we brought {blank2}.", zh: "小组作业需要{blank1}，所以我们带来了{blank2}。" },
    { id: "p-doorbell", blanks: 1, en: "The doorbell rang. It was {blank1}.", zh: "门铃响了，门外是{blank1}。" },
    { id: "p-picnic", blanks: 2, en: "A perfect picnic needs {blank1} and {blank2}.", zh: "一场完美野餐需要{blank1}和{blank2}。" },
    { id: "p-mascot", blanks: 1, en: "The new team mascot is {blank1}.", zh: "新的队伍吉祥物是{blank1}。" },
    { id: "p-weather", blanks: 1, en: "Today's forecast includes {blank1}.", zh: "今天的天气预报包括{blank1}。" }
  ];

  const answers = [
    { id: "a-dramatic-music", role: "noun", en: "dramatic background music", zh: "戏剧性的背景音乐" },
    { id: "a-tiny-parade", role: "noun", en: "a tiny parade", zh: "一支迷你游行队伍" },
    { id: "a-invisible-confetti", role: "noun", en: "invisible confetti", zh: "隐形彩纸" },
    { id: "a-perfect-snack", role: "noun", en: "the perfect snack", zh: "完美小零食" },
    { id: "a-overprepared-clipboard", role: "noun", en: "an overprepared clipboard", zh: "准备过头的夹板" },
    { id: "a-sandwich-sunglasses", role: "noun", en: "a sandwich wearing sunglasses", zh: "戴墨镜的三明治" },
    { id: "a-forgotten-umbrella", role: "noun", en: "a heroic forgotten umbrella", zh: "一把英勇的遗忘雨伞" },
    { id: "a-celebration-spreadsheet", role: "noun", en: "a celebration spreadsheet", zh: "一张庆祝电子表格" },
    { id: "a-mysterious-sticky-note", role: "noun", en: "a mysterious sticky note", zh: "一张神秘便利贴" },
    { id: "a-backpack-weightless", role: "noun", en: "a backpack that feels weightless", zh: "轻得像空气的背包" },
    { id: "a-well-timed-tea", role: "noun", en: "a well-timed cup of tea", zh: "一杯来得正好的茶" },
    { id: "a-calendar-with-jazz", role: "noun", en: "a calendar with jazz hands", zh: "会摆造型的日历" },
    { id: "a-chair-spin", role: "action", en: "a very serious chair spin", zh: "一次非常严肃的转椅表演" },
    { id: "a-snack-negotiation", role: "action", en: "peaceful snack negotiation", zh: "和平零食谈判" },
    { id: "a-cloud-shaped-like-toast", role: "noun", en: "a cloud shaped like toast", zh: "一朵像吐司的云" },
    { id: "a-quiet-victory-dance", role: "action", en: "a quiet victory dance", zh: "一段安静的胜利舞" }
  ];

  const state = {
    mode: "",
    role: "",
    roomId: "",
    memberId: "",
    memberName: "",
    cursor: "0",
    locale: "en",
    roundIndex: 0,
    judgeIndex: 0,
    round: null,
    players: {},
    scores: {},
    hands: {},
    submissions: {},
    revealed: false,
    winnerIds: [],
    status: "Choose a mode to start.",
    pollTimer: 0
  };

  function api() {
    return window.localchat || {};
  }

  function el(tag, className, text) {
    const node = document.createElement(tag);
    if (className) {
      node.className = className;
    }
    if (text !== undefined && text !== null) {
      node.textContent = text;
    }
    return node;
  }

  function bridge(method, params) {
    const parts = method.split(".");
    let target = api();
    for (let i = 0; i < parts.length; i += 1) {
      target = target && target[parts[i]];
    }
    if (typeof target !== "function") {
      return Promise.resolve({});
    }
    return target(params || {}).catch(function (error) {
      state.status = (error && error.errorMessage) || "Action failed";
      render();
      return {};
    });
  }

  function toast(message) {
    return bridge("ui.showToast", { message: message });
  }

  function storageGet(key) {
    const store = api().storage && api().storage.local;
    if (!store || !store.get) {
      return Promise.resolve("");
    }
    return store.get(key).catch(function () {
      return "";
    });
  }

  function storageSet(key, value) {
    const store = api().storage && api().storage.local;
    if (!store || !store.set) {
      return Promise.resolve();
    }
    return store.set(key, value).catch(function () {});
  }

  function keepScreenOn(enabled) {
    return bridge("ui.setKeepScreenOn", Boolean(enabled));
  }

  function safeId(value, fallback) {
    const raw = String(value || "").trim().replace(/[^a-zA-Z0-9._:-]/g, "-");
    return raw ? raw.slice(0, 80) : fallback;
  }

  function roomCode() {
    return "SFC-" + Math.floor(1000 + Math.random() * 9000);
  }

  function memberId() {
    return "member-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 7);
  }

  function localText(item) {
    return state.locale === "zh" ? item.zh : item.en;
  }

  function promptById(id) {
    return prompts.find(function (item) { return item.id === id; }) || prompts[0];
  }

  function answerById(id) {
    return answers.find(function (item) { return item.id === id; }) || answers[0];
  }

  function playerRows() {
    return Object.keys(state.players).map(function (id) {
      return state.players[id];
    }).sort(function (a, b) {
      return a.joinOrder - b.joinOrder || a.name.localeCompare(b.name);
    });
  }

  function addPlayer(id, name) {
    const playerId = safeId(id, "");
    if (!playerId) {
      return;
    }
    const playerName = String(name || "Player").trim().slice(0, 32) || "Player";
    if (!state.players[playerId]) {
      state.players[playerId] = {
        id: playerId,
        name: playerName,
        joinOrder: Object.keys(state.players).length
      };
    } else {
      state.players[playerId].name = playerName;
    }
    if (state.scores[playerId] === undefined) {
      state.scores[playerId] = 0;
    }
    if (!state.hands[playerId]) {
      state.hands[playerId] = [];
    }
  }

  function nextPrompt() {
    const prompt = prompts[state.roundIndex % prompts.length];
    state.roundIndex += 1;
    return prompt;
  }

  function drawAnswer(playerId) {
    const hand = state.hands[playerId] || [];
    let index = (state.roundIndex + hand.length + playerId.length) % answers.length;
    while (hand.indexOf(answers[index].id) !== -1) {
      index = (index + 1) % answers.length;
    }
    hand.push(answers[index].id);
    state.hands[playerId] = hand;
  }

  function fillHand(playerId) {
    addPlayer(playerId, state.players[playerId] && state.players[playerId].name);
    while (state.hands[playerId].length < handSize) {
      drawAnswer(playerId);
    }
  }

  function currentJudgeId() {
    const rows = playerRows();
    if (!rows.length) {
      return "";
    }
    return rows[state.judgeIndex % rows.length].id;
  }

  function renderPromptText(prompt, answerIds) {
    let text = localText(prompt);
    (answerIds || []).forEach(function (answerId, index) {
      text = text.replace("{blank" + (index + 1) + "}", localText(answerById(answerId)));
    });
    return text.replace(/\{blank\d+\}/g, "______");
  }

  function makeSubmissionId(playerId) {
    return "sub-" + playerId + "-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 6);
  }

  function sendEvent(event, extra) {
    if (!state.roomId) {
      return Promise.resolve({});
    }
    const payload = Object.assign({
      protocol: protocol,
      event: event,
      sessionId: state.roomId,
      memberId: state.memberId,
      memberName: state.memberName,
      createdAt: new Date().toISOString()
    }, extra || {});
    return bridge("room.message", {
      roomId: state.roomId,
      memberId: state.memberId,
      type: "safeFillComedy.event",
      payload: payload
    });
  }

  function isPayload(payload) {
    return payload && payload.protocol === protocol && typeof payload.event === "string";
  }

  function applyPayload(payload) {
    if (!isPayload(payload)) {
      return;
    }
    if (payload.event === eventSessionCreated) {
      state.status = "Room " + (payload.sessionId || state.roomId) + " is ready.";
      return;
    }
    if (payload.event === eventPlayerJoined) {
      addPlayer(payload.memberId, payload.memberName);
      state.status = (payload.memberName || "A player") + " joined.";
      return;
    }
    if (payload.event === eventRoundStarted) {
      state.round = {
        id: payload.roundId,
        promptId: payload.promptId,
        blanks: Number(payload.blanks || 1),
        judgeId: payload.judgeId,
        selected: []
      };
      state.revealed = false;
      state.submissions = {};
      if (Array.isArray(payload.handIds)) {
        state.hands[state.memberId] = payload.handIds.slice(0, handSize);
      } else {
        fillHand(state.memberId);
      }
      state.status = payload.judgeId === state.memberId ? "You are the judge. Wait for answers." : "Choose cards for the blanks.";
      keepScreenOn(true);
      return;
    }
    if (payload.event === eventSubmissionCast) {
      addPlayer(payload.memberId, payload.memberName);
      if (state.round && payload.roundId === state.round.id) {
        state.submissions[payload.submissionId] = {
          id: payload.submissionId,
          playerId: payload.memberId,
          answerIds: Array.isArray(payload.answerIds) ? payload.answerIds.slice(0, 2) : []
        };
        state.status = "A submission arrived.";
      }
      return;
    }
    if (payload.event === eventSubmissionPicked) {
      applyPick(payload.submissionId, false);
      return;
    }
    if (payload.event === eventGameOver) {
      state.winnerIds = Array.isArray(payload.winnerIds) ? payload.winnerIds : [];
      state.mode = "gameOver";
      state.status = "Game over.";
      keepScreenOn(false);
    }
  }

  function pollRoom() {
    if (!state.roomId || !api().room || !api().room.poll) {
      return;
    }
    bridge("room.poll", {
      roomId: state.roomId,
      cursor: state.cursor,
      limit: 50
    }).then(function (result) {
      const messages = Array.isArray(result.messages) ? result.messages : [];
      messages.forEach(function (message) {
        if (message && message.memberId !== state.memberId) {
          applyPayload(message.payload);
        }
      });
      if (result.nextCursor !== undefined && result.nextCursor !== null) {
        state.cursor = String(result.nextCursor);
      }
      render();
    });
  }

  function startPolling() {
    stopPolling();
    state.pollTimer = window.setInterval(pollRoom, 1200);
    pollRoom();
  }

  function stopPolling() {
    if (state.pollTimer) {
      window.clearInterval(state.pollTimer);
      state.pollTimer = 0;
    }
  }

  function startRound() {
    const players = playerRows();
    if (players.length < 1) {
      toast("Add players first");
      return;
    }
    players.forEach(function (player) {
      fillHand(player.id);
    });
    const judgeId = currentJudgeId();
    const prompt = nextPrompt();
    state.round = {
      id: "round-" + Date.now().toString(36),
      promptId: prompt.id,
      blanks: prompt.blanks,
      judgeId: judgeId,
      selected: []
    };
    state.revealed = false;
    state.submissions = {};
    state.status = judgeId === state.memberId ? "You are judging this round." : "Choose cards for the blanks.";
    keepScreenOn(true);
    sendEvent(eventRoundStarted, {
      roundId: state.round.id,
      promptId: prompt.id,
      blanks: prompt.blanks,
      judgeId: judgeId,
      handIds: state.hands[state.memberId] || []
    });
    render();
  }

  function startSolo() {
    state.mode = "play";
    state.role = "host";
    state.memberId = memberId();
    state.memberName = "You";
    state.roomId = "";
    state.players = {};
    state.scores = {};
    state.hands = {};
    addPlayer(state.memberId, state.memberName);
    addPlayer("bot-brisk", "Brisk Bot");
    addPlayer("bot-sunny", "Sunny Bot");
    stopPolling();
    startRound();
    autoBotSubmit();
  }

  function startHost(name) {
    state.mode = "lobby";
    state.role = "host";
    state.memberId = memberId();
    state.memberName = String(name || "Host").trim().slice(0, 32) || "Host";
    state.roomId = roomCode();
    state.players = {};
    state.scores = {};
    state.hands = {};
    addPlayer(state.memberId, state.memberName);
    state.status = "Creating room...";
    bridge("room.create", {
      roomId: state.roomId,
      memberId: state.memberId
    }).then(function (result) {
      if (result && result.roomId) {
        state.roomId = result.roomId;
      }
      state.status = "Share room " + state.roomId + " with nearby players.";
      storageSet(storageKey, JSON.stringify({ roomId: state.roomId, name: state.memberName }));
      sendEvent(eventSessionCreated, {});
      startPolling();
      render();
    });
    render();
  }

  function joinRoom(roomId, name) {
    const cleanRoom = safeId(roomId, "");
    if (!cleanRoom) {
      toast("Enter a room code first");
      return;
    }
    state.mode = "lobby";
    state.role = "player";
    state.memberId = memberId();
    state.memberName = String(name || "Player").trim().slice(0, 32) || "Player";
    state.roomId = cleanRoom;
    state.players = {};
    state.scores = {};
    state.hands = {};
    state.status = "Joining room...";
    addPlayer(state.memberId, state.memberName);
    bridge("room.join", {
      roomId: state.roomId,
      memberId: state.memberId
    }).then(function () {
      state.status = "Joined " + state.roomId + ". Wait for the host to start.";
      storageSet(storageKey, JSON.stringify({ roomId: state.roomId, name: state.memberName }));
      sendEvent(eventPlayerJoined, {});
      startPolling();
      render();
    });
    render();
  }

  function toggleAnswer(answerId) {
    if (!state.round || state.round.judgeId === state.memberId) {
      return;
    }
    const selected = state.round.selected || [];
    const existing = selected.indexOf(answerId);
    if (existing !== -1) {
      selected.splice(existing, 1);
    } else if (selected.length < state.round.blanks) {
      selected.push(answerId);
    } else {
      toast("This prompt has " + state.round.blanks + " blanks");
    }
    state.round.selected = selected;
    render();
  }

  function submitAnswers() {
    if (!state.round) {
      toast("Start a round first");
      return;
    }
    if (state.round.judgeId === state.memberId) {
      toast("The judge does not submit this round");
      return;
    }
    const selected = state.round.selected || [];
    if (selected.length !== state.round.blanks) {
      toast("Fill every blank before submitting");
      return;
    }
    const submissionId = makeSubmissionId(state.memberId);
    state.submissions[submissionId] = {
      id: submissionId,
      playerId: state.memberId,
      answerIds: selected.slice()
    };
    state.hands[state.memberId] = (state.hands[state.memberId] || []).filter(function (answerId) {
      return selected.indexOf(answerId) === -1;
    });
    fillHand(state.memberId);
    state.round.selected = [];
    state.status = "Submitted. Waiting for the judge.";
    sendEvent(eventSubmissionCast, {
      roundId: state.round.id,
      submissionId: submissionId,
      answerIds: selected
    });
    render();
  }

  function autoBotSubmit() {
    if (!state.round) {
      return;
    }
    playerRows().forEach(function (player) {
      if (player.id === state.memberId || player.id === state.round.judgeId) {
        return;
      }
      fillHand(player.id);
      const answerIds = state.hands[player.id].slice(0, state.round.blanks);
      const submissionId = makeSubmissionId(player.id);
      state.submissions[submissionId] = {
        id: submissionId,
        playerId: player.id,
        answerIds: answerIds
      };
      state.hands[player.id] = state.hands[player.id].slice(state.round.blanks);
      fillHand(player.id);
    });
    render();
  }

  function applyPick(submissionId, broadcast) {
    const submission = state.submissions[submissionId];
    if (!submission) {
      toast("Pick a submission first");
      return;
    }
    state.revealed = true;
    state.scores[submission.playerId] = (state.scores[submission.playerId] || 0) + 1;
    state.status = (state.players[submission.playerId] && state.players[submission.playerId].name || "A player") + " wins the round.";
    state.judgeIndex += 1;
    const winners = gameWinners();
    if (broadcast !== false) {
      sendEvent(eventSubmissionPicked, {
        roundId: state.round && state.round.id,
        submissionId: submissionId
      });
    }
    if (winners.length) {
      state.winnerIds = winners;
      state.mode = "gameOver";
      keepScreenOn(false);
      if (broadcast !== false) {
        sendEvent(eventGameOver, { winnerIds: winners });
      }
    }
    render();
  }

  function gameWinners() {
    const rows = playerRows();
    const reachedTarget = rows.some(function (player) {
      return (state.scores[player.id] || 0) >= targetScore;
    });
    const roundLimitReached = state.judgeIndex >= maxRounds;
    if (!reachedTarget && !roundLimitReached) {
      return [];
    }
    const high = Math.max.apply(null, rows.map(function (player) {
      return state.scores[player.id] || 0;
    }));
    return rows.filter(function (player) {
      return (state.scores[player.id] || 0) === high;
    }).map(function (player) {
      return player.id;
    });
  }

  function field(label, id, value) {
    const wrap = el("label", "field");
    const text = el("span", "", label);
    const input = document.createElement("input");
    input.id = id;
    input.value = value || "";
    wrap.appendChild(text);
    wrap.appendChild(input);
    return { wrap: wrap, input: input };
  }

  function renderShell() {
    document.body.innerHTML = "";
    if (!document.querySelector("link[data-sfc-style]")) {
      const link = document.createElement("link");
      link.rel = "stylesheet";
      link.href = "assets/style.css";
      link.setAttribute("data-sfc-style", "true");
      document.head.appendChild(link);
    }
    const app = el("main", "sfcApp");
    const topbar = el("section", "topbar");
    const brand = el("div", "brand");
    const mark = el("img");
    mark.alt = "";
    mark.src = "assets/safe-fill-blank-comedy-icon.png";
    const titleWrap = el("div");
    titleWrap.appendChild(el("h1", "", "Safe Fill-in-the-blank Comedy"));
    titleWrap.appendChild(el("div", "helper", "Curated comedy for nearby friends."));
    brand.appendChild(mark);
    brand.appendChild(titleWrap);
    topbar.appendChild(brand);
    topbar.appendChild(el("div", "roomBadge", state.roomId || "Local"));
    app.appendChild(topbar);
    const hero = el("section", "hero");
    const heroImage = el("img", "heroImage");
    heroImage.alt = "";
    heroImage.src = "assets/safe-fill-blank-comedy-header.webp";
    hero.appendChild(heroImage);
    app.appendChild(hero);
    document.body.appendChild(app);
    return app;
  }

  function modeButton(title, body, onClick) {
    const button = el("button", "modeButton");
    button.appendChild(el("strong", "", title));
    button.appendChild(el("span", "", body));
    button.addEventListener("click", onClick);
    return button;
  }

  function renderHome(app) {
    const surface = el("section", "surface");
    const modes = el("div", "modeGrid");
    modes.appendChild(modeButton("Quick Play", "One device plus deterministic bots.", startSolo));

    const setup = el("div", "joinGrid");
    const hostName = field("Host name", "host-name", state.memberName || "Host");
    const roomInput = field("Room code", "room-code", state.roomId || "");
    const playerName = field("Player name", "player-name", state.memberName || "Player");
    const hostButton = el("button", "primaryButton", "Create Room");
    hostButton.addEventListener("click", function () {
      startHost(hostName.input.value);
    });
    const joinButton = el("button", "secondaryButton", "Join Room");
    joinButton.addEventListener("click", function () {
      joinRoom(roomInput.input.value, playerName.input.value);
    });
    const hostBox = el("div", "controlStack");
    hostBox.appendChild(hostName.wrap);
    hostBox.appendChild(hostButton);
    const joinBox = el("div", "controlStack");
    joinBox.appendChild(roomInput.wrap);
    joinBox.appendChild(playerName.wrap);
    joinBox.appendChild(joinButton);
    setup.appendChild(hostBox);
    setup.appendChild(joinBox);

    modes.appendChild(modeButton("Host Room", "Create a LAN room and judge safely.", function () {
      hostName.input.focus();
    }));
    modes.appendChild(modeButton("Join Room", "Enter a code and submit curated cards.", function () {
      roomInput.input.focus();
    }));

    surface.appendChild(modes);
    surface.appendChild(setup);
    surface.appendChild(el("p", "status", state.status));
    app.appendChild(surface);
  }

  function renderPrompt() {
    const prompt = state.round ? promptById(state.round.promptId) : prompts[0];
    const card = el("section", "promptCard");
    card.appendChild(el("div", "promptTitle", "Round " + Math.max(1, state.judgeIndex + 1) + " · " + prompt.blanks + " blank" + (prompt.blanks > 1 ? "s" : "")));
    card.appendChild(el("p", "promptText", renderPromptText(prompt, [])));
    return card;
  }

  function renderAnswer(answerId) {
    const answer = answerById(answerId);
    const selected = state.round && (state.round.selected || []).indexOf(answerId) !== -1;
    const disabled = !state.round || state.round.judgeId === state.memberId || state.revealed;
    const button = el("button", "answerCard" + (selected ? " selected" : "") + (disabled ? " used" : ""));
    button.disabled = disabled;
    button.appendChild(el("div", "answerText", localText(answer)));
    button.appendChild(el("div", "tag", answer.role));
    button.addEventListener("click", function () {
      toggleAnswer(answerId);
    });
    return button;
  }

  function renderSubmission(submission) {
    const prompt = state.round ? promptById(state.round.promptId) : prompts[0];
    const card = el("button", "submissionCard");
    card.disabled = !(state.round && state.round.judgeId === state.memberId) || state.revealed;
    card.appendChild(el("div", "submissionText", renderPromptText(prompt, submission.answerIds)));
    const meta = el("div", "submissionMeta");
    meta.appendChild(el("span", "meta", state.revealed ? (state.players[submission.playerId] && state.players[submission.playerId].name || "Player") : "Anonymous"));
    meta.appendChild(el("span", "stateChip", "Pick"));
    card.appendChild(meta);
    card.addEventListener("click", function () {
      applyPick(submission.id, true);
    });
    return card;
  }

  function renderGame(app) {
    const surface = el("section", "surface tableGrid");
    const left = el("section", "controlStack");
    left.appendChild(renderPrompt());
    const submissions = el("div", "submissionGrid");
    Object.keys(state.submissions).sort().forEach(function (id) {
      submissions.appendChild(renderSubmission(state.submissions[id]));
    });
    if (!Object.keys(state.submissions).length) {
      submissions.appendChild(el("p", "status", "No submissions yet."));
    }
    left.appendChild(submissions);
    surface.appendChild(left);

    const controls = el("section", "controlStack");
    const judge = state.players[state.round && state.round.judgeId];
    controls.appendChild(el("div", "winner", "Judge: " + (judge ? judge.name : "Waiting")));
    if (state.role === "host") {
      const next = el("button", "primaryButton", state.round ? "Next Round" : "Start Round");
      next.addEventListener("click", function () {
        startRound();
        if (!state.roomId) {
          autoBotSubmit();
        }
      });
      controls.appendChild(next);
      if (!state.roomId) {
        const bots = el("button", "secondaryButton", "Bot Submissions");
        bots.addEventListener("click", autoBotSubmit);
        controls.appendChild(bots);
      }
    }
    const submit = el("button", "primaryButton", "Submit Cards");
    submit.disabled = !state.round || state.round.judgeId === state.memberId || state.revealed;
    submit.addEventListener("click", submitAnswers);
    controls.appendChild(submit);
    const home = el("button", "secondaryButton", "Back to Modes");
    home.addEventListener("click", function () {
      state.mode = "";
      state.role = "";
      state.round = null;
      stopPolling();
      keepScreenOn(false);
      render();
    });
    controls.appendChild(home);
    controls.appendChild(el("p", "status", state.status));
    controls.appendChild(renderScores());
    surface.appendChild(controls);
    app.appendChild(surface);

    const handPanel = el("section", "panel");
    handPanel.appendChild(el("h2", "", "Your Hand"));
    const hand = el("div", "answerGrid");
    (state.hands[state.memberId] || []).forEach(function (answerId) {
      hand.appendChild(renderAnswer(answerId));
    });
    handPanel.appendChild(hand);
    app.appendChild(handPanel);
  }

  function renderScores() {
    const panel = el("div", "scoreList");
    playerRows().forEach(function (player) {
      const row = el("div", "scoreRow");
      row.appendChild(el("strong", "", player.name));
      row.appendChild(el("span", "scoreChip", String(state.scores[player.id] || 0)));
      panel.appendChild(row);
    });
    return panel;
  }

  function renderLobby(app) {
    const surface = el("section", "surface tableGrid");
    const left = el("section", "controlStack");
    left.appendChild(el("h2", "", "Room Lobby"));
    left.appendChild(el("p", "status", state.status));
    left.appendChild(renderScores());
    surface.appendChild(left);
    const controls = el("section", "controlStack");
    if (state.role === "host") {
      const start = el("button", "primaryButton", "Start Game");
      start.addEventListener("click", startRound);
      controls.appendChild(start);
    } else {
      controls.appendChild(el("p", "status", "Waiting for the host to start."));
    }
    const home = el("button", "secondaryButton", "Back to Modes");
    home.addEventListener("click", function () {
      state.mode = "";
      stopPolling();
      render();
    });
    controls.appendChild(home);
    surface.appendChild(controls);
    app.appendChild(surface);
  }

  function renderGameOver(app) {
    const surface = el("section", "surface");
    const names = state.winnerIds.map(function (id) {
      return state.players[id] && state.players[id].name || "Player";
    });
    surface.appendChild(el("h2", "", "Game Over"));
    surface.appendChild(el("div", "winner", "Winner: " + (names.join(", ") || "Table tie")));
    surface.appendChild(renderScores());
    const again = el("button", "primaryButton", "Play Again");
    again.addEventListener("click", function () {
      state.judgeIndex = 0;
      state.roundIndex = 0;
      state.winnerIds = [];
      state.scores = {};
      playerRows().forEach(function (player) {
        state.scores[player.id] = 0;
      });
      state.mode = "play";
      startRound();
      if (!state.roomId) {
        autoBotSubmit();
      }
    });
    surface.appendChild(again);
    app.appendChild(surface);
  }

  function render() {
    const app = renderShell();
    if (state.mode === "play") {
      renderGame(app);
    } else if (state.mode === "lobby") {
      renderLobby(app);
    } else if (state.mode === "gameOver") {
      renderGameOver(app);
    } else {
      renderHome(app);
    }
  }

  function boot() {
    const language = (navigator.language || "").toLowerCase();
    state.locale = language.indexOf("zh") === 0 ? "zh" : "en";
    storageGet(storageKey).then(function (value) {
      if (value) {
        try {
          const saved = JSON.parse(value);
          state.roomId = saved.roomId || "";
          state.memberName = saved.name || "";
        } catch (error) {
          state.roomId = "";
        }
      }
      render();
    });
  }

  window.addEventListener("beforeunload", function () {
    stopPolling();
    keepScreenOn(false);
  });

  boot();
})();
