<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
<%@ page import="org.springframework.web.util.HtmlUtils" %>
<%
    boolean owner = Boolean.TRUE.equals(request.getAttribute("owner"));
    String ctx = request.getContextPath();
    String gameIdValue = HtmlUtils.htmlEscape(String.valueOf(request.getAttribute("gameId")));
    String gameNameValue = HtmlUtils.htmlEscape(String.valueOf(request.getAttribute("gameName")));
    String creatorValue = HtmlUtils.htmlEscape(String.valueOf(request.getAttribute("creator")));
    String likeCountFormattedValue = HtmlUtils.htmlEscape(String.valueOf(request.getAttribute("likeCountFormatted")));
    String creatorNoteValue = HtmlUtils.htmlEscape(String.valueOf(request.getAttribute("creatorNote")));
    String gitUrlValue = HtmlUtils.htmlEscape(String.valueOf(request.getAttribute("gitUrl")));
    String webglFrameSrc = String.valueOf(request.getAttribute("webglFrameSrc"));
    if (webglFrameSrc == null || webglFrameSrc.isBlank() || "null".equals(webglFrameSrc)) {
        webglFrameSrc = String.valueOf(request.getAttribute("webglUrl"));
    }
    if (webglFrameSrc == null || webglFrameSrc.isBlank() || "null".equals(webglFrameSrc)) {
        webglFrameSrc = "";
    } else if (webglFrameSrc.startsWith("/")) {
        webglFrameSrc = ctx + webglFrameSrc;
    }
    String webglFrameSrcValue = HtmlUtils.htmlEscape(webglFrameSrc);
