599 lines
34 KiB
HTML
599 lines
34 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<script>
|
||
// Affiche le bouton uniquement sur iOS Safari hors standalone
|
||
(function() {
|
||
var isIOS = /iphone|ipad|ipod/i.test(navigator.userAgent);
|
||
var isSafari = /safari/i.test(navigator.userAgent) && !/crios|fxios/i.test(navigator.userAgent);
|
||
var isStandalone = navigator.standalone === true;
|
||
if (isIOS && isSafari && !isStandalone) {
|
||
var btn = document.getElementById('btn-ios-install');
|
||
if (btn) btn.style.display = 'inline-flex';
|
||
}
|
||
})();
|
||
|
||
function openIosInstallModal() {
|
||
var m = document.getElementById('ios-install-modal');
|
||
if (m) { m.style.display = 'flex'; applyTranslations(); }
|
||
}
|
||
function closeIosInstallModal() {
|
||
var m = document.getElementById('ios-install-modal');
|
||
if (m) m.style.display = 'none';
|
||
}
|
||
// Fermer en cliquant sur le fond
|
||
document.getElementById('ios-install-modal').addEventListener('click', function(e) {
|
||
if (e.target === this) closeIosInstallModal();
|
||
});
|
||
</script>
|
||
|
||
<html lang="fr">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<!-- 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 name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover, user-scalable=no, maximum-scale=1, minimum-scale=1">
|
||
<meta name="mobile-web-app-capable" content="yes">
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||
<meta name="apple-mobile-web-app-title" content="LeGrandGeoQuiz">
|
||
<meta name="theme-color" content="#0d0f14">
|
||
<meta name="description" content="Le Grand GeoQuiz - Test your geography knowledge">
|
||
<meta property="og:title" content="LeGrandGeoQuiz">
|
||
<meta property="og:description" content="Test your geography knowledge with LeGrandGeoQuiz">
|
||
<meta property="og:image" content="/favicon.svg">
|
||
<title>LeGrandGeoQuiz</title>
|
||
<link rel="icon" type="image/svg+xml" href="favicon.svg">
|
||
<link rel="manifest" href="manifest.json">
|
||
<link rel="apple-touch-icon" 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">
|
||
<script src="js/zoom-prevention.js"></script>
|
||
</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> <span data-i18n="loading"></span></div>
|
||
<div id="setup-ready" class="hidden">
|
||
|
||
<ul class="rules">
|
||
<li><span class="ico">→</span><span data-i18n="rule1"></span></li>
|
||
<li><span class="ico">→</span><span data-i18n="rule2"></span></li>
|
||
<li><span class="ico">→</span><span data-i18n="rule3"></span></li>
|
||
<li><span class="ico">→</span><span data-i18n="rule4"></span></li>
|
||
<li><span class="ico">→</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 class="daily-played-msg hidden" id="daily-offline-msg" data-i18n="dailyOffline"></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 style="margin-top:10px;text-align:center;">
|
||
<button id="btn-ios-install"
|
||
onclick="openIosInstallModal()"
|
||
style="display:none;background:none;border:1px solid rgba(245,200,66,0.35);border-radius:8px;padding:6px 14px;font-family:'DM Mono',monospace;font-size:0.68em;color:#f5c842;cursor:pointer;transition:all 0.15s;gap:6px;align-items:center;"
|
||
onmouseover="this.style.background='rgba(245,200,66,0.08)'"
|
||
onmouseout="this.style.background='none'">
|
||
📲 <span data-i18n="iosInstallBtn"></span>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Modal instructions iOS -->
|
||
<div id="ios-install-modal" style="display:none;position:fixed;inset:0;z-index:9000;background:rgba(0,0,0,0.7);backdrop-filter:blur(4px);align-items:flex-end;justify-content:center;">
|
||
<div style="width:100%;max-width:480px;background:var(--surface);border:1px solid rgba(245,200,66,0.4);border-radius:20px 20px 0 0;padding:24px 20px calc(env(safe-area-inset-bottom,0px) + 24px);position:relative;">
|
||
<button onclick="closeIosInstallModal()" style="position:absolute;top:14px;right:16px;background:none;border:none;color:var(--muted);font-size:1.3em;cursor:pointer;">✕</button>
|
||
<div style="font-family:'Syne',sans-serif;font-size:1em;font-weight:800;color:#f5c842;margin-bottom:16px;">📲 <span data-i18n="iosInstallTitle"></span></div>
|
||
|
||
<!-- Étape 1 -->
|
||
<div style="display:flex;align-items:flex-start;gap:12px;margin-bottom:14px;">
|
||
<div style="min-width:28px;height:28px;border-radius:50%;background:rgba(245,200,66,0.15);border:1px solid rgba(245,200,66,0.4);display:flex;align-items:center;justify-content:center;font-family:'DM Mono',monospace;font-size:0.75em;color:#f5c842;font-weight:700;">1</div>
|
||
<div style="font-family:'DM Mono',monospace;font-size:0.78em;color:var(--text);line-height:1.5;">
|
||
<span data-i18n="iosInstallStep1"></span><br>
|
||
<span style="font-size:1.8em;line-height:1.4;">⎋</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Étape 2 -->
|
||
<div style="display:flex;align-items:flex-start;gap:12px;margin-bottom:20px;">
|
||
<div style="min-width:28px;height:28px;border-radius:50%;background:rgba(245,200,66,0.15);border:1px solid rgba(245,200,66,0.4);display:flex;align-items:center;justify-content:center;font-family:'DM Mono',monospace;font-size:0.75em;color:#f5c842;font-weight:700;">2</div>
|
||
<div style="font-family:'DM Mono',monospace;font-size:0.78em;color:var(--text);line-height:1.5;">
|
||
<span data-i18n="iosInstallStep2"></span><br>
|
||
<span style="color:#f5c842;font-weight:700;" data-i18n="iosInstallAdd"></span>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="font-family:'DM Mono',monospace;font-size:0.68em;color:var(--muted);text-align:center;" data-i18n="iosInstallNote"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- CUSTOM SETTINGS -->
|
||
<div id="panel-custom" class="panel hidden">
|
||
<div class="logo">◈ 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" data-i18n="presetSmallPop"></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> : <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-sort-btn" id="reverse-sort-btn" data-catid="">⇅ <span class="rsq">?</span></span>
|
||
<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">🌍</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>
|
||
<script>
|
||
if ('serviceWorker' in navigator) {
|
||
window.addEventListener('load', function() {
|
||
navigator.serviceWorker.register('/legrandgeoquiz/sw.js').catch(function(err) {
|
||
console.warn('SW registration failed:', err);
|
||
});
|
||
});
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|