247 Blackjack Score Setter :)

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

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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();
        }
    });

})();