%>
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <jsp:include page="/WEB-INF/views/theme-init.jsp"/>
    <title><%= gameNameValue %> — bibimbap</title>
    <style>
      html {
        color-scheme: light;
        --surface: #faf8f5;
        --card-bg: #fff;
        --text: #1a1a1a;
        --text-muted: #5c5c5c;
        --accent: #e8a54b;
        --accent-soft: rgba(232, 165, 75, 0.14);
        --border: rgba(0, 0, 0, 0.08);
        --webgl-bg: #0d0d0d;
        --panel-shadow: 0 4px 24px rgba(26, 26, 26, 0.06);
        --radius: 16px;
      }
      html[data-theme="dark"] {
        color-scheme: dark;
        --surface: #121212;
        --card-bg: #1e1e1e;
        --text: #ece8e1;
        --text-muted: #a39e96;
        --border: rgba(255, 255, 255, 0.1);
        --accent-soft: rgba(232, 165, 75, 0.12);
        --webgl-bg: #080808;
        --panel-shadow: 0 8px 32px rgba(0, 0, 0, 0.35);
      }
      body {
        margin: 0;
        min-height: 100vh;
        font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans KR", sans-serif;
        background: var(--surface);
        color: var(--text);
      }
      html[data-theme="dark"] body {
        background: linear-gradient(165deg, #1c1a18 0%, var(--surface) 38%, #0e0e0e 100%);
        background-attachment: fixed;
      }
      html:not([data-theme="dark"]) body {
        background: linear-gradient(165deg, #fff9f0 0%, var(--surface) 45%, #f3ede6 100%);
        background-attachment: fixed;
      }
      .game-page {
        max-width: 56rem;
        margin: 0 auto;
        padding: 1.25rem max(1rem, env(safe-area-inset-left)) 2.75rem max(1rem, env(safe-area-inset-right));
      }
      .game-back {
        display: inline-flex;
        align-items: center;
        gap: 0.45rem;
        margin-bottom: 1.25rem;
        padding: 0.5rem 0.95rem 0.5rem 0.75rem;
        font-size: 0.8125rem;
        font-weight: 600;
        color: var(--text-muted);
        text-decoration: none;
        background: var(--card-bg);
        border: 1px solid var(--border);
        border-radius: 999px;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06);
        transition: color 0.2s ease, border-color 0.2s ease, transform 0.15s ease, box-shadow 0.2s ease;
      }
      .game-back svg {
        width: 1rem;
        height: 1rem;
        flex-shrink: 0;
        opacity: 0.85;
      }
      .game-back:hover {
        color: var(--accent);
        border-color: rgba(232, 165, 75, 0.45);
        box-shadow: 0 4px 16px rgba(232, 165, 75, 0.12);
        transform: translateX(-2px);
      }
      .game-topbar {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 0.75rem;
        margin-bottom: 1.25rem;
      }
      .game-topbar .game-back {
        margin-bottom: 0;
      }
      .game-owner-actions {
        display: flex;
        gap: 0.5rem;
        flex-shrink: 0;
      }
      .game-owner-button {
        min-height: 2.25rem;
        padding: 0 0.8rem;
        border: 1px solid var(--border);
        border-radius: 999px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        background: var(--card-bg);
        color: var(--text);
        font-size: 0.8125rem;
        font-weight: 800;
        text-decoration: none;
        cursor: pointer;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06);
      }
      .game-owner-button:hover {
        border-color: rgba(232, 165, 75, 0.45);
        color: var(--accent);
      }
      .game-owner-button--danger:hover {
        border-color: rgba(180, 50, 50, 0.32);
        color: #b33;
        background: rgba(180, 50, 50, 0.06);
      }
      @media (max-width: 520px) {
        .game-topbar {
          align-items: stretch;
          flex-direction: column;
        }
        .game-owner-actions {
          justify-content: flex-end;
        }
      }

      .game-info-card {
        margin-bottom: 1.75rem;
        padding: 1.5rem 1.35rem 1.6rem;
        background: var(--card-bg);
        border: 1px solid var(--border);
        border-radius: var(--radius);
        box-shadow: var(--panel-shadow);
        position: relative;
        overflow: hidden;
      }
      .game-info-card::after {
        content: "";
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        height: 3px;
        background: linear-gradient(90deg, var(--accent), rgba(232, 165, 75, 0.25), transparent);
        opacity: 0.9;
      }
      .game-info-card h1 {
        margin: 0 0 1.25rem;
        padding-bottom: 0.85rem;
        font-size: clamp(1.5rem, 4.5vw, 1.85rem);
        font-weight: 800;
        letter-spacing: -0.04em;
        line-height: 1.2;
        border-bottom: 1px solid var(--border);
      }

      .game-meta-grid {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 0.65rem;
      }
      @media (max-width: 640px) {
        .game-meta-grid {
          grid-template-columns: 1fr;
        }
      }
      .game-meta-item {
        display: flex;
        flex-direction: column;
        gap: 0.35rem;
        padding: 0.85rem 0.9rem;
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: 12px;
        transition: border-color 0.2s ease, box-shadow 0.2s ease;
      }
      .game-meta-item:hover {
        border-color: rgba(232, 165, 75, 0.25);
        box-shadow: 0 4px 14px rgba(0, 0, 0, 0.06);
      }
      html[data-theme="dark"] .game-meta-item:hover {
        box-shadow: 0 4px 18px rgba(0, 0, 0, 0.35);
      }
      .game-meta-item__row {
        display: flex;
        align-items: center;
        gap: 0.45rem;
      }
      .game-meta-item__icon {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 1.75rem;
        height: 1.75rem;
        border-radius: 8px;
        background: var(--accent-soft);
        color: var(--accent);
        flex-shrink: 0;
      }
      .game-meta-item__icon svg {
        width: 0.95rem;
        height: 0.95rem;
      }
      .game-meta-item__label {
        font-size: 0.6875rem;
        font-weight: 700;
        letter-spacing: 0.06em;
        text-transform: uppercase;
        color: var(--text-muted);
      }
      .game-meta-item__value {
        font-size: 0.9375rem;
        font-weight: 700;
        letter-spacing: -0.02em;
        color: var(--text);
        word-break: break-word;
      }
      .game-meta-item--likes {
        padding: 0.65rem 0.75rem 0.75rem;
      }
      .game-meta-like-btn {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: 0.45rem;
        width: 100%;
        margin: 0;
        margin-top: 0.1rem;
        padding: 0.55rem 0.7rem;
        font: inherit;
        border: 1px solid var(--border);
        border-radius: 10px;
        background: var(--card-bg);
        color: var(--text);
        cursor: pointer;
        transition: border-color 0.2s ease, background 0.2s ease, box-shadow 0.2s ease, transform 0.12s ease;
        -webkit-tap-highlight-color: transparent;
        box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
      }
      .game-meta-like-btn:hover {
        border-color: rgba(232, 165, 75, 0.45);
        box-shadow: 0 3px 12px rgba(232, 165, 75, 0.12);
      }
      .game-meta-like-btn:active {
        transform: scale(0.98);
      }
      .game-meta-like-btn[aria-pressed="true"] {
        background: var(--accent-soft);
        border-color: rgba(232, 165, 75, 0.55);
        box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
      }
      .game-meta-item--likes:has(.game-meta-like-btn[aria-pressed="true"]) {
        border-color: rgba(232, 165, 75, 0.38);
      }
      .game-meta-like-btn__heart {
        width: 1.15rem;
        height: 1.15rem;
        flex-shrink: 0;
      }
      .game-meta-like-btn__heart path {
        fill: none;
        stroke: var(--accent);
        stroke-width: 2;
        stroke-linecap: round;
        stroke-linejoin: round;
      }
      .game-meta-like-btn[aria-pressed="true"] .game-meta-like-btn__heart path {
        fill: var(--accent);
        stroke: none;
      }
      .game-meta-like-btn__count {
        font-size: 1.0625rem;
        font-weight: 800;
        letter-spacing: -0.03em;
        font-variant-numeric: tabular-nums;
        color: var(--accent);
      }
      html[data-theme="dark"] .game-meta-like-btn[aria-pressed="true"] .game-meta-like-btn__count {
        color: #f0c47a;
      }

      .game-webgl {
        margin-bottom: 1.75rem;
      }
      .game-webgl__head {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 0.5rem 0.75rem;
        margin-bottom: 0.85rem;
      }
      .game-webgl__head h2 {
        margin: 0;
        font-size: 1.05rem;
        font-weight: 800;
        letter-spacing: -0.03em;
      }
      .game-webgl__icon {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 2rem;
        height: 2rem;
        border-radius: 10px;
        background: var(--accent-soft);
        color: var(--accent);
      }
      .game-webgl__icon svg {
        width: 1.1rem;
        height: 1.1rem;
      }
      .game-webgl__badge {
        margin-left: auto;
        padding: 0.2rem 0.55rem;
        font-size: 0.625rem;
        font-weight: 800;
        letter-spacing: 0.08em;
        text-transform: uppercase;
        color: var(--accent);
        background: var(--accent-soft);
        border: 1px solid rgba(232, 165, 75, 0.35);
        border-radius: 6px;
      }
      @media (max-width: 480px) {
        .game-webgl__badge {
          margin-left: 0;
        }
      }
      .game-webgl__shell {
        position: relative;
        padding: 3px;
        border-radius: 18px;
        background: linear-gradient(135deg, rgba(232, 165, 75, 0.45) 0%, rgba(232, 165, 75, 0.08) 50%, rgba(255, 255, 255, 0.06) 100%);
        box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25);
      }
      html:not([data-theme="dark"]) .game-webgl__shell {
        box-shadow: 0 12px 36px rgba(26, 26, 26, 0.12);
      }
      .game-webgl__frame-wrap {
        position: relative;
        width: 100%;
        aspect-ratio: 16 / 9;
        max-height: min(70vh, 520px);
        background: var(--webgl-bg);
        border-radius: 15px;
        overflow: hidden;
        border: 1px solid rgba(0, 0, 0, 0.35);
      }
      html:not([data-theme="dark"]) .game-webgl__frame-wrap {
        border-color: rgba(0, 0, 0, 0.12);
      }
      .game-webgl__frame-wrap iframe {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        border: none;
      }
      /* 제작자 한마디 ~ 덧글: 공통 패널 */
      .game-community {
        margin-top: 2rem;
        display: flex;
        flex-direction: column;
        gap: 1.25rem;
      }
      .game-panel {
        position: relative;
        padding: 1.35rem 1.35rem 1.5rem;
        background: var(--card-bg);
        border: 1px solid var(--border);
        border-radius: var(--radius);
        box-shadow: var(--panel-shadow);
        overflow: hidden;
      }
      .game-panel::before {
        content: "";
        position: absolute;
        left: 0;
        top: 0;
        bottom: 0;
        width: 4px;
        background: linear-gradient(180deg, var(--accent) 0%, rgba(232, 165, 75, 0.35) 100%);
        border-radius: 4px 0 0 4px;
      }
      .game-panel__head {
        display: flex;
        align-items: center;
        gap: 0.6rem;
        margin-bottom: 1rem;
      }
      .game-panel__icon {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 2.25rem;
        height: 2.25rem;
        border-radius: 10px;
        background: var(--accent-soft);
        color: var(--accent);
        flex-shrink: 0;
      }
      .game-panel__icon svg {
        width: 1.15rem;
        height: 1.15rem;
      }
      .game-panel__title {
        margin: 0;
        font-size: 0.9375rem;
        font-weight: 700;
        letter-spacing: -0.02em;
        color: var(--text);
      }
      .game-panel__subtitle {
        margin: 0.15rem 0 0;
        font-size: 0.75rem;
        font-weight: 500;
        color: var(--text-muted);
      }

      .game-panel--creator .game-panel__quote {
        margin: 0;
        padding: 1rem 1rem 1rem 1.15rem;
        font-size: 0.9375rem;
        line-height: 1.75;
        font-style: normal;
        color: var(--text);
        background: linear-gradient(135deg, var(--surface) 0%, transparent 55%);
        border-radius: 12px;
        border-left: 3px solid var(--accent);
      }
      .game-panel__git-wrap {
        margin-top: 1.15rem;
        padding-top: 1rem;
        border-top: 1px dashed var(--border);
      }
      .game-panel__git {
        display: inline-flex;
        align-items: center;
        gap: 0.5rem;
        padding: 0.55rem 1rem;
        font-size: 0.8125rem;
        font-weight: 600;
        color: var(--text);
        text-decoration: none;
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: 999px;
        transition: border-color 0.2s ease, background 0.2s ease, color 0.2s ease;
        word-break: break-all;
      }
      .game-panel__git svg {
        flex-shrink: 0;
        width: 1.1rem;
        height: 1.1rem;
        color: var(--text-muted);
      }
      .game-panel__git:hover {
        border-color: var(--accent);
        color: var(--accent);
        background: var(--accent-soft);
      }
      .game-panel__git:hover svg {
        color: var(--accent);
      }

      .game-comments__composer {
        margin-bottom: 1.25rem;
      }
      .game-comments__label {
        display: block;
        margin-bottom: 0.45rem;
        font-size: 0.8125rem;
        font-weight: 600;
        color: var(--text-muted);
      }
      .game-comments__form {
        display: flex;
        flex-direction: column;
        gap: 0.65rem;
      }
      .game-comments__form textarea {
        width: 100%;
        box-sizing: border-box;
        min-height: 6.5rem;
        padding: 0.85rem 1rem;
        font-size: 0.875rem;
        font-family: inherit;
        line-height: 1.55;
        color: var(--text);
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: 12px;
        resize: vertical;
        transition: border-color 0.2s ease, box-shadow 0.2s ease;
      }
      .game-comments__form textarea::placeholder {
        color: var(--text-muted);
        opacity: 0.85;
      }
      .game-comments__form textarea:focus {
        outline: none;
        border-color: var(--accent);
        box-shadow: 0 0 0 4px rgba(232, 165, 75, 0.18);
      }
      .game-comments__actions {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        justify-content: space-between;
        gap: 0.5rem;
      }
      .game-comments__form button[type="submit"] {
        padding: 0.55rem 1.5rem;
        font-size: 0.875rem;
        font-weight: 700;
        color: #1a1a1a;
        background: linear-gradient(180deg, #f0c978 0%, var(--accent) 100%);
        border: none;
        border-radius: 10px;
        cursor: pointer;
        box-shadow: 0 2px 8px rgba(232, 165, 75, 0.35);
        transition: transform 0.15s ease, box-shadow 0.2s ease;
        -webkit-tap-highlight-color: transparent;
      }
      .game-comments__form button[type="submit"]:hover {
        box-shadow: 0 4px 14px rgba(232, 165, 75, 0.45);
      }
      .game-comments__form button[type="submit"]:active {
        transform: scale(0.98);
      }
      .game-comments__hint {
        font-size: 0.6875rem;
        color: var(--text-muted);
      }

      .game-comments__list {
        margin: 0;
        padding: 0;
        list-style: none;
        display: flex;
        flex-direction: column;
        gap: 0.85rem;
      }
      .game-comments__empty {
        margin: 0;
        padding: 1.5rem 1rem;
        font-size: 0.875rem;
        text-align: center;
        color: var(--text-muted);
        background: var(--surface);
        border-radius: 12px;
        border: 1px dashed var(--border);
      }
      .game-comments__item {
        display: flex;
        gap: 0.75rem;
        align-items: flex-start;
        padding: 0.85rem 1rem;
        font-size: 0.875rem;
        line-height: 1.55;
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: 14px;
        transition: border-color 0.2s ease;
      }
      .game-comments__item:hover {
        border-color: rgba(232, 165, 75, 0.2);
      }
      .game-comments__avatar {
        flex-shrink: 0;
        width: 2.25rem;
        height: 2.25rem;
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 0.75rem;
        font-weight: 800;
        color: var(--accent);
        background: var(--accent-soft);
        border: 1px solid rgba(232, 165, 75, 0.25);
      }
      .game-comments__body {
        flex: 1;
        min-width: 0;
      }
      .game-comments__meta {
        display: flex;
        flex-wrap: wrap;
        align-items: baseline;
        gap: 0.35rem 0.65rem;
        margin-bottom: 0.4rem;
      }
      .game-comments__nick {
        font-size: 0.75rem;
        font-weight: 700;
        letter-spacing: -0.02em;
        color: var(--text);
      }
      .game-comments__meta time {
        font-size: 0.6875rem;
        font-weight: 500;
        letter-spacing: 0.02em;
        color: var(--text-muted);
      }
      .game-comments__item p {
        margin: 0;
        white-space: pre-wrap;
        word-break: break-word;
        color: var(--text);
      }
      .game-comments__footer {
        margin-top: 0.5rem;
        display: flex;
        justify-content: flex-end;
      }
      .game-comments__delete {
        padding: 0.25rem 0.6rem;
        font-size: 0.6875rem;
        font-weight: 600;
        color: var(--text-muted);
        background: transparent;
        border: 1px solid transparent;
        border-radius: 6px;
        cursor: pointer;
        transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
      }
      .game-comments__delete:hover {
        color: #b33;
        border-color: rgba(180, 50, 50, 0.25);
        background: rgba(180, 50, 50, 0.06);
      }
      .sr-only {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        border: 0;
      }
      @media (max-width: 480px) {
        .game-panel {
          padding: 1.15rem 1.1rem 1.35rem;
        }
        .game-comments__actions {
          flex-direction: column-reverse;
          align-items: stretch;
        }
        .game-comments__form button[type="submit"] {
          width: 100%;
        }
        .game-comments__hint {
          text-align: center;
        }
        .game-reviews__form-buttons {
          width: 100%;
          flex-direction: column-reverse;
        }
        .game-reviews__cancel,
        .game-reviews__form-buttons #game-review-submit {
          width: 100%;
        }
      }

      /* ── 로그인 게이트 (덧글/리뷰 공통) ───────────────────── */
      .game-comments__login-gate {
        margin: 0 0 0.25rem;
        padding: 1.1rem 1rem;
        font-size: 0.875rem;
        text-align: center;
        color: var(--text-muted);
        background: var(--surface);
        border: 1px dashed var(--border);
        border-radius: 12px;
      }
      .game-comments__login-gate a {
        font-weight: 700;
        color: var(--accent);
        text-decoration: none;
        border-bottom: 1px solid rgba(232, 165, 75, 0.4);
      }
      .game-comments__login-gate a:hover {
        border-bottom-color: var(--accent);
      }

      /* ── 별점 위젯 ───────────────────────────────────────── */
      .game-stars {
        display: inline-flex;
        gap: 0.15rem;
        line-height: 1;
      }
      .game-stars__btn {
        padding: 0.1rem;
        font-size: 1.6rem;
        line-height: 1;
        color: var(--border);
        background: none;
        border: none;
        cursor: pointer;
        transition: color 0.12s ease, transform 0.12s ease;
        -webkit-tap-highlight-color: transparent;
      }
      .game-stars__btn:hover {
        transform: scale(1.12);
      }
      .game-stars__btn.is-on {
        color: var(--accent);
        text-shadow: 0 1px 4px rgba(232, 165, 75, 0.45);
      }
      .game-stars__btn:active {
        transform: scale(0.88);
      }
      .game-stars--input:focus-within .game-stars__btn:focus-visible {
        outline: 2px solid var(--accent);
        outline-offset: 2px;
        border-radius: 4px;
      }
      /* 읽기 전용 별 표시 (리뷰 카드/요약) */
      .game-stars--display,
      .game-stars--avg {
        gap: 0.05rem;
        font-size: 0.95rem;
      }
      .game-stars--display .game-star,
      .game-stars--avg .game-star {
        color: var(--border);
      }
      .game-stars--display .game-star.is-on,
      .game-stars--avg .game-star.is-on {
        color: var(--accent);
      }

      /* ── 리뷰 작성/수정 폼 ───────────────────────────────── */
      .game-reviews__composer {
        margin-bottom: 1.25rem;
        padding: 1.1rem 1.15rem 1.2rem;
        background:
          radial-gradient(140% 120% at 100% 0%, rgba(232, 165, 75, 0.16), transparent 60%),
          var(--accent-soft);
        border: 1px solid rgba(232, 165, 75, 0.22);
        border-radius: 14px;
      }
      .game-reviews__rating-row {
        display: flex;
        align-items: center;
        gap: 0.85rem;
        margin-bottom: 0.85rem;
      }
      .game-reviews__rating-row .game-comments__label {
        margin-bottom: 0;
      }
      .game-reviews__form-buttons {
        display: flex;
        gap: 0.5rem;
        align-items: center;
      }
      .game-reviews__cancel {
        padding: 0.55rem 1.1rem;
        font-size: 0.875rem;
        font-weight: 600;
        color: var(--text-muted);
        background: var(--card-bg);
        border: 1px solid var(--border);
        border-radius: 10px;
        cursor: pointer;
        transition: border-color 0.15s ease, color 0.15s ease;
      }
      .game-reviews__cancel:hover {
        color: var(--text);
        border-color: var(--accent);
      }

      /* ── 리뷰 요약 (평균 별점) ───────────────────────────── */
      .game-reviews__summary {
        margin-left: auto;
        display: flex;
        align-items: center;
        gap: 0.5rem;
      }
      .game-reviews__avg {
        font-size: 1.35rem;
        font-weight: 800;
        letter-spacing: -0.02em;
        color: var(--accent);
      }
      .game-reviews__count {
        font-size: 0.75rem;
        font-weight: 600;
        color: var(--text-muted);
      }

      /* ── 리뷰 목록 (덧글 카드 패턴 확장) ─────────────────── */
      .game-reviews__list {
        margin: 0;
        padding: 0;
        list-style: none;
        display: flex;
        flex-direction: column;
        gap: 0.85rem;
      }
      .game-reviews__item {
        padding: 0.95rem 1.1rem;
        font-size: 0.875rem;
        line-height: 1.55;
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: 14px;
        transition: border-color 0.2s ease;
      }
      .game-reviews__item.is-mine {
        border-color: rgba(232, 165, 75, 0.4);
        background: var(--accent-soft);
      }
      .game-reviews__item-head {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 0.4rem 0.65rem;
        margin-bottom: 0.5rem;
      }
      .game-reviews__nick {
        font-size: 0.8125rem;
        font-weight: 700;
        letter-spacing: -0.02em;
        color: var(--text);
      }
      .game-reviews__mine-badge {
        padding: 0.05rem 0.45rem;
        font-size: 0.625rem;
        font-weight: 700;
        color: var(--accent);
        background: rgba(232, 165, 75, 0.16);
        border-radius: 999px;
      }
      .game-reviews__edited {
        font-size: 0.6875rem;
        font-weight: 500;
        color: var(--text-muted);
      }
      .game-reviews__time {
        margin-left: auto;
        font-size: 0.6875rem;
        font-weight: 500;
        letter-spacing: 0.02em;
        color: var(--text-muted);
      }
      .game-reviews__body {
        margin: 0;
        white-space: pre-wrap;
        word-break: break-word;
        color: var(--text);
      }
      .game-reviews__footer {
        margin-top: 0.6rem;
        display: flex;
        justify-content: flex-end;
        gap: 0.4rem;
      }
      .game-reviews__action {
        padding: 0.25rem 0.6rem;
        font-size: 0.6875rem;
        font-weight: 600;
        color: var(--text-muted);
        background: transparent;
        border: 1px solid transparent;
        border-radius: 6px;
        cursor: pointer;
        transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
      }
      .game-reviews__action--edit:hover {
        color: var(--accent);
        border-color: rgba(232, 165, 75, 0.3);
        background: var(--accent-soft);
      }
      .game-reviews__action--delete:hover {
        color: #b33;
        border-color: rgba(180, 50, 50, 0.25);
        background: rgba(180, 50, 50, 0.06);
      }
      /* 덧글 수정 폼 (인라인) */
      .game-comments__edit-form {
        margin-top: 0.6rem;
        display: flex;
        flex-direction: column;
        gap: 0.5rem;
      }
      .game-comments__edit-form textarea {
        width: 100%;
        box-sizing: border-box;
        min-height: 4.5rem;
        padding: 0.65rem 0.85rem;
        font-size: 0.875rem;
        font-family: inherit;
        line-height: 1.55;
        color: var(--text);
        background: var(--card-bg);
        border: 1px solid var(--border);
        border-radius: 10px;
        resize: vertical;
      }
      .game-comments__edit-form textarea:focus {
        outline: none;
        border-color: var(--accent);
        box-shadow: 0 0 0 3px rgba(232, 165, 75, 0.18);
      }

      /* ── 6축 평가 입력 그리드 ─────────────────────────────── */
      .game-reviews__axes {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 0.5rem 1.1rem;
        margin-bottom: 0.85rem;
      }
      @media (max-width: 480px) {
        .game-reviews__axes {
          grid-template-columns: 1fr;
        }
      }
      .game-reviews__axis {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 0.5rem;
        padding: 0.3rem 0.5rem;
        border-radius: 10px;
        transition: background 0.15s ease;
      }
      .game-reviews__axis:hover {
        background: rgba(232, 165, 75, 0.08);
      }
      .game-reviews__axis-label {
        font-size: 0.8125rem;
        font-weight: 600;
        color: var(--text);
      }
      .game-stars--axis {
        gap: 0.1rem;
      }
      .game-stars--axis .game-stars__btn {
        font-size: 1.25rem;
      }

      /* ── 육각형 레이더 ───────────────────────────────────── */
      .game-reviews__summary-panel {
        margin: 0 0 1.25rem;
        display: flex;
        align-items: center;
        gap: 1.25rem;
        padding: 1rem 1.25rem;
        background:
          radial-gradient(125% 125% at 0% 0%, var(--accent-soft), transparent 62%),
          var(--card-bg);
        border: 1px solid rgba(232, 165, 75, 0.22);
        border-radius: var(--radius);
        box-shadow: var(--panel-shadow);
      }
      @media (max-width: 480px) {
        .game-reviews__summary-panel {
          flex-direction: column;
          align-items: stretch;
          gap: 0.85rem;
        }
        .game-reviews__radar { justify-content: center; }
      }
      .game-reviews__radar {
        display: inline-flex;
        flex-shrink: 0;
      }
      .game-reviews__radar svg {
        display: block;
      }
      /* drop-shadow 가 viewBox 밖으로 번질 수 있어 클립 방지 */
      .game-axis-radar {
        overflow: visible;
      }
      .game-reviews__radar .game-axis-grid {
        fill: none;
        stroke: var(--border);
        stroke-width: 1;
      }
      .game-reviews__radar .game-axis-grid--outer {
        stroke: rgba(232, 165, 75, 0.4);
        stroke-width: 1.25;
      }
      .game-reviews__radar .game-axis-line {
        stroke: var(--border);
        stroke-width: 1;
        stroke-dasharray: 2 3;
      }
      /* fill 은 JS 가 url(#gradient) 프레젠테이션 속성으로 지정 — 여기서 fill 지정 금지(우선순위 역전) */
      .game-reviews__radar .game-axis-poly {
        stroke: var(--accent);
        stroke-width: 1.75;
        stroke-linejoin: round;
        filter: drop-shadow(0 2px 7px rgba(232, 165, 75, 0.38));
        transform-box: fill-box;
        transform-origin: center;
        animation: game-radar-pop 0.55s cubic-bezier(0.22, 1, 0.36, 1) both;
      }
      .game-reviews__radar .game-axis-dot {
        fill: var(--accent);
        stroke: var(--card-bg);
        stroke-width: 1.5;
        animation: game-radar-fade 0.7s ease both;
      }
      @keyframes game-radar-pop {
        from { opacity: 0; transform: scale(0.55); }
        to   { opacity: 1; transform: scale(1); }
      }
      @keyframes game-radar-fade {
        from { opacity: 0; }
        to   { opacity: 1; }
      }
      .game-reviews__radar-empty {
        font-size: 0.75rem;
        color: var(--text-muted);
      }
      .game-reviews__axis-legend {
        margin: 0;
        flex: 1;
        min-width: 11.5rem;
        display: grid;
        gap: 0.4rem 0;
        font-size: 0.75rem;
        color: var(--text-muted);
      }
      .game-reviews__axis-legend-row {
        display: grid;
        grid-template-columns: 3.4rem 1fr 1.15rem;
        align-items: center;
        gap: 0.55rem;
      }
      .game-reviews__axis-legend dt {
        font-weight: 600;
        color: var(--text);
      }
      .game-reviews__axis-legend dd {
        margin: 0;
        font-variant-numeric: tabular-nums;
        font-weight: 700;
        text-align: right;
        color: var(--accent);
      }
      .game-reviews__axis-meter {
        height: 0.4rem;
        border-radius: 999px;
        background: var(--border);
        overflow: hidden;
      }
      .game-reviews__axis-meter span {
        display: block;
        height: 100%;
        border-radius: 999px;
        background: linear-gradient(90deg, #f0c978, var(--accent));
        animation: game-meter-grow 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;
        transform-origin: left;
      }
      @keyframes game-meter-grow {
        from { transform: scaleX(0); }
        to   { transform: scaleX(1); }
      }
      @media (prefers-reduced-motion: reduce) {
        .game-reviews__radar .game-axis-poly,
        .game-reviews__radar .game-axis-dot,
        .game-reviews__axis-meter span { animation: none; }
      }
      .game-reviews__card-radar {
        margin-top: 0.6rem;
        display: inline-flex;
      }
      .game-reviews__card-radar svg {
        display: block;
      }

      /* ── sort 토글 / 더보기 ──────────────────────────────── */
      .game-comments__toolbar {
        display: flex;
        justify-content: flex-end;
        margin-bottom: 0.85rem;
      }
      .game-comments__sort {
        display: inline-flex;
        gap: 0.25rem;
        padding: 0.2rem;
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: 999px;
      }
      .game-comments__sort-btn {
        padding: 0.3rem 0.7rem;
        font-size: 0.6875rem;
        font-weight: 700;
        color: var(--text-muted);
        background: transparent;
        border: none;
        border-radius: 999px;
        cursor: pointer;
        transition: color 0.15s ease, background 0.15s ease;
      }
      .game-comments__sort-btn.is-active {
        color: #1a1a1a;
        background: linear-gradient(180deg, #f0c978, var(--accent));
        box-shadow: 0 1px 5px rgba(232, 165, 75, 0.4);
      }
      html[data-theme="dark"] .game-comments__sort-btn.is-active {
        color: #1a1a1a;
      }
      .game-comments__more {
        margin-top: 1rem;
        display: flex;
        justify-content: center;
      }
      .game-comments__more-btn {
        padding: 0.5rem 1.4rem;
        font-size: 0.8125rem;
        font-weight: 700;
        color: var(--text);
        background: var(--card-bg);
        border: 1px solid var(--border);
        border-radius: 999px;
        cursor: pointer;
        transition: border-color 0.15s ease, color 0.15s ease;
      }
      .game-comments__more-btn:hover {
        border-color: var(--accent);
        color: var(--accent);
        transform: translateY(-1px);
        box-shadow: 0 5px 16px rgba(232, 165, 75, 0.16);
      }
      @media (prefers-reduced-motion: reduce) {
        .game-comments__more-btn:hover { transform: none; }
      }

      /* ── 제출 잠금/로딩 ──────────────────────────────────── */
      .game-comments__hint.is-warn {
        color: #b33;
        font-weight: 700;
      }
      button[aria-busy="true"] {
        opacity: 0.65;
        cursor: progress;
      }
      .game-comments__edited {
        margin-left: 0.35rem;
        font-size: 0.6875rem;
        font-weight: 500;
        color: var(--text-muted);
      }
    </style>
</head>
<body>
    <jsp:include page="/WEB-INF/views/header.jsp"/>
    <main class="game-page">
        <div class="game-topbar">
            <a class="game-back" href="<%= ctx %>/">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
                목록으로
            </a>
            <% if (owner) { %>
            <div class="game-owner-actions" aria-label="게시물 관리">
                <a class="game-owner-button" href="<%= ctx %>/game/<%= gameIdValue %>/edit">수정</a>
                <button type="button" class="game-owner-button game-owner-button--danger" id="game-delete-button">삭제</button>
            </div>
            <% } %>
        </div>

        <section class="game-info-card" aria-labelledby="game-title">
            <h1 id="game-title"><%= gameNameValue %></h1>
            <div class="game-meta-grid">
                <div class="game-meta-item">
                    <div class="game-meta-item__row">
                        <span class="game-meta-item__icon" aria-hidden="true">
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 9h16M4 15h16M10 3L8 21M16 3l-2 18"/></svg>
                        </span>
                        <span class="game-meta-item__label">번호</span>
                    </div>
                    <span class="game-meta-item__value">#<%= gameIdValue %></span>
                </div>
                <div class="game-meta-item">
                    <div class="game-meta-item__row">
                        <span class="game-meta-item__icon" aria-hidden="true">
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
                        </span>
                        <span class="game-meta-item__label">제작자</span>
                    </div>
                    <span class="game-meta-item__value"><%= creatorValue %></span>
                </div>
                <div class="game-meta-item game-meta-item--likes">
                    <div class="game-meta-item__row">
                        <span class="game-meta-item__icon" aria-hidden="true">
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"/></svg>
                        </span>
                        <span class="game-meta-item__label">좋아요</span>
                    </div>
                    <button type="button" id="game-like-btn" class="game-meta-like-btn" aria-pressed="false" aria-label="좋아요">
                        <svg class="game-meta-like-btn__heart" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
                            <path d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"/>
                        </svg>
                        <span id="game-like-count" class="game-meta-like-btn__count"><%= likeCountFormattedValue %></span>
                    </button>
                </div>
            </div>
        </section>

        <section class="game-webgl" aria-labelledby="webgl-heading">
            <div class="game-webgl__head">
                <span class="game-webgl__icon" aria-hidden="true">
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="5 3 19 12 5 21 5 3"/></svg>
                </span>
                <h2 id="webgl-heading">플레이</h2>
                <span class="game-webgl__badge">WebGL</span>
            </div>
            <div class="game-webgl__shell">
                <div class="game-webgl__frame-wrap">
                    <iframe title="<%= gameNameValue %> WebGL"
                            src="<%= webglFrameSrcValue %>"
                            sandbox="allow-scripts allow-same-origin allow-pointer-lock"
                            allow="fullscreen; autoplay; xr-spatial-tracking"
                            loading="eager"
                            referrerpolicy="same-origin"></iframe>
                </div>
            </div>
        </section>

        <div class="game-community">
            <section class="game-panel game-panel--creator" aria-labelledby="creator-heading">
                <div class="game-panel__head">
                    <span class="game-panel__icon" aria-hidden="true">
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"/><path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"/></svg>
                    </span>
                    <div>
                        <h2 id="creator-heading" class="game-panel__title">제작자의 한마디</h2>
                        <p class="game-panel__subtitle">이 게임을 만들며 전하고 싶었던 이야기예요.</p>
                    </div>
                </div>
                <blockquote class="game-panel__quote"><%= creatorNoteValue %></blockquote>
                <div class="game-panel__git-wrap">
                    <a class="game-panel__git" href="<%= gitUrlValue %>" target="_blank" rel="noopener noreferrer">
                        <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
                        소스 코드 · GitHub
                    </a>
                </div>
            </section>

            <section class="game-panel game-panel--reviews" aria-labelledby="reviews-heading">
                <div class="game-panel__head">
                    <span class="game-panel__icon" aria-hidden="true">
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2l2.9 6.2 6.8.9-5 4.7 1.3 6.7L12 17.8 5.9 21.2 7.2 14.5l-5-4.7 6.8-.9z"/></svg>
                    </span>
                    <div>
                        <h2 id="reviews-heading" class="game-panel__title">리뷰</h2>
                        <p class="game-panel__subtitle">별점과 함께 플레이 평가를 남겨 주세요. 게임당 한 번 작성할 수 있어요.</p>
                    </div>
                    <div class="game-reviews__summary" id="game-reviews-summary" hidden>
                        <span class="game-reviews__avg" id="game-reviews-avg">–</span>
                        <div class="game-stars game-stars--avg" id="game-reviews-avg-stars" aria-hidden="true"></div>
                        <span class="game-reviews__count" id="game-reviews-count"></span>
                    </div>
                </div>
                <div class="game-reviews__summary-panel" id="game-reviews-summary-panel" hidden>
                    <div class="game-reviews__radar" id="game-reviews-summary-radar"></div>
                </div>
                <div class="game-reviews__composer" id="game-review-composer" hidden>
                    <form id="game-review-form" class="game-comments__form" novalidate>
                        <div class="game-reviews__axes" id="game-review-axes" aria-label="6축 평가"></div>
                        <div class="game-reviews__rating-row">
                            <span class="game-comments__label" id="game-review-stars-label">종합 별점 (선택)</span>
                            <div class="game-stars game-stars--input" id="game-review-stars" role="radiogroup" aria-labelledby="game-review-stars-label"></div>
                        </div>
                        <label class="game-comments__label" for="game-review-input">평가</label>
                        <textarea id="game-review-input" name="body" rows="4" maxlength="1000" placeholder="이 게임을 어떻게 즐기셨나요? (최소 10자)"></textarea>
                        <div class="game-comments__actions">
                            <span class="game-comments__hint" id="game-review-hint"><span id="game-review-counter">0/1000</span> · 6축은 필수, 종합 별점은 선택이에요</span>
                            <div class="game-reviews__form-buttons">
                                <button type="button" class="game-reviews__cancel" id="game-review-cancel" hidden>취소</button>
                                <button type="submit" id="game-review-submit">리뷰 등록</button>
                            </div>
                        </div>
                    </form>
                </div>
                <p class="game-comments__login-gate" id="game-review-login-gate" hidden>리뷰를 남기려면 <a href="<%= ctx %>/login">로그인</a>이 필요해요.</p>
                <div class="game-comments__toolbar" id="game-reviews-toolbar" hidden>
                    <div class="game-comments__sort" role="group" aria-label="리뷰 정렬">
                        <button type="button" class="game-comments__sort-btn is-active" data-sort="newest" aria-pressed="true">최신순</button>
                        <button type="button" class="game-comments__sort-btn" data-sort="rating_desc" aria-pressed="false">별점 높은순</button>
                        <button type="button" class="game-comments__sort-btn" data-sort="rating_asc" aria-pressed="false">별점 낮은순</button>
                    </div>
                </div>
                <p id="game-reviews-empty" class="game-comments__empty" hidden>아직 리뷰가 없습니다.<br>첫 번째 리뷰를 남겨 보세요.</p>
                <ul id="game-review-list" class="game-reviews__list" aria-labelledby="reviews-heading"></ul>
                <div class="game-comments__more" hidden>
                    <button type="button" class="game-comments__more-btn" id="game-reviews-more">더보기</button>
                </div>
            </section>

            <section class="game-panel game-panel--comments" aria-labelledby="comments-heading">
                <div class="game-panel__head">
                    <span class="game-panel__icon" aria-hidden="true">
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z"/></svg>
                    </span>
                    <div>
                        <h2 id="comments-heading" class="game-panel__title">덧글</h2>
                        <p class="game-panel__subtitle">플레이 소감이나 버그 제보를 남겨 주세요.</p>
                    </div>
                </div>
                <div class="game-comments__composer" id="game-comment-composer" hidden>
                    <form id="game-comment-form" class="game-comments__form" novalidate>
                        <label class="game-comments__label" for="game-comment-input">내용</label>
                        <textarea id="game-comment-input" name="comment" rows="4" maxlength="200" placeholder="여기에 덧글을 작성해 주세요." required></textarea>
                        <div class="game-comments__actions">
                            <span class="game-comments__hint"><span id="game-comment-counter">0/200</span></span>
                            <button type="submit" id="game-comment-submit">덧글 등록</button>
                        </div>
                    </form>
                </div>
                <p class="game-comments__login-gate" id="game-comment-login-gate" hidden>덧글을 남기려면 <a href="<%= ctx %>/login">로그인</a>이 필요해요.</p>
                <div class="game-comments__toolbar" id="game-comments-toolbar" hidden>
                    <div class="game-comments__sort" role="group" aria-label="덧글 정렬">
                        <button type="button" class="game-comments__sort-btn is-active" data-sort="oldest" aria-pressed="true">등록순</button>
                        <button type="button" class="game-comments__sort-btn" data-sort="newest" aria-pressed="false">최신순</button>
                    </div>
                </div>
                <p id="game-comments-empty" class="game-comments__empty" hidden>아직 덧글이 없습니다.<br>첫 번째 덧글을 남겨 보세요.</p>
                <ul id="game-comment-list" class="game-comments__list" aria-labelledby="comments-heading"></ul>
                <div class="game-comments__more" hidden>
                    <button type="button" class="game-comments__more-btn" id="game-comments-more">더보기</button>
                </div>
            </section>
        </div>
    </main>
    <jsp:include page="/WEB-INF/views/footer.jsp"/>

    <script>
(function () {
  var ctx = '${pageContext.request.contextPath}';
  var gameId = ${gameId};
  var baseLikes = ${likeCount};
  var LIKE_KEY = 'bibimbap-game-liked';
  var viewerId = ${empty currentUserId ? 'null' : currentUserId};
  var viewerRole = '${empty userRole ? "" : userRole}';

  function getLikedMap() {
    try {
      var raw = localStorage.getItem(LIKE_KEY);
      var o = raw ? JSON.parse(raw) : {};
      return o && typeof o === 'object' ? o : {};
    } catch (e) {
      return {};
    }
  }

  function setLiked(gameIdStr, liked) {
    var m = getLikedMap();
    if (liked) m[gameIdStr] = true;
    else delete m[gameIdStr];
    try {
      localStorage.setItem(LIKE_KEY, JSON.stringify(m));
    } catch (err) {}
  }

  function isLiked(gameIdStr) {
    return !!getLikedMap()[gameIdStr];
  }

  function formatCount(n) {
    return n.toLocaleString('ko-KR');
  }

  var likeBtn = document.getElementById('game-like-btn');
  var likeCountEl = document.getElementById('game-like-count');
  var gid = String(gameId);
  var deleteBtn = document.getElementById('game-delete-button');

  function deleteGame() {
    fetch(ctx + '/game/' + encodeURIComponent(gid), {
      method: 'DELETE',
      headers: window.BibimbapCsrf ? window.BibimbapCsrf.headers({
        'Accept': 'application/json',
        'X-Requested-With': 'XMLHttpRequest'
      }) : {
        'Accept': 'application/json',
        'X-Requested-With': 'XMLHttpRequest'
      }
    }).then(function (res) {
      return res.json().catch(function () {
        return { message: '게시물을 삭제하지 못했습니다.' };
      }).then(function (data) {
        if (!res.ok) {
          throw new Error(data && data.message ? data.message : '게시물을 삭제하지 못했습니다.');
        }
        return data;
      });
    }).then(function () {
      window.location.href = ctx + '/';
    }).catch(function (err) {
      if (window.BibimbapModal && typeof window.BibimbapModal.alert === 'function') {
        window.BibimbapModal.alert({
          title: '삭제 실패',
          message: err.message || '게시물을 삭제하지 못했습니다.',
          confirmText: '확인'
        });
      } else {
        alert(err.message || '게시물을 삭제하지 못했습니다.');
      }
    });
  }

  if (deleteBtn) {
    deleteBtn.addEventListener('click', function () {
      if (window.BibimbapModal && typeof window.BibimbapModal.confirm === 'function') {
        window.BibimbapModal.confirm({
          title: '게시물 삭제',
          message: '삭제하시겠습니까? 삭제 후 복구가 불가능합니다.',
          confirmText: '삭제',
          cancelText: '취소',
          onConfirm: deleteGame
        });
      } else if (confirm('게시물을 삭제할까요?')) {
        deleteGame();
      }
    });
  }

  function syncLike() {
    var liked = isLiked(gid);
    likeBtn.setAttribute('aria-pressed', liked ? 'true' : 'false');
    likeBtn.setAttribute('aria-label', liked ? '좋아요 취소' : '좋아요');
    likeCountEl.textContent = formatCount(baseLikes + (liked ? 1 : 0));
  }

  likeBtn.addEventListener('click', function () {
    var next = !isLiked(gid);
    setLiked(gid, next);
    syncLike();
  });
  syncLike();

  // ===== 공통 헬퍼 (서버 연동 덧글/리뷰) =====
  var AXIS_KEYS = ['immersion', 'creativity', 'controls', 'completeness', 'sound', 'visual'];
  var AXIS_LABELS_KO = {
    immersion: '몰입성', creativity: '창의성', controls: '조작성',
    completeness: '완성도', sound: '사운드', visual: '비주얼'
  };
  var SVG_NS = 'http://www.w3.org/2000/svg';
  var labelSeq = 0;

  function fmtAbsolute(iso) {
    try { return iso ? new Date(iso).toLocaleString('ko-KR') : ''; }
    catch (e) { return ''; }
  }
  // 상대시각: 7일 이내는 "n분/시간/일 전", 초과는 절대 날짜
  function fmtRelative(iso) {
    if (!iso) return '';
    var d = new Date(iso);
    if (isNaN(d.getTime())) return '';
    var diff = Date.now() - d.getTime();
    if (diff < 0) diff = 0;
    var sec = Math.floor(diff / 1000);
    if (sec < 60) return '방금 전';
    var min = Math.floor(sec / 60);
    if (min < 60) return min + '분 전';
    var hr = Math.floor(min / 60);
    if (hr < 24) return hr + '시간 전';
    var day = Math.floor(hr / 24);
    if (day <= 7) return day + '일 전';
    return d.toLocaleDateString('ko-KR');
  }
  // edited 면 "(수정됨)" 뱃지 동반. <time datetime=ISO title=절대시각>
  function buildTimeEl(iso, edited) {
    var t = document.createElement('time');
    t.dateTime = iso || '';
    t.title = fmtAbsolute(iso);
    t.textContent = fmtRelative(iso);
    if (!edited) return t;
    var frag = document.createDocumentFragment();
    frag.appendChild(t);
    var badge = document.createElement('span');
    badge.className = 'game-comments__edited';
    badge.textContent = '(수정됨)';
    frag.appendChild(badge);
    return frag;
  }
  function notifyError(title, message) {
    if (window.BibimbapModal && typeof window.BibimbapModal.alert === 'function') {
      window.BibimbapModal.alert({ title: title, message: message, confirmText: '확인' });
    } else { alert(message); }
  }
  function confirmAction(title, message, okText, onOk) {
    if (window.BibimbapModal && typeof window.BibimbapModal.confirm === 'function') {
      window.BibimbapModal.confirm({ title: title, message: message, confirmText: okText, cancelText: '취소', onConfirm: onOk });
    } else if (confirm(message)) { onOk(); }
  }
  function baseHeaders(extra) {
    var h = { 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' };
    if (extra) { Object.keys(extra).forEach(function (k) { h[k] = extra[k]; }); }
    return window.BibimbapCsrf ? window.BibimbapCsrf.headers(h) : h;
  }
  function formHeaders() {
    return baseHeaders({ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' });
  }
  function encodeForm(obj) {
    return Object.keys(obj).map(function (k) {
      return encodeURIComponent(k) + '=' + encodeURIComponent(obj[k] == null ? '' : obj[k]);
    }).join('&');
  }
  function api(url, opts) {
    return fetch(url, opts).then(function (res) {
      return res.json().catch(function () { return {}; }).then(function (data) {
        if (!res.ok) { throw new Error((data && data.message) || ('요청을 처리하지 못했습니다. (' + res.status + ')')); }
        return data;
      });
    });
  }
  // 본인(작성자) 또는 운영자(ADMIN)만 수정/삭제 버튼 노출 — 서버가 최종 권한 재검증
  function canActOn(authorUserId) {
    if (viewerId == null) return false;
    if (authorUserId != null && Number(authorUserId) === Number(viewerId)) return true;
    return viewerRole === 'ADMIN';
  }

  // 제출 버튼 잠금/로딩 — 연타 차단. lock() 호출 후 unlock() 으로 해제
  function lockButton(btn) {
    if (!btn) return function () {};
    var origText = btn.textContent;
    btn.disabled = true;
    btn.setAttribute('aria-busy', 'true');
    btn.textContent = '처리 중…';
    return function () {
      btn.disabled = false;
      btn.removeAttribute('aria-busy');
      btn.textContent = origText;
    };
  }

  // ===== 육각형 레이더 SVG (요약/카드 공용) =====
  // scores: AXIS_KEYS 순 6값(1~5, null/0=데이터 없음). cx/cy/R 좌표.
  var hexSeq = 0; // svg 별 그라디언트 id 고유화
  function buildHexRadar(scores, cx, cy, R) {
    var canvas = cx * 2; // cx==cy 가정. 요약 200 / 카드 120
    var svg = document.createElementNS(SVG_NS, 'svg');
    svg.setAttribute('viewBox', '0 0 ' + canvas + ' ' + canvas);
    svg.setAttribute('width', String(Math.round(canvas * 0.85)));
    svg.setAttribute('height', String(Math.round(canvas * 0.85)));
    svg.setAttribute('role', 'img');
    svg.setAttribute('class', 'game-axis-radar');

    var nums = AXIS_KEYS.map(function (k, i) {
      var v = scores ? Number(scores[i]) : 0;
      return (isFinite(v) && v > 0) ? v : 0;
    });
    var hasData = nums.some(function (v) { return v > 0; });

    function ptOnAxis(i, ratio) {
      var ang = (-90 + 60 * i) * Math.PI / 180;
      return {
        x: cx + R * ratio * Math.cos(ang),
        y: cy + R * ratio * Math.sin(ang)
      };
    }
    function polyPoints(ratioFn) {
      return AXIS_KEYS.map(function (k, i) {
        var p = ptOnAxis(i, ratioFn(i));
        return p.x.toFixed(2) + ',' + p.y.toFixed(2);
      }).join(' ');
    }

    // 채움용 방사형 앰버 그라디언트 (svg 별 고유 id — 한 페이지 다중 레이더 충돌 방지)
    var gid = 'hexfill-' + (++hexSeq);
    var defs = document.createElementNS(SVG_NS, 'defs');
    var grad = document.createElementNS(SVG_NS, 'radialGradient');
    grad.setAttribute('id', gid);
    grad.setAttribute('cx', '50%');
    grad.setAttribute('cy', '50%');
    grad.setAttribute('r', '62%');
    [['0%', 'rgba(240, 201, 120, 0.55)'], ['100%', 'rgba(232, 165, 75, 0.26)']].forEach(function (s) {
      var st = document.createElementNS(SVG_NS, 'stop');
      st.setAttribute('offset', s[0]);
      st.setAttribute('stop-color', s[1]);
      grad.appendChild(st);
    });
    defs.appendChild(grad);
    svg.appendChild(defs);

    // 동심 그리드 (4단계, 외곽 강조)
    for (var level = 1; level <= 4; level++) {
      var grid = document.createElementNS(SVG_NS, 'polygon');
      grid.setAttribute('class', 'game-axis-grid' + (level === 4 ? ' game-axis-grid--outer' : ''));
      (function (lv) {
        grid.setAttribute('points', polyPoints(function () { return lv / 4; }));
      })(level);
      svg.appendChild(grid);
    }
    // 축선
    AXIS_KEYS.forEach(function (k, i) {
      var line = document.createElementNS(SVG_NS, 'line');
      line.setAttribute('class', 'game-axis-line');
      var p = ptOnAxis(i, 1);
      line.setAttribute('x1', String(cx));
      line.setAttribute('y1', String(cy));
      line.setAttribute('x2', p.x.toFixed(2));
      line.setAttribute('y2', p.y.toFixed(2));
      svg.appendChild(line);
    });
    // score 폴리곤 + 꼭지점 점 (데이터 있을 때만)
    if (hasData) {
      var poly = document.createElementNS(SVG_NS, 'polygon');
      poly.setAttribute('class', 'game-axis-poly');
      poly.setAttribute('points', polyPoints(function (i) { return nums[i] / 5; }));
      poly.setAttribute('fill', 'url(#' + gid + ')');
      svg.appendChild(poly);
      AXIS_KEYS.forEach(function (k, i) {
        if (nums[i] <= 0) return;
        var p = ptOnAxis(i, nums[i] / 5);
        var dot = document.createElementNS(SVG_NS, 'circle');
        dot.setAttribute('class', 'game-axis-dot');
        dot.setAttribute('cx', p.x.toFixed(2));
        dot.setAttribute('cy', p.y.toFixed(2));
        dot.setAttribute('r', Math.max(1.6, R * 0.05).toFixed(2));
        svg.appendChild(dot);
      });
    }

    // a11y aria-label
    if (hasData) {
      svg.setAttribute('aria-label',
        '6축 평가: ' + AXIS_KEYS.map(function (k, i) {
          return AXIS_LABELS_KO[k] + ' ' + nums[i];
        }).join(', '));
    } else {
      svg.setAttribute('aria-label', '6축 평가 없음');
    }
    return { svg: svg, nums: nums, hasData: hasData };
  }

  // 시각 보조 <dl> (축명/미터바/점수) — 폴리곤만으로 종료 금지(§6.6)
  function buildRadarLegend(nums, hasData) {
    var dl = document.createElement('dl');
    dl.className = 'game-reviews__axis-legend';
    AXIS_KEYS.forEach(function (k, i) {
      var row = document.createElement('div');
      row.className = 'game-reviews__axis-legend-row';
      var dt = document.createElement('dt');
      dt.textContent = AXIS_LABELS_KO[k];
      var meter = document.createElement('div');
      meter.className = 'game-reviews__axis-meter';
      var bar = document.createElement('span');
      bar.style.width = (hasData ? (nums[i] / 5 * 100) : 0) + '%';
      meter.appendChild(bar);
      var dd = document.createElement('dd');
      dd.textContent = hasData ? String(nums[i]) : '–';
      row.appendChild(dt);
      row.appendChild(meter);
      row.appendChild(dd);
      dl.appendChild(row);
    });
    return dl;
  }

  // ===== 별점 radiogroup 완전 패턴 (C6) =====
  // axisScores: axisKey 별 선택값(0=미선택). buildStarRadioGroup 으로 위젯 생성.
  var axisScores = {};
  function buildStarRadioGroup(axisKey, labelText) {
    var labelId = 'star-label-' + axisKey + '-' + (++labelSeq);
    var group = document.createElement('div');
    group.className = 'game-stars game-stars--input game-stars--axis';
    group.setAttribute('role', 'radiogroup');
    group.setAttribute('aria-labelledby', labelId);

    var radios = [];
    function paint() {
      var sel = axisScores[axisKey] || 0;
      radios.forEach(function (b, idx) {
        var v = idx + 1;
        b.classList.toggle('is-on', v <= sel);
        b.setAttribute('aria-checked', v === sel ? 'true' : 'false');
        b.tabIndex = (v === sel) ? 0 : -1;
      });
      if (!sel) radios[0].tabIndex = 0; // 미선택 시 첫 radio tabindex=0
    }
    function select(v) {
      axisScores[axisKey] = v;
      paint();
    }

    for (var i = 1; i <= 5; i++) {
      (function (v) {
        var b = document.createElement('button');
        b.type = 'button';
        b.className = 'game-stars__btn';
        b.setAttribute('role', 'radio');
        b.setAttribute('aria-checked', 'false');
        b.setAttribute('aria-label', labelText + ' ' + v + '점');
        b.tabIndex = -1;
        b.textContent = '★';
        b.addEventListener('click', function () { select(v); b.focus(); });
        b.addEventListener('keydown', function (ev) {
          var cur = axisScores[axisKey] || 0;
          var next = null;
          switch (ev.key) {
            case 'ArrowRight': case 'ArrowUp': next = Math.min(5, (cur || v) + 1); break;
            case 'ArrowLeft': case 'ArrowDown': next = Math.max(1, (cur || v) - 1); break;
            case 'Home': next = 1; break;
            case 'End': next = 5; break;
            case ' ': case 'Spacebar': case 'Enter': next = v; break;
            default: return;
          }
          ev.preventDefault();
          select(next);
          radios[next - 1].focus();
        });
        radios.push(b);
        group.appendChild(b);
      })(i);
    }
    paint();
    return { group: group, labelId: labelId, set: select, paint: paint };
  }

  // ===== 덧글 (서버 연동) =====
  var cListEl = document.getElementById('game-comment-list');
  var cEmptyEl = document.getElementById('game-comments-empty');
  var cForm = document.getElementById('game-comment-form');
  var cInput = document.getElementById('game-comment-input');
  var cSubmit = document.getElementById('game-comment-submit');
  var cCounter = document.getElementById('game-comment-counter');
  var cComposer = document.getElementById('game-comment-composer');
  var cLoginGate = document.getElementById('game-comment-login-gate');
  var cToolbar = document.getElementById('game-comments-toolbar');
  var cMoreBtn = document.getElementById('game-comments-more');
  var cMoreWrap = cMoreBtn ? cMoreBtn.parentElement : null;
  var commentsUrl = ctx + '/game/' + encodeURIComponent(gid) + '/comments';
  var currentCommentPage = 0;
  var currentCommentSort = 'oldest';
  var COMMENT_MAX = 200;

  function openCommentEdit(li, body, p, c) {
    if (li.querySelector('.game-comments__edit-form')) return;
    var ef = document.createElement('form');
    ef.className = 'game-comments__edit-form';
    var ta = document.createElement('textarea');
    ta.maxLength = 200;
    ta.value = c.content || '';
    var actions = document.createElement('div');
    actions.className = 'game-comments__actions';
    var hint = document.createElement('span');
    hint.className = 'game-comments__hint';
    hint.textContent = '최대 200자';
    var btnWrap = document.createElement('div');
    btnWrap.className = 'game-reviews__form-buttons';
    var cancel = document.createElement('button');
    cancel.type = 'button';
    cancel.className = 'game-reviews__cancel';
    cancel.textContent = '취소';
    var save = document.createElement('button');
    save.type = 'submit';
    save.textContent = '저장';
    btnWrap.appendChild(cancel);
    btnWrap.appendChild(save);
    actions.appendChild(hint);
    actions.appendChild(btnWrap);
    ef.appendChild(ta);
    ef.appendChild(actions);
    p.hidden = true;
    body.appendChild(ef);
    ta.focus();
    cancel.addEventListener('click', function () { ef.remove(); p.hidden = false; });
    ta.addEventListener('input', function () { hint.textContent = ta.value.length + '/' + COMMENT_MAX; });
    hint.textContent = ta.value.length + '/' + COMMENT_MAX;
    ef.addEventListener('submit', function (ev) {
      ev.preventDefault();
      var text = (ta.value || '').trim();
      if (!text) { notifyError('입력 필요', '덧글 내용을 입력해 주세요.'); return; }
      if (text.length > COMMENT_MAX) { notifyError('입력 초과', '덧글은 200자 이내로 입력해 주세요.'); return; }
      var unlock = lockButton(save);
      // A1: PUT 응답 commentView 로 해당 li 갱신 (전체 재조회 대신 단건 교체)
      api(commentsUrl + '/' + encodeURIComponent(c.commentId), {
        method: 'PUT', headers: formHeaders(), body: encodeForm({ content: text })
      }).then(function (data) {
        unlock();
        var view = data && data.commentId != null ? data : null;
        if (view) {
          var fresh = buildCommentItem(view);
          li.parentNode.replaceChild(fresh, li);
        } else {
          ef.remove(); p.hidden = false;
        }
      }).catch(function (err) { unlock(); notifyError('수정 실패', err.message); });
    });
  }

  function buildCommentItem(c) {
    var li = document.createElement('li');
    li.className = 'game-comments__item';
    var nick = (c.authorName && String(c.authorName).trim()) ? String(c.authorName).trim() : '익명';

    var av = document.createElement('div');
    av.className = 'game-comments__avatar';
    av.setAttribute('aria-hidden', 'true');
    av.textContent = nick.charAt(0).toUpperCase();

    var body = document.createElement('div');
    body.className = 'game-comments__body';
    var meta = document.createElement('div');
    meta.className = 'game-comments__meta';
    var nickEl = document.createElement('span');
    nickEl.className = 'game-comments__nick';
    nickEl.textContent = nick;
    meta.appendChild(nickEl);
    meta.appendChild(buildTimeEl(c.updatedAt || c.createdAt, !!c.edited));

    var p = document.createElement('p');
    p.textContent = c.content || '';

    body.appendChild(meta);
    body.appendChild(p);

    if (canActOn(c.userId)) {
      var foot = document.createElement('div');
      foot.className = 'game-comments__footer';
      var editBtn = document.createElement('button');
      editBtn.type = 'button';
      editBtn.className = 'game-reviews__action game-reviews__action--edit';
      editBtn.textContent = '수정';
      editBtn.addEventListener('click', function () { openCommentEdit(li, body, p, c); });
      var del = document.createElement('button');
      del.type = 'button';
      del.className = 'game-reviews__action game-reviews__action--delete';
      del.textContent = '삭제';
      del.setAttribute('aria-label', '이 덧글 삭제');
      del.addEventListener('click', function () {
        confirmAction('덧글 삭제', '이 덧글을 삭제할까요?', '삭제', function () {
          api(commentsUrl + '/' + encodeURIComponent(c.commentId), { method: 'DELETE', headers: baseHeaders() })
            .then(function () {
              li.remove(); // C5: DOM 완전 제거 (tombstone 잔재 없음)
              cEmptyEl.hidden = cListEl.children.length > 0;
            })
            .catch(function (err) { notifyError('삭제 실패', err.message); });
        });
      });
      foot.appendChild(editBtn);
      foot.appendChild(del);
      body.appendChild(foot);
    }

    li.appendChild(av);
    li.appendChild(body);
    return li;
  }

  function setCommentMore(hasMore) {
    if (cMoreWrap) cMoreWrap.hidden = !hasMore;
  }
  function appendComments(list) {
    list.forEach(function (c) { cListEl.appendChild(buildCommentItem(c)); });
    cEmptyEl.hidden = cListEl.children.length > 0;
  }
  // page=0 일 때 목록 초기화, 이후 page 는 append
  function loadComments(page) {
    var p = page || 0;
    if (p === 0) { cListEl.innerHTML = ''; }
    var url = commentsUrl + '?page=' + p + '&sort=' + encodeURIComponent(currentCommentSort);
    return api(url, { headers: baseHeaders() })
      .then(function (data) {
        currentCommentPage = p;
        appendComments(Array.isArray(data.comments) ? data.comments : []);
        setCommentMore(!!data.hasMore);
        if (cToolbar) cToolbar.hidden = false;
      })
      .catch(function () { if (p === 0) cEmptyEl.hidden = false; });
  }

  if (cMoreBtn) {
    cMoreBtn.addEventListener('click', function () { loadComments(currentCommentPage + 1); });
  }
  if (cToolbar) {
    Array.prototype.forEach.call(cToolbar.querySelectorAll('.game-comments__sort-btn'), function (btn) {
      btn.addEventListener('click', function () {
        var sort = btn.getAttribute('data-sort');
        if (sort === currentCommentSort) return;
        currentCommentSort = sort;
        Array.prototype.forEach.call(cToolbar.querySelectorAll('.game-comments__sort-btn'), function (b) {
          var on = b === btn;
          b.classList.toggle('is-active', on);
          b.setAttribute('aria-pressed', on ? 'true' : 'false');
        });
        loadComments(0);
      });
    });
  }

  if (cForm && cInput) {
    if (cCounter) {
      cInput.addEventListener('input', function () { cCounter.textContent = cInput.value.length + '/' + COMMENT_MAX; });
    }
    if (viewerId == null) {
      if (cComposer) cComposer.hidden = true;
      if (cLoginGate) cLoginGate.hidden = false;
    } else {
      if (cComposer) cComposer.hidden = false;
      if (cLoginGate) cLoginGate.hidden = true;
      cForm.addEventListener('submit', function (ev) {
        ev.preventDefault();
        var text = (cInput.value || '').trim();
        if (!text) return;
        if (text.length > COMMENT_MAX) { notifyError('입력 초과', '덧글은 200자 이내로 입력해 주세요.'); return; }
        var unlock = lockButton(cSubmit);
        // A1: POST 응답 commentView 로 새 li 생성·삽입 (oldest=append / newest=prepend)
        api(commentsUrl, { method: 'POST', headers: formHeaders(), body: encodeForm({ content: text }) })
          .then(function (data) {
            unlock();
            cInput.value = '';
            if (cCounter) cCounter.textContent = '0/' + COMMENT_MAX;
            var view = data && data.commentId != null ? data : null;
            if (view) {
              var li = buildCommentItem(view);
              if (currentCommentSort === 'newest') cListEl.insertBefore(li, cListEl.firstChild);
              else cListEl.appendChild(li);
              cEmptyEl.hidden = true;
            } else {
              loadComments(0);
            }
          })
          .catch(function (err) { unlock(); notifyError('등록 실패', err.message); });
      });
    }
  }
  loadComments(0);

  // ===== 리뷰 (서버 연동) =====
  var rListEl = document.getElementById('game-review-list');
  var rEmptyEl = document.getElementById('game-reviews-empty');
  var rComposer = document.getElementById('game-review-composer');
  var rLoginGate = document.getElementById('game-review-login-gate');
  var rForm = document.getElementById('game-review-form');
  var rInput = document.getElementById('game-review-input');
  var rCounter = document.getElementById('game-review-counter');
  var rHint = document.getElementById('game-review-hint');
  var rAxesEl = document.getElementById('game-review-axes');
  var rStarsEl = document.getElementById('game-review-stars');
  var rStarsLabel = document.getElementById('game-review-stars-label');
  var rSubmit = document.getElementById('game-review-submit');
  var rCancel = document.getElementById('game-review-cancel');
  var rSummary = document.getElementById('game-reviews-summary');
  var rSummaryPanel = document.getElementById('game-reviews-summary-panel');
  var rSummaryRadar = document.getElementById('game-reviews-summary-radar');
  var rAvg = document.getElementById('game-reviews-avg');
  var rAvgStars = document.getElementById('game-reviews-avg-stars');
  var rCount = document.getElementById('game-reviews-count');
  var rToolbar = document.getElementById('game-reviews-toolbar');
  var rMoreBtn = document.getElementById('game-reviews-more');
  var rMoreWrap = rMoreBtn ? rMoreBtn.parentElement : null;
  var reviewsUrl = ctx + '/game/' + encodeURIComponent(gid) + '/reviews';
  var editingReviewId = null;   // null = 작성 모드, 값 = 수정 모드
  var currentReviewPage = 0;
  var currentReviewSort = 'newest';
  var REVIEW_MAX = 1000;
  var REVIEW_MIN = 10;

  // overall(종합) 별점 위젯 — 선택사항(빈값 허용)
  var overallWidget = null;
  // 6축 위젯 핸들 (axisKey -> widget)
  var axisWidgets = {};

  function buildAxisWidgets() {
    if (!rAxesEl) return;
    rAxesEl.innerHTML = '';
    axisWidgets = {};
    AXIS_KEYS.forEach(function (k) {
      var row = document.createElement('div');
      row.className = 'game-reviews__axis';
      var label = document.createElement('span');
      label.className = 'game-reviews__axis-label';
      label.textContent = AXIS_LABELS_KO[k];
      var w = buildStarRadioGroup(k, AXIS_LABELS_KO[k]);
      label.id = w.labelId;
      row.appendChild(label);
      row.appendChild(w.group);
      rAxesEl.appendChild(row);
      axisWidgets[k] = w;
    });
  }
  function buildOverallWidget() {
    if (!rStarsEl) return;
    rStarsEl.innerHTML = '';
    // overall 은 axisScores['overall'] 키 사용. 빈값 허용(미선택 시 rating 미전송)
    overallWidget = buildStarRadioGroup('overall', '종합 별점');
    rStarsEl.setAttribute('aria-labelledby', rStarsLabel ? rStarsLabel.id : '');
    // group wrapper 대신 rStarsEl 자체(마크업의 radiogroup)에 radio 들을 이식
    while (overallWidget.group.firstChild) {
      rStarsEl.appendChild(overallWidget.group.firstChild);
    }
  }

  function fillStars(container, rating) {
    container.innerHTML = '';
    for (var i = 1; i <= 5; i++) {
      var s = document.createElement('span');
      s.className = 'game-star' + (i <= rating ? ' is-on' : '');
      s.textContent = '★';
      container.appendChild(s);
    }
  }
  function buildStarDisplay(rating) {
    var wrap = document.createElement('div');
    wrap.className = 'game-stars game-stars--display';
    wrap.setAttribute('aria-hidden', 'true');
    fillStars(wrap, rating);
    return wrap;
  }

  function resetAxisInputs() {
    AXIS_KEYS.forEach(function (k) { axisScores[k] = 0; if (axisWidgets[k]) axisWidgets[k].paint(); });
    axisScores.overall = 0;
    repaintOverall();
  }
  function repaintOverall() {
    if (!rStarsEl) return;
    var sel = axisScores.overall || 0;
    var btns = rStarsEl.querySelectorAll('.game-stars__btn');
    Array.prototype.forEach.call(btns, function (b, idx) {
      var v = idx + 1;
      b.classList.toggle('is-on', v <= sel);
      b.setAttribute('aria-checked', v === sel ? 'true' : 'false');
      b.tabIndex = (v === sel) ? 0 : -1;
    });
    if (!sel && btns[0]) btns[0].tabIndex = 0;
  }

  function updateReviewCounter() {
    var len = ((rInput && rInput.value) || '').length;
    if (rCounter) rCounter.textContent = len + '/' + REVIEW_MAX;
    if (rHint) {
      var tooShort = len > 0 && len < REVIEW_MIN;
      rHint.classList.toggle('is-warn', tooShort);
    }
  }

  function startReviewEdit(r) {
    editingReviewId = r.reviewId;
    // 6축 prefill
    var ax = r.axes || {};
    AXIS_KEYS.forEach(function (k) {
      var v = Number(ax[k]) || 0;
      axisScores[k] = v;
      if (axisWidgets[k]) axisWidgets[k].paint();
    });
    // ratingManual=true 면 overall prefill, 아니면 미선택
    axisScores.overall = (r.ratingManual && Number(r.rating)) ? Number(r.rating) : 0;
    repaintOverall();
    if (rInput) rInput.value = r.body || '';
    updateReviewCounter();
    if (rSubmit) rSubmit.textContent = '리뷰 수정';
    if (rCancel) rCancel.hidden = false;
    if (rComposer) {
      rComposer.hidden = false;
      if (rComposer.scrollIntoView) rComposer.scrollIntoView({ behavior: 'smooth', block: 'center' });
    }
    if (rInput) rInput.focus();
  }
  function showComposerWrite() {
    editingReviewId = null;
    resetAxisInputs();
    if (rInput) rInput.value = '';
    updateReviewCounter();
    if (rSubmit) rSubmit.textContent = '리뷰 등록';
    if (rCancel) rCancel.hidden = true;
    if (rComposer) rComposer.hidden = false;
  }

  function buildReviewItem(r) {
    var mine = viewerId != null && r.userId != null && Number(r.userId) === Number(viewerId);
    var li = document.createElement('li');
    li.className = 'game-reviews__item' + (mine ? ' is-mine' : '');

    var head = document.createElement('div');
    head.className = 'game-reviews__item-head';
    head.appendChild(buildStarDisplay(Number(r.rating) || 0));

    var nick = (r.authorName && String(r.authorName).trim()) ? String(r.authorName).trim() : '익명';
    var nickEl = document.createElement('span');
    nickEl.className = 'game-reviews__nick';
    nickEl.textContent = nick;
    head.appendChild(nickEl);

    if (mine) {
      var mb = document.createElement('span');
      mb.className = 'game-reviews__mine-badge';
      mb.textContent = '내 리뷰';
      head.appendChild(mb);
    }
    var t = document.createElement('time');
    t.className = 'game-reviews__time';
    t.dateTime = (r.updatedAt || r.createdAt) || '';
    t.title = fmtAbsolute(r.updatedAt || r.createdAt);
    t.textContent = fmtRelative(r.updatedAt || r.createdAt);
    head.appendChild(t);
    if (r.edited) {
      var ed = document.createElement('span');
      ed.className = 'game-reviews__edited';
      ed.textContent = '(수정됨)';
      head.appendChild(ed);
    }
    li.appendChild(head);

    if (r.body && String(r.body).trim()) {
      var p = document.createElement('p');
      p.className = 'game-reviews__body';
      p.textContent = r.body;
      li.appendChild(p);
    }

    // 카드 컴팩트 육각형 (6축). axes null/누락 시 "데이터 없음"
    var cardScores = AXIS_KEYS.map(function (k) { return r.axes ? Number(r.axes[k]) || 0 : 0; });
    var cardRadar = buildHexRadar(cardScores, 60, 60, 44);
    var radarWrap = document.createElement('div');
    radarWrap.className = 'game-reviews__radar game-reviews__card-radar';
    if (cardRadar.hasData) {
      radarWrap.appendChild(cardRadar.svg);
      // §6.6: 폴리곤만으로 종료 금지 — 시각/스크린리더 보조 표 동반(시각적으로 숨김)
      var legend = buildRadarLegend(cardRadar.nums, true);
      legend.className += ' sr-only';
      radarWrap.appendChild(legend);
    } else {
      var none = document.createElement('span');
      none.className = 'game-reviews__radar-empty';
      none.textContent = '6축 평가 데이터 없음';
      radarWrap.appendChild(none);
    }
    li.appendChild(radarWrap);

    if (canActOn(r.userId)) {
      var foot = document.createElement('div');
      foot.className = 'game-reviews__footer';
      var editBtn = document.createElement('button');
      editBtn.type = 'button';
      editBtn.className = 'game-reviews__action game-reviews__action--edit';
      editBtn.textContent = '수정';
      editBtn.addEventListener('click', function () { startReviewEdit(r); });
      var del = document.createElement('button');
      del.type = 'button';
      del.className = 'game-reviews__action game-reviews__action--delete';
      del.textContent = '삭제';
      del.addEventListener('click', function () {
        confirmAction('리뷰 삭제', '이 리뷰를 삭제할까요?', '삭제', function () {
          api(reviewsUrl + '/' + encodeURIComponent(r.reviewId), { method: 'DELETE', headers: baseHeaders() })
            .then(function () { loadReviews(0); }) // C5: page0 재조회로 완전 제거 + summary 갱신
            .catch(function (err) { notifyError('삭제 실패', err.message); });
        });
      });
      foot.appendChild(editBtn);
      foot.appendChild(del);
      li.appendChild(foot);
    }
    return li;
  }

  // C3: 서버 summary 로 표시 (클라 평균계산 폐기)
  function renderSummary(summary) {
    if (!rSummary) return;
    if (!summary || !summary.reviewCount) {
      rSummary.hidden = true;
      if (rSummaryPanel) rSummaryPanel.hidden = true;
      return;
    }
    rSummary.hidden = false;
    var avg = Number(summary.avgRating) || 0;
    if (rAvg) rAvg.textContent = avg.toFixed(1);
    if (rCount) rCount.textContent = '(' + summary.reviewCount + ')';
    if (rAvgStars) fillStars(rAvgStars, Math.round(avg));

    // 요약 육각형 (200 viewBox). axes 값 null 이면 데이터 없음
    if (rSummaryPanel && rSummaryRadar) {
      rSummaryRadar.innerHTML = '';
      var scores = AXIS_KEYS.map(function (k) { return summary.axes ? Number(summary.axes[k]) || 0 : 0; });
      var radar = buildHexRadar(scores, 100, 100, 80);
      rSummaryPanel.hidden = false;
      if (radar.hasData) {
        rSummaryRadar.appendChild(radar.svg);
        rSummaryRadar.appendChild(buildRadarLegend(radar.nums, true));
      } else {
        var none = document.createElement('span');
        none.className = 'game-reviews__radar-empty';
        none.textContent = '6축 평가 데이터 없음';
        rSummaryRadar.appendChild(none);
      }
    }
  }

  function applyReviewGate(mineExists) {
    if (viewerId == null) {
      if (rComposer) rComposer.hidden = true;
      if (rLoginGate) rLoginGate.hidden = false;
      return;
    }
    if (rLoginGate) rLoginGate.hidden = true;
    if (editingReviewId != null) return; // 수정 진행 중이면 폼 유지
    if (mineExists) {
      if (rComposer) rComposer.hidden = true;   // 이미 작성 — 수정은 본인 카드의 '수정' 버튼으로
    } else {
      showComposerWrite();                       // 미작성 — 작성 폼 노출
    }
  }

  var reviewMineSeen = false;
  function appendReviews(list) {
    list.forEach(function (r) {
      rListEl.appendChild(buildReviewItem(r));
      if (r.userId != null && viewerId != null && Number(r.userId) === Number(viewerId)) reviewMineSeen = true;
    });
    rEmptyEl.hidden = rListEl.children.length > 0;
  }
  function setReviewMore(hasMore) {
    if (rMoreWrap) rMoreWrap.hidden = !hasMore;
  }
  function loadReviews(page) {
    var p = page || 0;
    if (p === 0) { rListEl.innerHTML = ''; reviewMineSeen = false; editingReviewId = null; }
    var url = reviewsUrl + '?page=' + p + '&sort=' + encodeURIComponent(currentReviewSort);
    return api(url, { headers: baseHeaders() })
      .then(function (data) {
        currentReviewPage = p;
        appendReviews(Array.isArray(data.reviews) ? data.reviews : []);
        setReviewMore(!!data.hasMore);
        if (rToolbar) rToolbar.hidden = false;
        if (p === 0) renderSummary(data.summary);
        if (!data.hasMore) applyReviewGate(reviewMineSeen); // 전체 로드 후 작성 폼 노출 판단
      })
      .catch(function () { if (p === 0) rEmptyEl.hidden = false; });
  }

  if (rMoreBtn) {
    rMoreBtn.addEventListener('click', function () { loadReviews(currentReviewPage + 1); });
  }
  if (rToolbar) {
    Array.prototype.forEach.call(rToolbar.querySelectorAll('.game-comments__sort-btn'), function (btn) {
      btn.addEventListener('click', function () {
        var sort = btn.getAttribute('data-sort');
        if (sort === currentReviewSort) return;
        currentReviewSort = sort;
        Array.prototype.forEach.call(rToolbar.querySelectorAll('.game-comments__sort-btn'), function (b) {
          var on = b === btn;
          b.classList.toggle('is-active', on);
          b.setAttribute('aria-pressed', on ? 'true' : 'false');
        });
        loadReviews(0);
      });
    });
  }

  // 위젯 초기화 (6축 + overall)
  buildAxisWidgets();
  buildOverallWidget();
  resetAxisInputs();

  if (rInput) {
    rInput.addEventListener('input', updateReviewCounter);
    updateReviewCounter();
  }
  if (rCancel) {
    rCancel.addEventListener('click', function () { loadReviews(0); });
  }
  if (rForm) {
    rForm.addEventListener('submit', function (ev) {
      ev.preventDefault();
      if (viewerId == null) { notifyError('로그인 필요', '리뷰를 남기려면 로그인이 필요합니다.'); return; }
      // 6축 전부 필수 (UX 선검증, 서버도 400 대칭)
      var missing = AXIS_KEYS.filter(function (k) { return !(axisScores[k] >= 1 && axisScores[k] <= 5); });
      if (missing.length) {
        notifyError('평가 필요', '6개 항목(' + missing.map(function (k) { return AXIS_LABELS_KO[k]; }).join(', ') + ')을 모두 선택해 주세요.');
        return;
      }
      var bodyText = ((rInput && rInput.value) || '').trim();
      if (bodyText.length < REVIEW_MIN) { notifyError('입력 필요', '평가는 최소 ' + REVIEW_MIN + '자 이상 입력해 주세요.'); return; }
      if (bodyText.length > REVIEW_MAX) { notifyError('입력 초과', '평가는 1,000자 이내로 입력해 주세요.'); return; }

      var payload = { body: bodyText };
      AXIS_KEYS.forEach(function (k) { payload[k] = axisScores[k]; });
      // overall 선택됐을 때만 rating 전송 (미선택이면 서버 6축 자동평균)
      if (axisScores.overall >= 1 && axisScores.overall <= 5) { payload.rating = axisScores.overall; }

      var isEdit = editingReviewId != null;
      var url = isEdit ? (reviewsUrl + '/' + encodeURIComponent(editingReviewId)) : reviewsUrl;
      var unlock = lockButton(rSubmit);
      api(url, { method: isEdit ? 'PUT' : 'POST', headers: formHeaders(), body: encodeForm(payload) })
        .then(function () { unlock(); loadReviews(0); })
        .catch(function (err) { unlock(); notifyError(isEdit ? '수정 실패' : '등록 실패', err.message); });
    });
  }
  loadReviews(0);
})();
    </script>
</body>
</html>
