247 Blackjack Score Setter :)

Set your bank/high score, manage save files, and view a strategy chart on 247blackjack.com

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         247 Blackjack Score Setter :)
// @namespace    http://tampermonkey.net/
// @version      3.1
// @description  Set your bank/high score, manage save files, and view a strategy chart on 247blackjack.com
// @author       Damian Berti
// @match        https://www.247blackjack.com/
// @grant        none
// @run-at       document-end
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // ── Constants ─────────────────────────────────────────────────────────────
    const BANK_KEY  = 'com.games247.blackjack.247.Bank';
    const HS_KEY    = 'com.games247.blackjack.247.Records';
    const SAVES_KEY = 'bj_save_files';

    // ═══════════════════════════════════════════════════════════════
    //  STRATEGY CHART DATA
    //  Actions: H=Hit  S=Stand  D=Double  P=Split  R=Surrender(else Hit)
    //  Dealer columns: 2 3 4 5 6 7 8 9 10 A
    // ═══════════════════════════════════════════════════════════════
    const DEALER_COLS = ['2','3','4','5','6','7','8','9','10','A'];

    const HARD = [
        { hand:'8',   row:['H','H','H','H','H','H','H','H','H','H'] },
        { hand:'9',   row:['H','D','D','D','D','H','H','H','H','H'] },
        { hand:'10',  row:['D','D','D','D','D','D','D','D','H','H'] },
        { hand:'11',  row:['D','D','D','D','D','D','D','D','D','H'] },
        { hand:'12',  row:['H','H','S','S','S','H','H','H','H','H'] },
        { hand:'13',  row:['S','S','S','S','S','H','H','H','H','H'] },
        { hand:'14',  row:['S','S','S','S','S','H','H','H','H','H'] },
        { hand:'15',  row:['S','S','S','S','S','H','H','H','R','H'] },
        { hand:'16',  row:['S','S','S','S','S','H','H','R','R','R'] },
        { hand:'17',  row:['S','S','S','S','S','S','S','S','S','S'] },
        { hand:'18+', row:['S','S','S','S','S','S','S','S','S','S'] },
    ];

    const SOFT = [
        { hand:'A+2', row:['H','H','H','D','D','H','H','H','H','H'] },
        { hand:'A+3', row:['H','H','H','D','D','H','H','H','H','H'] },
        { hand:'A+4', row:['H','H','D','D','D','H','H','H','H','H'] },
        { hand:'A+5', row:['H','H','D','D','D','H','H','H','H','H'] },
        { hand:'A+6', row:['H','D','D','D','D','H','H','H','H','H'] },
        { hand:'A+7', row:['S','D','D','D','D','S','S','H','H','H'] },
        { hand:'A+8', row:['S','S','S','S','S','S','S','S','S','S'] },
        { hand:'A+9', row:['S','S','S','S','S','S','S','S','S','S'] },
    ];

    const PAIRS = [
        { hand:'A+A',   row:['P','P','P','P','P','P','P','P','P','P'] },
        { hand:'2+2',   row:['P','P','P','P','P','P','H','H','H','H'] },
        { hand:'3+3',   row:['P','P','P','P','P','P','H','H','H','H'] },
        { hand:'4+4',   row:['H','H','H','P','P','H','H','H','H','H'] },
        { hand:'5+5',   row:['D','D','D','D','D','D','D','D','H','H'] },
        { hand:'6+6',   row:['P','P','P','P','P','H','H','H','H','H'] },
        { hand:'7+7',   row:['P','P','P','P','P','P','H','H','H','H'] },
        { hand:'8+8',   row:['P','P','P','P','P','P','P','P','P','P'] },
        { hand:'9+9',   row:['P','P','P','P','P','S','P','P','S','S'] },
        { hand:'10+10', row:['S','S','S','S','S','S','S','S','S','S'] },
    ];

    const ACTION_META = {
        H: { label:'H', title:'Hit',              bg:'#1a3a1a', color:'#6fcf97', border:'#2a6a3a' },
        S: { label:'S', title:'Stand',             bg:'#3a1a1a', color:'#eb8080', border:'#7a3030' },
        D: { label:'D', title:'Double Down',       bg:'#1a2a3a', color:'#7ab4f8', border:'#2a4a7a' },
        P: { label:'P', title:'Split',             bg:'#2a2a1a', color:'#f0d060', border:'#6a6020' },
        R: { label:'R', title:'Surrender (→ Hit)', bg:'#2a1a2a', color:'#c07af0', border:'#5a2a6a' },
    };

    // ── Styles ────────────────────────────────────────────────────────────────
    const style = document.createElement('style');
    style.textContent = `
        #bj-bank-panel {
            position: fixed;
            bottom: 20px;
            right: 20px;
            z-index: 999999;
            font-family: 'Segoe UI', Arial, sans-serif;
            font-size: 13px;
            display: flex;
            flex-direction: column;
            align-items: flex-end;
        }

        #bj-panels-row {
            display: flex;
            flex-direction: row;
            align-items: flex-end;
            gap: 8px;
            margin-bottom: 8px;
        }

        /* ── Toggle Button ── */
        #bj-bank-toggle {
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 5px;
            background: linear-gradient(135deg, #1a472a, #2d6a4f);
            color: #f0d060;
            border: 2px solid #f0d060;
            border-radius: 50px;
            padding: 7px 15px;
            cursor: pointer;
            font-weight: 700;
            font-size: 12px;
            letter-spacing: 0.5px;
            box-shadow: 0 4px 14px rgba(0,0,0,0.55);
            transition: background 0.2s, transform 0.1s;
            user-select: none;
        }
        #bj-bank-toggle:hover { background: linear-gradient(135deg, #22573a, #3a8a63); transform: scale(1.04); }
        #bj-bank-toggle .chip-icon { font-size: 15px; line-height: 1; }

        /* ── Shared panel boxes ── */
        #bj-strat-box,
        #bj-save-box,
        #bj-bank-box {
            display: none;
            flex-direction: column;
            gap: 8px;
            background: linear-gradient(160deg, #1b2a1b, #0f1f0f);
            border: 2px solid #f0d060;
            border-radius: 12px;
            padding: 12px 14px 11px;
            box-shadow: 0 8px 28px rgba(0,0,0,0.7);
            animation: bj-fadein 0.18s ease;
            align-self: flex-end;
        }
        #bj-save-box,
        #bj-bank-box { min-width: 240px; }
        #bj-strat-box { min-width: 0; }

        @keyframes bj-fadein {
            from { opacity: 0; transform: translateY(8px); }
            to   { opacity: 1; transform: translateY(0); }
        }
        #bj-strat-box.open,
        #bj-save-box.open,
        #bj-bank-box.open { display: flex; }

        /* ── Shared typography ── */
        .bj-panel-title {
            color: #f0d060;
            font-weight: 700;
            font-size: 13px;
            letter-spacing: 0.4px;
            display: flex;
            align-items: center;
            gap: 6px;
            border-bottom: 1px solid rgba(240,208,96,0.25);
            padding-bottom: 6px;
            margin-bottom: 0;
        }
        .bj-section-title {
            color: #f0d060;
            font-weight: 700;
            font-size: 12px;
            letter-spacing: 0.3px;
            display: flex;
            align-items: center;
            gap: 5px;
            margin-bottom: 1px;
        }
        .bj-divider { border: none; border-top: 1px solid rgba(240,208,96,0.18); margin: 1px 0; }

        /* ── Bank/HS inputs ── */
        #bj-bank-label, #bj-hs-label { color: #b5c9b5; font-size: 11px; margin-bottom: 1px; }
        #bj-bank-input, #bj-hs-input {
            background: #0d1a0d; color: #f0d060; border: 1.5px solid #3a8a63;
            border-radius: 7px; padding: 6px 9px; font-size: 13px; font-weight: 600;
            width: 100%; box-sizing: border-box; outline: none; transition: border-color 0.15s;
        }
        #bj-bank-input:focus, #bj-hs-input:focus { border-color: #f0d060; }
        #bj-bank-input::placeholder, #bj-hs-input::placeholder { color: #3a5a3a; font-weight: 400; }
        #bj-bank-input::-webkit-outer-spin-button, #bj-bank-input::-webkit-inner-spin-button,
        #bj-hs-input::-webkit-outer-spin-button,   #bj-hs-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
        #bj-bank-input[type=number], #bj-hs-input[type=number] { -moz-appearance: textfield; }

        #bj-bank-set-btn, #bj-hs-set-btn {
            background: linear-gradient(135deg, #f0d060, #c8a820); color: #0f1f0f;
            border: none; border-radius: 7px; padding: 7px 0; font-size: 12px; font-weight: 700;
            cursor: pointer; width: 100%; letter-spacing: 0.5px;
            box-shadow: 0 2px 7px rgba(0,0,0,0.3); transition: background 0.15s, transform 0.1s;
        }
        #bj-bank-set-btn:hover, #bj-hs-set-btn:hover { background: linear-gradient(135deg, #ffe87a, #dab830); transform: scale(1.02); }
        #bj-bank-set-btn:active, #bj-hs-set-btn:active { transform: scale(0.98); }

        #bj-bank-status, #bj-hs-status { font-size: 11px; text-align: center; min-height: 14px; color: #6fcf97; font-weight: 600; }
        #bj-bank-status.error, #bj-hs-status.error { color: #eb5757; }
        #bj-bank-current, #bj-hs-current { font-size: 10.5px; color: #7a9a7a; text-align: center; }
        #bj-bank-current span, #bj-hs-current span { color: #a8d5b8; font-weight: 600; }
        #bj-watermark { color: #3a5a3a; font-size: 9.5px; text-align: center; margin-top: 1px; letter-spacing: 0.3px; }

        /* ── Save file manager ── */
        #bj-new-save-row { display: flex; gap: 6px; align-items: center; }
        #bj-new-save-input {
            flex: 1; background: #0d1a0d; color: #f0d060; border: 1.5px solid #3a8a63;
            border-radius: 7px; padding: 6px 9px; font-size: 12px; font-weight: 600;
            outline: none; transition: border-color 0.15s; min-width: 0;
        }
        #bj-new-save-input:focus { border-color: #f0d060; }
        #bj-new-save-input::placeholder { color: #3a5a3a; font-weight: 400; }
        #bj-new-save-btn {
            background: linear-gradient(135deg, #f0d060, #c8a820); color: #0f1f0f; border: none;
            border-radius: 7px; padding: 6px 11px; font-size: 12px; font-weight: 700;
            cursor: pointer; white-space: nowrap; transition: background 0.15s, transform 0.1s;
        }
        #bj-new-save-btn:hover { background: linear-gradient(135deg, #ffe87a, #dab830); transform: scale(1.03); }
        #bj-new-save-btn:active { transform: scale(0.97); }

        #bj-save-status { font-size: 11px; text-align: center; min-height: 14px; color: #6fcf97; font-weight: 600; }
        #bj-save-status.error { color: #eb5757; }

        #bj-save-list { display: flex; flex-direction: column; gap: 6px; max-height: 200px; overflow-y: auto; padding-right: 2px; }
        #bj-save-list::-webkit-scrollbar { width: 4px; }
        #bj-save-list::-webkit-scrollbar-track { background: #0d1a0d; border-radius: 4px; }
        #bj-save-list::-webkit-scrollbar-thumb { background: #3a8a63; border-radius: 4px; }

        .bj-save-empty { color: #3a5a3a; font-size: 11px; text-align: center; padding: 10px 0 4px; font-style: italic; }

        .bj-save-card {
            background: rgba(13,26,13,0.7); border: 1.5px solid #2a5a3a; border-radius: 9px;
            padding: 7px 9px; display: flex; flex-direction: column; gap: 4px;
            transition: border-color 0.15s, background 0.15s; position: relative; overflow: hidden;
        }
        .bj-save-card::before {
            content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px;
            background: linear-gradient(90deg, transparent, rgba(240,208,96,0.12), transparent);
        }
        .bj-save-card:hover { border-color: #3a8a63; background: rgba(13,26,13,0.95); }
        .bj-save-card.active-save { border-color: #f0d060 !important; background: rgba(30,46,20,0.9) !important; }
        .bj-save-card.active-save::before { background: linear-gradient(90deg, transparent, rgba(240,208,96,0.35), transparent); }

        .bj-save-card-top { display: flex; align-items: center; gap: 5px; }
        .bj-save-name { flex: 1; color: #f0d060; font-weight: 700; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .bj-save-name-input {
            flex: 1; background: #0d1a0d; color: #f0d060; border: 1.5px solid #f0d060;
            border-radius: 5px; padding: 2px 6px; font-size: 12px; font-weight: 600; outline: none; min-width: 0;
        }
        .bj-save-actions { display: flex; gap: 3px; flex-shrink: 0; }
        .bj-icon-btn {
            background: none; border: 1.5px solid; border-radius: 5px; width: 23px; height: 23px;
            display: flex; align-items: center; justify-content: center;
            cursor: pointer; font-size: 11px; transition: background 0.15s, transform 0.1s; padding: 0; line-height: 1;
        }
        .bj-icon-btn:active { transform: scale(0.93); }
        .bj-btn-load         { border-color: #3a8a63; color: #6fcf97; }
        .bj-btn-load:hover   { background: rgba(58,138,99,0.25); }
        .bj-btn-overwrite    { border-color: #5a7aaa; color: #8ab4f8; }
        .bj-btn-overwrite:hover { background: rgba(90,122,170,0.25); }
        .bj-btn-rename-ok    { border-color: #6fcf97; color: #6fcf97; }
        .bj-btn-rename-ok:hover { background: rgba(111,207,151,0.2); }
        .bj-btn-rename       { border-color: #5a7aaa; color: #8ab4f8; }
        .bj-btn-rename:hover { background: rgba(90,122,170,0.25); }
        .bj-btn-delete       { border-color: #7a3030; color: #eb5757; }
        .bj-btn-delete:hover { background: rgba(235,87,87,0.2); }

        .bj-save-meta { display: flex; gap: 8px; font-size: 10.5px; color: #7a9a7a; }
        .bj-save-meta span { display: flex; align-items: center; gap: 2px; }
        .bj-save-meta b { color: #a8d5b8; font-weight: 600; }
        .bj-active-badge { font-size: 9px; font-weight: 700; color: #0f1f0f; background: #f0d060; border-radius: 3px; padding: 1px 4px; flex-shrink: 0; }

        /* ── Shared confirmation row (used for both load and overwrite) ── */
        .bj-confirm-row {
            display: none; align-items: center; gap: 5px; margin-top: 1px;
        }
        .bj-confirm-row.visible { display: flex; }
        .bj-confirm-row span { flex: 1; font-size: 10.5px; font-weight: 600; }
        .bj-confirm-row.load-confirm span  { color: #f0a040; }
        .bj-confirm-row.overwrite-confirm span { color: #8ab4f8; }
        .bj-btn-confirm-yes {
            background: linear-gradient(135deg, #eb5757, #b33); color: #fff; border: none;
            border-radius: 5px; padding: 3px 8px; font-size: 10.5px; font-weight: 700; cursor: pointer;
        }
        .bj-btn-confirm-yes:hover { opacity: 0.85; }
        /* Green variant for overwrite confirm */
        .bj-btn-confirm-yes.green {
            background: linear-gradient(135deg, #2d6a4f, #1a472a);
        }
        .bj-btn-confirm-yes.green:hover { opacity: 0.85; }
        .bj-btn-confirm-no {
            background: none; border: 1px solid #5a5a5a; color: #9a9a9a;
            border-radius: 5px; padding: 3px 8px; font-size: 10.5px; font-weight: 700; cursor: pointer;
        }
        .bj-btn-confirm-no:hover { opacity: 0.75; }

        /* ══════════════════════════════════════════
           STRATEGY CHART
        ══════════════════════════════════════════ */
        #bj-strat-tabs { display: flex; gap: 4px; }
        .bj-strat-tab {
            flex: 1; background: rgba(13,26,13,0.6); color: #7a9a7a;
            border: 1.5px solid #2a5a3a; border-radius: 6px; padding: 4px 0;
            font-size: 11px; font-weight: 700; cursor: pointer; text-align: center;
            transition: background 0.15s, color 0.15s, border-color 0.15s; letter-spacing: 0.3px;
        }
        .bj-strat-tab:hover { color: #b5c9b5; border-color: #3a8a63; }
        .bj-strat-tab.active { background: rgba(240,208,96,0.12); color: #f0d060; border-color: #f0d060; }

        #bj-strat-table-wrap { overflow-x: auto; }
        #bj-strat-table-wrap::-webkit-scrollbar { height: 4px; }
        #bj-strat-table-wrap::-webkit-scrollbar-track { background: #0d1a0d; border-radius: 4px; }
        #bj-strat-table-wrap::-webkit-scrollbar-thumb { background: #3a8a63; border-radius: 4px; }

        .bj-strat-table { border-collapse: collapse; font-size: 10.5px; width: 100%; }
        .bj-strat-table th {
            background: rgba(240,208,96,0.1); color: #f0d060; font-weight: 700;
            padding: 3px 5px; text-align: center; border: 1px solid rgba(240,208,96,0.15);
            font-size: 10px; letter-spacing: 0.3px;
        }
        .bj-strat-table th.hand-col { color: #b5c9b5; background: rgba(13,26,13,0.8); min-width: 38px; }
        .bj-strat-table td {
            padding: 3px 4px; text-align: center; border: 1px solid rgba(255,255,255,0.04);
            font-weight: 700; font-size: 10px; min-width: 20px; letter-spacing: 0.2px;
            transition: filter 0.1s;
        }
        .bj-strat-table td:hover { filter: brightness(1.45); cursor: default; }
        .bj-strat-table td.hand-label {
            color: #b5c9b5; background: rgba(13,26,13,0.5); font-weight: 600;
            font-size: 10px; white-space: nowrap; text-align: right; padding-right: 7px;
        }

        #bj-strat-legend { display: flex; flex-wrap: wrap; gap: 4px 10px; margin-top: 2px; }
        .bj-legend-item { display: flex; align-items: center; gap: 3px; font-size: 9.5px; color: #7a9a7a; }
        .bj-legend-swatch {
            width: 14px; height: 14px; border-radius: 3px; border: 1px solid rgba(255,255,255,0.12);
            font-size: 9px; font-weight: 700; display: flex; align-items: center; justify-content: center;
        }
    `;
    document.head.appendChild(style);

    // ── HTML ──────────────────────────────────────────────────────────────────
    const panel = document.createElement('div');
    panel.id = 'bj-bank-panel';
    panel.innerHTML = `
        <div id="bj-panels-row">

            <!-- ── STRATEGY CHART ── -->
            <div id="bj-strat-box">
                <div class="bj-panel-title">♠ Strategy Chart</div>
                <div id="bj-strat-tabs">
                    <button class="bj-strat-tab active" data-tab="hard">Hard</button>
                    <button class="bj-strat-tab"        data-tab="soft">Soft</button>
                    <button class="bj-strat-tab"        data-tab="pairs">Pairs</button>
                </div>
                <div id="bj-strat-table-wrap"></div>
                <div id="bj-strat-legend"></div>
            </div>

            <!-- ── SAVE FILE PANEL ── -->
            <div id="bj-save-box">
                <div class="bj-panel-title"><span>💾</span> Save Files</div>

                <div class="bj-section-title">➕ Create New Save</div>
                <div id="bj-new-save-row">
                    <input id="bj-new-save-input" type="text" maxlength="28" placeholder="Save name…" />
                    <button id="bj-new-save-btn">Save</button>
                </div>
                <div id="bj-save-status"></div>

                <hr class="bj-divider" />

                <div class="bj-section-title">📂 Saved Games</div>
                <div id="bj-save-list">
                    <div class="bj-save-empty">No saves yet — create one above!</div>
                </div>
            </div>

            <!-- ── BANK / HS EDITOR ── -->
            <div id="bj-bank-box">
                <div class="bj-panel-title"><span class="chip-icon">🪙</span> Editor</div>

                <div class="bj-section-title">💰 Bank</div>
                <div id="bj-bank-label">Enter new bank amount</div>
                <input id="bj-bank-input" type="number" min="0" placeholder="e.g. 50000" />
                <button id="bj-bank-set-btn">Set Bank &amp; Reload</button>
                <div id="bj-bank-status"></div>
                <div id="bj-bank-current">Current: <span id="bj-bank-cur-val">—</span></div>

                <hr class="bj-divider" />

                <div class="bj-section-title">🏆 High Score</div>
                <div id="bj-hs-label">Enter new high score</div>
                <input id="bj-hs-input" type="number" min="0" placeholder="e.g. 999999" />
                <button id="bj-hs-set-btn">Set High Score &amp; Reload</button>
                <div id="bj-hs-status"></div>
                <div id="bj-hs-current">Current: <span id="bj-hs-cur-val">—</span></div>
                <div id="bj-watermark">Made by Damian B</div>
            </div>

        </div>
        <button id="bj-bank-toggle"><span class="chip-icon">🃏</span> BJ Hack Client</button>
    `;
    document.body.appendChild(panel);

    // ── Element refs ──────────────────────────────────────────────────────────
    const toggleBtn      = document.getElementById('bj-bank-toggle');
    const stratBox       = document.getElementById('bj-strat-box');
    const saveBox        = document.getElementById('bj-save-box');
    const bankBox        = document.getElementById('bj-bank-box');

    const bankInput      = document.getElementById('bj-bank-input');
    const bankBtn        = document.getElementById('bj-bank-set-btn');
    const bankStatus     = document.getElementById('bj-bank-status');
    const bankCurVal     = document.getElementById('bj-bank-cur-val');

    const hsInput        = document.getElementById('bj-hs-input');
    const hsBtn          = document.getElementById('bj-hs-set-btn');
    const hsStatus       = document.getElementById('bj-hs-status');
    const hsCurVal       = document.getElementById('bj-hs-cur-val');

    const newSaveInput   = document.getElementById('bj-new-save-input');
    const newSaveBtn     = document.getElementById('bj-new-save-btn');
    const saveStatus     = document.getElementById('bj-save-status');
    const saveList       = document.getElementById('bj-save-list');

    const stratTableWrap = document.getElementById('bj-strat-table-wrap');
    const stratLegend    = document.getElementById('bj-strat-legend');
    const stratTabs      = document.querySelectorAll('.bj-strat-tab');

    // ═══════════════════════════════════════════════════════════════
    //  STRATEGY CHART
    // ═══════════════════════════════════════════════════════════════
    let activeStratTab = 'hard';

    function buildStratTable(data) {
        const table = document.createElement('table');
        table.className = 'bj-strat-table';
        // Header
        const thead = document.createElement('thead');
        const hr = document.createElement('tr');
        const cornerTh = document.createElement('th');
        cornerTh.className = 'hand-col';
        cornerTh.textContent = 'vs →';
        hr.appendChild(cornerTh);
        DEALER_COLS.forEach(c => {
            const th = document.createElement('th');
            th.textContent = c;
            hr.appendChild(th);
        });
        thead.appendChild(hr);
        table.appendChild(thead);
        // Body
        const tbody = document.createElement('tbody');
        data.forEach(({ hand, row }) => {
            const tr = document.createElement('tr');
            const handTd = document.createElement('td');
            handTd.className = 'hand-label';
            handTd.textContent = hand;
            tr.appendChild(handTd);
            row.forEach(action => {
                const td = document.createElement('td');
                const meta = ACTION_META[action] || ACTION_META['H'];
                td.textContent = meta.label;
                td.title = meta.title;
                td.style.background = meta.bg;
                td.style.color = meta.color;
                td.style.borderColor = meta.border + '55';
                tr.appendChild(td);
            });
            tbody.appendChild(tr);
        });
        table.appendChild(tbody);
        return table;
    }

    function buildLegend() {
        stratLegend.innerHTML = '';
        Object.values(ACTION_META).forEach(meta => {
            const item = document.createElement('div');
            item.className = 'bj-legend-item';
            item.innerHTML = `
                <div class="bj-legend-swatch" style="background:${meta.bg};color:${meta.color};border-color:${meta.border}55">${meta.label}</div>
                <span>${meta.title}</span>
            `;
            stratLegend.appendChild(item);
        });
    }

    function renderStratChart(tab) {
        stratTableWrap.innerHTML = '';
        const dataMap = { hard: HARD, soft: SOFT, pairs: PAIRS };
        stratTableWrap.appendChild(buildStratTable(dataMap[tab]));
    }

    stratTabs.forEach(tab => {
        tab.addEventListener('click', () => {
            stratTabs.forEach(t => t.classList.remove('active'));
            tab.classList.add('active');
            activeStratTab = tab.dataset.tab;
            renderStratChart(activeStratTab);
        });
    });

    // ═══════════════════════════════════════════════════════════════
    //  SAVE FILE LOGIC
    // ═══════════════════════════════════════════════════════════════
    let activeSaveId = null;

    function loadSaves() {
        try { return JSON.parse(localStorage.getItem(SAVES_KEY)) || []; }
        catch { return []; }
    }
    function writeSaves(saves) { localStorage.setItem(SAVES_KEY, JSON.stringify(saves)); }
    function captureGameState() {
        return {
            bank:      JSON.parse(localStorage.getItem(BANK_KEY) || 'null'),
            highScore: JSON.parse(localStorage.getItem(HS_KEY)   || 'null'),
        };
    }
    function applyGameState(state) {
        if (state.bank      != null) localStorage.setItem(BANK_KEY, JSON.stringify(state.bank));
        if (state.highScore != null) localStorage.setItem(HS_KEY,   JSON.stringify(state.highScore));
    }
    function getBankVal(state) { return state?.bank?.bank != null ? state.bank.bank : null; }
    function getHSVal(state)   { return state?.highScore?.highScore != null ? state.highScore.highScore : null; }
    function fmtNum(n)         { return n != null ? Number(n).toLocaleString() : '?'; }
    function timestamp()       { return new Date().toLocaleString(undefined, { month:'short', day:'numeric', hour:'2-digit', minute:'2-digit' }); }
    function escHtml(str)      { return String(str).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }

    function setStatus(el, msg, isError = false, duration = 0) {
        el.textContent = msg;
        el.className   = isError ? 'error' : '';
        if (duration > 0) setTimeout(() => { el.textContent = ''; el.className = ''; }, duration);
    }

    function renderSaves() {
        const saves = loadSaves();
        saveList.innerHTML = '';
        if (saves.length === 0) {
            saveList.innerHTML = '<div class="bj-save-empty">No saves yet — create one above!</div>';
            return;
        }
        saves.forEach((save) => {
            const isActive = save.id === activeSaveId;
            const card = document.createElement('div');
            card.className = 'bj-save-card' + (isActive ? ' active-save' : '');
            card.dataset.id = save.id;
            const bankVal = fmtNum(getBankVal(save.state));
            const hsVal   = fmtNum(getHSVal(save.state));
            card.innerHTML = `
                <div class="bj-save-card-top">
                    <span class="bj-save-name" title="${escHtml(save.name)}">${escHtml(save.name)}</span>
                    ${isActive ? '<span class="bj-active-badge">ACTIVE</span>' : ''}
                    <div class="bj-save-actions">
                        <button class="bj-icon-btn bj-btn-load"      title="Load save">▶</button>
                        <button class="bj-icon-btn bj-btn-overwrite"  title="Overwrite save with current game">💾</button>
                        <button class="bj-icon-btn bj-btn-rename"     title="Rename save">✏️</button>
                        <button class="bj-icon-btn bj-btn-delete"     title="Delete save">🗑</button>
                    </div>
                </div>
                <div class="bj-save-meta">
                    <span>💰 <b>${bankVal}</b></span>
                    <span>🏆 <b>${hsVal}</b></span>
                    <span style="margin-left:auto;font-size:9.5px;color:#4a6a4a">${escHtml(save.date)}</span>
                </div>
                <div class="bj-confirm-row load-confirm">
                    <span>⚠ Load &amp; reload?</span>
                    <button class="bj-btn-confirm-yes">Yes</button>
                    <button class="bj-btn-confirm-no">No</button>
                </div>
                <div class="bj-confirm-row overwrite-confirm">
                    <span>💾 Overwrite with current?</span>
                    <button class="bj-btn-confirm-yes green">Yes</button>
                    <button class="bj-btn-confirm-no">No</button>
                </div>
            `;

            // ── Load ──
            const loadBtn        = card.querySelector('.bj-btn-load');
            const loadConfirmRow = card.querySelector('.bj-confirm-row.load-confirm');
            const loadYes        = loadConfirmRow.querySelector('.bj-btn-confirm-yes');
            const loadNo         = loadConfirmRow.querySelector('.bj-btn-confirm-no');

            loadBtn.addEventListener('click', () => {
                overwriteConfirmRow.classList.remove('visible'); // close overwrite if open
                loadConfirmRow.classList.toggle('visible');
            });
            loadNo.addEventListener('click',  () => loadConfirmRow.classList.remove('visible'));
            loadYes.addEventListener('click', () => {
                activeSaveId = save.id;
                applyGameState(save.state);
                setStatus(saveStatus, `✓ "${save.name}" loaded! Reloading…`);
                setTimeout(() => location.reload(), 700);
            });

            // ── Overwrite ──
            const overwriteBtn        = card.querySelector('.bj-btn-overwrite');
            const overwriteConfirmRow = card.querySelector('.bj-confirm-row.overwrite-confirm');
            const overwriteYes        = overwriteConfirmRow.querySelector('.bj-btn-confirm-yes');
            const overwriteNo         = overwriteConfirmRow.querySelector('.bj-btn-confirm-no');

            overwriteBtn.addEventListener('click', () => {
                loadConfirmRow.classList.remove('visible'); // close load if open
                overwriteConfirmRow.classList.toggle('visible');
            });
            overwriteNo.addEventListener('click',  () => overwriteConfirmRow.classList.remove('visible'));
            overwriteYes.addEventListener('click', () => {
                const saves2 = loadSaves();
                const idx = saves2.findIndex(s => s.id === save.id);
                if (idx === -1) { setStatus(saveStatus, 'Save not found!', true, 2000); return; }
                saves2[idx].state = captureGameState();
                saves2[idx].date  = timestamp();
                writeSaves(saves2);
                activeSaveId = save.id;
                setStatus(saveStatus, `✓ "${saves2[idx].name}" overwritten!`, false, 2000);
                renderSaves();
            });

            // ── Rename ──
            const renameBtn   = card.querySelector('.bj-btn-rename');
            const nameDisplay = card.querySelector('.bj-save-name');
            renameBtn.addEventListener('click', () => {
                const input = document.createElement('input');
                input.type = 'text'; input.maxLength = 28; input.className = 'bj-save-name-input'; input.value = save.name;
                nameDisplay.replaceWith(input);
                renameBtn.className = 'bj-icon-btn bj-btn-rename-ok'; renameBtn.title = 'Confirm rename'; renameBtn.textContent = '✓';
                input.focus(); input.select();
                function commitRename() {
                    const newName = input.value.trim();
                    if (!newName) { setStatus(saveStatus, 'Name cannot be empty.', true, 2000); return; }
                    const saves2 = loadSaves(); const idx = saves2.findIndex(s => s.id === save.id);
                    if (idx !== -1) { saves2[idx].name = newName; writeSaves(saves2); save.name = newName; }
                    setStatus(saveStatus, `✓ Renamed to "${newName}"`, false, 2000);
                    renderSaves();
                }
                renameBtn.addEventListener('click', commitRename, { once: true });
                input.addEventListener('keydown', (e) => { if (e.key === 'Enter') commitRename(); if (e.key === 'Escape') renderSaves(); });
            });

            // ── Delete ──
            const deleteBtn = card.querySelector('.bj-btn-delete');
            let deleteConfirm = false;
            deleteBtn.addEventListener('click', () => {
                if (!deleteConfirm) {
                    deleteConfirm = true;
                    deleteBtn.style.background = 'rgba(235,87,87,0.25)';
                    deleteBtn.style.borderColor = '#eb5757';
                    setTimeout(() => { deleteConfirm = false; deleteBtn.style.background = ''; deleteBtn.style.borderColor = ''; }, 1500);
                    return;
                }
                const saves2 = loadSaves().filter(s => s.id !== save.id);
                writeSaves(saves2);
                if (activeSaveId === save.id) activeSaveId = null;
                setStatus(saveStatus, `🗑 "${save.name}" deleted.`, false, 2200);
                renderSaves();
            });

            saveList.appendChild(card);
        });
    }

    function createSave() {
        const name = newSaveInput.value.trim();
        if (!name) { setStatus(saveStatus, 'Enter a save name.', true, 2000); return; }
        const saves = loadSaves();
        if (saves.some(s => s.name.toLowerCase() === name.toLowerCase())) {
            setStatus(saveStatus, 'A save with that name already exists.', true, 2500); return;
        }
        const newSave = { id: Date.now().toString(36) + Math.random().toString(36).slice(2,6), name, date: timestamp(), state: captureGameState() };
        saves.push(newSave); writeSaves(saves);
        activeSaveId = newSave.id;
        newSaveInput.value = '';
        setStatus(saveStatus, `✓ "${name}" created & set as active!`, false, 2500);
        renderSaves();
    }

    newSaveBtn.addEventListener('click', createSave);
    newSaveInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') createSave(); });
    newSaveInput.addEventListener('input',   () => { if (saveStatus.classList.contains('error')) setStatus(saveStatus, ''); });

    // ═══════════════════════════════════════════════════════════════
    //  BANK / HS EDITOR
    // ═══════════════════════════════════════════════════════════════
    function getStoredValue(key, field) {
        try { const raw = localStorage.getItem(key); if (raw) { const p = JSON.parse(raw); return p[field] != null ? p[field] : '?'; } }
        catch {} return 'not set';
    }
    function formatDisplay(val) { const n = Number(val); return Number.isFinite(n) ? n.toLocaleString() : val; }
    function refreshCurrentDisplay() {
        bankCurVal.textContent = formatDisplay(getStoredValue(BANK_KEY, 'bank'));
        hsCurVal.textContent   = formatDisplay(getStoredValue(HS_KEY,   'highScore'));
    }
    function doSet(inputEl, statusEl, key, fieldName, label) {
        const raw = inputEl.value.trim();
        if (raw === '') { setStatus(statusEl, 'Please enter a number.', true); return; }
        const amount = Number(raw);
        if (!Number.isFinite(amount) || amount < 0) { setStatus(statusEl, 'Enter a valid positive number.', true); return; }
        const finalAmount = Math.floor(amount);
        try { localStorage.setItem(key, JSON.stringify({ [fieldName]: finalAmount })); }
        catch { setStatus(statusEl, 'Failed to write localStorage.', true); return; }
        setStatus(statusEl, `✓ ${label} set to ${finalAmount.toLocaleString()}! Reloading…`);
        setTimeout(() => location.reload(), 800);
    }

    bankBtn.addEventListener('click', () => doSet(bankInput, bankStatus, BANK_KEY, 'bank', 'Bank'));
    bankInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') doSet(bankInput, bankStatus, BANK_KEY, 'bank', 'Bank'); });
    bankInput.addEventListener('input',   () => { if (bankStatus.classList.contains('error')) setStatus(bankStatus, ''); });

    hsBtn.addEventListener('click', () => doSet(hsInput, hsStatus, HS_KEY, 'highScore', 'High Score'));
    hsInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') doSet(hsInput, hsStatus, HS_KEY, 'highScore', 'High Score'); });
    hsInput.addEventListener('input',   () => { if (hsStatus.classList.contains('error')) setStatus(hsStatus, ''); });

    // ── Toggle (all three panels) ─────────────────────────────────────────────
    toggleBtn.addEventListener('click', () => {
        const isOpen = bankBox.classList.contains('open');
        bankBox.classList.toggle('open', !isOpen);
        saveBox.classList.toggle('open', !isOpen);
        stratBox.classList.toggle('open', !isOpen);
        if (!isOpen) {
            refreshCurrentDisplay();
            renderSaves();
            renderStratChart(activeStratTab);
            buildLegend();
            newSaveInput.focus();
        }
    });

})();