tournament.html
Rama main · réplica de lectura del repositorio original.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="canonical" href="https://newtondarts.com/tournament.html">
<title>NewTon DC - Tournament Manager - Open Source Darts Tournament Software</title>
<meta name="description" content="Free, open-source darts tournament manager. Single and double elimination brackets for 4-48 players, with qualifiers above 32. Round Robin groups and cups. Offline-first, self-hostable. No signup required.">
<meta name="keywords" content="darts, tournament, bracket, double elimination, single elimination, tournament manager, open source, self-hosted, offline, darts scoring">
<link rel="icon" type="image/jpeg" href="images/logo.jpg">
<link rel="preload" href="fonts/Insignia-Regular.otf" as="font" type="font/otf" crossorigin>
<link rel="preload" href="fonts/Manrope-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin>
<link rel="stylesheet" href="css/styles.css">
<link rel="stylesheet" href="css/design-tokens.css">
<link rel="stylesheet" href="css/components.css">
<link rel="stylesheet" href="css/config-page.css">
<link rel="stylesheet" href="css/setup-page.css">
<link rel="stylesheet" href="css/registration-page.css">
<link rel="stylesheet" href="css/analytics-page.css">
<link rel="stylesheet" href="css/live-view.css">
<link rel="stylesheet" href="css/bracket-view.css">
<link rel="stylesheet" href="css/match-controls.css">
<link rel="stylesheet" href="css/dev-console.css">
<link rel="stylesheet" href="css/app-header.css">
<!-- Docker Configuration (only executes when served via PHP) -->
<script><?php
if (function_exists('getenv')) {
$demoMode = getenv('NEWTON_DEMO_MODE') === 'true' ? 'true' : 'false';
$githubUrl = getenv('NEWTON_GITHUB_URL') ?: 'https://github.com/skrodahl/NewTon';
// ?tm switches an analytics-only deployment into the full Tournament Manager,
// which is how a shared instance is maintained. NEWTON_TM_PASSWORD gates it:
// unset -> no gate, bare ?tm works (unchanged behaviour)
// set -> ?tm=<password> required; anything else falls through
// A wrong or missing password lands on the ordinary Analytics page rather than
// an error, so the gate never advertises that there is something behind it.
$tmSecret = getenv('NEWTON_TM_PASSWORD');
$tmGranted = isset($_GET['tm']) &&
(!$tmSecret || hash_equals((string)$tmSecret, (string)$_GET['tm']));
$appMode = $tmGranted ? 'full' : (getenv('NEWTON_MODE') ?: 'full');
$readonlyAnalytics = getenv('NEWTON_READONLY_ANALYTICS') === 'true' ? 'true' : 'false';
// NEWTON_API_KEY (api/api-check.php) guards uploads, deletes and corrections. A page
// opened with the correct ?tm password gets the key, so maintaining the instance
// works as before. Only behind a password: an open ?tm never hands the key out.
$apiKey = getenv('NEWTON_API_KEY');
$pageApiKey = ($tmGranted && $tmSecret && $apiKey) ? trim($apiKey) : '';
echo "\n window.NEWTON_CONFIG = {\n";
echo " demoMode: $demoMode,\n";
echo " appMode: '$appMode',\n";
// what the instance is, even when ?tm opens it in full mode (the player database
// is the club's uploaded copy on an analytics-only instance)
$instanceMode = getenv('NEWTON_MODE') === 'analytics' ? 'analytics' : 'full';
echo " instanceMode: '$instanceMode',\n";
echo " readonlyAnalytics: $readonlyAnalytics,\n";
echo " apiKey: " . json_encode($pageApiKey, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) . ",\n";
echo " githubUrl: '$githubUrl'\n";
echo " };\n ";
}
?></script>
<script>
// Initialize help system when DOM is ready
document.addEventListener('DOMContentLoaded', function () {
// Load help system after other initialization
setTimeout(() => {
if (typeof initializeHelpSystem === 'function') {
initializeHelpSystem();
}
}, 500);
// Analytics-only mode — navigate to Analytics on load
if (window.NEWTON_APP_MODE === 'analytics') {
setTimeout(() => {
if (typeof showPage === 'function') showPage('history');
}, 0);
}
// Show demo banner if demo mode is enabled (Docker only)
if (typeof window.NEWTON_CONFIG !== 'undefined' && window.NEWTON_CONFIG.demoMode) {
const banner = document.createElement('div');
banner.style.cssText = 'background: #2196F3; color: white; padding: 12px; text-align: center; font-size: 14px;';
banner.innerHTML = 'Darts tournament software. Free, open-source, secure, offline first, self-hostable.</br></br>📍 Demo Site: Everything you do is stored locally in your browser. Your data never leaves your device. - ' +
'<a href="https://newtondarts.com" style="color: white; text-decoration: underline;">newtondarts.com</a>' +
' - <a href="' + window.NEWTON_CONFIG.githubUrl + '" style="color: white; text-decoration: underline;">View on GitHub</a>';
document.body.insertBefore(banner, document.body.firstChild);
}
});
</script>
</head>
<body class="<?php echo $appMode === 'analytics' ? 'mode-analytics' : 'mode-full'; ?>">
<script>
window.NEWTON_APP_MODE = document.body.classList.contains('mode-analytics') ? 'analytics' : 'full';
</script>
<div class="container">
<div class="header">
<div class="header-top">
<h1>
<div id="clubLogo" class="logo-placeholder">
CLUB<br>LOGO
</div>
NewTon DC - Tournament Manager
</h1>
<div class="header-right">
<div id="headerTournamentStatus" class="header-tournament-status header-tournament-wide">
<strong>None</strong>
<span class="tournament-date"></span>
</div>
<div id="headerClock" class="header-clock"></div>
</div>
</div>
<div id="headerTournamentStatusNarrow" class="header-tournament-status header-tournament-narrow">
<span class="status-prefix">Active Tournament: </span><strong>None</strong>
</div>
<div class="nav">
<button class="nav-btn active" data-page="setup">Tournament Setup</button>
<button class="nav-btn" data-page="registration">Player Registration</button>
<button class="nav-btn" data-page="tournament">Tournament Bracket</button>
<button class="nav-btn" data-page="history">Analytics</button>
<button class="nav-btn" data-page="config"><span class="nt-in-full">Global Settings</span><span class="nt-in-analytics">Custom Settings</span></button>
<a class="nav-btn" href="chalker/index.html" target="_blank">Chalker</a>
</div>
</div>
<!-- Setup Page (css/setup-page.css): a new tournament, the current one, the saved tournaments and match history -->
<div id="setup" class="page active setup-page">
<div class="st-wrap">
<div class="st-title">
<div><h2>Tournament Setup</h2><p>Start a tournament, or pick one up where you left off.</p></div>
<button type="button" class="st-btn help-info-icon" data-page="setup">Help</button>
</div>
<div class="st-top">
<!-- Filled by renderSetupCurrent(), which also decides which comes first and whether the form is open -->
<section class="st-panel" id="setupCurrent" aria-label="Current tournament"></section>
<section class="st-panel st-create st-collapsed" id="setupNew" aria-label="New tournament">
<div class="st-create-bar">
<button type="button" class="st-new-toggle" id="setupNewToggle" aria-expanded="false" aria-controls="setupNewForm" onclick="toggleSetupNew()"><span class="st-plus" aria-hidden="true"></span>New tournament</button>
<h3 class="st-new-heading" id="setupNewHeading" hidden>Start a new tournament</h3>
<span class="st-or">or <button type="button" class="st-link" onclick="document.getElementById('importFile').click()">import a tournament file</button></span>
</div>
<div class="st-create-form" id="setupNewForm" hidden>
<div class="st-create-row">
<div class="st-field"><label for="tournamentName">Name</label><input type="text" class="st-text" id="tournamentName" placeholder="e.g. Thursday Cup #42"></div>
<div class="st-field"><label for="tournamentDate">Date</label><input type="date" class="st-text st-mono" id="tournamentDate"></div>
</div>
<div class="st-create-go"><button type="button" class="st-btn" id="setupCreateBtn" onclick="createTournament()">Create tournament</button></div>
</div>
<div id="importStatus" class="st-import-status" style="display: none;"></div>
<input type="file" id="importFile" accept=".json" style="display: none;" onchange="importTournament(event)">
<input type="file" id="uploadFileInput" accept=".json" style="display: none;" onchange="handleUploadFileSelected(event)">
</section>
</div>
<div class="st-bottom">
<section class="st-panel" aria-labelledby="setupListTitle">
<div class="st-panel-head">
<div class="st-head-l">
<h3 id="setupListTitle">Tournaments</h3>
<!-- This computer | Server tabs, filled by loadRecentTournaments() when a server answers -->
<div class="st-seg" id="setupListSource" hidden></div>
</div>
<button type="button" id="storage-indicator-link" class="st-storage" onclick="showStorageModal()" title="Storage used on this computer"><span id="storageIndicatorText">Storage --</span><span class="st-meter"><span id="storageIndicatorBar"></span></span></button>
</div>
<div class="st-scroll" id="recentTournaments"></div>
</section>
<section class="st-panel" aria-labelledby="matchHistoryHeading">
<div class="st-panel-head">
<h3 id="matchHistoryHeading">Match history</h3>
<span class="st-eyebrow" id="matchHistoryCount"></span>
</div>
<div class="st-hlist" id="matchResults"></div>
</section>
</div>
</div>
</div>
<!-- Registration Page (css/registration-page.css): Tonight (next step, players, payment, the
player database to pick from, Leaderboard) and the Player database (js/player-registry.js) -->
<div id="registration" class="page registration-page">
<div class="rg-wrap">
<div class="st-title">
<div><h2>Player Registration</h2><p id="registrationSubtitle">Add the players, and mark who has paid.</p></div>
<div class="rg-title-tools">
<div class="st-seg" id="registrationTabs" role="group" aria-label="Registration">
<button type="button" data-tab="tonight" aria-pressed="true" onclick="switchRegistrationTab('tonight')">Tonight</button><button type="button" data-tab="database" aria-pressed="false" onclick="switchRegistrationTab('database')">Player database</button>
</div>
<button type="button" class="st-btn help-info-icon" data-page="registration">Help</button>
</div>
</div>
<div class="rg-tab" id="registrationTonight">
<!-- Counts are kept by updatePlayerCount(); the rest by renderRegistrationNext() -->
<section class="rg-next" aria-label="Next step">
<div class="rg-band" id="registrationStatusBand" hidden></div>
<dl class="rg-counts">
<div><dt>Players</dt><dd id="playerCount">0</dd></div>
<div><dt>Paid</dt><dd id="paidCount">0</dd></div>
<div><dt>Unpaid</dt><dd id="unpaidCount">0</dd></div>
</dl>
<div class="st-next-what"><p class="st-eyebrow">Next step</p><b id="registrationNextTitle"></b><span class="st-hint" id="registrationNextHint"></span></div>
<button type="button" class="st-btn st-primary rg-next-btn" id="registrationNextBtn" onclick="showPage('tournament')">Open bracket →</button>
</section>
<div class="rg-cols" id="registrationCols">
<section class="st-panel" aria-labelledby="registrationPlayersTitle">
<div class="st-panel-head"><h3 id="registrationPlayersTitle">Players<small id="registrationPlayersHint">click a player to toggle paid</small></h3></div>
<div id="playersContainer" class="rg-plist"></div>
<div class="rg-pfoot" id="lateRegBtnContainer"></div>
</section>
<!-- Before the draw: payment and saved players -->
<div class="rg-side" id="playerListSection">
<section class="st-panel rg-pay" id="paymentPanel">
<img src="images/payment.png" alt="Payment QR code" onerror="document.getElementById('paymentPanel').hidden = true">
<div><b>Pay here</b><span>Scan to pay the entry fee.</span></div>
</section>
<!-- The player database to pick from: filled by renderPlayerList() -->
<section class="st-panel" aria-labelledby="savedPlayersTitle">
<div class="st-panel-head">
<h3 id="savedPlayersTitle">Add from the database<small id="playerListCount"></small></h3>
<button type="button" class="st-btn st-sm st-primary" onclick="openPlayerForm(null, _playerSearch)">+ New player</button>
</div>
<div class="rg-search"><input type="search" class="st-text" id="playerName" placeholder="Search short name, first or last name" autocomplete="off" aria-label="Search the player database"></div>
<div id="playerListContainer" class="rg-pick"></div>
<div class="rg-pick-foot"><span>Archived players are hidden. Enter adds the first match.</span><button type="button" class="st-link" onclick="switchRegistrationTab('database')">Manage database</button></div>
</section>
</div>
<!-- After the draw: the Leaderboard -->
<section class="st-panel" id="tournamentResultsSection" style="display: none;" aria-labelledby="leaderboardTitle">
<div class="st-panel-head">
<h3 id="leaderboardTitle">Leaderboard<small>click a player to edit statistics</small></h3>
<div class="rg-tools">
<button type="button" class="st-link" onclick="exportResultsCSV()">Export CSV</button>
<button type="button" class="st-link" onclick="exportResultsJSON()">Export JSON</button>
</div>
</div>
<div class="rg-scroll">
<table class="nt-leaderboard" id="resultsSection">
<thead>
<tr>
<th>Rank</th>
<th>Player</th>
<th>Points</th>
<th>Short legs</th>
<th>High outs</th>
<th>180s</th>
<th>Tons</th>
<th>Legs won</th>
<th>Legs lost</th>
</tr>
</thead>
<tbody id="resultsTableBody">
</tbody>
</table>
<div id="resultsEmptyMessage" class="st-empty st-small" style="display: none;"><span>No paid players yet.</span></div>
</div>
</section>
</div>
</div>
<!-- Player database: filled by renderPlayerDatabase() -->
<div class="rg-tab" id="registrationDatabase" hidden>
<div class="rg-notice" id="playerDbNotice" hidden>
<span><b><span id="playerDbNoticeCount"></span> created from your saved players and Analytics.</b> First and last names are empty: fill them in when you can, and merge any duplicates.</span>
<button type="button" class="st-link" onclick="dismissPlayerDbNotice()">Dismiss</button>
</div>
<section class="st-panel" aria-labelledby="playerDbTitle">
<div class="st-panel-head">
<h3 id="playerDbTitle">Player database<small id="playerDbCount"></small></h3>
<div class="rg-tools">
<input type="search" class="st-text rg-db-search" id="playerDbSearch" placeholder="Search" aria-label="Search the player database" oninput="onPlayerDbSearch(this.value)">
<label class="rg-check"><input type="checkbox" onchange="onPlayerDbShowArchived(this.checked)"> Show archived</label>
<button type="button" class="st-link" onclick="importPlayerListFromFile()" title="Add the players from a tournament export file">Import from file</button>
<button type="button" class="st-btn st-sm" onclick="openMergeDialog()">Merge…</button>
<button type="button" class="st-btn st-sm st-primary" onclick="openPlayerForm(null)">+ New player</button>
</div>
</div>
<div class="rg-db-scroll">
<table class="rg-db">
<thead><tr><th>Short name</th><th>First name</th><th>Last name</th><th>Previous names</th><th class="rg-num">Played</th><th class="rg-num">Last played</th><th>Status</th><th></th></tr></thead>
<tbody id="playerDbBody"></tbody>
</table>
</div>
<div class="rg-pfoot rg-db-foot"><span>Has played: kept for Analytics, so it can be archived but not deleted.</span><span>Renaming keeps the old name under Previous names, so older results still find the player.</span></div>
</section>
</div>
</div>
</div>
<!-- Player database dialogs (new / edit player, merge, delete): filled by player-management.js -->
<div id="playerDbModal" class="modal">
<div class="dlg rg-dlg" id="playerDbDialog"></div>
</div>
<!-- Tournament Page -->
<div id="tournament" class="page">
<div class="bracket-container">
<header class="bv-header">
<div class="bv-head-left">
<div class="bv-title-block">
<div class="bv-title" id="bvTitle">No tournament</div>
<div class="bv-subtitle" id="bvSubtitle"></div>
</div>
<nav class="bv-nav">
<button type="button" class="bv-btn bv-nav-btn analytics-back-btn" onclick="exitAnalyticsBracketPreview();" title="Return to Analytics">⬅ Analytics</button>
<span class="bv-links">
<button type="button" class="bv-link bv-nav-btn" onclick="showPage('setup')" title="Return to Setup page">Setup</button>
<button type="button" class="bv-link bv-nav-btn" onclick="showPage('registration')" title="Exit Tournament to edit statistics and see current results">Registration</button>
<button type="button" class="bv-link bv-nav-btn" onclick="showPage('config')" title="Open Config page">Config</button>
<button type="button" class="bv-link bv-nav-btn" onclick="showPage('history')" title="Open Analytics page">Analytics</button>
</span>
</nav>
</div>
<div class="bv-head-center">
<!-- The page's views: the bracket, or Match Controls in the same frame (showBracketView) -->
<div class="bv-seg bv-views bv-nav-btn" id="bvViews" role="group" aria-label="View">
<button type="button" data-view="bracket" aria-pressed="true" onclick="showBracketView('bracket')">Bracket</button><button type="button" data-view="controls" aria-pressed="false" onclick="showBracketView('controls')" title="Run the matches: lanes, the ready queue, referees">Match Controls<span class="bv-badge" id="bvResultsWaiting" hidden></span></button><button type="button" data-view="console" aria-pressed="false" onclick="showBracketView('console')" title="Developer Console: diagnostics and developer tools" id="bvConsoleTab" hidden>Console</button>
</div>
<button type="button" class="bv-btn bv-nav-btn bv-leaderboard" onclick="showStatisticsModal()" title="View tournament statistics and rankings">Leaderboard</button>
</div>
<div class="bv-head-right">
<div class="bv-tools">
<div class="bv-seg" id="bvGroupsView" hidden>
<button type="button" data-gc="groups" aria-pressed="true" onclick="BracketView.setGroupsView('groups')">Groups</button>
<button type="button" data-gc="cups" aria-pressed="false" onclick="BracketView.setGroupsView('cups')">Cups</button>
</div>
<div class="bv-seg" id="bvFinals" hidden>
<span class="bv-seglabel">Finals</span>
<button type="button" data-finals="right" aria-pressed="true" onclick="BracketView.setFinals('right')">Right</button>
<button type="button" data-finals="middle" aria-pressed="false" onclick="BracketView.setFinals('middle')">Middle</button>
</div>
<button type="button" class="bv-btn" onclick="resetZoom()" title="Fit the whole bracket">Fit all</button>
<div class="bv-seg bv-zoom">
<button type="button" onclick="zoomOut()" title="Zoom out" aria-label="Zoom out">−</button>
<output id="bvZoom">100%</output>
<button type="button" onclick="zoomIn()" title="Zoom in" aria-label="Zoom in">+</button>
</div>
</div>
<time class="bv-clock" id="bvClock" aria-label="Current time">--:--</time>
</div>
<div class="bv-header-row bv-status-row">
<div class="bv-status" id="bvStatus"></div>
<button type="button" class="bv-upload-chip" id="bvUploadChip" hidden onclick="retryRemoteUpload(true)">Not uploaded</button>
<span class="bv-live-badge" id="bvLiveBadge" hidden></span>
<div class="bv-legend">
<span><i class="bv-sw bv-sw-live"></i>Live</span>
<span><i class="bv-sw bv-sw-ready"></i>Ready</span>
<span><i class="bv-sw bv-sw-done"></i>Completed</span>
<span><i class="bv-sw bv-sw-pending"></i>Waiting</span>
<span><i class="bv-sw bv-sw-walkover"></i>Walkover</span>
<span><i class="bv-throw"></i>Throws first</span>
</div>
</div>
</header>
<div class="bv-stage">
<div class="bracket-viewport" id="bracketViewport">
<div class="bracket-canvas" id="bracketCanvas">
<div id="bracketMatches">
<!-- Bracket matches will be rendered here -->
</div>
<div id="bracketLines">
<!-- Connection lines will be rendered here -->
</div>
</div>
</div>
<!-- Match Controls (css/match-controls.css): a layer over the bracket, so the bracket keeps
its camera underneath. Filled by showCommandCenterModal() in js/bracket-rendering.js.
Keeps the old dialog's id: code that redraws it after an action looks for it. -->
<section id="matchCommandCenterModal" class="mc-view" aria-label="Match Controls" style="display:none">
<div class="mc-body" id="mcBody"></div>
</section>
<!-- While Match Controls shows, the bracket stays visible to its left, dimmed: click it, or the
pill on the drawer's corner, to see the bracket (css/bracket-view.css, .bv-on-controls) -->
<div class="bv-scrim" aria-hidden="true" onclick="showBracketView('bracket')" title="Show the bracket"></div>
<button type="button" class="bv-layer-close" onclick="showBracketView('bracket')" aria-label="Show the bracket"><span class="bv-hint-rest">← Bracket</span><span class="bv-hint-hover">Click or press ←</span></button>
<!-- Developer Console (css/dev-console.css): the third view, when it is enabled in Global
Settings. Filled by js/analytics.js (startDeveloperConsole / stopDeveloperConsole). -->
<section id="devConsoleView" class="dc-view" aria-label="Developer Console" style="display:none">
<div class="dc-frame">
<aside class="dc-nav" aria-label="Developer Console">
<div class="dc-group">
<h3>Status</h3>
<button type="button" data-dc="overview" onclick="showQuickOverview()"><span>Overview</span><small id="stat-overview"></small></button>
<button type="button" data-dc="transactions" onclick="showTransactionBreakdown()"><span>Transactions</span><small id="stat-transactions"></small></button>
<button type="button" data-dc="matches" onclick="showMatchStateDetails()"><span>Matches</span><small id="stat-matches"></small></button>
<button type="button" data-dc="players" onclick="showPlayerDetails()"><span>Players</span><small id="stat-players"></small></button>
<button type="button" data-dc="lanes" onclick="showLaneUsageDetails()"><span>Lanes</span><small id="stat-lanes"></small></button>
<button type="button" data-dc="storage" onclick="showLocalStorageUsage()"><span>Storage</span><small id="stat-storage"></small></button>
</div>
<div class="dc-group">
<h3>Inspect</h3>
<button type="button" data-dc="history" onclick="showTransactionHistory()">Transaction history</button>
<button type="button" data-dc="transaction-management" onclick="showTransactionLogManagement()">Transaction log</button>
<button type="button" data-dc="validation" onclick="commandValidateEverything()">Validate everything</button>
<button type="button" onclick="openQRInspector()">QR payload inspector</button>
</div>
<div class="dc-group">
<h3>Repair</h3>
<button type="button" onclick="commandReRenderBracket()">Re-render bracket</button>
<button type="button" onclick="commandRecalculateRankings()">Recalculate rankings</button>
<button type="button" onclick="commandRefreshDropdowns()">Refresh all dropdowns</button>
</div>
<div class="dc-group">
<h3>Change</h3>
<button type="button" onclick="commandToggleReadOnly()">Toggle read-only</button>
<button type="button" class="dc-warn" onclick="commandLateRegistration()">Late registration</button>
<button type="button" class="dc-danger" data-dc="reset-config" onclick="commandResetAllConfig()">Reset all config</button>
</div>
<p class="dc-stamp" id="analytics-timestamp"></p>
</aside>
<div class="dc-main">
<section class="dc-panel dc-content" id="analyticsContentArea"></section>
<section class="dc-panel dc-log" id="analyticsConsoleFooter">
<div class="dc-loghead">
<button type="button" class="dc-logtoggle" onclick="toggleConsoleOutput()"><span id="consoleToggleIcon">▶</span> Console output <small id="dcLogCount"></small></button>
<span class="dc-logacts"><button type="button" class="dc-btn dc-sm" onclick="copyConsoleOutput()">Copy</button><button type="button" class="dc-btn dc-sm" onclick="clearConsoleOutput()">Clear</button></span>
</div>
<div id="consoleOutputContent" class="dc-logbody" style="display: none"></div>
</section>
</div>
</div>
</section>
</div>
</div>
</div>
<!-- Configuration Page -->
<div id="config" class="page config-page">
<div class="cfg-wrap">
<div class="cfg-title">
<div>
<h2><span class="nt-in-full">Global Settings</span><span class="nt-in-analytics">Custom Settings</span></h2>
<p><span class="nt-in-full">Settings for this computer. They apply to every tournament.</span><span class="nt-in-analytics">Try other point values on the results: choose Custom in Analytics to see the standings with them. They're kept in this browser only; the club's results don't change.</span></p>
</div>
<button type="button" class="cfg-btn help-info-icon" data-page="config">Help</button>
</div>
<nav class="cfg-toc" aria-label="Settings sections">
<button type="button" data-target="cfg-club">Club<span class="cfg-dot"></span></button>
<button type="button" data-target="cfg-tournaments">Tournaments<span class="cfg-dot"></span></button>
<button type="button" data-target="cfg-matchday">Match day<span class="cfg-dot"></span></button>
<button type="button" data-target="cfg-bracket">Bracket<span class="cfg-dot"></span></button>
<button type="button" data-target="cfg-chalker">Chalker<span class="cfg-dot"></span></button>
<button type="button" data-target="cfg-server">Server & backup<span class="cfg-dot"></span></button>
<button type="button" data-target="cfg-developer">Developer<span class="cfg-dot"></span></button>
</nav>
<div class="cfg-main">
<!-- CLUB -->
<section class="cfg-section" id="cfg-club">
<div class="cfg-sec-head"><h3>Club</h3></div>
<div class="cfg-panel" id="configClub">
<div class="cfg-row">
<div><label class="cfg-label" for="applicationTitle">Club name</label><div class="cfg-desc">Shown in the page title and at the top of the bracket.</div></div>
<div class="cfg-ctl"><input class="cfg-text" type="text" id="applicationTitle" value="NewTon DC" placeholder="Your club name"></div>
</div>
<div class="cfg-row">
<div><div class="cfg-label">Logo</div><div class="cfg-desc">Put a square image named <code>logo.png</code>, <code>logo.jpg</code>, <code>logo.jpeg</code> or <code>logo.svg</code> in the <code>images</code> folder. The browser tab icon uses <code>logo.jpg</code>.</div></div>
<div class="cfg-ctl"><img class="cfg-logo" id="cfgLogoPreview" alt="Current logo" hidden></div>
</div>
</div>
</section>
<!-- TOURNAMENTS -->
<section class="cfg-section" id="cfg-tournaments">
<div class="cfg-sec-head"><h3>Tournaments</h3></div>
<div class="cfg-panel">
<div class="cfg-panel-title">Formats to offer</div>
<p class="cfg-panel-note">Which formats appear when starting a tournament. Hide the ones your club never plays. Existing tournaments are unaffected.</p>
<div class="cfg-choices" id="formatVisibilityOptions"></div>
</div>
<!-- Round Robin's own settings: shown while the format is offered (config-page.js) -->
<div class="cfg-panel" id="cfgRoundRobin">
<div class="cfg-panel-title">Round Robin</div>
<p class="cfg-panel-note">Everybody plays everybody in their group, in a fixed order with a referee from the group. A tournament keeps the settings it was drawn with.</p>
<div class="cfg-row">
<div><div class="cfg-label">Structure</div><div class="cfg-desc"><b>Groups and cups</b>: groups no larger than <b>Largest group</b> (with Top two, an even number of groups), then an A cup and a B cup, single elimination; 6 to 32 players. <b>One group</b>: everybody plays everybody, and the table decides the placings; 3 to 8 players.</div></div>
<div class="cfg-ctl"><div class="cfg-seg" data-target="rrStructure"><button type="button" data-value="groups">Groups and cups</button><button type="button" data-value="single">One group</button></div><input type="hidden" id="rrStructure"></div>
</div>
<div class="cfg-row cfg-rr-single">
<div><div class="cfg-label">Play each other</div><div class="cfg-desc">One group: <b>Once</b>, everybody plays everybody once; <b>Twice</b>, a double round robin, everybody plays everybody twice (the return round in the same order, player 1 and player 2 swapped). Twice doubles the matches: 4 players play 12, 8 play 56.</div></div>
<div class="cfg-ctl"><div class="cfg-seg" data-target="rrMeetings"><button type="button" data-value="1">Once</button><button type="button" data-value="2">Twice</button></div><input type="hidden" id="rrMeetings"></div>
</div>
<div class="cfg-row cfg-rr-cups">
<div><div class="cfg-label">Largest group</div><div class="cfg-desc">The most players in one group. The draw makes the fewest groups it can (with Top two, an even number), so a larger limit means fewer, longer groups: with Top half, 12 players are 3 groups of 4 at 4, and 2 groups of 6 at 6. A group of 4 plays 6 matches, of 5 plays 10, of 6 plays 15.</div></div>
<div class="cfg-ctl"><div class="cfg-seg" data-target="rrMaxGroup"><button type="button" data-value="4">4</button><button type="button" data-value="5">5</button><button type="button" data-value="6">6</button></div><input type="hidden" id="rrMaxGroup"></div>
</div>
<div class="cfg-row cfg-rr-cups">
<div><div class="cfg-label">To the A cup</div><div class="cfg-desc"><b>Top two</b>: the group winners and runners-up play the A cup, the rest the B cup. <b>Top half</b>: everyone is ranked across the groups (group place first, then results per match) and the two cups are the same size (A one larger when the number is odd), so a small field doesn't leave a tiny B cup. Top half only applies with a B cup: without one, the top two of each group play the A cup.</div></div>
<div class="cfg-ctl"><div class="cfg-seg" data-target="rrCupEntry"><button type="button" data-value="top2">Top two</button><button type="button" data-value="half">Top half</button></div><input type="hidden" id="rrCupEntry"></div>
</div>
<div class="cfg-row cfg-rr-cups">
<div><div class="cfg-label">Group rematches in cup round 1</div><div class="cfg-desc"><b>Allow</b>: the cups are drawn top seed against bottom seed, even when they played in the same group. <b>Avoid</b>: a seed gets the nearest opponent from another group instead, where possible.</div></div>
<div class="cfg-ctl"><div class="cfg-seg" data-target="rrRematches"><button type="button" data-value="allow">Allow</button><button type="button" data-value="avoid">Avoid</button></div><input type="hidden" id="rrRematches"></div>
</div>
<div class="cfg-row cfg-rr-cups">
<div><label class="cfg-label" for="rrBCup">Play the B cup</label><div class="cfg-desc">On to start with at <b>Draw the cups</b>; you can still change it there.</div></div>
<div class="cfg-ctl"><span class="cfg-switch"><input type="checkbox" id="rrBCup" checked><span></span></span></div>
</div>
</div>
<!-- Swiss's own settings: shown while the format is offered (config-page.js) -->
<div class="cfg-panel" id="cfgSwiss">
<div class="cfg-panel-title">Swiss</div>
<p class="cfg-panel-note">A fixed number of rounds; each pairs players with the same record who haven't met. Both settings are how the draw starts; you can change them for each draw.</p>
<div class="cfg-row">
<div><div class="cfg-label">Rounds</div><div class="cfg-desc"><b>By players</b>: 3 for 5 to 8 players, 4 for 9 to 16, 5 for 17 to 32, 6 for 33 to 48. Or always the same number. Never more than one fewer than the players.</div></div>
<div class="cfg-ctl"><div class="cfg-seg" data-target="swissRoundsSetting"><button type="button" data-value="auto">By players</button><button type="button" data-value="3">3</button><button type="button" data-value="4">4</button><button type="button" data-value="5">5</button><button type="button" data-value="6">6</button></div><input type="hidden" id="swissRoundsSetting"></div>
</div>
<div class="cfg-row">
<div><div class="cfg-label">Finish</div><div class="cfg-desc"><b>Semifinals and final</b>: after the last round, the top four play semifinals (1st v 4th, 2nd v 3rd), a bronze final and a final. <b>The table decides</b>: the table after the last round gives the placings.</div></div>
<div class="cfg-ctl"><div class="cfg-seg" data-target="swissFinish"><button type="button" data-value="top4">Semifinals and final</button><button type="button" data-value="table">The table decides</button></div><input type="hidden" id="swissFinish"></div>
</div>
</div>
<div class="cfg-panel">
<div class="cfg-panel-title">Seeding</div>
<p class="cfg-panel-note">Keeps the best players apart in the draw, using the ranking of earlier tournaments with the same name. Everyone else is still drawn at random, and new players are never seeded. It reads the tournaments in this browser.</p>
<div class="cfg-row">
<div><div class="cfg-label">Use seeding</div><div class="cfg-desc"><b>Off</b>: the draw is always random. <b>Available</b>: the draw offers it in Match Controls, and you tick it. <b>On</b>: it is ticked to start with.</div></div>
<div class="cfg-ctl"><div class="cfg-seg" data-target="seedingMode"><button type="button" data-value="off">Off</button><button type="button" data-value="available">Available</button><button type="button" data-value="on">On</button></div><input type="hidden" id="seedingMode"></div>
</div>
<div class="cfg-row">
<div><div class="cfg-label">Seeded players</div><div class="cfg-desc">How many of the best players are seeded, as a share of the bracket: 1/8, 1/4 or 1/2. <b>All</b> seeds everyone with a ranking, so the top seed meets the bottom seed. You can change it for each draw.</div></div>
<div class="cfg-ctl"><div class="cfg-seg" data-target="seedingSeeds"><button type="button" data-value="eighth">1/8</button><button type="button" data-value="quarter">1/4</button><button type="button" data-value="half">1/2</button><button type="button" data-value="all">All</button></div><input type="hidden" id="seedingSeeds"></div>
</div>
<div class="cfg-seedtable">
<table>
<caption>Players seeded</caption>
<thead><tr><th></th><th>1/8</th><th>1/4</th><th>1/2</th><th>All</th></tr></thead>
<tbody>
<tr><th>8-player bracket</th><td>none</td><td>2 players</td><td>4 players</td><td>all ranked</td></tr>
<tr><th>16-player bracket</th><td>2 players</td><td>4 players</td><td>8 players</td><td>all ranked</td></tr>
<tr><th>32-player bracket</th><td>4 players</td><td>8 players</td><td>16 players</td><td>all ranked</td></tr>
</tbody>
</table>
<p>The bracket is the next size up from the number of players: 13 players play in a 16-player bracket.</p>
</div>
<p class="cfg-panel-note">The best seeds get the byes. Players with no ranking are never seeded, and only get a bye if there are more byes than seeds.</p>
</div>
<div class="cfg-panel">
<div class="cfg-panel-title">Match length <button type="button" class="cfg-link" data-reset="legs">Reset to defaults</button></div>
<div class="cfg-cols">
<div>
<h4>Double elimination</h4>
<div class="cfg-line"><span>Regular rounds</span><span class="cfg-step" data-target="regularRoundsLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="regularRoundsLegs"></div>
<div class="cfg-line"><span>Frontside semifinal</span><span class="cfg-step" data-target="frontsideSemifinalLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="frontsideSemifinalLegs"></div>
<div class="cfg-line"><span>Backside semifinal</span><span class="cfg-step" data-target="backsideSemifinalLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="backsideSemifinalLegs"></div>
<div class="cfg-line"><span>Backside final</span><span class="cfg-step" data-target="backsideFinalLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="backsideFinalLegs"></div>
<div class="cfg-line"><span>Grand final</span><span class="cfg-step" data-target="grandFinalLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="grandFinalLegs"></div>
</div>
<div>
<h4>Single elimination</h4>
<div class="cfg-line"><span>Regular rounds</span><span class="cfg-step" data-target="seRegularRoundsLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="seRegularRoundsLegs"></div>
<div class="cfg-line"><span>Quarterfinal</span><span class="cfg-step" data-target="seQuarterfinalLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="seQuarterfinalLegs"></div>
<div class="cfg-line"><span>Semifinal</span><span class="cfg-step" data-target="seSemifinalLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="seSemifinalLegs"></div>
<div class="cfg-line"><span>Bronze final</span><span class="cfg-step" data-target="seBronzeLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="seBronzeLegs"></div>
<div class="cfg-line"><span>Final</span><span class="cfg-step" data-target="seFinalLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="seFinalLegs"></div>
</div>
<div>
<h4>Groups and cups</h4>
<div class="cfg-line"><span>Group matches</span><span class="cfg-step" data-target="groupMatchesLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="groupMatchesLegs"></div>
<div class="cfg-line"><span>Cup rounds</span><span class="cfg-step" data-target="cupRoundsLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="cupRoundsLegs"></div>
<div class="cfg-line"><span>Cup semifinal</span><span class="cfg-step" data-target="cupSemifinalLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="cupSemifinalLegs"></div>
<div class="cfg-line"><span>Cup bronze final</span><span class="cfg-step" data-target="cupBronzeLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="cupBronzeLegs"></div>
<div class="cfg-line"><span>Cup final</span><span class="cfg-step" data-target="cupFinalLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="cupFinalLegs"></div>
<p class="cfg-panel-note">Cup rounds are every round before the semifinals. The A and B cups play the same lengths.</p>
</div>
<div>
<h4>Swiss</h4>
<div class="cfg-line"><span>Rounds</span><span class="cfg-step" data-target="swissRoundsLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="swissRoundsLegs"></div>
<div class="cfg-line"><span>Semifinal</span><span class="cfg-step" data-target="swissSemifinalLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="swissSemifinalLegs"></div>
<div class="cfg-line"><span>Bronze final</span><span class="cfg-step" data-target="swissBronzeLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="swissBronzeLegs"></div>
<div class="cfg-line"><span>Final</span><span class="cfg-step" data-target="swissFinalLegs" data-min="1" data-max="21" data-step="2" data-format="bo"><button type="button" aria-label="Shorter">−</button><output></output><button type="button" aria-label="Longer">+</button></span><input type="hidden" id="swissFinalLegs"></div>
<p class="cfg-panel-note">Semifinal, bronze final and final: the top four, after the last round.</p>
</div>
</div>
</div>
<div class="cfg-panel" id="configPoints">
<div class="cfg-panel-title">Points <button type="button" class="cfg-link" data-reset="points">Reset to defaults</button></div>
<div class="cfg-cols">
<div>
<h4>Placing</h4>
<div class="cfg-line"><span>Taking part</span><input class="cfg-num" type="number" min="0" id="participationPoints" aria-label="Taking part points"></div>
<div class="cfg-line cfg-subline"><label for="nonQualifiedParticipation">Also for qualifier losers</label><span class="cfg-switch"><input type="checkbox" id="nonQualifiedParticipation" checked><span></span></span></div>
<div class="cfg-line"><span class="cfg-rank">1st</span><input class="cfg-num" type="number" min="0" id="firstPlacePoints" aria-label="1st points"></div>
<div class="cfg-line"><span class="cfg-rank">2nd</span><input class="cfg-num" type="number" min="0" id="secondPlacePoints" aria-label="2nd points"></div>
<div class="cfg-line"><span class="cfg-rank">3rd</span><input class="cfg-num" type="number" min="0" id="thirdPlacePoints" aria-label="3rd points"></div>
<div class="cfg-line"><span class="cfg-rank">4th</span><input class="cfg-num" type="number" min="0" id="fourthPlacePoints" aria-label="4th points"></div>
<div class="cfg-line"><span class="cfg-rank">5th–6th</span><input class="cfg-num" type="number" min="0" id="fifthSixthPlacePoints" aria-label="5th–6th points"></div>
<div class="cfg-line"><span class="cfg-rank">7th–8th</span><input class="cfg-num" type="number" min="0" id="seventhEighthPlacePoints" aria-label="7th–8th points"></div>
</div>
<div>
<h4>Achievements, each</h4>
<div class="cfg-line"><span>180</span><input class="cfg-num" type="number" min="0" id="oneEightyPoints" aria-label="180 points"></div>
<div class="cfg-line"><span>High out <small>101+</small></span><input class="cfg-num" type="number" min="0" id="highOutPoints" aria-label="High out 101+ points"></div>
<div class="cfg-line"><span>Short leg</span><input class="cfg-num" type="number" min="0" id="shortLegPoints" aria-label="Short leg points"></div>
<div class="cfg-line"><span>Ton <small>100+</small></span><input class="cfg-num" type="number" min="0" id="tonPoints" aria-label="Ton 100+ points"></div>
</div>
</div>
</div>
</section>
<!-- MATCH DAY -->
<section class="cfg-section" id="cfg-matchday">
<div class="cfg-sec-head"><h3>Match day</h3></div>
<div class="cfg-panel">
<div class="cfg-row">
<div><div class="cfg-label">Lanes</div><div class="cfg-desc">How many dartboards you have.</div></div>
<div class="cfg-ctl"><span class="cfg-step" data-target="maxLanes" data-min="1" data-max="20" data-step="1"><button type="button" aria-label="Fewer lanes">−</button><output></output><button type="button" aria-label="More lanes">+</button></span><input type="hidden" id="maxLanes"></div>
</div>
<div class="cfg-row stack">
<div><div class="cfg-label">Lanes not in use</div><div class="cfg-desc">Click a lane to leave it out of lane assignment, for example a broken board.</div></div>
<div class="cfg-ctl"><div class="cfg-lanes" id="cfgLaneChips"></div><input type="hidden" id="excludedLanes"><button type="button" class="cfg-btn" style="margin-left:auto" onclick="showLaneUsage()">Show current lane usage</button></div>
</div>
<div class="cfg-row">
<div><label class="cfg-label" for="confirmWinnerSelection">Confirm the winner</label><div class="cfg-desc">Ask for the score and statistics when a winner is chosen. When off, matches complete at once with nothing entered.</div></div>
<div class="cfg-ctl"><span class="cfg-switch"><input type="checkbox" id="confirmWinnerSelection" checked><span></span></span></div>
</div>
<div class="cfg-row">
<div><label class="cfg-label" for="autoOpenMatchControls">Start on Match Controls</label><div class="cfg-desc">The Tournament Bracket page opens on Match Controls; off, it opens on the bracket. Switch with the tabs in its header.</div></div>
<div class="cfg-ctl"><span class="cfg-switch"><input type="checkbox" id="autoOpenMatchControls" checked><span></span></span></div>
</div>
<div class="cfg-row">
<div><label class="cfg-label" for="defaultPaid">New players are paid</label><div class="cfg-desc">Players added to a tournament start as paid. To remove a player, set them to unpaid first.</div></div>
<div class="cfg-ctl"><span class="cfg-switch"><input type="checkbox" id="defaultPaid"><span></span></span></div>
</div>
<div class="cfg-row">
<div><div class="cfg-label">Referee suggestions</div><div class="cfg-desc">How many players to suggest in each group (winners, losers, assignments).</div></div>
<div class="cfg-ctl"><span class="cfg-step" data-target="refereeSuggestionsLimit" data-min="5" data-max="20" data-step="1"><button type="button" aria-label="Fewer">−</button><output></output><button type="button" aria-label="More">+</button></span><input type="hidden" id="refereeSuggestionsLimit"></div>
</div>
</div>
</section>
<!-- BRACKET -->
<section class="cfg-section" id="cfg-bracket">
<div class="cfg-sec-head"><h3>Bracket</h3></div>
<div class="cfg-panel">
<div class="cfg-row">
<div><div class="cfg-label">Finals position</div><div class="cfg-desc">Both formats. In the middle, double elimination puts the finals between the frontside and backside; single elimination draws the two halves facing a final in the middle, with the bronze final under it. The same setting as the Finals toggle in the bracket header.</div></div>
<div class="cfg-ctl cfg-pics" data-target="bracketFinals">
<button type="button" class="cfg-pic" data-value="right"><svg width="96" height="44" viewBox="0 0 96 44" aria-hidden="true"><g fill="none" stroke="#a8a29e" stroke-width="1"><path d="M16 7.5H21.5V17.5H16M21.5 12.5H26M16 27.5H21.5V37.5H16M21.5 32.5H26M38 12.5H43.5V32.5H38M43.5 22.5H48M60 22.5H72"/></g><g fill="#6b7280"><rect x="4" y="5" width="12" height="5" rx="1"/><rect x="4" y="15" width="12" height="5" rx="1"/><rect x="4" y="25" width="12" height="5" rx="1"/><rect x="4" y="35" width="12" height="5" rx="1"/><rect x="26" y="10" width="12" height="5" rx="1"/><rect x="26" y="30" width="12" height="5" rx="1"/><rect x="48" y="20" width="12" height="5" rx="1"/></g><rect x="72" y="18" width="18" height="9" rx="1.5" fill="#1f2937"/></svg>Right</button>
<button type="button" class="cfg-pic" data-value="middle"><svg width="96" height="44" viewBox="0 0 96 44" aria-hidden="true"><g fill="none" stroke="#a8a29e" stroke-width="1"><path d="M16 12.5H19.5V32.5H16M19.5 22.5H22M34 22.5H39M57 22.5H62M80 12.5H76.5V32.5H80M76.5 22.5H74"/></g><g fill="#6b7280"><rect x="4" y="10" width="12" height="5" rx="1"/><rect x="4" y="30" width="12" height="5" rx="1"/><rect x="22" y="20" width="12" height="5" rx="1"/><rect x="62" y="20" width="12" height="5" rx="1"/><rect x="80" y="10" width="12" height="5" rx="1"/><rect x="80" y="30" width="12" height="5" rx="1"/></g><rect x="39" y="18" width="18" height="9" rx="1.5" fill="#1f2937"/></svg>Middle</button>
</div>
<input type="hidden" id="bracketFinals">
</div>
</div>
</section>
<!-- CHALKER -->
<section class="cfg-section" id="cfg-chalker">
<div class="cfg-sec-head"><div><h3>Chalker</h3><p>Sent to the Chalker when a match starts.</p></div></div>
<div class="cfg-panel">
<div class="cfg-row">
<div><div class="cfg-label">Game</div><div class="cfg-desc">Starting score for every leg.</div></div>
<div class="cfg-ctl">
<div class="cfg-seg" id="cfgX01Seg"><button type="button" data-value="301">301</button><button type="button" data-value="501">501</button><button type="button" data-value="701">701</button><button type="button" data-value="other">Other</button></div>
<select id="chalkerX01Format" hidden><option value="301">301</option><option value="501" selected>501</option><option value="701">701</option></select>
<input type="checkbox" id="chalkerX01CustomToggle" hidden>
<input class="cfg-text mono" type="number" id="chalkerX01Custom" min="2" max="1001" placeholder="2–1001" style="display: none; width: 110px;" aria-label="Starting score">
<p class="cfg-warn" id="chalkerX01Warning" style="display: none;">Unusual scores may make larger QR codes that are harder to scan.</p>
</div>
</div>
<div class="cfg-row">
<div><label class="cfg-label" for="chalkerMaxRounds">Most rounds per leg</label><div class="cfg-desc">A leg ends after this many rounds of three darts.</div></div>
<div class="cfg-ctl"><select class="cfg-text" id="chalkerMaxRounds"><option value="7">7 rounds · 21 darts</option><option value="8">8 rounds · 24 darts</option><option value="9">9 rounds · 27 darts</option><option value="10">10 rounds · 30 darts</option><option value="11">11 rounds · 33 darts</option><option value="12">12 rounds · 36 darts</option><option value="13">13 rounds · 39 darts</option><option value="14">14 rounds · 42 darts</option><option value="15">15 rounds · 45 darts</option><option value="16">16 rounds · 48 darts</option><option value="17">17 rounds · 51 darts</option><option value="18">18 rounds · 54 darts</option><option value="19">19 rounds · 57 darts</option><option value="20">20 rounds · 60 darts</option><option value="100">No limit</option></select></div>
</div>
<div class="cfg-row">
<div><div class="cfg-label">Short leg</div><div class="cfg-desc">A leg won in this many darts or fewer counts as a short leg.</div></div>
<div class="cfg-ctl"><span class="cfg-step" data-target="chalkerShortLegThreshold" data-min="3" data-max="60" data-step="1" data-format="darts"><button type="button" aria-label="Fewer darts">−</button><output></output><button type="button" aria-label="More darts">+</button></span><input type="hidden" id="chalkerShortLegThreshold"></div>
</div>
<div class="cfg-row">
<div><div class="cfg-label">Handover</div><div class="cfg-desc" id="cfgHandoverDesc"></div></div>
<div class="cfg-ctl"><div class="cfg-seg" data-target="chalkerHandover"><button type="button" data-value="qr">QR code</button><button type="button" data-value="network">Network</button><button type="button" data-value="none">None</button></div><input type="hidden" id="chalkerHandover"></div>
</div>
</div>
</section>
<!-- SERVER -->
<section class="cfg-section" id="cfg-server">
<div class="cfg-sec-head"><div><h3>Server & backup</h3><p>Only when the Tournament Manager runs from the Docker image.</p></div><a class="cfg-link" href="docker-quickstart.html" target="_blank" rel="noopener">Docker guide</a></div>
<div class="cfg-panel">
<div class="cfg-row">
<div><label class="cfg-label" for="serverIdDisplay">Server ID</label><div class="cfg-desc">Identifies this Tournament Manager in QR codes. Set automatically.</div></div>
<div class="cfg-ctl"><input class="cfg-text mono" type="text" id="serverIdDisplay" readonly style="width: 180px;"></div>
</div>
<div class="cfg-row">
<div><label class="cfg-label" for="autoUploadTournament">Back up finished tournaments</label><div class="cfg-desc">Save each tournament to the server when it finishes, so any computer connected to the server can open it. <a class="cfg-link" href="automatic-backup.html" target="_blank" rel="noopener">How backup works</a></div></div>
<div class="cfg-ctl"><span class="cfg-switch"><input type="checkbox" id="autoUploadTournament"><span></span></span></div>
</div>
<div class="cfg-row">
<div><label class="cfg-label" for="liveUploadTournament">Live copy while it is played</label><div class="cfg-desc">Send a slim copy of the tournament to the server after every change: the players and their statistics, the matches, scores and lanes. <strong>Anyone who can open the server can read it</strong> until the finished tournament is backed up (or three days pass). Needs the server’s API.</div></div>
<div class="cfg-ctl"><span class="cfg-switch"><input type="checkbox" id="liveUploadTournament"><span></span></span></div>
</div>
<div class="cfg-row">
<div><label class="cfg-label" for="allowSharedTournamentDelete">Allow deleting tournaments</label><div class="cfg-desc">Shows delete buttons for shared tournaments and in the Analytics register.</div></div>
<div class="cfg-ctl"><span class="cfg-switch"><input type="checkbox" id="allowSharedTournamentDelete"><span></span></span></div>
</div>
</div>
<div class="cfg-panel">
<div class="cfg-panel-title">Remote backup <span style="font-weight:500;color:var(--nt-muted);font-size:13px">optional</span></div>
<div class="cfg-row">
<div><label class="cfg-label" for="remoteServerUrl">Server address</label><div class="cfg-desc">Also upload tournaments to this server.</div></div>
<div class="cfg-ctl"><input class="cfg-text mono" type="text" id="remoteServerUrl" placeholder="https://newton.example.com"></div>
</div>
<div class="cfg-row">
<div><label class="cfg-label" for="remoteServerApiKey">API key</label><div class="cfg-desc">The <code>NEWTON_API_KEY</code> set on that server.</div></div>
<div class="cfg-ctl"><input class="cfg-text mono" type="password" id="remoteServerApiKey" autocomplete="off" spellcheck="false" placeholder="API key"><button type="button" class="cfg-btn" id="remoteTestBtn" onclick="testRemoteConnection()">Test connection</button></div>
</div>
<p class="cfg-test" id="remoteTestResult" role="status" hidden></p>
<p class="cfg-test cfg-ok cfg-lock" id="remoteLockNote" hidden><span>✓ Verified. The address and key are locked, so they can't be changed by accident.</span><button type="button" class="cfg-btn" id="remoteUnlockBtn">Change…</button></p>
<input type="hidden" id="remoteServerVerified">
</div>
</section>
<!-- DEVELOPER -->
<section class="cfg-section" id="cfg-developer">
<div class="cfg-sec-head"><h3>Developer</h3></div>
<div class="cfg-panel">
<div class="cfg-row">
<div><label class="cfg-label" for="developerMode">Developer Console</label><div class="cfg-desc">Diagnostics and developer tools. Adds a Console tab to the bracket page.</div></div>
<div class="cfg-ctl"><span class="cfg-switch"><input type="checkbox" id="developerMode"><span></span></span></div>
</div>
</div>
</section>
</div>
</div>
<div class="cfg-savebar" id="cfgSaveBar" role="region" aria-label="Unsaved changes">
<span id="cfgBarText"><span class="cfg-count" id="cfgCount">1</span> <span id="cfgCountText">unsaved change</span></span>
<button type="button" class="cfg-btn ghost" id="cfgDiscard">Discard</button>
<button type="button" class="cfg-btn save" id="cfgSave">Save changes</button>
</div>
<div class="cfg-toast" id="cfgToast" role="status">Settings saved</div>
<div class="cfg-dialog-back" id="cfgUnlock" hidden>
<div class="cfg-dialog" role="dialog" aria-modal="true" aria-labelledby="cfgUnlockTitle">
<h3 id="cfgUnlockTitle">Change the server address or key?</h3>
<p>They are locked because the key was verified against this server. The key is cleared, and backups use whatever you save next. Run <b>Test connection</b> again to lock them.</p>
<div class="cfg-acts">
<button type="button" class="cfg-btn" id="cfgUnlockCancel">Cancel</button>
<button type="button" class="cfg-btn primary" id="cfgUnlockGo">Change</button>
</div>
</div>
</div>
<div class="cfg-dialog-back" id="cfgLeave" hidden>
<div class="cfg-dialog" role="dialog" aria-modal="true" aria-labelledby="cfgLeaveTitle">
<h3 id="cfgLeaveTitle">Save your changes?</h3>
<p>You have <b id="cfgLeaveCount">1 unsaved change</b> on <span class="nt-in-full">Global Settings</span><span class="nt-in-analytics">Custom Settings</span>. Save before you continue?</p>
<div class="cfg-acts">
<button type="button" class="cfg-btn" id="cfgLeaveStay">Stay</button>
<button type="button" class="cfg-btn" id="cfgLeaveDiscard">Discard and continue</button>
<button type="button" class="cfg-btn primary" id="cfgLeaveSave">Save and continue</button>
</div>
</div>
</div>
</div>
<!-- Analytics (css/analytics-page.css): the views, the point mode and the lens on one strip, then the active view -->
<!-- Watching a live tournament: the list of matches (js/live-view.js) -->
<div id="liveList" class="page"></div>
<div id="history" class="page">
<div class="an-wrap">
<div class="an-live" id="liveNowStrip" hidden></div>
<div class="st-title">
<div><h2>Analytics</h2><p>Your tournament history. Nothing here changes the results.</p></div>
<div class="an-title-tools">
<!-- Maintenance tools. Hidden in analytics-only mode (see .mode-analytics in
css/styles.css) because that deployment is shared with club members, and
importing a register overwrites match history. Reachable with ?tm. -->
<span class="an-maint analytics-maintenance-tool">
<span class="an-lbl">Register file</span>
<button type="button" class="st-btn st-sm" onclick="NewtonHistory.exportDB()">Export</button>
<button type="button" class="st-btn st-sm" onclick="document.getElementById('historyImportFile').click()">Import</button>
<button type="button" class="st-btn st-sm" onclick="document.getElementById('tournamentImportFile').click()">Import tournament</button>
</span>
<input type="file" id="historyImportFile" accept=".json" style="display:none;" onchange="NewtonHistory.importDB(event)">
<input type="file" id="tournamentImportFile" accept=".json" style="display:none;" onchange="NewtonHistory.importTournament(event)">
<button type="button" class="st-btn help-info-icon" data-page="history">Help</button>
</div>
</div>
<!-- What you look at (view, points) and which tournaments it counts (the lens) -->
<section class="an-strip" id="analyticsStrip" aria-label="Analytics controls">
<div class="an-strip-row">
<div class="an-views">
<div class="st-seg" role="group" aria-label="View">
<button type="button" class="analytics-view-btn" data-view="dashboard" aria-pressed="true">Dashboard</button><button type="button" class="analytics-view-btn" data-view="leaderboard" aria-pressed="false">Leaderboard</button><button type="button" class="analytics-view-btn" data-view="players" aria-pressed="false">Players</button><button type="button" class="analytics-view-btn" data-view="register" aria-pressed="false">Register</button>
</div>
</div>
<!-- Points: as each tournament was scored, or custom (NewtonHistory points controls) -->
<div class="an-points">
<span class="an-lbl">Points</span>
<div class="st-seg" role="group" aria-label="Points">
<button type="button" id="anPointsPlayed" aria-pressed="true" title="Each tournament's own point values, placement and attendance included">As played</button><button type="button" id="anPointsCustom" aria-pressed="false" aria-expanded="false" aria-controls="anPointsPanel">Custom</button>
</div>
<div class="an-points-panel" id="anPointsPanel" role="dialog" aria-label="Custom points" hidden>
<fieldset>
<legend>Point values</legend>
<label><input type="radio" name="anPointValues" value="current" checked> <span class="nt-in-full">From Global Settings</span><span class="nt-in-analytics">From Custom Settings</span></label>
<label><input type="radio" name="anPointValues" value="original"> As each tournament was played</label>
</fieldset>
<fieldset>
<legend>Count</legend>
<label><input type="checkbox" id="anCountPlacement" checked> Placement points</label>
<label><input type="checkbox" id="anCountAttendance" checked> Attendance points</label>
</fieldset>
<p>Achievement points (180s, high outs, short legs) always count.</p>
</div>
</div>
</div>
<div class="an-strip-row">
<!-- Filled by renderScopeIndicator() -->
<div id="analyticsScopeIndicator" class="an-scope"></div>
<div class="an-lens">
<span class="an-lbl">Lens</span>
<input type="search" id="analyticsTextFilter" class="an-text an-name" placeholder="Tournament name" aria-label="Tournament name" oninput="NewtonHistory.onTextFilter(this.value)">
<span class="an-dates">
<input type="date" id="analyticsDateFrom" class="an-text" aria-label="From" onchange="NewtonHistory.onDateFilter()">
<span class="an-lbl">to</span>
<input type="date" id="analyticsDateTo" class="an-text" aria-label="To" onchange="NewtonHistory.onDateFilter()">
</span>
<button type="button" class="st-btn st-sm" id="lensCurrentHalf" onclick="NewtonHistory.setHalfYear(0)"></button>
<button type="button" class="st-btn st-sm" id="lensPreviousHalf" onclick="NewtonHistory.setHalfYear(-1)"></button>
<button type="button" class="st-link" id="analyticsResetBtn" onclick="NewtonHistory.resetFilters()">Show all</button>
</div>
</div>
</section>
<!-- Dashboard: filled by renderDashboard() -->
<div id="analyticsViewDashboard" class="analytics-view active"></div>
<!-- Leaderboard -->
<div id="analyticsViewLeaderboard" class="analytics-view">
<section class="st-panel">
<div class="st-panel-head">
<h3>Leaderboard <small id="leaderboardMeta"></small></h3>
<div class="an-tools">
<span class="an-legend"><i></i>Top 16</span>
<button type="button" class="st-btn st-sm" onclick="NewtonHistory.exportLeaderboardCSV()">Export CSV</button>
<button type="button" class="st-btn st-sm" onclick="NewtonHistory.exportLeaderboardJSON()">Export JSON</button>
</div>
</div>
<div id="leaderboardTableContainer"></div>
<div class="an-foot">Avg is from Chalker matches only. Click a column to sort, a player to open their profile.</div>
</section>
</div>
<!-- Players: the list, and a profile or comparison of the ticked players -->
<div id="analyticsViewPlayers" class="analytics-view">
<div class="an-players">
<section class="st-panel">
<div class="st-panel-head"><h3>Players <small id="playersMeta"></small></h3></div>
<div id="playersTableContainer"></div>
<div class="an-foot">Tick one player for a profile, up to six to compare. The box at the top ticks the six best.</div>
</section>
<section class="st-panel" id="playersProfilePanel">
<div class="an-empty"><b>No player selected</b>Tick a name to see their profile, or several to compare them.</div>
</section>
</div>
</div>
<!-- Register: tournaments and matches -->
<div id="analyticsViewRegister" class="analytics-view">
<!-- Tabs and the path back: filled by _updateBreadcrumb() -->
<div class="an-reg-nav" id="registerBreadcrumb"></div>
<!-- Tournament list -->
<section class="st-panel" id="historyTournamentList">
<div class="st-panel-head">
<h3>Tournaments <small id="historyTournamentMeta"></small></h3>
<span class="an-note">Ticked tournaments are what every view counts.</span>
</div>
<div id="historyTournamentTableContainer"></div>
</section>
<!-- All matches (flat list across the lens) -->
<section class="st-panel" id="historyAllMatches" style="display:none;">
<div class="st-panel-head"><h3>Matches <small id="historyAllMatchesMeta"></small></h3></div>
<div id="historyAllMatchesTableContainer"></div>
</section>
<!-- One tournament's matches -->
<section class="st-panel" id="historyMatchList" style="display:none;">
<div class="st-panel-head">
<h3 id="historyMatchListTitle"></h3>
<div class="an-tools" id="historyMatchListTools"></div>
</div>
<dl class="an-facts" id="historyMatchListMeta"></dl>
<div id="historyMatchTableContainer"></div>
</section>
<!-- Match detail -->
<section class="st-panel" id="historyMatchDetail" style="display:none;">
<div id="historyMatchDetailBody"></div>
</section>
</div>
</div>
</div>
<!-- Upload to Server Modal -->
<div id="uploadToServerModal" class="modal">
<div class="dlg">
<div class="dlg__main">
<h2 class="dlg__title">Backup to server</h2>
<div class="upload-source-options">
<label class="upload-source-option">
<input type="radio" name="uploadSource" value="active" checked>
<span>Active tournament</span>
</label>
<label class="upload-source-option">
<input type="radio" name="uploadSource" value="file">
<span>From file</span>
</label>
</div>
<div id="uploadDestinationInfo" class="upload-destination"></div>
</div>
<div class="dlg__foot">
<button class="btn" onclick="popDialog()">Cancel</button>
<button class="btn btn-success" onclick="executeUpload()">Upload</button>
</div>
</div>
</div>
<!-- History: Delete Tournament Confirmation Modal -->
<div id="historyDeleteTournamentModal" class="modal">
<div class="dlg dlg--split">
<div class="dlg__grid">
<aside class="dlg__sidebar">
<div class="dlg__sidebar-label">Name</div>
<div class="dlg__sidebar-value" id="historyDeleteTournamentDisplayName">-</div>
<div class="dlg__sidebar-label">Date</div>
<div class="dlg__sidebar-value" id="historyDeleteTournamentDate">-</div>
<div class="dlg__sidebar-label">Tournament Status</div>
<div class="dlg__sidebar-value">Completed</div>
</aside>
<div class="dlg__main">
<span class="dlg__pill dlg__pill--danger">Destructive</span>
<h2 class="dlg__title">Delete tournament</h2>
<p class="dlg__desc">All match records for this tournament will be permanently removed from Analytics. This cannot be undone.</p>
<div>
<label class="dlg__label" for="historyDeleteTournamentInput">Type the tournament name to confirm</label>
<input type="text" id="historyDeleteTournamentInput" class="dlg__input" autocomplete="off" oninput="NewtonHistory.onDeleteInputChange()">
</div>
</div>
</div>
<div class="dlg__foot">
<button class="btn" onclick="popDialog()">Cancel</button>
<button class="btn btn-danger" id="historyDeleteTournamentBtn" disabled onclick="NewtonHistory.confirmDeleteTournament()">Delete Tournament</button>
</div>
</div>
</div>
<!-- Analytics: Achievement Corrections Modal (Docker only, full mode / ?tm) -->
<div id="correctionsModal" class="modal">
<div class="dlg dlg--split dlg--wide">
<div class="dlg__grid">
<aside class="dlg__sidebar">
<div class="dlg__sidebar-label">Tournament</div>
<div class="dlg__sidebar-value" id="correctionsTournamentName">-</div>
<div class="dlg__sidebar-label">Date</div>
<div class="dlg__sidebar-value" id="correctionsTournamentDate">-</div>
<div class="dlg__sidebar-label">As recorded</div>
<div class="dlg__sidebar-value" id="correctionsRecorded" style="white-space:pre-line;">-</div>
</aside>
<div class="dlg__main">
<h2 class="dlg__title">Correct achievements</h2>
<p class="dlg__desc">Corrections are saved on the server and apply to Analytics on every device. The recorded tournament is not changed.</p>
<div>
<label class="dlg__label" for="correctionsPlayer">Player</label>
<select id="correctionsPlayer" class="dlg__input" onchange="NewtonHistory.selectCorrectionPlayer(this.value)"></select>
</div>
<div class="stats-counter-row">
<div class="stat-counter">
<div class="stat-counter__label">180s</div>
<div class="stat-counter__controls">
<button type="button" class="stat-counter__btn stat-counter__btn--minus" onclick="NewtonHistory.adjustCorrection('oneEighties', -1)">−</button>
<span class="stat-counter__value" id="corrOneEighties">0</span>
<button type="button" class="stat-counter__btn stat-counter__btn--plus" onclick="NewtonHistory.adjustCorrection('oneEighties', 1)">+</button>
</div>
</div>
<div class="stat-counter">
<div class="stat-counter__label">Tons</div>
<div class="stat-counter__controls">
<button type="button" class="stat-counter__btn stat-counter__btn--minus" onclick="NewtonHistory.adjustCorrection('tons', -1)">−</button>
<span class="stat-counter__value" id="corrTons">0</span>
<button type="button" class="stat-counter__btn stat-counter__btn--plus" onclick="NewtonHistory.adjustCorrection('tons', 1)">+</button>
</div>
</div>
<div class="stat-counter">
<div class="stat-counter__label">Lollipops</div>
<div class="stat-counter__controls">
<button type="button" class="stat-counter__btn stat-counter__btn--minus" onclick="NewtonHistory.adjustCorrection('lollipops', -1)">−</button>
<span class="stat-counter__value" id="corrLollipops">0</span>
<button type="button" class="stat-counter__btn stat-counter__btn--plus" onclick="NewtonHistory.adjustCorrection('lollipops', 1)">+</button>
</div>
</div>
</div>
<div class="stat-list-editor">
<label class="dlg__label" for="corrShortLegInput">Short Legs (max 21 darts)</label>
<div class="stat-list-editor__input-row">
<input type="number" id="corrShortLegInput" class="dlg__input" min="9" max="21" placeholder="Enter dart count">
<button type="button" class="btn" onclick="NewtonHistory.addCorrectionValue('shortLegs')">Add</button>
</div>
<div id="corrShortLegsList" class="stat-list-editor__items"></div>
</div>
<div class="stat-list-editor">
<label class="dlg__label" for="corrHighOutInput">High Out Score (101+)</label>
<div class="stat-list-editor__input-row">
<input type="number" id="corrHighOutInput" class="dlg__input" min="101" max="170" placeholder="Enter high out score">
<button type="button" class="btn" onclick="NewtonHistory.addCorrectionValue('highOuts')">Add</button>
</div>
<div id="corrHighOutsList" class="stat-list-editor__items"></div>
</div>
</div>
</div>
<div class="dlg__foot">
<button class="btn" onclick="popDialog()">Cancel</button>
<button class="btn" onclick="NewtonHistory.resetCorrectionPlayer()">Reset Player</button>
<button class="btn btn-success" onclick="NewtonHistory.saveCorrections()">Save Corrections</button>
</div>
</div>
</div>
<!-- Statistics Modal -->
<div id="statsModal" class="modal">
<div class="dlg">
<div class="dlg__main">
<h2 class="dlg__title" id="statsPlayerName">Edit player statistics</h2>
<!-- Counter row: 180s / Tons / Lollipops -->
<div class="stats-counter-row">
<div class="stat-counter">
<div class="stat-counter__label">180s</div>
<div class="stat-counter__controls">
<button type="button" class="stat-counter__btn stat-counter__btn--minus" onclick="decrement180s()">−</button>
<span class="stat-counter__value" id="current180sCount">0</span>
<button type="button" class="stat-counter__btn stat-counter__btn--plus" onclick="increment180s()">+</button>
</div>
</div>
<div class="stat-counter">
<div class="stat-counter__label">Tons</div>
<div class="stat-counter__controls">
<button type="button" class="stat-counter__btn stat-counter__btn--minus" onclick="decrementTons()">−</button>
<span class="stat-counter__value" id="currentTonsCount">0</span>
<button type="button" class="stat-counter__btn stat-counter__btn--plus" onclick="incrementTons()">+</button>
</div>
</div>
<div class="stat-counter">
<div class="stat-counter__label">Lollipops</div>
<div class="stat-counter__controls">
<button type="button" class="stat-counter__btn stat-counter__btn--minus" onclick="decrementLollipops()">−</button>
<span class="stat-counter__value" id="currentLollipopsCount">0</span>
<button type="button" class="stat-counter__btn stat-counter__btn--plus" onclick="incrementLollipops()">+</button>
</div>
</div>
</div>
<!-- Short Legs list editor -->
<div class="stat-list-editor">
<label class="dlg__label" for="statsShortLegDarts">Short Legs (max 21 darts)</label>
<div class="stat-list-editor__input-row">
<input type="number" id="statsShortLegDarts" class="dlg__input" min="9" max="21" placeholder="Enter dart count">
<button type="button" class="btn" onclick="addShortLeg()">Add</button>
</div>
<div id="shortLegsList" class="stat-list-editor__items"></div>
</div>
<!-- High Outs list editor -->
<div class="stat-list-editor">
<label class="dlg__label" for="statsHighOut">High Out Score (101+)</label>
<div class="stat-list-editor__input-row">
<input type="number" id="statsHighOut" class="dlg__input" min="101" max="170" placeholder="Enter high out score">
<button type="button" class="btn" onclick="addHighOut()">Add</button>
</div>
<div id="highOutsList" class="stat-list-editor__items"></div>
</div>
</div>
<div class="dlg__foot">
<button class="btn" onclick="closeStatsModal()">Cancel</button>
<button class="btn btn-success" onclick="saveStats()">Save Statistics</button>
</div>
</div>
</div>
<!-- Result QR Scan Modal -->
<div id="qrResultScanModal" class="modal">
<div class="modal-content" style="max-width: 480px; text-align: center;">
<h3>Scan QR Code</h3>
<video id="qrResultScanVideo" style="width: 100%; border-radius: 6px; background: #000; margin: 12px 0;" autoplay playsinline muted></video>
<p style="color: #6b7280; font-size: 14px; margin-bottom: 8px;">Point the camera at the QR code on the Chalker.</p>
<p id="qrResultScanError" style="color: #dc2626; display: none; margin-bottom: 8px;"></p>
<button class="btn" onclick="stopResultQRScanner(); popDialog();">Cancel</button>
</div>
</div>
<!-- Result QR Preview Modal -->
<div id="qrResultPreviewModal" class="modal">
<div class="dlg dlg--wide">
<div class="dlg__main">
<h2 class="dlg__title" id="qrResultPreviewTitle">Result QR</h2>
<div id="qrResultPreviewContent" style="overflow-x: auto;"></div>
</div>
<!-- Populated by showResultQRPreview() — close only, or close + completion buttons -->
<div id="qrResultPreviewActions" class="dlg__foot">
<button class="btn" onclick="popDialog()">Close</button>
</div>
</div>
</div>
<!-- Load JavaScript files in the correct order -->
<script src="lib/qrcode-generator.js"></script>
<script src="lib/jsQR.js"></script>
<script src="js/newton-integrity.js"></script>
<script src="js/newton-db.js"></script>
<script src="js/types.js"></script>
<script src="js/main.js"></script>
<script src="js/lane-management.js"></script>
<script src="js/tournament-management.js"></script>
<script src="js/live-upload.js"></script>
<script src="js/live-view.js"></script>
<script src="js/player-registry.js"></script>
<script src="js/player-management.js"></script>
<script src="js/clean-match-progression.js"></script>
<script src="js/groups.js"></script>
<script src="js/qualifiers.js"></script>
<script src="js/plate.js"></script>
<script src="js/swiss.js"></script>
<script src="js/bracket-rendering.js"></script>
<script src="js/bracket-view.js"></script>
<script src="js/newton-csv.js"></script>
<script src="js/results-config.js"></script>
<script src="js/config-page.js"></script>
<script src="js/dynamic-help-system.js"></script>
<script src="js/analytics.js"></script>
<script src="js/newton-stats.js"></script>
<script src="js/newton-table.js"></script>
<script src="js/newton-charts.js"></script>
<script src="js/newton-history.js"></script>
<script src="js/seeding.js"></script>
<script src="js/qr-bridge.js"></script>
<!-- Optional network client. Separately licensed (see licensed/LICENSE.md) and may be
absent; the Tournament Manager works fully without it. -->
<script src="licensed/js/network-tm.js?v=1"></script>
<footer>
<span id="footerContent"></span></br> Created by Håvard Skrödahl, NewTon DC Malmö
</footer>
<!-- Updated Winner Confirmation Modal with validation -->
<div id="winnerConfirmModal" class="modal">
<div class="dlg dlg--split dlg--wide">
<div class="dlg__grid">
<aside class="dlg__sidebar">
<div class="dlg__sidebar-label">Match</div>
<div class="dlg__sidebar-value" id="winnerSidebarMatch">-</div>
<div class="dlg__sidebar-label">Bracket</div>
<div class="dlg__sidebar-value" id="winnerSidebarBracket">-</div>
<div class="dlg__sidebar-label">Edit Statistics</div>
<div class="dlg__sidebar-value winner-stats-stack">
<button type="button" class="btn" id="winnerStatsLink">-</button>
<button type="button" class="btn" id="loserStatsLink">-</button>
</div>
</aside>
<div class="dlg__main">
<h2 class="dlg__title" id="winnerConfirmDialogTitle">Confirm match winner</h2>
<div id="winnerConfirmBody">
<!-- Progression populated by JS -->
</div>
<!-- Leg score form -->
<div id="legScoresSection"
style="margin: 20px 0; padding: 15px; background: #f8f9fa; border: 1px solid #e5e7eb;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
<span style="font-weight: 600; color: #111827;">Final Score (optional):</span>
</div>
<div style="display: flex; justify-content: space-between; align-items: center;">
<div style="display: flex; align-items: center;">
<span id="winnerNameForLegs" style="font-weight: 600; color: #065f46; margin-right: 10px; min-width: 80px;">Winner</span>
<input type="number" id="winnerLegs" min="0" max="7" style="width: 60px; padding: 6px 8px; border: 1px solid #d1d5db; text-align: center;">
<span style="margin: 0 8px; color: #6b7280;">legs</span>
</div>
<div style="display: flex; align-items: center;">
<span id="loserNameForLegs" style="font-weight: 600; color: #6b7280; margin-right: 10px; min-width: 80px;">Loser</span>
<input type="number" id="loserLegs" min="0" max="7" value="0" style="width: 60px; padding: 6px 8px; border: 1px solid #d1d5db; text-align: center;">
<span style="margin: 0 8px; color: #6b7280;">legs</span>
</div>
</div>
<!-- Validation message inserted by JS -->
</div>
<!-- Populated by renderCompletionAchievements() when stats are entered during this session -->
<div id="completionAchievementsSummary" style="display:none;"></div>
</div>
</div>
<div class="dlg__foot">
<button class="btn" id="winnerConfirmCancel">Cancel</button>
<button class="btn btn-success" id="scanResultQRBtn" onclick="openResultQRScanner(this.dataset.matchId)" style="white-space: nowrap; box-shadow: 0 0 12px rgba(249, 115, 22, 0.4), 0 0 20px rgba(249, 115, 22, 0.1); border-color: #fb923c;">Scan Results QR</button>
<button class="btn btn-success" id="winnerConfirmOK">Confirm Winner</button>
</div>
</div>
</div>
<!-- Match Controls Modal -->
<!-- Match Controls Modal -->
<!-- Statistics Modal -->
<!-- Leaderboard dialog, opened from the bracket header and Match Controls (no exports here) -->
<div id="statisticsModal" class="modal">
<div class="dlg dlg--leaderboard">
<div class="dlg__main">
<h2 class="dlg__title">Leaderboard<small>click a player to edit statistics</small></h2>
<div class="nt-leaderboard-scroll">
<table class="nt-leaderboard" id="statisticsTable">
<thead>
<tr>
<th>Rank</th>
<th>Player</th>
<th>Points</th>
<th>Short legs</th>
<th>High outs</th>
<th>180s</th>
<th>Tons</th>
<th>Legs won</th>
<th>Legs lost</th>
</tr>
</thead>
<tbody id="statisticsTableBody">
</tbody>
</table>
</div>
</div>
<div class="dlg__foot">
<button class="btn btn-primary" onclick="popDialog()">Close</button>
</div>
</div>
</div>
<!-- Undo Confirmation Modal -->
<div id="undoConfirmModal" class="modal">
<div class="dlg dlg--split">
<div class="dlg__grid">
<aside class="dlg__sidebar">
<div class="dlg__sidebar-label">Match</div>
<div class="dlg__sidebar-value" id="undoSidebarMatch">-</div>
<div class="dlg__sidebar-label">Bracket</div>
<div class="dlg__sidebar-value" id="undoSidebarBracket">-</div>
<div class="dlg__sidebar-label">Players</div>
<div class="dlg__sidebar-value" id="undoSidebarPlayers">-</div>
</aside>
<div class="dlg__main">
<span class="dlg__pill">Destructive</span>
<h2 class="dlg__title">Undo match</h2>
<div id="undoConsequences">
<!-- Consequences populated by JS -->
</div>
</div>
</div>
<div class="dlg__foot">
<button class="btn" id="undoConfirmCancel">Cancel</button>
<button class="btn btn-warning" id="undoConfirmOK">Undo match</button>
<button class="btn btn-danger" id="undoConfirmOKWithAchievements" style="display:none;">Undo match + achievements</button>
</div>
</div>
</div>
<!-- Bracket Generation Confirmation Modal -->
<div id="bracketConfirmModal" class="modal">
<div class="dlg dlg--split dlg--wide">
<div class="dlg__grid">
<aside class="dlg__sidebar">
<div class="dlg__sidebar-label">Name</div>
<div class="dlg__sidebar-value" id="bracketConfirmName">-</div>
<div class="dlg__sidebar-label">Format</div>
<div class="dlg__sidebar-value" id="bracketConfirmFormat">-</div>
<div class="dlg__sidebar-label" id="bracketConfirmSizeLabel">Bracket Size</div>
<div class="dlg__sidebar-value" id="bracketConfirmSize">-</div>
<div class="dlg__sidebar-label">Players</div>
<div class="dlg__sidebar-value" id="bracketConfirmPlayerCount">-</div>
<div class="dlg__sidebar-label" id="bracketConfirmByesLabel">Byes</div>
<div class="dlg__sidebar-value" id="bracketConfirmByes">-</div>
</aside>
<div class="dlg__main">
<h2 class="dlg__title" id="bracketConfirmTitle">Generate Bracket</h2>
<p class="dlg__desc" id="bracketConfirmDesc">These players will be placed into the bracket. Make sure all players are registered before proceeding.</p>
<div id="bracketConfirmPlayers" class="bracket-confirm-players"></div>
</div>
</div>
<div class="dlg__foot">
<button class="btn" onclick="popDialog()" autofocus>Cancel</button>
<button class="btn btn-success" onclick="confirmBracketGeneration()">Generate</button>
</div>
</div>
</div>
<!-- Undo the cup draw (groups and cups): back to the group stage -->
<div id="undoCupDrawModal" class="modal">
<div class="dlg dlg--split">
<div class="dlg__grid">
<aside class="dlg__sidebar">
<div class="dlg__sidebar-label">Name</div>
<div class="dlg__sidebar-value" id="undoCupsName">-</div>
<div class="dlg__sidebar-label">A cup</div>
<div class="dlg__sidebar-value" id="undoCupsA">-</div>
<div class="dlg__sidebar-label">B cup</div>
<div class="dlg__sidebar-value" id="undoCupsB">-</div>
</aside>
<div class="dlg__main">
<h2 class="dlg__title">Undo the cup draw?</h2>
<p class="dlg__desc">The cups are removed and the group stage opens again: group results can be undone, and the cups are drawn again from the group tables when you're ready.</p>
<p class="dlg__desc">Only possible while no cup match has been started or played.</p>
</div>
</div>
<div class="dlg__foot">
<button class="btn" onclick="popDialog()" autofocus>Cancel</button>
<button class="btn btn-warning" onclick="undoCupDrawConfirmed()">Undo the cup draw</button>
</div>
</div>
</div>
<!-- Tournament In Progress Warning Modal -->
<div id="tournamentProgressModal" class="modal">
<div class="dlg dlg--split">
<div class="dlg__grid">
<aside class="dlg__sidebar">
<div class="dlg__sidebar-label">Name</div>
<div class="dlg__sidebar-value" id="progressTournamentName">-</div>
<div class="dlg__sidebar-label">Tournament Status</div>
<div class="dlg__sidebar-value" id="progressTournamentStatus">-</div>
<div class="dlg__sidebar-label">Progress</div>
<div class="dlg__sidebar-value" id="progressMatchProgress">-</div>
<div class="dlg__sidebar-label">Players</div>
<div class="dlg__sidebar-value" id="progressPlayerCount">-</div>
</aside>
<div class="dlg__main">
<span class="dlg__pill">Warning</span>
<h2 class="dlg__title">Tournament already in progress</h2>
<p class="dlg__desc">To start fresh or change the bracket, reset the tournament from the <strong>Setup</strong> page. Players are kept; only matches and bracket are cleared.</p>
<p class="dlg__desc">If a player arrived late, use the <strong>Player arrived late?</strong> button at the bottom of the Tournament Players list instead.</p>
</div>
</div>
<div class="dlg__foot">
<button class="btn" onclick="popDialog()">Got It</button>
</div>
</div>
</div>
<!-- Reset Tournament Confirmation Modal -->
<div id="resetTournamentModal" class="modal">
<div class="dlg dlg--split">
<div class="dlg__grid">
<aside class="dlg__sidebar">
<div class="dlg__sidebar-label">Name</div>
<div class="dlg__sidebar-value" id="resetTournamentName">-</div>
<div class="dlg__sidebar-label">Tournament Status</div>
<div class="dlg__sidebar-value" id="resetTournamentStatus">-</div>
<div class="dlg__sidebar-label">Progress</div>
<div class="dlg__sidebar-value" id="resetMatchProgress">-</div>
<div class="dlg__sidebar-label">Players</div>
<div class="dlg__sidebar-value" id="resetPlayerCount">-</div>
</aside>
<div class="dlg__main">
<span class="dlg__pill">Destructive</span>
<h2 class="dlg__title">Reset tournament</h2>
<p class="dlg__desc">All matches, brackets and standings will be permanently deleted. Players are kept, but their elimination status is reset.</p>
<div>
<label class="dlg__label" for="resetConfirmationInput">Type the tournament name to confirm</label>
<input type="text" id="resetConfirmationInput" class="dlg__input" autocomplete="off">
</div>
</div>
</div>
<div class="dlg__foot">
<button class="btn" onclick="popDialog()">Cancel</button>
<button class="btn btn-warning" id="confirmResetBtn" onclick="confirmReset()" disabled>Reset Tournament</button>
</div>
</div>
</div>
<!-- Delete Tournament Confirmation Modal -->
<div id="deleteTournamentModal" class="modal">
<div class="dlg dlg--split">
<div class="dlg__grid">
<aside class="dlg__sidebar">
<div class="dlg__sidebar-label">Name</div>
<div class="dlg__sidebar-value" id="deleteTournamentName">-</div>
<div class="dlg__sidebar-label">Date</div>
<div class="dlg__sidebar-value" id="deleteTournamentDate">-</div>
<div class="dlg__sidebar-label">Tournament Status</div>
<div class="dlg__sidebar-value" id="deleteTournamentStatus">-</div>
<div class="dlg__sidebar-label">Players</div>
<div class="dlg__sidebar-value" id="deleteTournamentPlayers">-</div>
</aside>
<div class="dlg__main">
<span class="dlg__pill dlg__pill--danger">Destructive</span>
<h2 class="dlg__title">Delete tournament</h2>
<p class="dlg__desc">The tournament file will be completely removed from your device. All tournament data, player statistics, match results, history, and configuration go with it.</p>
<p class="dlg__desc">The tournament's record in Analytics is not affected and remains available for review.</p>
</div>
</div>
<div class="dlg__foot">
<button class="btn" onclick="popDialog()">Cancel</button>
<button class="btn btn-danger" id="confirmDeleteBtn" onclick="confirmDeleteTournament()">Delete Tournament</button>
</div>
</div>
</div>
<!-- Import Tournament Overwrite Modal -->
<div id="importOverwriteModal" class="modal">
<div class="dlg dlg--split">
<div class="dlg__grid">
<aside class="dlg__sidebar">
<div class="dlg__sidebar-label">Name</div>
<div class="dlg__sidebar-value" id="importTournamentName">-</div>
<div class="dlg__sidebar-label">Date</div>
<div class="dlg__sidebar-value" id="importTournamentDate">-</div>
<div class="dlg__sidebar-label">Tournament Status</div>
<div class="dlg__sidebar-value" id="importTournamentStatus">-</div>
</aside>
<div class="dlg__main">
<span class="dlg__pill">Destructive</span>
<h2 class="dlg__title">Tournament already exists</h2>
<p class="dlg__desc">Importing this file will replace the matching tournament on your device. All existing match results, player statistics, and tournament data will be lost.</p>
<p class="dlg__desc">Alternative: cancel and rename the imported tournament file to keep both.</p>
</div>
</div>
<div class="dlg__foot">
<button class="btn" onclick="popDialog()">Cancel Import</button>
<button class="btn btn-warning" id="confirmOverwriteBtn" onclick="confirmOverwriteTournament()">Overwrite Existing</button>
</div>
</div>
</div>
<!-- Import Tournament Confirmation Modal -->
<div id="importConfirmModal" class="modal">
<div class="dlg dlg--split">
<div class="dlg__grid">
<aside class="dlg__sidebar">
<div class="dlg__sidebar-label">Name</div>
<div class="dlg__sidebar-value" id="importConfirmName">-</div>
<div class="dlg__sidebar-label">Date</div>
<div class="dlg__sidebar-value" id="importConfirmDate">-</div>
<div class="dlg__sidebar-label">Tournament Status</div>
<div class="dlg__sidebar-value" id="importConfirmStatus">-</div>
<div class="dlg__sidebar-label">Progress</div>
<div class="dlg__sidebar-value" id="importConfirmMatches">-</div>
<div class="dlg__sidebar-label">Players</div>
<div class="dlg__sidebar-value" id="importConfirmPlayers">-</div>
</aside>
<div class="dlg__main">
<span class="dlg__pill" id="importOldFormatPill" style="display: none;">Old format</span>
<h2 class="dlg__title">Import tournament</h2>
<p class="dlg__desc">Your current tournament data is automatically saved and can be reloaded at any time — importing will not cause any data loss.</p>
<p class="dlg__desc" id="importOldFormatWarning" style="display: none;">This tournament is from an older version. It will import successfully, but undo history will not be available. All other features work normally.</p>
</div>
</div>
<div class="dlg__foot">
<button class="btn" onclick="popDialog()">Cancel</button>
<button class="btn btn-primary" id="confirmImportBtn" onclick="confirmImport()">Import Tournament</button>
</div>
</div>
</div>
<!-- Load Tournament Confirmation Modal -->
<div id="loadTournamentModal" class="modal">
<div class="dlg dlg--split">
<div class="dlg__grid">
<aside class="dlg__sidebar">
<div class="dlg__sidebar-label">Name</div>
<div class="dlg__sidebar-value" id="loadTournamentName">-</div>
<div class="dlg__sidebar-label">Date</div>
<div class="dlg__sidebar-value" id="loadTournamentDate">-</div>
<div class="dlg__sidebar-label">Format</div>
<div class="dlg__sidebar-value" id="loadTournamentFormat">-</div>
<div class="dlg__sidebar-label">Tournament Status</div>
<div class="dlg__sidebar-value" id="loadTournamentStatus">-</div>
<div class="dlg__sidebar-label">Progress</div>
<div class="dlg__sidebar-value" id="loadMatchProgress">-</div>
<div class="dlg__sidebar-label">Players</div>
<div class="dlg__sidebar-value" id="loadPlayerCount">-</div>
</aside>
<div class="dlg__main">
<h2 class="dlg__title">Load tournament</h2>
<p class="dlg__desc" id="loadTournamentDesc">-</p>
</div>
</div>
<div class="dlg__foot">
<button class="btn" onclick="popDialog()">Cancel</button>
<button class="btn btn-primary" id="confirmLoadBtn" onclick="confirmLoadTournament()">Load Tournament</button>
</div>
</div>
</div>
<!-- Analytics Import Confirmation Modal -->
<div id="analyticsImportModal" class="modal">
<div class="dlg dlg--split">
<div class="dlg__grid">
<aside class="dlg__sidebar">
<div class="dlg__sidebar-label">Name</div>
<div class="dlg__sidebar-value" id="analyticsImportName">-</div>
<div class="dlg__sidebar-label">Date</div>
<div class="dlg__sidebar-value" id="analyticsImportDate">-</div>
<div class="dlg__sidebar-label">Tournament Status</div>
<div class="dlg__sidebar-value" id="analyticsImportStatus">-</div>
<div class="dlg__sidebar-label">Progress</div>
<div class="dlg__sidebar-value" id="analyticsImportProgress">-</div>
<div class="dlg__sidebar-label">Players</div>
<div class="dlg__sidebar-value" id="analyticsImportPlayers">-</div>
</aside>
<div class="dlg__main">
<span class="dlg__pill" id="analyticsImportMissingSettingsPill" style="display: none;">Missing settings</span>
<h2 class="dlg__title">Import to Analytics</h2>
<p class="dlg__desc" id="analyticsImportDesc">This adds the tournament to the Analytics register. No existing data is modified.</p>
<p class="dlg__desc" id="analyticsImportWarning" style="display: none;">This export does not include point values or match length settings — current Global Settings will be used instead.</p>
</div>
</div>
<div class="dlg__foot">
<button class="btn" onclick="popDialog()">Cancel</button>
<button class="btn btn-success" id="analyticsImportConfirmBtn">Import</button>
</div>
</div>
</div>
<!-- Export Confirmation Modal -->
<div id="exportConfirmModal" class="modal">
<div class="dlg dlg--split">
<div class="dlg__grid">
<aside class="dlg__sidebar">
<div class="dlg__sidebar-label">Name</div>
<div class="dlg__sidebar-value" id="exportTournamentName">-</div>
<div class="dlg__sidebar-label">Date</div>
<div class="dlg__sidebar-value" id="exportTournamentDate">-</div>
<div class="dlg__sidebar-label">Tournament Status</div>
<div class="dlg__sidebar-value" id="exportTournamentStatus">-</div>
<div class="dlg__sidebar-label">Progress</div>
<div class="dlg__sidebar-value" id="exportMatchProgress">-</div>
<div class="dlg__sidebar-label">Players</div>
<div class="dlg__sidebar-value" id="exportPlayerCount">-</div>
</aside>
<div class="dlg__main">
<span class="dlg__pill" id="exportIncompletePill" style="display: none;">Incomplete</span>
<h2 class="dlg__title">Export tournament results</h2>
<p class="dlg__desc">Save as <strong id="exportFormat">-</strong>: <strong id="exportFilename">-</strong></p>
<p class="dlg__desc" id="exportIncompleteWarning" style="display: none;">This tournament isn't finished yet — the export will reflect current standings, not final rankings.</p>
</div>
</div>
<div class="dlg__foot">
<button class="btn" onclick="popDialog()">Cancel</button>
<button class="btn btn-success" id="confirmExportBtn" onclick="confirmExport()">Export Results</button>
</div>
</div>
</div>
<!-- Match Detail Modal (from Setup → Match History) -->
<div id="matchDetailModal" class="modal">
<div class="dlg dlg--match">
<!-- Filled by NewtonHistory.openMatchModal() (the same match detail as in Analytics) -->
<div class="dlg__main" id="matchDetailModalBody"></div>
<div class="dlg__foot">
<button class="btn" onclick="popDialog()">Close</button>
</div>
</div>
</div>
<!-- Match QR Modal -->
<div id="matchQRModal" class="modal">
<div class="modal-content" style="max-width: 580px; text-align: center;">
<h3 id="matchQRTitle">Chalker QR Code</h3>
<p id="matchQRSubtitle" style="color: #6b7280; margin: 0 0 12px 0; font-size: 17px;"></p>
<div id="matchQRCode" style="display: flex; justify-content: center; margin: 8px 0;"></div>
<p id="matchQRWarning" style="color: #d97706; margin: 12px 0 0 0; display: none;"></p>
<div class="text-center mt-20">
<button class="btn" onclick="popDialog()">Close</button>
</div>
</div>
</div>
<!-- Late Registration Info Modal -->
<div id="lateRegInfoModal" class="modal">
<div class="dlg">
<div class="dlg__main">
<h2 class="dlg__title">Registering a late arrival</h2>
<p class="dlg__desc" id="lateRegInfoFirstPara">To register a late arrival, enable the <strong>Developer Console</strong> in <strong>Global Settings</strong>, then open the <strong>Console</strong> tab on the Tournament Bracket page.</p>
<div class="dlg__note">
<p class="dlg__desc">In the <strong>Developer Console</strong>, use <strong>Late registration</strong> to add players.</p>
<p class="dlg__desc">The new player will be placed randomly in an available walkover spot in Frontside Round 1 and participate as a fully registered, paid player from that point forward.</p>
</div>
<p class="dlg__desc" style="font-style: italic;">The Developer Console is for changes made after a tournament has started — outside the normal workflow.</p>
</div>
<div class="dlg__foot">
<button class="btn btn-primary" onclick="popDialog()">Got it</button>
</div>
</div>
</div>
</body>
</html>