{"id":1018,"date":"2025-09-16T06:02:13","date_gmt":"2025-09-16T06:02:13","guid":{"rendered":"https:\/\/badminton.ghancaballes.com\/single-skilled\/?page_id=1018"},"modified":"2025-10-20T04:10:04","modified_gmt":"2025-10-20T04:10:04","slug":"bs","status":"publish","type":"page","link":"https:\/\/badminton.ghancaballes.com\/single-skilled\/","title":{"rendered":"New Badminton Shuffler"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"1018\" class=\"elementor elementor-1018\" data-elementor-post-type=\"page\">\n\t\t\t\t<div data-marvy_enable_drop_animation=\"false\" data-marvy_enable_fancy_rotate=\"false\" data-marvy_enable_flying_object=\"false\" data-marvy_enable_ripples_animation=\"false\" data-marvy_enable_waves_animation=\"false\" data-marvy_enable_rings_animation=\"false\" data-marvy_enable_topology_animation=\"false\" data-marvy_enable_gradient_animation=\"false\" data-marvy_enable_snow_animation=\"false\" data-marvy_enable_firework_animation=\"false\" data-marvy_enable_cloud_animation=\"false\" class=\"elementor-element elementor-element-ce4f76e e-flex e-con-boxed e-con e-parent\" data-id=\"ce4f76e\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-33f6e53 elementor-widget elementor-widget-shortcode\" data-id=\"33f6e53\" data-element_type=\"widget\" data-widget_type=\"shortcode.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-shortcode\">    <link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\n    <link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin>\n    <link href=\"https:\/\/fonts.googleapis.com\/css2?family=Roboto:wght@400;500;700&display=swap\" rel=\"stylesheet\">\n\n    <style>\n        :root {\n            --m3-primary: #2563eb;\n            --m3-on-primary: #ffffff;\n            --m3-surface: #f7f9fc;\n            --m3-surface-variant: #eef2f7;\n            --m3-on-surface: #1f2937;\n            --m3-outline: #cbd5e1;\n            --m3-error: #dc2626;\n\n            --bds-primary: var(--m3-primary);\n            --bds-secondary: #10b981;\n            --bds-danger: var(--m3-error);\n            --bds-warning: #f59e0b;\n            --bds-info: #8b5cf6;\n\n            --bds-light-gray: var(--m3-surface-variant);\n            --bds-medium-gray: #d1d5db;\n            --bds-dark-gray: #6b7280;\n            --bds-text: var(--m3-on-surface);\n            --bds-text-light: #6b7280;\n            --bds-bg: var(--m3-surface);\n            --bds-card-bg: #ffffff;\n            --bds-border: var(--m3-outline);\n            --bds-input-bg: #ffffff;\n\n            --bds-radius: 12px;\n            --bds-shadow: 0 4px 12px rgba(0,0,0,0.06);\n            --bds-shadow-lg: 0 12px 24px rgba(0,0,0,0.10);\n        }\n        #bds-app-wrapper.dark-mode {\n            --m3-primary: #60a5fa;\n            --m3-on-primary: #0b1220;\n            --m3-surface: #0b1220;\n            --m3-surface-variant: #111827;\n            --m3-on-surface: #e5e7eb;\n            --m3-outline: #374151;\n\n            --bds-primary: var(--m3-primary);\n            --bds-text: var(--m3-on-surface);\n            --bds-text-light: #9ca3af;\n            --bds-bg: var(--m3-surface);\n            --bds-card-bg: #111827;\n            --bds-border: var(--m3-outline);\n            --bds-input-bg: #0f172a;\n            --bds-light-gray: var(--m3-surface-variant);\n            --bds-medium-gray: #374151;\n        }\n\n        #bds-app-wrapper { font-family: 'Roboto', -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif !important; line-height: 1.6; font-size: 16px; text-align: left; background-color: var(--bds-bg); color: var(--bds-text); }\n        #bds-app-wrapper *, #bds-app-wrapper *::before, #bds-app-wrapper *::after { box-sizing: border-box; }\n        #bds-app-wrapper .app-container { max-width: 600px; margin: 0 auto; padding: 2rem 1rem 8rem 1rem; }\n\n        #bds-app-wrapper h1, #bds-app-wrapper h3, #bds-app-wrapper h4, #bds-app-wrapper h5 { font-weight: 700; line-height: 1.3; color: var(--bds-text); margin: 0 0 1rem 0; }\n        #bds-app-wrapper h1 { font-size: 1.75rem; display: flex; align-items: center; }\n        #bds-app-wrapper h3 { font-size: 1.25rem; }\n        #bds-app-wrapper h4 { font-size: 1.1rem; border-bottom: 1px solid var(--bds-border); padding-bottom: 0.75rem; margin-bottom: 1rem; }\n        #bds-app-wrapper h5 { font-size: 1rem; margin-top: 1.5rem; color: var(--bds-text-light); text-transform: uppercase; letter-spacing: .5px; }\n\n        #bds-app-wrapper button, #bds-app-wrapper input, #bds-app-wrapper select { font-family: 'Roboto', sans-serif !important; font-size: 1rem; line-height: 1.5; margin: 0; transition: all 0.2s ease-in-out; }\n        #bds-app-wrapper input, #bds-app-wrapper select { width: 100%; padding: 14px; margin-bottom: 1rem; border: 1px solid var(--bds-border); border-radius: var(--bds-radius); background-color: var(--bds-input-bg); color: var(--bds-text); }\n        #bds-app-wrapper input:focus, #bds-app-wrapper select:focus { border-color: var(--bds-primary); box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2); outline: none; }\n        #bds-app-wrapper button { display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem; min-height: 44px; padding: 10px 16px; font-weight: 600; border: none; border-radius: 8px; cursor: pointer; color: var(--m3-on-primary); background-color: var(--bds-primary); box-shadow: var(--bds-shadow); }\n        #bds-app-wrapper button:hover:not(:disabled) { transform: translateY(-1px); box-shadow: var(--bds-shadow-lg); }\n        #bds-app-wrapper button:active:not(:disabled) { transform: translateY(0); }\n        #bds-app-wrapper button:disabled { background-color: var(--bds-medium-gray); cursor: not-allowed; box-shadow: none; }\n        #bds-app-wrapper button.btn-secondary { background-color: var(--bds-secondary); color: #fff; }\n        #bds-app-wrapper button.btn-danger { background-color: var(--bds-danger); color: #fff; }\n        #bds-app-wrapper button.btn-info { background-color: var(--bds-info); color: #fff; }\n        #bds-app-wrapper button.btn-warning { background-color: var(--bds-warning); color: #1f2937; }\n        #bds-app-wrapper button.btn-toggle { background-color: var(--bds-card-bg); color: var(--bds-text); border: 1px solid var(--bds-border); box-shadow: none; }\n\n        header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem; }\n        .bds-app-title { display: flex; align-items: center; gap: 0.75rem; flex-grow: 1; }\n        .bds-logo { height: 40px; width: auto; border-radius: 6px; }\n        .version-tag { font-size: 0.8rem; font-weight: 500; color: var(--bds-text-light); background-color: var(--bds-light-gray); padding: 2px 8px; border-radius: 8px; vertical-align: middle; margin-left: 0.75rem; }\n\n        .bds-card { background: var(--bds-card-bg); padding: 1.25rem; border-radius: var(--bds-radius); box-shadow: var(--bds-shadow); margin-bottom: 1.25rem; }\n\n        .court-visual { display: flex; align-items: center; justify-content: space-around; margin: 1.25rem 0; }\n        .player-box { background: var(--bds-primary); color: white; padding: 12px; border-radius: var(--bds-radius); font-weight: 600; display: flex; align-items: center; justify-content: center; gap: 8px; width: 45%; min-height: 44px; }\n        .player-box.right { background: var(--bds-secondary); }\n        .player-skill { display: inline-flex; align-items: center; font-size: 0.75rem; padding: 2px 6px; border-radius: 999px; background: rgba(255,255,255,0.18); }\n        .vs-divider { font-size: 1.1rem; font-weight: 700; color: var(--bds-dark-gray); }\n\n        .court-actions { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 0.5rem; }\n        .court-action-buttons { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }\n\n        .winner-selection { display: none; background: var(--bds-light-gray); padding: 1rem; border-radius: var(--bds-radius); margin-top: 0.5rem; }\n\n        .player-tag-container { display: flex; flex-wrap: wrap; gap: 0.5rem; }\n        .player-tag { display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px; border-radius: 999px; background-color: var(--bds-light-gray); font-size: 0.9rem; color: var(--bds-text); }\n        .player-tag .skill-dot { width: 8px; height: 8px; border-radius: 999px; display: inline-block; }\n        .skill-Beg { background: #f97316; }  \/* orange *\/\n        .skill-Int { background: #3b82f6; }  \/* blue *\/\n        .skill-Adv { background: #10b981; }  \/* green *\/\n\n        .pending-list { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 0.5rem; }\n        .pending-item { display: flex; justify-content: space-between; align-items: center; padding: 10px; background: var(--bds-light-gray); border-radius: 10px; }\n        .pending-item .meta { display: inline-flex; align-items: center; gap: 8px; font-weight: 600; }\n        .pending-item .remove-btn { background: transparent; color: var(--bds-danger); border: 1px solid var(--bds-danger); box-shadow: none; padding: 6px 10px; border-radius: 8px; }\n\n        .modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.45); z-index: 10000; display: none; align-items: flex-end; }\n        .modal-overlay.is-open { display: flex; }\n        .modal-container { background: var(--bds-card-bg); width: 100%; max-width: 600px; margin: 0 auto; border-top-left-radius: 20px; border-top-right-radius: 20px; animation: bds-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1); max-height: 90vh; display: flex; flex-direction: column; }\n        @keyframes bds-slide-up { from { transform: translateY(100%); } to { transform: translateY(0); } }\n        .modal-header { display: flex; justify-content: space-between; align-items: center; padding: 1.25rem; flex-shrink: 0; border-bottom: 1px solid var(--bds-border); }\n        .modal-body { padding: 1.25rem; overflow-y: auto; flex-grow: 1; }\n        .modal-footer { display: flex; justify-content: flex-end; gap: 0.5rem; padding: 0.75rem 1.25rem; flex-shrink: 0; border-top: 1px solid var(--bds-border); background-color: var(--bds-light-gray); }\n        .close-modal-btn { background: none; color: var(--bds-text); box-shadow: none; border: 1px solid var(--bds-border); }\n\n        .bottom-nav { position: fixed; bottom: 0; left: 0; right: 0; background: var(--bds-card-bg); padding: 0.5rem 1rem; box-shadow: 0 -2px 10px rgba(0,0,0,0.1); display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.5rem; max-width: 600px; margin: 0 auto; z-index: 9999; border-top: 1px solid var(--bds-border); }\n        .nav-btn { display: flex; flex-direction: column; align-items: center; gap: 4px; background: none; color: var(--bds-text-light); padding: 8px 4px; border-radius: var(--bds-radius); font-size: 0.8rem; box-shadow: none; }\n        .nav-btn:hover { background: var(--bds-light-gray); color: var(--bds-text); }\n\n        .notification { position: fixed; top: -100px; left: 50%; transform: translateX(-50%); padding: 14px 24px; border-radius: var(--bds-radius); color: white; font-weight: 600; z-index: 10001; text-align: center; transition: top 0.3s ease-in-out, opacity 0.3s ease-in-out; background-color: var(--bds-primary); box-shadow: var(--bds-shadow-lg); }\n        .notification.show { top: 60px; opacity: 1; }\n        .notification.error { background-color: var(--bds-danger); }\n\n        .hidden { display: none !important; }\n\n        .modal-tabs { display: flex; border-bottom: 1px solid var(--bds-border); margin-bottom: 1rem; }\n        .tab-link { flex: 1; background: none; border: none; color: var(--bds-text-light); padding: 1rem 0; border-bottom: 3px solid transparent; border-radius: 0; margin-bottom: -1px; font-weight: 600; }\n        .tab-link.active { color: var(--bds-primary); border-bottom-color: var(--bds-primary); }\n\n        \/* Theme switch slider *\/\n        .theme-switch { position: relative; display: inline-block; width: 50px; height: 28px; }\n        .theme-switch input { opacity: 0; width: 0; height: 0; }\n        .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: var(--bds-medium-gray); transition: .4s; border-radius: 28px; }\n        .slider:before { position: absolute; content: \"\"; height: 20px; width: 20px; left: 4px; bottom: 4px; background-color: white; transition: .4s; border-radius: 50%; }\n        input:checked + .slider { background-color: var(--bds-primary); }\n        input:checked + .slider:before { transform: translateX(22px); }\n\n        \/* Help content *\/\n        .help-content h4 { margin-top: 1rem; margin-bottom: 0.5rem; color: var(--bds-primary); }\n        .help-content ul { list-style: none; padding-left: 0; margin-bottom: 1rem; }\n        .help-content li { padding-left: 1.5rem; position: relative; margin-bottom: 0.75rem; }\n        .help-content li::before { content: '\u2022'; position: absolute; left: 0; top: 0; color: var(--bds-primary); font-size: 1.2rem; line-height: 1; }\n        .help-content code { background: var(--bds-light-gray); padding: 0 6px; border-radius: 6px; }\n    <\/style>\n\n    <div id=\"bds-app-wrapper\">\n        <div id=\"notification\" class=\"notification\"><\/div>\n        <div class=\"app-container\">\n            <header>\n                <div class=\"bds-app-title\">\n                    <img decoding=\"async\" src=\"https:\/\/badminton.ghancaballes.com\/single-skilled\/wp-content\/uploads\/2025\/07\/blacksheeptext.png\" alt=\"Logo\" class=\"bds-logo\">\n                    <h1>BlackSheep Shuffler \u2014 Singles <span class=\"version-tag\">v1.0.0<\/span><\/h1>\n                <\/div>\n                <div class=\"theme-switch-wrapper\">\n                    <label class=\"theme-switch\" for=\"theme-checkbox\" title=\"Toggle Light\/Dark Mode\">\n                        <input type=\"checkbox\" id=\"theme-checkbox\" \/>\n                        <div class=\"slider\"><\/div>\n                    <\/label>\n                <\/div>\n            <\/header>\n\n            <!-- Initial Setup: Add-by-1 with skill and pending list -->\n            <div id=\"setupControls\" class=\"bds-card\">\n                <h3>Initial Setup<\/h3>\n                <div>\n                    <label for=\"playerNameInput\">Player Name<\/label>\n                    <input type=\"text\" id=\"playerNameInput\" placeholder=\"Enter player name...\" \/>\n                <\/div>\n                <div>\n                    <label for=\"playerSkillSelect\">Skill<\/label>\n                    <select id=\"playerSkillSelect\">\n                        <option value=\"Beg\">Beginner<\/option>\n                        <option value=\"Int\" selected>Intermediate<\/option>\n                        <option value=\"Adv\">Advanced<\/option>\n                    <\/select>\n                <\/div>\n                <button id=\"addPendingPlayerBtn\" class=\"btn-secondary\" style=\"width:100%; margin-bottom:0.75rem;\">Add Player<\/button>\n\n                <div>\n                    <h4>Players to Start<\/h4>\n                    <div id=\"pendingPlayersList\" class=\"pending-list\"><\/div>\n                <\/div>\n\n                <div>\n                    <label for=\"courtCount\">Number of courts<\/label>\n                    <input type=\"number\" id=\"courtCount\" placeholder=\"Number of courts\" min=\"1\" max=\"10\" \/>\n                <\/div>\n                <div style=\"display:grid; grid-template-columns:1fr 1fr; gap:0.75rem; margin-top:0.5rem;\">\n                    <button id=\"aboutBtn\" class=\"btn-info\">About<\/button>\n                    <button id=\"startBtn\" class=\"btn-primary\" disabled>Start<\/button>\n                <\/div>\n            <\/div>\n\n            <div id=\"results\"><\/div>\n        <\/div>\n\n        <div class=\"bottom-nav hidden\" id=\"actionBar\">\n            <button id=\"statsBtn\" class=\"nav-btn\">\n                <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 20v-6M6 20v-10M18 20V4\"\/><\/svg><span>Stats<\/span>\n            <\/button>\n            <button id=\"historyBtn\" class=\"nav-btn\">\n                <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M1 4v6h6M23 20v-6h-6\"\/><path d=\"M20.49 9A9 9 0 0 0 7.54 5.04M3.51 15a9 9 0 0 0 12.95 4.97\"\/><\/svg><span>History<\/span>\n            <\/button>\n            <button id=\"manageGameBtn\" class=\"nav-btn\">\n                <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12.22 2h-4.44a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8.88z\"\/><path d=\"M18 2v6h6M12 18v-6M9 15h6\"\/><\/svg><span>Manage<\/span>\n            <\/button>\n            <button id=\"helpBtn\" class=\"nav-btn\">\n                <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"\/><path d=\"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3\"\/><path d=\"M12 17h.01\"\/><\/svg><span>Help<\/span>\n            <\/button>\n        <\/div>\n\n        <!-- Management Modal -->\n        <div class=\"modal-overlay\" id=\"managementModal\">\n            <div class=\"modal-container\">\n                <div class=\"modal-header\"><h3>Management<\/h3><\/div>\n                <div class=\"modal-body\">\n                    <div id=\"mainManagementPanel\">\n                        <div class=\"management-section\">\n                            <h5>Add New Player<\/h5>\n                            <input id=\"mgmtNewPlayerName\" type=\"text\" placeholder=\"Enter player name...\" \/>\n                            <select id=\"mgmtNewPlayerSkill\">\n                                <option value=\"Beg\">Beginner<\/option>\n                                <option value=\"Int\" selected>Intermediate<\/option>\n                                <option value=\"Adv\">Advanced<\/option>\n                            <\/select>\n                            <button id=\"addPlayerBtn\" class=\"btn-primary\">Add Player<\/button>\n                        <\/div>\n\n                        <div class=\"management-section\">\n                            <h5>Change Skill<\/h5>\n                            <select id=\"skillChangePlayerSelect\"><option value=\"\">Select Player<\/option><\/select>\n                            <select id=\"skillChangeSkillSelect\">\n                                <option value=\"Beg\">Beginner<\/option>\n                                <option value=\"Int\" selected>Intermediate<\/option>\n                                <option value=\"Adv\">Advanced<\/option>\n                            <\/select>\n                            <button id=\"changeSkillBtn\" class=\"btn-secondary\">Update Skill<\/button>\n                        <\/div>\n\n                        <div class=\"management-section\">\n                            <h5>Remove Player<\/h5>\n                            <select id=\"playerToRemove\"><option value=\"\">Select Player<\/option><\/select>\n                            <button id=\"removePlayerBtn\" class=\"btn-danger\">Remove Selected Player<\/button>\n                        <\/div>\n\n                        <div class=\"management-section\">\n                            <h5>Court Management<\/h5>\n                            <div style=\"display:grid; grid-template-columns:1fr 1fr; gap:0.75rem;\">\n                                <button id=\"addCourtBtn\" class=\"btn-primary\">Add Court<\/button>\n                                <div>\n                                    <select id=\"courtToRemove\" style=\"margin-bottom:0;\"><option value=\"\">Select Court<\/option><\/select>\n                                    <button id=\"removeCourtBtn\" class=\"btn-danger\" style=\"margin-top:0.5rem;\">Remove Selected<\/button>\n                                <\/div>\n                            <\/div>\n                        <\/div>\n\n                        <div class=\"management-section\">\n                            <h5>Game Actions<\/h5>\n                            <button id=\"undoBtn\" class=\"btn-secondary hidden\">Undo Last Action<\/button>\n                            <button id=\"resetGameBtn\" class=\"btn-danger\" style=\"margin-top:0.5rem;\">Reset Everything<\/button>\n                        <\/div>\n                    <\/div>\n\n                    <div id=\"editTeamsPanel\" class=\"hidden\">\n                        <h4 id=\"editCourtTitle\"><\/h4>\n                        <div style=\"display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; margin-bottom: 0.75rem;\">\n                            <div>\n                                <h5>Player Left<\/h5>\n                                <select id=\"editPlayerL\"><\/select>\n                            <\/div>\n                            <div>\n                                <h5>Player Right<\/h5>\n                                <select id=\"editPlayerR\"><\/select>\n                            <\/div>\n                        <\/div>\n                        <button id=\"saveTeamsBtn\" class=\"btn-primary\">Save Players<\/button>\n                        <button data-action=\"cancel-submodal\" class=\"btn-secondary\" style=\"margin-top:0.5rem;\">Cancel<\/button>\n                    <\/div>\n\n                    <div id=\"swapPlayerPanel\" class=\"hidden\">\n                        <h4 id=\"swapCourtTitle\"><\/h4>\n                        <div class=\"management-section\">\n                            <label for=\"playerToSwapOut\">Player to remove:<\/label>\n                            <select id=\"playerToSwapOut\"><\/select>\n                        <\/div>\n                        <div class=\"management-section\">\n                            <label for=\"playerToSwapIn\">Player to add:<\/label>\n                            <select id=\"playerToSwapIn\"><\/select>\n                        <\/div>\n                        <button id=\"confirmSwapBtn\" class=\"btn-primary\">Confirm Swap<\/button>\n                        <button data-action=\"cancel-submodal\" class=\"btn-secondary\" style=\"margin-top:0.5rem;\">Cancel<\/button>\n                    <\/div>\n                <\/div>\n                <div class=\"modal-footer\"><button class=\"close-modal-btn\">Close<\/button><\/div>\n            <\/div>\n        <\/div>\n\n        <!-- Help Modal -->\n        <div class=\"modal-overlay\" id=\"helpModal\">\n            <div class=\"modal-container\">\n                <div class=\"modal-header\"><h3>Help & How-To<\/h3><\/div>\n                <div class=\"modal-body\">\n                    <div class=\"help-content\">\n                        <h4>Quick Start<\/h4>\n                        <ul>\n                            <li>Add players one by one with their skill (Beginner, Intermediate, Advanced).<\/li>\n                            <li>Set the number of courts and press Start. You need at least 2 players and enough players to fill the courts.<\/li>\n                            <li>The app assigns matches by prioritizing those who rested longest and keeping opponents varied.<\/li>\n                        <\/ul>\n\n                        <h4>Completing Matches<\/h4>\n                        <ul>\n                            <li>Tap Complete Match, then choose the winner (Left or Right). No scores required.<\/li>\n                            <li>Players move to the resting list after each match; new matches fill automatically when possible.<\/li>\n                        <\/ul>\n\n                        <h4>Managing Players & Courts<\/h4>\n                        <ul>\n                            <li>Use the Manage panel to add players (with skill), change a player\u2019s skill, remove players, add\/remove courts, and undo the last action.<\/li>\n                            <li>Changing skills affects future matches and manual edits\/swaps; it won\u2019t alter ongoing matches.<\/li>\n                            <li>You cannot remove a player who\u2019s currently in a match.<\/li>\n                        <\/ul>\n\n                        <h4>Editing & Swapping<\/h4>\n                        <ul>\n                            <li>Edit lets you change the two players on a court. Swapping replaces one in-match player with someone resting.<\/li>\n                            <li>The app validates choices and blocks invalid combinations.<\/li>\n                        <\/ul>\n\n                        <h4>Stats & History<\/h4>\n                        <ul>\n                            <li>Player Stats shows total matches and wins-losses, and when a player left if removed.<\/li>\n                            <li>Pairing Stats shows how often two players have faced each other.<\/li>\n                            <li>History lists all matches and winners in reverse order.<\/li>\n                        <\/ul>\n\n                        <h4>Tips<\/h4>\n                        <ul>\n                            <li>If a court can\u2019t be filled right away, the app waits until compatible players are available.<\/li>\n                            <li>Use the skill change tool to keep matches balanced as players arrive or improve.<\/li>\n                            <li>Theme toggle in the header switches between light and dark modes.<\/li>\n                        <\/ul>\n                    <\/div>\n                <\/div>\n                <div class=\"modal-footer\"><button class=\"close-modal-btn\">Close<\/button><\/div>\n            <\/div>\n        <\/div>\n\n        <!-- Stats Modal -->\n        <div class=\"modal-overlay\" id=\"statsModal\">\n            <div class=\"modal-container\">\n                <div class=\"modal-header\"><h3>Statistics<\/h3><small id=\"stats-date\"><\/small><\/div>\n                <div class=\"modal-body\">\n                    <div class=\"modal-tabs\">\n                        <button class=\"tab-link active\" data-tab=\"player-stats-content\">Player Stats<\/button>\n                        <button class=\"tab-link\" data-tab=\"pairing-stats-content\">Pairing Stats<\/button>\n                    <\/div>\n                    <div id=\"player-stats-content\" class=\"tab-content active\"><\/div>\n                    <div id=\"pairing-stats-content\" class=\"tab-content\">\n                        <select id=\"pairingPlayerSelect\" style=\"margin-bottom: 1rem;\"><\/select>\n                        <div id=\"pairing-results\"><\/div>\n                    <\/div>\n                <\/div>\n                <div class=\"modal-footer\">\n                    <button class=\"close-modal-btn\">Close<\/button>\n                <\/div>\n            <\/div>\n        <\/div>\n\n        <!-- History Modal -->\n        <div class=\"modal-overlay\" id=\"historyModal\">\n            <div class=\"modal-container\">\n                <div class=\"modal-header\"><h3>Match History<\/h3><\/div>\n                <div class=\"modal-body\" id=\"historyModalBody\"><\/div>\n                <div class=\"modal-footer\"><button class=\"close-modal-btn\">Close<\/button><\/div>\n            <\/div>\n        <\/div>\n\n        <!-- About Modal -->\n        <div class=\"modal-overlay\" id=\"aboutModal\">\n            <div class=\"modal-container\">\n                <div class=\"modal-header\"><h3>About & How-To<\/h3><\/div>\n                <div class=\"modal-body\">\n                    <div class=\"about-content\">\n                        <p><strong>Author:<\/strong> Ghan Caballes<\/p>\n                        <p><strong>Collaborator:<\/strong> Van Ruiz, Aljun Montebon, Owen Pelito<\/p>\n                        <p><strong>Version:<\/strong> 1.0.0<\/p>\n                    <\/div>\n                    <div class=\"help-content\">\n                        <h4>How to Use<\/h4>\n                        <ul>\n                            <li>Add players with skill, set courts, press Start.<\/li>\n                            <li>Complete matches by choosing the winner; new matches auto-fill when possible.<\/li>\n                            <li>Manage skills, players, and courts in the Manage panel. Undo is available.<\/li>\n                        <\/ul>\n\n                        <h4>Fair Shuffle<\/h4>\n                        <ul>\n                            <li>Prioritizes players who rested longest.<\/li>\n                            <li>Minimizes repeat opponents among available options.<\/li>\n                            <li>If no match fits at the moment, courts wait for compatible players.<\/li>\n                        <\/ul>\n                    <\/div>\n                <\/div>\n                <div class=\"modal-footer\"><button class=\"close-modal-btn\">Close<\/button><\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <script>\n    document.addEventListener('DOMContentLoaded', () => {\n        const appWrapper = document.getElementById('bds-app-wrapper');\n        if (!appWrapper) { return; }\n\n        \/\/ Pending (pre-start) players: [{ name, lower, skill }]\n        let pendingPlayers = [];\n\n        \/\/ State\n        let state = {\n            courts: [], \/\/ [{ players:[pL,pR], left:pL, right:pR, startTime }]\n            restingPlayers: [],\n            playCount: {}, \/\/ { name: { wins, losses, games, isActive, lastGameEndTime, removalTimestamp } }\n            matchHistory: [], \/\/ { court, left, right, winner('L'|'R'), startTime, endTime }\n            opponentPairings: {}, \/\/ key=p1|p2 => count\n            allPlayersList: [],\n            playerMeta: {}, \/\/ lowerName => { name, skill, isActive:true }\n            gameInProgress: false,\n            undoStack: []\n        };\n\n        const RESTRICTED_VS_BEGINNER = new Set(['chad','charie','chris','hannah','tope','angel','bobby']);\n\n        \/\/ Helpers\n        const get = (sel, parent=document) => parent.querySelector(sel);\n        const getAll = (sel, parent=document) => parent.querySelectorAll(sel);\n        const showNotification = (msg, type='success') => { const el = get('#notification'); el.textContent = msg; el.className = `notification ${type} show`; setTimeout(() => el.classList.remove('show'), 2200); };\n        const normalizeName = (n) => (n || '').trim().toLowerCase();\n        const getSkill = (name) => state.playerMeta[normalizeName(name)]?.skill || 'Int';\n        const isBeg = (name) => getSkill(name) === 'Beg';\n        const isAdv = (name) => getSkill(name) === 'Adv';\n        const isRestrictedName = (name) => RESTRICTED_VS_BEGINNER.has(normalizeName(name));\n        const getPairKey = (a,b) => [a,b].sort().join('|');\n\n        const saveState = () => { if (state.gameInProgress) { const s = { ...state, undoStack: [] }; localStorage.setItem('badmintonSinglesState', JSON.stringify(s)); } };\n        const loadState = () => {\n            const raw = localStorage.getItem('badmintonSinglesState');\n            if (!raw) return;\n            try {\n                const loaded = JSON.parse(raw);\n                if (loaded && loaded.gameInProgress) {\n                    if (!loaded.undoStack) loaded.undoStack = [];\n                    if (!loaded.playerMeta) {\n                        loaded.playerMeta = {};\n                        (loaded.allPlayersList||[]).forEach(n => { const ln = normalizeName(n); if (!loaded.playerMeta[ln]) loaded.playerMeta[ln] = { name:n, skill:'Int', isActive:true }; });\n                    }\n                    state = loaded;\n                    get('#setupControls').classList.add('hidden');\n                    get('#actionBar').classList.remove('hidden');\n                    renderAll();\n                }\n            } catch(e){ console.error(e); localStorage.removeItem('badmintonSinglesState'); }\n        };\n\n        const applyTheme = (theme) => { if (theme==='dark'){ appWrapper.classList.add('dark-mode'); get('#theme-checkbox').checked = true; } else { appWrapper.classList.remove('dark-mode'); get('#theme-checkbox').checked = false; } };\n        const loadTheme = () => applyTheme(localStorage.getItem('theme') || 'light');\n\n        const getCombinations = (array, size) => {\n            function* f(prefix, rest) {\n                if (prefix.length === size) { yield prefix; return; }\n                for (let i=0;i<rest.length;i++) yield* f(prefix.concat(rest[i]), rest.slice(i+1));\n            }\n            return Array.from(f([], array));\n        };\n\n        const commandProcessor = {\n            execute(cmd) {\n                const before = JSON.parse(JSON.stringify(state));\n                const ok = cmd.execute();\n                if (ok) {\n                    state.undoStack.push(() => { state = before; });\n                    if (state.undoStack.length > 10) state.undoStack.shift();\n                }\n                renderAll();\n                return ok;\n            },\n            undo() {\n                if (!state.undoStack.length) { showNotification('Nothing to undo.','error'); return; }\n                const u = state.undoStack.pop();\n                u();\n                renderAll();\n                showNotification('Last action has been undone.');\n            }\n        };\n\n        const validateMatch = (left, right) => {\n            \/\/ Confidential constraints enforced silently\n            if ((isBeg(left) && isAdv(right)) || (isBeg(right) && isAdv(left))) {\n                return { ok:false, msg:'Invalid combination.' };\n            }\n            if (isBeg(left) && isRestrictedName(right)) return { ok:false, msg:'Invalid combination.' };\n            if (isBeg(right) && isRestrictedName(left)) return { ok:false, msg:'Invalid combination.' };\n            return { ok:true, msg:'' };\n        };\n\n        const initOpponentPairings = (players) => {\n            state.opponentPairings = state.opponentPairings || {};\n            for (let i=0;i<players.length;i++){\n                for (let j=i+1;j<players.length;j++){\n                    const key = getPairKey(players[i], players[j]);\n                    if (!(key in state.opponentPairings)) state.opponentPairings[key] = 0;\n                }\n            }\n        };\n\n        const updateOpponentPairing = (left, right, inc=1) => {\n            const key = getPairKey(left, right);\n            state.opponentPairings[key] = (state.opponentPairings[key] || 0) + inc;\n        };\n\n        const findBestMatch = (candidates) => {\n            if (candidates.length < 2) return null;\n            const pairs = getCombinations(candidates, 2);\n            let best = null; let bestScore = Infinity;\n            for (const [p1, p2] of pairs) {\n                const v = validateMatch(p1, p2);\n                if (!v.ok) continue;\n                const score = state.opponentPairings[getPairKey(p1,p2)] || 0;\n                if (score < bestScore) {\n                    bestScore = score;\n                    best = { players:[p1,p2], left:p1, right:p2 };\n                }\n            }\n            return best;\n        };\n\n        const refillAllEmptyCourts = () => {\n            let avail = [...state.restingPlayers];\n            const empty = state.courts.map((c,i)=>i).filter(i => !state.courts[i].players || state.courts[i].players.length===0);\n            for (const ci of empty) {\n                if (avail.length < 2) break;\n                const pq = avail.sort((a,b) => (state.playCount[a].lastGameEndTime||0) - (state.playCount[b].lastGameEndTime||0));\n                const poolSizes = [6, 10, 14];\n                let placed = false;\n                for (const size of poolSizes) {\n                    const candidates = pq.slice(0, size);\n                    const best = findBestMatch(candidates);\n                    if (best) {\n                        state.courts[ci] = { players: best.players, left: best.left, right: best.right, startTime: new Date().toISOString() };\n                        updateOpponentPairing(best.left, best.right, 1);\n                        avail = avail.filter(p => !best.players.includes(p));\n                        placed = true;\n                        break;\n                    }\n                }\n                if (!placed) {\n                    showNotification(`No compatible match for Court ${ci+1} yet.`, 'error');\n                    break;\n                }\n            }\n            state.restingPlayers = avail;\n        };\n\n        const RecordWinnerCommand = (courtIndex, winner) => ({\n            execute: () => {\n                const court = state.courts[courtIndex];\n                if (!court || !court.players || court.players.length !== 2) return false;\n                const { left, right, startTime, players } = court;\n                const now = Date.now();\n\n                [left, right].forEach(p => {\n                    const ps = state.playCount[p];\n                    ps.games++;\n                    ps.lastGameEndTime = now;\n                });\n                if (winner === 'L') state.playCount[left].wins++, state.playCount[right].losses++;\n                else state.playCount[right].wins++, state.playCount[left].losses++;\n\n                state.restingPlayers.push(...players);\n                state.matchHistory.push({ court: courtIndex+1, left, right, winner, startTime, endTime: new Date(now).toISOString() });\n                state.courts[courtIndex] = { players: [], left: null, right: null, startTime: null };\n                showNotification('Winner Recorded!');\n                refillAllEmptyCourts();\n                return true;\n            }\n        });\n\n        const SavePlayersCommand = (courtIndex, newLeft, newRight) => ({\n            execute: () => {\n                if (!newLeft || !newRight || newLeft === newRight) {\n                    showNotification('Choose two distinct players.','error'); return false;\n                }\n                const v = validateMatch(newLeft, newRight);\n                if (!v.ok) { showNotification(v.msg,'error'); return false; }\n\n                const court = state.courts[courtIndex];\n                \/\/ rollback previous pairing weight\n                if (court.left && court.right) updateOpponentPairing(court.left, court.right, -1);\n\n                court.left = newLeft;\n                court.right = newRight;\n                court.players = [newLeft, newRight];\n                updateOpponentPairing(newLeft, newRight, 1);\n                showNotification(`Players for Court ${courtIndex+1} updated.`);\n                closeModal('managementModal');\n                return true;\n            }\n        });\n\n        const SwapPlayerCommand = (courtIndex, playerOut, playerIn) => ({\n            execute: () => {\n                const court = state.courts[courtIndex];\n                if (!court || !playerOut || !playerIn) return false;\n                let newLeft = court.left;\n                let newRight = court.right;\n\n                if (newLeft === playerOut) newLeft = playerIn;\n                else if (newRight === playerOut) newRight = playerIn;\n                else { showNotification('Selected player is not on this court.','error'); return false; }\n\n                const v = validateMatch(newLeft, newRight);\n                if (!v.ok) { showNotification(v.msg,'error'); return false; }\n\n                \/\/ Update pairing counts\n                updateOpponentPairing(court.left, court.right, -1);\n                court.left = newLeft;\n                court.right = newRight;\n                court.players = [newLeft, newRight];\n                updateOpponentPairing(court.left, court.right, 1);\n\n                state.restingPlayers = state.restingPlayers.filter(p => p !== playerIn);\n                state.restingPlayers.push(playerOut);\n\n                showNotification(`${playerIn} swapped with ${playerOut}.`);\n                closeModal('managementModal');\n                return true;\n            }\n        });\n\n        \/\/ Renderers\n        const renderEmptyState = (icon, message) => `<div class=\"empty-state\" style=\"text-align:center; padding:1.5rem; border:2px dashed var(--bds-border); border-radius:var(--bds-radius);\"><div class=\"icon\" style=\"font-size:2rem; opacity:0.6;\">${icon}<\/div><p style=\"margin-top:0.5rem;\">${message}<\/p><\/div>`;\n        const skillLabel = (name) => name ? `<span class=\"player-skill\">${getSkill(name)}<\/span>` : '';\n\n        const renderAll = () => {\n            const resultsEl = get('#results');\n            resultsEl.innerHTML = '';\n            state.courts.forEach((court, idx) => resultsEl.appendChild(renderCourt(court, idx)));\n            resultsEl.appendChild(renderResting());\n            get('#undoBtn').classList.toggle('hidden', state.undoStack.length === 0);\n            updateDropdowns();\n            saveState();\n        };\n\n        const renderCourt = (court, idx) => {\n            const div = document.createElement('div');\n            div.className = 'bds-card court';\n            div.dataset.court = idx;\n            const hasPlayers = court.players && court.players.length === 2;\n\n            const leftBox = `<div class=\"player-box\">${court.left ? `${court.left} ${skillLabel(court.left)}` : '--'}<\/div>`;\n            const rightBox = `<div class=\"player-box right\">${court.right ? `${court.right} ${skillLabel(court.right)}` : '--'}<\/div>`;\n\n            const buttonsHTML = hasPlayers ? `\n                <button class=\"btn-primary complete-button\" data-action=\"complete\">Complete Match<\/button>\n                <div class=\"winner-selection\">\n                    <div style=\"display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem;\">\n                        <button data-action=\"record-winner-left\" class=\"btn-primary\">Left Won<\/button>\n                        <button data-action=\"record-winner-right\" class=\"btn-secondary\">Right Won<\/button>\n                    <\/div>\n                    <div style=\"margin-top:0.5rem;\">\n                        <button data-action=\"cancel-win\" class=\"btn-toggle\" style=\"width:100%;\">Cancel<\/button>\n                    <\/div>\n                <\/div>\n                <div class=\"court-action-buttons\">\n                    <button class=\"btn-info\" data-action=\"edit-players\">Edit<\/button>\n                    <button class=\"btn-warning\" data-action=\"swap\">Swap<\/button>\n                <\/div>\n            ` : renderEmptyState('\ud83c\udff8', 'Waiting for players...');\n\n            div.innerHTML = `\n                <h3>Court ${idx + 1}<\/h3>\n                <div class=\"court-visual\">\n                    ${leftBox}\n                    <div class=\"vs-divider\">VS<\/div>\n                    ${rightBox}\n                <\/div>\n                <div class=\"court-actions\">${buttonsHTML}<\/div>\n            `;\n            return div;\n        };\n\n        const renderResting = () => {\n            const wrap = document.createElement('div');\n            wrap.className = 'bds-card';\n            const players = state.restingPlayers;\n            const content = players.length ? `\n                <div class=\"player-tag-container\">\n                    ${[...players]\n                        .sort((a,b) => (state.playCount[a]?.lastGameEndTime||0) - (state.playCount[b]?.lastGameEndTime||0))\n                        .map(p => `<span class=\"player-tag\"><span class=\"skill-dot skill-${getSkill(p)}\"><\/span> ${p} (${state.playCount[p]?.games||0})<\/span>`).join('')}\n                <\/div>\n            ` : renderEmptyState('\ud83d\ude34','All players are on court!');\n            wrap.innerHTML = `<h4>Resting Players (${players.length})<\/h4>${content}`;\n            return wrap;\n        };\n\n        const populateSelect = (el, options, placeholder, currentVal) => {\n            if (!el) return;\n            el.innerHTML = '';\n            if (placeholder !== undefined) {\n                const ph = document.createElement('option');\n                ph.value = '';\n                ph.textContent = placeholder;\n                el.appendChild(ph);\n            }\n            options.forEach(o => {\n                const opt = document.createElement('option');\n                if (typeof o === 'object') { opt.value = o.value; opt.textContent = o.text; }\n                else { opt.value = o; opt.textContent = o; }\n                el.appendChild(opt);\n            });\n            if (currentVal) el.value = currentVal;\n        };\n\n        const updateDropdowns = () => {\n            const active = state.allPlayersList.filter(p => state.playCount[p]?.isActive);\n            const activeOptions = active.map(p => ({ value: p, text: `${p} (${getSkill(p)})` }));\n            const courtOptions = state.courts.map((c,i)=>`Court ${i+1}`);\n            populateSelect(get('#playerToRemove'), activeOptions, 'Select Player');\n            populateSelect(get('#skillChangePlayerSelect'), activeOptions, 'Select Player');\n            populateSelect(get('#courtToRemove'), courtOptions, 'Select Court');\n            populateSelect(get('#pairingPlayerSelect'), state.allPlayersList, 'Select a player');\n        };\n\n        const renderPlayerStats = () => {\n            const c = get('#player-stats-content');\n            c.innerHTML = '';\n            const list = state.allPlayersList.map(p => ({ name:p, skill:getSkill(p), ...state.playCount[p] }));\n            list.sort((a,b) => (b.isActive - a.isActive) || (b.games - a.games) || (b.wins - a.wins));\n            if (!list.length) { c.innerHTML = renderEmptyState('\ud83d\udcca','No stats to show yet.'); return; }\n            list.forEach(({ name, skill, wins, losses, games, isActive, removalTimestamp }) => {\n                const row = document.createElement('div'); row.className = 'stat-row';\n                const info = `${games} Matches (${wins}W - ${losses}L)`;\n                const timeStr = isActive ? '' : `<div class=\"removal-info\">Left at ${new Date(removalTimestamp).toLocaleTimeString([], {hour:'2-digit', minute:'2-digit'})}<\/div>`;\n                row.innerHTML = `<span class=\"stat-player-name ${!isActive?'removed':''}\">${name} (${skill})<\/span><div class=\"stat-text\"><span>${info}<\/span>${timeStr}<\/div>`;\n                c.appendChild(row);\n            });\n            get('#stats-date').textContent = new Date().toLocaleDateString(undefined, {year:'numeric', month:'long', day:'numeric'});\n        };\n\n        const renderPairingStats = (selected) => {\n            const c = get('#pairing-results');\n            if (!selected) { c.innerHTML = renderEmptyState('\ud83e\udd1d','Select a player to see their opponent stats.'); return; }\n            const others = state.allPlayersList.filter(p => p !== selected);\n            if (!others.length) { c.innerHTML = renderEmptyState('\ud83e\udd37','No other players to compare.'); return; }\n            let html = '';\n            others.forEach(op => {\n                const oppCount = state.opponentPairings[getPairKey(selected, op)] || 0;\n                const info = state.playCount[op];\n                const isRemoved = !info.isActive;\n                const timeString = isRemoved ? new Date(info.removalTimestamp).toLocaleTimeString([], {hour:'2-digit', minute:'2-digit'}) : '';\n                const removalInfo = isRemoved ? `<span class=\"removal-info\">(Left at ${timeString})<\/span>` : '';\n                html += `<div class=\"stat-row\"><span class=\"stat-player-name ${isRemoved?'removed':''}\">${op} (${getSkill(op)}) ${removalInfo}<\/span><span>Opponent: ${oppCount}<\/span><\/div>`;\n            });\n            c.innerHTML = html || renderEmptyState('\ud83e\udd37','No matches yet.');\n        };\n\n        const renderHistory = () => {\n            const c = get('#historyModalBody');\n            c.innerHTML = '';\n            if (!state.matchHistory.length) { c.innerHTML = renderEmptyState('\ud83d\udcdc','No matches recorded.'); return; }\n            [...state.matchHistory].reverse().forEach(m => {\n                const leftWin = m.winner === 'L' ? 'winner' : '';\n                const rightWin = m.winner === 'R' ? 'winner' : '';\n                const item = document.createElement('div'); item.className = 'history-item';\n                item.innerHTML = `\n                    <div class=\"history-court\">C${m.court}<\/div>\n                    <div class=\"history-teams\"><span class=\"${leftWin}\">${m.left}<\/span> vs <span class=\"${rightWin}\">${m.right}<\/span><\/div>\n                `;\n                c.appendChild(item);\n            });\n        };\n\n        \/\/ Sheets removed entirely (none)\n\n        \/\/ Management actions\n        const addPlayerMidSession = () => {\n            const name = (get('#mgmtNewPlayerName').value || '').trim();\n            const skill = get('#mgmtNewPlayerSkill').value;\n            if (!name) return;\n            const lower = normalizeName(name);\n            if (state.allPlayersList.some(p => normalizeName(p) === lower)) { showNotification('Player already exists.','error'); return; }\n            state.allPlayersList.push(name);\n            state.playerMeta[lower] = { name, skill, isActive: true };\n            state.restingPlayers.push(name);\n            state.playCount[name] = { wins:0, losses:0, games:0, isActive:true, lastGameEndTime:0, removalTimestamp:null };\n            initOpponentPairings([name]);\n            get('#mgmtNewPlayerName').value = '';\n            showNotification(`${name} (${skill}) added.`);\n            renderAll();\n        };\n\n        const changeSkillForPlayer = () => {\n            const player = get('#skillChangePlayerSelect').value;\n            const newSkill = get('#skillChangeSkillSelect').value;\n            if (!player) { showNotification('Select a player.','error'); return; }\n            const lower = normalizeName(player);\n            if (!state.playerMeta[lower]) { showNotification('Player not found.','error'); return; }\n            state.playerMeta[lower].skill = newSkill;\n            showNotification(`Updated ${player} to ${newSkill}.`);\n            renderAll();\n        };\n\n        const removePlayer = () => {\n            const player = get('#playerToRemove').value;\n            if (!player) return;\n            if (state.courts.some(c => c.players.includes(player))) { showNotification('Cannot remove a player who is on a court.','error'); return; }\n            if (state.playCount[player]) {\n                state.playCount[player].isActive = false;\n                state.playCount[player].removalTimestamp = new Date().toISOString();\n            }\n            state.restingPlayers = state.restingPlayers.filter(p => p !== player);\n            showNotification(`${player} has been removed.`);\n            renderAll();\n        };\n\n        const addCourt = () => {\n            const activeCount = state.allPlayersList.filter(p => state.playCount[p]?.isActive).length;\n            const maxCourts = Math.floor(activeCount \/ 2);\n            if (state.courts.length >= maxCourts) { showNotification('Not enough players to add a new court.','error'); return; }\n            state.courts.push({ players: [], left: null, right: null, startTime: null });\n            showNotification('A new court has been added.');\n            refillAllEmptyCourts();\n            renderAll();\n        };\n\n        const removeCourt = () => {\n            const val = get('#courtToRemove').value;\n            if (!val) { showNotification('Please select a court.','error'); return; }\n            const idx = parseInt(val.replace('Court ', '')) - 1;\n            if (isNaN(idx) || idx < 0 || idx >= state.courts.length) { showNotification('Invalid court selected.','error'); return; }\n            const court = state.courts[idx];\n            if (court.players && court.players.length === 2) {\n                updateOpponentPairing(court.left, court.right, -1);\n                state.restingPlayers.push(...court.players);\n            }\n            state.courts.splice(idx, 1);\n            showNotification(`Court ${idx+1} has been removed.`);\n            renderAll();\n        };\n\n        const openEditModal = (idx) => {\n            const court = state.courts[idx];\n            get('#managementModal').dataset.editingCourt = idx;\n            get('#editCourtTitle').textContent = `Edit Players for Court ${idx+1}`;\n            populateSelect(get('#editPlayerL'), court.players, 'Select Player', court.left);\n            populateSelect(get('#editPlayerR'), court.players, 'Select Player', court.right);\n            get('#mainManagementPanel').classList.add('hidden');\n            get('#editTeamsPanel').classList.remove('hidden');\n            openModal('managementModal');\n        };\n\n        const openSwapModal = (idx) => {\n            const court = state.courts[idx];\n            get('#managementModal').dataset.editingCourt = idx;\n            get('#swapCourtTitle').textContent = `Swap Player on Court ${idx+1}`;\n            populateSelect(get('#playerToSwapOut'), court.players, 'Select player on court');\n            populateSelect(get('#playerToSwapIn'), state.restingPlayers, 'Select resting player');\n            get('#mainManagementPanel').classList.add('hidden');\n            get('#swapPlayerPanel').classList.remove('hidden');\n            openModal('managementModal');\n        };\n\n        \/\/ Pending setup\n        const renderPendingList = () => {\n            const list = get('#pendingPlayersList');\n            list.innerHTML = '';\n            if (!pendingPlayers.length) list.innerHTML = renderEmptyState('\u2795','Add players one by one to get started.');\n            else pendingPlayers.forEach((p, i) => {\n                const row = document.createElement('div'); row.className = 'pending-item';\n                row.innerHTML = `<div class=\"meta\">${p.name} <span class=\"player-skill\">${p.skill}<\/span><\/div><button class=\"remove-btn\" data-remove-index=\"${i}\">Remove<\/button>`;\n                list.appendChild(row);\n            });\n            validateStartEligibility();\n        };\n\n        const validateStartEligibility = () => {\n            const startBtn = get('#startBtn');\n            const courts = parseInt(get('#courtCount').value) || 0;\n            const ok = pendingPlayers.length >= 2 && courts >= 1 && courts <= Math.floor(pendingPlayers.length \/ 2);\n            startBtn.disabled = !ok;\n        };\n\n        const addPendingPlayer = () => {\n            const name = (get('#playerNameInput').value || '').trim();\n            const skill = get('#playerSkillSelect').value;\n            if (!name) { showNotification('Enter a player name.','error'); return; }\n            const lower = normalizeName(name);\n            if (!lower) { showNotification('Invalid name.','error'); return; }\n            if (pendingPlayers.some(p => p.lower === lower)) { showNotification('Player already added.','error'); return; }\n            pendingPlayers.push({ name, lower, skill });\n            get('#playerNameInput').value = '';\n            renderPendingList();\n        };\n\n        const initializeGameFromPending = () => {\n            const courtCount = parseInt(get('#courtCount').value);\n            if (!courtCount || courtCount < 1) { showNotification('Please enter a valid number of courts.','error'); return; }\n            if (pendingPlayers.length < 2) { showNotification('At least 2 players are required.','error'); return; }\n            if (courtCount > Math.floor(pendingPlayers.length \/ 2)) { showNotification('Not enough players for the number of courts specified.','error'); return; }\n\n            const names = pendingPlayers.map(p => p.name);\n            state = {\n                courts: Array.from({ length: courtCount }, () => ({ players: [], left: null, right: null, startTime: null })),\n                restingPlayers: [...names],\n                playCount: {},\n                matchHistory: [],\n                opponentPairings: {},\n                allPlayersList: [...names],\n                playerMeta: {},\n                gameInProgress: true,\n                undoStack: []\n            };\n\n            pendingPlayers.forEach(p => {\n                const lower = normalizeName(p.name);\n                state.playerMeta[lower] = { name: p.name, skill: p.skill, isActive: true };\n                state.playCount[p.name] = { wins:0, losses:0, games:0, isActive:true, lastGameEndTime:0, removalTimestamp:null };\n            });\n            initOpponentPairings(names);\n\n            get('#setupControls').classList.add('hidden');\n            get('#actionBar').classList.remove('hidden');\n\n            refillAllEmptyCourts();\n            renderAll();\n        };\n\n        \/\/ Events\n        appWrapper.addEventListener('click', (e) => {\n            const btn = e.target.closest('button');\n            if (!btn) return;\n\n            const id = btn.id;\n            const action = btn.dataset.action;\n\n            const actions = {\n                \/\/ Setup\n                addPendingPlayerBtn: addPendingPlayer,\n                startBtn: initializeGameFromPending,\n                aboutBtn: () => openModal('aboutModal'),\n\n                \/\/ Nav\n                helpBtn: () => openModal('helpModal'),\n                statsBtn: () => openModal('statsModal', renderPlayerStats),\n                historyBtn: () => openModal('historyModal', renderHistory),\n                manageGameBtn: () => openModal('managementModal'),\n\n                \/\/ Management\n                undoBtn: () => commandProcessor.undo(),\n                addPlayerBtn: addPlayerMidSession,\n                changeSkillBtn: changeSkillForPlayer,\n                removePlayerBtn: removePlayer,\n                addCourtBtn: addCourt,\n                removeCourtBtn: removeCourt,\n                resetGameBtn: () => { if (confirm('Are you sure? This will erase all data.')) { localStorage.removeItem('badmintonSinglesState'); window.location.reload(); } },\n                saveTeamsBtn: () => {\n                    const idx = parseInt(get('#managementModal').dataset.editingCourt);\n                    const left = get('#editPlayerL').value;\n                    const right = get('#editPlayerR').value;\n                    commandProcessor.execute(SavePlayersCommand(idx, left, right));\n                },\n                confirmSwapBtn: () => {\n                    const idx = parseInt(get('#managementModal').dataset.editingCourt);\n                    const pOut = get('#playerToSwapOut').value;\n                    const pIn = get('#playerToSwapIn').value;\n                    commandProcessor.execute(SwapPlayerCommand(idx, pOut, pIn));\n                }\n            };\n            if (actions[id]) { actions[id](); return; }\n\n            if (btn.matches('.close-modal-btn')) {\n                const modal = btn.closest('.modal-overlay');\n                if (modal) closeModal(modal.id);\n                return;\n            }\n\n            if (action) {\n                const courtDiv = btn.closest('.court');\n                if (courtDiv) {\n                    const idx = parseInt(courtDiv.dataset.court);\n                    if (action === 'complete' || action === 'cancel-win') {\n                        get('.winner-selection', courtDiv).style.display = action === 'complete' ? 'block' : 'none';\n                        get('.complete-button', courtDiv).style.display = action === 'complete' ? 'none' : 'block';\n                        get('.court-action-buttons', courtDiv).style.display = action === 'complete' ? 'none' : 'grid';\n                    } else if (action === 'record-winner-left') {\n                        commandProcessor.execute(RecordWinnerCommand(idx, 'L'));\n                    } else if (action === 'record-winner-right') {\n                        commandProcessor.execute(RecordWinnerCommand(idx, 'R'));\n                    } else if (action === 'edit-players') {\n                        \/\/ Populate edit selectors with only the two on this court\n                        const court = state.courts[idx];\n                        get('#managementModal').dataset.editingCourt = idx;\n                        get('#editCourtTitle').textContent = `Edit Players for Court ${idx+1}`;\n                        populateSelect(get('#editPlayerL'), court.players, 'Select Player', court.left);\n                        populateSelect(get('#editPlayerR'), court.players, 'Select Player', court.right);\n                        get('#mainManagementPanel').classList.add('hidden');\n                        get('#editTeamsPanel').classList.remove('hidden');\n                        openModal('managementModal');\n                    } else if (action === 'swap') {\n                        openSwapModal(idx);\n                    }\n                } else if (action === 'cancel-submodal') {\n                    resetManagementModal();\n                }\n            }\n        });\n\n        \/\/ Remove from pending list\n        get('#pendingPlayersList').addEventListener('click', (e) => {\n            const btn = e.target.closest('button[data-remove-index]');\n            if (!btn) return;\n            const idx = parseInt(btn.dataset.removeIndex);\n            if (!isNaN(idx)) {\n                pendingPlayers.splice(idx, 1);\n                renderPendingList();\n            }\n        });\n\n        \/\/ Theme and misc events\n        get('#theme-checkbox').addEventListener('change', e => { const t = e.target.checked ? 'dark' : 'light'; localStorage.setItem('theme', t); applyTheme(t); });\n        get('#pairingPlayerSelect').addEventListener('change', e => renderPairingStats(e.target.value));\n        appWrapper.addEventListener('click', e => { if (e.target.classList.contains('modal-overlay')) closeModal(e.target.id); });\n\n        \/\/ Edit panel uniqueness helper\n        const editPanel = get('#editTeamsPanel');\n        editPanel.addEventListener('focusin', e => { if(e.target.tagName==='SELECT') e.target.dataset.previousValue = e.target.value; });\n        editPanel.addEventListener('change', e => {\n            if (e.target.tagName!=='SELECT') return;\n            const changed = e.target;\n            const newVal = changed.value;\n            const oldVal = changed.dataset.previousValue;\n            if (newVal === oldVal) return;\n            const other = Array.from(getAll('select', editPanel)).find(s => s !== changed && s.value === newVal);\n            if (other) other.value = oldVal;\n            changed.dataset.previousValue = newVal;\n        });\n\n        \/\/ Setup form aids\n        get('#courtCount').addEventListener('input', validateStartEligibility);\n        get('#playerNameInput').addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); addPendingPlayer(); } });\n\n        \/\/ Initial\n        loadTheme();\n        loadState();\n        renderPendingList();\n\n        \/\/ Modal helpers\n        function openModal(id, cb){ const m = get('#'+id); if (!m) return; m.classList.add('is-open'); if (cb) cb(); }\n        function closeModal(id){ const m = get('#'+id); if (!m) return; m.classList.remove('is-open'); if (id==='managementModal') resetManagementModal(); }\n        function resetManagementModal(){ get('#mainManagementPanel').classList.remove('hidden'); get('#editTeamsPanel').classList.add('hidden'); get('#swapPlayerPanel').classList.add('hidden'); delete get('#managementModal').dataset.editingCourt; }\n    });\n    <\/script>\n    <\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-1018","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/badminton.ghancaballes.com\/single-skilled\/wp-json\/wp\/v2\/pages\/1018","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/badminton.ghancaballes.com\/single-skilled\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/badminton.ghancaballes.com\/single-skilled\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/badminton.ghancaballes.com\/single-skilled\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/badminton.ghancaballes.com\/single-skilled\/wp-json\/wp\/v2\/comments?post=1018"}],"version-history":[{"count":9,"href":"https:\/\/badminton.ghancaballes.com\/single-skilled\/wp-json\/wp\/v2\/pages\/1018\/revisions"}],"predecessor-version":[{"id":1030,"href":"https:\/\/badminton.ghancaballes.com\/single-skilled\/wp-json\/wp\/v2\/pages\/1018\/revisions\/1030"}],"wp:attachment":[{"href":"https:\/\/badminton.ghancaballes.com\/single-skilled\/wp-json\/wp\/v2\/media?parent=1018"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}