/*!
* Scooby site i18n — runtime translation + language selector.
*
* Drop this one tag into any page and it handles the rest:
*
*
* How it works: the page stays authored in English. Dictionaries map the
* English source string to a translation, so no per-element markup is needed
* and new pages are covered automatically once their strings are in the
* dictionary. See TRANSLATIONS.md.
*/
(function () {
'use strict';
if (window.__scoobyI18n) return; // already loaded on this page
var DEFAULT_LANG = 'en';
var STORAGE_KEY = 'scooby.lang';
// Which corner the floating selector pins to when there is no navbar to sit in.
var FLOAT_SIDE = 'right';
var LANGS = [
{ code: 'en', label: 'EN', native: 'English' },
{ code: 'es', label: 'ES', native: 'Español' },
{ code: 'pt', label: 'PT', native: 'Português' },
{ code: 'fr', label: 'FR', native: 'Français' },
{ code: 'de', label: 'DE', native: 'Deutsch' },
{ code: 'ru', label: 'RU', native: 'Русский' },
{ code: 'tr', label: 'TR', native: 'Türkçe' },
{ code: 'pl', label: 'PL', native: 'Polski' },
{ code: 'it', label: 'IT', native: 'Italiano' },
{ code: 'zh', label: 'ZH', native: '简体中文' },
{ code: 'ja', label: 'JA', native: '日本語' },
{ code: 'ko', label: 'KO', native: '한국어' },
{ code: 'th', label: 'TH', native: 'ไทย' },
{ code: 'vi', label: 'VI', native: 'Tiếng Việt' },
{ code: 'id', label: 'ID', native: 'Indonesia' },
{ code: 'hi', label: 'HI', native: 'हिन्दी' },
{ code: 'sr', label: 'SR', native: 'Српски' },
{ code: 'nl', label: 'NL', native: 'Nederlands' },
{ code: 'ka', label: 'KA', native: 'ქართული' },
// Right-to-left: these also flip and mirror the selector.
{ code: 'ar', label: 'AR', native: 'العربية', rtl: true },
{ code: 'he', label: 'HE', native: 'עברית', rtl: true }
];
var CODES = LANGS.map(function (l) { return l.code; });
// Resolve sibling dictionaries from this script's own URL so the code works
// at any directory depth, and over file:// as well as http://.
var DICTIONARY_QUERY = '';
var BASE = (function () {
var s = document.currentScript;
if (!s) {
var all = document.getElementsByTagName('script');
for (var i = all.length - 1; i >= 0; i--) {
if (/i18n\.js(\?|$)/.test(all[i].src)) { s = all[i]; break; }
}
}
var version = s && s.src && s.src.match(/[?&]v=([^]+)/);
if (version) DICTIONARY_QUERY = '?v=' + version[1];
return s && s.src ? s.src.replace(/[^/]*$/, '') : '/languages/';
})();
// ---------------------------------------------------------------- state ---
var dicts = { en: {} }; // lang -> { english: translated }
var current = DEFAULT_LANG;
var textOriginals = new WeakMap(); // text node -> original English
var textRendered = new WeakMap(); // distinguish app updates from our own translations
var attrOriginals = new WeakMap(); // element -> { attr: original }
var attrRendered = new WeakMap();
var templateCache = new WeakMap();
var titleOriginal = null;
var titleRendered = null;
var loadErrorNode = null;
var observer = null;
var applying = false;
// ------------------------------------------------------------ preference ---
function stored() {
try { return localStorage.getItem(STORAGE_KEY); } catch (e) { return null; }
}
function store(code) {
try { localStorage.setItem(STORAGE_KEY, code); } catch (e) { /* private mode */ }
}
function detect() {
var saved = stored();
if (saved && CODES.indexOf(saved) !== -1) return saved;
var navLangs = navigator.languages || [navigator.language || ''];
for (var i = 0; i < navLangs.length; i++) {
var base = String(navLangs[i]).toLowerCase().split('-')[0];
if (CODES.indexOf(base) !== -1) return base;
}
return DEFAULT_LANG;
}
// ------------------------------------------------- anti-flash of English ---
// Only hide content when we already know a non-English language is coming,
// so English visitors never pay for this.
var initial = detect();
function hideWhileLoading() {
if (initial === DEFAULT_LANG) return;
var st = document.createElement('style');
st.id = 'i18n-cloak';
st.textContent = 'html.i18n-loading body{visibility:hidden!important}';
(document.head || document.documentElement).appendChild(st);
document.documentElement.classList.add('i18n-loading');
// Never leave the page blank if a dictionary fails to arrive.
setTimeout(reveal, 1800);
}
function reveal() {
document.documentElement.classList.remove('i18n-loading');
}
// ------------------------------------------------------- dictionary load ---
var pending = {};
function loadDict(code, cb) {
if (code === DEFAULT_LANG || dicts[code]) return cb(null);
if (pending[code]) return pending[code].push(cb);
pending[code] = [cb];
var s = document.createElement('script');
s.src = BASE + code + '.js' + DICTIONARY_QUERY;
s.async = true;
var finished = false;
var timer = setTimeout(function () { finish(new Error('dictionary ' + code + ' timed out')); }, 12000);
function finish(err) {
if (finished) return;
finished = true;
clearTimeout(timer);
if (err && s.parentNode) s.parentNode.removeChild(s);
flush(code, err);
}
s.onload = function () { finish(dicts[code] ? null : new Error('dictionary ' + code + ' did not register')); };
s.onerror = function () { finish(new Error('dictionary ' + code + ' failed to load')); };
(document.head || document.documentElement).appendChild(s);
}
function flush(code, err) {
var q = pending[code] || [];
delete pending[code];
q.forEach(function (fn) { try { fn(err); } catch (e) {} });
}
// Dictionary files call this.
function register(code, table) {
dicts[code] = table || {};
if (pending[code]) flush(code, null);
}
// ------------------------------------------------------------ traversal ---
var SKIP_TAGS = { SCRIPT: 1, STYLE: 1, CODE: 1, PRE: 1, TEXTAREA: 1, NOSCRIPT: 1, SVG: 1, CANVAS: 1 };
var ATTRS = ['placeholder', 'title', 'alt', 'aria-label', 'aria-description', 'aria-valuetext', 'data-tooltip', 'value'];
function skipped(el) {
for (var n = el; n && n.nodeType === 1; n = n.parentNode) {
if (SKIP_TAGS[n.tagName]) return true;
if (n.hasAttribute && n.hasAttribute('data-i18n-skip')) return true;
if (n.getAttribute && n.getAttribute('translate') === 'no') return true;
if (n.classList && n.classList.contains('notranslate')) return true;
}
return false;
}
var norm = function (s) { return s.replace(/\s+/g, ' ').trim(); };
function templates(table) {
var cached = templateCache.get(table);
if (cached) return cached;
cached = [];
Object.keys(table).forEach(function (key) {
var names = [], pattern = '', cursor = 0, match;
var marker = /\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
while ((match = marker.exec(key))) {
pattern += key.slice(cursor, match.index).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
var name = match[1];
names.push(name);
if (/^(?:count|total|shown|bytes|size|number)$/.test(name)) pattern += '([0-9][0-9.,]*)';
else if (name === 'platform') pattern += '(PS4(?:\\s*\\/\\s*PS5)?|PS5|PS3|PS Vita|Xbox 360|Nintendo Switch|Steam Deck|PC)';
else if (name === 'filename') pattern += '([^\\r\\n<>]+\\.(?:pkg|fpkg|elf|bin|prx|sprx|self|zip|rar|7z|iso|json|vpk|pup|jar|csc|gsc|lua|txt|ini))';
else pattern += '([^\\r\\n]+?)';
cursor = marker.lastIndex;
}
if (!names.length || typeof table[key] !== 'string') return;
pattern += key.slice(cursor).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
cached.push({pattern: new RegExp('^' + pattern + '$'), names: names, value: table[key], specificity: key.replace(marker, '').length});
});
cached.sort(function (a, b) { return b.specificity - a.specificity; });
templateCache.set(table, cached);
return cached;
}
function localizedDate(value) {
if (current === DEFAULT_LANG || typeof Intl === 'undefined' || typeof Intl.DateTimeFormat !== 'function') return value;
var match = /^(0?[1-9]|[12][0-9]|3[01]) (January|February|March|April|May|June|July|August|September|October|November|December) ([12][0-9]{3})$/.exec(String(value));
if (!match) return value;
var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
var day = Number(match[1]), month = months.indexOf(match[2]), year = Number(match[3]);
var date = new Date(Date.UTC(year, month, day));
if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month || date.getUTCDate() !== day) return value;
try {
return new Intl.DateTimeFormat(current, {day: 'numeric', month: 'long', year: 'numeric', timeZone: 'UTC'}).format(date);
} catch (e) { return value; }
}
function interpolate(value, values, localizeDates) {
return value.replace(/\{([A-Za-z_][A-Za-z0-9_]*)\}/g, function (marker, name) {
if (!Object.prototype.hasOwnProperty.call(values, name)) return marker;
var result = String(values[name]);
return localizeDates && name === 'date' ? localizedDate(result) : result;
});
}
function lookup(source, table, values) {
var key = norm(source);
if (Object.prototype.hasOwnProperty.call(table, key) && typeof table[key] === 'string') {
return values ? interpolate(table[key], values, key.indexOf('{date}') !== -1) : table[key];
}
if (values) return interpolate(source, values);
var patterns = templates(table);
for (var i = 0; i < patterns.length; i++) {
var rule = patterns[i], match = rule.pattern.exec(key);
if (!match) continue;
var captures = {}, valid = true;
for (var j = 0; j < rule.names.length; j++) {
var name = rule.names[j], value = match[j + 1];
if (Object.prototype.hasOwnProperty.call(captures, name) && captures[name] !== value) { valid = false; break; }
captures[name] = value;
}
if (valid) return interpolate(rule.value, captures, rule.names.indexOf('date') !== -1);
}
return undefined;
}
function translateText(node, table) {
if (skipped(node.parentNode)) return;
var original = textOriginals.get(node);
var value = node.nodeValue;
if (original === undefined || value !== textRendered.get(node) && value !== original) {
original = node.nodeValue;
textOriginals.set(node, original);
if (!norm(original) || !/[A-Za-z]/.test(original)) { textRendered.set(node, original); return; }
}
var key = norm(original);
var hit = lookup(key, table);
if (hit === undefined) {
textRendered.set(node, original);
if (node.nodeValue !== original) node.nodeValue = original; // reverting
return;
}
// Keep the surrounding whitespace the layout may depend on.
var lead = (original.match(/^\s*/) || [''])[0];
var trail = (original.match(/\s*$/) || [''])[0];
var next = lead + hit + trail;
textRendered.set(node, next);
if (node.nodeValue !== next) node.nodeValue = next;
}
function translateAttrs(el, table, attrs) {
if (skipped(el)) return;
var saved = attrOriginals.get(el);
var rendered = attrRendered.get(el);
attrs = attrs || ATTRS;
for (var i = 0; i < attrs.length; i++) {
var a = attrs[i];
if (a === 'value' && (el.tagName !== 'INPUT' || !/^(?:button|submit|reset)$/i.test(el.getAttribute('type') || ''))) continue;
if (!el.hasAttribute(a)) continue;
if (!saved) { saved = {}; attrOriginals.set(el, saved); }
if (!rendered) { rendered = {}; attrRendered.set(el, rendered); }
var value = el.getAttribute(a);
if (saved[a] === undefined || value !== rendered[a] && value !== saved[a]) saved[a] = value;
var key = norm(saved[a]);
if (!key) continue;
var hit = lookup(key, table);
var next = hit === undefined ? saved[a] : hit;
rendered[a] = next;
if (value !== next) el.setAttribute(a, next);
}
}
function apply(root, table) {
// Text nodes.
var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false);
var batch = [], n;
while ((n = walker.nextNode())) batch.push(n);
for (var i = 0; i < batch.length; i++) translateText(batch[i], table);
// Attributes.
var sel = ATTRS.map(function (a) { return '[' + a + ']'; }).join(',');
var els = root.querySelectorAll ? root.querySelectorAll(sel) : [];
for (var j = 0; j < els.length; j++) translateAttrs(els[j], table);
if (root.nodeType === 1 && root.matches && root.matches(sel)) translateAttrs(root, table);
}
function applyMeta(table) {
if (titleOriginal === null || document.title !== titleRendered && document.title !== titleOriginal) titleOriginal = document.title;
var t = lookup(titleOriginal, table);
titleRendered = t === undefined ? titleOriginal : t;
if (document.title !== titleRendered) document.title = titleRendered;
var metas = document.querySelectorAll('meta[name="description"],meta[property="og:title"],meta[property="og:description"],meta[name="twitter:title"],meta[name="twitter:description"]');
for (var i = 0; i < metas.length; i++) translateAttrs(metas[i], table, ['content']);
}
function finishApplying() {
applying = false;
if (observer) observer.takeRecords();
}
function refresh(root) {
if(current===DEFAULT_LANG)return;
var table = dicts[current] || {};
applying = true;
try {
if (root && root.nodeType === 3) translateText(root, table);
else apply(root || document.body || document.documentElement, table);
applyMeta(table);
} finally { finishApplying(); }
}
// ------------------------------------------------------- dynamic content ---
function startObserver() {
if ((window.WWB_HOST_CONSOLE === true || window.WhatsiteConsoleLite && window.WhatsiteConsoleLite.enabled) && current === DEFAULT_LANG) {
if (observer) observer.disconnect();
observer = null;
return;
}
if (observer || !window.MutationObserver) return;
observer = new MutationObserver(function (records) {
if (applying || current === DEFAULT_LANG) return;
var table = dicts[current] || {};
applying = true;
try {
for (var i = 0; i < records.length; i++) {
var record = records[i];
if (record.type === 'characterData') {
if (record.target.parentNode && record.target.parentNode.tagName === 'TITLE') applyMeta(table);
else translateText(record.target, table);
} else if (record.type === 'attributes') {
if (record.target.tagName === 'META' && record.attributeName === 'content') applyMeta(table);
else if (record.attributeName === 'translate' || record.attributeName === 'data-i18n-skip') apply(record.target, table);
else translateAttrs(record.target, table);
} else {
if (record.target && record.target.tagName === 'TITLE') { applyMeta(table); continue; }
var added = record.addedNodes || [];
for (var j = 0; j < added.length; j++) {
var node = added[j];
if (node.nodeType === 3) translateText(node, table);
else if (node.nodeType === 1) {
apply(node, table);
if (node.tagName === 'META' || node.tagName === 'TITLE') applyMeta(table);
}
}
}
}
} finally { finishApplying(); }
});
observer.observe(document.documentElement, { childList: true, characterData: true, attributes: true, attributeFilter: ATTRS.concat(['content', 'translate', 'data-i18n-skip']), subtree: true });
}
// ------------------------------------------------------------- switching ---
var requestSeq = 0;
function setLang(code, opts) {
opts = opts || {};
if (CODES.indexOf(code) === -1) code = DEFAULT_LANG;
// Dictionaries load over the network, so a slow one must never overwrite a
// newer choice made while it was still in flight.
var mySeq = ++requestSeq;
loadDict(code, function (err) {
if (mySeq !== requestSeq) return; // superseded by a later switch
if (err) {
if (!loadErrorNode) {
loadErrorNode = document.createElement('div');
loadErrorNode.className = 'i18n-load-error';
loadErrorNode.setAttribute('role', 'alert');
loadErrorNode.setAttribute('data-i18n-skip', '');
(document.body || document.documentElement).appendChild(loadErrorNode);
}
var message = 'Unable to load this language. Please try again.';
loadErrorNode.textContent = lookup(message, dicts[current] || {}) || message;
syncUI();
reveal();
return;
}
if (loadErrorNode && loadErrorNode.parentNode) loadErrorNode.parentNode.removeChild(loadErrorNode);
loadErrorNode = null;
current = code;
var table = dicts[code] || {};
applying = true;
try {
apply(document.body || document.documentElement, table);
applyMeta(table);
} finally { finishApplying(); }
document.documentElement.setAttribute('lang', code);
// Right-to-left scripts need the whole document mirrored, not just translated.
var meta = LANGS.filter(function (l) { return l.code === code; })[0];
document.documentElement.setAttribute('dir', meta && meta.rtl ? 'rtl' : 'ltr');
if (!opts.silent) store(code);
syncUI();
reveal();
startObserver();
try {
window.dispatchEvent(new CustomEvent('scooby:langchange', { detail: { lang: code } }));
} catch (e) {}
});
}
// -------------------------------------------------------------- selector ---
var CSS = [
'.i18n-load-error{position:fixed;inset-inline-end:16px;bottom:20px;z-index:1300;max-width:calc(100vw - 32px);padding:12px 16px;border:1px solid rgba(255,255,255,.25);border-radius:10px;background:#17191e;color:#fff;font-size:.9rem}',
'.i18n-switcher{position:relative;display:inline-flex;flex-shrink:0;font-family:inherit;z-index:1200}',
'.i18n-switcher *{box-sizing:border-box}',
'.i18n-toggle{display:inline-flex;align-items:center;gap:7px;padding:7px 11px;border-radius:50px;',
'border:1px solid rgba(255,255,255,.15);background:rgba(255,255,255,.04);color:#e9e9f1;',
'font-family:inherit;font-size:.82rem;font-weight:600;letter-spacing:.02em;cursor:pointer;',
'line-height:1;transition:background .18s,border-color .18s,color .18s;white-space:nowrap}',
'.i18n-toggle:hover{background:rgba(255,255,255,.09);border-color:rgba(255,255,255,.28);color:#fff}',
'.i18n-toggle:focus-visible{outline:2px solid #34d399;outline-offset:2px}',
'.i18n-toggle svg{width:15px;height:15px;flex-shrink:0;stroke:currentColor;fill:none;stroke-width:2}',
'.i18n-toggle .i18n-caret{width:11px;height:11px;opacity:.65;transition:transform .2s}',
'.i18n-switcher.open .i18n-toggle .i18n-caret{transform:rotate(180deg)}',
'.i18n-switcher.open .i18n-toggle{background:rgba(255,255,255,.1);border-color:rgba(255,255,255,.3);color:#fff}',
/* Capped so a long language list scrolls instead of running off-screen. */
'.i18n-menu{position:absolute;top:calc(100% + 9px);left:0;min-width:186px;margin:0;padding:6px;',
'max-height:min(70vh,520px);overflow-y:auto;overscroll-behavior:contain;',
'list-style:none;background:#111114;border:1px solid rgba(255,255,255,.13);border-radius:13px;',
'box-shadow:0 18px 48px rgba(0,0,0,.62);opacity:0;visibility:hidden;transform:translateY(-6px);',
'transition:opacity .17s,transform .17s,visibility .17s}',
'.i18n-switcher.open .i18n-menu{opacity:1;visibility:visible;transform:none}',
'.i18n-menu li{margin:0;padding:0;list-style:none}',
'.i18n-option{display:flex;align-items:center;gap:10px;width:100%;padding:9px 11px;border:0;',
'border-radius:9px;background:transparent;color:#a9a9ba;font-family:inherit;font-size:.87rem;',
'font-weight:500;text-align:left;cursor:pointer;transition:background .15s,color .15s}',
'.i18n-option:hover{background:rgba(255,255,255,.06);color:#fff}',
'.i18n-option[aria-selected="true"]{color:#fff;background:rgba(16,185,129,.14)}',
'.i18n-badge{display:inline-flex;align-items:center;justify-content:center;min-width:30px;padding:3px 6px;',
'border-radius:5px;background:rgba(255,255,255,.08);font-size:.68rem;font-weight:700;letter-spacing:.05em;',
'color:#d5d5e2;flex-shrink:0}',
/* Drawn SVG flags - emoji flags do not render on Windows. */
'.i18n-flag{display:inline-block;width:20px;height:14px;border-radius:2.5px;overflow:hidden;',
'flex-shrink:0;line-height:0;box-shadow:0 0 0 1px rgba(255,255,255,.14) inset}',
'.i18n-flag svg{display:block;width:100%;height:100%}',
'.i18n-toggle .i18n-flag{width:19px;height:13px}',
'.i18n-option[aria-selected="true"] .i18n-badge{background:rgba(16,185,129,.28);color:#fff}',
'.i18n-check{margin-left:auto;width:14px;height:14px;stroke:#34d399;fill:none;stroke-width:2.5;opacity:0;flex-shrink:0}',
'.i18n-option[aria-selected="true"] .i18n-check{opacity:1}',
// Left group so the selector sits beside the brand without breaking space-between.
'.i18n-lead{display:flex;align-items:center;gap:14px;flex-shrink:0}',
'.wwb-host-home .topbar{flex-wrap:wrap}',
// Fallback for pages with no navigation bar.
'.i18n-floating{position:fixed;top:16px;left:16px;z-index:99999}',
'.i18n-floating .i18n-toggle{background:rgba(12,12,14,.9);border-color:rgba(255,255,255,.18);',
'backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}',
'.i18n-floating.i18n-float-right{left:auto;right:16px}',
'.i18n-float-right .i18n-menu{left:auto;right:0}',
'[dir="rtl"] .i18n-floating.i18n-float-right{right:auto;left:16px}',
'[dir="rtl"] .i18n-float-right .i18n-menu{right:auto;left:0}',
'@media (max-width:600px){.i18n-menu{min-width:170px}.i18n-toggle{padding:6px 9px;font-size:.78rem}}',
/* ---- right-to-left ---- */
/* The dropdown and the floating pill are anchored with `left`, which has to
flip so the menu stays inside the viewport when the page is mirrored. */
'[dir="rtl"] .i18n-menu{left:auto;right:0}',
'[dir="rtl"] .i18n-floating{left:auto;right:16px}',
'[dir="rtl"] .i18n-option{text-align:right}',
'[dir="rtl"] .i18n-check{margin-left:0;margin-right:auto}',
'[dir="rtl"] .i18n-suggested-tag{margin-left:0;margin-right:auto}',
/* Language names stay in their own script and direction. */
'.i18n-option,.i18n-pick{unicode-bidi:isolate}',
/* ---- first-visit language picker ---- */
'.i18n-modal-overlay{position:fixed;inset:0;z-index:100000;background:rgba(4,4,6,.84);',
'-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);display:flex;align-items:center;',
'justify-content:center;padding:20px;opacity:0;transition:opacity .25s;font-family:inherit}',
'.i18n-modal-overlay.show{opacity:1}',
'.i18n-modal{width:100%;max-width:520px;max-height:90vh;overflow-y:auto;background:#0f0f12;',
'border:1px solid rgba(255,255,255,.12);border-radius:18px;padding:26px;',
'box-shadow:0 30px 80px rgba(0,0,0,.7);transform:translateY(12px) scale(.985);transition:transform .25s}',
'.i18n-modal-overlay.show .i18n-modal{transform:none}',
'.i18n-modal-head{display:flex;align-items:center;gap:11px;margin-bottom:7px}',
'.i18n-modal-head svg{width:21px;height:21px;stroke:#34d399;fill:none;stroke-width:2;flex-shrink:0}',
'.i18n-modal h2{margin:0;font-size:1.15rem;font-weight:800;color:#fff;letter-spacing:-.01em}',
'.i18n-modal-sub{margin:0 0 18px;color:#9a9aaa;font-size:.88rem;line-height:1.5}',
'.i18n-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px}',
'.i18n-pick{display:flex;align-items:center;gap:10px;width:100%;padding:11px 12px;',
'border:1px solid rgba(255,255,255,.1);border-radius:11px;background:rgba(255,255,255,.03);',
'color:#d5d5e2;font-family:inherit;font-size:.9rem;font-weight:600;cursor:pointer;text-align:left;',
'transition:background .15s,border-color .15s,color .15s}',
'.i18n-pick:hover{background:rgba(16,185,129,.14);border-color:rgba(16,185,129,.5);color:#fff}',
'.i18n-pick:focus-visible{outline:2px solid #34d399;outline-offset:2px}',
'.i18n-pick.suggested{border-color:rgba(16,185,129,.55);background:rgba(16,185,129,.1);color:#fff}',
'.i18n-suggested-tag{margin-left:auto;font-size:.62rem;font-weight:700;letter-spacing:.06em;',
'color:#34d399;text-transform:uppercase}',
'.i18n-modal-foot{margin-top:17px;text-align:center}',
'.i18n-dismiss{background:none;border:0;color:#6a6a7c;font-family:inherit;font-size:.82rem;',
'cursor:pointer;text-decoration:underline;padding:6px}',
'.i18n-dismiss:hover{color:#9a9aaa}',
'@media (max-width:520px){.i18n-modal{padding:20px}.i18n-pick{padding:10px;font-size:.85rem;gap:8px}}',
/* ---- site-aligned language redesign ---- */
':root{--i18n-accent:var(--accent,#10b981);--i18n-accent-light:var(--accent-light,#6ee7b7);--i18n-bg:var(--bg-secondary,#131316);--i18n-surface:var(--bg-tertiary,#1a1a1e)}',
'.i18n-toggle{padding:8px 11px;border-radius:11px;border-color:rgba(16,185,129,.3);background:linear-gradient(180deg,rgba(255,255,255,.09),rgba(255,255,255,.035));color:var(--text-primary,#f4f4f5);box-shadow:0 8px 22px rgba(0,0,0,.18),inset 0 1px 0 rgba(255,255,255,.08);}',
'.i18n-toggle:hover,.i18n-switcher.open .i18n-toggle{background:linear-gradient(180deg,rgba(16,185,129,.2),rgba(16,185,129,.08));border-color:rgba(110,231,183,.68);color:#fff;box-shadow:0 10px 26px rgba(0,0,0,.25),0 0 0 3px rgba(16,185,129,.08)}',
'.i18n-toggle:focus-visible,.i18n-pick:focus-visible,.i18n-option:focus-visible{outline:2px solid var(--i18n-accent-light);outline-offset:3px}',
'.i18n-toggle .i18n-caret{color:var(--i18n-accent-light);opacity:.9}',
'.i18n-menu{width:242px;min-width:242px;margin:0;padding:9px;top:calc(100% + 10px);background:rgba(19,19,22,.97);border-color:rgba(16,185,129,.3);border-radius:15px;box-shadow:0 28px 64px -20px rgba(0,0,0,.9),0 0 0 1px rgba(16,185,129,.07);backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px);position:absolute}',
'.i18n-menu::before{content:"";position:absolute;top:0;left:18px;right:18px;height:2px;border-radius:999px;background:linear-gradient(90deg,transparent,var(--i18n-accent-light),transparent);opacity:.9}',
'.i18n-option{gap:10px;padding:10px 11px;border-radius:10px;color:var(--text-secondary,#a1a1aa);font-size:.84rem;transition:background .18s,color .18s,transform .18s}',
'.i18n-option:hover{background:rgba(16,185,129,.1);color:#fff;transform:translateX(2px)}',
'.i18n-option[aria-selected="true"]{background:linear-gradient(90deg,rgba(16,185,129,.2),rgba(16,185,129,.06));color:#fff;box-shadow:inset 2px 0 0 var(--i18n-accent)}',
'.i18n-badge{min-width:31px;padding:4px 6px;border:1px solid rgba(255,255,255,.08);border-radius:6px;background:rgba(255,255,255,.06);color:#d9fced;font-size:.64rem}',
'.i18n-option[aria-selected="true"] .i18n-badge{background:rgba(16,185,129,.22);border-color:rgba(110,231,183,.3);color:#fff}',
'.i18n-check{stroke:var(--i18n-accent-light)}',
'.i18n-modal-overlay{background:radial-gradient(circle at 50% 18%,rgba(16,185,129,.12),transparent 42%),rgba(4,7,9,.9);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);padding:18px}',
'.i18n-modal{position:relative;isolation:isolate;max-width:600px;max-height:88vh;padding:30px;background:linear-gradient(180deg,rgba(19,19,22,.99),rgba(10,10,12,.99));border:1px solid rgba(16,185,129,.28);border-radius:20px;box-shadow:0 34px 90px -22px rgba(0,0,0,.92),0 0 0 1px rgba(16,185,129,.06),0 0 48px rgba(16,185,129,.08);overflow-y:auto}',
'.i18n-modal::before{content:"";position:absolute;z-index:-1;top:0;left:34px;right:34px;height:3px;border-radius:0 0 999px 999px;background:linear-gradient(90deg,transparent,var(--i18n-accent),transparent);box-shadow:0 0 22px rgba(16,185,129,.38)}',
'.i18n-modal::after{content:"";position:absolute;z-index:-1;top:-110px;right:-100px;width:260px;height:260px;border-radius:50%;background:radial-gradient(circle,rgba(16,185,129,.12),transparent 68%);pointer-events:none}',
'.i18n-modal-head{position:relative;z-index:1;gap:13px;margin-bottom:10px}',
'.i18n-modal-head svg{width:38px;height:38px;padding:9px;border:1px solid rgba(110,231,183,.26);border-radius:12px;background:rgba(16,185,129,.12);stroke:var(--i18n-accent-light);stroke-width:1.8}',
'.i18n-modal h2{font-size:1.28rem;letter-spacing:-.025em}',
'.i18n-modal-sub{max-width:500px;margin:0 0 22px;color:var(--text-secondary,#a1a1aa);font-size:.9rem;line-height:1.6}',
'.i18n-grid{gap:10px}',
'.i18n-pick{min-height:48px;padding:12px 13px;border-color:rgba(255,255,255,.1);border-radius:12px;background:rgba(255,255,255,.035);color:#d9d9e2;font-size:.9rem;transition:background .18s,border-color .18s,color .18s,transform .18s,box-shadow .18s}',
'.i18n-pick:hover{background:rgba(16,185,129,.12);border-color:rgba(110,231,183,.54);color:#fff;transform:translateY(-1px);box-shadow:0 8px 20px rgba(0,0,0,.2)}',
'.i18n-pick.suggested{border-color:rgba(110,231,183,.62);background:linear-gradient(90deg,rgba(16,185,129,.19),rgba(16,185,129,.07));box-shadow:inset 3px 0 0 var(--i18n-accent),0 8px 22px rgba(0,0,0,.14);color:#fff}',
'.i18n-suggested-tag{padding:3px 7px;border:1px solid rgba(110,231,183,.25);border-radius:999px;background:rgba(16,185,129,.12);color:var(--i18n-accent-light);font-size:.58rem}',
'.i18n-modal-foot{margin-top:21px;padding-top:16px;border-top:1px solid rgba(255,255,255,.08)}',
'.i18n-dismiss{padding:8px 14px;border:1px solid rgba(255,255,255,.12);border-radius:999px;color:var(--text-muted,#8b8b93);text-decoration:none;background:rgba(255,255,255,.025);transition:background .18s,border-color .18s,color .18s}',
'.i18n-dismiss:hover{background:rgba(255,255,255,.07);border-color:rgba(255,255,255,.22);color:#fff}',
'.i18n-menu,.i18n-modal{scrollbar-width:thin;scrollbar-color:rgba(16,185,129,.45) transparent}',
'.i18n-menu::-webkit-scrollbar,.i18n-modal::-webkit-scrollbar{width:6px}',
'.i18n-menu::-webkit-scrollbar-thumb,.i18n-modal::-webkit-scrollbar-thumb{background:rgba(16,185,129,.42);border-radius:999px}',
'@media (max-width:520px){.i18n-menu{width:220px;min-width:220px}.i18n-modal{padding:24px 18px;border-radius:17px}.i18n-grid{gap:8px}.i18n-pick{min-height:44px;padding:10px;font-size:.82rem;gap:8px}.i18n-modal-sub{font-size:.85rem;margin-bottom:18px}}'
].join('');
var GLOBE = '';
// Inline SVG flags. Emoji flags are not rendered on Windows (they fall back to
// the two letters), so they are drawn instead of using the emoji codepoints.
var STAR = '0,-1 .225,-.309 .951,-.309 .363,.118 .588,.809 0,.382 -.588,.809 -.363,.118 -.951,-.309 -.225,-.309';
function star(x, y, r, rot) {
return '
Select the language you would like to browse the site in. ' + 'You can change it any time from Language in the sidebar, or the pill in the top-right corner.
'; var grid = '