geoquiz/index.html
Joshua Hirsig ea561cd0bd Modularize index.html into separate CSS/JS files
Split the monolithic 5510-line index.html into 14 separate files:
- css/styles.css (1085 lines) - all styling
- js/translations.js - i18n (FR/EN/UA/DE)
- js/data.js - categories, presets, fallback data
- js/utils.js - shuffle, seed encoding/decoding
- js/seed.js - seed generation algorithms
- js/ribbon.js - country/category ribbon selectors
- js/setup.js - custom panel, mode selection, presets
- js/game.js - core game loop, state, timer
- js/game-end.js - end screen, Hungarian algorithm, odometer
- js/daily.js - daily mode, seeded RNG, leaderboard
- js/hints.js - tooltips, country descriptions
- js/ui-effects.js - animations, ripple, particles
- js/mobile.js - responsive ribbon tabs

Also allow .docx/.xlsx in .gitignore (used as dev resources).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 19:53:23 +01:00

513 lines
28 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="fr">
<head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-220WPFE2DM"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag("js", new Date());
gtag("config", "G-220WPFE2DM");
</script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>LeGrandGeoQuiz</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@400;700;800&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/twemoji@14.0.2/dist/twemoji.min.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div id="lang-switcher">
<button class="lang-btn" onclick="setLang('fr')">FR</button>
<button class="lang-btn" onclick="setLang('en')">EN</button>
<button class="lang-btn" onclick="setLang('ua')">UA</button>
<button class="lang-btn" onclick="setLang('de')">DE</button>
</div>
<div id="app">
<!-- SETUP -->
<div id="panel-setup" class="panel">
<div class="logo">◈ LeGrandGeoQuiz</div>
<h1>Geo<span class="highlight">Quiz</span></h1>
<p class="subtitle" data-i18n="subtitle"></p>
<div id="loader"><div class="dot-spin"></div>&nbsp;<span data-i18n="loading"></span></div>
<div id="setup-ready" class="hidden">
<ul class="rules">
<li><span class="ico">&#8594;</span><span data-i18n="rule1"></span></li>
<li><span class="ico">&#8594;</span><span data-i18n="rule2"></span></li>
<li><span class="ico">&#8594;</span><span data-i18n="rule3"></span></li>
<li><span class="ico">&#8594;</span><span data-i18n="rule4"></span></li>
<li><span class="ico">&#8594;</span><span data-i18n="rule5"></span></li>
</ul>
<div style="display:flex;flex-direction:column;gap:8px;margin-top:4px;">
<!-- Mode Daily ⚡ -->
<div style="border:1px solid rgba(245,200,66,0.4);border-radius:12px;padding:10px 12px;background:rgba(245,200,66,0.04);">
<button class="btn-daily" id="btn-daily" onclick="startDailyGame()">
🏆 <span data-i18n="btnDaily"></span>
<span class="daily-badge" id="daily-date-badge"></span>
</button>
<div class="daily-played-msg hidden" id="daily-played-msg" data-i18n="dailyAlreadyPlayed"></div>
<div style="text-align:center;margin-top:6px;">
<button onclick="openLeaderboard()" style="background:none;border:none;color:#f5c842;font-family:'DM Mono',monospace;font-size:0.67em;cursor:pointer;opacity:0.8;text-decoration:underline;" data-i18n="dailyLeaderboardLink"></button>
</div>
</div>
<!-- Mode Normal -->
<div style="border:1px solid rgba(79,255,176,0.25);border-radius:12px;padding:10px 12px;background:rgba(79,255,176,0.04);">
<button class="btn-primary" style="display:block;width:100%;margin-top:0;" onclick="startGame('normal')" data-i18n="btnNormal"></button>
</div>
<!-- Mode Hardcore -->
<div style="border:1px solid rgba(255,61,90,0.25);border-radius:12px;padding:10px 12px;background:rgba(255,61,90,0.04);">
<button class="btn-hardcore" onclick="startGame('hardcore')" data-i18n="btnHardcore"></button>
</div>
<!-- Mode Reverse -->
<div style="border:1px solid rgba(245,200,66,0.3);border-radius:12px;padding:10px 12px;background:rgba(245,200,66,0.04);">
<button class="btn-reverse" onclick="startGame('reverse')" data-i18n="btnReverse"></button>
</div>
<!-- Mode Personnalisé + Seed -->
<div style="border:1px solid rgba(167,139,250,0.3);border-radius:12px;padding:10px 12px;background:rgba(167,139,250,0.04);">
<div style="margin-bottom:10px;">
<div style="font-family:'DM Mono',monospace;font-size:0.7em;color:var(--accent3);margin-bottom:6px;" data-i18n="seedLabel"></div>
<div style="display:flex;gap:8px;">
<input id="seed-input" type="text" data-i18n-placeholder="seedPlaceholder"
style="flex:1;padding:9px 12px;background:var(--surface2);border:1px solid rgba(167,139,250,0.3);border-radius:8px;color:var(--text);font-family:'DM Mono',monospace;font-size:0.75em;outline:none;">
<button style="margin-top:0;padding:9px 14px;font-size:0.8em;border-radius:8px;border:1px solid rgba(167,139,250,0.4);background:rgba(167,139,250,0.1);color:var(--accent3);font-family:'DM Mono',monospace;font-weight:700;cursor:pointer;" onclick="startWithSeedInput()" data-i18n="seedLoad"></button>
</div>
</div>
<button class="btn-custom" onclick="openCustomPanel()" data-i18n="btnCustom"></button>
</div>
</div>
<!-- GitHub link -->
<div style="margin-top:18px;text-align:center;">
<a href="https://github.com/mathieuviart/legrandgeoquiz" target="_blank" rel="noopener"
style="display:inline-flex;align-items:center;gap:7px;font-family:'DM Mono',monospace;font-size:0.68em;color:var(--muted);text-decoration:none;padding:6px 12px;border-radius:8px;border:1px solid var(--border);transition:all 0.15s;"
onmouseover="this.style.borderColor='var(--accent3)';this.style.color='var(--accent3)'"
onmouseout="this.style.borderColor='var(--border)';this.style.color='var(--muted)'">
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor" style="flex-shrink:0">
<path d="M12 0C5.374 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0112 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z"/>
</svg>
github.com/mathieuviart/legrandgeoquiz
</a>
</div>
</div>
</div>
<!-- CUSTOM SETTINGS -->
<div id="panel-custom" class="panel hidden">
<div class="logo">&#9672; LeGrandGeoQuiz</div>
<h1><span class="highlight" data-i18n="customTitle"></span></h1>
<div class="custom-section">
<span class="custom-label" data-i18n="customCountLabel"></span>
<div class="custom-value-display"><span id="custom-n-display">8</span> <span data-i18n="customCountUnit"></span></div>
<input type="range" class="custom-slider" id="custom-n-slider" min="2" max="16" value="8"
oninput="ribbonOnSliderChange(this.value)">
</div>
<div class="custom-section">
<span class="custom-label" data-i18n="customTimeLabel"></span>
<div class="time-slider-wrap">
<div class="time-slider-val" id="time-slider-val">20s</div>
<input type="range" class="custom-slider" id="time-slider"
min="0" max="7" value="4"
oninput="selectTimeSlider(this.value)">
<div class="time-slider-ticks">
<span>1s</span><span>5s</span><span>10s</span><span>15s</span>
<span class="active-tick">20s</span><span>30s</span><span>60s</span><span></span>
</div>
</div>
</div>
<div class="custom-section">
<span class="custom-label" data-i18n="customSubModeLabel"></span>
<div class="submode-pills">
<div class="submode-pill active-normal" id="submode-pill-normal" onclick="selectSubModePill('normal')">
<span class="sp-icon">🎯</span>
<span class="sp-label" data-i18n="btnNormal"></span>
</div>
<div class="submode-pill" id="submode-pill-hardcore" onclick="selectSubModePill('hardcore')">
<span class="sp-icon">💀</span>
<span class="sp-label" data-i18n="btnHardcore"></span>
</div>
<div class="submode-pill" id="submode-pill-reverse" onclick="selectSubModePill('reverse')">
<span class="sp-icon">🔄</span>
<span class="sp-label" data-i18n="btnReverse"></span>
</div>
</div>
</div>
<div class="custom-section">
<span class="custom-label">
<span data-i18n="customDraftLabel"></span>
<button class="draft-info-btn" onclick="toggleDraftInfo()" title="?">?</button>
</span>
<div class="draft-tooltip" id="draft-tooltip" data-i18n="customDraftInfo"></div>
<div class="draft-pills">
<div class="draft-pill active" id="draft-pill-auto" onclick="selectDraftPill('auto')">
<span class="dp-icon">🔀</span>
<span class="dp-label" data-i18n="draftAuto"></span>
</div>
<div class="draft-pill" id="draft-pill-cats" onclick="selectDraftPill('cats')">
<span class="dp-icon">📊</span>
<span class="dp-label" data-i18n="draftCats"></span>
</div>
<div class="draft-pill" id="draft-pill-countries" onclick="selectDraftPill('countries')">
<span class="dp-icon">🌍</span>
<span class="dp-label" data-i18n="draftCountries"></span>
</div>
</div>
</div>
<div style="display:flex;flex-direction:column;gap:10px;margin-top:4px;">
<!-- Country ribbon selector -->
<div class="ribbon-section">
<div class="ribbon-section-label">
<span data-i18n="ribbonCountriesTitle"></span>
<span class="ribbon-count" id="ribbon-incl-count">0 forcés</span>
</div>
<input type="text" class="ribbon-search" id="ribbon-search" data-i18n-placeholder="ribbonSearchCountry" oninput="ribbonFilter(this.value)">
<div class="ribbon-wrap">
<!-- EXCLUSION -->
<div class="ribbon-col">
<div class="ribbon-col-title excl" data-i18n="ribbonColExcl"></div>
<div class="ribbon-list excl" id="ribbon-excl" ondragover="ribbonDragOver(event)" ondrop="ribbonDrop(event,'excl')"></div>
</div>
<!-- Buttons Excl ↔ Poss -->
<div class="ribbon-btns" id="ribbon-btns-ep">
<button class="ribbon-btn danger" onclick="ribbonMove('excl','poss',false)" data-i18n-title="ribbonTitleToExcl"></button>
<button class="ribbon-btn danger" onclick="ribbonMove('excl','poss',true)" data-i18n-title="ribbonTitleAllToExcl">»</button>
<div class="ribbon-btn-sep"></div>
<button class="ribbon-btn" onclick="ribbonMove('poss','excl',false)" data-i18n-title="ribbonTitleExcl"></button>
<button class="ribbon-btn" onclick="ribbonMove('poss','excl',true)" data-i18n-title="ribbonTitleAllExcl">«</button>
</div>
<!-- POSSIBLE -->
<div class="ribbon-col">
<div class="ribbon-col-title poss" data-i18n="ribbonColPoss"></div>
<div class="ribbon-list poss" id="ribbon-poss" ondragover="ribbonDragOver(event)" ondrop="ribbonDrop(event,'poss')"></div>
</div>
<!-- Buttons Poss ↔ Incl -->
<div class="ribbon-btns" id="ribbon-btns-pi">
<button class="ribbon-btn success" onclick="ribbonMove('poss','incl',false)" data-i18n-title="ribbonTitleIncl"></button>
<div class="ribbon-btn-sep"></div>
<button class="ribbon-btn" onclick="ribbonMove('incl','poss',false)" data-i18n-title="ribbonTitleRemove"></button>
</div>
<!-- INCLUSION -->
<div class="ribbon-col">
<div class="ribbon-col-title incl" data-i18n="ribbonColIncl"></div>
<div class="ribbon-list incl" id="ribbon-incl" ondragover="ribbonDragOver(event)" ondrop="ribbonDrop(event,'incl')"></div>
</div>
</div>
<div class="ribbon-slots">
<span id="ribbon-slots-txt">0 / 8 slots forcés</span>
<div class="ribbon-slots-bar">
<div class="ribbon-slots-fill" id="ribbon-slots-fill" style="width:0%"></div>
</div>
</div>
</div>
<!-- Category ribbon selector -->
<div class="ribbon-section">
<div class="ribbon-section-label">
<span data-i18n="ribbonCatsTitle"></span>
<span class="ribbon-count" id="catribbon-incl-count">0 forcées</span>
</div>
<input type="text" class="ribbon-search" id="catribbon-search" data-i18n-placeholder="ribbonSearchCat" oninput="catRibbonFilter(this.value)">
<div class="ribbon-wrap">
<!-- EXCLUSION -->
<div class="ribbon-col">
<div class="ribbon-col-title excl" data-i18n="ribbonColExclCat"></div>
<div class="ribbon-list excl" id="catribbon-excl" ondragover="catRibbonDragOver(event)" ondrop="catRibbonDrop(event,'excl')"></div>
</div>
<!-- Buttons Excl ↔ Poss -->
<div class="ribbon-btns">
<button class="ribbon-btn danger" onclick="catRibbonMove('excl','poss',false)" data-i18n-title="ribbonTitleToExcl"></button>
<button class="ribbon-btn danger" onclick="catRibbonMove('excl','poss',true)" data-i18n-title="ribbonTitleAllToExcl">»</button>
<div class="ribbon-btn-sep"></div>
<button class="ribbon-btn" onclick="catRibbonMove('poss','excl',false)" data-i18n-title="ribbonTitleExcl"></button>
<button class="ribbon-btn" onclick="catRibbonMove('poss','excl',true)" data-i18n-title="ribbonTitleAllExcl">«</button>
</div>
<!-- POSSIBLE -->
<div class="ribbon-col">
<div class="ribbon-col-title poss" data-i18n="ribbonColPoss"></div>
<div class="ribbon-list poss" id="catribbon-poss" ondragover="catRibbonDragOver(event)" ondrop="catRibbonDrop(event,'poss')"></div>
</div>
<!-- Buttons Poss ↔ Incl -->
<div class="ribbon-btns">
<button class="ribbon-btn success" onclick="catRibbonMove('poss','incl',false)" data-i18n-title="ribbonTitleIncl"></button>
<div class="ribbon-btn-sep"></div>
<button class="ribbon-btn" onclick="catRibbonMove('incl','poss',false)" data-i18n-title="ribbonTitleRemove"></button>
</div>
<!-- INCLUSION -->
<div class="ribbon-col">
<div class="ribbon-col-title incl" data-i18n="ribbonColInclCat"></div>
<div class="ribbon-list incl" id="catribbon-incl" ondragover="catRibbonDragOver(event)" ondrop="catRibbonDrop(event,'incl')"></div>
</div>
</div>
<div class="ribbon-slots">
<span id="catribbon-slots-txt">0 / 8 slots forcés</span>
<div class="ribbon-slots-bar">
<div class="ribbon-slots-fill" id="catribbon-slots-fill" style="width:0%"></div>
</div>
</div>
</div>
<!-- Presets accordion -->
<div class="presets-section">
<button class="presets-toggle" id="presets-toggle" onclick="togglePresets()">
<span><span data-i18n="presetHeader"></span></span>
<span class="pt-arrow"></span>
</button>
<div class="presets-body" id="presets-body">
<div class="presets-grid">
<button class="preset-btn" onclick="applyPreset('no-russia',this)">
<span class="p-icon">🚫🇷🇺</span>
<span class="p-name" data-i18n="presetNoRussia"></span>
</button>
<button class="preset-btn" onclick="applyPreset('europe',this)">
<span class="p-icon">🇪🇺</span>
<span class="p-name" data-i18n="presetEuropean"></span>
</button>
<button class="preset-btn" onclick="applyPreset('africa',this)">
<span class="p-icon">🌍</span>
<span class="p-name" data-i18n="presetAfrican"></span>
</button>
<button class="preset-btn" onclick="applyPreset('americas',this)">
<span class="p-icon">🌎</span>
<span class="p-name" data-i18n="presetAmericas"></span>
</button>
<button class="preset-btn" onclick="applyPreset('asia',this)">
<span class="p-icon">🌏</span>
<span class="p-name" data-i18n="presetAsian"></span>
</button>
<button class="preset-btn" onclick="applyPreset('oceania',this)">
<span class="p-icon">🏝️</span>
<span class="p-name" data-i18n="presetOceanian"></span>
</button>
<button class="preset-btn" onclick="applyPreset('top100pib',this)">
<span class="p-icon">💰</span>
<span class="p-name" data-i18n="presetTop100PIB"></span>
</button>
<button class="preset-btn" onclick="applyPreset('sport',this)">
<span class="p-icon">🏆</span>
<span class="p-name" data-i18n="presetSport"></span>
</button>
<button class="preset-btn" onclick="applyPreset('chill',this)">
<span class="p-icon">😌</span>
<span class="p-name" data-i18n="presetChill"></span>
</button>
<button class="preset-btn" onclick="applyPreset('ultra',this)">
<span class="p-icon">💀🔥</span>
<span class="p-name" data-i18n="presetUltraHC"></span>
</button>
<button class="preset-btn" onclick="applyPreset('small',this)">
<span class="p-icon">🔬</span>
<span class="p-name"> 1M hab.</span>
</button>
<button class="preset-btn" onclick="applyPreset('france-neighbors',this)">
<span class="p-icon">🇫🇷🤝</span>
<span class="p-name" data-i18n="presetNeighborsFR"></span>
</button>
<button class="preset-btn" onclick="applyPreset('landlocked',this)">
<span class="p-icon">🏔️</span>
<span class="p-name" data-i18n="presetLandlocked"></span>
</button>
<button class="preset-btn" onclick="applyPreset('islands',this)">
<span class="p-icon">🏝️</span>
<span class="p-name"> data-i18n="presetIslands"></span>
</button>
<button class="preset-btn" onclick="applyPreset('flag-guesser',this)">
<span class="p-icon">🚩</span>
<span class="p-name" data-i18n="presetFlagGuesser"></span>
</button>
</div>
</div>
</div>
<button class="btn-custom" style="background:rgba(167,139,250,0.1);" onclick="startGame('custom')" data-i18n="customStart"></button>
<button class="btn-secondary" style="margin-top:0;" onclick="closeCustomPanel()" data-i18n="back"></button>
</div>
</div>
<!-- GAME -->
<div id="panel-game" class="panel hidden">
<div class="score-strip">
<div class="score-val"><span data-i18n="score"></span>&nbsp;:&nbsp;<span id="total-score">0</span></div>
<div class="turn-label" id="turn-label"></div>
</div>
<div id="game-seed-display" style="font-family:'DM Mono',monospace;font-size:0.65em;color:var(--muted);margin-bottom:10px;word-break:break-all;"></div>
<div class="timer-row">
<span class="timer-label" data-i18n="time"></span>
<span class="timer-num" id="timer-num">20</span>
<div class="progress-track"><div class="progress-fill" id="progress-fill"></div></div>
</div>
<div class="feedback" id="feedback"></div>
<!-- REVERSE MODE: active category display -->
<div id="reverse-cat-display">
<div class="reverse-cat-icon" id="reverse-cat-icon">?</div>
<div class="reverse-cat-name" id="reverse-cat-name"></div>
<div class="reverse-cat-info-row">
<span class="reverse-info-toggle" onclick="toggleReverseCatInfo()" data-i18n="reverseInfoBtn"></span>
</div>
<div class="reverse-cat-tooltip" id="reverse-cat-tooltip"></div>
</div>
<!-- REVERSE MODE: country selection grid -->
<div id="countries-grid"></div>
<div class="country-block">
<div class="country-flag" id="country-flag">&#127757;</div>
<div class="country-info">
<div class="country-info-wrap">
<div class="country-name" id="country-name">---</div>
<button class="hint-btn hint-btn-1" id="hint-btn-1" onclick="revealHint1(event)">💡 <span data-i18n="hint1btn"></span></button>
<button class="hint-btn hint-btn-2" id="hint-btn-2" onclick="revealHint2(event)" style="display:none">🔍 <span data-i18n="hint2btn"></span></button>
</div>
<div class="turn-label" id="country-sub"></div>
</div>
</div>
<div id="hint-panel">
<span class="hint-label hint-label-1" id="hint-label-1"></span>
<div class="hint-line hint-line-1" id="hint-text-1"></div>
<div id="hint-block-2" style="display:none">
<span class="hint-label hint-label-2" id="hint-label-2"></span>
<div class="hint-line hint-line-2" id="hint-text-2"></div>
</div>
</div>
<div class="cats-grid" id="cats-grid"></div>
</div>
<!-- COUNTDOWN -->
<div id="countdown-overlay">
<div class="countdown-num" id="countdown-num">3</div>
<div class="countdown-sub" id="countdown-sub"></div>
</div>
<!-- REVEAL SCREEN -->
<div id="end-reveal-screen" style="display:none">
<div class="reveal-title-big" id="reveal-screen-title" data-i18n="revealTitle"></div>
<div class="reveal-rows" id="reveal-rows-container"></div>
<div class="reveal-running-score" id="reveal-running-score">0</div>
<div class="reveal-running-label" id="reveal-running-label" data-i18n="revealScoreLabel"></div>
</div>
<!-- END -->
<div id="panel-end" class="panel hidden">
<div class="logo" data-i18n="gameOver"></div>
<h1><span data-i18n="finalScore"></span><br><span class="highlight" data-i18n="final"></span></h1>
<div class="end-score" id="final-score">0</div>
<p class="end-tagline" id="end-tagline"></p>
<!-- Optimal score block -->
<div id="opt-block" style="margin:16px 0;background:var(--surface2);border:1px solid var(--border);border-radius:12px;padding:14px 16px;text-align:left;">
<div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px;">
<div>
<div style="font-family:'DM Mono',monospace;font-size:0.7em;color:var(--muted);letter-spacing:.08em;" data-i18n="optYours"></div>
<div style="font-size:1.6em;font-weight:800;color:var(--accent);" id="opt-your-score"></div>
</div>
<div style="font-size:1.4em;color:var(--muted);">vs</div>
<div style="text-align:right">
<div style="font-family:'DM Mono',monospace;font-size:0.7em;color:var(--muted);letter-spacing:.08em;" data-i18n="optBest"></div>
<div style="font-size:1.6em;font-weight:800;color:#4fc3f7;" id="opt-best-score"></div>
</div>
</div>
<div id="opt-diff-row" style="margin-top:8px;font-family:'DM Mono',monospace;font-size:0.78em;color:var(--muted);text-align:center;"></div>
<button id="opt-btn" class="btn-secondary" style="margin-top:12px;width:100%;font-size:0.82em;padding:10px;" onclick="showOptModal()" data-i18n="optBtn"></button>
</div>
<div class="seed-box" id="seed-display"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:16px;">
<button class="btn-secondary" style="margin:0;font-size:0.8em;" onclick="copySeed()" id="btn-copy-seed" data-i18n="copySeed"></button>
<button class="btn-secondary" style="margin:0;font-size:0.8em;border-color:rgba(79,255,176,0.4);color:var(--accent);" onclick="copyUrl()" id="btn-copy-url" data-i18n="copyUrl"></button>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;">
<button class="btn-secondary" style="margin:0;font-size:0.85em;" onclick="resetToMenu()" data-i18n="backToMenu"></button>
<button class="btn-primary" style="margin:0;font-size:0.85em;" onclick="replayGame()" data-i18n="playAgain"></button>
</div>
</div>
</div>
<!-- ── Daily leaderboard modal ─────────────────────────────────────────────── -->
<div id="daily-lb-overlay" onclick="if(event.target===this)closeLbModal()">
<div class="daily-modal-box">
<div class="daily-modal-title">🏆 <span data-i18n="dailyLbTitle"></span></div>
<div class="daily-modal-date" id="daily-lb-date"></div>
<div class="lb-header">
<span>#</span><span data-i18n="dailyLbColPlayer"></span><span style="text-align:right" data-i18n="dailyLbColScore"></span>
</div>
<div id="daily-lb-rows"><div class="lb-empty" data-i18n="dailyLbEmpty"></div></div>
<div class="lb-total" id="daily-lb-total"></div>
<button onclick="closeLbModal()" style="margin-top:16px;width:100%;padding:9px;background:var(--surface2);border:1px solid var(--border);border-radius:8px;color:var(--muted);font-family:'DM Mono',monospace;font-size:0.78em;cursor:pointer;" data-i18n="optClose"></button>
</div>
</div>
<!-- ── Daily submit score modal ─────────────────────────────────────────────── -->
<div id="daily-submit-overlay" onclick="if(event.target===this)skipDailySubmit()">
<div class="daily-modal-box">
<div class="daily-modal-title" style="margin-bottom:6px;">🏆 <span data-i18n="dailySubmitTitle"></span></div>
<div style="font-family:'DM Mono',monospace;font-size:0.73em;color:var(--muted);margin-bottom:14px;" data-i18n="dailySubmitDesc"></div>
<div style="font-family:'Syne',sans-serif;font-size:2.2em;font-weight:800;color:#f5c842;text-align:center;margin-bottom:16px;" id="daily-score-display"></div>
<input class="daily-pseudo-input" id="daily-pseudo-input" type="text" maxlength="20" data-i18n-placeholder="dailyPseudoPlaceholder"
onkeydown="if(event.key==='Enter')submitDailyScore()">
<button class="btn-daily-submit" onclick="submitDailyScore()" data-i18n="dailySubmitBtn"></button>
<div class="daily-submit-msg" id="daily-submit-msg"></div>
<button onclick="skipDailySubmit()" style="margin-top:8px;width:100%;padding:7px;background:none;border:none;color:var(--muted);font-family:'DM Mono',monospace;font-size:0.7em;cursor:pointer;text-decoration:underline;" data-i18n="dailySkip"></button>
</div>
</div>
<div id="country-tooltip"></div>
<!-- Optimal combo modal -->
<div id="opt-modal" style="display:none;position:fixed;inset:0;z-index:600;background:rgba(0,0,0,0.7);backdrop-filter:blur(4px);overflow-y:auto;padding:20px;">
<div style="max-width:480px;margin:auto;background:var(--surface);border:1px solid #4fc3f7;border-radius:16px;padding:24px;box-shadow:0 16px 48px rgba(0,0,0,0.6);">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;">
<div style="font-family:'Syne',sans-serif;font-size:1.1em;font-weight:800;color:#4fc3f7;" data-i18n="optTitle"></div>
<button onclick="hideOptModal()" style="background:none;border:none;color:var(--muted);font-size:1.4em;cursor:pointer;line-height:1;"></button>
</div>
<p id="opt-modal-explain" style="font-family:'DM Mono',monospace;font-size:0.75em;color:var(--muted);margin-bottom:16px;line-height:1.5;"></p>
<div id="opt-modal-rows"></div>
<div class="opt-footer">
<span class="opt-footer-label" data-i18n="optBest"></span>
<span class="opt-footer-opt" id="opt-modal-total"></span>
<span class="opt-footer-mine" id="opt-modal-mine"></span>
<span class="opt-footer-diff" id="opt-modal-diff-total"></span>
</div>
<button class="btn-secondary" style="margin-top:16px;width:100%;font-size:0.85em;" onclick="hideOptModal()" data-i18n="optClose"></button>
</div>
</div>
<div id="cat-tooltip">
<div class="tt-title" id="tt-title"></div>
<div class="tt-body" id="tt-body"></div>
</div>
<script src="config.js"></script>
<script src="js/translations.js"></script>
<script src="js/data.js"></script>
<script src="js/utils.js"></script>
<script src="js/ui-effects.js"></script>
<script src="js/hints.js"></script>
<script src="js/ribbon.js"></script>
<script src="js/seed.js"></script>
<script src="js/setup.js"></script>
<script src="js/mobile.js"></script>
<script src="js/game.js"></script>
<script src="js/game-end.js"></script>
<script src="js/daily.js"></script>
<script>
// ─── BOOT ─────────────────────────────────────────────────────────────────────
setLang(currentLang);
initApp();
</script>
</body>
</html>