// JustZix i18n — runtime słownik z dynamicznym przełącznikiem języka.
// Zasada: chrome.i18n używamy TYLKO w manifeście (__MSG_*__). Cała UI używa JZ_I18N.t().
// Powód: chrome.i18n ma sztywny język = Chrome UI lang, bez przełącznika bez restartu.
//
// Kontekst: ten plik ładuje się w 4 miejscach:
//  - options.html / popup.html (window)
//  - content_scripts (window strony, izolowany world)
//  - service worker / background.js (self) — przez importScripts()
// Eksport idzie więc przez globalThis żeby działało wszędzie.
(function () {
const _root = (typeof globalThis !== 'undefined') ? globalThis : (typeof window !== 'undefined') ? window : self;
_root.JZ_I18N = (() => {
  const SUPPORTED = ['pl', 'en', 'de', 'fr', 'it', 'es', 'ru', 'zh'];
  const DEFAULT = 'pl';

  const STRINGS = {
    pl: {
      // === Dashboard (strona główna panelu edycji, od v2.13.148) ===
      'dashboard.title': 'Pulpit',
      'dashboard.subtitle': 'Przegląd Twojej biblioteki JustZix i szybkie skróty.',
      'dashboard.stats.title': 'Biblioteka',
      'dashboard.stats.folders': 'Katalogi',
      'dashboard.stats.groups': 'Grupy',
      'dashboard.stats.rules': 'Zestawy',
      'dashboard.stats.actions': 'Akcje',
      'dashboard.stats.bars': 'Paski akcji',
      'dashboard.stats.windows': 'Okna na froncie',
      'dashboard.stats.withCss': 'Zestawy z CSS',
      'dashboard.stats.withJs': 'Zestawy z JS',
      'dashboard.stats.clicks': 'Kliknięć akcji',
      'dashboard.version.title': 'Wersja wtyczki',
      'dashboard.version.checking': 'Sprawdzanie…',
      'dashboard.version.current': 'Zainstalowana: :version',
      'dashboard.version.upToDate': 'Masz aktualną wersję',
      'dashboard.version.outdated': 'Dostępna nowsza wersja: :version',
      'dashboard.storage.title': 'Pamięć',
      'dashboard.backup.title': 'Ostatni backup',
      'dashboard.backup.signIn': 'Zaloguj się do synchronizacji, aby tworzyć backupy w chmurze.',
      'dashboard.backup.none': 'Brak backupów w chmurze.',
      'dashboard.backup.noLabel': '(bez etykiety)',
      'dashboard.backup.last': 'Ostatni backup: :date',
      'dashboard.recent.title': 'Ostatnio edytowane zestawy',
      'dashboard.recent.empty': 'Nie edytowano jeszcze żadnego zestawu.',
      'dashboard.examples.title': 'Ostatnie przykłady',
      'dashboard.posts.title': 'Z bloga',
      'dashboard.feed.loading': 'Wczytywanie…',
      'dashboard.feed.empty': 'Brak pozycji do wyświetlenia.',
      'dashboard.home.title': 'Pulpit',
      'dashboard.usage.title': 'Statystyki użycia',
      'dashboard.usage.cssApplied': 'Zastosowań CSS',
      'dashboard.usage.jsExecuted': 'Uruchomień JS',
      'dashboard.usage.windowsShown': 'Wyświetleń okien',
      'dashboard.usage.empty': 'Brak danych — używaj wtyczki na stronach.',
      'dashboard.tip.title': 'Wskazówka',
      'dashboard.tip.prev': 'Poprzednia wskazówka',
      'dashboard.tip.next': 'Następna wskazówka',
      'dashboard.tip.manual': 'Otwórz pełną instrukcję',
      'tip.dash.1': 'Prawy klik na pływającym przycisku otwiera panel z listą katalogów i zestawów.',
      'tip.dash.2': 'Skrót Ctrl+Alt+S włącza tryb edycji paska akcji — elementy można dowolnie rozmieszczać i skalować.',
      'tip.dash.3': 'Każdy zestaw może mieć własne wzorce URL, które zawężają go do konkretnych podstron.',
      'tip.dash.4': 'Skrót Ctrl+Alt+\\ otwiera AI Helper — asystenta generującego CSS/JS pod konkretne zadanie.',
      'tip.dash.5': 'Edytory kodu mają wyszukiwarkę — Ctrl+Alt+F przenosi do niej fokus.',
      'tip.dash.6': 'Lewy klik na pływającym przycisku włącza lub wyłącza wszystkie pasujące katalogi naraz.',
      'tip.dash.7': 'Historia zestawu trzyma 5 ostatnich wersji CSS i JS — zawsze możesz cofnąć zmianę.',
      'tip.dash.8': 'Przeciągnij zestaw na nazwę innej grupy lub katalogu, aby go tam przenieść.',
      'tip.dash.9': 'Okna TEMP (Ctrl+Alt+G/H/J/K) to tymczasowe pola CSS/JS i konsole do szybkich testów.',
      'tip.dash.10': 'Akcje SELECT, INPUT, SLIDER i TOGGLE3 zapamiętują swoją wartość — przeżywa odświeżenie strony.',
      'tip.dash.11': 'Synchronizacja chmurowa utrzymuje Twoją bibliotekę spójną między urządzeniami.',
      'tip.dash.12': 'Zamiast wysyłać plik JSON, udostępnij katalog linkiem (Shares) z czasem wygaśnięcia.',
      'tip.dash.13': 'Wyszukiwarka w sidebarze szybko odnajdzie katalog, grupę lub zestaw po nazwie.',
      'tip.dash.14': 'Dodawaj tagi do zestawów i filtruj bibliotekę po tagach, gdy projektów przybywa.',
      'tip.dash.15': 'Trzyliterowa etykieta i kolor katalogu wyróżniają jego pływający przycisk na stronie.',
      'tip.dash.16': 'Przycisk „URL” wstawia adres aktywnej karty prosto do wzorca URL — bez kopiowania.',
      'tip.dash.17': 'Wtyczka na bieżąco sprawdza poprawność wpisywanych wzorców URL.',
      'tip.dash.18': 'Edytor ostrzega o błędach składni CSS, zanim zapiszesz zestaw.',
      'tip.dash.19': 'Przy imporcie podejrzysz kod JavaScript, zanim zdecydujesz się go dodać.',
      'tip.dash.20': 'Wyłączenie katalogu kaskadowo wyłącza wszystkie jego grupy i zestawy.',
      'tip.dash.21': 'Filtr „Tylko aktywne” ukrywa wyłączone elementy, gdy biblioteka się rozrasta.',
      'tip.dash.22': 'Przeciągnij pasek akcji obok innego, aby je skleić w jeden spójny zestaw.',
      'tip.dash.23': 'Okno konsoli JS działa jak REPL — testuj fragmenty kodu na żywo na stronie.',
      'tip.dash.24': 'Okno Output Console przechwytuje wpisy console.log ze strony i pokazuje je na froncie.',
      'tip.dash.25': 'Możesz wyeksportować pojedynczy katalog, grupę lub zestaw — nie tylko całą bibliotekę.',
      'tip.dash.26': 'JustZix dokłada własne pozycje do menu kontekstowego przeglądarki (prawy klik na stronie).',
      'tip.dash.27': 'Granicę między panelami edytora kodu przesuniesz myszką, dopasowując widok do siebie.',
      'tip.dash.28': 'Wtyczka sama powiadomi Cię, gdy dostępna będzie nowsza wersja.',
      'tip.dash.29': 'Klik w ikonę rozszerzenia otwiera widget z szybkim włączaniem katalogów i pasków.',
      'tip.dash.30': 'Kod JavaScript zestawu może wywoływać akcje pasków przez wbudowane API JustZix.',
      'tip.dash.31': 'Ctrl+Enter wykonuje kod lub wysyła wiadomość — w oknie JS, konsoli JS i AI Helperze.',
      'tip.dash.32': 'W konsoli JS strzałki ↑ / ↓ przewijają historię wpisanych komend.',
      'tip.dash.33': 'Ctrl+L czyści output konsoli JS bez zamykania okna.',
      'tip.dash.34': 'Ctrl+Shift+L czyści aktywną zakładkę każdej otwartej konsoli Output.',
      'tip.dash.35': 'Ctrl+Shift+H otwiera okno pomocy API w konsoli Output.',
      'tip.dash.36': 'W edytorze kodu Ctrl+Alt+→ i Ctrl+Alt+← skaczą do następnego i poprzedniego wyniku wyszukiwania.',
      'tip.dash.37': 'Klawisz Esc czyści pole wyszukiwania w konsoli Output.',
      'tip.dash.38': 'Podwójny klik w belkę okna TEMP przełącza je między rozmiarem domyślnym a ostatnio ustawionym.',
      'tip.dash.39': 'Okno TEMP zapamiętuje ostatni rozmiar osobno dla każdego typu i pamięta go po restarcie przeglądarki.',
      'tip.dash.40': 'Przewijanie kółkiem wewnątrz okna na froncie nie przewija strony pod spodem.',
      'tip.dash.41': 'Każde okno na froncie ma 8 uchwytów zmiany rozmiaru — 4 w narożnikach i 4 na krawędziach.',
      'tip.dash.42': 'Prawy klik na nagłówku okna otwiera menu z opcjami zamknięcia, rozłączenia, fontu i rozmiaru.',
      'tip.dash.43': 'Przeciągnij okno blisko innego — przyciągną się magnetycznie i utworzą wspólną grupę.',
      'tip.dash.44': 'Konsola Output ma 6 zakładek: Wszystko, JZ, Console, Network, Errors i DataLayer.',
      'tip.dash.45': 'W zakładce Network konsoli Output Alt+klik na wierszu dodaje jego domenę do filtra „Domeny+".',
      'tip.dash.46': 'Alt+Shift+klik na wierszu Network dodaje domenę do filtra wykluczeń „Domeny-".',
      'tip.dash.47': 'Każda zakładka konsoli Output ma własny, niezależny zestaw filtrów.',
      'tip.dash.48': 'W zakładce DataLayer przeciągnij belkę między kolumnami, aby zmienić ich szerokość.',
      'tip.dash.49': 'W trybie edycji paska przycisk „+ Etykieta" dodaje statyczne podpisy tekstowe.',
      'tip.dash.50': 'Dwuklik na etykiecie paska otwiera edytor jej tekstu, fontu, rozmiaru i koloru.',
      'tip.dash.51': 'W trybie edycji paska Shift+przeciągnięcie po pustym polu zaznacza elementy prostokątem.',
      'tip.dash.52': 'W trybie edycji paska Ctrl+klik dodaje lub usuwa element z zaznaczenia.',
      'tip.dash.53': 'Klik w numer (badge) elementu akcji kopiuje jego stałe ID do schowka.',
      'tip.dash.54': 'Zestaw może mieć włączone ponowne wykonanie JS przy zmianach UI na stronach typu SPA.',
      'tip.dash.55': 'Poza BUTTON, SELECT i INPUT akcje mają też typy SLIDER, TEXTAREA i TOGGLE3.',
      'tip.dash.56': 'Motyw jasny/ciemny i język interfejsu synchronizują się jako wspólne ustawienie konta.',
      'tip.dash.57': 'Statystyki użycia na Pulpicie sumują dane ze wszystkich sparowanych urządzeń.',
      'tip.dash.58': 'Belkę pod edytorem kodu można pociągnąć w dół, aby powiększyć obszar pracy.',
      'tip.dash.59': 'Pionowy pasek między sidebarem a edytorem zwija lewy panel dla większej przestrzeni.',
      'tip.dash.60': 'Ikona domu w nagłówku Pulpitu i w okruchach nawigacji wraca prosto do Pulpitu.',
      // === Popup ===
      'popup.toggle.on.label':       'Wtyczka włączona',
      'popup.toggle.on.status':      'Wstrzykiwanie aktywne',
      'popup.toggle.off.label':      'Wtyczka wyłączona',
      'popup.toggle.off.status':     'Żaden CSS/JS nie jest wstrzykiwany',
      'popup.editMode.label':        'Tryb edycji paska akcji',
      'popup.editMode.on':           'Włączony — klik aby wyłączyć (Ctrl+Alt+S)',
      'popup.editMode.off':          'Wyłączony — klik aby włączyć (Ctrl+Alt+S)',
      'popup.editMode.unavailable':  'Niedostępny na tej stronie',
      'popup.empty.title':           'Brak katalogów zestawów dla tej strony.',
      'popup.empty.hint':            'Dodaj je w trybie edycji.',
      'popup.empty.folderRules':     'Brak aktywnych zestawów w tym katalogu',
      'popup.fallback.groupName':    'Grupa',
      'popup.fallback.unnamed':      '(bez nazwy)',
      'popup.restore':               'Przywróć pływający przycisk (:count)',
      'popup.editBtn':               'Panel edycji',
      'popup.actionBarsTitle':       'Paski akcji',
      'popup.fallback.barName':      'Pasek',
      'popup.hideForPage':           'Ukryj pływający przycisk dla tej strony',
      'popup.showForPage':           'Pokaż pływający przycisk dla tej strony',

      // === Options — strona ===
      'options.title':                          'JustZix — Edycja',

      // === Options — sidebar ===
      'sidebar.folders.title':                  'Katalogi',
      'sidebar.addFolder.label':                'Dodaj katalog',
      'sidebar.search.placeholder':             'Szukaj (nazwa, tag, treść kodu)',
      'sidebar.clearSearch.label':              'Wyczyść',
      'sidebar.activeOnly.label':               'Tylko aktywne',
      'sidebar.activeOnly.title':               'Ukryj zestawy niezależne (oznaczone ○) — pokaż tylko zestawy aktywne w katalogu (●)',
      'sidebar.expandAll.label':                'Rozwiń całe drzewo',
      'sidebar.collapseAll.label':              'Zwiń całe drzewo',
      'sidebar.toggle.title':                   'Zwiń/rozwiń panel katalogów',

      // === Options — settings menu ===
      'settings.menu.label':                    'Ustawienia',
      'settings.theme.label':                   'Motyw',
      'settings.theme.title':                   'Wybierz motyw: jasny / ciemny / auto',
      'settings.help.label':                    'Instrukcja obsługi',
      'settings.tutorial.label':                'Samouczek',
      'settings.export.label':                  'Eksport ustawień',
      'settings.import.label':                  'Import ustawień',
      'settings.importUrl.label':               'Importuj z URL',
      'settings.importUrl.title':               'Zaimportuj eksport z linka JustZix (JZS-XXXX-XXXX-XXXX-XXXX)',
      'settings.sync.label':                    'Synchronizacja chmurowa',
      'settings.shares.label':                  'Moje share',
      'settings.shares.title':                  'Lista wystawionych czasowych linków publicznych do eksportów',
      'settings.projectSite.label':             'Strona projektu',
      'settings.projectSite.title':             'Otwórz justzix.com w nowej karcie',
      'settings.syncStatusDot.title':           'Synchronizacja chmurowa — niezalogowany',
      'settings.lang.label':                    'Język',
      'settings.lang.title':                    'Język interfejsu wtyczki',
      'settings.lang.auto':                     'Auto',
      'settings.lang.autoTitle':                'Wykryj z przeglądarki (domyślny)',
      'toast.langChanged':                      'Język ustawiony: :lang',
      'lang.name.auto':                         'Auto',
      'lang.name.pl':                           'Polski',
      'lang.name.en':                           'Angielski',
      'settings.version.label':                 'Wersja',
      'settings.version.checkTitle':            'Kliknij, aby sprawdzić najnowszą wersję (force-refresh, pomija cache 30 min)',
      'settings.version.checking':              'Sprawdzam…',
      'settings.update.available':              'Dostępna nowa wersja: v:version ⬇',
      'settings.update.notReady':               '(paczka jeszcze nie wystawiona)',

      // === Options — folder editor ===
      'folder.icon.title':                      'Etykieta na pływającym przycisku (max 3 litery, UPPERCASE). Domyślnie: ZIX',
      'folder.color.title':                     'Kolor katalogu (opcjonalnie)',
      'folder.colorClear.title':                'Usuń kolor',
      'folder.name.placeholder':                'Nazwa katalogu (np. justzix.com)',
      'folder.url.placeholder':                 'Wzorzec URL (np. https://justzix.com/* )',
      'folder.urls.label':                      'Wzorce URL katalogu:',
      'folder.urls.title':                      'Wzorce URL katalogu — co najmniej jeden musi pasować, żeby zestawy w tym katalogu się uruchomiły',
      'folder.urls.placeholder':                'https://justzix.com/*  (Enter = dodaj)',
      'folder.insertUrl.title':                 'Wstaw URL aktywnej karty',
      'folder.active.label':                    'Aktywny',
      'folder.active.title':                    'Aktywny — gdy odznaczysz, katalog (i wszystkie jego grupy/zestawy) zostają wymuszono wyłączone i edycja włącznika jest zablokowana',
      'folder.enabled.label':                   'Włączony',
      'folder.enabled.title':                   'Włączony — bieżący stan katalogu (kaskaduje do aktywnych grup i zestawów)',
      'folder.actionsPanel.label':              'Panel akcji',
      'folder.actionsPanel.title':              'Pokaż pływający panel z przyciskami akcji dla tego katalogu',
      'folder.layout.label':                    'Układ:',
      'folder.layout.title':                    'Układ przycisków w panelu akcji',
      'folder.layout.vertical':                 'Pionowy',
      'folder.layout.horizontal':               'Poziomy',
      'folder.addGroup.label':                  '+ Grupa',
      'folder.addGroup.title':                  'Dodaj nową grupę w tym katalogu',
      'folder.export.label':                    'Eksport',
      'folder.export.title':                    'Eksportuj ten katalog',
      'folder.import.label':                    'Import',
      'folder.import.title':                    'Importuj katalog z pliku',
      'folder.delete.label':                    'Usuń katalog',

      // === Options — group editor ===
      'group.parentLabel':                      'Grupa w katalogu:',
      'folder.label':                           'Katalog:',
      'group.label':                            'Grupa:',
      'group.name.placeholder':                 'Nazwa grupy',
      'group.urls.label':                       'Wzorce URL grupy:',
      'group.urls.title':                       'Dodatkowe wzorce URL grupy — dodatkowe ograniczenie nakładające się na wzorzec katalogu, działa dla wszystkich zestawów w grupie',
      'group.urls.placeholder':                 'https://example.com/admin/*  (Tab = autouzupełnienie z katalogu, Enter = dodaj)',
      'group.active.label':                     'Aktywna',
      'group.active.title':                     'Aktywna — gdy odznaczysz, grupa (i wszystkie jej zestawy) zostają wymuszono wyłączone i edycja włącznika jest zablokowana',
      'group.enabled.label':                    'Włączona',
      'group.enabled.title':                    'Włączona — bieżący stan grupy (kaskaduje do aktywnych zestawów)',
      'group.addRule.label':                    '+ Dodaj zestaw',
      'group.export.title':                     'Eksportuj tę grupę',
      'group.import.title':                     'Importuj zestawy do tej grupy',
      'group.move.label':                       'Przenieś',
      'group.move.title':                       'Przenieś grupę do innego katalogu',
      'group.duplicate.label':                  'Duplikuj',
      'group.duplicate.title':                  'Duplikuj grupę z zestawami',
      'group.delete.label':                     'Usuń grupę',

      // === Options — rule editor ===
      'rule.label':                             'Zestaw:',
      'rule.back.label':                        'Wróć do grupy',
      'rule.export.title':                      'Eksportuj ten zestaw do pliku',
      'rule.share.label':                       'Udostępnij URL',
      'rule.share.title':                       'Udostępnij ten zestaw jako czasowy link publiczny',
      'rule.import.title':                      'Importuj zestaw (zastąpi obecny)',

      // === Options — empty state ===
      'empty.title':                            'Wybierz lub dodaj katalog',
      'empty.body':                             'Każdy katalog grupuje zestawy CSS/JS dla jednego wzorca URL.',

      // === Domyślne nazwy nowych encji (tworzone w options.js) ===
      'default.groupName':                      'Domyślna',
      'default.folderName':                     'Nowy katalog',
      'default.ruleName':                       'Nowy zestaw',

      // === Plural — formy rzeczowników do interpolacji w toastach ===
      // PL ma 4 formy (one/few/many/other), używamy ich za pośrednictwem Intl.PluralRules.
      'plural.rule.one':                        'zestaw',
      'plural.rule.few':                        'zestawy',
      'plural.rule.many':                       'zestawów',
      'plural.rule.other':                      'zestawów',
      'plural.folder.one':                      'katalog',
      'plural.folder.few':                      'katalogi',
      'plural.folder.many':                     'katalogów',
      'plural.folder.other':                    'katalogów',
      'plural.group.one':                       'grupa',
      'plural.group.few':                       'grupy',
      'plural.group.many':                      'grup',
      'plural.group.other':                     'grup',
      'plural.action.one':                      'akcja',
      'plural.action.few':                      'akcje',
      'plural.action.many':                     'akcji',
      'plural.action.other':                    'akcji',
      'plural.jsLine.one':                      'linia JS',
      'plural.jsLine.few':                      'linie JS',
      'plural.jsLine.many':                     'linii JS',
      'plural.jsLine.other':                    'linii JS',
      'toast.importedRulesToGroup':             'Zaimportowano :count :word do grupy',
      'toast.exportedRules':                    'Wyeksportowano :count :word',

      // === Sidebar — render dynamiczny ===
      'sidebar.empty.folders':                  'Brak katalogów',
      'sidebar.empty.activeFolders':            'Brak aktywnych katalogów',
      'sidebar.folder.toggleHint':              'Włącz/wyłącz cały katalog',
      'sidebar.folder.inactiveHint':            'Katalog nieaktywny — odblokuj w edytorze katalogu',
      'sidebar.group.toggleHint':               'Włącz/wyłącz grupę',
      'sidebar.group.inactiveHint':             'Grupa nieaktywna — odblokuj w edytorze grupy',
      'sidebar.rule.urlHint':                   'Ograniczenie URL na poziomie zestawu',
      'sidebar.rule.activeInFolder':            'Aktywny w katalogu',
      'sidebar.rule.inactiveInFolder':          'Nieaktywny w katalogu',
      'sidebar.fallback.folderName':            '(katalog)',
      'sidebar.fallback.groupName':             '(grupa)',
      'sidebar.fallback.unnamed':               '(bez nazwy)',
      'sidebar.flag.css':                       'Zawiera CSS',
      'sidebar.flag.js':                        'Zawiera JavaScript',
      'sidebar.flag.actions':                   'Zawiera akcje',
      'sidebar.flag.groupUrls':                 'Grupa ma ograniczenie URL',

      // === Folder/Group/Rule editor — dynamiczne ===
      'rules.empty.folder':                     'Brak zestawów w tym katalogu. Dodaj grupę i utwórz w niej pierwszy zestaw, lub kliknij grupę w sidebarze.',
      'rules.empty.group':                      'Brak zestawów w tej grupie. Dodaj pierwszy klikając "+ Dodaj zestaw".',
      'rule.namePlaceholder':                   'Nazwa zestawu',
      'rule.activeTooltip':                     'Aktywny - czy uwzględniany przy włączaniu/wyłączaniu całego katalogu',
      'rule.urls.tooltip':                      'Dodatkowe wzorce URL — gdy ustawione, zestaw uruchamia się tylko na podstronach pasujących do co najmniej jednego wzorca (oprócz wzorca katalogu)',
      'rule.urls.placeholder.empty':            'https://example.com/admin/*  (Tab — wstaw wzorzec katalogu, Enter — dodaj)',

      // === Move menu (przenieś grupę / zestaw) ===
      'moveMenu.section.moveGroup':             'Przenieś grupę do',
      'moveMenu.section.moveRule':              'Przenieś do grupy',
      'moveMenu.section.copyRule':              'Kopiuj do grupy',
      'moveMenu.empty.folders':                 'Brak innych katalogów',
      'moveMenu.empty.groups':                  'Brak innych grup',

      // === Confirm dialogs — dynamiczne ===
      'confirm.deleteFolderWithStats':          'Usunąć katalog ":name" wraz z :groups i :rules?',
      'confirm.deleteFolder':                   'Usunąć katalog ":name"?',
      'confirm.deleteGroupWithRules':           'Usunąć grupę ":name" wraz z :count :word?',
      'confirm.deleteGroup':                    'Usunąć grupę ":name"?',
      'confirm.replacePartial':                 'Zastąpić obecne :before częściowym backupem (:after)?\n\nWszystkie dane spoza pliku zostaną utracone. Operacja nieodwracalna.',
      'confirm.overwriteAll':                   'Nadpisać :stats obecnymi danymi z pliku?\n\nTa operacja jest nieodwracalna.',

      // === Default folder name in autoCreated group / "Nowa grupa" ===
      'default.newGroupName':                   'Nowa grupa',

      // === Eksport — summary + statusy ===
      'export.summary.full':                    ':icon Pełen backup: :stats + globalne ustawienia',
      'export.summary.partial':                 ':icon Częściowy backup: :stats',
      'export.empty.folder':                    'Brak katalogów do eksportu',
      'export.empty.group':                     'Grupa jest pusta — brak zestawów do eksportu',
      'export.summary.toExport':                ':icon Do eksportu: :count :word',
      'export.summary.noneSelected':            ':icon Brak zaznaczonych zestawów',

      // === Import detection (banery) ===
      'import.detection.invalid':               '<strong style="color:#dc2626;">:icon Nieprawidłowy format:</strong> brak listy katalogów (<code>folders</code>).',
      'import.detection.partialContains':       'Zawiera: :stats',
      'import.detection.willReplace':           ':stats',

      // === Inline labels ===
      'inline.fileGroupLabel':                  'grupa',
      'shares.empty':                           'Nie masz aktywnych linków.<br><br>Aby utworzyć — otwórz eksport (cały / katalog / grupa / zestaw)<br>i kliknij <strong>„Udostępnij URL"</strong>.',

      // === Share preview / typy eksportu ===
      'shareType.folder':                       'Pojedynczy katalog',
      'shareType.group':                        'Pojedyncza grupa',
      'shareType.rule':                         'Pojedynczy zestaw',
      'shareType.full':                         'Pełen eksport',

      // === Owner suffix przy importowaniu z share ===
      'shareImport.suffix.group':               'grupa',
      'shareImport.suffix.rule':                'zestaw',

      // === Rule import — prompt z opcjami ===
      'prompt.ruleConflict':                    'Importujesz ":new". Co zrobić z obecnym zestawem ":current"?\n\n  1 lub "zastąp"  — Zastąp obecny zestaw\n  2 lub "kopia"   — Dodaj jako nowy zestaw obok obecnego (w tej samej grupie)\n\nWpisz 1 lub 2:',
      'prompt.folderConflicts':                 'Wykryto :count :word o tym samym ID jak istniejące:\n:names\n\nWpisz jedno z:\n  1 lub "nadpisz"  — Nadpisz istniejące katalogi (usunie ich obecne grupy i zestawy)\n  2 lub "kopia"    — Zaimportuj jako kopie (nowe ID, "(import)" w nazwie folderu)\n  Anuluj           — Przerwij import\n\nWybór:',

      // === Group editor — dodatkowe ===
      'group.empty.noGroupsInFolder':           'Brak grup w katalogu',

      // === Folder import — conflict messages ===
      'folderImport.conflict.sameId':           'Importowany katalog ma takie samo ID jak istniejący "<strong>:name</strong>" (:count :word). Wybierz akcję:',
      'folderImport.conflict.sameName':         'Istnieje już katalog o nazwie "<strong>:name</strong>" (:count :word). Wybierz akcję:',

      // === Stany dynamiczne przycisków ===
      'btn.shortcutPress':                      '⌨ naciśnij Ctrl+Alt+Shift + znak (Esc)',
      'btn.generating':                         'Generuję…',
      'btn.importing':                          'Importuję…',
      'btn.showKey':                            'Pokaż klucz',
      'btn.hideKey':                            'Ukryj klucz',

      // === Rule editor — placeholdery + tooltipy ===
      'rule.editor.urlsPlaceholder':            'https://example.com/admin/*  (Tab — wstaw wzorzec katalogu, Enter — dodaj)',
      'rule.editor.urlsPlaceholderWithPattern': ':pattern  (Tab — wstaw, Enter — dodaj)',
      'rule.editor.toggleHint':                 'Włącz/wyłącz ten zestaw',
      'rule.editor.toggleLockedHint':           'Zestaw nieaktywny — odblokuj zaznaczając Aktywny',
      'sidebar.fallback.ruleName':              '(zestaw)',
      'sidebar.fallback.ruleUnnamed':           '(zestaw bez nazwy)',
      'confirm.deleteRule':                     'Usunąć zestaw ":name"?',

      // === Rule card (renderowane przez createRuleCard / renderActionsList) ===
      'rule.card.tagsTitle':                    'Tagi (oddzielone przecinkami)',
      'rule.card.activeLabel':                  'Aktywny',
      'rule.card.enabledLabel':                 'Włączony',
      'rule.card.history.title':                'Historia zmian',
      'rule.card.history.label':                'Historia',
      'rule.card.move.label':                   'Przenieś',
      'rule.card.move.btnTitle':                'Przenieś zestaw do innej grupy',
      'rule.card.copy.label':                   'Skopiuj',
      'rule.card.copy.btnTitle':                'Skopiuj zestaw do innej grupy',
      'rule.card.duplicate.label':              'Duplikuj',
      'rule.card.delete.label':                 'Usuń',
      'rule.card.urlsLabel':                    'Wzorce URL zestawu:',
      'rule.card.tab.actions':                  'Akcje',
      'rule.card.lineCount':                    'Liczba linii kodu',
      'rule.card.js.runOnce.label':             'Wykonaj JS tylko raz',
      'rule.card.js.runOnce.title':             'Nie uruchamiaj JS ponownie po nawigacji SPA (zmiana adresu bez przeładowania strony). Zalecane dla skryptów dodających globalne listenery, interwały lub nadpisujących window.',
      'rule.card.actions.info':                 'Przyciski akcji pojawią się w pływającym panelu (przycisk "Panel akcji" w katalogu). Max 4-znakowy skrót, własny kolor, kod JS wykonywany po kliknięciu.',
      'rule.card.actions.add':                  '+ Dodaj przycisk akcji',
      'rule.card.actions.addWithCount':         '+ Dodaj przycisk akcji (:count)',
      'rule.card.action.visibleLabel':          'widoczny',
      'rule.card.duplicate.suffix':             '(kopia)',
      'rule.card.action.codePreview':           '(:count :word) :first',
      'rule.card.action.fallback.unlabeled':    '(bez etykiety)',
      'rule.card.fallbackName':                 'Zestaw',

      // === Folder export modal — dodatkowe ===
      'export.empty.foldersToExport':           'Brak katalogów do eksportu',
      'export.summary.toExportFolders':         ':icon Do eksportu: :groupCount :groupWord, :ruleCount :ruleWord',
      'export.summary.invalidFolderFile':       'Nieprawidłowy format pliku — oczekiwany eksport pojedynczego katalogu.',
      'export.summary.noConflicts':             ':icon Brak konfliktów. Katalog "<strong>:name</strong>" z :count :word zostanie zaimportowany jako nowy.',
      'export.summary.fileContains':            '<strong style="color:#374151;">:icon Zawartość pliku (:count :word):</strong>',
      'export.summary.jsHeader':                ':icon JavaScript zestawu:',
      'export.summary.emptyRule':               'pusty zestaw',
      'toast.exportedFolder':                   'Wyeksportowano katalog (:groupCount :groupWord, :ruleCount :ruleWord)',
      'inline.fileFolderLabel':                 'katalog',
      'inline.fileRuleLabel':                   'zestaw',
      'default.newGroupNameInline':             'Nowa grupa',

      // === Sync conflict modal counts (3-element line) ===
      'syncConflict.countLine':                 ':folders :foldersWord / :groups :groupsWord / :rules :rulesWord',

      // === Backup restore confirm ===
      'confirm.restoreBackup':                  'Przywrócić backup #:id?\n\nWSZYSTKIE obecne katalogi/grupy/zestawy zostaną zastąpione tym co jest w backupie. Inne urządzenia zsynchronizują się przy następnym pull.\n\nOperacja nieodwracalna (poza utworzeniem nowego backupu PRZED restore).',

      // === Loaders / placeholders ===
      'loading.shares':                         'Ładowanie…',
      'loading.generic':                        'Ładowanie…',

      // === Sidebar — drag & arrows tooltips ===
      'sidebar.dragHandle.title':               'Przeciągnij aby zmienić kolejność',
      'sidebar.expandCollapse.title':           'Rozwiń/zwiń',
      'sidebar.folderActive.title':             'Aktywny',
      'sidebar.folderInactive.title':           'Nieaktywny — wymuszono wyłączony',
      'sidebar.groupActive.title':              'Aktywna',
      'sidebar.groupInactive.title':            'Nieaktywna — wymuszono wyłączona',
      'sidebar.searchNoResults':                'Brak wyników dla ":query"',

      // === Theme ===
      'settings.theme.toggleToDark':            'Przełącz na tryb ciemny',
      'settings.theme.toggleToLight':           'Przełącz na tryb jasny',
      'settings.theme.labelDark':               'Tryb ciemny',
      'settings.theme.labelLight':              'Tryb jasny',
      'settings.theme.labelAuto':               'Auto (system)',
      'settings.theme.toggleToAuto':            'Przełącz na tryb automatyczny',

      // === URL chip remove ===
      'btn.removeUrlChip':                      'Usuń',

      // === CSS validator ===
      'cssValidator.unclosedComment':           'Niezamknięty komentarz /* ... */',
      'cssValidator.missingCloseBraces':        'Niedomknięte klamry: brakuje :count :word }',
      'plural.brace.one':                       'klamry zamykającej',
      'plural.brace.few':                       'klamer zamykających',
      'plural.brace.many':                      'klamer zamykających',
      'plural.brace.other':                     'klamer zamykających',
      'cssValidator.extraCloseBrace':           'Nadmiarowa klamra zamykająca } (linia :line)',
      'jsValidator.unterminatedString':         'Niezamknięty literał tekstowy (linia :line)',
      'jsValidator.unclosedBracket':            'Niedomknięty nawias :bracket (linia :line)',
      'jsValidator.extraBracket':               'Nadmiarowy nawias zamykający :bracket (linia :line)',

      // === Action buttons ===
      'action.shortcut.empty':                  'kliknij + naciśnij skrót',
      'action.code.empty':                      'kliknij aby dodać kod…',
      'action.stats.title':                     'Liczba kliknięć w panelu akcji',
      'action.visible.title':                   'Widoczny w panelu akcji (odhaczone = ukryty, ale nie usunięty)',
      'action.shortcut.title':                  'Skrót klawiszowy — kliknij i naciśnij kombinację. Esc anuluje. Skrót działa na pasujących stronach niezależnie od widoczności panelu.',
      'action.shortcut.titleAfterClick':        'Skrót klawiszowy. Kliknij i naciśnij kombinację by zmienić. Esc anuluje. × usuwa.',
      'action.shortcut.removeBtn':              'Usuń skrót klawiszowy',
      'action.delete.btn':                      'Usuń przycisk akcji',
      'action.menu.btn.duplicate':              'Duplikuj akcję tutaj',
      'action.menu.btn.move':                   'Przenieś akcję do innego zestawu',
      'action.menu.btn.copy':                   'Skopiuj akcję do innego zestawu',
      'action.menu.section.moveTo':             'Przenieś do zestawu...',
      'action.menu.section.copyTo':             'Skopiuj do zestawu...',
      'action.menu.empty.rules':                'Brak innych zestawów',
      'action.menu.toast.moved':                'Akcja przeniesiona do „:target"',
      'action.menu.toast.copied':               'Akcja skopiowana do „:target"',
      'action.shortcut.conflictTitle':          'Konflikt: ten skrót ma też :count :word:\n:list\n\nPrzy naciśnięciu zadziała tylko pierwsza w kolejności renderowania.',
      'action.shortcut.conflictItem':           '• ":action" w ":rule" (:folder)',
      'plural.actionAlt.one':                   'inna akcja',
      'plural.actionAlt.few':                   'inne akcje',
      'plural.actionAlt.many':                  'innych akcji',
      'plural.actionAlt.other':                 'innych akcji',
      'plural.more.one':                        'innych',
      'plural.more.few':                        'innych',
      'plural.more.many':                       'więcej',
      'plural.more.other':                      'więcej',
      'action.shortcut.conflictMore':           ' (i :count :word)',
      'action.shortcut.conflictToast':          'Konflikt: ten skrót ma już akcja ":actionLabel" w ":ruleName":more. Tylko jedna z nich zadziała.',
      'confirm.deleteAction':                   'Usunąć przycisk akcji ":label"?',

      // === History ===
      'history.header':                         'Ostatnie :count zapisanych wersji. Kliknij "Przywróć" aby cofnąć do tej wersji (obecna wersja zostanie zachowana w historii).',
      'history.modalTitle':                     'Historia zmian: ":name"',
      'history.version':                        'Wersja :n',
      'history.restore':                        'Przywróć',
      'history.close':                          'Zamknij',

      // === Export — pozostałe ===
      'export.summary.noElements':              ':icon Brak zaznaczonych elementów do eksportu',
      'export.summary.toExportFolderShort':     ':icon Do eksportu: :groupCount :groupWord, :ruleCount :ruleWord',
      'export.summary.noneSelectedShort':       ':icon Brak zaznaczonych zestawów',

      // === Import detection ===
      'import.detection.parseError':            '<strong style="color:#dc2626;">:icon Błąd parsowania JSON:</strong> :msg',
      'import.detection.partialBackup':         '<strong style="color:#5b21b6;">:icon Częściowy backup (wybrane elementy)</strong><br>',
      'import.detection.fullBackup':            '<strong style="color:#006870;">:icon Pełen backup ustawień:legacy</strong><br>',
      'import.detection.legacySuffix':          ' (starszy format)',
      'import.detection.willOverwrite':         '<br><br><strong style="color:#b91c1c;">:icon Zostaną nadpisane obecne ustawienia:</strong>',
      'import.detection.exportFirst':           '<br><span style="color:#7f1d1d;font-size:11px;">Zalecane: zrób eksport <strong>przed</strong> importem.</span>',
      'import.detection.folder':                '<strong style="color:#155e75;">:icon Eksport katalogu</strong> "<strong>:name</strong>"<br><span style="color:#155e75;">Zawiera: :stats</span>',
      'import.detection.group':                 '<strong style="color:#155e75;">:icon Eksport grupy</strong> "<strong>:name</strong>"<br><span style="color:#155e75;">Zawiera: :stats</span>',
      'import.detection.rule':                  '<strong style="color:#155e75;">:icon Eksport zestawu</strong> "<strong>:name</strong>"',

      // === Import — selektory targetu ===
      'import.target.folder.title':             'Gdzie umieścić importowany katalog?',
      'import.target.folder.newCopy':           'Dodaj jako nowy katalog top-level',
      'import.target.folder.overwrite':         'Nadpisz istniejący katalog ":name" (zachowuje ID — sync rozpozna jako edycję)',
      'import.target.folder.mergeInto':         'Scal grupy/zestawy z istniejącym katalogiem:',
      'import.target.group.title':              'Gdzie umieścić importowaną grupę?',
      'import.target.group.existingFolder':     'Dodaj do istniejącego katalogu:',
      'import.target.group.newFolder':          'Utwórz nowy katalog dla tej grupy',
      'import.target.rule.title':               'Gdzie umieścić importowany zestaw?',
      'import.target.rule.existingGroup':       'Dodaj do istniejącej grupy:',
      'import.target.rule.newFolder':           'Utwórz nowy katalog i grupę dla tego zestawu',

      // === Share errors (create flow) ===
      'share.error.unknown':                    'Nieznany błąd',
      'share.error.shareFailed':                'Masz już 20 aktywnych linków albo przekroczyłeś 50 MB. Odwołaj stare w „Moje share".',
      'share.error.badRequest':                 'Nie udało się wygenerować linku — sprawdź czy eksport jest poprawny. :msg',
      'share.error.rateLimited':                'Zbyt wiele linków utworzonych w ostatniej godzinie (limit: 30). Spróbuj za chwilę.',
      'share.error.invalidToken':               'Sesja wygasła — zaloguj się ponownie w panelu sync i spróbuj jeszcze raz.',
      'share.error.serverError':                'Problem z serwerem. Spróbuj ponownie za chwilę.',
      'share.error.fallback':                   'Błąd: :msg',
      'share.error.tooLarge':                   'Eksport za duży — backend dopuszcza max 5 MB. Spróbuj udostępnić mniejszą część.',

      // === Share subject (create modal) ===
      'share.subject.partial':                  'Częściowy eksport biblioteki',
      'share.subject.full':                     'Pełen eksport biblioteki',

      // === Time relative formatting (fmtRelHuman) ===
      'rel.lessThanMinute':                     'mniej niż minuta',
      'rel.min':                                ':n min',
      'rel.hMin':                               ':h h :m min',
      'rel.h':                                  ':h h',
      'rel.dH':                                 ':d d :h h',
      'rel.d':                                  ':d d',

      // === Shares list ===
      'shares.expired':                         'wygasł',
      'shares.fetchFailed':                     'Nie udało się pobrać listy: :msg',
      'shares.confirmRevoke':                   'Odwołać share #:id?\n\nLink przestanie działać natychmiast — odbiorcy dostaną 404 przy następnej próbie pobrania.\n\nUWAGA: jeśli ktoś już pobrał payload, będzie miał kopię lokalnie — odwołanie nie wycofa już pobranej zawartości.',
      'shares.revoked':                         'Odwołano share #:id',

      // === Share preview details ===
      'sharePreview.senderLabel':               'Nadawca:',
      'sharePreview.senderAnonymous':           'anonimowy',
      'sharePreview.expiresLabel':              'Wygasa:',
      'sharePreview.expiresValue':              'za :rel',
      'sharePreview.typeLabel':                 'Typ:',
      'sharePreview.sizeLabel':                 'Rozmiar:',
      'sharePreview.contentLabel':              'Zawartość:',
      'sharePreview.cssLabel':                  'CSS:',
      'sharePreview.cssValueChars':             ':n znaków',
      'sharePreview.jsLabel':                   'JavaScript:',
      'sharePreview.downloadsLabel':            'Pobrań:',
      'sharePreview.codePreviewTitle':          'Podgląd kodu z udostępnionego eksportu',

      // === Share import errors (preview) ===
      'shareImport.error.unknown':              'Nieznany błąd',
      'shareImport.error.badRequest':           'Token jest nieprawidłowy. Sprawdź czy skopiowałeś cały link.',
      'shareImport.error.notFound':             'Ten link wygasł lub został odwołany przez nadawcę. Poproś o nowy.',
      'shareImport.error.rateLimited':          'Zbyt wiele prób. Zaczekaj chwilę i spróbuj ponownie.',
      'shareImport.error.serverError':          'Problem z serwerem. Spróbuj ponownie.',
      'shareImport.error.fallback':             'Błąd: :msg',
      'shareImport.error.invalidTokenFormat':   'Nieprawidłowy format tokena',

      // === Rule import ===
      'ruleImport.unknownFormat':               'Nieznany lub nieobsługiwany format eksportu: :type',
      'prompt.ruleConflict.body':               'Importujesz ":new". Co zrobić z obecnym zestawem ":current"?\n\n  1 lub "zastap"  — Zastąp obecny zawartością z pliku\n  2 lub "kopia"   — Dodaj jako nowy zestaw obok obecnego (w tej samej grupie)\n  Anuluj          — Przerwij import\n\nWybór:',

      // === Folder export ===
      'folderExport.exportedToast':             'Wyeksportowano katalog (:groupCount :groupWord, :ruleCount :ruleWord)',

      // === Folder import — szczegóły ===
      'folderImport.parseError':                'Błąd parsowania JSON: :msg',
      'folderImport.invalidFormat':             'Nieprawidłowy format pliku — oczekiwany eksport pojedynczego katalogu.',
      'folderImport.noConflicts':               ':icon Brak konfliktów. Katalog "<strong>:name</strong>" z :count :word zostanie zaimportowany jako nowy.',
      'folderImport.conflictSameId':            'Importowany katalog ma takie samo ID jak istniejący "<strong>:name</strong>" (:count :word). Wybierz akcję:',
      'folderImport.conflictSameName':          'Istnieje już katalog o nazwie "<strong>:name</strong>" (:count :word). Wybierz akcję:',
      'folderImport.fileContents':              '<strong style="color:#374151;">:icon Zawartość pliku (:count :word):</strong>',
      'folderImport.jsHeader':                  ':icon JavaScript zestawu:',
      'folderImport.codePreviewTitle':          'Podgląd kodu: :name',
      'folderImport.previewJsBtn.title':        'Podgląd kodu JS',

      // === Sync — toasts + status messages ===
      'sync.toast.synced':                      'Zsynchronizowano: :applied zmian, :deletes usunięć',
      'sync.rel.justNow':                       'przed chwilą',
      'sync.tooltip.syncing':                   'Synchronizuję…',
      'sync.tooltip.error':                     'Synchronizacja: błąd. Ostatnia udana: :last',
      'sync.tooltip.connected':                 'Synchronizacja chmurowa — połączono. Ostatnia: :last',
      'sync.error.copyManually':                'Nie udało się skopiować — zaznacz i Ctrl+C ręcznie.',
      'sync.error.registerFailed':              'Błąd rejestracji: :msg',
      'sync.error.unknown':                     'nieznany',
      'sync.error.invalidKeyFormat':            'Nieprawidłowy format klucza — oczekujemy SYNC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX.',
      'sync.status.sendingLocal':               'Wysyłam :count lokalnych encji na konto.',
      'sync.status.cancelled':                  'Logowanie anulowane — wylogowano. Dane lokalne nietknięte.',
      'sync.status.sendingAndDeleting':         'Wysyłam :local lokalnych encji, usuwam :remote z konta…',
      'sync.status.merged':                     'Scalono. Lokalne unikalne (:count pozycji) pójdą na konto przy następnym sync.',
      'sync.error.firstSyncFailed':             'Login OK ale pierwsza sync zawiodła: :msg',
      'sync.error.loginFailed':                 'Błąd logowania: :msg',
      'sync.error.invalidEmail':                'Podaj prawidłowy email.',
      'sync.recovery.sent':                     'Jeśli ten email jest zarejestrowany, wysłaliśmy link odzyskiwania. Sprawdź skrzynkę.',
      'sync.error.generic':                     'Błąd: :msg',
      'sync.account.thisDevice':                'to urządzenie: :label',
      'sync.session.noLabel':                   '(bez etykiety)',
      'sync.session.thisDeviceSuffix':          ' (to urządzenie)',
      'sync.session.revoke':                    'Odwołaj',
      'sync.error.revokeFailed':                'Błąd odwołania: :msg',
      'sync.session.fetchFailed':               'Nie udało się pobrać listy sesji: :msg',
      'sync.key.copyManual':                    'Nie udało się skopiować — kliknij „Pokaż klucz" i Ctrl+C ręcznie.',
      'sync.key.removed':                       'Klucz usunięty z lokalnego storage.',
      'sync.backups.empty':                     'Brak backupów chmurowych. Utwórz pierwszy klikając "Utwórz backup teraz".',
      'sync.backups.fetchFailed':               'Nie udało się pobrać listy: :msg',
      'sync.backups.creating':                  'Tworzę backup chmurowy…',
      'sync.backups.createFailed':              'Błąd tworzenia backupu: :msg',
      'sync.backups.restored':                  'Przywrócono backup #:id. Zastąpiono :count encji. newCursor=:cursor.',
      'sync.backups.restoreFailed':             'Błąd przywracania: :msg',
      'sync.backups.downloadFailed':            'Błąd pobierania: :msg',
      'sync.backups.confirmDelete':             'Usunąć backup #:id z chmury? Operacja nieodwracalna.',
      'sync.backups.deleted':                   'Backup #:id usunięty.',
      'sync.backups.deleteFailed':              'Błąd usuwania: :msg',
      'sync.pushPullSummary':                   'Push: :pushedAccepted zaakceptowanych, :pushedRejected odrzuconych • Pull: :applied encji, :deletes usunięć.',
      'sync.returned':                          'Sync zwrócił: :result',
      'sync.snapshotFailed':                    'Błąd snapshotu: :msg',
      'sync.reconcile.running':                 'Pełny resync z kontem…',
      'sync.reconcile.okPushed':                'Pełny resync OK — wysłano :count brakujących encji na konto.',
      'sync.reconcile.okInSync':                'Pełny resync OK — biblioteka zgodna z kontem.',
      'sync.reconcile.failed':                  'Pełny resync nie powiódł się: :msg',
      'sync.email.saveButFailed':               'Email zapisany, ale wysłanie linku zawiodło — spróbuj ponownie.',
      'sync.email.verifyLinkSent':              'Wysłano link weryfikacji na :email. Sprawdź skrzynkę i kliknij link.',
      'sync.email.removed':                     'Email usunięty.',

      // === Sync result modal — strategie ===
      'syncResult.merge.title':                 'Scalono z chmurą',
      'syncResult.merge.desc':                  'Encje obecne w obu miejscach zostały nadpisane wersją z konta. Lokalne unikatowe pozostały i pójdą na konto przy najbliższej synchronizacji.',
      'syncResult.merge.tableHeader':           '<table class="result-table"><thead><tr><th>Typ</th><th>Z chmury: nadpisane</th><th>Z chmury: nowe</th><th>Lokalne zachowane → push</th><th>Łącznie po scaleniu</th></tr></thead><tbody>',
      'syncResult.useServer.title':             'Zastąpiono lokalne danymi z chmury',
      'syncResult.useServer.desc':              'Lokalna biblioteka została całkowicie zastąpiona stanem z konta. Lokalne unikatowe encje (jeśli były) zostały utracone.',
      'syncResult.useServer.tableHeader':       '<table class="result-table"><thead><tr><th>Typ</th><th>Pobrane z chmury</th><th>Lokalne utracone</th><th>Wspólne (nadpisane)</th><th>Łącznie po sync</th></tr></thead><tbody>',
      'syncResult.sendMine.title':              'Wysyłam lokalne na konto',
      'syncResult.sendMine.desc':               'Lokalna biblioteka zostanie pchnięta na konto. Encje obecne w chmurze których brak lokalnie — zostaną oznaczone jako usunięte (tombstone) na koncie.',
      'syncResult.sendMine.tableHeader':        '<table class="result-table"><thead><tr><th>Typ</th><th>Wysyłane (push)</th><th>Nadpisuje na koncie</th><th>Nowe na koncie</th><th>Usuwane z konta</th></tr></thead><tbody>',
      'syncResult.fallback.title':              'Synchronizacja zakończona',
      'syncResult.empty':                       'Brak szczegółów.',
      'syncResult.label.folders':               'Katalogi',
      'syncResult.label.groups':                'Grupy',
      'syncResult.label.rules':                 'Zestawy',
      'syncResult.label.actions':               'Akcje',

      // === Options — toasty (notyfikacje na dole strony) ===
      'toast.saved':                            'Zapisano',
      'toast.cantDeleteLastGroup':              'Nie można usunąć ostatniej grupy w katalogu',
      'toast.noOtherTab':                       'Brak innej karty z URL strony',
      'toast.urlInserted':                      'Wstawiono URL z karty',
      'toast.urlAlreadyAdded':                  'Wzorzec już jest na liście: :pattern',
      'toast.error':                            'Błąd: :msg',
      'toast.shortcutMustIncludeMod':           'Dozwolony skrót: Ctrl+Alt+Shift + litera (oprócz G H J K L), cyfra lub znak (oprócz \\).',
      'toast.shortcutCollidesChrome':           'Uwaga: skrót koliduje ze skrótem Chrome (np. nowa karta). Może nie zadziałać — wybierz inny.',
      'toast.copied':                           'Skopiowano: :text',
      'toast.copyFailed':                       'Kopiowanie nie powiodło się',
      'toast.copyClipboard':                    'Skopiowano do schowka',
      'toast.copyLink':                         'Skopiowano link',
      'toast.copyToken':                        'Skopiowano token',
      'toast.noHistory':                        'Brak historii zmian dla tego zestawu',
      'toast.versionRestored':                  'Przywrócono wersję',
      'toast.fileNotFullImport':                'Plik nie nadaje się do pełnego importu',
      'toast.unknownOption':                    'Nieznana opcja — anulowano import',
      'toast.importedSettings':                 'Zaimportowano ustawienia',
      'toast.parseError':                       'Błąd parsowania: :msg',
      'toast.invalidPartialFormat':             'Nieprawidłowy format — oczekiwany eksport grupy, katalogu lub zestawu',
      'toast.groupImport.folderRejected':       'To eksport katalogu (wiele grup). Zaimportuj go przez import na poziomie katalogu, nie grupy.',
      'toast.exportedRule':                     'Wyeksportowano zestaw',
      'toast.signInFirst':                      'Najpierw zaloguj się w panelu synchronizacji',
      'toast.exportNotReady':                   'Eksport nie jest gotowy',
      'toast.noRulesSelected':                  'Brak zaznaczonych zestawów',
      'toast.revokeError':                      'Błąd odwoływania: :msg',
      'toast.importedFromLink':                 'Zaimportowano z linka',
      'toast.invalidRuleFormat':                'Nieprawidłowy format pliku — oczekiwany eksport zestawu',
      'toast.importedReplaced':                 'Zaimportowano (zastąpiono)',
      'toast.importedAsCopy':                   'Zaimportowano jako kopię',
      'toast.noImportData':                     'Brak danych do importu',
      'toast.importedFolder':                   'Zaimportowano katalog: :name',
      'toast.importedGroup':                    'Zaimportowano grupę: :name',
      'toast.importedRule':                     'Zaimportowano zestaw: :name',

      // === Options — natywne dialogi confirm() ===
      'confirm.restoreVersion':                 'Przywrócić tę wersję? Obecna zawartość zostanie dodana do historii jako nowy snapshot.',
      'confirm.revokeSession':                  'Odwołać tę sesję?',
      'confirm.removeKey':                      'Usunąć klucz z tego urządzenia?\n\nLogowanie nadal działa (sesja jest niezależna od klucza), ale nie będzie można podejrzeć klucza ani skopiować go na drugie urządzenie z tego komputera.\n\nUpewnij się, że masz kopię klucza zapisaną gdzie indziej (menedżer haseł).',
      'confirm.snapshot':                       'Pełny snapshot pobiera całą bibliotekę z chmury i zastępuje stan lokalny. Twoje pending zmiany NIE zostaną utracone (wciąż w kolejce). Kontynuować?',
      'confirm.removeEmail':                    'Usunąć email z konta? Bez emaila nie będziesz mieć możliwości recovery przy zgubieniu klucza.',
      'confirm.signOut':                        'Wylogować to urządzenie? Lokalna biblioteka zostaje, ale przestaje się synchronizować.',

      // === Modal — eksport pełen ===
      'modal.export.title':                     'Eksport ustawień',
      'modal.export.mode.all':                  'Wszystko',
      'modal.export.mode.select':               'Wybrane katalogi/zestawy',
      'modal.export.selectAll':                 'Zaznacz wszystkie',
      'modal.export.deselectAll':               'Odznacz wszystkie',
      'modal.export.hint':                      'Możesz skopiować JSON ze schowka lub pobrać plik:',
      'modal.export.close':                     'Zamknij',
      'modal.export.share':                     'Udostępnij URL',
      'modal.export.share.title':               'Udostępnij ten eksport jako czasowy link publiczny',
      'modal.export.download':                  'Pobierz plik',
      'modal.export.copy':                      'Kopiuj do schowka',

      // === Modal — import (uniwersalny) ===
      'modal.import.title':                     'Import ustawień',
      'modal.import.warningStrong':             'Uwaga:',
      'modal.import.warningRest':               ' pełen import nadpisze wszystkie obecne ustawienia.',
      'modal.import.pickerHint':                'Wklej JSON dowolnego eksportu JustZix (pełen backup, katalog, grupa lub zestaw) — formularz automatycznie rozpozna typ i pokaże opcje umieszczenia.',
      'modal.import.placeholder':               'Wklej JSON tutaj lub wybierz plik...',
      'modal.import.cancel':                    'Anuluj',
      'modal.import.import':                    'Importuj',

      // === Modal — eksport katalogu ===
      'modal.folderExport.titlePrefix':         'Eksport katalogu:',
      'modal.folderExport.hint':                'Zaznacz grupy/zestawy do eksportu.',
      'modal.folderExport.cancel':              'Anuluj',
      'modal.folderExport.share':               'Udostępnij URL',
      'modal.folderExport.shareTitle':          'Udostępnij ten katalog jako czasowy link publiczny',
      'modal.folderExport.download':            'Pobierz plik',

      // === Modal — eksport grupy ===
      'modal.groupExport.titlePrefix':          'Eksport grupy:',
      'modal.groupExport.hint':                 'Zaznacz zestawy do eksportu.',
      'modal.groupExport.cancel':               'Anuluj',
      'modal.groupExport.share':                'Udostępnij URL',
      'modal.groupExport.shareTitle':           'Udostępnij tę grupę jako czasowy link publiczny',
      'modal.groupExport.download':             'Pobierz plik',

      // === Modal — import katalogu ===
      'modal.folderImport.title':               'Import katalogu',
      'modal.folderImport.placeholder':         'Wklej JSON katalogu lub wybierz plik...',
      'modal.folderImport.conflicts':           'Wykryto konflikty:',
      'modal.folderImport.action.rename':       'Importuj jako nowy katalog (z dopiskiem "(import)")',
      'modal.folderImport.action.overwrite':    'Nadpisz istniejący katalog',
      'modal.folderImport.action.merge':        'Scal — dodaj nowe zestawy do istniejącego katalogu (zachowując oba)',
      'modal.folderImport.cancel':              'Anuluj',
      'modal.folderImport.import':              'Importuj',

      // === Paski akcji (Faza 2 wielu pasków akcji, od v2.9) ===
      'actionBars.btn.label':                   'Paski akcji',
      'actionBars.btn.title':                   'Zarządzaj paskami akcji przypisanymi do tego katalogu',
      'actionBars.btn.titleGroup':              'Zarządzaj paskami akcji przypisanymi do tej grupy',
      'actionBars.btn.titleRule':               'Zarządzaj paskami akcji przypisanymi do tego zestawu',
      'actionBars.parentType.folder':           'Katalog',
      'actionBars.parentType.group':            'Grupa',
      'actionBars.parentType.rule':             'Zestaw',
      'actionBars.parentType.folder.short':     '[K]',
      'actionBars.parentType.group.short':      '[G]',
      'actionBars.parentType.rule.short':       '[Z]',
      'actionBars.empty':                       'Brak pasków akcji na tym poziomie. Kliknij "+ Dodaj nowy pasek" aby utworzyć pierwszy.',
      'actionBars.section.owned':               'Bary tego poziomu',
      'actionBars.section.fromFolder':          '↑ Odziedziczone z katalogu "<strong>:name</strong>"',
      'actionBars.section.fromGroup':           '↑ Odziedziczone z grupy "<strong>:name</strong>"',
      'actionBars.elementsCount':               'Elementów: :count',
      'actionBars.enabled':                     'Włączony',
      'actionBars.name.placeholder':            'NAZWA',
      'actionBars.name.title':                  'Etykieta na krawędzi paska (max 6 znaków, UPPERCASE). Puste = bez etykiety.',
      'actionBars.color.title':                 'Kolor tła paska',
      'actionBars.orientation.title':           'Kliknij aby przełączyć orientację paska (pionowy ↔ poziomy)',
      'actionBars.color.clear':                 'Usuń kolor tła',
      'actionBars.moveTo':                      'Przenieś do:',
      'actionBars.delete':                      'Usuń',
      'actionBars.confirmDelete':               'Usunąć ten pasek?',
      'actionBars.confirmDeleteWithElements':   'Usunąć ten pasek? Przypisane elementy (:count) zachowają swoje definicje, ale stracą przypisanie do paska — będzie trzeba im ręcznie przypisać inny pasek.',
      'actionBars.deleteMigrate.message':       'Pasek ma :count przypisanych elementów. Wybierz dokąd je przenieść przed usunięciem (lub odepnij od paska, żeby zostały bez przypisania):',
      'actionBars.deleteMigrate.detachOption':  '— odepnij elementy (bez paska) —',
      'actionBars.deleteMigrate.confirm':       'Przenieś i usuń',
      'actionBars.deleteMigrate.cancel':        'Anuluj',
      'modal.actionBars.titlePrefix':           'Paski akcji —',
      'modal.actionBars.hint':                  'Paski przypisane do tego poziomu. Bary folderu agregują elementy ze wszystkich aktywnych zestawów folderu, bary grupy — tylko z zestawów grupy, bary zestawu — gdy zestaw aktywny. Pasek bez widocznych elementów nie zostanie wyświetlony.',
      'modal.actionBars.add':                   '+ Dodaj nowy pasek',
      'modal.actionBars.close':                 'Zamknij',

      // === Okna — dropdown wybór typu (od v2.13.41, kontener dla CSS / JS / konsole) ===
      'windows.btn.label':                      'Okna',
      'windows.btn.title':                      'Zarządzaj oknami przypisanymi do tego katalogu (CSS / JS / konsole)',
      'windows.btn.titleGroup':                 'Zarządzaj oknami przypisanymi do tej grupy',
      'windows.btn.titleRule':                  'Zarządzaj oknami przypisanymi do tego zestawu',
      'windows.menu.title':                     'Wybierz typ okna:',
      'windows.menu.comingSoon':                'wkrótce',
      // Etykiety per typ — wszystkie 4 są wymienione w dropdownie, na razie tylko CSS aktywne.
      'jsPanes.btn.label':                      'Okna JS',
      'jsConsoles.btn.label':                   'Konsole JS',
      'outputConsoles.btn.label':               'Konsole Output',

      // === Okna CSS (Faza 2 "Okna na froncie", od v2.13.40) ===
      'cssPanes.btn.label':                     'Okna CSS',
      'cssPanes.contextMenu.disable':           'Zamknij okno CSS',
      'cssPanes.btn.title':                     'Zarządzaj oknami CSS przypisanymi do tego katalogu',
      'cssPanes.btn.titleGroup':                'Zarządzaj oknami CSS przypisanymi do tej grupy',
      'cssPanes.btn.titleRule':                 'Zarządzaj oknami CSS przypisanymi do tego zestawu',
      'cssPanes.empty':                         'Brak okien CSS na tym poziomie. Kliknij "+ Dodaj nowe okno CSS" aby utworzyć pierwsze.',
      'cssPanes.section.owned':                 'Okna tego poziomu',
      'cssPanes.name.placeholder':              'Nazwa okna',
      'cssPanes.color.title':                   'Kolor kropki w nagłówku okna',
      // Customowe kolory okien (od v2.13.58) — wspólne dla CSS / JS pane.
      'panes.style.dot':                        'Kropka',
      'panes.style.headerBg':                   'Tło belki',
      'panes.style.headerText':                 'Tekst belki',
      'panes.style.bodyBg':                     'Tło pola',
      'panes.style.bodyText':                   'Tekst pola',
      'panes.style.runBtn':                     'Przycisk Run',
      'panes.style.reset':                      'Wyczyść',
      'mcp.default.title':                      'Przywróć domyślny',
      // Etykiety kolorów akcji (od v2.13.60) — krótkie, bez instrukcji ("nazwa czego dotyczy wystarczy").
      'action.colors.bg':                       'Tło',
      'action.colors.text':                     'Tekst',
      'action.colors.dropdownBg':               'Tło dropdown',
      'action.colors.option':                   'Opcja',
      'action.colors.selected':                 'Zaznaczone',
      'action.colors.hover':                    'Hover',
      'action.colors.label':                    'Etykieta',
      'action.colors.value':                    'Wartość',
      'action.colors.placeholder':              'Placeholder',
      'action.colors.bgInactive':               'Tło nieaktywne',
      'action.colors.inactiveText':             'Tekst nieaktywny',
      'cssPanes.enabled.label':                 'Włączone',
      'cssPanes.enabled.title':                 'Czy okno ma się pojawiać na pasujących stronach',
      'cssPanes.delete.title':                  'Usuń to okno',
      'cssPanes.delete.confirm':                'Usunąć okno CSS „:name"? Zawartość textarea w aktywnych kartach zostanie utracona.',
      'modal.cssPanes.titlePrefix':             'Okna CSS —',
      'modal.cssPanes.hint':                    'Floating textarea wstrzykiwana na strony z URL pasującym do tego poziomu. Treść zachowywana w sessionStorage per tab (znika po zamknięciu karty). Live <style> injection — zmiana CSS widoczna natychmiast.',
      'modal.cssPanes.add':                     '+ Dodaj nowe okno CSS',
      'modal.cssPanes.close':                   'Zamknij',
      'cssPane.frontend.close.title':           'Ukryj okno (zostanie ukryte do końca karty)',

      // === Okna JS (Faza 2 cont., od v2.13.56) ===
      'jsPanes.contextMenu.disable':            'Zamknij okno JS',
      'jsPanes.btn.title':                      'Zarządzaj oknami JS przypisanymi do tego katalogu',
      'jsPanes.btn.titleGroup':                 'Zarządzaj oknami JS przypisanymi do tej grupy',
      'jsPanes.btn.titleRule':                  'Zarządzaj oknami JS przypisanymi do tego zestawu',
      'jsPanes.empty':                          'Brak okien JS na tym poziomie. Kliknij "+ Dodaj nowe okno JS" aby utworzyć pierwsze.',
      'jsPanes.section.owned':                  'Okna tego poziomu',
      'jsPanes.name.placeholder':               'Nazwa okna',
      'jsPanes.color.title':                    'Kolor kropki w nagłówku okna',
      'jsPanes.enabled.label':                  'Włączone',
      'jsPanes.enabled.title':                  'Czy okno ma się pojawiać na pasujących stronach',
      'jsPanes.delete.title':                   'Usuń to okno',
      'jsPanes.delete.confirm':                 'Usunąć okno JS „:name"? Zawartość textarea w aktywnych kartach zostanie utracona.',
      'modal.jsPanes.titlePrefix':              'Okna JS —',
      'modal.jsPanes.hint':                     'Floating textarea wykonująca JS w kontekście strony (MAIN world, new Function) per zmianę. Treść per-tab w sessionStorage. Błędy wykonania widoczne w czerwonym overlay pod kodem.',
      'modal.jsPanes.add':                      '+ Dodaj nowe okno JS',
      'modal.jsPanes.close':                    'Zamknij',
      'jsPane.frontend.close.title':            'Ukryj okno (zostanie ukryte do końca karty)',

      // === Konsole JS (Faza 2 cont., od v2.13.66) ===
      'jsConsoles.contextMenu.disable':         'Zamknij konsolę JS',
      'jsConsoles.empty':                       'Brak konsol na tym poziomie. Kliknij "+ Dodaj nową konsolę" aby utworzyć pierwszą.',
      'jsConsoles.section.owned':               'Konsole tego poziomu',
      'jsConsoles.name.placeholder':            'Nazwa konsoli',
      'jsConsoles.enabled.label':               'Włączona',
      'jsConsoles.enabled.title':               'Czy konsola ma się pojawiać na pasujących stronach',
      'jsConsoles.delete.title':                'Usuń tę konsolę',
      'jsConsoles.delete.confirm':              'Usunąć konsolę „:name"? Historia outputu w aktywnych kartach zostanie utracona.',
      'modal.jsConsoles.titlePrefix':           'Konsole JS —',
      'modal.jsConsoles.hint':                  'Interaktywna konsola REPL — Ctrl+Enter wykonuje kod w MAIN world strony, output (lub błąd) appenduje się do log area pod inputem. Output per-tab w sessionStorage.',
      'modal.jsConsoles.add':                   '+ Dodaj nową konsolę',
      'modal.jsConsoles.close':                 'Zamknij',
      'jsConsole.frontend.close.title':         'Ukryj konsolę (do końca karty)',
      'jsConsole.input.placeholder':            'Kod do wykonania (Ctrl+Enter aby uruchomić, Ctrl+L wyczyść)',
      'jsConsole.clear.title':                  'Wyczyść output (Ctrl+L)',

      // === Output Console (Faza 2 finish, od v2.13.72) — read-only log viewer. ===
      'panes.style.clearBtn':                   'Przycisk Clear',
      'panes.style.actionBtns':                 'Przyciski akcji',
      'outputConsoles.contextMenu.disable':     'Zamknij konsolę Output',
      'outputConsoles.empty':                   'Brak konsol output na tym poziomie. Kliknij "+ Dodaj nową konsolę output" aby utworzyć pierwszą.',
      'outputConsoles.section.owned':           'Konsole output tego poziomu',
      'outputConsoles.name.placeholder':        'Nazwa konsoli output',
      'outputConsoles.enabled.label':           'Włączona',
      'outputConsoles.enabled.title':           'Czy konsola output ma się pojawiać na pasujących stronach',
      'outputConsoles.delete.title':            'Usuń tę konsolę output',
      'outputConsoles.delete.confirm':          'Usunąć konsolę output „:name"? Historia logów w aktywnych kartach zostanie utracona.',
      'modal.outputConsoles.titlePrefix':       'Konsole Output —',
      'modal.outputConsoles.hint':              'Read-only log viewer — przechwytuje console.log/warn/error/info ze strony (w tym async). Wyczyść (⌫) tylko czyści podgląd, nie konsolę przeglądarki.',
      'modal.outputConsoles.add':               '+ Dodaj nową konsolę output',
      'modal.outputConsoles.close':             'Zamknij',
      'outputConsole.frontend.close.title':     'Ukryj konsolę output (do końca karty)',
      'outputConsole.clear.title':              'Wyczyść podgląd (Ctrl+Shift+L)',
      'outputConsole.autoscroll.title':         'Auto-scroll do najnowszych logów',
      'outputConsole.empty':                    '(brak logów — czekam na console.* / JZ.* ze strony)',
      // viewMode + filters + search (od v2.13.73, per spec sekcja 6).
      'outputConsole.mode.standard.title':      'Pokazuj tylko console.* (klik / Ctrl+Shift+K → JZ.*)',
      'outputConsole.mode.jz.title':            'Pokazuj tylko window.JZ.* (klik / Ctrl+Shift+K → oba)',
      'outputConsole.mode.both.title':          'Pokazuj oba: console.* + JZ.* (klik / Ctrl+Shift+K → tylko console.*)',
      'outputConsole.filters.title':            'Filtry poziomów',
      'outputConsole.filters.level.log':        'log',
      'outputConsole.filters.level.info':       'info',
      'outputConsole.filters.level.warn':       'warn',
      'outputConsole.filters.level.error':      'error',
      'outputConsole.filters.level.debug':      'debug',
      'outputConsole.search.title':             'Szukaj w logach (Ctrl+Shift+S; Esc czyści)',
      'outputConsole.search.placeholder':       'Filtruj wpisy…',
      'outputConsole.tab.all':                  'Wszystko',
      'outputConsole.tab.jz':                   'JZconsole',
      'outputConsole.tab.console':              'Console',
      'outputConsole.tab.network':              'Network',
      'outputConsole.tab.errors':               'Błędy',
      'outputConsole.tab.datalayer':            'DataLayer',
      'outputConsole.net.field.callTime':       'Czas wywołania',
      'outputConsole.net.field.status':         'Status',
      'outputConsole.net.field.type':           'Typ',
      'outputConsole.net.field.method':         'Metoda',
      'outputConsole.net.field.domain':         'Domena',
      'outputConsole.net.field.path':           'Ścieżka',
      'outputConsole.net.field.remote':         'Adres zdalny',
      'outputConsole.net.field.initiator':      'Initiator',
      'outputConsole.net.field.time':           'Czas',
      'outputConsole.net.field.size':           'Rozmiar',
      'outputConsole.net.field.url':            'URL',
      'outputConsole.net.field.error':          'Błąd',
      'outputConsole.net.status.err':           'Błędy/Inne',
      'outputConsole.filter.all':               'wszystkie',
      'outputConsole.filter.none':              'żadne',
      'outputConsole.net.domainFilter':         'filtruj po domenie…',
      'outputConsole.net.domainsInclude':       'Domeny+',
      'outputConsole.net.domainsExclude':       'Domeny-',
      'outputConsole.dl.newPushes':             'Nowe pushe',
      'outputConsole.dl.currentState':          'Aktualny stan',
      'outputConsole.dl.expandAll':             'rozwiń wszystko',
      'outputConsole.dl.collapseAll':           'zwiń wszystko',
      'outputConsole.net.size':                 'Rozmiar',
      'outputConsole.net.time':                 'Czas',
      'outputConsole.err.field.type':           'Typ',
      'outputConsole.err.field.location':       'Lokalizacja',
      'outputConsole.err.field.stack':          'Stack',
      // API help popup (od v2.13.78, Ctrl+Shift+H).
      'outputConsole.apiHelp.title': 'JustZix — skróty i okna na froncie',
      'outputConsole.apiHelp.sec.global': 'Skróty globalne — działają na każdej stronie',
      'outputConsole.apiHelp.g.tempCss': 'Okno TEMP CSS — otwórz / zamknij',
      'outputConsole.apiHelp.g.tempJs': 'Okno TEMP JS — otwórz / zamknij',
      'outputConsole.apiHelp.g.tempJsc': 'Okno TEMP konsola JS — otwórz / zamknij',
      'outputConsole.apiHelp.g.tempOut': 'Okno TEMP Output Console — otwórz / zamknij',
      'outputConsole.apiHelp.g.ai': 'AI Helper — otwórz / zamknij',
      'outputConsole.apiHelp.g.edit': 'Tryb edycji paska akcji — włącz / wyłącz',
      'outputConsole.apiHelp.sec.window': 'Wewnątrz okna — gdy ma fokus',
      'outputConsole.apiHelp.w.run': 'Wykonaj kod / wyślij wiadomość — okno JS, konsola JS, AI Helper',
      'outputConsole.apiHelp.w.history': 'Historia wpisanych komend — konsola JS',
      'outputConsole.apiHelp.w.clearConsole': 'Wyczyść output — konsola JS',
      'outputConsole.apiHelp.w.escSearch': 'Wyczyść pole wyszukiwania — Output Console',
      'outputConsole.apiHelp.sec.output': 'Output Console',
      'outputConsole.apiHelp.o.clear': 'Wyczyść aktywną zakładkę wszystkich konsol Output',
      'outputConsole.apiHelp.o.help': 'Pokaż / ukryj tę pomoc',
      'outputConsole.apiHelp.o.tabs': '6 zakładek: All, JZ, Console, Network, Errors, DataLayer — każda z własnym, niezależnym zestawem filtrów.',
      'outputConsole.apiHelp.o.network': 'Network: Alt+klik na wierszu dodaje domenę do filtra „Domeny+", a Alt+Shift+klik — do „Domeny-".',
      'outputConsole.apiHelp.o.datalayer': 'DataLayer: przeciągnij belkę między kolumnami, aby zmienić ich szerokość.',
      'outputConsole.apiHelp.sec.mouse': 'Okna na froncie — obsługa myszą',
      'outputConsole.apiHelp.m.drag': 'Przeciągnij okno za belkę nagłówka, aby zmienić jego położenie.',
      'outputConsole.apiHelp.m.resize': 'Zmień rozmiar okna, ciągnąc za jeden z 8 uchwytów na krawędziach i w narożnikach.',
      'outputConsole.apiHelp.m.snap': 'Zbliż okno do innego — przyciągną się magnetycznie i utworzą grupę przesuwaną razem.',
      'outputConsole.apiHelp.m.context': 'Prawy klik na belce nagłówka otwiera menu: zamknij okno, rozłącz, font, rozmiar tekstu.',
      'outputConsole.apiHelp.m.tempDbl': 'Dwuklik w belkę okna TEMP przełącza je między rozmiarem domyślnym a zapamiętanym.',
      'outputConsole.apiHelp.m.scroll': 'Przewijanie kółkiem wewnątrz okna przewija tylko jego zawartość, nie stronę pod spodem.',
      'outputConsole.apiHelp.sec.editmode': 'Tryb edycji paska akcji — po włączeniu Ctrl+Alt+S',
      'outputConsole.apiHelp.em.undo': 'Cofnij ostatnią zmianę',
      'outputConsole.apiHelp.em.delete': 'Usuń zaznaczone etykiety',
      'outputConsole.apiHelp.em.deselect': 'Odznacz wszystkie elementy',
      'outputConsole.apiHelp.em.move': 'Przeciągnij zaznaczone elementy myszą — kilka naraz przesuwa się razem.',
      'outputConsole.apiHelp.em.select': 'Klik zaznacza element, Ctrl+klik dodaje lub usuwa go z zaznaczenia, Shift+przeciągnięcie po pustym polu zaznacza prostokątem.',
      'outputConsole.apiHelp.em.label': 'Przycisk „+ Etykieta" dodaje statyczny podpis tekstowy; dwuklik na etykiecie otwiera jej edytor.',
      'outputConsole.apiHelp.sec.logging': 'Logowanie do Output Console',
      'outputConsole.apiHelp.jzApi': 'Własny logger (kind=jz)',
      'outputConsole.apiHelp.aliases': 'Aliasy: window.__JUSTZIX__ (zawsze), window.JZ (jeśli niezajęte przez stronę).',
      'outputConsole.apiHelp.consoleApi': 'Standardowe console.* (kind=console, automatyczny capture)',
      'outputConsole.apiHelp.consoleNote': 'Każde console.* ze strony jest przekazywane do oryginalnej konsoli (DevTools nadal działają) i wysyłane do Output Console.',
      'outputConsole.apiHelp.footer': 'Pełna instrukcja (8 języków): panel JustZix → Pomoc.',
      // === Onboarding / samouczek ===
      'content.exec.cspBlocked': 'Ta strona blokuje wykonywanie skryptów (CSP). Włącz „Skrypty użytkownika" dla JustZix w ustawieniach przeglądarki.',
      'onboard.ui.back': 'Wstecz',
      'onboard.ui.next': 'Dalej',
      'onboard.ui.finish': 'Zakończ',
      'onboard.ui.copy': 'Kopiuj',
      'onboard.ui.copied': 'Skopiowano',
      'onboard.ui.hint': 'Wykonaj tę akcję, aby przejść dalej',
      'onboard.ui.close': 'Zamknij',
      'onboard.front.title': 'Samouczek frontu',
      'onboard.front.lead': 'Poznaj narzędzia JustZix dostępne bezpośrednio na stronie — pływający przycisk, panel, okna kodu i paski akcji.',
      'onboard.front.titleFirst': 'JustZix na stronie',
      'onboard.front.leadFirst': 'JustZix działa też bezpośrednio na stronie. Ten samouczek pokaże pływający przycisk, okna CSS/JS i paski akcji — krok po kroku.',
      'onboard.front.empty': 'Samouczki frontu pojawią się wkrótce.',
      'onboard.lr.titleFirst': 'Witaj w JustZix',
      'onboard.lr.title': 'Centrum samouczków',
      'onboard.lr.leadFirst': 'Pierwszy raz tutaj? Interaktywny samouczek przeprowadzi Cię przez zbudowanie kompletnego, działającego rozwiązania — krok po kroku, prosto w panelu.',
      'onboard.lr.lead': 'Uruchom interaktywny przewodnik — poprowadzi Cię krok po kroku przez budowę gotowego rozwiązania.',
      'onboard.lr.scenarios': 'Samouczki',
      'onboard.lr.start': 'Rozpocznij',
      'onboard.lr.later': 'Może później',
      'onboard.lr.noAuto': 'Nie pokazuj przy starcie',
      'onboard.proj.name': 'Twój pierwszy projekt',
      'onboard.proj.desc': 'Od zera do działającego rozwiązania: katalog, pasek akcji, grupa, zestaw z CSS i JS oraz przycisk przełączający efekt.',
      'onboard.proj.meta': '19 kroków · ~3 min',
      'onboard.proj.s1.title': 'Witaj w JustZix',
      'onboard.proj.s1.body': 'JustZix wstrzykuje Twój własny CSS i JavaScript na wybrane strony. Zbudujemy teraz kompletne rozwiązanie z działającym przyciskiem — krok po kroku. Zajmie to około 3 minut.',
      'onboard.proj.s2.title': 'Dodaj katalog',
      'onboard.proj.s2.body': 'Katalog grupuje reguły dla jednego serwisu. Kliknij podświetlony przycisk <code>+</code> w panelu bocznym.',
      'onboard.proj.s3.title': 'Nazwa katalogu',
      'onboard.proj.s3.body': 'Wpisz nazwę, np. <code>Mój pierwszy projekt</code> — to etykieta wyłącznie dla Ciebie.',
      'onboard.proj.s4.title': 'Wzorzec URL',
      'onboard.proj.s4.body': 'Decyduje, na jakich stronach katalog działa. Wpisz adres z gwiazdką i naciśnij Enter, np. <code>https://example.com/*</code>. Aby ćwiczyć na dowolnej stronie, wpisz samo <code>*</code>.',
      'onboard.proj.s5.title': 'Etykieta przycisku',
      'onboard.proj.s5.body': 'Maksymalnie 3 litery — pojawią się na pływającym przycisku JustZix widocznym na stronie.',
      'onboard.proj.s6.title': 'Pasek akcji',
      'onboard.proj.s6.body': 'Pasek akcji to panel z interaktywnymi przyciskami widoczny na stronie. Najpierw go utworzymy — kliknij podświetlony przycisk pasków akcji.',
      'onboard.proj.s7.title': 'Dodaj pasek',
      'onboard.proj.s7.body': 'Kliknij, aby utworzyć nowy pasek akcji w tym katalogu.',
      'onboard.proj.s8.title': 'Zamknij menedżer',
      'onboard.proj.s8.body': 'Pasek gotowy. Zamknij okno menedżera pasków, aby wrócić do katalogu.',
      'onboard.proj.s9.title': 'Otwórz grupę',
      'onboard.proj.s9.body': 'Tworząc katalog, JustZix dodał już pierwszą grupę. W panelu bocznym rozwiń katalog i kliknij tę grupę, aby ją otworzyć, a potem kliknij „Dalej”.',
      'onboard.proj.s10.title': 'Dodaj zestaw',
      'onboard.proj.s10.body': 'Zestaw to właściwa reguła — para kodu CSS i JavaScript. Kliknij podświetlony przycisk dodawania zestawu.',
      'onboard.proj.s11.title': 'Otwórz zestaw',
      'onboard.proj.s11.body': 'Nowy zestaw pojawił się w drzewie pod grupą. Kliknij go, aby otworzyć edytor zestawu, a potem kliknij „Dalej”.',
      'onboard.proj.s12.title': 'Kod CSS',
      'onboard.proj.s12.body': 'Zakładka CSS jest już aktywna. Wklej poniższy kod — wyszarza całą stronę i przygotowuje przełącznik efektu.',
      'onboard.proj.s13.title': 'Zakładka JavaScript',
      'onboard.proj.s13.body': 'Kliknij zakładkę <code>JavaScript</code>, aby dodać kod JS do zestawu.',
      'onboard.proj.s14.title': 'Kod JavaScript',
      'onboard.proj.s14.body': 'Kod JS uruchamia się po wejściu na stronę. Wklej poniższy — wyświetli komunikat potwierdzający, że JustZix działa. Pole ma walidację składni.',
      'onboard.proj.s15.title': 'Zakładka Akcje',
      'onboard.proj.s15.body': 'Teraz dodamy interaktywny przycisk. Kliknij zakładkę akcji w edytorze zestawu.',
      'onboard.proj.s16.title': 'Dodaj przycisk',
      'onboard.proj.s16.body': 'Kliknij, aby dodać element akcji. Domyślnie powstaje przycisk uruchamiający kod na żądanie.',
      'onboard.proj.s17.title': 'Kod przycisku',
      'onboard.proj.s17.body': 'Każdy przycisk ma własny kod uruchamiany po kliknięciu na stronie. Kliknij pole kodu nowego przycisku.',
      'onboard.proj.s18.title': 'Kod przełącznika',
      'onboard.proj.s18.body': 'Wklej kod, który włącza i wyłącza wyszarzenie ustawione w CSS. Następnie zapisz zmiany w oknie edytora kodu.',
      'onboard.proj.s19.title': 'Gotowe!',
      'onboard.proj.s19.body': 'Masz kompletne rozwiązanie: katalog, pasek akcji, grupę, zestaw z kodem CSS i JS oraz działający przycisk. Wejdź na stronę pasującą do wzorca URL — zobaczysz komunikat, wyszarzenie i przycisk, który je przełącza. Samouczek uruchomisz ponownie z menu ustawień.',
      // Font + size context menu (od v2.13.70) — wspólne dla CSS/JS pane + JS Console.
      'panes.contextMenu.font':                 'Font',
      'panes.contextMenu.size':                 'Rozmiar',
      'panes.contextMenu.defaultFont':          'Domyślny',
      'modal.confirmDismissDirty':              'Masz niezapisane zmiany. Zamknąć i utracić?',
      'modal.confirmSaveBeforeClose':           'Masz niezapisane zmiany.\n\nKliknij OK, aby zapisać i zamknąć.\nKliknij Anuluj, aby przejść do potwierdzenia odrzucenia zmian.',
      'modal.confirmDiscardChanges':            'Zamknąć BEZ zapisywania? Zmiany zostaną utracone.\n\nKliknij OK, aby utracić zmiany.\nKliknij Anuluj, aby wrócić do edycji.',
      'actionBars.moveLocked':                  'Nie można przenieść — bar ma elementy, które byłyby poza nowym scope. Najpierw odpowiednio zmień ich przypisanie.',
      'action.bar.noneOption':                  '— bez paska —',
      'action.bar.unnamed':                     '(bez nazwy)',
      'action.bar.selectTitle':                 'Przypisz akcję do paska. Bary dziedziczone w dół (folder → grupa → zestaw).',
      'action.bar.noBarsHint':                  'Brak pasków akcji w drzewie zestawu — dodaj pasek przez przycisk "Paski akcji" w folderze/grupie/zestawie.',

      // === Typed actions (od v2.12.x) ===
      'action.color.title':                     'Kolor tła (button/select/input/slider). Klik = wybór z palety, lub wpisz hex obok.',
      'action.colorText.title':                 'Kolor tekstu (i paska dla SLIDER). Domyślnie biały gdy nie ustawiony.',
      'action.colorHex.title':                  'Kod hex — kopiuj (Ctrl+C) lub wklej (Ctrl+V) jak zwykły tekst. Akceptuje #RGB lub #RRGGBB.',
      'action.type.title':                      'Typ elementu (przycisk, dropdown SELECT, …)',
      'action.type.button':                     'Przycisk',
      'action.type.select':                     'Lista (SELECT)',
      'action.type.input':                      'Pole tekstowe (INPUT)',
      'action.type.slider':                     'Suwak (SLIDER)',
      'action.type.textarea':                   'Pole wielowierszowe (TEXTAREA)',
      'action.type.toggle3':                    'Przełącznik 3-stanowy (TOGGLE3)',

      // Memory scope (od v2.13.0; ruleSet dodany v2.13.4) — wspólne dla state-bearing types
      'action.memory.label':                    'Pamiętaj wartość:',
      'action.memory.none':                     'Nie pamiętaj (reset po reload)',
      'action.memory.page':                     'Per strona (URL)',
      'action.memory.host':                     'Per domena',
      'action.memory.ruleSet':                  'W obrębie zestawu (wszystkie URL gdzie działa)',
      'action.memory.global':                   'Globalnie (wszystkie strony, dzielone)',
      'action.input.middleBtn':                 'INPUT · kod :code',
      'action.slider.middleBtn':                'SLIDER · :min–:max krok :step :unit · kod :code',
      'action.textarea.middleBtn':              'TEXTAREA · :rows wierszy · kod :code',
      'action.toggle3.middleBtn':               'TOGGLE3 · :filled/3 stanów · kod :code',
      'action.toggle3.defaultState1':           'OFF',
      'action.toggle3.defaultState2':           'MID',
      'action.toggle3.defaultState3':           'ON',
      'action.toggle3.stateLabel.placeholder':  'label (max 5)',
      'action.toggle3.stateValue.placeholder':  'value (do JS)',
      'action.toggle3.defaultStateTitle':       'Stan domyślny (gdy brak memory)',

      'action.select.middleBtn.staticEmpty':    'SELECT static (brak opcji)',
      'action.select.middleBtn.static':         'SELECT static · :count opcji · kod :code',
      'action.select.middleBtn.js':             'SELECT js · kod :code',
      'action.select.empty':                    'Brak opcji. Dodaj pierwszą przyciskiem niżej.',
      'action.select.untitled':                 '(bez nazwy)',
      'action.select.optionNamePlaceholder':    'Nazwa (widoczna)',
      'action.select.optionValuePlaceholder':   'value (przekazana do JS)',
      'action.select.deleteOption':             'Usuń tę opcję',

      // === Modal — kod akcji ===
      'modal.actionCode.titlePrefix':           'Kod przycisku akcji:',
      'modal.actionCode.body':                  'Kod JavaScript wykonywany po kliknięciu przycisku akcji w pływającym panelu. Wykonuje się w kontekście strony.',
      'modal.actionCode.cancel':                'Anuluj',
      'modal.actionCode.save':                  'Zapisz',

      // === Modal — edytor INPUT (od v2.13.0) ===
      'modal.inputEditor.titlePrefix':          'Edytor INPUT:',
      'modal.inputEditor.body':                 'Pole tekstowe z opcjonalnym kodem JS uruchamianym przy zmianie (Enter/blur). Value zapamiętywana zgodnie z wybranym scope.',
      'modal.inputEditor.placeholder':          'Placeholder:',
      'modal.inputEditor.defaultValue':         'Wartość domyślna:',
      'modal.inputEditor.colorPlaceholder':     'Kolor placeholder:',
      'modal.inputEditor.codeLabel':            'Kod JS (na change):',
      'modal.inputEditor.hint':                 'Dostępne w kodzie: <code>value</code> (string z input), <code>$el</code> (HTMLInputElement), <code>$action</code> (id, label).',
      'modal.inputEditor.close':                'Zamknij',
      'modal.colorClear':                       'Przywróć kolor domyślny',
      'modal.sliderEditor.colorLabel':          'Kolor nazwy (lewa):',
      'modal.sliderEditor.colorValue':          'Kolor wartości (prawa):',
      'modal.selectOptions.appearance.title':   'Wygląd dropdownu:',
      'modal.selectOptions.appearance.hint':    'Dla <strong>js variant</strong>: tło dropdownu i kolor opcji działają we wszystkich przeglądarkach. Tło wybranej i hover są browser-specific (zależą od OS) — dla pełnej customizacji wybierz <em>static</em> variant.',
      'modal.selectOptions.colorDropdownBg':    'Tło dropdownu:',
      'modal.selectOptions.colorOption':        'Kolor nazw opcji:',
      'modal.selectOptions.colorSelected':      'Tło wybranej opcji:',
      'modal.selectOptions.colorHover':         'Tło hover:',
      'modal.sliderEditor.titlePrefix':         'Edytor SLIDER:',
      'modal.sliderEditor.body':                'Suwak (range) z opcjonalnym kodem JS uruchamianym po zwolnieniu suwaka (`change`). Wartość przekazywana jako liczba (Number).',
      'modal.sliderEditor.min':                 'Minimum:',
      'modal.sliderEditor.max':                 'Maksimum:',
      'modal.sliderEditor.step':                'Krok:',
      'modal.sliderEditor.defaultValue':        'Wartość domyślna:',
      'modal.sliderEditor.unit':                'Jednostka (opcj.):',
      'modal.sliderEditor.codeLabel':           'Kod JS (na change):',
      'modal.sliderEditor.hint':                'Dostępne w kodzie: <code>value</code> (Number), <code>$el</code> (HTMLInputElement type=range), <code>$action</code> (id, label).',
      'modal.sliderEditor.close':               'Zamknij',
      'modal.sliderEditor.errMaxGtMin':         'Maksimum musi być większe od minimum.',
      'modal.sliderEditor.errStepPositive':     'Krok musi być dodatni.',
      'modal.textareaEditor.titlePrefix':       'Edytor TEXTAREA:',
      'modal.textareaEditor.body':              'Multi-line pole tekstowe z opcjonalnym kodem JS na <code>change</code> (blur). <strong>Enter</strong> w textarea to legit newline — NIE odpala kodu (różnica od INPUT).',
      'modal.textareaEditor.placeholder':       'Placeholder:',
      'modal.textareaEditor.defaultValue':      'Wartość domyślna:',
      'modal.textareaEditor.rows':              'Liczba wierszy:',
      'modal.textareaEditor.colorPlaceholder':  'Kolor placeholder:',
      'modal.textareaEditor.codeLabel':         'Kod JS (na change/blur):',
      'modal.textareaEditor.hint':              'Dostępne w kodzie: <code>value</code> (string z textarea, może zawierać <code>\\n</code>), <code>$el</code> (HTMLTextAreaElement), <code>$action</code> (id, label).',
      'modal.textareaEditor.close':             'Zamknij',

      // === Modal — edytor TOGGLE3 (od v2.13.25) ===
      'modal.toggle3Editor.titlePrefix':        'Edytor TOGGLE3:',
      'modal.toggle3Editor.body':               '3-stanowy przełącznik (segmented control). Klik na nieaktywny stan aktywuje go i odpala kod JS z <code>value</code> wybranego stanu. Memory zapisuje aktualny indeks (0-2) per akcja.',
      'modal.toggle3Editor.statesTitle':        'Stany (dokładnie 3, radio = default):',
      'modal.toggle3Editor.colorBg':            'Kolor tła:',
      'modal.toggle3Editor.colorHover':         'Kolor onhover:',
      'modal.toggle3Editor.colorInactiveText':  'Kolor tekstu (nieaktywne):',
      'modal.toggle3Editor.codeLabel':          'Kod JS (na zmianę stanu):',
      'modal.toggle3Editor.hint':               'Dostępne w kodzie: <code>value</code> (state.value, fallback do state.label), <code>stateIdx</code> (0-2), <code>stateLabel</code>, <code>$el</code> (kontener div), <code>$action</code>.',
      'modal.toggle3Editor.close':              'Zamknij',

      // === Modal — edytor SELECT (od v2.12.1, redesign v2.12.2 — warianty) ===
      'modal.selectOptions.titlePrefix':        'Edytor SELECT:',
      'modal.selectOptions.idLabel':            'ID akcji:',
      'modal.selectOptions.selectorLabel':      'Selektor JS:',
      'modal.selectOptions.copyId':             'Kopiuj ID',
      'modal.selectOptions.copySelector':       'Kopiuj selektor JS',
      'modal.selectOptions.variant.static':     'Statyczne opcje + jeden kod JS',
      'modal.selectOptions.variant.js':         'Tylko kod JS (zarządza dropdownem)',
      'modal.selectOptions.static.body':        'Każda opcja: <strong>name</strong> (widoczna w dropdownie) + <strong>value</strong> (przekazana do kodu jako zmienna <code>value</code>). Jeden wspólny kod uruchamiany przy zmianie.',
      'modal.selectOptions.static.hint':        'W kodzie dostępne: <code>value</code> (string), <code>$el</code> (element select), <code>$action</code> (id, label).',
      'modal.selectOptions.staticCodeLabel':    'Kod JS (wspólny):',
      'modal.selectOptions.js.body':            'Twój kod JS jest uruchamiany raz po wstawieniu dropdownu do strony. Dostaje referencję <code>$el</code> do pustego <code>&lt;select&gt;</code> — populujesz opcje i obsługujesz <code>change</code> sam.',
      'modal.selectOptions.js.hint':            'Dostępne: <code>$el</code> (HTMLSelectElement), <code>$action</code> (id, label).',
      'modal.selectOptions.jsCodeLabel':        'Kod JS:',
      'modal.selectOptions.addOption':          '+ Dodaj opcję',
      'modal.selectOptions.close':              'Zamknij',

      // === Modal — konflikt synchronizacji ===
      'modal.syncConflict.title':               'Konflikt synchronizacji',
      'modal.syncConflict.description':         'Zalogowano się do konta, które ma już dane w chmurze, a na tym urządzeniu też masz lokalną bibliotekę. Wybierz, co zrobić — operacja jest nieodwracalna lokalnie, ale lokalne dane można przed zatwierdzeniem wyeksportować.',
      'modal.syncConflict.localLabel':          'Na tym urządzeniu lokalnie:',
      'modal.syncConflict.serverLabel':         'Na koncie chmurowym:',
      'modal.syncConflict.useServer.title':     'Użyj danych z konta chmurowego',
      'modal.syncConflict.useServer.body':      'Lokalne katalogi/grupy/zestawy zostaną zastąpione tymi z konta. Wybierz, jeśli to konto ma nowsze i dokładniejsze dane od tego urządzenia.',
      'modal.syncConflict.sendMine.title':      'Wyślij moje lokalne na konto',
      'modal.syncConflict.sendMine.body':       'Stan na koncie zostanie zastąpiony tym, co masz na tym urządzeniu. Wybierz, jeśli to urządzenie ma nowsze i dokładniejsze dane.',
      'modal.syncConflict.merge.title':         'Połącz oba zestawy',
      'modal.syncConflict.merge.body':          'Encje lokalne i chmurowe zostaną zachowane razem. Jeśli ten sam element jest w obu miejscach (taki sam ID), wygrywa wersja z konta. Lokalne unikalne pójdą na konto przy najbliższej synchronizacji.',
      'modal.syncConflict.exportLocal':         'Najpierw wyeksportuj lokalne',
      'modal.syncConflict.cancel':              'Anuluj logowanie (wyloguj)',

      // === Modal — import z linka share ===
      'modal.shareImport.title':                'Importuj z linka JustZix',
      'modal.shareImport.body':                 'Wklej cały link albo sam token <strong>JZS-XXXX-XXXX-XXXX-XXXX</strong>:',
      'modal.shareImport.placeholder':          'https://www.justzix.com/s/JZS-XXXX-XXXX-XXXX-XXXX',
      'modal.shareImport.cancel':               'Anuluj',
      'modal.shareImport.fetch':                'Pobierz',
      'modal.shareImport.preview.title':        'Podgląd udostępnionego eksportu',
      'modal.shareImport.jsWarn':               '<strong>⚠ UWAGA:</strong> Eksport zawiera kod JavaScript. Kod będzie wykonywać się na stronach pasujących do reguł — sprawdź co robi przed zaimportowaniem.',
      'modal.shareImport.showCode':             'Pokaż kod JavaScript',
      'modal.shareImport.back':                 '← Wstecz',
      'modal.shareImport.confirm':              'Zaimportuj',

      // === Modal — moje share ===
      'modal.shares.title':                     'Moje udostępnione linki',
      'modal.shares.body':                      'Czasowe publiczne linki do eksportów. Backend trzyma payload do wygaśnięcia, potem zostaje skasowany.',
      'modal.shares.howTo':                     'Aby utworzyć nowy link — otwórz eksport (cały / katalog / grupa / zestaw) i kliknij <strong>„Udostępnij URL"</strong>.',
      'modal.shares.refresh':                   'Odśwież',
      'modal.shares.close':                     'Zamknij',

      // === Modal — generowanie share ===
      'modal.shareCreate.title':                'Udostępnij eksport po linku',
      'modal.shareCreate.activeFor':            'Link będzie aktywny przez:',
      'modal.shareCreate.ttl.1h':               '1 godzinę',
      'modal.shareCreate.ttl.6h':               '6 godzin',
      'modal.shareCreate.ttl.12h':              '12 godzin',
      'modal.shareCreate.ttl.24h':              '24 godziny',
      'modal.shareCreate.ttl.default':          '(domyślnie)',
      'modal.shareCreate.ttl.48h':              '48 godzin',
      'modal.shareCreate.expireNote':           'Po wygaśnięciu link przestanie działać. Możesz odwołać go wcześniej w „Moje share".',
      'modal.shareCreate.cancel':               'Anuluj',
      'modal.shareCreate.generate':             'Wygeneruj link',
      'modal.shareCreate.result.title':         'Link wygenerowany',
      'modal.shareCreate.result.urlLabel':      'Wyślij ten link odbiorcy:',
      'modal.shareCreate.result.copyUrl':       'Skopiuj link',
      'modal.shareCreate.result.tokenLabel':    'Lub sam token (do wklejenia w „Importuj z URL"):',
      'modal.shareCreate.result.copyToken':     'Skopiuj token',
      'modal.shareCreate.result.goToMine':      'Zobacz „Moje share"',
      'modal.shareCreate.result.close':         'Zamknij',

      // === Modal — wynik synchronizacji ===
      'modal.syncResult.title':                 'Wynik synchronizacji',
      'modal.syncResult.close':                 'OK, rozumiem',

      // === Modal — synchronizacja chmurowa ===
      'modal.sync.title':                       'Synchronizacja chmurowa',
      'modal.sync.signedOut.tab.register':      'Pierwsze urządzenie',
      'modal.sync.signedOut.tab.login':         'Mam już klucz',
      'modal.sync.signedOut.tab.recover':       'Zgubiłem klucz',
      'modal.sync.signedOut.register.body':     'Wygeneruj nowy klucz synchronizacji. <strong>Zapisz go w bezpiecznym miejscu</strong> — backend trzyma tylko hash. Klucz pozwala zalogować się na drugim urządzeniu i widzieć tę samą bibliotekę.',
      'modal.sync.signedOut.deviceLabel':       'Etykieta urządzenia (opcjonalnie):',
      'modal.sync.signedOut.deviceLabel.placeholder1': 'np. PC robocze',
      'modal.sync.signedOut.deviceLabel.placeholder2': 'np. Laptop',
      'modal.sync.signedOut.yourKey':           'Twój klucz synchronizacji:',
      'modal.sync.signedOut.regenerate':        'Wygeneruj nowy',
      'modal.sync.signedOut.copyClipboard':     'Kopiuj do schowka',
      'modal.sync.signedOut.warnSave':          '⚠ <strong>Skopiuj i zapisz klucz teraz.</strong> Klucz będzie też dostępny w zakładce „Klucz" po rejestracji — ale lepiej mieć kopię w menedżerze haseł.',
      'modal.sync.signedOut.login.body':        'Wklej klucz, który widziałeś przy rejestracji pierwszego urządzenia. Format: <code>SYNC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX</code>.',
      'modal.sync.signedOut.keyLabel':          'Klucz synchronizacji:',
      'modal.sync.signedOut.recover.body':      'Jeśli na koncie był ustawiony i <strong>zweryfikowany</strong> email — wpisz go i wyślij link odzyskiwania. Po kliknięciu linka w mailu dostaniesz <strong>nowy klucz</strong> (jednorazowo). Wszystkie aktywne sesje zostaną wylogowane.',
      'modal.sync.signedOut.email':             'Email konta:',
      'modal.sync.signedOut.email.placeholder': 'ktos@example.com',
      'modal.sync.signedOut.cancel':            'Anuluj',
      'modal.sync.signedOut.regSubmit':         'Zarejestruj urządzenie',
      'modal.sync.signedOut.loginSubmit':       'Zaloguj się',
      'modal.sync.signedOut.recoverSubmit':     'Wyślij link odzyskiwania',
      'modal.sync.signedIn.tab.status':         'Status',
      'modal.sync.signedIn.tab.key':            'Klucz dostępu',
      'modal.sync.signedIn.tab.backups':        'Backupy',
      'modal.sync.signedIn.tab.email':          'Email i recovery',
      'modal.sync.signedIn.status.account':     'Konto',
      'modal.sync.signedIn.status.pending':     'Pending zmian:',
      'modal.sync.signedIn.status.cloudLib':    'Biblioteka w chmurze',
      'modal.sync.signedIn.status.lastCursor':  'Ostatni pull (cursor):',
      'modal.sync.signedIn.status.syncNow':     'Synchronizuj teraz',
      'modal.sync.signedIn.status.snapshot':    'Pełen snapshot',
      'modal.sync.signedIn.status.refresh':     'Odśwież dane konta',
      'modal.sync.signedIn.status.reconcile':   'Pełny resync z kontem',
      'modal.sync.signedIn.status.sessions':    'Aktywne sesje (urządzenia)',
      'modal.sync.signedIn.status.history':     'Historia synchronizacji',
      'modal.sync.history.empty':               'Brak zarejestrowanych synchronizacji.',
      'modal.sync.history.viewAll':             'Pokaż pełną historię',
      'modal.sync.history.changes':             'Zmiany:',
      'modal.sync.history.device':              'Urządzenie:',
      'modal.sync.history.fromDevices':         'Pobrano z:',
      'modal.sync.history.stateAfter':          'Stan po synchronizacji:',
      'modal.sync.history.noDetail':            'Brak szczegółów zmian (starszy wpis).',
      'modal.sync.signedIn.key.body':           'Klucz synchronizacji dla tego konta. Wpisz go na drugim urządzeniu by zsynchronizować bibliotekę.',
      'modal.sync.signedIn.key.yourKey':        'Twój klucz:',
      'modal.sync.signedIn.key.reveal':         'Pokaż klucz',
      'modal.sync.signedIn.key.copy':           'Kopiuj do schowka',
      'modal.sync.signedIn.key.forget':         'Usuń klucz z tego urządzenia',
      'modal.sync.signedIn.key.warn':           '⚠ Klucz daje pełen dostęp do biblioteki. Nie pokazuj go publicznie. Jeśli nie chcesz trzymać go lokalnie — kliknij <strong>Usuń klucz z tego urządzenia</strong> (kopię zachowaj sobie w menedżerze haseł). Logowanie nadal działa, ale nie da się go już podejrzeć.',
      'modal.sync.signedIn.key.noKey':          'Klucz nie jest zapisany na tym urządzeniu (zalogowano jeszcze przed wprowadzeniem tej funkcji albo został ręcznie usunięty). Możesz go odzyskać tylko przez recovery email lub jeśli masz zapisaną kopię gdzie indziej.',
      'modal.sync.signedIn.backups.hint':       'Backupy chmurowe — restore replace całą bibliotekę.',
      'modal.sync.signedIn.backups.label':      'Etykieta nowego backupu (opcjonalnie):',
      'modal.sync.signedIn.backups.placeholder':'np. Przed dużą zmianą',
      'modal.sync.signedIn.backups.create':     'Utwórz backup teraz',
      'modal.sync.signedIn.backups.refresh':    'Odśwież listę',
      'modal.sync.signedIn.email.body':         'Email pozwala na <strong>recovery</strong> — gdy zgubisz klucz, możesz dostać nowy klikając link wysłany na ten adres. Dodanie emaila wymaga weryfikacji (link w mailu).',
      'modal.sync.signedIn.email.add':          'Dodaj email (recovery):',
      'modal.sync.signedIn.email.placeholder':  'ktos@example.com',
      'modal.sync.signedIn.email.submit':       'Wyślij link weryfikacji',
      'modal.sync.signedIn.email.remove':       'Usuń email',
      'modal.sync.signedIn.email.recoveryFlow': 'Recovery flow: na innym urządzeniu (lub w sytuacji bez klucza) wybierz w panelu logowania zakładkę <strong>„Zgubiłem klucz"</strong>, podaj ten email — backend wyśle link, który wygeneruje nowy klucz i unieważni wszystkie aktywne sesje.',
      'modal.sync.signedIn.logout':             'Wyloguj to urządzenie',
      'modal.sync.signedIn.close':              'Zamknij',

      // === Content script (floating button + actions panel) ===
      'content.float.titleGloballyOff':         'Wtyczka globalnie wyłączona — kliknij ikonę wtyczki by włączyć',
      'content.float.titleNormal':              'Lewy klik: włącz/wyłącz katalog • Prawy klik: pokaż listę zestawów • Przeciągnij aby przesunąć',
      'content.panel.empty':                    'Brak aktywnych zestawów',
      'content.panel.actionBarsTitle':          'Paski akcji',
      'content.panel.connectionsTitle':         'Połączenia',
      'content.actionsPanel.dragHandle':        'Przeciągnij aby przesunąć',
      'content.editMode.banner':                'Tryb edycji paska — klik/Ctrl+klik zaznacza, przeciągnięcie przesuwa · Ctrl+Z cofa · Ctrl+Alt+S aby wyjść',
      'content.editMode.undoEmpty':             'Brak zmian do cofnięcia',
      'content.editMode.addLabel':              '+ Label',
      'content.editMode.resetBar':              'Reset paska',
      'content.editMode.resetConfirm':          'Zresetować pasek do układu domyślnego? Custom pozycje, rozmiary i labelki zostaną usunięte.',
      'content.labelEditor.text':               'Tekst:',
      'content.labelEditor.font':               'Font:',
      'content.labelEditor.size':               'Rozmiar:',
      'content.labelEditor.color':              'Kolor:',
      'content.labelEditor.delete':             'Usuń',
      'content.labelEditor.close':              'Zamknij',
      'content.labelContextMenu.delete':        'Usuń etykietę',
      'content.bar.hideUntilReload':            'Zamknij ten pasek (do reloadu)',
      'content.bar.disconnectFrom':             'Rozłącz od: :name',
      'content.bar.disconnectAll':              'Rozłącz wszystkie połączenia',
      'content.bar.fallbackName':               'Pasek',
      'content.float.label':                    '⊕ Główny przycisk',
      'content.toast.noFoldersForPage':         'Brak katalogów dopasowanych do tej strony',
      'content.toast.floatVisible':             'Pływający przycisk włączony',
      'content.toast.floatHidden':              'Pływający przycisk ukryty',

      // === Background (context menu + JS error logger) ===
      'background.contextMenu.toggleFloat':     'Włącz/wyłącz pływający przycisk dla tej strony',
      'background.contextMenu.editMode':        'Tryb edycji paska akcji (włącz/wyłącz)',
      'background.contextMenu.actionBars':      'Paski akcji',
      'background.contextMenu.actionBarsEmpty': '(brak pasków dla tej strony)',
      'background.contextMenu.actionBarFallback': '(bez nazwy)',
      'background.contextMenu.cssPanes':         'Okna CSS',
      'background.contextMenu.cssPanesEmpty':    '(brak okien CSS dla tej strony)',
      'background.contextMenu.cssPaneFallback':  '(bez nazwy)',
      'background.contextMenu.jsPanes':          'Okna JS',
      'background.contextMenu.jsPanesEmpty':     '(brak okien JS dla tej strony)',
      'background.contextMenu.jsPaneFallback':   '(bez nazwy)',
      'background.contextMenu.jsConsoles':       'Konsole JS',
      'background.contextMenu.jsConsolesEmpty':  '(brak konsol dla tej strony)',
      'background.contextMenu.outputConsoles':       'Konsole Output',
      'background.contextMenu.outputConsolesEmpty':  '(brak konsol output dla tej strony)',
      'background.contextMenu.outputConsoleFallback':'(bez nazwy)',
      // TEMP panes (od v2.13.81).
      'background.contextMenu.tempPanes':            'Tymczasowe okno (TEMP)',
      'background.contextMenu.tempCss':              'CSS pane TEMP (Ctrl+Alt+G)',
      'background.contextMenu.tempJs':               'JS pane TEMP (Ctrl+Alt+H)',
      'background.contextMenu.tempJsConsole':        'JS Console TEMP (Ctrl+Alt+J)',
      'background.contextMenu.tempOutput':           'Output Console TEMP (Ctrl+Alt+K)',
      'pane.collapse.title':                      'Zwiń okno do paska tytułu',
      'pane.expand.title':                        'Rozwiń okno',
      'aiHelper.form.submit':                     'OK',
      'aiHelper.form.cancel':                     'Anuluj',
      'aiHelper.form.yes':                        'Tak',
      'aiHelper.form.no':                         'Nie',
      'aiHelper.form.askTitle':                   'Pytanie',
      'aiHelper.form.confirmTitle':               'Potwierdzenie',
      'aiHelper.form.updateConfirmTitle':               'Zaktualizować?',
      'aiHelper.style.title':                           'Wygląd AI Helper',
      'aiHelper.style.role.user':                       'Twoje wiadomości',
      'aiHelper.style.role.assistant':                  'Odpowiedzi AI',
      'aiHelper.style.role.tool':                       'Wynik narzędzia',
      'aiHelper.style.role.form':                       'Formularze',
      'aiHelper.style.font':                            'Font',
      'aiHelper.style.size':                            'Rozmiar',
      'aiHelper.style.color':                           'Tekst',
      'aiHelper.style.bg':                              'Tło',
      'aiHelper.style.reset':                           'Przywróć domyślne',
      'aiHelper.style.resetAll':                        'Przywróć wszystkie domyślne',
      'aiHelper.zorder.up':                             'Ustaw wyżej',
      'aiHelper.zorder.down':                           'Ustaw niżej',
      'aiHelper.confirm.update.folder':                 'Aktualizacja katalogu',
      'aiHelper.confirm.update.group':                  'Aktualizacja grupy',
      'aiHelper.confirm.update.rule':                   'Aktualizacja zestawu',
      'aiHelper.confirm.update.bar':                    'Aktualizacja paska akcji',
      'aiHelper.confirm.update.action':                 'Aktualizacja akcji',
      'aiHelper.confirm.update.cssPane':                'Aktualizacja okna CSS',
      'aiHelper.confirm.update.jsPane':                 'Aktualizacja okna JS',
      'aiHelper.confirm.update.jsConsole':              'Aktualizacja konsoli JS',
      'aiHelper.confirm.update.outputConsole':          'Aktualizacja Output Console',
      'aiHelper.form.createConfirmTitle':         'Utworzyć?',
      'aiHelper.form.confirmed':                  'potwierdzone',
      'aiHelper.form.cancelled':                  'anulowane',
      'tempPane.close.title':                        'Zamknij tymczasowe okno (zawartość zachowana w sesji)',
      'tempPane.clear.title':                        'Wyczyść zawartość okna',
      'background.contextMenu.jsConsoleFallback':'(bez nazwy)',
      'background.jsError.prefix':              '[JustZix] Błąd JS w zestawie/akcji ":id":\n',
      // === AI Helper (od v2.13.127) ===
      'settings.aiHelper.label': 'AI Helper',
      'settings.aiHelper.title': 'Skonfiguruj klucze API dla AI Helper',
      'modal.aiHelper.title': 'AI Helper',
      'modal.aiHelper.intro': 'Wprowadź klucze API dostawców, z których chcesz korzystać. Wystarczy jeden.',
      'modal.aiHelper.keyOpenai': 'Klucz API — OpenAI',
      'modal.aiHelper.keyAnthropic': 'Klucz API — Anthropic',
      'modal.aiHelper.keyGemini': 'Klucz API — Gemini',
      'modal.aiHelper.defaultProvider': 'Domyślny dostawca',
      'modal.aiHelper.defaultModel': 'Domyślny model',
      'modal.aiHelper.modelAuto': '— auto (najwyższy) —',
      'modal.aiHelper.loadModels': 'Pobierz modele',
      'modal.aiHelper.securityNote': '<strong>Bezpieczeństwo:</strong> klucze API są domyślnie przechowywane wyłącznie w pamięci lokalnej tej przeglądarki (<code>chrome.storage.local</code>). Zaznaczenie „synchronizuj" przy kluczu wysyła ten klucz przez chmurę JustZix (tę samą, którą wtyczka synchronizuje pozostałe dane) na inne Twoje urządzenia. Klucze bez zaznaczenia nigdy nie opuszczają tego urządzenia.',
      'modal.aiHelper.syncKey':                'synchronizuj',
      'modal.aiHelper.syncKeyTitle': 'Synchronizuj ten klucz między urządzeniami przez chmurę JustZix',
      'modal.aiHelper.cancel': 'Anuluj',
      'modal.aiHelper.save': 'Zapisz',
      'modal.aiHelper.needKey': 'Najpierw wprowadź klucz API.',
      'modal.aiHelper.loadingModels': 'Pobieranie modeli…',
      'modal.aiHelper.errModels': 'Nie udało się pobrać modeli',
      'modal.aiHelper.modelsLoaded': 'modeli dostępnych',
      'aiHelper.win.title': 'AI Helper',
      'aiHelper.win.close': 'Zamknij',
      'aiHelper.win.provider': 'Dostawca',
      'aiHelper.win.model': 'Model',
      'aiHelper.win.clear': 'Wyczyść rozmowę',
      'aiHelper.win.clearConfirm': 'Wyczyścić całą rozmowę i zacząć od nowa?',
      'aiHelper.win.inputPlaceholder': 'Zapytaj o pomoc z CSS, JS lub akcją…',
      'aiHelper.win.send': 'Wyślij (Ctrl+Enter)',
      'aiHelper.win.welcome': 'Cześć! Pomogę Ci przygotować CSS, JavaScript lub akcje dla tej strony. Opisz, co chcesz osiągnąć.',
      'aiHelper.win.you': 'Ty',
      'aiHelper.win.assistant': 'AI Helper',
      'aiHelper.win.thinking': 'Myślę…',
      'aiHelper.win.error': 'Błąd połączenia z API',
      'aiHelper.win.noKeys': 'Brak kluczy API. Dodaj klucz w Ustawieniach → AI Helper.',
      'aiHelper.win.noModel': 'Wybierz model.',
      'aiHelper.win.loadingModels': 'Ładowanie…',
      'aiHelper.win.modelsError': 'Błąd pobierania modeli',
      'background.contextMenu.aiHelper': 'AI Helper',
      'popup.aiHelper.label': 'AI Helper',
      'popup.aiHelper.on': 'Otwarty — kliknij aby zamknąć (Ctrl+Alt+\\)',
      'popup.aiHelper.off': 'Zamknięty — kliknij aby otworzyć (Ctrl+Alt+\\)',
      'popup.aiHelper.unavailable': 'Niedostępny na tej stronie',
      // === AI Helper etap 2 (od v2.13.128) ===
      'aiHelper.win.maxRounds': 'Osiągnięto limit kroków narzędzi w jednej turze. Napisz, jak kontynuować.',
      'aiHelper.win.copy':                        'Kopiuj rozmowę',
      'aiHelper.win.copy.all':                    'Kopiuj całą rozmowę',
      'aiHelper.win.copy.lastInput':              'Kopiuj ostatni prompt API (system + historia + input)',
      'aiHelper.win.copyNoInput':                 'Brak inputu do skopiowania',
      'aiHelper.copy.lastInput.prefix':           '[JustZix → przekaż do API] Skopiowano CAŁY ostatni prompt wysłany do modelu (system + historia + ostatnia wiadomość). Wyślij poniższy blok do API jako PIERWSZĄ user-message nowej rozmowy. USUŃ ten nagłówek przed wysłaniem.',
      'aiHelper.win.copied':                      'Skopiowano',
      'aiHelper.win.copyFailed':                  'Nie udało się skopiować — sprawdź uprawnienia do schowka.',
      'aiHelper.win.font':                        'Font',
      'aiHelper.win.fontSize':                    'Rozmiar',
      'aiHelper.win.defaultFont':                 'Domyślny',
      'aiHelper.win.stop':                        'Zatrzymaj agenta',
      'aiHelper.win.stopped':                     'Przerwano przez użytkownika.',
      'aiHelper.win.ctxUsage':                    'Wykorzystanie kontekstu modelu',
      'aiHelper.win.ctxUsageDetail':              'Kontekst',
      'aiHelper.win.compact':                     'Kompaktuj rozmowę (podsumowanie LLM)',
      'aiHelper.win.compactNoop':                 'Historia jest za krótka — nie ma czego kompaktować.',
      'aiHelper.win.compactNoProvider':           'Wybierz najpierw dostawcę i model.',
      'aiHelper.win.compactErr':                  'Błąd kompaktowania',
      'aiHelper.win.compactEmpty':                'Model zwrócił pusty wynik.',
      'aiHelper.win.compactedPrefix':             'Zsumowano',
      'aiHelper.win.compactedSuffix':             'wcześniejszych wiadomości:',
      'aiHelper.win.telemetryTooltip':            'Czasy i tokeny tej sesji rozmowy',
      'aiHelper.win.telemetryTimers':             'Czas: od ostatniej wiadomości (u) / od ostatniego narzędzia (t) / suma aktywności agenta (a)',
      'aiHelper.win.telemetryCurrent':            'Tokeny w aktualnym działaniu (↑ wejście / ↓ wyjście)',
      'aiHelper.win.telemetrySession':            'Tokeny łącznie w tej sesji',
      'aiHelper.win.msgTelem':                    'Tokeny tej rundy i czas odpowiedzi modelu',
      'aiHelper.win.toolDur':                     'Czas wykonania narzędzia',
      'aiHelper.win.stepIdBadge':                 'Numer kroku w historii',
      'aiHelper.form.freeText':                   'Lub odpowiedz tekstem',
      'aiHelper.form.freeTextOnly':               'Twoja odpowiedź',
      'aiHelper.form.freeTextPlaceholder':        'Wpisz odpowiedź…',
      'aiHelper.win.turnLimit.body':              'Osiągnięto limit 10 kroków narzędzi w tej turze. Agent zatrzymał się — sprawdź ostatnie narzędzie i jego wynik. Kontynuować od tego punktu z resetem limitu, czy zatrzymać?',
      'aiHelper.win.turnLimit.lastTool':          'Ostatnie narzędzie',
      'aiHelper.win.turnLimit.lastResult':        'Wynik',
      'aiHelper.win.turnLimit.continue':          'Kontynuuj (reset limitu)',
      'aiHelper.win.turnLimit.stop':              'Zatrzymaj',
      'aiHelper.win.turnLimit.stoppedByUser':     'Zatrzymano.',
      'aiHelper.copy.title':                      'Rozmowa JustZix AI Helper',
      'aiHelper.copy.toolResult':                 'Wynik narzędzia',
      'aiHelper.tool.label': 'narzędzie',
      'aiHelper.tool.rejected': 'Użytkownik odrzucił operację.',
      'aiHelper.confirm.folder': 'AI Helper chce utworzyć FOLDER:',
      'aiHelper.confirm.group': 'AI Helper chce utworzyć GRUPĘ:',
      'aiHelper.confirm.rule': 'AI Helper chce utworzyć ZESTAW:',
      'aiHelper.confirm.create.cssPane': 'AI Helper chce utworzyć OKNO CSS:',
      'aiHelper.confirm.create.jsPane': 'AI Helper chce utworzyć OKNO JS:',
      'aiHelper.confirm.create.jsConsole': 'AI Helper chce utworzyć KONSOLĘ JS:',
      'aiHelper.confirm.create.outputConsole': 'AI Helper chce utworzyć OUTPUT CONSOLE:',
      'aiHelper.confirm.setPaneCode': 'AI Helper chce wstawić kod do okna „:name” (:id).\nDługość: obecnie :old zn., nowy :new zn.\nUWAGA: kod okna persistent żyje per-karta (sessionStorage), nie między sesjami.',
      'aiHelper.confirm.bar': 'AI Helper chce utworzyć PASEK AKCJI:',
      'aiHelper.confirm.action': 'AI Helper chce utworzyć AKCJĘ:',
      // === Wyszukiwanie w kodzie (od v2.13.130) ===
      'codeSearch.placeholder': 'Szukaj w kodzie… (Ctrl+Alt+F)',
      'codeSearch.btn': 'Szukaj',
      'codeSearch.prev': 'Poprzedni wynik (Ctrl+Alt+←)',
      'codeSearch.next': 'Następny wynik (Ctrl+Alt+→)',
      'codeSearch.noMatch': 'Brak wyników',
    },
    en: {
      // === Dashboard (options-page home, since v2.13.148) ===
      'dashboard.title': 'Dashboard',
      'dashboard.subtitle': 'An overview of your JustZix library and quick shortcuts.',
      'dashboard.stats.title': 'Library',
      'dashboard.stats.folders': 'Folders',
      'dashboard.stats.groups': 'Groups',
      'dashboard.stats.rules': 'Rule sets',
      'dashboard.stats.actions': 'Actions',
      'dashboard.stats.bars': 'Action bars',
      'dashboard.stats.windows': 'In-page windows',
      'dashboard.stats.withCss': 'Sets with CSS',
      'dashboard.stats.withJs': 'Sets with JS',
      'dashboard.stats.clicks': 'Action clicks',
      'dashboard.version.title': 'Extension version',
      'dashboard.version.checking': 'Checking…',
      'dashboard.version.current': 'Installed: :version',
      'dashboard.version.upToDate': 'You are up to date',
      'dashboard.version.outdated': 'A newer version is available: :version',
      'dashboard.storage.title': 'Storage',
      'dashboard.backup.title': 'Last backup',
      'dashboard.backup.signIn': 'Sign in to cloud sync to create cloud backups.',
      'dashboard.backup.none': 'No cloud backups yet.',
      'dashboard.backup.noLabel': '(no label)',
      'dashboard.backup.last': 'Last backup: :date',
      'dashboard.recent.title': 'Recently edited rule sets',
      'dashboard.recent.empty': 'No rule set has been edited yet.',
      'dashboard.examples.title': 'Latest examples',
      'dashboard.posts.title': 'From the blog',
      'dashboard.feed.loading': 'Loading…',
      'dashboard.feed.empty': 'Nothing to show yet.',
      'dashboard.home.title': 'Dashboard',
      'dashboard.usage.title': 'Usage stats',
      'dashboard.usage.cssApplied': 'CSS applied',
      'dashboard.usage.jsExecuted': 'JS executed',
      'dashboard.usage.windowsShown': 'Windows shown',
      'dashboard.usage.empty': 'No data yet — use the extension on web pages.',
      'dashboard.tip.title': 'Tip',
      'dashboard.tip.prev': 'Previous tip',
      'dashboard.tip.next': 'Next tip',
      'dashboard.tip.manual': 'Open the full manual',
      'tip.dash.1': 'Right-click the floating button to open the panel with the list of folders and rule sets.',
      'tip.dash.2': 'Ctrl+Alt+S toggles action-bar edit mode — elements can be freely positioned and resized.',
      'tip.dash.3': 'Each rule set can have its own URL patterns that narrow it down to specific subpages.',
      'tip.dash.4': 'Ctrl+Alt+\\ opens AI Helper — an assistant that generates CSS/JS for a specific task.',
      'tip.dash.5': 'The code editors have a search bar — Ctrl+Alt+F moves focus to it.',
      'tip.dash.6': 'Left-clicking the floating button enables or disables all matching folders at once.',
      'tip.dash.7': 'A rule set keeps its last 5 CSS and JS versions in history — you can always undo a change.',
      'tip.dash.8': 'Drag a rule set onto another group or folder name to move it there.',
      'tip.dash.9': 'TEMP windows (Ctrl+Alt+G/H/J/K) are throwaway CSS/JS panes and consoles for quick tests.',
      'tip.dash.10': 'SELECT, INPUT, SLIDER and TOGGLE3 actions remember their value — it survives a page reload.',
      'tip.dash.11': 'Cloud sync keeps your library consistent across devices.',
      'tip.dash.12': 'Instead of sending a JSON file, share a folder via a link (Shares) with an expiry time.',
      'tip.dash.13': 'The sidebar search box quickly finds a folder, group or rule set by name.',
      'tip.dash.14': 'Add tags to rule sets and filter your library by tag as your projects grow.',
      'tip.dash.15': 'A three-letter label and a colour make a folder\'s floating button stand out on the page.',
      'tip.dash.16': 'The "URL" button drops the active tab\'s address straight into a URL pattern — no copy-paste.',
      'tip.dash.17': 'JustZix validates the URL patterns you type as you go.',
      'tip.dash.18': 'The editor warns you about CSS syntax errors before you save a rule set.',
      'tip.dash.19': 'When importing, you can preview the JavaScript code before deciding to add it.',
      'tip.dash.20': 'Disabling a folder cascades down and switches off all its groups and rule sets.',
      'tip.dash.21': 'The "Active only" filter hides disabled items when your library gets large.',
      'tip.dash.22': 'Drag an action bar next to another to snap them into one connected unit.',
      'tip.dash.23': 'The JS Console window works like a REPL — test code snippets live on the page.',
      'tip.dash.24': 'The Output Console window captures the page\'s console.log entries and shows them on screen.',
      'tip.dash.25': 'You can export a single folder, group or rule set — not just the whole library.',
      'tip.dash.26': 'JustZix adds its own entries to the browser\'s right-click context menu.',
      'tip.dash.27': 'Drag the divider between code editor panels with the mouse to fit the view to your needs.',
      'tip.dash.28': 'The extension notifies you on its own when a newer version is available.',
      'tip.dash.29': 'Clicking the extension icon opens a widget for quickly toggling folders and bars.',
      'tip.dash.30': 'A rule set\'s JavaScript can trigger bar actions through the built-in JustZix API.',
      'tip.dash.31': 'Ctrl+Enter runs the code or sends the message — in the JS pane, JS Console and AI Helper.',
      'tip.dash.32': 'In the JS Console the ↑ / ↓ arrows scroll through the history of typed commands.',
      'tip.dash.33': 'Ctrl+L clears the JS Console output without closing the window.',
      'tip.dash.34': 'Ctrl+Shift+L clears the active tab of every open Output Console.',
      'tip.dash.35': 'Ctrl+Shift+H opens the API help window in the Output Console.',
      'tip.dash.36': 'In the code editor Ctrl+Alt+→ and Ctrl+Alt+← jump to the next and previous search result.',
      'tip.dash.37': 'The Esc key clears the search field in the Output Console.',
      'tip.dash.38': 'Double-clicking a TEMP window\'s header bar toggles it between the default and the last size.',
      'tip.dash.39': 'A TEMP window remembers its last size per type and keeps it across a browser restart.',
      'tip.dash.40': 'Scrolling with the wheel inside an in-page window no longer scrolls the page underneath.',
      'tip.dash.41': 'Every in-page window has 8 resize handles — 4 in the corners and 4 on the edges.',
      'tip.dash.42': 'A right click on a window header opens a menu with close, disconnect, font and size options.',
      'tip.dash.43': 'Drag a window close to another — they snap together magnetically into one group.',
      'tip.dash.44': 'The Output Console has 6 tabs: All, JZ, Console, Network, Errors and DataLayer.',
      'tip.dash.45': 'On the Output Console Network tab, Alt+click on a row adds its domain to the include filter.',
      'tip.dash.46': 'Alt+Shift+click on a Network row adds the domain to the exclude filter.',
      'tip.dash.47': 'Each Output Console tab keeps its own independent set of filters.',
      'tip.dash.48': 'On the DataLayer tab, drag the divider between the columns to resize them.',
      'tip.dash.49': 'In bar edit mode the "+ Label" button adds static text labels.',
      'tip.dash.50': 'Double-click a bar label to open the editor for its text, font, size and color.',
      'tip.dash.51': 'In bar edit mode, Shift+drag over an empty area selects elements with a rectangle.',
      'tip.dash.52': 'In bar edit mode, Ctrl+click adds or removes an element from the selection.',
      'tip.dash.53': 'Click an action element\'s number badge to copy its stable ID to the clipboard.',
      'tip.dash.54': 'A rule set can be set to re-run its JS on UI changes on SPA-type pages.',
      'tip.dash.55': 'Beyond BUTTON, SELECT and INPUT, actions also have SLIDER, TEXTAREA and TOGGLE3 types.',
      'tip.dash.56': 'The light/dark theme and the interface language sync as a shared account setting.',
      'tip.dash.57': 'The usage stats on the Dashboard sum data from all paired devices.',
      'tip.dash.58': 'The bar below the code editor can be dragged down to enlarge the working area.',
      'tip.dash.59': 'The vertical bar between the sidebar and the editor collapses the left panel for more room.',
      'tip.dash.60': 'The home icon in the Dashboard header and in the breadcrumb returns straight to the Dashboard.',
      // === Popup ===
      'popup.toggle.on.label':       'Plugin enabled',
      'popup.toggle.on.status':      'Injection active',
      'popup.toggle.off.label':      'Plugin disabled',
      'popup.toggle.off.status':     'No CSS/JS is being injected',
      'popup.editMode.label':        'Action bar edit mode',
      'popup.editMode.on':           'On — click to turn off (Ctrl+Alt+S)',
      'popup.editMode.off':          'Off — click to turn on (Ctrl+Alt+S)',
      'popup.editMode.unavailable':  'Not available on this page',
      'popup.empty.title':           'No rule folders match this page.',
      'popup.empty.hint':            'Add them in edit mode.',
      'popup.empty.folderRules':     'No active rules in this folder',
      'popup.fallback.groupName':    'Group',
      'popup.fallback.unnamed':      '(unnamed)',
      'popup.restore':               'Restore floating button (:count)',
      'popup.editBtn':               'Edit panel',
      'popup.actionBarsTitle':       'Action bars',
      'popup.fallback.barName':      'Bar',
      'popup.hideForPage':           'Hide floating button for this page',
      'popup.showForPage':           'Show floating button for this page',

      // === Options — page ===
      'options.title':                          'JustZix — Editor',

      // === Options — sidebar ===
      'sidebar.folders.title':                  'Folders',
      'sidebar.addFolder.label':                'Add folder',
      'sidebar.search.placeholder':             'Search (name, tag, code content)',
      'sidebar.clearSearch.label':              'Clear',
      'sidebar.activeOnly.label':               'Active only',
      'sidebar.activeOnly.title':               'Hide independent rule sets (marked ○) — show only rules active in their folder (●)',
      'sidebar.expandAll.label':                'Expand entire tree',
      'sidebar.collapseAll.label':              'Collapse entire tree',
      'sidebar.toggle.title':                   'Collapse/expand folder panel',

      // === Options — settings menu ===
      'settings.menu.label':                    'Settings',
      'settings.theme.label':                   'Theme',
      'settings.theme.title':                   'Pick a theme: light / dark / auto',
      'settings.help.label':                    'User manual',
      'settings.tutorial.label':                'Tutorial',
      'settings.export.label':                  'Export settings',
      'settings.import.label':                  'Import settings',
      'settings.importUrl.label':               'Import from URL',
      'settings.importUrl.title':               'Import an export from a JustZix link (JZS-XXXX-XXXX-XXXX-XXXX)',
      'settings.sync.label':                    'Cloud sync',
      'settings.shares.label':                  'My shares',
      'settings.shares.title':                  'List of issued time-limited public links to exports',
      'settings.projectSite.label':             'Project website',
      'settings.projectSite.title':             'Open justzix.com in a new tab',
      'settings.lang.label':                    'Language',
      'settings.lang.title':                    'Extension UI language',
      'settings.lang.auto':                     'Auto',
      'settings.lang.autoTitle':                'Detect from browser (default)',
      'toast.langChanged':                      'Language set: :lang',
      'lang.name.auto':                         'Auto',
      'lang.name.pl':                           'Polish',
      'lang.name.en':                           'English',
      'settings.version.label':                 'Version',
      'settings.version.checkTitle':            'Click to check for latest version (force-refresh, bypasses 30 min cache)',
      'settings.version.checking':              'Checking…',
      'settings.update.available':              'New version available: v:version ⬇',
      'settings.update.notReady':               '(package not yet released)',
      'settings.syncStatusDot.title':           'Cloud sync — not signed in',

      // === Options — folder editor ===
      'folder.icon.title':                      'Floating button label (max 3 letters, UPPERCASE). Default: ZIX',
      'folder.color.title':                     'Folder color (optional)',
      'folder.colorClear.title':                'Clear color',
      'folder.name.placeholder':                'Folder name (e.g. justzix.com)',
      'folder.url.placeholder':                 'URL pattern (e.g. https://justzix.com/* )',
      'folder.urls.label':                      'Folder URL patterns:',
      'folder.urls.title':                      'Folder URL patterns — at least one must match for any rule in this folder to run',
      'folder.urls.placeholder':                'https://justzix.com/*  (Enter to add)',
      'folder.insertUrl.title':                 'Insert active tab URL',
      'folder.active.label':                    'Active',
      'folder.active.title':                    'Active — when unchecked, the folder (and all its groups/rules) are forcefully disabled and the toggle is locked',
      'folder.enabled.label':                   'Enabled',
      'folder.enabled.title':                   'Enabled — current state of the folder (cascades to active groups and rules)',
      'folder.actionsPanel.label':              'Actions panel',
      'folder.actionsPanel.title':              'Show floating panel with action buttons for this folder',
      'folder.layout.label':                    'Layout:',
      'folder.layout.title':                    'Action panel button layout',
      'folder.layout.vertical':                 'Vertical',
      'folder.layout.horizontal':               'Horizontal',
      'folder.addGroup.label':                  '+ Group',
      'folder.addGroup.title':                  'Add a new group in this folder',
      'folder.export.label':                    'Export',
      'folder.export.title':                    'Export this folder',
      'folder.import.label':                    'Import',
      'folder.import.title':                    'Import a folder from file',
      'folder.delete.label':                    'Delete folder',

      // === Options — group editor ===
      'group.parentLabel':                      'Group in folder:',
      'folder.label':                           'Folder:',
      'group.label':                            'Group:',
      'group.name.placeholder':                 'Group name',
      'group.urls.label':                       'Group URL patterns:',
      'group.urls.title':                       'Additional group URL patterns — extra restriction layered on top of the folder pattern, applies to all rules in the group',
      'group.urls.placeholder':                 'https://example.com/admin/*  (Tab = autocomplete from folder, Enter = add)',
      'group.active.label':                     'Active',
      'group.active.title':                     'Active — when unchecked, the group (and all its rules) are forcefully disabled and the toggle is locked',
      'group.enabled.label':                    'Enabled',
      'group.enabled.title':                    'Enabled — current state of the group (cascades to active rules)',
      'group.addRule.label':                    '+ Add rule set',
      'group.export.title':                     'Export this group',
      'group.import.title':                     'Import rule sets into this group',
      'group.move.label':                       'Move',
      'group.move.title':                       'Move group to another folder',
      'group.duplicate.label':                  'Duplicate',
      'group.duplicate.title':                  'Duplicate group with rule sets',
      'group.delete.label':                     'Delete group',

      // === Options — rule editor ===
      'rule.label':                             'Rule set:',
      'rule.back.label':                        'Back to group',
      'rule.export.title':                      'Export this rule set to file',
      'rule.share.label':                       'Share URL',
      'rule.share.title':                       'Share this rule set as a time-limited public link',
      'rule.import.title':                      'Import rule set (will replace current)',

      // === Options — empty state ===
      'empty.title':                            'Select or add a folder',
      'empty.body':                             'Each folder groups CSS/JS rule sets for one URL pattern.',

      // === Default names for newly created entities (options.js) ===
      'default.groupName':                      'Default',
      'default.folderName':                     'New folder',
      'default.ruleName':                       'New rule set',

      // === Plural noun forms used by toasts ===
      // EN has 2 forms (one/other), PL has 4 — both filled, Intl.PluralRules picks correctly.
      'plural.rule.one':                        'rule set',
      'plural.rule.few':                        'rule sets',
      'plural.rule.many':                       'rule sets',
      'plural.rule.other':                      'rule sets',
      'plural.folder.one':                      'folder',
      'plural.folder.few':                      'folders',
      'plural.folder.many':                     'folders',
      'plural.folder.other':                    'folders',
      'plural.group.one':                       'group',
      'plural.group.few':                       'groups',
      'plural.group.many':                      'groups',
      'plural.group.other':                     'groups',
      'plural.action.one':                      'action',
      'plural.action.few':                      'actions',
      'plural.action.many':                     'actions',
      'plural.action.other':                    'actions',
      'plural.jsLine.one':                      'JS line',
      'plural.jsLine.few':                      'JS lines',
      'plural.jsLine.many':                     'JS lines',
      'plural.jsLine.other':                    'JS lines',
      'toast.importedRulesToGroup':             'Imported :count :word into group',
      'toast.exportedRules':                    'Exported :count :word',

      // === Sidebar — dynamic render ===
      'sidebar.empty.folders':                  'No folders',
      'sidebar.empty.activeFolders':            'No active folders',
      'sidebar.folder.toggleHint':              'Enable/disable entire folder',
      'sidebar.folder.inactiveHint':            'Folder inactive — unlock it in the folder editor',
      'sidebar.group.toggleHint':               'Enable/disable group',
      'sidebar.group.inactiveHint':             'Group inactive — unlock it in the group editor',
      'sidebar.rule.urlHint':                   'URL restriction at rule set level',
      'sidebar.rule.activeInFolder':            'Active in folder',
      'sidebar.rule.inactiveInFolder':          'Inactive in folder',
      'sidebar.fallback.folderName':            '(folder)',
      'sidebar.fallback.groupName':             '(group)',
      'sidebar.fallback.unnamed':               '(no name)',
      'sidebar.flag.css':                       'Contains CSS',
      'sidebar.flag.js':                        'Contains JavaScript',
      'sidebar.flag.actions':                   'Contains actions',
      'sidebar.flag.groupUrls':                 'Group has URL restriction',

      // === Folder/Group/Rule editor — dynamic ===
      'rules.empty.folder':                     'No rule sets in this folder. Add a group and create the first rule set in it, or click a group in the sidebar.',
      'rules.empty.group':                      'No rule sets in this group. Add the first one by clicking "+ Add rule set".',
      'rule.namePlaceholder':                   'Rule set name',
      'rule.activeTooltip':                     'Active — whether this rule is included when toggling the entire folder',
      'rule.urls.tooltip':                      'Additional URL patterns — when set, the rule fires only on subpages matching at least one pattern (in addition to the folder pattern)',
      'rule.urls.placeholder.empty':            'https://example.com/admin/*  (Tab — insert folder pattern, Enter — add)',

      // === Move menu ===
      'moveMenu.section.moveGroup':             'Move group to',
      'moveMenu.section.moveRule':              'Move to group',
      'moveMenu.section.copyRule':              'Copy to group',
      'moveMenu.empty.folders':                 'No other folders',
      'moveMenu.empty.groups':                  'No other groups',

      // === Confirm dialogs — dynamic ===
      'confirm.deleteFolderWithStats':          'Delete folder ":name" along with :groups and :rules?',
      'confirm.deleteFolder':                   'Delete folder ":name"?',
      'confirm.deleteGroupWithRules':           'Delete group ":name" along with :count :word?',
      'confirm.deleteGroup':                    'Delete group ":name"?',
      'confirm.replacePartial':                 'Replace current :before with partial backup (:after)?\n\nAll data outside the file will be lost. The operation is irreversible.',
      'confirm.overwriteAll':                   'Overwrite :stats with current data from file?\n\nThis operation is irreversible.',

      // === Default name for newly created group ===
      'default.newGroupName':                   'New group',

      // === Export — summary + states ===
      'export.summary.full':                    ':icon Full backup: :stats + global settings',
      'export.summary.partial':                 ':icon Partial backup: :stats',
      'export.empty.folder':                    'No folders to export',
      'export.empty.group':                     'Group is empty — no rule sets to export',
      'export.summary.toExport':                ':icon To export: :count :word',
      'export.summary.noneSelected':            ':icon No rule sets selected',

      // === Import detection (banners) ===
      'import.detection.invalid':               '<strong style="color:#dc2626;">:icon Invalid format:</strong> missing folders list (<code>folders</code>).',
      'import.detection.partialContains':       'Contains: :stats',
      'import.detection.willReplace':           ':stats',

      // === Inline labels ===
      'inline.fileGroupLabel':                  'group',
      'shares.empty':                           'You have no active links.<br><br>To create one — open an export (full / folder / group / rule set)<br>and click <strong>"Share URL"</strong>.',

      // === Share preview / export types ===
      'shareType.folder':                       'Single folder',
      'shareType.group':                        'Single group',
      'shareType.rule':                         'Single rule set',
      'shareType.full':                         'Full export',

      // === Owner suffix when importing from share ===
      'shareImport.suffix.group':               'group',
      'shareImport.suffix.rule':                'rule set',

      // === Rule import — prompt with options ===
      'prompt.ruleConflict':                    'You are importing ":new". What to do with the current rule set ":current"?\n\n  1 or "replace"  — Replace the current rule set\n  2 or "copy"     — Add as a new rule set next to the current one (in the same group)\n\nEnter 1 or 2:',
      'prompt.folderConflicts':                 'Detected :count :word with the same ID as existing:\n:names\n\nEnter one of:\n  1 or "overwrite"  — Overwrite existing folders (deletes their current groups and rule sets)\n  2 or "copy"       — Import as copies (new IDs, "(import)" in folder name)\n  Cancel            — Abort import\n\nChoice:',

      // === Group editor — additional ===
      'group.empty.noGroupsInFolder':           'No groups in folder',

      // === Folder import — conflict messages ===
      'folderImport.conflict.sameId':           'The imported folder has the same ID as existing "<strong>:name</strong>" (:count :word). Choose action:',
      'folderImport.conflict.sameName':         'A folder named "<strong>:name</strong>" already exists (:count :word). Choose action:',

      // === Dynamic button states ===
      'btn.shortcutPress':                      '⌨ press Ctrl+Alt+Shift + a key (Esc)',
      'btn.generating':                         'Generating…',
      'btn.importing':                          'Importing…',
      'btn.showKey':                            'Show key',
      'btn.hideKey':                            'Hide key',

      // === Rule editor — placeholders + tooltips ===
      'rule.editor.urlsPlaceholder':            'https://example.com/admin/*  (Tab — insert folder pattern, Enter — add)',
      'rule.editor.urlsPlaceholderWithPattern': ':pattern  (Tab — insert, Enter — add)',
      'rule.editor.toggleHint':                 'Enable/disable this rule set',
      'rule.editor.toggleLockedHint':           'Rule set is inactive — unlock by checking Active',
      'sidebar.fallback.ruleName':              '(rule set)',
      'sidebar.fallback.ruleUnnamed':           '(unnamed rule set)',
      'confirm.deleteRule':                     'Delete rule set ":name"?',

      // === Rule card (rendered by createRuleCard / renderActionsList) ===
      'rule.card.tagsTitle':                    'Tags (comma-separated)',
      'rule.card.activeLabel':                  'Active',
      'rule.card.enabledLabel':                 'Enabled',
      'rule.card.history.title':                'Change history',
      'rule.card.history.label':                'History',
      'rule.card.move.label':                   'Move',
      'rule.card.move.btnTitle':                'Move rule set to another group',
      'rule.card.copy.label':                   'Copy',
      'rule.card.copy.btnTitle':                'Copy rule set to another group',
      'rule.card.duplicate.label':              'Duplicate',
      'rule.card.delete.label':                 'Delete',
      'rule.card.urlsLabel':                    'Rule set URL patterns:',
      'rule.card.tab.actions':                  'Actions',
      'rule.card.lineCount':                    'Lines of code',
      'rule.card.js.runOnce.label':             'Run JS only once',
      'rule.card.js.runOnce.title':             'Do not re-run JS after SPA navigation (URL change without page reload). Recommended for scripts that add global listeners, intervals or patch window.',
      'rule.card.actions.info':                 'Action buttons appear in the floating panel ("Actions panel" toggle in the folder). Max 4-char shortcut, custom color, JS code runs on click.',
      'rule.card.actions.add':                  '+ Add action button',
      'rule.card.actions.addWithCount':         '+ Add action button (:count)',
      'rule.card.action.visibleLabel':          'visible',
      'rule.card.duplicate.suffix':             '(copy)',
      'rule.card.action.codePreview':           '(:count :word) :first',
      'rule.card.action.fallback.unlabeled':    '(no label)',
      'rule.card.fallbackName':                 'Rule set',

      // === Folder export modal — additional ===
      'export.empty.foldersToExport':           'No folders to export',
      'export.summary.toExportFolders':         ':icon To export: :groupCount :groupWord, :ruleCount :ruleWord',
      'export.summary.invalidFolderFile':       'Invalid file format — expected single folder export.',
      'export.summary.noConflicts':             ':icon No conflicts. Folder "<strong>:name</strong>" with :count :word will be imported as new.',
      'export.summary.fileContains':            '<strong style="color:#374151;">:icon File contents (:count :word):</strong>',
      'export.summary.jsHeader':                ':icon Rule set JavaScript:',
      'export.summary.emptyRule':               'empty rule set',
      'toast.exportedFolder':                   'Exported folder (:groupCount :groupWord, :ruleCount :ruleWord)',
      'inline.fileFolderLabel':                 'folder',
      'inline.fileRuleLabel':                   'rule set',
      'default.newGroupNameInline':             'New group',

      // === Sync conflict modal counts ===
      'syncConflict.countLine':                 ':folders :foldersWord / :groups :groupsWord / :rules :rulesWord',

      // === Backup restore confirm ===
      'confirm.restoreBackup':                  'Restore backup #:id?\n\nALL current folders/groups/rule sets will be replaced with what is in the backup. Other devices will sync on next pull.\n\nIrreversible (apart from creating a new backup BEFORE restore).',

      // === Loaders / placeholders ===
      'loading.shares':                         'Loading…',
      'loading.generic':                        'Loading…',

      // === Sidebar — drag & arrows tooltips ===
      'sidebar.dragHandle.title':               'Drag to reorder',
      'sidebar.expandCollapse.title':           'Expand/collapse',
      'sidebar.folderActive.title':             'Active',
      'sidebar.folderInactive.title':           'Inactive — forcefully disabled',
      'sidebar.groupActive.title':              'Active',
      'sidebar.groupInactive.title':            'Inactive — forcefully disabled',
      'sidebar.searchNoResults':                'No results for ":query"',

      // === Theme ===
      'settings.theme.toggleToDark':            'Switch to dark mode',
      'settings.theme.toggleToLight':           'Switch to light mode',
      'settings.theme.labelDark':               'Dark mode',
      'settings.theme.labelLight':              'Light mode',
      'settings.theme.labelAuto':               'Auto (system)',
      'settings.theme.toggleToAuto':            'Switch to auto mode',

      // === URL chip remove ===
      'btn.removeUrlChip':                      'Remove',

      // === CSS validator ===
      'cssValidator.unclosedComment':           'Unclosed comment /* ... */',
      'cssValidator.missingCloseBraces':        'Unclosed braces: missing :count :word }',
      'plural.brace.one':                       'closing brace',
      'plural.brace.few':                       'closing braces',
      'plural.brace.many':                      'closing braces',
      'plural.brace.other':                     'closing braces',
      'cssValidator.extraCloseBrace':           'Extra closing brace } (line :line)',
      'jsValidator.unterminatedString':         'Unterminated string literal (line :line)',
      'jsValidator.unclosedBracket':            'Unclosed bracket :bracket (line :line)',
      'jsValidator.extraBracket':               'Extra closing bracket :bracket (line :line)',

      // === Action buttons ===
      'action.shortcut.empty':                  'click + press shortcut',
      'action.code.empty':                      'click to add code…',
      'action.stats.title':                     'Click count in actions panel',
      'action.visible.title':                   'Visible in actions panel (unchecked = hidden, but not removed)',
      'action.shortcut.title':                  'Keyboard shortcut — click and press a combination. Esc cancels. The shortcut works on matching pages regardless of panel visibility.',
      'action.shortcut.titleAfterClick':        'Keyboard shortcut. Click and press a combination to change. Esc cancels. × removes.',
      'action.shortcut.removeBtn':              'Remove keyboard shortcut',
      'action.delete.btn':                      'Delete action button',
      'action.menu.btn.duplicate':              'Duplicate action here',
      'action.menu.btn.move':                   'Move action to another rule set',
      'action.menu.btn.copy':                   'Copy action to another rule set',
      'action.menu.section.moveTo':             'Move to rule set...',
      'action.menu.section.copyTo':             'Copy to rule set...',
      'action.menu.empty.rules':                'No other rule sets',
      'action.menu.toast.moved':                'Action moved to ":target"',
      'action.menu.toast.copied':               'Action copied to ":target"',
      'action.shortcut.conflictTitle':          'Conflict: this shortcut is also used by :count :word:\n:list\n\nWhen pressed, only the first one in render order will fire.',
      'action.shortcut.conflictItem':           '• ":action" in ":rule" (:folder)',
      'plural.actionAlt.one':                   'other action',
      'plural.actionAlt.few':                   'other actions',
      'plural.actionAlt.many':                  'other actions',
      'plural.actionAlt.other':                 'other actions',
      'plural.more.one':                        'other',
      'plural.more.few':                        'others',
      'plural.more.many':                       'more',
      'plural.more.other':                      'more',
      'action.shortcut.conflictMore':           ' (and :count :word)',
      'action.shortcut.conflictToast':          'Conflict: this shortcut is already used by action ":actionLabel" in ":ruleName":more. Only one of them will fire.',
      'confirm.deleteAction':                   'Delete action button ":label"?',

      // === History ===
      'history.header':                         'Last :count saved versions. Click "Restore" to revert to that version (the current one will be saved to history).',
      'history.modalTitle':                     'Change history: ":name"',
      'history.version':                        'Version :n',
      'history.restore':                        'Restore',
      'history.close':                          'Close',

      // === Export — additional ===
      'export.summary.noElements':              ':icon No elements selected for export',
      'export.summary.toExportFolderShort':     ':icon To export: :groupCount :groupWord, :ruleCount :ruleWord',
      'export.summary.noneSelectedShort':       ':icon No rule sets selected',

      // === Import detection ===
      'import.detection.parseError':            '<strong style="color:#dc2626;">:icon JSON parse error:</strong> :msg',
      'import.detection.partialBackup':         '<strong style="color:#5b21b6;">:icon Partial backup (selected elements)</strong><br>',
      'import.detection.fullBackup':            '<strong style="color:#006870;">:icon Full settings backup:legacy</strong><br>',
      'import.detection.legacySuffix':          ' (older format)',
      'import.detection.willOverwrite':         '<br><br><strong style="color:#b91c1c;">:icon Current settings will be overwritten:</strong>',
      'import.detection.exportFirst':           '<br><span style="color:#7f1d1d;font-size:11px;">Recommended: export <strong>before</strong> importing.</span>',
      'import.detection.folder':                '<strong style="color:#155e75;">:icon Folder export</strong> "<strong>:name</strong>"<br><span style="color:#155e75;">Contains: :stats</span>',
      'import.detection.group':                 '<strong style="color:#155e75;">:icon Group export</strong> "<strong>:name</strong>"<br><span style="color:#155e75;">Contains: :stats</span>',
      'import.detection.rule':                  '<strong style="color:#155e75;">:icon Rule set export</strong> "<strong>:name</strong>"',

      // === Import — target selectors ===
      'import.target.folder.title':             'Where to place the imported folder?',
      'import.target.folder.newCopy':           'Add as a new top-level folder',
      'import.target.folder.overwrite':         'Overwrite existing folder ":name" (keeps ID — sync sees it as an edit)',
      'import.target.folder.mergeInto':         'Merge groups/rule sets into an existing folder:',
      'import.target.group.title':              'Where to place the imported group?',
      'import.target.group.existingFolder':     'Add to an existing folder:',
      'import.target.group.newFolder':          'Create a new folder for this group',
      'import.target.rule.title':               'Where to place the imported rule set?',
      'import.target.rule.existingGroup':       'Add to an existing group:',
      'import.target.rule.newFolder':           'Create a new folder and group for this rule set',

      // === Share errors (create flow) ===
      'share.error.unknown':                    'Unknown error',
      'share.error.shareFailed':                'You already have 20 active links or exceeded 50 MB. Revoke old ones in "My shares".',
      'share.error.badRequest':                 'Failed to generate link — check that the export is valid. :msg',
      'share.error.rateLimited':                'Too many links created in the last hour (limit: 30). Try again in a moment.',
      'share.error.invalidToken':               'Session expired — sign in again in the sync panel and try once more.',
      'share.error.serverError':                'Server problem. Try again in a moment.',
      'share.error.fallback':                   'Error: :msg',
      'share.error.tooLarge':                   'Export too large — backend allows max 5 MB. Try sharing a smaller part.',

      // === Share subject (create modal) ===
      'share.subject.partial':                  'Partial library export',
      'share.subject.full':                     'Full library export',

      // === Time relative formatting (fmtRelHuman) ===
      'rel.lessThanMinute':                     'less than a minute',
      'rel.min':                                ':n min',
      'rel.hMin':                               ':h h :m min',
      'rel.h':                                  ':h h',
      'rel.dH':                                 ':d d :h h',
      'rel.d':                                  ':d d',

      // === Shares list ===
      'shares.expired':                         'expired',
      'shares.fetchFailed':                     'Failed to fetch list: :msg',
      'shares.confirmRevoke':                   'Revoke share #:id?\n\nThe link will stop working immediately — recipients will get 404 on the next fetch attempt.\n\nNOTE: if someone has already downloaded the payload, they will have a local copy — revocation does not pull back already-downloaded content.',
      'shares.revoked':                         'Share #:id revoked',

      // === Share preview details ===
      'sharePreview.senderLabel':               'Sender:',
      'sharePreview.senderAnonymous':           'anonymous',
      'sharePreview.expiresLabel':              'Expires:',
      'sharePreview.expiresValue':              'in :rel',
      'sharePreview.typeLabel':                 'Type:',
      'sharePreview.sizeLabel':                 'Size:',
      'sharePreview.contentLabel':              'Contents:',
      'sharePreview.cssLabel':                  'CSS:',
      'sharePreview.cssValueChars':             ':n chars',
      'sharePreview.jsLabel':                   'JavaScript:',
      'sharePreview.downloadsLabel':            'Downloads:',
      'sharePreview.codePreviewTitle':          'Code preview from shared export',

      // === Share import errors (preview) ===
      'shareImport.error.unknown':              'Unknown error',
      'shareImport.error.badRequest':           'Token is invalid. Check that you copied the full link.',
      'shareImport.error.notFound':             'This link expired or was revoked by sender. Ask for a new one.',
      'shareImport.error.rateLimited':          'Too many attempts. Wait a moment and try again.',
      'shareImport.error.serverError':          'Server problem. Try again.',
      'shareImport.error.fallback':             'Error: :msg',
      'shareImport.error.invalidTokenFormat':   'Invalid token format',

      // === Rule import ===
      'ruleImport.unknownFormat':               'Unknown or unsupported export format: :type',
      'prompt.ruleConflict.body':               'You are importing ":new". What to do with the current rule set ":current"?\n\n  1 or "replace"  — Replace current with file contents\n  2 or "copy"     — Add as a new rule set next to the current (in the same group)\n  Cancel          — Abort import\n\nChoice:',

      // === Folder export ===
      'folderExport.exportedToast':             'Folder exported (:groupCount :groupWord, :ruleCount :ruleWord)',

      // === Folder import — details ===
      'folderImport.parseError':                'JSON parse error: :msg',
      'folderImport.invalidFormat':             'Invalid file format — expected single folder export.',
      'folderImport.noConflicts':               ':icon No conflicts. Folder "<strong>:name</strong>" with :count :word will be imported as new.',
      'folderImport.conflictSameId':            'The imported folder has the same ID as existing "<strong>:name</strong>" (:count :word). Choose action:',
      'folderImport.conflictSameName':          'A folder named "<strong>:name</strong>" already exists (:count :word). Choose action:',
      'folderImport.fileContents':              '<strong style="color:#374151;">:icon File contents (:count :word):</strong>',
      'folderImport.jsHeader':                  ':icon Rule set JavaScript:',
      'folderImport.codePreviewTitle':          'Code preview: :name',
      'folderImport.previewJsBtn.title':        'Preview JS code',

      // === Sync — toasts + status messages ===
      'sync.toast.synced':                      'Synced: :applied changes, :deletes deletions',
      'sync.rel.justNow':                       'just now',
      'sync.tooltip.syncing':                   'Syncing…',
      'sync.tooltip.error':                     'Sync: error. Last successful: :last',
      'sync.tooltip.connected':                 'Cloud sync — connected. Last: :last',
      'sync.error.copyManually':                'Failed to copy — select and Ctrl+C manually.',
      'sync.error.registerFailed':              'Registration error: :msg',
      'sync.error.unknown':                     'unknown',
      'sync.error.invalidKeyFormat':            'Invalid key format — expecting SYNC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX.',
      'sync.status.sendingLocal':               'Sending :count local entities to account.',
      'sync.status.cancelled':                  'Sign-in cancelled — signed out. Local data untouched.',
      'sync.status.sendingAndDeleting':         'Sending :local local entities, deleting :remote from account…',
      'sync.status.merged':                     'Merged. Local-unique (:count items) will go to account on next sync.',
      'sync.error.firstSyncFailed':             'Login OK but first sync failed: :msg',
      'sync.error.loginFailed':                 'Login error: :msg',
      'sync.error.invalidEmail':                'Enter a valid email.',
      'sync.recovery.sent':                     'If this email is registered, we sent a recovery link. Check your inbox.',
      'sync.error.generic':                     'Error: :msg',
      'sync.account.thisDevice':                'this device: :label',
      'sync.session.noLabel':                   '(no label)',
      'sync.session.thisDeviceSuffix':          ' (this device)',
      'sync.session.revoke':                    'Revoke',
      'sync.error.revokeFailed':                'Revoke error: :msg',
      'sync.session.fetchFailed':               'Failed to fetch session list: :msg',
      'sync.key.copyManual':                    'Failed to copy — click "Show key" and Ctrl+C manually.',
      'sync.key.removed':                       'Key removed from local storage.',
      'sync.backups.empty':                     'No cloud backups. Create the first one by clicking "Create backup now".',
      'sync.backups.fetchFailed':               'Failed to fetch list: :msg',
      'sync.backups.creating':                  'Creating cloud backup…',
      'sync.backups.createFailed':              'Backup creation error: :msg',
      'sync.backups.restored':                  'Restored backup #:id. Replaced :count entities. newCursor=:cursor.',
      'sync.backups.restoreFailed':             'Restore error: :msg',
      'sync.backups.downloadFailed':            'Download error: :msg',
      'sync.backups.confirmDelete':             'Delete backup #:id from cloud? Irreversible.',
      'sync.backups.deleted':                   'Backup #:id deleted.',
      'sync.backups.deleteFailed':              'Delete error: :msg',
      'sync.pushPullSummary':                   'Push: :pushedAccepted accepted, :pushedRejected rejected • Pull: :applied entities, :deletes deletions.',
      'sync.returned':                          'Sync returned: :result',
      'sync.snapshotFailed':                    'Snapshot error: :msg',
      'sync.reconcile.running':                 'Full resync with account…',
      'sync.reconcile.okPushed':                'Full resync OK — pushed :count missing entities to the account.',
      'sync.reconcile.okInSync':                'Full resync OK — library matches the account.',
      'sync.reconcile.failed':                  'Full resync failed: :msg',
      'sync.email.saveButFailed':               'Email saved, but link sending failed — try again.',
      'sync.email.verifyLinkSent':              'Verification link sent to :email. Check your inbox and click the link.',
      'sync.email.removed':                     'Email removed.',

      // === Sync result modal — strategies ===
      'syncResult.merge.title':                 'Merged with cloud',
      'syncResult.merge.desc':                  'Entities present in both places were overwritten with the account version. Local-unique ones remain and will go to the account on next sync.',
      'syncResult.merge.tableHeader':           '<table class="result-table"><thead><tr><th>Type</th><th>From cloud: overwritten</th><th>From cloud: new</th><th>Local kept → push</th><th>Total after merge</th></tr></thead><tbody>',
      'syncResult.useServer.title':             'Replaced local with cloud data',
      'syncResult.useServer.desc':              'Local library was completely replaced with the account state. Local-unique entities (if any) were lost.',
      'syncResult.useServer.tableHeader':       '<table class="result-table"><thead><tr><th>Type</th><th>Fetched from cloud</th><th>Local lost</th><th>Common (overwritten)</th><th>Total after sync</th></tr></thead><tbody>',
      'syncResult.sendMine.title':              'Sending local to account',
      'syncResult.sendMine.desc':               'Local library will be pushed to the account. Entities present in cloud but missing locally — will be marked as deleted (tombstone) on the account.',
      'syncResult.sendMine.tableHeader':        '<table class="result-table"><thead><tr><th>Type</th><th>Sent (push)</th><th>Overwrites on account</th><th>New on account</th><th>Deleted from account</th></tr></thead><tbody>',
      'syncResult.fallback.title':              'Sync completed',
      'syncResult.empty':                       'No details.',
      'syncResult.label.folders':               'Folders',
      'syncResult.label.groups':                'Groups',
      'syncResult.label.rules':                 'Rule sets',
      'syncResult.label.actions':               'Actions',

      // === Options — toasts (bottom notifications) ===
      'toast.saved':                            'Saved',
      'toast.cantDeleteLastGroup':              'Cannot delete the last group in a folder',
      'toast.noOtherTab':                       'No other tab with a page URL',
      'toast.urlInserted':                      'Inserted URL from tab',
      'toast.urlAlreadyAdded':                  'Pattern already on the list: :pattern',
      'toast.error':                            'Error: :msg',
      'toast.shortcutMustIncludeMod':           'Allowed shortcut: Ctrl+Alt+Shift + a letter (except G H J K L), a digit or a symbol (except \\).',
      'toast.shortcutCollidesChrome':           'Warning: shortcut collides with a Chrome shortcut (e.g. new tab). It may not work — choose another.',
      'toast.copied':                           'Copied: :text',
      'toast.copyFailed':                       'Copy failed',
      'toast.copyClipboard':                    'Copied to clipboard',
      'toast.copyLink':                         'Link copied',
      'toast.copyToken':                        'Token copied',
      'toast.noHistory':                        'No history for this rule set',
      'toast.versionRestored':                  'Version restored',
      'toast.fileNotFullImport':                'File is not suitable for full import',
      'toast.unknownOption':                    'Unknown option — import cancelled',
      'toast.importedSettings':                 'Settings imported',
      'toast.parseError':                       'Parse error: :msg',
      'toast.invalidPartialFormat':             'Invalid format — expected group, folder or rule set export',
      'toast.groupImport.folderRejected':       'This is a folder export (multiple groups). Import it via folder-level import, not group-level.',
      'toast.exportedRule':                     'Rule set exported',
      'toast.signInFirst':                      'Sign in first in the sync panel',
      'toast.exportNotReady':                   'Export is not ready',
      'toast.noRulesSelected':                  'No rule sets selected',
      'toast.revokeError':                      'Revoke error: :msg',
      'toast.importedFromLink':                 'Imported from link',
      'toast.invalidRuleFormat':                'Invalid file format — expected rule set export',
      'toast.importedReplaced':                 'Imported (replaced)',
      'toast.importedAsCopy':                   'Imported as copy',
      'toast.noImportData':                     'No data to import',
      'toast.importedFolder':                   'Folder imported: :name',
      'toast.importedGroup':                    'Group imported: :name',
      'toast.importedRule':                     'Rule set imported: :name',

      // === Options — native confirm() dialogs ===
      'confirm.restoreVersion':                 'Restore this version? Current content will be added to history as a new snapshot.',
      'confirm.revokeSession':                  'Revoke this session?',
      'confirm.removeKey':                      'Remove the key from this device?\n\nSign-in still works (the session is independent from the key), but you will not be able to view or copy the key to another device from this computer.\n\nMake sure you have a copy of the key saved elsewhere (password manager).',
      'confirm.snapshot':                       'Full snapshot downloads the entire library from the cloud and replaces the local state. Your pending changes will NOT be lost (they remain in the queue). Continue?',
      'confirm.removeEmail':                    'Remove the email from the account? Without email, you will not be able to recover from a lost key.',
      'confirm.signOut':                        'Sign out this device? Local library stays, but stops synchronizing.',

      // === Modal — full export ===
      'modal.export.title':                     'Export settings',
      'modal.export.mode.all':                  'Everything',
      'modal.export.mode.select':               'Selected folders/rules',
      'modal.export.selectAll':                 'Select all',
      'modal.export.deselectAll':               'Deselect all',
      'modal.export.hint':                      'You can copy the JSON to clipboard or download the file:',
      'modal.export.close':                     'Close',
      'modal.export.share':                     'Share URL',
      'modal.export.share.title':               'Share this export as a time-limited public link',
      'modal.export.download':                  'Download file',
      'modal.export.copy':                      'Copy to clipboard',

      // === Modal — full import ===
      'modal.import.title':                     'Import settings',
      'modal.import.pickerHint':                'Paste JSON from any JustZix export (full backup, folder, group or rule set) — the form will detect the type and offer placement options.',
      'modal.import.warningStrong':             'Warning:',
      'modal.import.warningRest':               ' a full import will overwrite all current settings.',
      'modal.import.placeholder':               'Paste JSON here or choose a file...',
      'modal.import.cancel':                    'Cancel',
      'modal.import.import':                    'Import',

      // === Modal — folder export ===
      'modal.folderExport.titlePrefix':         'Export folder:',
      'modal.folderExport.hint':                'Select groups/rules to export.',
      'modal.folderExport.cancel':              'Cancel',
      'modal.folderExport.share':               'Share URL',
      'modal.folderExport.shareTitle':          'Share this folder as a time-limited public link',
      'modal.folderExport.download':            'Download file',

      // === Modal — group export ===
      'modal.groupExport.titlePrefix':          'Export group:',
      'modal.groupExport.hint':                 'Select rule sets to export.',
      'modal.groupExport.cancel':               'Cancel',
      'modal.groupExport.share':                'Share URL',
      'modal.groupExport.shareTitle':           'Share this group as a time-limited public link',
      'modal.groupExport.download':             'Download file',

      // === Modal — folder import ===
      'modal.folderImport.title':               'Import folder',
      'modal.folderImport.placeholder':         'Paste folder JSON or choose a file...',
      'modal.folderImport.conflicts':           'Conflicts detected:',
      'modal.folderImport.action.rename':       'Import as a new folder (with "(import)" suffix)',
      'modal.folderImport.action.overwrite':    'Overwrite existing folder',
      'modal.folderImport.action.merge':        'Merge — add new rule sets to existing folder (keeping both)',
      'modal.folderImport.cancel':              'Cancel',
      'modal.folderImport.import':              'Import',

      // === Modal — action code ===
      // === Action bars (Phase 2 of multi-bars system, since v2.9) ===
      'actionBars.btn.label':                   'Action bars',
      'actionBars.btn.title':                   'Manage action bars assigned to this folder',
      'actionBars.btn.titleGroup':              'Manage action bars assigned to this group',
      'actionBars.btn.titleRule':               'Manage action bars assigned to this rule set',
      'actionBars.parentType.folder':           'Folder',
      'actionBars.parentType.group':            'Group',
      'actionBars.parentType.rule':             'Rule set',
      'actionBars.parentType.folder.short':     '[F]',
      'actionBars.parentType.group.short':      '[G]',
      'actionBars.parentType.rule.short':       '[R]',
      'actionBars.empty':                       'No action bars at this level. Click "+ Add new bar" to create the first one.',
      'actionBars.section.owned':               'Bars at this level',
      'actionBars.section.fromFolder':          '↑ Inherited from folder "<strong>:name</strong>"',
      'actionBars.section.fromGroup':           '↑ Inherited from group "<strong>:name</strong>"',
      'actionBars.elementsCount':               'Elements: :count',
      'actionBars.enabled':                     'Enabled',
      'actionBars.name.placeholder':            'NAME',
      'actionBars.name.title':                  'Label at the edge of the bar (max 6 chars, UPPERCASE). Empty = no label.',
      'actionBars.color.title':                 'Bar background color',
      'actionBars.orientation.title':           'Click to toggle bar orientation (vertical ↔ horizontal)',
      'actionBars.color.clear':                 'Clear background color',
      'actionBars.moveTo':                      'Move to:',
      'actionBars.delete':                      'Delete',
      'actionBars.confirmDelete':               'Delete this bar?',
      'actionBars.confirmDeleteWithElements':   'Delete this bar? Assigned elements (:count) will keep their definitions but lose the bar assignment — you will need to assign them to another bar manually.',
      'actionBars.deleteMigrate.message':       'This bar has :count assigned elements. Choose where to move them before deleting (or detach them from any bar):',
      'actionBars.deleteMigrate.detachOption':  '— detach elements (no bar) —',
      'actionBars.deleteMigrate.confirm':       'Move and delete',
      'actionBars.deleteMigrate.cancel':        'Cancel',
      'modal.actionBars.titlePrefix':           'Action bars —',
      'modal.actionBars.hint':                  'Bars assigned to this level. Folder bars aggregate elements from all active rule sets in the folder, group bars — only from the group, rule-set bars — only when the rule set is active. A bar with no visible elements will not be rendered.',
      'modal.actionBars.add':                   '+ Add new bar',
      'modal.actionBars.close':                 'Close',

      // === Windows — dropdown type selector (since v2.13.41) ===
      'windows.btn.label':                      'Windows',
      'windows.btn.title':                      'Manage windows assigned to this folder (CSS / JS / consoles)',
      'windows.btn.titleGroup':                 'Manage windows assigned to this group',
      'windows.btn.titleRule':                  'Manage windows assigned to this rule set',
      'windows.menu.title':                     'Choose window type:',
      'windows.menu.comingSoon':                'coming soon',
      'jsPanes.btn.label':                      'JS panes',
      'jsConsoles.btn.label':                   'JS consoles',
      'outputConsoles.btn.label':               'Output consoles',

      // === CSS panes (Phase 2 "Windows on the front", since v2.13.40) ===
      'cssPanes.btn.label':                     'CSS panes',
      'cssPanes.contextMenu.disable':           'Close CSS pane',
      'cssPanes.btn.title':                     'Manage CSS panes assigned to this folder',
      'cssPanes.btn.titleGroup':                'Manage CSS panes assigned to this group',
      'cssPanes.btn.titleRule':                 'Manage CSS panes assigned to this rule set',
      'cssPanes.empty':                         'No CSS panes at this level. Click "+ Add new CSS pane" to create the first one.',
      'cssPanes.section.owned':                 'Panes at this level',
      'cssPanes.name.placeholder':              'Pane name',
      'cssPanes.color.title':                   'Header dot color',
      // Custom pane colors (since v2.13.58) — shared CSS / JS pane.
      'panes.style.dot':                        'Dot',
      'panes.style.headerBg':                   'Header bg',
      'panes.style.headerText':                 'Header text',
      'panes.style.bodyBg':                     'Body bg',
      'panes.style.bodyText':                   'Body text',
      'panes.style.runBtn':                     'Run button',
      'panes.style.reset':                      'Clear',
      'mcp.default.title':                      'Restore default',
      // Action color labels (since v2.13.60) — short, no instructions.
      'action.colors.bg':                       'Background',
      'action.colors.text':                     'Text',
      'action.colors.dropdownBg':               'Dropdown bg',
      'action.colors.option':                   'Option',
      'action.colors.selected':                 'Selected',
      'action.colors.hover':                    'Hover',
      'action.colors.label':                    'Label',
      'action.colors.value':                    'Value',
      'action.colors.placeholder':              'Placeholder',
      'action.colors.bgInactive':               'Inactive bg',
      'action.colors.inactiveText':             'Inactive text',
      'cssPanes.enabled.label':                 'Enabled',
      'cssPanes.enabled.title':                 'Whether the pane should appear on matching pages',
      'cssPanes.delete.title':                  'Delete this pane',
      'cssPanes.delete.confirm':                'Delete CSS pane ":name"? Textarea content in active tabs will be lost.',
      'modal.cssPanes.titlePrefix':             'CSS panes —',
      'modal.cssPanes.hint':                    'Floating textarea injected on pages matching this level\'s URLs. Content kept in sessionStorage per tab (lost when tab closes). Live <style> injection — CSS changes apply instantly.',
      'modal.cssPanes.add':                     '+ Add new CSS pane',
      'modal.cssPanes.close':                   'Close',
      'cssPane.frontend.close.title':           'Hide pane (will stay hidden until tab closes)',

      // === JS panes (Phase 2 cont., since v2.13.56) ===
      'jsPanes.contextMenu.disable':            'Close JS pane',
      'jsPanes.btn.title':                      'Manage JS panes assigned to this folder',
      'jsPanes.btn.titleGroup':                 'Manage JS panes assigned to this group',
      'jsPanes.btn.titleRule':                  'Manage JS panes assigned to this rule set',
      'jsPanes.empty':                          'No JS panes at this level. Click "+ Add new JS pane" to create the first one.',
      'jsPanes.section.owned':                  'Panes at this level',
      'jsPanes.name.placeholder':               'Pane name',
      'jsPanes.color.title':                    'Header dot color',
      'jsPanes.enabled.label':                  'Enabled',
      'jsPanes.enabled.title':                  'Whether the pane should appear on matching pages',
      'jsPanes.delete.title':                   'Delete this pane',
      'jsPanes.delete.confirm':                 'Delete JS pane ":name"? Textarea content in active tabs will be lost.',
      'modal.jsPanes.titlePrefix':              'JS panes —',
      'modal.jsPanes.hint':                     'Floating textarea executing JS in the page context (MAIN world, new Function) on each change. Content kept per-tab in sessionStorage. Execution errors shown in red overlay below the code.',
      'modal.jsPanes.add':                      '+ Add new JS pane',
      'modal.jsPanes.close':                    'Close',
      'jsPane.frontend.close.title':            'Hide pane (will stay hidden until tab closes)',

      // === JS Consoles (Phase 2 cont., since v2.13.66) ===
      'jsConsoles.contextMenu.disable':         'Close JS Console',
      'jsConsoles.empty':                       'No consoles at this level. Click "+ Add new console" to create the first one.',
      'jsConsoles.section.owned':               'Consoles at this level',
      'jsConsoles.name.placeholder':            'Console name',
      'jsConsoles.enabled.label':               'Enabled',
      'jsConsoles.enabled.title':               'Whether the console should appear on matching pages',
      'jsConsoles.delete.title':                'Delete this console',
      'jsConsoles.delete.confirm':              'Delete console ":name"? Output history in active tabs will be lost.',
      'modal.jsConsoles.titlePrefix':           'JS Consoles —',
      'modal.jsConsoles.hint':                  'Interactive REPL — Ctrl+Enter runs code in page MAIN world, output (or error) appends to log area below the input. Output kept per-tab in sessionStorage.',
      'modal.jsConsoles.add':                   '+ Add new console',
      'modal.jsConsoles.close':                 'Close',
      'jsConsole.frontend.close.title':         'Hide console (until tab closes)',
      'jsConsole.input.placeholder':            'Code to run (Ctrl+Enter to execute, Ctrl+L to clear)',
      'jsConsole.clear.title':                  'Clear output (Ctrl+L)',

      // === Output Console (Phase 2 finish, since v2.13.72) — read-only log viewer. ===
      'panes.style.clearBtn':                   'Clear button',
      'panes.style.actionBtns':                 'Action buttons',
      'outputConsoles.contextMenu.disable':     'Close Output Console',
      'outputConsoles.empty':                   'No output consoles at this level. Click "+ Add new output console" to create the first one.',
      'outputConsoles.section.owned':           'Output consoles at this level',
      'outputConsoles.name.placeholder':        'Output console name',
      'outputConsoles.enabled.label':           'Enabled',
      'outputConsoles.enabled.title':           'Whether the output console should appear on matching pages',
      'outputConsoles.delete.title':            'Delete this output console',
      'outputConsoles.delete.confirm':          'Delete output console ":name"? Log history in active tabs will be lost.',
      'modal.outputConsoles.titlePrefix':       'Output Consoles —',
      'modal.outputConsoles.hint':              'Read-only log viewer — captures console.log/warn/error/info from the page (including async). Clear (⌫) only clears the viewer, not the browser console.',
      'modal.outputConsoles.add':               '+ Add new output console',
      'modal.outputConsoles.close':             'Close',
      'outputConsole.frontend.close.title':     'Hide output console (until tab closes)',
      'outputConsole.clear.title':              'Clear viewer (Ctrl+Shift+L)',
      'outputConsole.autoscroll.title':         'Auto-scroll to latest logs',
      'outputConsole.empty':                    '(no logs — waiting for console.* / JZ.* from the page)',
      // viewMode + filters + search (since v2.13.73, per spec section 6).
      'outputConsole.mode.standard.title':      'Show console.* only (click / Ctrl+Shift+K → JZ.*)',
      'outputConsole.mode.jz.title':            'Show window.JZ.* only (click / Ctrl+Shift+K → both)',
      'outputConsole.mode.both.title':          'Show both: console.* + JZ.* (click / Ctrl+Shift+K → console.* only)',
      'outputConsole.filters.title':            'Level filters',
      'outputConsole.filters.level.log':        'log',
      'outputConsole.filters.level.info':       'info',
      'outputConsole.filters.level.warn':       'warn',
      'outputConsole.filters.level.error':      'error',
      'outputConsole.filters.level.debug':      'debug',
      'outputConsole.search.title':             'Search in logs (Ctrl+Shift+S; Esc clears)',
      'outputConsole.search.placeholder':       'Filter entries…',
      'outputConsole.tab.all':                  'All',
      'outputConsole.tab.jz':                   'JZconsole',
      'outputConsole.tab.console':              'Console',
      'outputConsole.tab.network':              'Network',
      'outputConsole.tab.errors':               'Errors',
      'outputConsole.tab.datalayer':            'DataLayer',
      'outputConsole.net.field.callTime':       'Call time',
      'outputConsole.net.field.status':         'Status',
      'outputConsole.net.field.type':           'Type',
      'outputConsole.net.field.method':         'Method',
      'outputConsole.net.field.domain':         'Domain',
      'outputConsole.net.field.path':           'Path',
      'outputConsole.net.field.remote':         'Remote address',
      'outputConsole.net.field.initiator':      'Initiator',
      'outputConsole.net.field.time':           'Time',
      'outputConsole.net.field.size':           'Size',
      'outputConsole.net.field.url':            'URL',
      'outputConsole.net.field.error':          'Error',
      'outputConsole.net.status.err':           'Errors/Others',
      'outputConsole.filter.all':               'all',
      'outputConsole.filter.none':              'none',
      'outputConsole.net.domainFilter':         'filter by domain…',
      'outputConsole.net.domainsInclude':       'Domains+',
      'outputConsole.net.domainsExclude':       'Domains-',
      'outputConsole.dl.newPushes':             'New pushes',
      'outputConsole.dl.currentState':          'Current state',
      'outputConsole.dl.expandAll':             'expand all',
      'outputConsole.dl.collapseAll':           'collapse all',
      'outputConsole.net.size':                 'Size',
      'outputConsole.net.time':                 'Time',
      'outputConsole.err.field.type':           'Type',
      'outputConsole.err.field.location':       'Location',
      'outputConsole.err.field.stack':          'Stack',
      // API help popup (since v2.13.78, Ctrl+Shift+H).
      'outputConsole.apiHelp.title': 'JustZix — shortcuts & in-page windows',
      'outputConsole.apiHelp.sec.global': 'Global shortcuts — work on any page',
      'outputConsole.apiHelp.g.tempCss': 'TEMP CSS window — open / close',
      'outputConsole.apiHelp.g.tempJs': 'TEMP JS window — open / close',
      'outputConsole.apiHelp.g.tempJsc': 'TEMP JS Console — open / close',
      'outputConsole.apiHelp.g.tempOut': 'TEMP Output Console — open / close',
      'outputConsole.apiHelp.g.ai': 'AI Helper — open / close',
      'outputConsole.apiHelp.g.edit': 'Action bar edit mode — on / off',
      'outputConsole.apiHelp.sec.window': 'Inside a window — when focused',
      'outputConsole.apiHelp.w.run': 'Run the code / send the message — JS pane, JS Console, AI Helper',
      'outputConsole.apiHelp.w.history': 'History of typed commands — JS Console',
      'outputConsole.apiHelp.w.clearConsole': 'Clear the output — JS Console',
      'outputConsole.apiHelp.w.escSearch': 'Clear the search field — Output Console',
      'outputConsole.apiHelp.sec.output': 'Output Console',
      'outputConsole.apiHelp.o.clear': 'Clear the active tab of every Output Console',
      'outputConsole.apiHelp.o.help': 'Show / hide this help',
      'outputConsole.apiHelp.o.tabs': '6 tabs: All, JZ, Console, Network, Errors, DataLayer — each with its own independent set of filters.',
      'outputConsole.apiHelp.o.network': 'Network: Alt+click a row to add its domain to the "include" filter, Alt+Shift+click for the "exclude" filter.',
      'outputConsole.apiHelp.o.datalayer': 'DataLayer: drag the divider between the columns to resize them.',
      'outputConsole.apiHelp.sec.mouse': 'In-page windows — mouse actions',
      'outputConsole.apiHelp.m.drag': 'Drag a window by its header bar to reposition it.',
      'outputConsole.apiHelp.m.resize': 'Resize a window by pulling one of its 8 handles on the edges and corners.',
      'outputConsole.apiHelp.m.snap': 'Move a window close to another — they snap together magnetically into a group that moves as one.',
      'outputConsole.apiHelp.m.context': 'A right click on the header bar opens a menu: close window, disconnect, font, text size.',
      'outputConsole.apiHelp.m.tempDbl': 'Double-click a TEMP window\'s header bar to toggle between the default and the remembered size.',
      'outputConsole.apiHelp.m.scroll': 'Scrolling with the wheel inside a window scrolls only its content, not the page underneath.',
      'outputConsole.apiHelp.sec.editmode': 'Action bar edit mode — after Ctrl+Alt+S',
      'outputConsole.apiHelp.em.undo': 'Undo the last change',
      'outputConsole.apiHelp.em.delete': 'Delete the selected labels',
      'outputConsole.apiHelp.em.deselect': 'Deselect all elements',
      'outputConsole.apiHelp.em.move': 'Drag the selected elements with the mouse — several move together.',
      'outputConsole.apiHelp.em.select': 'A click selects an element, Ctrl+click adds or removes it from the selection, Shift+drag over an empty area selects with a rectangle.',
      'outputConsole.apiHelp.em.label': 'The "+ Label" button adds a static text label; double-click a label to open its editor.',
      'outputConsole.apiHelp.sec.logging': 'Logging to the Output Console',
      'outputConsole.apiHelp.jzApi': 'Own logger (kind=jz)',
      'outputConsole.apiHelp.aliases': 'Aliases: window.__JUSTZIX__ (always), window.JZ (if not taken by the page).',
      'outputConsole.apiHelp.consoleApi': 'Standard console.* (kind=console, captured automatically)',
      'outputConsole.apiHelp.consoleNote': 'Each page console.* is passed through to the original console (DevTools keep working) and dispatched to the Output Console.',
      'outputConsole.apiHelp.footer': 'Full manual (8 languages): JustZix panel → Help.',
      // === Onboarding / tutorial ===
      'content.exec.cspBlocked': 'This page blocks script execution (CSP). Enable "User scripts" for JustZix in your browser settings.',
      'onboard.ui.back': 'Back',
      'onboard.ui.next': 'Next',
      'onboard.ui.finish': 'Finish',
      'onboard.ui.copy': 'Copy',
      'onboard.ui.copied': 'Copied',
      'onboard.ui.hint': 'Perform this action to continue',
      'onboard.ui.close': 'Close',
      'onboard.front.title': 'Front-end tutorial',
      'onboard.front.lead': 'Discover the JustZix tools available directly on the page — the floating button, the panel, code windows and action bars.',
      'onboard.front.titleFirst': 'JustZix on the page',
      'onboard.front.leadFirst': 'JustZix also works directly on the page. This tutorial shows the floating button, CSS/JS windows and action bars — step by step.',
      'onboard.front.empty': 'Front-end tutorials are coming soon.',
      'onboard.lr.titleFirst': 'Welcome to JustZix',
      'onboard.lr.title': 'Tutorial centre',
      'onboard.lr.leadFirst': 'First time here? The interactive tutorial walks you through building a complete, working solution — step by step, right in the panel.',
      'onboard.lr.lead': 'Launch the interactive guide — it walks you step by step through building a finished solution.',
      'onboard.lr.scenarios': 'Tutorials',
      'onboard.lr.start': 'Start',
      'onboard.lr.later': 'Maybe later',
      'onboard.lr.noAuto': 'Do not show on startup',
      'onboard.proj.name': 'Your first project',
      'onboard.proj.desc': 'From zero to a working solution: a folder, an action bar, a group, a rule set with CSS and JS, and a button that toggles the effect.',
      'onboard.proj.meta': '19 steps · ~3 min',
      'onboard.proj.s1.title': 'Welcome to JustZix',
      'onboard.proj.s1.body': 'JustZix injects your own CSS and JavaScript into chosen pages. We will now build a complete solution with a working button — step by step. It takes about 3 minutes.',
      'onboard.proj.s2.title': 'Add a folder',
      'onboard.proj.s2.body': 'A folder groups the rules for one site. Click the highlighted <code>+</code> button in the side panel.',
      'onboard.proj.s3.title': 'Folder name',
      'onboard.proj.s3.body': 'Enter a name, e.g. <code>My first project</code> — it is a label just for you.',
      'onboard.proj.s4.title': 'URL pattern',
      'onboard.proj.s4.body': 'This decides which pages the folder works on. Type an address with an asterisk and press Enter, e.g. <code>https://example.com/*</code>. To practise on any page, type just <code>*</code>.',
      'onboard.proj.s5.title': 'Button label',
      'onboard.proj.s5.body': 'Up to 3 letters — they appear on the floating JustZix button shown on the page.',
      'onboard.proj.s6.title': 'Action bar',
      'onboard.proj.s6.body': 'An action bar is an on-page panel with interactive buttons. Let us create one first — click the highlighted action bars button.',
      'onboard.proj.s7.title': 'Add a bar',
      'onboard.proj.s7.body': 'Click to create a new action bar in this folder.',
      'onboard.proj.s8.title': 'Close the manager',
      'onboard.proj.s8.body': 'The bar is ready. Close the action bars manager window to return to the folder.',
      'onboard.proj.s9.title': 'Open the group',
      'onboard.proj.s9.body': 'When you created the folder, JustZix already added a first group. In the side panel expand the folder and click that group to open it, then click "Next".',
      'onboard.proj.s10.title': 'Add a rule set',
      'onboard.proj.s10.body': 'A rule set is the actual rule — a pair of CSS and JavaScript code. Click the highlighted add rule set button.',
      'onboard.proj.s11.title': 'Open the rule set',
      'onboard.proj.s11.body': 'The new rule set appeared in the tree under the group. Click it to open the rule set editor, then click "Next".',
      'onboard.proj.s12.title': 'CSS code',
      'onboard.proj.s12.body': 'The CSS tab is already active. Paste the code below — it greyscales the whole page and prepares the effect toggle.',
      'onboard.proj.s13.title': 'JavaScript tab',
      'onboard.proj.s13.body': 'Click the <code>JavaScript</code> tab to add JS code to the rule set.',
      'onboard.proj.s14.title': 'JavaScript code',
      'onboard.proj.s14.body': 'JS code runs when you open the page. Paste the code below — it shows a message confirming JustZix works. The field has syntax validation.',
      'onboard.proj.s15.title': 'Actions tab',
      'onboard.proj.s15.body': 'Now we will add an interactive button. Click the actions tab in the rule set editor.',
      'onboard.proj.s16.title': 'Add a button',
      'onboard.proj.s16.body': 'Click to add an action element. By default it creates a button that runs code on demand.',
      'onboard.proj.s17.title': 'Button code',
      'onboard.proj.s17.body': 'Every button has its own code that runs when clicked on the page. Click the code field of the new button.',
      'onboard.proj.s18.title': 'Toggle code',
      'onboard.proj.s18.body': 'Paste the code that turns the greyscale set in CSS on and off. Then save the changes in the code editor window.',
      'onboard.proj.s19.title': 'Done!',
      'onboard.proj.s19.body': 'You have a complete solution: a folder, an action bar, a group, a rule set with CSS and JS code, and a working button. Open a page matching the URL pattern — you will see the message, the greyscale and the button that toggles it. You can restart the tutorial from the settings menu.',
      // Font + size context menu (since v2.13.70) — shared by CSS/JS pane + JS Console.
      'panes.contextMenu.font':                 'Font',
      'panes.contextMenu.size':                 'Size',
      'panes.contextMenu.defaultFont':          'Default',
      'modal.confirmDismissDirty':              'You have unsaved changes. Close and lose them?',
      'modal.confirmSaveBeforeClose':           'You have unsaved changes.\n\nClick OK to save and close.\nClick Cancel to proceed to discard confirmation.',
      'modal.confirmDiscardChanges':            'Close WITHOUT saving? Changes will be lost.\n\nClick OK to discard changes.\nClick Cancel to keep editing.',
      'actionBars.moveLocked':                  'Cannot move — the bar has elements that would fall outside the new scope. Reassign them first.',
      'action.bar.noneOption':                  '— no bar —',
      'action.bar.unnamed':                     '(unnamed)',
      'action.bar.selectTitle':                 'Assign this action to a bar. Bars inherit downward (folder → group → rule).',
      'action.bar.noBarsHint':                  'No action bars in the rule set tree — add a bar via the "Action bars" button in folder/group/rule.',

      // === Typed actions (since v2.12.x) ===
      'action.color.title':                     'Background color (button/select/input/slider). Click = picker, or type hex next to it.',
      'action.colorText.title':                 'Text color (and bar fill for SLIDER). Defaults to white when unset.',
      'action.colorHex.title':                  'Hex code — copy (Ctrl+C) or paste (Ctrl+V) as plain text. Accepts #RGB or #RRGGBB.',
      'action.type.title':                      'Element type (button, dropdown SELECT, …)',
      'action.type.button':                     'Button',
      'action.type.select':                     'Dropdown (SELECT)',
      'action.type.input':                      'Text field (INPUT)',
      'action.type.slider':                     'Range slider (SLIDER)',
      'action.type.textarea':                   'Multi-line field (TEXTAREA)',
      'action.type.toggle3':                    '3-state toggle (TOGGLE3)',

      // Memory scope (since v2.13.0; ruleSet added v2.13.4) — shared across state-bearing types
      'action.memory.label':                    'Remember value:',
      'action.memory.none':                     'Do not remember (resets on reload)',
      'action.memory.page':                     'Per page (URL)',
      'action.memory.host':                     'Per domain',
      'action.memory.ruleSet':                  'Within rule set (all URLs where it runs)',
      'action.memory.global':                   'Globally (all pages, shared)',
      'action.input.middleBtn':                 'INPUT · code :code',
      'action.slider.middleBtn':                'SLIDER · :min–:max step :step :unit · code :code',
      'action.textarea.middleBtn':              'TEXTAREA · :rows rows · code :code',
      'action.toggle3.middleBtn':               'TOGGLE3 · :filled/3 states · code :code',
      'action.toggle3.defaultState1':           'OFF',
      'action.toggle3.defaultState2':           'MID',
      'action.toggle3.defaultState3':           'ON',
      'action.toggle3.stateLabel.placeholder':  'label (max 5)',
      'action.toggle3.stateValue.placeholder':  'value (to JS)',
      'action.toggle3.defaultStateTitle':       'Default state (when no memory)',

      'action.select.middleBtn.staticEmpty':    'SELECT static (no options)',
      'action.select.middleBtn.static':         'SELECT static · :count options · code :code',
      'action.select.middleBtn.js':             'SELECT js · code :code',
      'action.select.empty':                    'No options. Add the first one with the button below.',
      'action.select.untitled':                 '(untitled)',
      'action.select.optionNamePlaceholder':    'Name (visible)',
      'action.select.optionValuePlaceholder':   'value (passed to JS)',
      'action.select.deleteOption':             'Delete this option',

      // === Modal — action code ===
      'modal.actionCode.titlePrefix':           'Action button code:',
      'modal.actionCode.body':                  'JavaScript code executed when the action button is clicked in the floating panel. Runs in the page context.',
      'modal.actionCode.cancel':                'Cancel',
      'modal.actionCode.save':                  'Save',

      // === Modal — INPUT editor (since v2.13.0) ===
      'modal.inputEditor.titlePrefix':          'INPUT editor:',
      'modal.inputEditor.body':                 'Text field with optional JS code triggered on change (Enter/blur). Value is remembered according to the selected scope.',
      'modal.inputEditor.placeholder':          'Placeholder:',
      'modal.inputEditor.defaultValue':         'Default value:',
      'modal.inputEditor.colorPlaceholder':     'Placeholder color:',
      'modal.inputEditor.codeLabel':            'JS code (on change):',
      'modal.inputEditor.hint':                 'Available in code: <code>value</code> (string from input), <code>$el</code> (HTMLInputElement), <code>$action</code> (id, label).',
      'modal.inputEditor.close':                'Close',
      'modal.colorClear':                       'Restore default color',
      'modal.sliderEditor.colorLabel':          'Label color (left):',
      'modal.sliderEditor.colorValue':          'Value color (right):',
      'modal.selectOptions.appearance.title':   'Dropdown appearance:',
      'modal.selectOptions.appearance.hint':    'For <strong>js variant</strong>: dropdown background and option color work in all browsers. Selected and hover backgrounds are browser-specific (depend on OS) — for full customization use <em>static</em> variant.',
      'modal.selectOptions.colorDropdownBg':    'Dropdown background:',
      'modal.selectOptions.colorOption':        'Option text color:',
      'modal.selectOptions.colorSelected':      'Selected option bg:',
      'modal.selectOptions.colorHover':         'Hover bg:',
      'modal.sliderEditor.titlePrefix':         'SLIDER editor:',
      'modal.sliderEditor.body':                'Range slider with optional JS code triggered after the user releases the slider (`change`). Value is passed as a Number.',
      'modal.sliderEditor.min':                 'Minimum:',
      'modal.sliderEditor.max':                 'Maximum:',
      'modal.sliderEditor.step':                'Step:',
      'modal.sliderEditor.defaultValue':        'Default value:',
      'modal.sliderEditor.unit':                'Unit (optional):',
      'modal.sliderEditor.codeLabel':           'JS code (on change):',
      'modal.sliderEditor.hint':                'Available in code: <code>value</code> (Number), <code>$el</code> (HTMLInputElement type=range), <code>$action</code> (id, label).',
      'modal.sliderEditor.close':               'Close',
      'modal.sliderEditor.errMaxGtMin':         'Maximum must be greater than minimum.',
      'modal.sliderEditor.errStepPositive':     'Step must be positive.',
      'modal.textareaEditor.titlePrefix':       'TEXTAREA editor:',
      'modal.textareaEditor.body':              'Multi-line text field with optional JS code on <code>change</code> (blur). <strong>Enter</strong> in textarea is a legit newline — does NOT trigger code (differs from INPUT).',
      'modal.textareaEditor.placeholder':       'Placeholder:',
      'modal.textareaEditor.defaultValue':      'Default value:',
      'modal.textareaEditor.rows':              'Number of rows:',
      'modal.textareaEditor.colorPlaceholder':  'Placeholder color:',
      'modal.textareaEditor.codeLabel':         'JS code (on change/blur):',
      'modal.textareaEditor.hint':              'Available in code: <code>value</code> (string from textarea, may contain <code>\\n</code>), <code>$el</code> (HTMLTextAreaElement), <code>$action</code> (id, label).',
      'modal.textareaEditor.close':             'Close',

      // === Modal — TOGGLE3 editor (od v2.13.25) ===
      'modal.toggle3Editor.titlePrefix':        'TOGGLE3 editor:',
      'modal.toggle3Editor.body':               '3-state segmented control. Clicking an inactive state activates it and runs JS code with <code>value</code> of the chosen state. Memory persists the current index (0-2) per action.',
      'modal.toggle3Editor.statesTitle':        'States (exactly 3, radio = default):',
      'modal.toggle3Editor.colorBg':            'Background color:',
      'modal.toggle3Editor.colorHover':         'Hover color:',
      'modal.toggle3Editor.colorInactiveText':  'Inactive text color:',
      'modal.toggle3Editor.codeLabel':          'JS code (on state change):',
      'modal.toggle3Editor.hint':               'Available in code: <code>value</code> (state.value, falls back to state.label), <code>stateIdx</code> (0-2), <code>stateLabel</code>, <code>$el</code> (container div), <code>$action</code>.',
      'modal.toggle3Editor.close':              'Close',

      // === Modal — SELECT editor (since v2.12.1, redesign v2.12.2 — variants) ===
      'modal.selectOptions.titlePrefix':        'SELECT editor:',
      'modal.selectOptions.idLabel':            'Action ID:',
      'modal.selectOptions.selectorLabel':      'JS selector:',
      'modal.selectOptions.copyId':             'Copy ID',
      'modal.selectOptions.copySelector':       'Copy JS selector',
      'modal.selectOptions.variant.static':     'Static options + single JS code',
      'modal.selectOptions.variant.js':         'JS code only (manages dropdown)',
      'modal.selectOptions.static.body':        'Each option: <strong>name</strong> (visible in dropdown) + <strong>value</strong> (passed to code as <code>value</code> variable). One shared code runs on change.',
      'modal.selectOptions.static.hint':        'In code available: <code>value</code> (string), <code>$el</code> (select element), <code>$action</code> (id, label).',
      'modal.selectOptions.staticCodeLabel':    'JS code (shared):',
      'modal.selectOptions.js.body':            'Your JS code runs once after the dropdown is inserted into the page. Receives reference <code>$el</code> to an empty <code>&lt;select&gt;</code> — you populate options and handle <code>change</code> yourself.',
      'modal.selectOptions.js.hint':            'Available: <code>$el</code> (HTMLSelectElement), <code>$action</code> (id, label).',
      'modal.selectOptions.jsCodeLabel':        'JS code:',
      'modal.selectOptions.addOption':          '+ Add option',
      'modal.selectOptions.close':              'Close',

      // === Modal — sync conflict ===
      'modal.syncConflict.title':               'Synchronization conflict',
      'modal.syncConflict.description':         'You signed in to an account that already has data in the cloud, and this device also has a local library. Choose what to do — the operation is irreversible locally, but local data can be exported before confirming.',
      'modal.syncConflict.localLabel':          'On this device locally:',
      'modal.syncConflict.serverLabel':         'On the cloud account:',
      'modal.syncConflict.useServer.title':     'Use data from the cloud account',
      'modal.syncConflict.useServer.body':      'Local folders/groups/rules will be replaced with those from the account. Choose this if the account has newer and more accurate data than this device.',
      'modal.syncConflict.sendMine.title':      'Send my local data to the account',
      'modal.syncConflict.sendMine.body':       'Account state will be replaced with what you have on this device. Choose this if this device has newer and more accurate data.',
      'modal.syncConflict.merge.title':         'Combine both sets',
      'modal.syncConflict.merge.body':          'Local and cloud entities will be kept together. If the same item exists in both places (same ID), the account version wins. Local-only items will be sent to the account on next sync.',
      'modal.syncConflict.exportLocal':         'Export local first',
      'modal.syncConflict.cancel':              'Cancel sign-in (sign out)',

      // === Modal — share import ===
      'modal.shareImport.title':                'Import from a JustZix link',
      'modal.shareImport.body':                 'Paste the full link or just the token <strong>JZS-XXXX-XXXX-XXXX-XXXX</strong>:',
      'modal.shareImport.placeholder':          'https://www.justzix.com/s/JZS-XXXX-XXXX-XXXX-XXXX',
      'modal.shareImport.cancel':               'Cancel',
      'modal.shareImport.fetch':                'Fetch',
      'modal.shareImport.preview.title':        'Preview of shared export',
      'modal.shareImport.jsWarn':               '<strong>⚠ WARNING:</strong> The export contains JavaScript code. The code will run on pages matching the rules — review what it does before importing.',
      'modal.shareImport.showCode':             'Show JavaScript code',
      'modal.shareImport.back':                 '← Back',
      'modal.shareImport.confirm':              'Import',

      // === Modal — my shares ===
      'modal.shares.title':                     'My shared links',
      'modal.shares.body':                      'Time-limited public links to exports. The backend keeps the payload until expiration, then deletes it.',
      'modal.shares.howTo':                     'To create a new link — open an export (full / folder / group / rule set) and click <strong>"Share URL"</strong>.',
      'modal.shares.refresh':                   'Refresh',
      'modal.shares.close':                     'Close',

      // === Modal — share create ===
      'modal.shareCreate.title':                'Share export via link',
      'modal.shareCreate.activeFor':            'The link will be active for:',
      'modal.shareCreate.ttl.1h':               '1 hour',
      'modal.shareCreate.ttl.6h':               '6 hours',
      'modal.shareCreate.ttl.12h':              '12 hours',
      'modal.shareCreate.ttl.24h':              '24 hours',
      'modal.shareCreate.ttl.default':          '(default)',
      'modal.shareCreate.ttl.48h':              '48 hours',
      'modal.shareCreate.expireNote':           'After expiration the link will stop working. You can revoke it earlier from "My shares".',
      'modal.shareCreate.cancel':               'Cancel',
      'modal.shareCreate.generate':             'Generate link',
      'modal.shareCreate.result.title':         'Link generated',
      'modal.shareCreate.result.urlLabel':      'Send this link to the recipient:',
      'modal.shareCreate.result.copyUrl':       'Copy link',
      'modal.shareCreate.result.tokenLabel':    'Or just the token (to paste in "Import from URL"):',
      'modal.shareCreate.result.copyToken':     'Copy token',
      'modal.shareCreate.result.goToMine':      'View "My shares"',
      'modal.shareCreate.result.close':         'Close',

      // === Modal — sync result ===
      'modal.syncResult.title':                 'Sync result',
      'modal.syncResult.close':                 'OK, got it',

      // === Modal — cloud sync ===
      'modal.sync.title':                       'Cloud synchronization',
      'modal.sync.signedOut.tab.register':      'First device',
      'modal.sync.signedOut.tab.login':         'I have a key',
      'modal.sync.signedOut.tab.recover':       'I lost my key',
      'modal.sync.signedOut.register.body':     'Generate a new sync key. <strong>Save it in a safe place</strong> — the backend stores only a hash. The key allows signing in on a second device and seeing the same library.',
      'modal.sync.signedOut.deviceLabel':       'Device label (optional):',
      'modal.sync.signedOut.deviceLabel.placeholder1': 'e.g. Work PC',
      'modal.sync.signedOut.deviceLabel.placeholder2': 'e.g. Laptop',
      'modal.sync.signedOut.yourKey':           'Your sync key:',
      'modal.sync.signedOut.regenerate':        'Generate new',
      'modal.sync.signedOut.copyClipboard':     'Copy to clipboard',
      'modal.sync.signedOut.warnSave':          '⚠ <strong>Copy and save the key now.</strong> The key will also be available in the "Key" tab after registration — but it is better to keep a copy in a password manager.',
      'modal.sync.signedOut.login.body':        'Paste the key you saw when registering the first device. Format: <code>SYNC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX</code>.',
      'modal.sync.signedOut.keyLabel':          'Sync key:',
      'modal.sync.signedOut.recover.body':      'If a <strong>verified</strong> email was set on the account — enter it and send a recovery link. After clicking the link in the email you will receive a <strong>new key</strong> (one-time). All active sessions will be signed out.',
      'modal.sync.signedOut.email':             'Account email:',
      'modal.sync.signedOut.email.placeholder': 'someone@example.com',
      'modal.sync.signedOut.cancel':            'Cancel',
      'modal.sync.signedOut.regSubmit':         'Register device',
      'modal.sync.signedOut.loginSubmit':       'Sign in',
      'modal.sync.signedOut.recoverSubmit':     'Send recovery link',
      'modal.sync.signedIn.tab.status':         'Status',
      'modal.sync.signedIn.tab.key':            'Access key',
      'modal.sync.signedIn.tab.backups':        'Backups',
      'modal.sync.signedIn.tab.email':          'Email & recovery',
      'modal.sync.signedIn.status.account':     'Account',
      'modal.sync.signedIn.status.pending':     'Pending changes:',
      'modal.sync.signedIn.status.cloudLib':    'Cloud library',
      'modal.sync.signedIn.status.lastCursor':  'Last pull (cursor):',
      'modal.sync.signedIn.status.syncNow':     'Sync now',
      'modal.sync.signedIn.status.snapshot':    'Full snapshot',
      'modal.sync.signedIn.status.refresh':     'Refresh account data',
      'modal.sync.signedIn.status.reconcile':   'Full resync with account',
      'modal.sync.signedIn.status.sessions':    'Active sessions (devices)',
      'modal.sync.signedIn.status.history':     'Sync history',
      'modal.sync.history.empty':               'No syncs recorded yet.',
      'modal.sync.history.viewAll':             'View full history',
      'modal.sync.history.changes':             'Changes:',
      'modal.sync.history.device':              'Device:',
      'modal.sync.history.fromDevices':         'Pulled from:',
      'modal.sync.history.stateAfter':          'State after sync:',
      'modal.sync.history.noDetail':            'No change details (older entry).',
      'modal.sync.signedIn.key.body':           'Sync key for this account. Enter it on a second device to synchronize the library.',
      'modal.sync.signedIn.key.yourKey':        'Your key:',
      'modal.sync.signedIn.key.reveal':         'Show key',
      'modal.sync.signedIn.key.copy':           'Copy to clipboard',
      'modal.sync.signedIn.key.forget':         'Remove key from this device',
      'modal.sync.signedIn.key.warn':           '⚠ The key gives full access to the library. Do not show it publicly. If you do not want to keep it locally — click <strong>Remove key from this device</strong> (keep a copy in a password manager). Sign-in still works, but the key cannot be viewed again.',
      'modal.sync.signedIn.key.noKey':          'The key is not stored on this device (signed in before this feature existed, or removed manually). You can recover it only via recovery email or if you have a copy stored elsewhere.',
      'modal.sync.signedIn.backups.hint':       'Cloud backups — restore replaces the entire library.',
      'modal.sync.signedIn.backups.label':      'New backup label (optional):',
      'modal.sync.signedIn.backups.placeholder':'e.g. Before big change',
      'modal.sync.signedIn.backups.create':     'Create backup now',
      'modal.sync.signedIn.backups.refresh':    'Refresh list',
      'modal.sync.signedIn.email.body':         'Email enables <strong>recovery</strong> — when you lose the key, you can get a new one by clicking a link sent to that address. Adding email requires verification (link in the mail).',
      'modal.sync.signedIn.email.add':          'Add email (recovery):',
      'modal.sync.signedIn.email.placeholder':  'someone@example.com',
      'modal.sync.signedIn.email.submit':       'Send verification link',
      'modal.sync.signedIn.email.remove':       'Remove email',
      'modal.sync.signedIn.email.recoveryFlow': 'Recovery flow: on another device (or in a no-key situation) select the <strong>"I lost my key"</strong> tab in the sign-in panel, enter this email — the backend will send a link that generates a new key and invalidates all active sessions.',
      'modal.sync.signedIn.logout':             'Sign out this device',
      'modal.sync.signedIn.close':              'Close',

      // === Content script (floating button + actions panel) ===
      'content.float.titleGloballyOff':         'Plugin globally disabled — click the extension icon to enable',
      'content.float.titleNormal':              'Left click: enable/disable folder • Right click: show rule set list • Drag to move',
      'content.panel.empty':                    'No active rule sets',
      'content.panel.actionBarsTitle':          'Action bars',
      'content.panel.connectionsTitle':         'Connections',
      'content.actionsPanel.dragHandle':        'Drag to move',
      'content.editMode.banner':                'Bar edit mode — click/Ctrl+click selects, drag moves · Ctrl+Z undo · Ctrl+Alt+S to exit',
      'content.editMode.undoEmpty':             'Nothing to undo',
      'content.editMode.addLabel':              '+ Label',
      'content.editMode.resetBar':              'Reset bar',
      'content.editMode.resetConfirm':          'Reset the bar to the default layout? Custom positions, sizes and labels will be removed.',
      'content.labelEditor.text':               'Text:',
      'content.labelEditor.font':               'Font:',
      'content.labelEditor.size':               'Size:',
      'content.labelEditor.color':              'Color:',
      'content.labelEditor.delete':             'Delete',
      'content.labelEditor.close':              'Close',
      'content.labelContextMenu.delete':        'Delete label',
      'content.bar.hideUntilReload':            'Close this bar (until reload)',
      'content.bar.disconnectFrom':             'Disconnect from: :name',
      'content.bar.disconnectAll':              'Disconnect all connections',
      'content.bar.fallbackName':               'Bar',
      'content.float.label':                    '⊕ Main button',
      'content.toast.noFoldersForPage':         'No folders match this page',
      'content.toast.floatVisible':             'Floating button enabled',
      'content.toast.floatHidden':              'Floating button hidden',

      // === Background (context menu + JS error logger) ===
      'background.contextMenu.toggleFloat':     'Enable/disable floating button for this page',
      'background.contextMenu.editMode':        'Action bar edit mode (toggle on/off)',
      'background.contextMenu.actionBars':      'Action bars',
      'background.contextMenu.actionBarsEmpty': '(no bars for this page)',
      'background.contextMenu.actionBarFallback': '(unnamed)',
      'background.contextMenu.cssPanes':         'CSS panes',
      'background.contextMenu.cssPanesEmpty':    '(no CSS panes for this page)',
      'background.contextMenu.cssPaneFallback':  '(unnamed)',
      'background.contextMenu.jsPanes':          'JS panes',
      'background.contextMenu.jsPanesEmpty':     '(no JS panes for this page)',
      'background.contextMenu.jsPaneFallback':   '(unnamed)',
      'background.contextMenu.jsConsoles':       'JS consoles',
      'background.contextMenu.jsConsolesEmpty':  '(no consoles for this page)',
      'background.contextMenu.jsConsoleFallback':'(unnamed)',
      'background.contextMenu.outputConsoles':       'Output consoles',
      'background.contextMenu.outputConsolesEmpty':  '(no output consoles for this page)',
      'background.contextMenu.outputConsoleFallback':'(unnamed)',
      // TEMP panes (since v2.13.81).
      'background.contextMenu.tempPanes':            'Temporary window (TEMP)',
      'background.contextMenu.tempCss':              'TEMP CSS pane (Ctrl+Alt+G)',
      'background.contextMenu.tempJs':               'TEMP JS pane (Ctrl+Alt+H)',
      'background.contextMenu.tempJsConsole':        'TEMP JS Console (Ctrl+Alt+J)',
      'background.contextMenu.tempOutput':           'TEMP Output Console (Ctrl+Alt+K)',
      'pane.collapse.title':                      'Collapse window to the title bar',
      'pane.expand.title':                        'Expand window',
      'aiHelper.form.submit':                     'OK',
      'aiHelper.form.cancel':                     'Cancel',
      'aiHelper.form.yes':                        'Yes',
      'aiHelper.form.no':                         'No',
      'aiHelper.form.askTitle':                   'Question',
      'aiHelper.form.confirmTitle':               'Confirmation',
      'aiHelper.form.updateConfirmTitle':               'Mettre à jour ?',
      'aiHelper.style.title':                           'Apparence d’AI Helper',
      'aiHelper.style.role.user':                       'Vos messages',
      'aiHelper.style.role.assistant':                  'Réponses de l’IA',
      'aiHelper.style.role.tool':                       'Résultats des outils',
      'aiHelper.style.role.form':                       'Formulaires',
      'aiHelper.style.font':                            'Police',
      'aiHelper.style.size':                            'Taille',
      'aiHelper.style.color':                           'Texte',
      'aiHelper.style.bg':                              'Fond',
      'aiHelper.style.reset':                           'Rétablir les valeurs par défaut',
      'aiHelper.style.resetAll':                        'Rétablir toutes les valeurs par défaut',
      'aiHelper.confirm.update.folder':                 'Mise à jour du dossier',
      'aiHelper.confirm.update.group':                  'Mise à jour du groupe',
      'aiHelper.confirm.update.rule':                   'Mise à jour de la règle',
      'aiHelper.confirm.update.bar':                    'Mise à jour de la barre d’actions',
      'aiHelper.confirm.update.action':                 'Mise à jour de l’action',
      'aiHelper.confirm.update.cssPane':                'Mise à jour de la fenêtre CSS',
      'aiHelper.confirm.update.jsPane':                 'Mise à jour de la fenêtre JS',
      'aiHelper.confirm.update.jsConsole':             'Mise à jour de la console JS',
      'aiHelper.confirm.update.outputConsole':         'Mise à jour de la console de sortie',
      'aiHelper.form.updateConfirmTitle':               'Update?',
      'aiHelper.style.title':                           'AI Helper appearance',
      'aiHelper.style.role.user':                       'Your messages',
      'aiHelper.style.role.assistant':                  'AI responses',
      'aiHelper.style.role.tool':                       'Tool results',
      'aiHelper.style.role.form':                       'Forms',
      'aiHelper.style.font':                            'Font',
      'aiHelper.style.size':                            'Size',
      'aiHelper.style.color':                           'Text',
      'aiHelper.style.bg':                              'Background',
      'aiHelper.style.reset':                           'Reset to defaults',
      'aiHelper.style.resetAll':                        'Reset all to defaults',
      'aiHelper.confirm.update.folder':                 'Update folder',
      'aiHelper.confirm.update.group':                  'Update group',
      'aiHelper.confirm.update.rule':                   'Update rule set',
      'aiHelper.confirm.update.bar':                    'Update action bar',
      'aiHelper.confirm.update.action':                 'Update action',
      'aiHelper.confirm.update.cssPane':                'Update CSS window',
      'aiHelper.confirm.update.jsPane':                 'Update JS window',
      'aiHelper.confirm.update.jsConsole':             'Update JS console',
      'aiHelper.confirm.update.outputConsole':         'Update Output Console',
      'aiHelper.form.createConfirmTitle':         'Create?',
      'aiHelper.form.confirmed':                  'confirmed',
      'aiHelper.form.cancelled':                  'cancelled',
      'tempPane.close.title':                        'Close temp window (content kept for the session)',
      'tempPane.clear.title':                        'Clear window content',
      'background.jsError.prefix':              '[JustZix] JS error in rule set/action ":id":\n',
      // === AI Helper (od v2.13.127) ===
      'settings.aiHelper.label': 'AI Helper',
      'settings.aiHelper.title': 'Configure API keys for AI Helper',
      'modal.aiHelper.title': 'AI Helper',
      'modal.aiHelper.intro': 'Enter the API keys of the providers you want to use. One is enough.',
      'modal.aiHelper.keyOpenai': 'API key — OpenAI',
      'modal.aiHelper.keyAnthropic': 'API key — Anthropic',
      'modal.aiHelper.keyGemini': 'API key — Gemini',
      'modal.aiHelper.defaultProvider': 'Default provider',
      'modal.aiHelper.defaultModel': 'Default model',
      'modal.aiHelper.modelAuto': '— auto (highest) —',
      'modal.aiHelper.loadModels': 'Load models',
      'modal.aiHelper.securityNote': '<strong>Security:</strong> by default API keys are stored only in this browser’s local storage (<code>chrome.storage.local</code>). Ticking “sync” next to a key sends that key through the JustZix cloud (the same one the extension uses to sync the rest of its data) to your other devices. Unticked keys never leave this device.',
      'modal.aiHelper.syncKey':                'sync',
      'modal.aiHelper.syncKeyTitle': 'Sync this key across your devices via the JustZix cloud',
      'modal.aiHelper.cancel': 'Cancel',
      'modal.aiHelper.save': 'Save',
      'modal.aiHelper.needKey': 'Enter an API key first.',
      'modal.aiHelper.loadingModels': 'Loading models…',
      'modal.aiHelper.errModels': 'Failed to load models',
      'modal.aiHelper.modelsLoaded': 'models available',
      'aiHelper.win.title': 'AI Helper',
      'aiHelper.win.close': 'Close',
      'aiHelper.win.provider': 'Provider',
      'aiHelper.win.model': 'Model',
      'aiHelper.win.clear': 'Clear conversation',
      'aiHelper.win.clearConfirm': 'Clear the whole conversation and start over?',
      'aiHelper.win.inputPlaceholder': 'Ask for help with CSS, JS or an action…',
      'aiHelper.win.send': 'Send (Ctrl+Enter)',
      'aiHelper.win.welcome': 'Hi! I’ll help you prepare CSS, JavaScript or actions for this page. Describe what you want to achieve.',
      'aiHelper.win.you': 'You',
      'aiHelper.win.assistant': 'AI Helper',
      'aiHelper.win.thinking': 'Thinking…',
      'aiHelper.win.error': 'API connection error',
      'aiHelper.win.noKeys': 'No API keys. Add a key in Settings → AI Helper.',
      'aiHelper.win.noModel': 'Select a model.',
      'aiHelper.win.loadingModels': 'Loading…',
      'aiHelper.win.modelsError': 'Error loading models',
      'background.contextMenu.aiHelper': 'AI Helper',
      'popup.aiHelper.label': 'AI Helper',
      'popup.aiHelper.on': 'Open — click to close (Ctrl+Alt+\\)',
      'popup.aiHelper.off': 'Closed — click to open (Ctrl+Alt+\\)',
      'popup.aiHelper.unavailable': 'Unavailable on this page',
      // === AI Helper etap 2 (od v2.13.128) ===
      'aiHelper.win.maxRounds': 'Reached the tool-step limit for this turn. Tell me how to continue.',
      'aiHelper.win.copy':                        'Copy conversation',
      'aiHelper.win.copy.all':                    'Copy whole conversation',
      'aiHelper.win.copy.lastInput':              'Copy last API prompt (system + history + input)',
      'aiHelper.win.copyNoInput':                 'Nothing to copy',
      'aiHelper.copy.lastInput.prefix':           '[JustZix → forward to API] Copied the WHOLE last prompt sent to the model (system + history + last message). Send the block below to the API as the FIRST user-message of a new conversation. STRIP this header before sending.',
      'aiHelper.win.copied':                      'Copied',
      'aiHelper.win.copyFailed':                  'Failed to copy — check clipboard permissions.',
      'aiHelper.win.font':                        'Font',
      'aiHelper.win.fontSize':                    'Size',
      'aiHelper.win.defaultFont':                 'Default',
      'aiHelper.win.stop':                        'Stop the agent',
      'aiHelper.win.stopped':                     'Stopped by the user.',
      'aiHelper.win.ctxUsage':                    'Model context usage',
      'aiHelper.win.ctxUsageDetail':              'Context',
      'aiHelper.win.compact':                     'Compact conversation (LLM summary)',
      'aiHelper.win.compactNoop':                 'History is too short — nothing to compact.',
      'aiHelper.win.compactNoProvider':           'Select a provider and model first.',
      'aiHelper.win.compactErr':                  'Compaction error',
      'aiHelper.win.compactEmpty':                'Model returned an empty result.',
      'aiHelper.win.compactedPrefix':             'Summarized',
      'aiHelper.win.compactedSuffix':             'earlier messages:',
      'aiHelper.win.telemetryTooltip':            'Times and tokens for this conversation session',
      'aiHelper.win.telemetryTimers':             'Time: since last message (u) / since last tool (t) / total agent activity (a)',
      'aiHelper.win.telemetryCurrent':            'Tokens in current action (↑ input / ↓ output)',
      'aiHelper.win.telemetrySession':            'Total tokens this session',
      'aiHelper.win.msgTelem':                    'Tokens this round and model response time',
      'aiHelper.win.toolDur':                     'Tool execution time',
      'aiHelper.win.stepIdBadge':                 'History step number',
      'aiHelper.form.freeText':                   'Or reply with text',
      'aiHelper.form.freeTextOnly':               'Your reply',
      'aiHelper.form.freeTextPlaceholder':        'Type your reply…',
      'aiHelper.win.turnLimit.body':              'Reached the 10-step tool limit for this turn. The agent paused — check the last tool and its result. Continue from here with a reset limit, or stop?',
      'aiHelper.win.turnLimit.lastTool':          'Last tool',
      'aiHelper.win.turnLimit.lastResult':        'Result',
      'aiHelper.win.turnLimit.continue':          'Continue (reset limit)',
      'aiHelper.win.turnLimit.stop':              'Stop',
      'aiHelper.win.turnLimit.stoppedByUser':     'Stopped.',
      'aiHelper.copy.title':                      'JustZix AI Helper conversation',
      'aiHelper.copy.toolResult':                 'Tool result',
      'aiHelper.tool.label': 'tool',
      'aiHelper.tool.rejected': 'The user rejected the operation.',
      'aiHelper.confirm.folder': 'AI Helper wants to create a FOLDER:',
      'aiHelper.confirm.group': 'AI Helper wants to create a GROUP:',
      'aiHelper.confirm.rule': 'AI Helper wants to create a RULE SET:',
      'aiHelper.confirm.create.cssPane': 'AI Helper wants to create a CSS WINDOW:',
      'aiHelper.confirm.create.jsPane': 'AI Helper wants to create a JS WINDOW:',
      'aiHelper.confirm.create.jsConsole': 'AI Helper wants to create a JS CONSOLE:',
      'aiHelper.confirm.create.outputConsole': 'AI Helper wants to create an OUTPUT CONSOLE:',
      'aiHelper.confirm.setPaneCode': 'AI Helper wants to insert code into window ":name" (:id).\nLength: currently :old chars, new :new chars.\nNOTE: persistent window code lives per-tab (sessionStorage), not across sessions.',
      'aiHelper.confirm.bar': 'AI Helper wants to create an ACTION BAR:',
      'aiHelper.confirm.action': 'AI Helper wants to create an ACTION:',
      // === Wyszukiwanie w kodzie (od v2.13.130) ===
      'codeSearch.placeholder': 'Search in code… (Ctrl+Alt+F)',
      'codeSearch.btn': 'Search',
      'codeSearch.prev': 'Previous match (Ctrl+Alt+←)',
      'codeSearch.next': 'Next match (Ctrl+Alt+→)',
      'codeSearch.noMatch': 'No matches',
    },

    de: {
      'dashboard.title': 'Dashboard',
      'dashboard.subtitle': 'Ein Überblick über Ihre JustZix-Bibliothek und Schnellzugriffe.',
      'dashboard.stats.title': 'Bibliothek',
      'dashboard.stats.folders': 'Ordner',
      'dashboard.stats.groups': 'Gruppen',
      'dashboard.stats.rules': 'Regelsätze',
      'dashboard.stats.actions': 'Aktionen',
      'dashboard.stats.bars': 'Aktionsleisten',
      'dashboard.stats.windows': 'In-Seite-Fenster',
      'dashboard.stats.withCss': 'Sätze mit CSS',
      'dashboard.stats.withJs': 'Sätze mit JS',
      'dashboard.stats.clicks': 'Aktionsklicks',
      'dashboard.version.title': 'Erweiterungsversion',
      'dashboard.version.checking': 'Wird geprüft…',
      'dashboard.version.current': 'Installiert: :version',
      'dashboard.version.upToDate': 'Sie sind auf dem neuesten Stand',
      'dashboard.version.outdated': 'Eine neuere Version ist verfügbar: :version',
      'dashboard.storage.title': 'Speicher',
      'dashboard.backup.title': 'Letztes Backup',
      'dashboard.backup.signIn': 'Melden Sie sich bei der Cloud-Synchronisierung an, um Cloud-Backups zu erstellen.',
      'dashboard.backup.none': 'Noch keine Cloud-Backups.',
      'dashboard.backup.noLabel': '(keine Bezeichnung)',
      'dashboard.backup.last': 'Letztes Backup: :date',
      'dashboard.recent.title': 'Kürzlich bearbeitete Regelsätze',
      'dashboard.recent.empty': 'Es wurde noch kein Regelsatz bearbeitet.',
      'dashboard.examples.title': 'Neueste Beispiele',
      'dashboard.posts.title': 'Aus dem Blog',
      'dashboard.feed.loading': 'Wird geladen…',
      'dashboard.feed.empty': 'Nichts anzuzeigen.',
      'dashboard.home.title': 'Dashboard',
      'dashboard.usage.title': 'Nutzungsstatistik',
      'dashboard.usage.cssApplied': 'CSS angewendet',
      'dashboard.usage.jsExecuted': 'JS ausgeführt',
      'dashboard.usage.windowsShown': 'Fenster angezeigt',
      'dashboard.usage.empty': 'Noch keine Daten — nutze die Erweiterung auf Webseiten.',
      'dashboard.tip.title': 'Tipp',
      'dashboard.tip.prev': 'Vorheriger Tipp',
      'dashboard.tip.next': 'Nächster Tipp',
      'dashboard.tip.manual': 'Vollständiges Handbuch öffnen',
      'tip.dash.1': 'Klicken Sie mit der rechten Maustaste auf den schwebenden Button, um das Panel mit der Liste der Ordner und Regelsätze zu öffnen.',
      'tip.dash.2': 'Ctrl+Alt+S schaltet den Bearbeitungsmodus der Aktionsleiste um — Elemente können frei positioniert und in der Größe verändert werden.',
      'tip.dash.3': 'Jeder Regelsatz kann eigene URL-Muster haben, die ihn auf bestimmte Unterseiten eingrenzen.',
      'tip.dash.4': 'Ctrl+Alt+\\ öffnet den KI-Helfer — einen Assistenten, der CSS/JS für eine bestimmte Aufgabe generiert.',
      'tip.dash.5': 'Die Code-Editoren haben eine Suchleiste — Ctrl+Alt+F verschiebt den Fokus dorthin.',
      'tip.dash.6': 'Ein Linksklick auf den schwebenden Button aktiviert oder deaktiviert alle passenden Ordner auf einmal.',
      'tip.dash.7': 'Ein Regelsatz behält seine letzten 5 CSS- und JS-Versionen im Verlauf — Sie können eine Änderung jederzeit rückgängig machen.',
      'tip.dash.8': 'Ziehen Sie einen Regelsatz auf den Namen einer anderen Gruppe oder eines anderen Ordners, um ihn dorthin zu verschieben.',
      'tip.dash.9': 'TEMP-Fenster (Ctrl+Alt+G/H/J/K) sind Einweg-CSS/JS-Bereiche und -Konsolen für schnelle Tests.',
      'tip.dash.10': 'SELECT-, INPUT-, SLIDER- und TOGGLE3-Aktionen merken sich ihren Wert — er bleibt nach einem Neuladen der Seite erhalten.',
      'tip.dash.11': 'Die Cloud-Synchronisierung hält Ihre Bibliothek geräteübergreifend konsistent.',
      'tip.dash.12': 'Anstatt eine JSON-Datei zu senden, teilen Sie einen Ordner über einen Link (Freigaben) mit einer Ablaufzeit.',
      'tip.dash.13': 'Die Suchleiste in der Seitenleiste findet Ordner, Gruppen und Regelsätze blitzschnell nach Namen.',
      'tip.dash.14': 'Versieh Regelsätze mit Tags und filtere deine Bibliothek nach Tags, wenn die Projekte mehr werden.',
      'tip.dash.15': 'Ein dreibuchstabiges Kürzel und eine Farbe heben den schwebenden Button eines Ordners hervor.',
      'tip.dash.16': 'Die Schaltfläche „URL” fügt die Adresse des aktiven Tabs direkt in ein URL-Muster ein — ohne Kopieren.',
      'tip.dash.17': 'JustZix prüft die eingegebenen URL-Muster sofort auf Gültigkeit.',
      'tip.dash.18': 'Der Editor warnt vor CSS-Syntaxfehlern, bevor du einen Regelsatz speicherst.',
      'tip.dash.19': 'Beim Import kannst du den JavaScript-Code ansehen, bevor du ihn hinzufügst.',
      'tip.dash.20': 'Das Deaktivieren eines Ordners schaltet kaskadierend alle seine Gruppen und Regelsätze ab.',
      'tip.dash.21': 'Der Filter „Nur aktive” blendet deaktivierte Einträge aus, wenn die Bibliothek wächst.',
      'tip.dash.22': 'Ziehe eine Aktionsleiste neben eine andere, um sie zu einer Einheit zu verbinden.',
      'tip.dash.23': 'Das JS-Konsolenfenster funktioniert wie eine REPL — teste Code-Schnipsel live auf der Seite.',
      'tip.dash.24': 'Das Output-Console-Fenster fängt die console.log-Ausgaben der Seite ab und zeigt sie an.',
      'tip.dash.25': 'Du kannst einen einzelnen Ordner, eine Gruppe oder einen Regelsatz exportieren — nicht nur alles.',
      'tip.dash.26': 'JustZix fügt eigene Einträge zum Rechtsklick-Kontextmenü des Browsers hinzu.',
      'tip.dash.27': 'Ziehe die Trennlinie zwischen den Code-Editor-Bereichen mit der Maus, um die Ansicht anzupassen.',
      'tip.dash.28': 'Die Erweiterung benachrichtigt dich von selbst, sobald eine neuere Version verfügbar ist.',
      'tip.dash.29': 'Ein Klick auf das Erweiterungssymbol öffnet ein Widget zum schnellen Umschalten von Ordnern und Leisten.',
      'tip.dash.30': 'Das JavaScript eines Regelsatzes kann Leistenaktionen über die eingebaute JustZix-API auslösen.',
      'tip.dash.31': 'Ctrl+Enter führt den Code aus oder sendet die Nachricht — im JS-Pane, in der JS-Konsole und im AI Helper.',
      'tip.dash.32': 'In der JS-Konsole blättern die Pfeile ↑ / ↓ durch den Verlauf der eingegebenen Befehle.',
      'tip.dash.33': 'Ctrl+L leert die Ausgabe der JS-Konsole, ohne das Fenster zu schließen.',
      'tip.dash.34': 'Ctrl+Shift+L leert den aktiven Tab jeder geöffneten Output-Konsole.',
      'tip.dash.35': 'Ctrl+Shift+H öffnet das API-Hilfefenster in der Output-Konsole.',
      'tip.dash.36': 'Im Code-Editor springen Ctrl+Alt+→ und Ctrl+Alt+← zum nächsten und vorherigen Suchergebnis.',
      'tip.dash.37': 'Die Esc-Taste leert das Suchfeld in der Output-Konsole.',
      'tip.dash.38': 'Ein Doppelklick auf die Kopfzeile eines TEMP-Fensters schaltet zwischen Standard- und letzter Größe um.',
      'tip.dash.39': 'Ein TEMP-Fenster merkt sich seine letzte Größe pro Typ und behält sie über einen Browser-Neustart.',
      'tip.dash.40': 'Das Scrollen mit dem Rad in einem Fenster scrollt nicht mehr die darunterliegende Seite.',
      'tip.dash.41': 'Jedes Fenster hat 8 Resize-Griffe — 4 in den Ecken und 4 an den Kanten.',
      'tip.dash.42': 'Ein Rechtsklick auf eine Fenster-Kopfzeile öffnet ein Menü mit Schließen, Trennen, Schrift und Größe.',
      'tip.dash.43': 'Ziehe ein Fenster nah an ein anderes — sie rasten magnetisch zu einer Gruppe zusammen.',
      'tip.dash.44': 'Die Output-Konsole hat 6 Tabs: Alle, JZ, Console, Network, Errors und DataLayer.',
      'tip.dash.45': 'Im Network-Tab der Output-Konsole fügt Alt+Klick auf eine Zeile ihre Domain zum Einschluss-Filter hinzu.',
      'tip.dash.46': 'Alt+Shift+Klick auf eine Network-Zeile fügt die Domain zum Ausschluss-Filter hinzu.',
      'tip.dash.47': 'Jeder Tab der Output-Konsole hat seinen eigenen, unabhängigen Filtersatz.',
      'tip.dash.48': 'Im DataLayer-Tab ziehst du die Leiste zwischen den Spalten, um ihre Breite zu ändern.',
      'tip.dash.49': 'Im Leisten-Bearbeitungsmodus fügt die Schaltfläche „+ Label" statische Textbeschriftungen hinzu.',
      'tip.dash.50': 'Ein Doppelklick auf ein Leisten-Label öffnet den Editor für Text, Schrift, Größe und Farbe.',
      'tip.dash.51': 'Im Bearbeitungsmodus wählt Shift+Ziehen über einen leeren Bereich Elemente mit einem Rechteck aus.',
      'tip.dash.52': 'Im Bearbeitungsmodus fügt Ctrl+Klick ein Element zur Auswahl hinzu oder entfernt es.',
      'tip.dash.53': 'Ein Klick auf das Nummern-Badge eines Aktionselements kopiert seine stabile ID in die Zwischenablage.',
      'tip.dash.54': 'Ein Regelsatz kann sein JS bei UI-Änderungen auf SPA-artigen Seiten erneut ausführen.',
      'tip.dash.55': 'Neben BUTTON, SELECT und INPUT haben Aktionen auch die Typen SLIDER, TEXTAREA und TOGGLE3.',
      'tip.dash.56': 'Das helle/dunkle Thema und die Oberflächensprache synchronisieren sich als gemeinsame Konto-Einstellung.',
      'tip.dash.57': 'Die Nutzungsstatistik im Dashboard summiert die Daten aller gekoppelten Geräte.',
      'tip.dash.58': 'Die Leiste unter dem Code-Editor lässt sich nach unten ziehen, um den Arbeitsbereich zu vergrößern.',
      'tip.dash.59': 'Die vertikale Leiste zwischen Seitenleiste und Editor klappt das linke Panel für mehr Platz ein.',
      'tip.dash.60': 'Das Haus-Symbol in der Dashboard-Kopfzeile und im Breadcrumb führt direkt zurück zum Dashboard.',
      // === Popup ===
      'popup.toggle.on.label':       'Plugin aktiviert',
      'popup.toggle.on.status':      'Injektion aktiv',
      'popup.toggle.off.label':      'Plugin deaktiviert',
      'popup.toggle.off.status':     'Es wird kein CSS/JS injiziert',
      'popup.editMode.label':        'Bearbeitungsmodus der Aktionsleiste',
      'popup.editMode.on':           'Ein — klicken zum Ausschalten (Ctrl+Alt+S)',
      'popup.editMode.off':          'Aus — klicken zum Einschalten (Ctrl+Alt+S)',
      'popup.editMode.unavailable':  'Auf dieser Seite nicht verfügbar',
      'popup.empty.title':           'Keine Regelordner passen zu dieser Seite.',
      'popup.empty.hint':            'Im Bearbeitungsmodus hinzufügen.',
      'popup.empty.folderRules':     'Keine aktiven Regeln in diesem Ordner',
      'popup.fallback.groupName':    'Gruppe',
      'popup.fallback.unnamed':      '(ohne Namen)',
      'popup.restore':               'Schwebenden Button wiederherstellen (:count)',
      'popup.editBtn':               'Bearbeitungspanel',
      'popup.actionBarsTitle':       'Aktionsleisten',
      'popup.fallback.barName':      'Leiste',
      'popup.hideForPage':           'Schwebenden Button für diese Seite ausblenden',
      'popup.showForPage':           'Schwebenden Button für diese Seite anzeigen',

      // === Options — page ===
      'options.title':                          'JustZix — Editor',

      // === Options — sidebar ===
      'sidebar.folders.title':                  'Ordner',
      'sidebar.addFolder.label':                'Ordner hinzufügen',
      'sidebar.search.placeholder':             'Suchen (Name, Tag, Code-Inhalt)',
      'sidebar.clearSearch.label':              'Löschen',
      'sidebar.activeOnly.label':               'Nur aktive',
      'sidebar.activeOnly.title':               'Unabhängige Regelsätze ausblenden (mit ○ markiert) — nur im Ordner aktive Regeln anzeigen (●)',
      'sidebar.expandAll.label':                'Gesamten Baum aufklappen',
      'sidebar.collapseAll.label':              'Gesamten Baum zuklappen',
      'sidebar.toggle.title':                   'Ordnerpanel ein-/ausklappen',

      // === Options — settings menu ===
      'settings.menu.label':                    'Einstellungen',
      'settings.theme.label':                   'Motiv',
      'settings.theme.title':                   'Motiv wählen: hell / dunkel / Auto',
      'settings.help.label':                    'Benutzerhandbuch',
      'settings.tutorial.label':                'Tutorial',
      'settings.export.label':                  'Einstellungen exportieren',
      'settings.import.label':                  'Einstellungen importieren',
      'settings.importUrl.label':               'Aus URL importieren',
      'settings.importUrl.title':               'Einen Export aus einem JustZix-Link importieren (JZS-XXXX-XXXX-XXXX-XXXX)',
      'settings.sync.label':                    'Cloud-Synchronisierung',
      'settings.shares.label':                  'Meine Freigaben',
      'settings.shares.title':                  'Liste der ausgegebenen zeitlich begrenzten öffentlichen Links zu Exporten',
      'settings.projectSite.label':             'Projektwebsite',
      'settings.projectSite.title':             'justzix.com in einem neuen Tab öffnen',
      'settings.lang.label':                    'Sprache',
      'settings.lang.title':                    'Sprache der Plugin-Oberfläche',
      'settings.lang.auto':                     'Auto',
      'settings.lang.autoTitle':                'Aus dem Browser erkennen (Standard)',
      'toast.langChanged':                      'Sprache eingestellt: :lang',
      'lang.name.auto':                         'Auto',
      'lang.name.pl':                           'Polnisch',
      'lang.name.en':                           'Englisch',
      'settings.version.label':                 'Version',
      'settings.version.checkTitle':            'Klicken, um nach der neuesten Version zu suchen (erzwungene Aktualisierung, umgeht 30-Min-Cache)',
      'settings.version.checking':              'Wird geprüft…',
      'settings.update.available':              'Neue Version verfügbar: v:version ⬇',
      'settings.update.notReady':               '(Paket noch nicht veröffentlicht)',
      'settings.syncStatusDot.title':           'Cloud-Synchronisierung — nicht angemeldet',

      // === Options — folder editor ===
      'folder.icon.title':                      'Beschriftung des schwebenden Buttons (max. 3 Buchstaben, GROSSBUCHSTABEN). Standard: ZIX',
      'folder.color.title':                     'Ordnerfarbe (optional)',
      'folder.colorClear.title':                'Farbe entfernen',
      'folder.name.placeholder':                'Ordnername (z. B. justzix.com)',
      'folder.url.placeholder':                 'URL-Muster (z. B. https://justzix.com/* )',
      'folder.urls.label':                      'URL-Muster des Ordners:',
      'folder.urls.title':                      'URL-Muster des Ordners — mindestens eines muss passen, damit eine Regel in diesem Ordner ausgeführt wird',
      'folder.urls.placeholder':                'https://justzix.com/*  (Enter zum Hinzufügen)',
      'folder.insertUrl.title':                 'URL des aktiven Tabs einfügen',
      'folder.active.label':                    'Aktiv',
      'folder.active.title':                    'Aktiv — wenn nicht aktiviert, werden der Ordner (und alle seine Gruppen/Regeln) zwangsweise deaktiviert und der Schalter gesperrt',
      'folder.enabled.label':                   'Eingeschaltet',
      'folder.enabled.title':                   'Eingeschaltet — aktueller Zustand des Ordners (wirkt sich auf aktive Gruppen und Regeln aus)',
      'folder.actionsPanel.label':              'Aktionspanel',
      'folder.actionsPanel.title':              'Schwebendes Panel mit Aktionsbuttons für diesen Ordner anzeigen',
      'folder.layout.label':                    'Layout:',
      'folder.layout.title':                    'Button-Layout des Aktionspanels',
      'folder.layout.vertical':                 'Vertikal',
      'folder.layout.horizontal':               'Horizontal',
      'folder.addGroup.label':                  '+ Gruppe',
      'folder.addGroup.title':                  'Eine neue Gruppe in diesem Ordner hinzufügen',
      'folder.export.label':                    'Exportieren',
      'folder.export.title':                    'Diesen Ordner exportieren',
      'folder.import.label':                    'Importieren',
      'folder.import.title':                    'Einen Ordner aus Datei importieren',
      'folder.delete.label':                    'Ordner löschen',

      // === Options — group editor ===
      'group.parentLabel':                      'Gruppe im Ordner:',
      'folder.label':                           'Ordner:',
      'group.label':                            'Gruppe:',
      'group.name.placeholder':                 'Gruppenname',
      'group.urls.label':                       'URL-Muster der Gruppe:',
      'group.urls.title':                       'Zusätzliche URL-Muster der Gruppe — zusätzliche Einschränkung über dem Ordnermuster, gilt für alle Regeln der Gruppe',
      'group.urls.placeholder':                 'https://example.com/admin/*  (Tab = aus Ordner vervollständigen, Enter = hinzufügen)',
      'group.active.label':                     'Aktiv',
      'group.active.title':                     'Aktiv — wenn nicht aktiviert, werden die Gruppe (und alle ihre Regeln) zwangsweise deaktiviert und der Schalter gesperrt',
      'group.enabled.label':                    'Eingeschaltet',
      'group.enabled.title':                    'Eingeschaltet — aktueller Zustand der Gruppe (wirkt sich auf aktive Regeln aus)',
      'group.addRule.label':                    '+ Regelsatz hinzufügen',
      'group.export.title':                     'Diese Gruppe exportieren',
      'group.import.title':                     'Regelsätze in diese Gruppe importieren',
      'group.move.label':                       'Verschieben',
      'group.move.title':                       'Gruppe in einen anderen Ordner verschieben',
      'group.duplicate.label':                  'Duplizieren',
      'group.duplicate.title':                  'Gruppe mit Regelsätzen duplizieren',
      'group.delete.label':                     'Gruppe löschen',

      // === Options — rule editor ===
      'rule.label':                             'Regelsatz:',
      'rule.back.label':                        'Zurück zur Gruppe',
      'rule.export.title':                      'Diesen Regelsatz in Datei exportieren',
      'rule.share.label':                       'URL teilen',
      'rule.share.title':                       'Diesen Regelsatz als zeitlich begrenzten öffentlichen Link teilen',
      'rule.import.title':                      'Regelsatz importieren (ersetzt den aktuellen)',

      // === Options — empty state ===
      'empty.title':                            'Ordner auswählen oder hinzufügen',
      'empty.body':                             'Jeder Ordner gruppiert CSS/JS-Regelsätze für ein URL-Muster.',

      // === Default names for newly created entities (options.js) ===
      'default.groupName':                      'Standard',
      'default.folderName':                     'Neuer Ordner',
      'default.ruleName':                       'Neuer Regelsatz',

      // === Plural noun forms used by toasts ===
      'plural.rule.one':                        'Regelsatz',
      'plural.rule.few':                        'Regelsätze',
      'plural.rule.many':                       'Regelsätze',
      'plural.rule.other':                      'Regelsätze',
      'plural.folder.one':                      'Ordner',
      'plural.folder.few':                      'Ordner',
      'plural.folder.many':                     'Ordner',
      'plural.folder.other':                    'Ordner',
      'plural.group.one':                       'Gruppe',
      'plural.group.few':                       'Gruppen',
      'plural.group.many':                      'Gruppen',
      'plural.group.other':                     'Gruppen',
      'plural.action.one':                      'Aktion',
      'plural.action.few':                      'Aktionen',
      'plural.action.many':                     'Aktionen',
      'plural.action.other':                    'Aktionen',
      'plural.jsLine.one':                      'JS-Zeile',
      'plural.jsLine.few':                      'JS-Zeilen',
      'plural.jsLine.many':                     'JS-Zeilen',
      'plural.jsLine.other':                    'JS-Zeilen',
      'toast.importedRulesToGroup':             ':count :word in Gruppe importiert',
      'toast.exportedRules':                    ':count :word exportiert',

      // === Sidebar — dynamic render ===
      'sidebar.empty.folders':                  'Keine Ordner',
      'sidebar.empty.activeFolders':            'Keine aktiven Ordner',
      'sidebar.folder.toggleHint':              'Gesamten Ordner ein-/ausschalten',
      'sidebar.folder.inactiveHint':            'Ordner inaktiv — im Ordner-Editor entsperren',
      'sidebar.group.toggleHint':               'Gruppe ein-/ausschalten',
      'sidebar.group.inactiveHint':             'Gruppe inaktiv — im Gruppen-Editor entsperren',
      'sidebar.rule.urlHint':                   'URL-Einschränkung auf Regelsatz-Ebene',
      'sidebar.rule.activeInFolder':            'Im Ordner aktiv',
      'sidebar.rule.inactiveInFolder':          'Im Ordner inaktiv',
      'sidebar.fallback.folderName':            '(Ordner)',
      'sidebar.fallback.groupName':             '(Gruppe)',
      'sidebar.fallback.unnamed':               '(kein Name)',
      'sidebar.flag.css':                       'Enthält CSS',
      'sidebar.flag.js':                        'Enthält JavaScript',
      'sidebar.flag.actions':                   'Enthält Aktionen',
      'sidebar.flag.groupUrls':                 'Gruppe hat URL-Einschränkung',

      // === Folder/Group/Rule editor — dynamic ===
      'rules.empty.folder':                     'Keine Regelsätze in diesem Ordner. Fügen Sie eine Gruppe hinzu und erstellen Sie darin den ersten Regelsatz, oder klicken Sie auf eine Gruppe in der Seitenleiste.',
      'rules.empty.group':                      'Keine Regelsätze in dieser Gruppe. Fügen Sie den ersten über „+ Regelsatz hinzufügen" hinzu.',
      'rule.namePlaceholder':                   'Name des Regelsatzes',
      'rule.activeTooltip':                     'Aktiv — ob diese Regel beim Umschalten des gesamten Ordners einbezogen wird',
      'rule.urls.tooltip':                      'Zusätzliche URL-Muster — wenn gesetzt, wird die Regel nur auf Unterseiten ausgeführt, die mindestens einem Muster entsprechen (zusätzlich zum Ordnermuster)',
      'rule.urls.placeholder.empty':            'https://example.com/admin/*  (Tab — Ordnermuster einfügen, Enter — hinzufügen)',

      // === Move menu ===
      'moveMenu.section.moveGroup':             'Gruppe verschieben nach',
      'moveMenu.section.moveRule':              'Verschieben in Gruppe',
      'moveMenu.section.copyRule':              'Kopieren in Gruppe',
      'moveMenu.empty.folders':                 'Keine anderen Ordner',
      'moveMenu.empty.groups':                  'Keine anderen Gruppen',

      // === Confirm dialogs — dynamic ===
      'confirm.deleteFolderWithStats':          'Ordner „:name" zusammen mit :groups und :rules löschen?',
      'confirm.deleteFolder':                   'Ordner „:name" löschen?',
      'confirm.deleteGroupWithRules':           'Gruppe „:name" zusammen mit :count :word löschen?',
      'confirm.deleteGroup':                    'Gruppe „:name" löschen?',
      'confirm.replacePartial':                 'Aktuelle :before durch teilweise Sicherung (:after) ersetzen?\n\nAlle Daten außerhalb der Datei gehen verloren. Der Vorgang ist unwiderruflich.',
      'confirm.overwriteAll':                   ':stats mit aktuellen Daten aus der Datei überschreiben?\n\nDieser Vorgang ist unwiderruflich.',

      // === Default name for newly created group ===
      'default.newGroupName':                   'Neue Gruppe',

      // === Export — summary + states ===
      'export.summary.full':                    ':icon Vollständige Sicherung: :stats + globale Einstellungen',
      'export.summary.partial':                 ':icon Teilweise Sicherung: :stats',
      'export.empty.folder':                    'Keine Ordner zum Exportieren',
      'export.empty.group':                     'Gruppe ist leer — keine Regelsätze zum Exportieren',
      'export.summary.toExport':                ':icon Zu exportieren: :count :word',
      'export.summary.noneSelected':            ':icon Keine Regelsätze ausgewählt',

      // === Import detection (banners) ===
      'import.detection.invalid':               '<strong style="color:#dc2626;">:icon Ungültiges Format:</strong> Ordnerliste fehlt (<code>folders</code>).',
      'import.detection.partialContains':       'Enthält: :stats',
      'import.detection.willReplace':           ':stats',

      // === Inline labels ===
      'inline.fileGroupLabel':                  'Gruppe',
      'shares.empty':                           'Sie haben keine aktiven Links.<br><br>Um einen zu erstellen — öffnen Sie einen Export (vollständig / Ordner / Gruppe / Regelsatz)<br>und klicken Sie auf <strong>„URL teilen"</strong>.',

      // === Share preview / export types ===
      'shareType.folder':                       'Einzelner Ordner',
      'shareType.group':                        'Einzelne Gruppe',
      'shareType.rule':                         'Einzelner Regelsatz',
      'shareType.full':                         'Vollständiger Export',

      // === Owner suffix when importing from share ===
      'shareImport.suffix.group':               'Gruppe',
      'shareImport.suffix.rule':                'Regelsatz',

      // === Rule import — prompt with options ===
      'prompt.ruleConflict':                    'Sie importieren „:new". Was soll mit dem aktuellen Regelsatz „:current" geschehen?\n\n  1 oder "replace"  — Den aktuellen Regelsatz ersetzen\n  2 oder "copy"     — Als neuen Regelsatz neben dem aktuellen hinzufügen (in derselben Gruppe)\n\n1 oder 2 eingeben:',
      'prompt.folderConflicts':                 ':count :word mit derselben ID wie vorhandene erkannt:\n:names\n\nGeben Sie eine der folgenden Optionen ein:\n  1 oder "overwrite"  — Vorhandene Ordner überschreiben (löscht deren aktuelle Gruppen und Regelsätze)\n  2 oder "copy"       — Als Kopien importieren (neue IDs, „(import)" im Ordnernamen)\n  Cancel              — Import abbrechen\n\nAuswahl:',

      // === Group editor — additional ===
      'group.empty.noGroupsInFolder':           'Keine Gruppen im Ordner',

      // === Folder import — conflict messages ===
      'folderImport.conflict.sameId':           'Der importierte Ordner hat dieselbe ID wie der vorhandene „<strong>:name</strong>" (:count :word). Aktion wählen:',
      'folderImport.conflict.sameName':         'Ein Ordner mit dem Namen „<strong>:name</strong>" existiert bereits (:count :word). Aktion wählen:',

      // === Dynamic button states ===
      'btn.shortcutPress':                      '⌨ Ctrl+Alt+Shift + Taste drücken (Esc)',
      'btn.generating':                         'Wird generiert…',
      'btn.importing':                          'Wird importiert…',
      'btn.showKey':                            'Schlüssel anzeigen',
      'btn.hideKey':                            'Schlüssel ausblenden',

      // === Rule editor — placeholders + tooltips ===
      'rule.editor.urlsPlaceholder':            'https://example.com/admin/*  (Tab — Ordnermuster einfügen, Enter — hinzufügen)',
      'rule.editor.urlsPlaceholderWithPattern': ':pattern  (Tab — einfügen, Enter — hinzufügen)',
      'rule.editor.toggleHint':                 'Diesen Regelsatz ein-/ausschalten',
      'rule.editor.toggleLockedHint':           'Regelsatz ist inaktiv — durch Aktivieren von „Aktiv" entsperren',
      'sidebar.fallback.ruleName':              '(Regelsatz)',
      'sidebar.fallback.ruleUnnamed':           '(unbenannter Regelsatz)',
      'confirm.deleteRule':                     'Regelsatz „:name" löschen?',

      // === Rule card (rendered by createRuleCard / renderActionsList) ===
      'rule.card.tagsTitle':                    'Tags (durch Komma getrennt)',
      'rule.card.activeLabel':                  'Aktiv',
      'rule.card.enabledLabel':                 'Eingeschaltet',
      'rule.card.history.title':                'Änderungsverlauf',
      'rule.card.history.label':                'Verlauf',
      'rule.card.move.label':                   'Verschieben',
      'rule.card.move.btnTitle':                'Regelsatz in eine andere Gruppe verschieben',
      'rule.card.copy.label':                   'Kopieren',
      'rule.card.copy.btnTitle':                'Regelsatz in eine andere Gruppe kopieren',
      'rule.card.duplicate.label':              'Duplizieren',
      'rule.card.delete.label':                 'Löschen',
      'rule.card.urlsLabel':                    'URL-Muster des Regelsatzes:',
      'rule.card.tab.actions':                  'Aktionen',
      'rule.card.lineCount':                    'Anzahl der Codezeilen',
      'rule.card.js.runOnce.label':             'JS nur einmal ausführen',
      'rule.card.js.runOnce.title':             'JS nach SPA-Navigation (URL-Wechsel ohne Neuladen) nicht erneut ausführen. Empfohlen für Skripte mit globalen Listenern, Intervallen oder window-Patches.',
      'rule.card.actions.info':                 'Aktionsbuttons erscheinen im schwebenden Panel (Schalter „Aktionspanel" im Ordner). Kürzel mit max. 4 Zeichen, eigene Farbe, JS-Code wird beim Klick ausgeführt.',
      'rule.card.actions.add':                  '+ Aktionsbutton hinzufügen',
      'rule.card.actions.addWithCount':         '+ Aktionsbutton hinzufügen (:count)',
      'rule.card.action.visibleLabel':          'sichtbar',
      'rule.card.duplicate.suffix':             '(Kopie)',
      'rule.card.action.codePreview':           '(:count :word) :first',
      'rule.card.action.fallback.unlabeled':    '(keine Beschriftung)',
      'rule.card.fallbackName':                 'Regelsatz',

      // === Folder export modal — additional ===
      'export.empty.foldersToExport':           'Keine Ordner zum Exportieren',
      'export.summary.toExportFolders':         ':icon Zu exportieren: :groupCount :groupWord, :ruleCount :ruleWord',
      'export.summary.invalidFolderFile':       'Ungültiges Dateiformat — Einzelordner-Export erwartet.',
      'export.summary.noConflicts':             ':icon Keine Konflikte. Ordner „<strong>:name</strong>" mit :count :word wird als neu importiert.',
      'export.summary.fileContains':            '<strong style="color:#374151;">:icon Dateiinhalt (:count :word):</strong>',
      'export.summary.jsHeader':                ':icon JavaScript des Regelsatzes:',
      'export.summary.emptyRule':               'leerer Regelsatz',
      'toast.exportedFolder':                   'Ordner exportiert (:groupCount :groupWord, :ruleCount :ruleWord)',
      'inline.fileFolderLabel':                 'Ordner',
      'inline.fileRuleLabel':                   'Regelsatz',
      'default.newGroupNameInline':             'Neue Gruppe',

      // === Sync conflict modal counts ===
      'syncConflict.countLine':                 ':folders :foldersWord / :groups :groupsWord / :rules :rulesWord',

      // === Backup restore confirm ===
      'confirm.restoreBackup':                  'Sicherung #:id wiederherstellen?\n\nALLE aktuellen Ordner/Gruppen/Regelsätze werden durch den Inhalt der Sicherung ersetzt. Andere Geräte synchronisieren beim nächsten Abruf.\n\nUnwiderruflich (außer der Erstellung einer neuen Sicherung VOR der Wiederherstellung).',

      // === Loaders / placeholders ===
      'loading.shares':                         'Wird geladen…',
      'loading.generic':                        'Wird geladen…',

      // === Sidebar — drag & arrows tooltips ===
      'sidebar.dragHandle.title':               'Ziehen zum Umsortieren',
      'sidebar.expandCollapse.title':           'Auf-/Zuklappen',
      'sidebar.folderActive.title':             'Aktiv',
      'sidebar.folderInactive.title':           'Inaktiv — zwangsweise deaktiviert',
      'sidebar.groupActive.title':              'Aktiv',
      'sidebar.groupInactive.title':            'Inaktiv — zwangsweise deaktiviert',
      'sidebar.searchNoResults':                'Keine Ergebnisse für „:query"',

      // === Theme ===
      'settings.theme.toggleToDark':            'In den dunklen Modus wechseln',
      'settings.theme.toggleToLight':           'In den hellen Modus wechseln',
      'settings.theme.labelDark':               'Dunkler Modus',
      'settings.theme.labelLight':              'Heller Modus',
      'settings.theme.labelAuto':               'Auto (System)',
      'settings.theme.toggleToAuto':            'In den automatischen Modus wechseln',

      // === URL chip remove ===
      'btn.removeUrlChip':                      'Entfernen',

      // === CSS validator ===
      'cssValidator.unclosedComment':           'Nicht geschlossener Kommentar /* ... */',
      'cssValidator.missingCloseBraces':        'Nicht geschlossene Klammern: es fehlen :count :word }',
      'plural.brace.one':                       'schließende Klammer',
      'plural.brace.few':                       'schließende Klammern',
      'plural.brace.many':                      'schließende Klammern',
      'plural.brace.other':                     'schließende Klammern',
      'cssValidator.extraCloseBrace':           'Zusätzliche schließende Klammer } (Zeile :line)',
      'jsValidator.unterminatedString':         'Nicht abgeschlossenes String-Literal (Zeile :line)',
      'jsValidator.unclosedBracket':            'Nicht geschlossene Klammer :bracket (Zeile :line)',
      'jsValidator.extraBracket':               'Zusätzliche schließende Klammer :bracket (Zeile :line)',

      // === Action buttons ===
      'action.shortcut.empty':                  'klicken + Kürzel drücken',
      'action.code.empty':                      'klicken, um Code hinzuzufügen…',
      'action.stats.title':                     'Anzahl der Klicks im Aktionspanel',
      'action.visible.title':                   'Sichtbar im Aktionspanel (nicht aktiviert = ausgeblendet, aber nicht entfernt)',
      'action.shortcut.title':                  'Tastenkürzel — klicken und eine Kombination drücken. Esc bricht ab. Das Kürzel funktioniert auf passenden Seiten unabhängig von der Panel-Sichtbarkeit.',
      'action.shortcut.titleAfterClick':        'Tastenkürzel. Klicken und eine Kombination drücken zum Ändern. Esc bricht ab. × entfernt.',
      'action.shortcut.removeBtn':              'Tastenkürzel entfernen',
      'action.delete.btn':                      'Aktionsbutton löschen',
      'action.menu.btn.duplicate':              'Aktion hier duplizieren',
      'action.menu.btn.move':                   'Aktion in einen anderen Regelsatz verschieben',
      'action.menu.btn.copy':                   'Aktion in einen anderen Regelsatz kopieren',
      'action.menu.section.moveTo':             'Verschieben in Regelsatz...',
      'action.menu.section.copyTo':             'Kopieren in Regelsatz...',
      'action.menu.empty.rules':                'Keine anderen Regelsätze',
      'action.menu.toast.moved':                'Aktion verschoben nach „:target"',
      'action.menu.toast.copied':               'Aktion kopiert nach „:target"',
      'action.shortcut.conflictTitle':          'Konflikt: dieses Kürzel wird auch von :count :word verwendet:\n:list\n\nBeim Drücken wird nur das erste in der Render-Reihenfolge ausgelöst.',
      'action.shortcut.conflictItem':           '• „:action" in „:rule" (:folder)',
      'plural.actionAlt.one':                   'andere Aktion',
      'plural.actionAlt.few':                   'andere Aktionen',
      'plural.actionAlt.many':                  'andere Aktionen',
      'plural.actionAlt.other':                 'andere Aktionen',
      'plural.more.one':                        'weitere',
      'plural.more.few':                        'weitere',
      'plural.more.many':                       'weitere',
      'plural.more.other':                      'weitere',
      'action.shortcut.conflictMore':           ' (und :count :word)',
      'action.shortcut.conflictToast':          'Konflikt: dieses Kürzel wird bereits von der Aktion „:actionLabel" in „:ruleName" verwendet:more. Nur eine davon wird ausgelöst.',
      'confirm.deleteAction':                   'Aktionsbutton „:label" löschen?',

      // === History ===
      'history.header':                         'Letzte :count gespeicherte Versionen. Klicken Sie auf „Wiederherstellen", um zu dieser Version zurückzukehren (die aktuelle wird im Verlauf gespeichert).',
      'history.modalTitle':                     'Änderungsverlauf: „:name"',
      'history.version':                        'Version :n',
      'history.restore':                        'Wiederherstellen',
      'history.close':                          'Schließen',

      // === Export — additional ===
      'export.summary.noElements':              ':icon Keine Elemente für den Export ausgewählt',
      'export.summary.toExportFolderShort':     ':icon Zu exportieren: :groupCount :groupWord, :ruleCount :ruleWord',
      'export.summary.noneSelectedShort':       ':icon Keine Regelsätze ausgewählt',

      // === Import detection ===
      'import.detection.parseError':            '<strong style="color:#dc2626;">:icon JSON-Parsing-Fehler:</strong> :msg',
      'import.detection.partialBackup':         '<strong style="color:#5b21b6;">:icon Teilweise Sicherung (ausgewählte Elemente)</strong><br>',
      'import.detection.fullBackup':            '<strong style="color:#006870;">:icon Vollständige Einstellungssicherung:legacy</strong><br>',
      'import.detection.legacySuffix':          ' (älteres Format)',
      'import.detection.willOverwrite':         '<br><br><strong style="color:#b91c1c;">:icon Aktuelle Einstellungen werden überschrieben:</strong>',
      'import.detection.exportFirst':           '<br><span style="color:#7f1d1d;font-size:11px;">Empfohlen: <strong>vor</strong> dem Import exportieren.</span>',
      'import.detection.folder':                '<strong style="color:#155e75;">:icon Ordner-Export</strong> „<strong>:name</strong>"<br><span style="color:#155e75;">Enthält: :stats</span>',
      'import.detection.group':                 '<strong style="color:#155e75;">:icon Gruppen-Export</strong> „<strong>:name</strong>"<br><span style="color:#155e75;">Enthält: :stats</span>',
      'import.detection.rule':                  '<strong style="color:#155e75;">:icon Regelsatz-Export</strong> „<strong>:name</strong>"',

      // === Import — target selectors ===
      'import.target.folder.title':             'Wo soll der importierte Ordner platziert werden?',
      'import.target.folder.newCopy':           'Als neuen Ordner auf oberster Ebene hinzufügen',
      'import.target.folder.overwrite':         'Vorhandenen Ordner „:name" überschreiben (behält ID — Sync sieht es als Bearbeitung)',
      'import.target.folder.mergeInto':         'Gruppen/Regelsätze in einen vorhandenen Ordner zusammenführen:',
      'import.target.group.title':              'Wo soll die importierte Gruppe platziert werden?',
      'import.target.group.existingFolder':     'Zu einem vorhandenen Ordner hinzufügen:',
      'import.target.group.newFolder':          'Einen neuen Ordner für diese Gruppe erstellen',
      'import.target.rule.title':               'Wo soll der importierte Regelsatz platziert werden?',
      'import.target.rule.existingGroup':       'Zu einer vorhandenen Gruppe hinzufügen:',
      'import.target.rule.newFolder':           'Einen neuen Ordner und eine neue Gruppe für diesen Regelsatz erstellen',

      // === Share errors (create flow) ===
      'share.error.unknown':                    'Unbekannter Fehler',
      'share.error.shareFailed':                'Sie haben bereits 20 aktive Links oder 50 MB überschritten. Alte Links unter „Meine Freigaben" widerrufen.',
      'share.error.badRequest':                 'Link-Generierung fehlgeschlagen — prüfen Sie, ob der Export gültig ist. :msg',
      'share.error.rateLimited':                'Zu viele Links in der letzten Stunde erstellt (Limit: 30). Versuchen Sie es gleich erneut.',
      'share.error.invalidToken':               'Sitzung abgelaufen — melden Sie sich im Sync-Panel erneut an und versuchen Sie es noch einmal.',
      'share.error.serverError':                'Serverproblem. Versuchen Sie es gleich erneut.',
      'share.error.fallback':                   'Fehler: :msg',
      'share.error.tooLarge':                   'Export zu groß — das Backend erlaubt max. 5 MB. Teilen Sie einen kleineren Teil.',

      // === Share subject (create modal) ===
      'share.subject.partial':                  'Teilweiser Bibliotheksexport',
      'share.subject.full':                     'Vollständiger Bibliotheksexport',

      // === Time relative formatting (fmtRelHuman) ===
      'rel.lessThanMinute':                     'weniger als eine Minute',
      'rel.min':                                ':n Min.',
      'rel.hMin':                               ':h Std. :m Min.',
      'rel.h':                                  ':h Std.',
      'rel.dH':                                 ':d T :h Std.',
      'rel.d':                                  ':d T',

      // === Shares list ===
      'shares.expired':                         'abgelaufen',
      'shares.fetchFailed':                     'Liste konnte nicht abgerufen werden: :msg',
      'shares.confirmRevoke':                   'Freigabe #:id widerrufen?\n\nDer Link funktioniert sofort nicht mehr — Empfänger erhalten beim nächsten Abrufversuch einen 404.\n\nHINWEIS: Wenn jemand die Daten bereits heruntergeladen hat, hat er eine lokale Kopie — der Widerruf holt bereits heruntergeladene Inhalte nicht zurück.',
      'shares.revoked':                         'Freigabe #:id widerrufen',

      // === Share preview details ===
      'sharePreview.senderLabel':               'Absender:',
      'sharePreview.senderAnonymous':           'anonym',
      'sharePreview.expiresLabel':              'Läuft ab:',
      'sharePreview.expiresValue':              'in :rel',
      'sharePreview.typeLabel':                 'Typ:',
      'sharePreview.sizeLabel':                 'Größe:',
      'sharePreview.contentLabel':              'Inhalt:',
      'sharePreview.cssLabel':                  'CSS:',
      'sharePreview.cssValueChars':             ':n Zeichen',
      'sharePreview.jsLabel':                   'JavaScript:',
      'sharePreview.downloadsLabel':            'Downloads:',
      'sharePreview.codePreviewTitle':          'Code-Vorschau aus geteiltem Export',

      // === Share import errors (preview) ===
      'shareImport.error.unknown':              'Unbekannter Fehler',
      'shareImport.error.badRequest':           'Token ist ungültig. Prüfen Sie, ob Sie den vollständigen Link kopiert haben.',
      'shareImport.error.notFound':             'Dieser Link ist abgelaufen oder wurde vom Absender widerrufen. Bitten Sie um einen neuen.',
      'shareImport.error.rateLimited':          'Zu viele Versuche. Warten Sie einen Moment und versuchen Sie es erneut.',
      'shareImport.error.serverError':          'Serverproblem. Versuchen Sie es erneut.',
      'shareImport.error.fallback':             'Fehler: :msg',
      'shareImport.error.invalidTokenFormat':   'Ungültiges Token-Format',

      // === Rule import ===
      'ruleImport.unknownFormat':               'Unbekanntes oder nicht unterstütztes Exportformat: :type',
      'prompt.ruleConflict.body':               'Sie importieren „:new". Was soll mit dem aktuellen Regelsatz „:current" geschehen?\n\n  1 oder "replace"  — Aktuellen durch Dateiinhalt ersetzen\n  2 oder "copy"     — Als neuen Regelsatz neben dem aktuellen hinzufügen (in derselben Gruppe)\n  Cancel            — Import abbrechen\n\nAuswahl:',

      // === Folder export ===
      'folderExport.exportedToast':             'Ordner exportiert (:groupCount :groupWord, :ruleCount :ruleWord)',

      // === Folder import — details ===
      'folderImport.parseError':                'JSON-Parsing-Fehler: :msg',
      'folderImport.invalidFormat':             'Ungültiges Dateiformat — Einzelordner-Export erwartet.',
      'folderImport.noConflicts':               ':icon Keine Konflikte. Ordner „<strong>:name</strong>" mit :count :word wird als neu importiert.',
      'folderImport.conflictSameId':            'Der importierte Ordner hat dieselbe ID wie der vorhandene „<strong>:name</strong>" (:count :word). Aktion wählen:',
      'folderImport.conflictSameName':          'Ein Ordner mit dem Namen „<strong>:name</strong>" existiert bereits (:count :word). Aktion wählen:',
      'folderImport.fileContents':              '<strong style="color:#374151;">:icon Dateiinhalt (:count :word):</strong>',
      'folderImport.jsHeader':                  ':icon JavaScript des Regelsatzes:',
      'folderImport.codePreviewTitle':          'Code-Vorschau: :name',
      'folderImport.previewJsBtn.title':        'JS-Code in der Vorschau anzeigen',

      // === Sync — toasts + status messages ===
      'sync.toast.synced':                      'Synchronisiert: :applied Änderungen, :deletes Löschungen',
      'sync.rel.justNow':                       'gerade eben',
      'sync.tooltip.syncing':                   'Wird synchronisiert…',
      'sync.tooltip.error':                     'Sync: Fehler. Letzte erfolgreiche: :last',
      'sync.tooltip.connected':                 'Cloud-Synchronisierung — verbunden. Letzte: :last',
      'sync.error.copyManually':                'Kopieren fehlgeschlagen — manuell auswählen und Strg+C drücken.',
      'sync.error.registerFailed':              'Registrierungsfehler: :msg',
      'sync.error.unknown':                     'unbekannt',
      'sync.error.invalidKeyFormat':            'Ungültiges Schlüsselformat — erwartet SYNC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX.',
      'sync.status.sendingLocal':               ':count lokale Entitäten werden an das Konto gesendet.',
      'sync.status.cancelled':                  'Anmeldung abgebrochen — abgemeldet. Lokale Daten unverändert.',
      'sync.status.sendingAndDeleting':         ':local lokale Entitäten werden gesendet, :remote vom Konto gelöscht…',
      'sync.status.merged':                     'Zusammengeführt. Lokal-einzigartige (:count Elemente) gehen beim nächsten Sync ans Konto.',
      'sync.error.firstSyncFailed':             'Anmeldung OK, aber erste Synchronisierung fehlgeschlagen: :msg',
      'sync.error.loginFailed':                 'Anmeldefehler: :msg',
      'sync.error.invalidEmail':                'Geben Sie eine gültige E-Mail-Adresse ein.',
      'sync.recovery.sent':                     'Falls diese E-Mail registriert ist, haben wir einen Wiederherstellungslink gesendet. Prüfen Sie Ihren Posteingang.',
      'sync.error.generic':                     'Fehler: :msg',
      'sync.account.thisDevice':                'dieses Gerät: :label',
      'sync.session.noLabel':                   '(keine Beschriftung)',
      'sync.session.thisDeviceSuffix':          ' (dieses Gerät)',
      'sync.session.revoke':                    'Widerrufen',
      'sync.error.revokeFailed':                'Widerrufsfehler: :msg',
      'sync.session.fetchFailed':               'Sitzungsliste konnte nicht abgerufen werden: :msg',
      'sync.key.copyManual':                    'Kopieren fehlgeschlagen — auf „Schlüssel anzeigen" klicken und manuell Strg+C drücken.',
      'sync.key.removed':                       'Schlüssel aus dem lokalen Speicher entfernt.',
      'sync.backups.empty':                     'Keine Cloud-Sicherungen. Erstellen Sie die erste mit „Jetzt Sicherung erstellen".',
      'sync.backups.fetchFailed':               'Liste konnte nicht abgerufen werden: :msg',
      'sync.backups.creating':                  'Cloud-Sicherung wird erstellt…',
      'sync.backups.createFailed':              'Fehler beim Erstellen der Sicherung: :msg',
      'sync.backups.restored':                  'Sicherung #:id wiederhergestellt. :count Entitäten ersetzt. newCursor=:cursor.',
      'sync.backups.restoreFailed':             'Wiederherstellungsfehler: :msg',
      'sync.backups.downloadFailed':            'Download-Fehler: :msg',
      'sync.backups.confirmDelete':             'Sicherung #:id aus der Cloud löschen? Unwiderruflich.',
      'sync.backups.deleted':                   'Sicherung #:id gelöscht.',
      'sync.backups.deleteFailed':              'Löschfehler: :msg',
      'sync.pushPullSummary':                   'Push: :pushedAccepted akzeptiert, :pushedRejected abgelehnt • Pull: :applied Entitäten, :deletes Löschungen.',
      'sync.returned':                          'Sync ergab: :result',
      'sync.snapshotFailed':                    'Snapshot-Fehler: :msg',
      'sync.reconcile.running':                 'Vollständige Resynchronisierung mit Konto…',
      'sync.reconcile.okPushed':                'Resync OK — :count fehlende Entitäten ans Konto gesendet.',
      'sync.reconcile.okInSync':                'Resync OK — Bibliothek stimmt mit dem Konto überein.',
      'sync.reconcile.failed':                  'Vollständige Resynchronisierung fehlgeschlagen: :msg',
      'sync.email.saveButFailed':               'E-Mail gespeichert, aber Linkversand fehlgeschlagen — versuchen Sie es erneut.',
      'sync.email.verifyLinkSent':              'Bestätigungslink an :email gesendet. Prüfen Sie Ihren Posteingang und klicken Sie auf den Link.',
      'sync.email.removed':                     'E-Mail entfernt.',

      // === Sync result modal — strategies ===
      'syncResult.merge.title':                 'Mit der Cloud zusammengeführt',
      'syncResult.merge.desc':                  'An beiden Stellen vorhandene Entitäten wurden mit der Kontoversion überschrieben. Lokal-einzigartige bleiben erhalten und gehen beim nächsten Sync ans Konto.',
      'syncResult.merge.tableHeader':           '<table class="result-table"><thead><tr><th>Typ</th><th>Aus Cloud: überschrieben</th><th>Aus Cloud: neu</th><th>Lokal behalten → Push</th><th>Gesamt nach Zusammenführung</th></tr></thead><tbody>',
      'syncResult.useServer.title':             'Lokale Daten durch Cloud-Daten ersetzt',
      'syncResult.useServer.desc':              'Die lokale Bibliothek wurde vollständig durch den Kontostand ersetzt. Lokal-einzigartige Entitäten (falls vorhanden) gingen verloren.',
      'syncResult.useServer.tableHeader':       '<table class="result-table"><thead><tr><th>Typ</th><th>Aus Cloud abgerufen</th><th>Lokal verloren</th><th>Gemeinsam (überschrieben)</th><th>Gesamt nach Sync</th></tr></thead><tbody>',
      'syncResult.sendMine.title':              'Lokale Daten werden an das Konto gesendet',
      'syncResult.sendMine.desc':               'Die lokale Bibliothek wird an das Konto übertragen. In der Cloud vorhandene, lokal fehlende Entitäten werden auf dem Konto als gelöscht (Tombstone) markiert.',
      'syncResult.sendMine.tableHeader':        '<table class="result-table"><thead><tr><th>Typ</th><th>Gesendet (Push)</th><th>Überschreibungen auf Konto</th><th>Neu auf Konto</th><th>Vom Konto gelöscht</th></tr></thead><tbody>',
      'syncResult.fallback.title':              'Synchronisierung abgeschlossen',
      'syncResult.empty':                       'Keine Details.',
      'syncResult.label.folders':               'Ordner',
      'syncResult.label.groups':                'Gruppen',
      'syncResult.label.rules':                 'Regelsätze',
      'syncResult.label.actions':               'Aktionen',

      // === Options — toasts (bottom notifications) ===
      'toast.saved':                            'Gespeichert',
      'toast.cantDeleteLastGroup':              'Die letzte Gruppe in einem Ordner kann nicht gelöscht werden',
      'toast.noOtherTab':                       'Kein anderer Tab mit einer Seiten-URL',
      'toast.urlInserted':                      'URL aus Tab eingefügt',
      'toast.urlAlreadyAdded':                  'Muster bereits in der Liste: :pattern',
      'toast.error':                            'Fehler: :msg',
      'toast.shortcutMustIncludeMod':           'Erlaubtes Kürzel: Ctrl+Alt+Shift + Buchstabe (außer G H J K L), Ziffer oder Zeichen (außer \\).',
      'toast.shortcutCollidesChrome':           'Warnung: Kürzel kollidiert mit einem Chrome-Kürzel (z. B. neuer Tab). Es funktioniert möglicherweise nicht — wählen Sie ein anderes.',
      'toast.copied':                           'Kopiert: :text',
      'toast.copyFailed':                       'Kopieren fehlgeschlagen',
      'toast.copyClipboard':                    'In die Zwischenablage kopiert',
      'toast.copyLink':                         'Link kopiert',
      'toast.copyToken':                        'Token kopiert',
      'toast.noHistory':                        'Kein Verlauf für diesen Regelsatz',
      'toast.versionRestored':                  'Version wiederhergestellt',
      'toast.fileNotFullImport':                'Datei ist für einen vollständigen Import nicht geeignet',
      'toast.unknownOption':                    'Unbekannte Option — Import abgebrochen',
      'toast.importedSettings':                 'Einstellungen importiert',
      'toast.parseError':                       'Parsing-Fehler: :msg',
      'toast.invalidPartialFormat':             'Ungültiges Format — Gruppen-, Ordner- oder Regelsatz-Export erwartet',
      'toast.groupImport.folderRejected':       'Das ist ein Ordner-Export (mehrere Gruppen). Importieren Sie ihn über den Ordner-Import, nicht den Gruppen-Import.',
      'toast.exportedRule':                     'Regelsatz exportiert',
      'toast.signInFirst':                      'Melden Sie sich zuerst im Sync-Panel an',
      'toast.exportNotReady':                   'Export ist nicht bereit',
      'toast.noRulesSelected':                  'Keine Regelsätze ausgewählt',
      'toast.revokeError':                      'Widerrufsfehler: :msg',
      'toast.importedFromLink':                 'Aus Link importiert',
      'toast.invalidRuleFormat':                'Ungültiges Dateiformat — Regelsatz-Export erwartet',
      'toast.importedReplaced':                 'Importiert (ersetzt)',
      'toast.importedAsCopy':                   'Als Kopie importiert',
      'toast.noImportData':                     'Keine Daten zum Importieren',
      'toast.importedFolder':                   'Ordner importiert: :name',
      'toast.importedGroup':                    'Gruppe importiert: :name',
      'toast.importedRule':                     'Regelsatz importiert: :name',

      // === Options — native confirm() dialogs ===
      'confirm.restoreVersion':                 'Diese Version wiederherstellen? Der aktuelle Inhalt wird als neuer Snapshot dem Verlauf hinzugefügt.',
      'confirm.revokeSession':                  'Diese Sitzung widerrufen?',
      'confirm.removeKey':                      'Den Schlüssel von diesem Gerät entfernen?\n\nDie Anmeldung funktioniert weiterhin (die Sitzung ist unabhängig vom Schlüssel), aber Sie können den Schlüssel von diesem Computer aus nicht mehr anzeigen oder auf ein anderes Gerät kopieren.\n\nStellen Sie sicher, dass Sie eine Kopie des Schlüssels woanders gespeichert haben (Passwort-Manager).',
      'confirm.snapshot':                       'Ein vollständiger Snapshot lädt die gesamte Bibliothek aus der Cloud herunter und ersetzt den lokalen Zustand. Ihre ausstehenden Änderungen gehen NICHT verloren (sie bleiben in der Warteschlange). Fortfahren?',
      'confirm.removeEmail':                    'Die E-Mail vom Konto entfernen? Ohne E-Mail können Sie bei verlorenem Schlüssel keine Wiederherstellung durchführen.',
      'confirm.signOut':                        'Dieses Gerät abmelden? Die lokale Bibliothek bleibt, aber die Synchronisierung wird beendet.',

      // === Modal — full export ===
      'modal.export.title':                     'Einstellungen exportieren',
      'modal.export.mode.all':                  'Alles',
      'modal.export.mode.select':               'Ausgewählte Ordner/Regeln',
      'modal.export.selectAll':                 'Alle auswählen',
      'modal.export.deselectAll':               'Auswahl aufheben',
      'modal.export.hint':                      'Sie können das JSON in die Zwischenablage kopieren oder die Datei herunterladen:',
      'modal.export.close':                     'Schließen',
      'modal.export.share':                     'URL teilen',
      'modal.export.share.title':               'Diesen Export als zeitlich begrenzten öffentlichen Link teilen',
      'modal.export.download':                  'Datei herunterladen',
      'modal.export.copy':                      'In die Zwischenablage kopieren',

      // === Modal — full import ===
      'modal.import.title':                     'Einstellungen importieren',
      'modal.import.pickerHint':                'Fügen Sie JSON aus einem beliebigen JustZix-Export ein (vollständige Sicherung, Ordner, Gruppe oder Regelsatz) — das Formular erkennt den Typ und bietet Platzierungsoptionen.',
      'modal.import.warningStrong':             'Warnung:',
      'modal.import.warningRest':               ' ein vollständiger Import überschreibt alle aktuellen Einstellungen.',
      'modal.import.placeholder':               'JSON hier einfügen oder eine Datei auswählen...',
      'modal.import.cancel':                    'Abbrechen',
      'modal.import.import':                    'Importieren',

      // === Modal — folder export ===
      'modal.folderExport.titlePrefix':         'Ordner exportieren:',
      'modal.folderExport.hint':                'Gruppen/Regeln zum Exportieren auswählen.',
      'modal.folderExport.cancel':              'Abbrechen',
      'modal.folderExport.share':               'URL teilen',
      'modal.folderExport.shareTitle':          'Diesen Ordner als zeitlich begrenzten öffentlichen Link teilen',
      'modal.folderExport.download':            'Datei herunterladen',

      // === Modal — group export ===
      'modal.groupExport.titlePrefix':          'Gruppe exportieren:',
      'modal.groupExport.hint':                 'Regelsätze zum Exportieren auswählen.',
      'modal.groupExport.cancel':               'Abbrechen',
      'modal.groupExport.share':                'URL teilen',
      'modal.groupExport.shareTitle':           'Diese Gruppe als zeitlich begrenzten öffentlichen Link teilen',
      'modal.groupExport.download':             'Datei herunterladen',

      // === Modal — folder import ===
      'modal.folderImport.title':               'Ordner importieren',
      'modal.folderImport.placeholder':         'Ordner-JSON einfügen oder eine Datei auswählen...',
      'modal.folderImport.conflicts':           'Konflikte erkannt:',
      'modal.folderImport.action.rename':       'Als neuen Ordner importieren (mit Suffix „(import)")',
      'modal.folderImport.action.overwrite':    'Vorhandenen Ordner überschreiben',
      'modal.folderImport.action.merge':        'Zusammenführen — neue Regelsätze zum vorhandenen Ordner hinzufügen (beide behalten)',
      'modal.folderImport.cancel':              'Abbrechen',
      'modal.folderImport.import':              'Importieren',

      // === Modal — action code ===
      // === Action bars (Phase 2 of multi-bars system, since v2.9) ===
      'actionBars.btn.label':                   'Aktionsleisten',
      'actionBars.btn.title':                   'Diesem Ordner zugewiesene Aktionsleisten verwalten',
      'actionBars.btn.titleGroup':              'Dieser Gruppe zugewiesene Aktionsleisten verwalten',
      'actionBars.btn.titleRule':               'Diesem Regelsatz zugewiesene Aktionsleisten verwalten',
      'actionBars.parentType.folder':           'Ordner',
      'actionBars.parentType.group':            'Gruppe',
      'actionBars.parentType.rule':             'Regelsatz',
      'actionBars.parentType.folder.short':     '[O]',
      'actionBars.parentType.group.short':      '[G]',
      'actionBars.parentType.rule.short':       '[R]',
      'actionBars.empty':                       'Keine Aktionsleisten auf dieser Ebene. Klicken Sie auf „+ Neue Leiste hinzufügen", um die erste zu erstellen.',
      'actionBars.section.owned':               'Leisten auf dieser Ebene',
      'actionBars.section.fromFolder':          '↑ Vom Ordner „<strong>:name</strong>" geerbt',
      'actionBars.section.fromGroup':           '↑ Von der Gruppe „<strong>:name</strong>" geerbt',
      'actionBars.elementsCount':               'Elemente: :count',
      'actionBars.enabled':                     'Eingeschaltet',
      'actionBars.name.placeholder':            'NAME',
      'actionBars.name.title':                  'Beschriftung am Rand der Leiste (max. 6 Zeichen, GROSSBUCHSTABEN). Leer = keine Beschriftung.',
      'actionBars.color.title':                 'Hintergrundfarbe der Leiste',
      'actionBars.orientation.title':           'Klicken zum Umschalten der Leistenausrichtung (vertikal ↔ horizontal)',
      'actionBars.color.clear':                 'Hintergrundfarbe entfernen',
      'actionBars.moveTo':                      'Verschieben nach:',
      'actionBars.delete':                      'Löschen',
      'actionBars.confirmDelete':               'Diese Leiste löschen?',
      'actionBars.confirmDeleteWithElements':   'Diese Leiste löschen? Zugewiesene Elemente (:count) behalten ihre Definitionen, verlieren aber die Leistenzuordnung — Sie müssen sie manuell einer anderen Leiste zuweisen.',
      'actionBars.deleteMigrate.message':       'Diese Leiste hat :count zugewiesene Elemente. Wählen Sie vor dem Löschen, wohin sie verschoben werden sollen (oder lösen Sie sie von jeder Leiste):',
      'actionBars.deleteMigrate.detachOption':  '— Elemente lösen (keine Leiste) —',
      'actionBars.deleteMigrate.confirm':       'Verschieben und löschen',
      'actionBars.deleteMigrate.cancel':        'Abbrechen',
      'modal.actionBars.titlePrefix':           'Aktionsleisten —',
      'modal.actionBars.hint':                  'Dieser Ebene zugewiesene Leisten. Ordnerleisten aggregieren Elemente aus allen aktiven Regelsätzen des Ordners, Gruppenleisten — nur aus der Gruppe, Regelsatz-Leisten — nur wenn der Regelsatz aktiv ist. Eine Leiste ohne sichtbare Elemente wird nicht gerendert.',
      'modal.actionBars.add':                   '+ Neue Leiste hinzufügen',
      'modal.actionBars.close':                 'Schließen',

      // === Windows — dropdown type selector (since v2.13.41) ===
      'windows.btn.label':                      'Fenster',
      'windows.btn.title':                      'Diesem Ordner zugewiesene Fenster verwalten (CSS / JS / Konsolen)',
      'windows.btn.titleGroup':                 'Dieser Gruppe zugewiesene Fenster verwalten',
      'windows.btn.titleRule':                  'Diesem Regelsatz zugewiesene Fenster verwalten',
      'windows.menu.title':                     'Fenstertyp wählen:',
      'windows.menu.comingSoon':                'demnächst',
      'jsPanes.btn.label':                      'JS-Panels',
      'jsConsoles.btn.label':                   'JS-Konsolen',
      'outputConsoles.btn.label':               'Ausgabekonsolen',

      // === CSS panes (Phase 2 "Windows on the front", since v2.13.40) ===
      'cssPanes.btn.label':                     'CSS-Panels',
      'cssPanes.contextMenu.disable':           'CSS-Panel schließen',
      'cssPanes.btn.title':                     'Diesem Ordner zugewiesene CSS-Panels verwalten',
      'cssPanes.btn.titleGroup':                'Dieser Gruppe zugewiesene CSS-Panels verwalten',
      'cssPanes.btn.titleRule':                 'Diesem Regelsatz zugewiesene CSS-Panels verwalten',
      'cssPanes.empty':                         'Keine CSS-Panels auf dieser Ebene. Klicken Sie auf „+ Neues CSS-Panel hinzufügen", um das erste zu erstellen.',
      'cssPanes.section.owned':                 'Panels auf dieser Ebene',
      'cssPanes.name.placeholder':              'Panel-Name',
      'cssPanes.color.title':                   'Farbe des Kopfzeilen-Punkts',
      'panes.style.dot':                        'Punkt',
      'panes.style.headerBg':                   'Kopf-Hintergrund',
      'panes.style.headerText':                 'Kopf-Text',
      'panes.style.bodyBg':                     'Körper-Hintergrund',
      'panes.style.bodyText':                   'Körper-Text',
      'panes.style.runBtn':                     'Ausführen-Button',
      'panes.style.reset':                      'Löschen',
      'mcp.default.title':                      'Standard wiederherstellen',
      'action.colors.bg':                       'Hintergrund',
      'action.colors.text':                     'Text',
      'action.colors.dropdownBg':               'Dropdown-Hintergrund',
      'action.colors.option':                   'Option',
      'action.colors.selected':                 'Ausgewählt',
      'action.colors.hover':                    'Hover',
      'action.colors.label':                    'Beschriftung',
      'action.colors.value':                    'Wert',
      'action.colors.placeholder':              'Platzhalter',
      'action.colors.bgInactive':               'Inaktiver Hintergrund',
      'action.colors.inactiveText':             'Inaktiver Text',
      'cssPanes.enabled.label':                 'Eingeschaltet',
      'cssPanes.enabled.title':                 'Ob das Panel auf passenden Seiten erscheinen soll',
      'cssPanes.delete.title':                  'Dieses Panel löschen',
      'cssPanes.delete.confirm':                'CSS-Panel „:name" löschen? Der Textbereich-Inhalt in aktiven Tabs geht verloren.',
      'modal.cssPanes.titlePrefix':             'CSS-Panels —',
      'modal.cssPanes.hint':                    'Schwebender Textbereich, der auf Seiten injiziert wird, die den URLs dieser Ebene entsprechen. Inhalt wird im sessionStorage pro Tab gehalten (geht beim Schließen des Tabs verloren). Live-<style>-Injektion — CSS-Änderungen werden sofort übernommen.',
      'modal.cssPanes.add':                     '+ Neues CSS-Panel hinzufügen',
      'modal.cssPanes.close':                   'Schließen',
      'cssPane.frontend.close.title':           'Panel ausblenden (bleibt ausgeblendet, bis der Tab geschlossen wird)',

      // === JS panes (Phase 2 cont., since v2.13.56) ===
      'jsPanes.contextMenu.disable':            'JS-Panel schließen',
      'jsPanes.btn.title':                      'Diesem Ordner zugewiesene JS-Panels verwalten',
      'jsPanes.btn.titleGroup':                 'Dieser Gruppe zugewiesene JS-Panels verwalten',
      'jsPanes.btn.titleRule':                  'Diesem Regelsatz zugewiesene JS-Panels verwalten',
      'jsPanes.empty':                          'Keine JS-Panels auf dieser Ebene. Klicken Sie auf „+ Neues JS-Panel hinzufügen", um das erste zu erstellen.',
      'jsPanes.section.owned':                  'Panels auf dieser Ebene',
      'jsPanes.name.placeholder':               'Panel-Name',
      'jsPanes.color.title':                    'Farbe des Kopfzeilen-Punkts',
      'jsPanes.enabled.label':                  'Eingeschaltet',
      'jsPanes.enabled.title':                  'Ob das Panel auf passenden Seiten erscheinen soll',
      'jsPanes.delete.title':                   'Dieses Panel löschen',
      'jsPanes.delete.confirm':                 'JS-Panel „:name" löschen? Der Textbereich-Inhalt in aktiven Tabs geht verloren.',
      'modal.jsPanes.titlePrefix':              'JS-Panels —',
      'modal.jsPanes.hint':                     'Schwebender Textbereich, der JS im Seitenkontext (MAIN world, new Function) bei jeder Änderung ausführt. Inhalt wird pro Tab im sessionStorage gehalten. Ausführungsfehler werden im roten Overlay unter dem Code angezeigt.',
      'modal.jsPanes.add':                      '+ Neues JS-Panel hinzufügen',
      'modal.jsPanes.close':                    'Schließen',
      'jsPane.frontend.close.title':            'Panel ausblenden (bleibt ausgeblendet, bis der Tab geschlossen wird)',

      // === JS Consoles (Phase 2 cont., since v2.13.66) ===
      'jsConsoles.contextMenu.disable':         'JS-Konsole schließen',
      'jsConsoles.empty':                       'Keine Konsolen auf dieser Ebene. Klicken Sie auf „+ Neue Konsole hinzufügen", um die erste zu erstellen.',
      'jsConsoles.section.owned':               'Konsolen auf dieser Ebene',
      'jsConsoles.name.placeholder':            'Konsolenname',
      'jsConsoles.enabled.label':               'Eingeschaltet',
      'jsConsoles.enabled.title':               'Ob die Konsole auf passenden Seiten erscheinen soll',
      'jsConsoles.delete.title':                'Diese Konsole löschen',
      'jsConsoles.delete.confirm':              'Konsole „:name" löschen? Der Ausgabeverlauf in aktiven Tabs geht verloren.',
      'modal.jsConsoles.titlePrefix':           'JS-Konsolen —',
      'modal.jsConsoles.hint':                  'Interaktive REPL — Strg+Enter führt Code im MAIN world der Seite aus, die Ausgabe (oder der Fehler) wird an den Log-Bereich unter der Eingabe angehängt. Ausgabe wird pro Tab im sessionStorage gehalten.',
      'modal.jsConsoles.add':                   '+ Neue Konsole hinzufügen',
      'modal.jsConsoles.close':                 'Schließen',
      'jsConsole.frontend.close.title':         'Konsole ausblenden (bis der Tab geschlossen wird)',
      'jsConsole.input.placeholder':            'Auszuführender Code (Strg+Enter zum Ausführen, Strg+L zum Löschen)',
      'jsConsole.clear.title':                  'Ausgabe löschen (Strg+L)',

      // === Output Console (Phase 2 finish, since v2.13.72) — read-only log viewer. ===
      'panes.style.clearBtn':                   'Löschen-Button',
      'panes.style.actionBtns':                 'Aktionsbuttons',
      'outputConsoles.contextMenu.disable':     'Ausgabekonsole schließen',
      'outputConsoles.empty':                   'Keine Ausgabekonsolen auf dieser Ebene. Klicken Sie auf „+ Neue Ausgabekonsole hinzufügen", um die erste zu erstellen.',
      'outputConsoles.section.owned':           'Ausgabekonsolen auf dieser Ebene',
      'outputConsoles.name.placeholder':        'Name der Ausgabekonsole',
      'outputConsoles.enabled.label':           'Eingeschaltet',
      'outputConsoles.enabled.title':           'Ob die Ausgabekonsole auf passenden Seiten erscheinen soll',
      'outputConsoles.delete.title':            'Diese Ausgabekonsole löschen',
      'outputConsoles.delete.confirm':          'Ausgabekonsole „:name" löschen? Der Log-Verlauf in aktiven Tabs geht verloren.',
      'modal.outputConsoles.titlePrefix':       'Ausgabekonsolen —',
      'modal.outputConsoles.hint':              'Schreibgeschützter Log-Viewer — erfasst console.log/warn/error/info von der Seite (auch asynchron). Löschen (⌫) leert nur den Viewer, nicht die Browser-Konsole.',
      'modal.outputConsoles.add':               '+ Neue Ausgabekonsole hinzufügen',
      'modal.outputConsoles.close':             'Schließen',
      'outputConsole.frontend.close.title':     'Ausgabekonsole ausblenden (bis der Tab geschlossen wird)',
      'outputConsole.clear.title':              'Viewer löschen (Strg+Shift+L)',
      'outputConsole.autoscroll.title':         'Automatisch zu den neuesten Logs scrollen',
      'outputConsole.empty':                    '(keine Logs — Warten auf console.* / JZ.* von der Seite)',
      'outputConsole.mode.standard.title':      'Nur console.* anzeigen (Klick / Strg+Shift+K → JZ.*)',
      'outputConsole.mode.jz.title':            'Nur window.JZ.* anzeigen (Klick / Strg+Shift+K → beide)',
      'outputConsole.mode.both.title':          'Beide anzeigen: console.* + JZ.* (Klick / Strg+Shift+K → nur console.*)',
      'outputConsole.filters.title':            'Stufenfilter',
      'outputConsole.filters.level.log':        'log',
      'outputConsole.filters.level.info':       'info',
      'outputConsole.filters.level.warn':       'warn',
      'outputConsole.filters.level.error':      'error',
      'outputConsole.filters.level.debug':      'debug',
      'outputConsole.search.title':             'In Logs suchen (Strg+Shift+S; Esc löscht)',
      'outputConsole.search.placeholder':       'Einträge filtern…',
      'outputConsole.tab.all':                  'Alle',
      'outputConsole.tab.jz':                   'JZconsole',
      'outputConsole.tab.console':              'Console',
      'outputConsole.tab.network':              'Network',
      'outputConsole.tab.errors':               'Fehler',
      'outputConsole.tab.datalayer':            'DataLayer',
      'outputConsole.net.field.callTime':       'Aufrufzeit',
      'outputConsole.net.field.status':         'Status',
      'outputConsole.net.field.type':           'Typ',
      'outputConsole.net.field.method':         'Methode',
      'outputConsole.net.field.domain':         'Domain',
      'outputConsole.net.field.path':           'Pfad',
      'outputConsole.net.field.remote':         'Remote-Adresse',
      'outputConsole.net.field.initiator':      'Initiator',
      'outputConsole.net.field.time':           'Zeit',
      'outputConsole.net.field.size':           'Größe',
      'outputConsole.net.field.url':            'URL',
      'outputConsole.net.field.error':          'Fehler',
      'outputConsole.net.status.err':           'Fehler/Andere',
      'outputConsole.filter.all':               'alle',
      'outputConsole.filter.none':              'keine',
      'outputConsole.net.domainFilter':         'nach Domain filtern…',
      'outputConsole.net.domainsInclude':       'Domains+',
      'outputConsole.net.domainsExclude':       'Domains-',
      'outputConsole.dl.newPushes':             'Neue Pushes',
      'outputConsole.dl.currentState':          'Aktueller Stand',
      'outputConsole.dl.expandAll':             'alle ausklappen',
      'outputConsole.dl.collapseAll':           'alle einklappen',
      'outputConsole.net.size':                 'Größe',
      'outputConsole.net.time':                 'Zeit',
      'outputConsole.err.field.type':           'Typ',
      'outputConsole.err.field.location':       'Ort',
      'outputConsole.err.field.stack':          'Stack',
      'outputConsole.apiHelp.title': 'JustZix — Kürzel & Fenster auf der Seite',
      'outputConsole.apiHelp.sec.global': 'Globale Kürzel — funktionieren auf jeder Seite',
      'outputConsole.apiHelp.g.tempCss': 'TEMP-CSS-Fenster — öffnen / schließen',
      'outputConsole.apiHelp.g.tempJs': 'TEMP-JS-Fenster — öffnen / schließen',
      'outputConsole.apiHelp.g.tempJsc': 'TEMP-JS-Konsole — öffnen / schließen',
      'outputConsole.apiHelp.g.tempOut': 'TEMP-Output-Konsole — öffnen / schließen',
      'outputConsole.apiHelp.g.ai': 'AI Helper — öffnen / schließen',
      'outputConsole.apiHelp.g.edit': 'Leisten-Bearbeitungsmodus — ein / aus',
      'outputConsole.apiHelp.sec.window': 'Im Fenster — wenn es fokussiert ist',
      'outputConsole.apiHelp.w.run': 'Code ausführen / Nachricht senden — JS-Pane, JS-Konsole, AI Helper',
      'outputConsole.apiHelp.w.history': 'Verlauf der eingegebenen Befehle — JS-Konsole',
      'outputConsole.apiHelp.w.clearConsole': 'Ausgabe leeren — JS-Konsole',
      'outputConsole.apiHelp.w.escSearch': 'Suchfeld leeren — Output-Konsole',
      'outputConsole.apiHelp.sec.output': 'Output-Konsole',
      'outputConsole.apiHelp.o.clear': 'Den aktiven Tab jeder Output-Konsole leeren',
      'outputConsole.apiHelp.o.help': 'Diese Hilfe anzeigen / ausblenden',
      'outputConsole.apiHelp.o.tabs': '6 Tabs: All, JZ, Console, Network, Errors, DataLayer — jeder mit seinem eigenen Filtersatz.',
      'outputConsole.apiHelp.o.network': 'Network: Alt+Klick auf eine Zeile fügt ihre Domain zum Einschluss-Filter hinzu, Alt+Shift+Klick zum Ausschluss-Filter.',
      'outputConsole.apiHelp.o.datalayer': 'DataLayer: ziehe die Leiste zwischen den Spalten, um sie zu skalieren.',
      'outputConsole.apiHelp.sec.mouse': 'Fenster auf der Seite — mit der Maus',
      'outputConsole.apiHelp.m.drag': 'Ziehe ein Fenster an seiner Kopfzeile, um es zu verschieben.',
      'outputConsole.apiHelp.m.resize': 'Ändere die Fenstergröße, indem du einen der 8 Griffe an Kanten und Ecken ziehst.',
      'outputConsole.apiHelp.m.snap': 'Bewege ein Fenster nah an ein anderes — sie rasten magnetisch zu einer gemeinsam beweglichen Gruppe zusammen.',
      'outputConsole.apiHelp.m.context': 'Ein Rechtsklick auf die Kopfzeile öffnet ein Menü: Fenster schließen, trennen, Schrift, Textgröße.',
      'outputConsole.apiHelp.m.tempDbl': 'Ein Doppelklick auf die Kopfzeile eines TEMP-Fensters schaltet zwischen Standard- und gemerkter Größe um.',
      'outputConsole.apiHelp.m.scroll': 'Das Scrollen mit dem Rad in einem Fenster scrollt nur seinen Inhalt, nicht die Seite darunter.',
      'outputConsole.apiHelp.sec.editmode': 'Leisten-Bearbeitungsmodus — nach Ctrl+Alt+S',
      'outputConsole.apiHelp.em.undo': 'Letzte Änderung rückgängig machen',
      'outputConsole.apiHelp.em.delete': 'Ausgewählte Labels löschen',
      'outputConsole.apiHelp.em.deselect': 'Alle Elemente abwählen',
      'outputConsole.apiHelp.em.move': 'Ziehe die ausgewählten Elemente mit der Maus — mehrere bewegen sich zusammen.',
      'outputConsole.apiHelp.em.select': 'Ein Klick wählt ein Element, Ctrl+Klick fügt es zur Auswahl hinzu oder entfernt es, Shift+Ziehen über einen leeren Bereich wählt mit einem Rechteck aus.',
      'outputConsole.apiHelp.em.label': 'Die Schaltfläche „+ Label" fügt eine statische Textbeschriftung hinzu; ein Doppelklick auf ein Label öffnet seinen Editor.',
      'outputConsole.apiHelp.sec.logging': 'Protokollieren in die Output-Konsole',
      'outputConsole.apiHelp.jzApi': 'Eigener Logger (kind=jz)',
      'outputConsole.apiHelp.aliases': 'Aliase: window.__JUSTZIX__ (immer), window.JZ (falls von der Seite nicht belegt).',
      'outputConsole.apiHelp.consoleApi': 'Standard-console.* (kind=console, automatisch erfasst)',
      'outputConsole.apiHelp.consoleNote': 'Jedes console.* der Seite wird an die ursprüngliche Konsole durchgereicht (DevTools funktionieren weiter) und an die Output-Konsole gesendet.',
      'outputConsole.apiHelp.footer': 'Vollständiges Handbuch (8 Sprachen): JustZix-Panel → Hilfe.',
      // === Onboarding / Tutorial ===
      'content.exec.cspBlocked': 'Diese Seite blockiert die Skriptausführung (CSP). Aktiviere „Benutzerskripte" für JustZix in den Browsereinstellungen.',
      'onboard.ui.back': 'Zurück',
      'onboard.ui.next': 'Weiter',
      'onboard.ui.finish': 'Fertig',
      'onboard.ui.copy': 'Kopieren',
      'onboard.ui.copied': 'Kopiert',
      'onboard.ui.hint': 'Führe diese Aktion aus, um fortzufahren',
      'onboard.ui.close': 'Schließen',
      'onboard.front.title': 'Frontend-Tutorial',
      'onboard.front.lead': 'Entdecke die JustZix-Werkzeuge direkt auf der Seite — die schwebende Schaltfläche, das Panel, Code-Fenster und Aktionsleisten.',
      'onboard.front.titleFirst': 'JustZix auf der Seite',
      'onboard.front.leadFirst': 'JustZix funktioniert auch direkt auf der Seite. Dieses Tutorial zeigt die schwebende Schaltfläche, CSS/JS-Fenster und Aktionsleisten — Schritt für Schritt.',
      'onboard.front.empty': 'Frontend-Tutorials folgen in Kürze.',
      'onboard.lr.titleFirst': 'Willkommen bei JustZix',
      'onboard.lr.title': 'Tutorial-Zentrum',
      'onboard.lr.leadFirst': 'Zum ersten Mal hier? Das interaktive Tutorial führt dich durch den Aufbau einer kompletten, funktionierenden Lösung — Schritt für Schritt, direkt im Panel.',
      'onboard.lr.lead': 'Starte den interaktiven Leitfaden — er führt dich Schritt für Schritt durch den Aufbau einer fertigen Lösung.',
      'onboard.lr.scenarios': 'Tutorials',
      'onboard.lr.start': 'Starten',
      'onboard.lr.later': 'Vielleicht später',
      'onboard.lr.noAuto': 'Beim Start nicht anzeigen',
      'onboard.proj.name': 'Dein erstes Projekt',
      'onboard.proj.desc': 'Von null zu einer funktionierenden Lösung: ein Verzeichnis, eine Aktionsleiste, eine Gruppe, ein Regelsatz mit CSS und JS sowie eine Schaltfläche, die den Effekt umschaltet.',
      'onboard.proj.meta': '19 Schritte · ~3 Min',
      'onboard.proj.s1.title': 'Willkommen bei JustZix',
      'onboard.proj.s1.body': 'JustZix injiziert deinen eigenen CSS- und JavaScript-Code in ausgewählte Seiten. Wir bauen jetzt eine komplette Lösung mit einer funktionierenden Schaltfläche — Schritt für Schritt. Es dauert etwa 3 Minuten.',
      'onboard.proj.s2.title': 'Verzeichnis hinzufügen',
      'onboard.proj.s2.body': 'Ein Verzeichnis bündelt die Regeln für eine Website. Klicke die hervorgehobene Schaltfläche <code>+</code> in der Seitenleiste.',
      'onboard.proj.s3.title': 'Verzeichnisname',
      'onboard.proj.s3.body': 'Gib einen Namen ein, z. B. <code>Mein erstes Projekt</code> — er ist nur ein Etikett für dich.',
      'onboard.proj.s4.title': 'URL-Muster',
      'onboard.proj.s4.body': 'Es entscheidet, auf welchen Seiten das Verzeichnis arbeitet. Gib eine Adresse mit Sternchen ein und drücke Enter, z. B. <code>https://example.com/*</code>. Zum Üben auf jeder Seite gib nur <code>*</code> ein.',
      'onboard.proj.s5.title': 'Schaltflächen-Etikett',
      'onboard.proj.s5.body': 'Maximal 3 Buchstaben — sie erscheinen auf der schwebenden JustZix-Schaltfläche auf der Seite.',
      'onboard.proj.s6.title': 'Aktionsleiste',
      'onboard.proj.s6.body': 'Eine Aktionsleiste ist ein Panel mit interaktiven Schaltflächen auf der Seite. Erstellen wir zuerst eine — klicke die hervorgehobene Aktionsleisten-Schaltfläche.',
      'onboard.proj.s7.title': 'Leiste hinzufügen',
      'onboard.proj.s7.body': 'Klicke, um eine neue Aktionsleiste in diesem Verzeichnis zu erstellen.',
      'onboard.proj.s8.title': 'Manager schließen',
      'onboard.proj.s8.body': 'Die Leiste ist fertig. Schließe das Aktionsleisten-Manager-Fenster, um zum Verzeichnis zurückzukehren.',
      'onboard.proj.s9.title': 'Gruppe öffnen',
      'onboard.proj.s9.body': 'Beim Erstellen des Verzeichnisses hat JustZix bereits eine erste Gruppe hinzugefügt. Klappe das Verzeichnis in der Seitenleiste auf und klicke diese Gruppe an, um sie zu öffnen, dann klicke "Weiter".',
      'onboard.proj.s10.title': 'Regelsatz hinzufügen',
      'onboard.proj.s10.body': 'Ein Regelsatz ist die eigentliche Regel — ein Paar aus CSS- und JavaScript-Code. Klicke die hervorgehobene Schaltfläche zum Hinzufügen eines Regelsatzes.',
      'onboard.proj.s11.title': 'Regelsatz öffnen',
      'onboard.proj.s11.body': 'Der neue Regelsatz ist im Baum unter der Gruppe erschienen. Klicke ihn an, um den Regelsatz-Editor zu öffnen, dann klicke "Weiter".',
      'onboard.proj.s12.title': 'CSS-Code',
      'onboard.proj.s12.body': 'Der CSS-Tab ist bereits aktiv. Füge den folgenden Code ein — er färbt die ganze Seite in Graustufen und bereitet den Effekt-Umschalter vor.',
      'onboard.proj.s13.title': 'JavaScript-Tab',
      'onboard.proj.s13.body': 'Klicke den <code>JavaScript</code>-Tab, um JS-Code zum Regelsatz hinzuzufügen.',
      'onboard.proj.s14.title': 'JavaScript-Code',
      'onboard.proj.s14.body': 'JS-Code läuft, wenn du die Seite öffnest. Füge den folgenden Code ein — er zeigt eine Meldung, die bestätigt, dass JustZix funktioniert. Das Feld hat eine Syntaxprüfung.',
      'onboard.proj.s15.title': 'Aktionen-Tab',
      'onboard.proj.s15.body': 'Jetzt fügen wir eine interaktive Schaltfläche hinzu. Klicke den Aktionen-Tab im Regelsatz-Editor.',
      'onboard.proj.s16.title': 'Schaltfläche hinzufügen',
      'onboard.proj.s16.body': 'Klicke, um ein Aktionselement hinzuzufügen. Standardmäßig entsteht eine Schaltfläche, die Code auf Abruf ausführt.',
      'onboard.proj.s17.title': 'Schaltflächen-Code',
      'onboard.proj.s17.body': 'Jede Schaltfläche hat ihren eigenen Code, der beim Klick auf der Seite läuft. Klicke das Codefeld der neuen Schaltfläche.',
      'onboard.proj.s18.title': 'Umschalter-Code',
      'onboard.proj.s18.body': 'Füge den Code ein, der die in CSS gesetzten Graustufen ein- und ausschaltet. Speichere dann die Änderungen im Code-Editor-Fenster.',
      'onboard.proj.s19.title': 'Fertig!',
      'onboard.proj.s19.body': 'Du hast eine komplette Lösung: ein Verzeichnis, eine Aktionsleiste, eine Gruppe, einen Regelsatz mit CSS- und JS-Code und eine funktionierende Schaltfläche. Öffne eine Seite, die zum URL-Muster passt — du siehst die Meldung, die Graustufen und die Schaltfläche, die sie umschaltet. Das Tutorial kannst du über das Einstellungsmenü neu starten.',
      'panes.contextMenu.font':                 'Schriftart',
      'panes.contextMenu.size':                 'Größe',
      'panes.contextMenu.defaultFont':          'Standard',
      'modal.confirmDismissDirty':              'Sie haben ungespeicherte Änderungen. Schließen und verwerfen?',
      'modal.confirmSaveBeforeClose':           'Sie haben ungespeicherte Änderungen.\n\nKlicken Sie OK, um zu speichern und zu schließen.\nKlicken Sie Abbrechen, um zur Verwerfen-Bestätigung zu gelangen.',
      'modal.confirmDiscardChanges':            'Schließen OHNE zu speichern? Die Änderungen gehen verloren.\n\nKlicken Sie OK, um die Änderungen zu verwerfen.\nKlicken Sie Abbrechen, um weiter zu bearbeiten.',
      'actionBars.moveLocked':                  'Verschieben nicht möglich — die Leiste hat Elemente, die außerhalb des neuen Bereichs liegen würden. Weisen Sie diese zuerst neu zu.',
      'action.bar.noneOption':                  '— keine Leiste —',
      'action.bar.unnamed':                     '(ohne Namen)',
      'action.bar.selectTitle':                 'Diese Aktion einer Leiste zuweisen. Leisten werden nach unten vererbt (Ordner → Gruppe → Regel).',
      'action.bar.noBarsHint':                  'Keine Aktionsleisten im Regelsatz-Baum — fügen Sie eine Leiste über den Button „Aktionsleisten" in Ordner/Gruppe/Regel hinzu.',

      // === Typed actions (since v2.12.x) ===
      'action.color.title':                     'Hintergrundfarbe (Button/Select/Input/Slider). Klick = Farbwähler, oder Hex daneben eingeben.',
      'action.colorText.title':                 'Textfarbe (und Leistenfüllung für SLIDER). Standardmäßig weiß, wenn nicht gesetzt.',
      'action.colorHex.title':                  'Hex-Code — kopieren (Strg+C) oder als reinen Text einfügen (Strg+V). Akzeptiert #RGB oder #RRGGBB.',
      'action.type.title':                      'Elementtyp (Button, Dropdown SELECT, …)',
      'action.type.button':                     'Button',
      'action.type.select':                     'Dropdown (SELECT)',
      'action.type.input':                      'Textfeld (INPUT)',
      'action.type.slider':                     'Schieberegler (SLIDER)',
      'action.type.textarea':                   'Mehrzeiliges Feld (TEXTAREA)',
      'action.type.toggle3':                    '3-Zustands-Umschalter (TOGGLE3)',

      'action.memory.label':                    'Wert merken:',
      'action.memory.none':                     'Nicht merken (wird beim Neuladen zurückgesetzt)',
      'action.memory.page':                     'Pro Seite (URL)',
      'action.memory.host':                     'Pro Domain',
      'action.memory.ruleSet':                  'Innerhalb des Regelsatzes (alle URLs, auf denen er läuft)',
      'action.memory.global':                   'Global (alle Seiten, gemeinsam)',
      'action.input.middleBtn':                 'INPUT · Code :code',
      'action.slider.middleBtn':                'SLIDER · :min–:max Schritt :step :unit · Code :code',
      'action.textarea.middleBtn':              'TEXTAREA · :rows Zeilen · Code :code',
      'action.toggle3.middleBtn':               'TOGGLE3 · :filled/3 Zustände · Code :code',
      'action.toggle3.defaultState1':           'OFF',
      'action.toggle3.defaultState2':           'MID',
      'action.toggle3.defaultState3':           'ON',
      'action.toggle3.stateLabel.placeholder':  'Beschriftung (max. 5)',
      'action.toggle3.stateValue.placeholder':  'Wert (an JS)',
      'action.toggle3.defaultStateTitle':       'Standardzustand (wenn kein Speicher)',

      'action.select.middleBtn.staticEmpty':    'SELECT statisch (keine Optionen)',
      'action.select.middleBtn.static':         'SELECT statisch · :count Optionen · Code :code',
      'action.select.middleBtn.js':             'SELECT js · Code :code',
      'action.select.empty':                    'Keine Optionen. Fügen Sie die erste mit dem Button unten hinzu.',
      'action.select.untitled':                 '(ohne Titel)',
      'action.select.optionNamePlaceholder':    'Name (sichtbar)',
      'action.select.optionValuePlaceholder':   'Wert (an JS übergeben)',
      'action.select.deleteOption':             'Diese Option löschen',

      // === Modal — action code ===
      'modal.actionCode.titlePrefix':           'Code des Aktionsbuttons:',
      'modal.actionCode.body':                  'JavaScript-Code, der ausgeführt wird, wenn der Aktionsbutton im schwebenden Panel angeklickt wird. Läuft im Seitenkontext.',
      'modal.actionCode.cancel':                'Abbrechen',
      'modal.actionCode.save':                  'Speichern',

      // === Modal — INPUT editor (since v2.13.0) ===
      'modal.inputEditor.titlePrefix':          'INPUT-Editor:',
      'modal.inputEditor.body':                 'Textfeld mit optionalem JS-Code, der bei Änderung (Enter/Verlassen) ausgelöst wird. Der Wert wird gemäß dem gewählten Bereich gemerkt.',
      'modal.inputEditor.placeholder':          'Platzhalter:',
      'modal.inputEditor.defaultValue':         'Standardwert:',
      'modal.inputEditor.colorPlaceholder':     'Platzhalterfarbe:',
      'modal.inputEditor.codeLabel':            'JS-Code (bei Änderung):',
      'modal.inputEditor.hint':                 'Im Code verfügbar: <code>value</code> (String aus dem Input), <code>$el</code> (HTMLInputElement), <code>$action</code> (id, label).',
      'modal.inputEditor.close':                'Schließen',
      'modal.colorClear':                       'Standardfarbe wiederherstellen',
      'modal.sliderEditor.colorLabel':          'Beschriftungsfarbe (links):',
      'modal.sliderEditor.colorValue':          'Wertfarbe (rechts):',
      'modal.selectOptions.appearance.title':   'Erscheinungsbild des Dropdowns:',
      'modal.selectOptions.appearance.hint':    'Für die <strong>js-Variante</strong>: Dropdown-Hintergrund und Optionsfarbe funktionieren in allen Browsern. Hintergründe für ausgewählt und Hover sind browserspezifisch (vom Betriebssystem abhängig) — für volle Anpassung die <em>statische</em> Variante verwenden.',
      'modal.selectOptions.colorDropdownBg':    'Dropdown-Hintergrund:',
      'modal.selectOptions.colorOption':        'Textfarbe der Option:',
      'modal.selectOptions.colorSelected':      'Hintergrund der ausgewählten Option:',
      'modal.selectOptions.colorHover':         'Hover-Hintergrund:',
      'modal.sliderEditor.titlePrefix':         'SLIDER-Editor:',
      'modal.sliderEditor.body':                'Schieberegler mit optionalem JS-Code, der ausgelöst wird, nachdem der Benutzer den Regler loslässt (`change`). Der Wert wird als Number übergeben.',
      'modal.sliderEditor.min':                 'Minimum:',
      'modal.sliderEditor.max':                 'Maximum:',
      'modal.sliderEditor.step':                'Schritt:',
      'modal.sliderEditor.defaultValue':        'Standardwert:',
      'modal.sliderEditor.unit':                'Einheit (optional):',
      'modal.sliderEditor.codeLabel':           'JS-Code (bei Änderung):',
      'modal.sliderEditor.hint':                'Im Code verfügbar: <code>value</code> (Number), <code>$el</code> (HTMLInputElement type=range), <code>$action</code> (id, label).',
      'modal.sliderEditor.close':               'Schließen',
      'modal.sliderEditor.errMaxGtMin':         'Das Maximum muss größer als das Minimum sein.',
      'modal.sliderEditor.errStepPositive':     'Der Schritt muss positiv sein.',
      'modal.textareaEditor.titlePrefix':       'TEXTAREA-Editor:',
      'modal.textareaEditor.body':              'Mehrzeiliges Textfeld mit optionalem JS-Code bei <code>change</code> (Verlassen). <strong>Enter</strong> im Textbereich ist ein echter Zeilenumbruch — löst KEINEN Code aus (anders als INPUT).',
      'modal.textareaEditor.placeholder':       'Platzhalter:',
      'modal.textareaEditor.defaultValue':      'Standardwert:',
      'modal.textareaEditor.rows':              'Anzahl der Zeilen:',
      'modal.textareaEditor.colorPlaceholder':  'Platzhalterfarbe:',
      'modal.textareaEditor.codeLabel':         'JS-Code (bei Änderung/Verlassen):',
      'modal.textareaEditor.hint':              'Im Code verfügbar: <code>value</code> (String aus dem Textbereich, kann <code>\\n</code> enthalten), <code>$el</code> (HTMLTextAreaElement), <code>$action</code> (id, label).',
      'modal.textareaEditor.close':             'Schließen',

      // === Modal — TOGGLE3 editor (od v2.13.25) ===
      'modal.toggle3Editor.titlePrefix':        'TOGGLE3-Editor:',
      'modal.toggle3Editor.body':               '3-Zustands-Segmentsteuerung. Das Anklicken eines inaktiven Zustands aktiviert ihn und führt JS-Code mit dem <code>value</code> des gewählten Zustands aus. Der Speicher behält den aktuellen Index (0-2) pro Aktion.',
      'modal.toggle3Editor.statesTitle':        'Zustände (genau 3, Radio = Standard):',
      'modal.toggle3Editor.colorBg':            'Hintergrundfarbe:',
      'modal.toggle3Editor.colorHover':         'Hover-Farbe:',
      'modal.toggle3Editor.colorInactiveText':  'Textfarbe inaktiv:',
      'modal.toggle3Editor.codeLabel':          'JS-Code (bei Zustandsänderung):',
      'modal.toggle3Editor.hint':               'Im Code verfügbar: <code>value</code> (state.value, fällt zurück auf state.label), <code>stateIdx</code> (0-2), <code>stateLabel</code>, <code>$el</code> (Container-Div), <code>$action</code>.',
      'modal.toggle3Editor.close':              'Schließen',

      // === Modal — SELECT editor (since v2.12.1, redesign v2.12.2 — variants) ===
      'modal.selectOptions.titlePrefix':        'SELECT-Editor:',
      'modal.selectOptions.idLabel':            'Aktions-ID:',
      'modal.selectOptions.selectorLabel':      'JS-Selektor:',
      'modal.selectOptions.copyId':             'ID kopieren',
      'modal.selectOptions.copySelector':       'JS-Selektor kopieren',
      'modal.selectOptions.variant.static':     'Statische Optionen + einzelner JS-Code',
      'modal.selectOptions.variant.js':         'Nur JS-Code (verwaltet das Dropdown)',
      'modal.selectOptions.static.body':        'Jede Option: <strong>Name</strong> (im Dropdown sichtbar) + <strong>Wert</strong> (an den Code als Variable <code>value</code> übergeben). Ein gemeinsamer Code läuft bei Änderung.',
      'modal.selectOptions.static.hint':        'Im Code verfügbar: <code>value</code> (String), <code>$el</code> (Select-Element), <code>$action</code> (id, label).',
      'modal.selectOptions.staticCodeLabel':    'JS-Code (gemeinsam):',
      'modal.selectOptions.js.body':            'Ihr JS-Code läuft einmal, nachdem das Dropdown in die Seite eingefügt wurde. Erhält die Referenz <code>$el</code> auf ein leeres <code>&lt;select&gt;</code> — Sie füllen die Optionen und behandeln <code>change</code> selbst.',
      'modal.selectOptions.js.hint':            'Verfügbar: <code>$el</code> (HTMLSelectElement), <code>$action</code> (id, label).',
      'modal.selectOptions.jsCodeLabel':        'JS-Code:',
      'modal.selectOptions.addOption':          '+ Option hinzufügen',
      'modal.selectOptions.close':              'Schließen',

      // === Modal — sync conflict ===
      'modal.syncConflict.title':               'Synchronisierungskonflikt',
      'modal.syncConflict.description':         'Sie haben sich bei einem Konto angemeldet, das bereits Daten in der Cloud hat, und dieses Gerät hat ebenfalls eine lokale Bibliothek. Wählen Sie, was zu tun ist — der Vorgang ist lokal unwiderruflich, aber lokale Daten können vor der Bestätigung exportiert werden.',
      'modal.syncConflict.localLabel':          'Auf diesem Gerät lokal:',
      'modal.syncConflict.serverLabel':         'Auf dem Cloud-Konto:',
      'modal.syncConflict.useServer.title':     'Daten vom Cloud-Konto verwenden',
      'modal.syncConflict.useServer.body':      'Lokale Ordner/Gruppen/Regeln werden durch die des Kontos ersetzt. Wählen Sie dies, wenn das Konto neuere und genauere Daten als dieses Gerät hat.',
      'modal.syncConflict.sendMine.title':      'Meine lokalen Daten an das Konto senden',
      'modal.syncConflict.sendMine.body':       'Der Kontostand wird durch das ersetzt, was Sie auf diesem Gerät haben. Wählen Sie dies, wenn dieses Gerät neuere und genauere Daten hat.',
      'modal.syncConflict.merge.title':         'Beide Sätze kombinieren',
      'modal.syncConflict.merge.body':          'Lokale und Cloud-Entitäten werden zusammen behalten. Wenn dasselbe Element an beiden Stellen existiert (gleiche ID), gewinnt die Kontoversion. Nur lokal vorhandene Elemente werden beim nächsten Sync an das Konto gesendet.',
      'modal.syncConflict.exportLocal':         'Zuerst lokal exportieren',
      'modal.syncConflict.cancel':              'Anmeldung abbrechen (abmelden)',

      // === Modal — share import ===
      'modal.shareImport.title':                'Aus einem JustZix-Link importieren',
      'modal.shareImport.body':                 'Fügen Sie den vollständigen Link oder nur den Token <strong>JZS-XXXX-XXXX-XXXX-XXXX</strong> ein:',
      'modal.shareImport.placeholder':          'https://www.justzix.com/s/JZS-XXXX-XXXX-XXXX-XXXX',
      'modal.shareImport.cancel':               'Abbrechen',
      'modal.shareImport.fetch':                'Abrufen',
      'modal.shareImport.preview.title':        'Vorschau des geteilten Exports',
      'modal.shareImport.jsWarn':               '<strong>⚠ WARNUNG:</strong> Der Export enthält JavaScript-Code. Der Code läuft auf Seiten, die den Regeln entsprechen — prüfen Sie, was er tut, bevor Sie importieren.',
      'modal.shareImport.showCode':             'JavaScript-Code anzeigen',
      'modal.shareImport.back':                 '← Zurück',
      'modal.shareImport.confirm':              'Importieren',

      // === Modal — my shares ===
      'modal.shares.title':                     'Meine geteilten Links',
      'modal.shares.body':                      'Zeitlich begrenzte öffentliche Links zu Exporten. Das Backend behält die Daten bis zum Ablauf und löscht sie dann.',
      'modal.shares.howTo':                     'Um einen neuen Link zu erstellen — öffnen Sie einen Export (vollständig / Ordner / Gruppe / Regelsatz) und klicken Sie auf <strong>„URL teilen"</strong>.',
      'modal.shares.refresh':                   'Aktualisieren',
      'modal.shares.close':                     'Schließen',

      // === Modal — share create ===
      'modal.shareCreate.title':                'Export per Link teilen',
      'modal.shareCreate.activeFor':            'Der Link wird aktiv sein für:',
      'modal.shareCreate.ttl.1h':               '1 Stunde',
      'modal.shareCreate.ttl.6h':               '6 Stunden',
      'modal.shareCreate.ttl.12h':              '12 Stunden',
      'modal.shareCreate.ttl.24h':              '24 Stunden',
      'modal.shareCreate.ttl.default':          '(Standard)',
      'modal.shareCreate.ttl.48h':              '48 Stunden',
      'modal.shareCreate.expireNote':           'Nach Ablauf funktioniert der Link nicht mehr. Sie können ihn früher über „Meine Freigaben" widerrufen.',
      'modal.shareCreate.cancel':               'Abbrechen',
      'modal.shareCreate.generate':             'Link generieren',
      'modal.shareCreate.result.title':         'Link generiert',
      'modal.shareCreate.result.urlLabel':      'Senden Sie diesen Link an den Empfänger:',
      'modal.shareCreate.result.copyUrl':       'Link kopieren',
      'modal.shareCreate.result.tokenLabel':    'Oder nur den Token (zum Einfügen in „Aus URL importieren"):',
      'modal.shareCreate.result.copyToken':     'Token kopieren',
      'modal.shareCreate.result.goToMine':      '„Meine Freigaben" anzeigen',
      'modal.shareCreate.result.close':         'Schließen',

      // === Modal — sync result ===
      'modal.syncResult.title':                 'Synchronisierungsergebnis',
      'modal.syncResult.close':                 'OK, verstanden',

      // === Modal — cloud sync ===
      'modal.sync.title':                       'Cloud-Synchronisierung',
      'modal.sync.signedOut.tab.register':      'Erstes Gerät',
      'modal.sync.signedOut.tab.login':         'Ich habe einen Schlüssel',
      'modal.sync.signedOut.tab.recover':       'Ich habe meinen Schlüssel verloren',
      'modal.sync.signedOut.register.body':     'Generieren Sie einen neuen Sync-Schlüssel. <strong>Bewahren Sie ihn an einem sicheren Ort auf</strong> — das Backend speichert nur einen Hash. Der Schlüssel ermöglicht die Anmeldung auf einem zweiten Gerät und den Zugriff auf dieselbe Bibliothek.',
      'modal.sync.signedOut.deviceLabel':       'Gerätebeschriftung (optional):',
      'modal.sync.signedOut.deviceLabel.placeholder1': 'z. B. Arbeits-PC',
      'modal.sync.signedOut.deviceLabel.placeholder2': 'z. B. Laptop',
      'modal.sync.signedOut.yourKey':           'Ihr Sync-Schlüssel:',
      'modal.sync.signedOut.regenerate':        'Neuen generieren',
      'modal.sync.signedOut.copyClipboard':     'In die Zwischenablage kopieren',
      'modal.sync.signedOut.warnSave':          '⚠ <strong>Kopieren und speichern Sie den Schlüssel jetzt.</strong> Der Schlüssel ist nach der Registrierung auch im Tab „Schlüssel" verfügbar — es ist jedoch besser, eine Kopie in einem Passwort-Manager aufzubewahren.',
      'modal.sync.signedOut.login.body':        'Fügen Sie den Schlüssel ein, den Sie bei der Registrierung des ersten Geräts gesehen haben. Format: <code>SYNC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX</code>.',
      'modal.sync.signedOut.keyLabel':          'Sync-Schlüssel:',
      'modal.sync.signedOut.recover.body':      'Wenn auf dem Konto eine <strong>bestätigte</strong> E-Mail festgelegt wurde — geben Sie sie ein und senden Sie einen Wiederherstellungslink. Nach dem Klick auf den Link in der E-Mail erhalten Sie einen <strong>neuen Schlüssel</strong> (einmalig). Alle aktiven Sitzungen werden abgemeldet.',
      'modal.sync.signedOut.email':             'Konto-E-Mail:',
      'modal.sync.signedOut.email.placeholder': 'jemand@example.com',
      'modal.sync.signedOut.cancel':            'Abbrechen',
      'modal.sync.signedOut.regSubmit':         'Gerät registrieren',
      'modal.sync.signedOut.loginSubmit':       'Anmelden',
      'modal.sync.signedOut.recoverSubmit':     'Wiederherstellungslink senden',
      'modal.sync.signedIn.tab.status':         'Status',
      'modal.sync.signedIn.tab.key':            'Zugriffsschlüssel',
      'modal.sync.signedIn.tab.backups':        'Sicherungen',
      'modal.sync.signedIn.tab.email':          'E-Mail & Wiederherstellung',
      'modal.sync.signedIn.status.account':     'Konto',
      'modal.sync.signedIn.status.pending':     'Ausstehende Änderungen:',
      'modal.sync.signedIn.status.cloudLib':    'Cloud-Bibliothek',
      'modal.sync.signedIn.status.lastCursor':  'Letzter Abruf (Cursor):',
      'modal.sync.signedIn.status.syncNow':     'Jetzt synchronisieren',
      'modal.sync.signedIn.status.snapshot':    'Vollständiger Snapshot',
      'modal.sync.signedIn.status.refresh':     'Kontodaten aktualisieren',
      'modal.sync.signedIn.status.reconcile':   'Vollständige Synchronisierung mit Konto',
      'modal.sync.signedIn.status.sessions':    'Aktive Sitzungen (Geräte)',
      'modal.sync.signedIn.status.history':     'Synchronisierungsverlauf',
      'modal.sync.history.empty':               'Noch keine Synchronisierungen aufgezeichnet.',
      'modal.sync.history.viewAll':             'Vollständigen Verlauf anzeigen',
      'modal.sync.history.changes':             'Änderungen:',
      'modal.sync.history.device':              'Gerät:',
      'modal.sync.history.fromDevices':         'Abgerufen von:',
      'modal.sync.history.stateAfter':          'Status nach Synchronisierung:',
      'modal.sync.history.noDetail':            'Keine Änderungsdetails (älterer Eintrag).',
      'modal.sync.signedIn.key.body':           'Sync-Schlüssel für dieses Konto. Geben Sie ihn auf einem zweiten Gerät ein, um die Bibliothek zu synchronisieren.',
      'modal.sync.signedIn.key.yourKey':        'Ihr Schlüssel:',
      'modal.sync.signedIn.key.reveal':         'Schlüssel anzeigen',
      'modal.sync.signedIn.key.copy':           'In die Zwischenablage kopieren',
      'modal.sync.signedIn.key.forget':         'Schlüssel von diesem Gerät entfernen',
      'modal.sync.signedIn.key.warn':           '⚠ Der Schlüssel gewährt vollen Zugriff auf die Bibliothek. Zeigen Sie ihn nicht öffentlich. Wenn Sie ihn nicht lokal aufbewahren möchten — klicken Sie auf <strong>Schlüssel von diesem Gerät entfernen</strong> (bewahren Sie eine Kopie in einem Passwort-Manager auf). Die Anmeldung funktioniert weiterhin, aber der Schlüssel kann nicht erneut angezeigt werden.',
      'modal.sync.signedIn.key.noKey':          'Der Schlüssel ist auf diesem Gerät nicht gespeichert (Anmeldung vor Einführung dieser Funktion oder manuell entfernt). Sie können ihn nur über die Wiederherstellungs-E-Mail wiederherstellen oder wenn Sie eine Kopie woanders gespeichert haben.',
      'modal.sync.signedIn.backups.hint':       'Cloud-Sicherungen — die Wiederherstellung ersetzt die gesamte Bibliothek.',
      'modal.sync.signedIn.backups.label':      'Beschriftung der neuen Sicherung (optional):',
      'modal.sync.signedIn.backups.placeholder':'z. B. Vor großer Änderung',
      'modal.sync.signedIn.backups.create':     'Jetzt Sicherung erstellen',
      'modal.sync.signedIn.backups.refresh':    'Liste aktualisieren',
      'modal.sync.signedIn.email.body':         'Die E-Mail ermöglicht die <strong>Wiederherstellung</strong> — wenn Sie den Schlüssel verlieren, können Sie einen neuen erhalten, indem Sie auf einen an diese Adresse gesendeten Link klicken. Das Hinzufügen einer E-Mail erfordert eine Bestätigung (Link in der Mail).',
      'modal.sync.signedIn.email.add':          'E-Mail hinzufügen (Wiederherstellung):',
      'modal.sync.signedIn.email.placeholder':  'jemand@example.com',
      'modal.sync.signedIn.email.submit':       'Bestätigungslink senden',
      'modal.sync.signedIn.email.remove':       'E-Mail entfernen',
      'modal.sync.signedIn.email.recoveryFlow': 'Wiederherstellungsablauf: wählen Sie auf einem anderen Gerät (oder in einer Situation ohne Schlüssel) den Tab <strong>„Ich habe meinen Schlüssel verloren"</strong> im Anmeldepanel, geben Sie diese E-Mail ein — das Backend sendet einen Link, der einen neuen Schlüssel generiert und alle aktiven Sitzungen ungültig macht.',
      'modal.sync.signedIn.logout':             'Dieses Gerät abmelden',
      'modal.sync.signedIn.close':              'Schließen',

      // === Content script (floating button + actions panel) ===
      'content.float.titleGloballyOff':         'Plugin global deaktiviert — klicken Sie auf das Erweiterungssymbol zum Aktivieren',
      'content.float.titleNormal':              'Linksklick: Ordner ein-/ausschalten • Rechtsklick: Regelsatzliste anzeigen • Ziehen zum Verschieben',
      'content.panel.empty':                    'Keine aktiven Regelsätze',
      'content.panel.actionBarsTitle':          'Aktionsleisten',
      'content.panel.connectionsTitle':         'Verbindungen',
      'content.actionsPanel.dragHandle':        'Ziehen zum Verschieben',
      'content.editMode.banner':                'Leisten-Bearbeitungsmodus — Klick/Ctrl+Klick wählt aus, Ziehen verschiebt · Ctrl+Z rückgängig · Ctrl+Alt+S zum Beenden',
      'content.editMode.undoEmpty':             'Nichts rückgängig zu machen',
      'content.editMode.addLabel':              '+ Label',
      'content.editMode.resetBar':              'Leiste zurücksetzen',
      'content.editMode.resetConfirm':          'Die Leiste auf das Standardlayout zurücksetzen? Eigene Positionen, Größen und Beschriftungen werden entfernt.',
      'content.labelEditor.text':               'Text:',
      'content.labelEditor.font':               'Schriftart:',
      'content.labelEditor.size':               'Größe:',
      'content.labelEditor.color':              'Farbe:',
      'content.labelEditor.delete':             'Löschen',
      'content.labelEditor.close':              'Schließen',
      'content.labelContextMenu.delete':        'Beschriftung löschen',
      'content.bar.hideUntilReload':            'Diese Leiste schließen (bis zum Neuladen)',
      'content.bar.disconnectFrom':             'Trennen von: :name',
      'content.bar.disconnectAll':              'Alle Verbindungen trennen',
      'content.bar.fallbackName':               'Leiste',
      'content.float.label':                    '⊕ Hauptbutton',
      'content.toast.noFoldersForPage':         'Keine Ordner passen zu dieser Seite',
      'content.toast.floatVisible':             'Schwebender Button aktiviert',
      'content.toast.floatHidden':              'Schwebender Button ausgeblendet',

      // === Background (context menu + JS error logger) ===
      'background.contextMenu.toggleFloat':     'Schwebenden Button für diese Seite ein-/ausschalten',
      'background.contextMenu.editMode':        'Bearbeitungsmodus der Aktionsleiste (ein-/ausschalten)',
      'background.contextMenu.actionBars':      'Aktionsleisten',
      'background.contextMenu.actionBarsEmpty': '(keine Leisten für diese Seite)',
      'background.contextMenu.actionBarFallback': '(ohne Namen)',
      'background.contextMenu.cssPanes':         'CSS-Panels',
      'background.contextMenu.cssPanesEmpty':    '(keine CSS-Panels für diese Seite)',
      'background.contextMenu.cssPaneFallback':  '(ohne Namen)',
      'background.contextMenu.jsPanes':          'JS-Panels',
      'background.contextMenu.jsPanesEmpty':     '(keine JS-Panels für diese Seite)',
      'background.contextMenu.jsPaneFallback':   '(ohne Namen)',
      'background.contextMenu.jsConsoles':       'JS-Konsolen',
      'background.contextMenu.jsConsolesEmpty':  '(keine Konsolen für diese Seite)',
      'background.contextMenu.jsConsoleFallback':'(ohne Namen)',
      'background.contextMenu.outputConsoles':       'Ausgabekonsolen',
      'background.contextMenu.outputConsolesEmpty':  '(keine Ausgabekonsolen für diese Seite)',
      'background.contextMenu.outputConsoleFallback':'(ohne Namen)',
      'background.contextMenu.tempPanes':            'Temporäres Fenster (TEMP)',
      'background.contextMenu.tempCss':              'TEMP CSS-Panel (Ctrl+Alt+G)',
      'background.contextMenu.tempJs':               'TEMP JS-Panel (Ctrl+Alt+H)',
      'background.contextMenu.tempJsConsole':        'TEMP JS-Konsole (Ctrl+Alt+J)',
      'background.contextMenu.tempOutput':           'TEMP Ausgabekonsole (Ctrl+Alt+K)',
      'pane.collapse.title':                      'Fenster auf Titelleiste reduzieren',
      'pane.expand.title':                        'Fenster ausklappen',
      'aiHelper.form.submit':                     'OK',
      'aiHelper.form.cancel':                     'Abbrechen',
      'aiHelper.form.yes':                        'Ja',
      'aiHelper.form.no':                         'Nein',
      'aiHelper.form.askTitle':                   'Frage',
      'aiHelper.form.confirmTitle':               'Bestätigung',
      'aiHelper.form.updateConfirmTitle':               'Aktualisieren?',
      'aiHelper.style.title':                           'AI Helper – Aussehen',
      'aiHelper.style.role.user':                       'Deine Nachrichten',
      'aiHelper.style.role.assistant':                  'AI-Antworten',
      'aiHelper.style.role.tool':                       'Tool-Ergebnisse',
      'aiHelper.style.role.form':                       'Formulare',
      'aiHelper.style.font':                            'Schrift',
      'aiHelper.style.size':                            'Größe',
      'aiHelper.style.color':                           'Text',
      'aiHelper.style.bg':                              'Hintergrund',
      'aiHelper.style.reset':                           'Standard wiederherstellen',
      'aiHelper.style.resetAll':                        'Alle Standardwerte wiederherstellen',
      'aiHelper.confirm.update.folder':                 'Katalog aktualisieren',
      'aiHelper.confirm.update.group':                  'Gruppe aktualisieren',
      'aiHelper.confirm.update.rule':                   'Regelwerk aktualisieren',
      'aiHelper.confirm.update.bar':                    'Aktionsleiste aktualisieren',
      'aiHelper.confirm.update.action':                 'Aktion aktualisieren',
      'aiHelper.confirm.update.cssPane':                'CSS-Fenster aktualisieren',
      'aiHelper.confirm.update.jsPane':                 'JS-Fenster aktualisieren',
      'aiHelper.confirm.update.jsConsole':             'JS-Konsole aktualisieren',
      'aiHelper.confirm.update.outputConsole':         'Output-Konsole aktualisieren',
      'aiHelper.form.createConfirmTitle':         'Erstellen?',
      'aiHelper.form.confirmed':                  'bestätigt',
      'aiHelper.form.cancelled':                  'abgebrochen',
      'tempPane.close.title':                        'Temporäres Fenster schließen (Inhalt bleibt für die Sitzung erhalten)',
      'tempPane.clear.title':                        'Fensterinhalt löschen',
      'background.jsError.prefix':              '[JustZix] JS-Fehler in Regelsatz/Aktion ":id":\n',
      // === AI Helper (od v2.13.127) ===
      'settings.aiHelper.label': 'AI Helper',
      'settings.aiHelper.title': 'API-Schlüssel für AI Helper konfigurieren',
      'modal.aiHelper.title': 'AI Helper',
      'modal.aiHelper.intro': 'Gib die API-Schlüssel der Anbieter ein, die du nutzen möchtest. Einer genügt.',
      'modal.aiHelper.keyOpenai': 'API-Schlüssel — OpenAI',
      'modal.aiHelper.keyAnthropic': 'API-Schlüssel — Anthropic',
      'modal.aiHelper.keyGemini': 'API-Schlüssel — Gemini',
      'modal.aiHelper.defaultProvider': 'Standardanbieter',
      'modal.aiHelper.defaultModel': 'Standardmodell',
      'modal.aiHelper.modelAuto': '— auto (höchstes) —',
      'modal.aiHelper.loadModels': 'Modelle laden',
      'modal.aiHelper.securityNote': '<strong>Sicherheit:</strong> API-Schlüssel werden standardmäßig nur im lokalen Speicher dieses Browsers (<code>chrome.storage.local</code>) gespeichert. Wird „Sync" neben einem Schlüssel aktiviert, wird dieser Schlüssel über die JustZix-Cloud (dieselbe, mit der die Erweiterung ihre übrigen Daten synchronisiert) an deine anderen Geräte gesendet. Nicht markierte Schlüssel verlassen dieses Gerät nie.',
      'modal.aiHelper.syncKey':                'Sync',
      'modal.aiHelper.syncKeyTitle': 'Diesen Schlüssel über die JustZix-Cloud geräteübergreifend synchronisieren',
      'modal.aiHelper.cancel': 'Abbrechen',
      'modal.aiHelper.save': 'Speichern',
      'modal.aiHelper.needKey': 'Gib zuerst einen API-Schlüssel ein.',
      'modal.aiHelper.loadingModels': 'Modelle werden geladen…',
      'modal.aiHelper.errModels': 'Modelle konnten nicht geladen werden',
      'modal.aiHelper.modelsLoaded': 'Modelle verfügbar',
      'aiHelper.win.title': 'AI Helper',
      'aiHelper.win.close': 'Schließen',
      'aiHelper.win.provider': 'Anbieter',
      'aiHelper.win.model': 'Modell',
      'aiHelper.win.clear': 'Unterhaltung löschen',
      'aiHelper.win.clearConfirm': 'Die gesamte Unterhaltung löschen und neu beginnen?',
      'aiHelper.win.inputPlaceholder': 'Frag nach Hilfe mit CSS, JS oder einer Aktion…',
      'aiHelper.win.send': 'Senden (Strg+Enter)',
      'aiHelper.win.welcome': 'Hallo! Ich helfe dir, CSS, JavaScript oder Aktionen für diese Seite vorzubereiten. Beschreibe, was du erreichen möchtest.',
      'aiHelper.win.you': 'Du',
      'aiHelper.win.assistant': 'AI Helper',
      'aiHelper.win.thinking': 'Denke nach…',
      'aiHelper.win.error': 'API-Verbindungsfehler',
      'aiHelper.win.noKeys': 'Keine API-Schlüssel. Füge einen Schlüssel unter Einstellungen → AI Helper hinzu.',
      'aiHelper.win.noModel': 'Wähle ein Modell.',
      'aiHelper.win.loadingModels': 'Lädt…',
      'aiHelper.win.modelsError': 'Fehler beim Laden der Modelle',
      'background.contextMenu.aiHelper': 'AI Helper',
      'popup.aiHelper.label': 'AI Helper',
      'popup.aiHelper.on': 'Geöffnet — zum Schließen klicken (Strg+Alt+\\)',
      'popup.aiHelper.off': 'Geschlossen — zum Öffnen klicken (Strg+Alt+\\)',
      'popup.aiHelper.unavailable': 'Auf dieser Seite nicht verfügbar',
      // === AI Helper etap 2 (od v2.13.128) ===
      'aiHelper.win.maxRounds': 'Das Limit der Tool-Schritte für diese Runde ist erreicht. Sag mir, wie es weitergehen soll.',
      'aiHelper.win.copy':                        'Gespräch kopieren',
      'aiHelper.win.copy.all':                    'Ganzes Gespräch kopieren',
      'aiHelper.win.copy.lastInput':              'Letzten API-Prompt kopieren (System + Verlauf + Eingabe)',
      'aiHelper.win.copyNoInput':                 'Nichts zu kopieren',
      'aiHelper.copy.lastInput.prefix':           '[JustZix → an API weiterleiten] Der GESAMTE letzte an das Modell gesendete Prompt wurde kopiert (System + Verlauf + letzte Nachricht). Sende den Block unten als ERSTE user-message einer neuen Konversation an die API. ENTFERNE diesen Header vor dem Senden.',
      'aiHelper.win.copied':                      'Kopiert',
      'aiHelper.win.copyFailed':                  'Kopieren fehlgeschlagen — prüfe die Zwischenablage-Berechtigung.',
      'aiHelper.win.font':                        'Schrift',
      'aiHelper.win.fontSize':                    'Größe',
      'aiHelper.win.defaultFont':                 'Standard',
      'aiHelper.win.stop':                        'Agent stoppen',
      'aiHelper.win.stopped':                     'Vom Benutzer unterbrochen.',
      'aiHelper.win.ctxUsage':                    'Modell-Kontextnutzung',
      'aiHelper.win.ctxUsageDetail':              'Kontext',
      'aiHelper.win.compact':                     'Konversation verdichten (LLM-Zusammenfassung)',
      'aiHelper.win.compactNoop':                 'Verlauf zu kurz — nichts zu verdichten.',
      'aiHelper.win.compactNoProvider':           'Zuerst Anbieter und Modell auswählen.',
      'aiHelper.win.compactErr':                  'Verdichtungsfehler',
      'aiHelper.win.compactEmpty':                'Modell lieferte ein leeres Ergebnis.',
      'aiHelper.win.compactedPrefix':             'Zusammengefasst',
      'aiHelper.win.compactedSuffix':             'frühere Nachrichten:',
      'aiHelper.win.telemetryTooltip':            'Zeiten und Tokens dieser Sitzung',
      'aiHelper.win.telemetryTimers':             'Zeit: seit letzter Nachricht (u) / seit letztem Tool (t) / gesamte Agent-Aktivität (a)',
      'aiHelper.win.telemetryCurrent':            'Tokens in aktueller Aktion (↑ Eingabe / ↓ Ausgabe)',
      'aiHelper.win.telemetrySession':            'Tokens insgesamt in dieser Sitzung',
      'aiHelper.win.msgTelem':                    'Tokens dieser Runde und Modell-Antwortzeit',
      'aiHelper.win.toolDur':                     'Tool-Ausführungszeit',
      'aiHelper.win.stepIdBadge':                 'Verlaufsschritt-Nummer',
      'aiHelper.form.freeText':                   'Oder mit Text antworten',
      'aiHelper.form.freeTextOnly':               'Deine Antwort',
      'aiHelper.form.freeTextPlaceholder':        'Antwort eingeben…',
      'aiHelper.win.turnLimit.body':              'Das 10-Schritte-Limit für Tools in dieser Runde wurde erreicht. Der Agent hat pausiert — prüfe das letzte Tool und sein Ergebnis. Hier mit zurückgesetztem Limit fortfahren oder anhalten?',
      'aiHelper.win.turnLimit.lastTool':          'Letztes Tool',
      'aiHelper.win.turnLimit.lastResult':        'Ergebnis',
      'aiHelper.win.turnLimit.continue':          'Fortfahren (Limit zurücksetzen)',
      'aiHelper.win.turnLimit.stop':              'Anhalten',
      'aiHelper.win.turnLimit.stoppedByUser':     'Angehalten.',
      'aiHelper.copy.title':                      'JustZix AI Helper – Gespräch',
      'aiHelper.copy.toolResult':                 'Tool-Ergebnis',
      'aiHelper.tool.label': 'Tool',
      'aiHelper.tool.rejected': 'Der Benutzer hat den Vorgang abgelehnt.',
      'aiHelper.confirm.folder': 'AI Helper möchte einen ORDNER erstellen:',
      'aiHelper.confirm.group': 'AI Helper möchte eine GRUPPE erstellen:',
      'aiHelper.confirm.rule': 'AI Helper möchte ein REGELSET erstellen:',
      'aiHelper.confirm.create.cssPane': 'AI Helper möchte ein CSS-FENSTER erstellen:',
      'aiHelper.confirm.create.jsPane': 'AI Helper möchte ein JS-FENSTER erstellen:',
      'aiHelper.confirm.create.jsConsole': 'AI Helper möchte eine JS-KONSOLE erstellen:',
      'aiHelper.confirm.create.outputConsole': 'AI Helper möchte eine OUTPUT-KONSOLE erstellen:',
      'aiHelper.confirm.setPaneCode': 'AI Helper möchte Code in das Fenster „:name“ (:id) einfügen.\nLänge: aktuell :old Z., neu :new Z.\nHINWEIS: Persistenter Fenster-Code lebt pro Tab (sessionStorage), nicht sitzungsübergreifend.',
      'aiHelper.confirm.bar': 'AI Helper möchte eine AKTIONSLEISTE erstellen:',
      'aiHelper.confirm.action': 'AI Helper möchte eine AKTION erstellen:',
      // === Wyszukiwanie w kodzie (od v2.13.130) ===
      'codeSearch.placeholder': 'Im Code suchen… (Strg+Alt+F)',
      'codeSearch.btn': 'Suchen',
      'codeSearch.prev': 'Vorheriger Treffer (Strg+Alt+←)',
      'codeSearch.next': 'Nächster Treffer (Strg+Alt+→)',
      'codeSearch.noMatch': 'Keine Treffer',
    },

    fr: {
      'dashboard.title': 'Tableau de bord',
      'dashboard.subtitle': 'Un aperçu de votre bibliothèque JustZix et des raccourcis rapides.',
      'dashboard.stats.title': 'Bibliothèque',
      'dashboard.stats.folders': 'Dossiers',
      'dashboard.stats.groups': 'Groupes',
      'dashboard.stats.rules': 'Jeux de règles',
      'dashboard.stats.actions': 'Actions',
      'dashboard.stats.bars': 'Barres d\'actions',
      'dashboard.stats.windows': 'Fenêtres intégrées',
      'dashboard.stats.withCss': 'Jeux avec CSS',
      'dashboard.stats.withJs': 'Jeux avec JS',
      'dashboard.stats.clicks': 'Clics d\'action',
      'dashboard.version.title': 'Version de l\'extension',
      'dashboard.version.checking': 'Vérification…',
      'dashboard.version.current': 'Installée : :version',
      'dashboard.version.upToDate': 'Vous êtes à jour',
      'dashboard.version.outdated': 'Une version plus récente est disponible : :version',
      'dashboard.storage.title': 'Stockage',
      'dashboard.backup.title': 'Dernière sauvegarde',
      'dashboard.backup.signIn': 'Connectez-vous à la synchronisation cloud pour créer des sauvegardes cloud.',
      'dashboard.backup.none': 'Aucune sauvegarde cloud pour l\'instant.',
      'dashboard.backup.noLabel': '(sans libellé)',
      'dashboard.backup.last': 'Dernière sauvegarde : :date',
      'dashboard.recent.title': 'Jeux de règles modifiés récemment',
      'dashboard.recent.empty': 'Aucun jeu de règles n\'a encore été modifié.',
      'dashboard.examples.title': 'Derniers exemples',
      'dashboard.posts.title': 'Du blog',
      'dashboard.feed.loading': 'Chargement…',
      'dashboard.feed.empty': 'Rien à afficher pour le moment.',
      'dashboard.home.title': 'Tableau de bord',
      'dashboard.usage.title': 'Statistiques d\'utilisation',
      'dashboard.usage.cssApplied': 'CSS appliqué',
      'dashboard.usage.jsExecuted': 'JS exécuté',
      'dashboard.usage.windowsShown': 'Fenêtres affichées',
      'dashboard.usage.empty': 'Aucune donnée — utilisez l\'extension sur des pages web.',
      'dashboard.tip.title': 'Astuce',
      'dashboard.tip.prev': 'Astuce précédente',
      'dashboard.tip.next': 'Astuce suivante',
      'dashboard.tip.manual': 'Ouvrir le manuel complet',
      'tip.dash.1': 'Faites un clic droit sur le bouton flottant pour ouvrir le panneau avec la liste des dossiers et des jeux de règles.',
      'tip.dash.2': 'Ctrl+Alt+S bascule le mode édition des barres d\'actions — les éléments peuvent être librement positionnés et redimensionnés.',
      'tip.dash.3': 'Chaque jeu de règles peut avoir ses propres motifs d\'URL qui le limitent à des sous-pages spécifiques.',
      'tip.dash.4': 'Ctrl+Alt+\\ ouvre l\'Assistant IA — un assistant qui génère du CSS/JS pour une tâche précise.',
      'tip.dash.5': 'Les éditeurs de code disposent d\'une barre de recherche — Ctrl+Alt+F y déplace le focus.',
      'tip.dash.6': 'Un clic gauche sur le bouton flottant active ou désactive tous les dossiers correspondants à la fois.',
      'tip.dash.7': 'Un jeu de règles conserve ses 5 dernières versions CSS et JS dans l\'historique — vous pouvez toujours annuler une modification.',
      'tip.dash.8': 'Faites glisser un jeu de règles sur le nom d\'un autre groupe ou dossier pour l\'y déplacer.',
      'tip.dash.9': 'Les fenêtres TEMP (Ctrl+Alt+G/H/J/K) sont des volets CSS/JS et consoles jetables pour des tests rapides.',
      'tip.dash.10': 'Les actions SELECT, INPUT, SLIDER et TOGGLE3 mémorisent leur valeur — elle survit au rechargement de la page.',
      'tip.dash.11': 'La synchronisation cloud maintient votre bibliothèque cohérente sur tous vos appareils.',
      'tip.dash.12': 'Au lieu d\'envoyer un fichier JSON, partagez un dossier via un lien (Partages) avec une date d\'expiration.',
      'tip.dash.13': 'La recherche de la barre latérale retrouve un dossier, un groupe ou un jeu de règles par son nom.',
      'tip.dash.14': 'Ajoutez des tags aux jeux de règles et filtrez votre bibliothèque par tag quand les projets se multiplient.',
      'tip.dash.15': 'Une étiquette de trois lettres et une couleur font ressortir le bouton flottant d\'un dossier.',
      'tip.dash.16': 'Le bouton « URL » insère l\'adresse de l\'onglet actif directement dans un motif d\'URL, sans copier-coller.',
      'tip.dash.17': 'JustZix vérifie la validité des motifs d\'URL au fur et à mesure que vous les saisissez.',
      'tip.dash.18': 'L\'éditeur signale les erreurs de syntaxe CSS avant que vous n\'enregistriez un jeu de règles.',
      'tip.dash.19': 'Lors d\'un import, vous pouvez prévisualiser le code JavaScript avant de décider de l\'ajouter.',
      'tip.dash.20': 'Désactiver un dossier coupe en cascade tous ses groupes et jeux de règles.',
      'tip.dash.21': 'Le filtre « Actifs seulement » masque les éléments désactivés quand la bibliothèque grandit.',
      'tip.dash.22': 'Faites glisser une barre d\'actions près d\'une autre pour les accrocher en un seul ensemble.',
      'tip.dash.23': 'La fenêtre Console JS fonctionne comme un REPL : testez des extraits de code en direct sur la page.',
      'tip.dash.24': 'La fenêtre Output Console capture les entrées console.log de la page et les affiche à l\'écran.',
      'tip.dash.25': 'Vous pouvez exporter un seul dossier, groupe ou jeu de règles, pas seulement toute la bibliothèque.',
      'tip.dash.26': 'JustZix ajoute ses propres entrées au menu contextuel (clic droit) du navigateur.',
      'tip.dash.27': 'Déplacez à la souris la séparation entre les panneaux de l\'éditeur de code pour ajuster la vue.',
      'tip.dash.28': 'L\'extension vous prévient d\'elle-même dès qu\'une version plus récente est disponible.',
      'tip.dash.29': 'Cliquer sur l\'icône de l\'extension ouvre un widget pour activer rapidement dossiers et barres.',
      'tip.dash.30': 'Le JavaScript d\'un jeu de règles peut déclencher des actions de barre via l\'API intégrée de JustZix.',
      'tip.dash.31': 'Ctrl+Entrée exécute le code ou envoie le message — dans le panneau JS, la Console JS et l\'AI Helper.',
      'tip.dash.32': 'Dans la Console JS, les flèches ↑ / ↓ parcourent l\'historique des commandes saisies.',
      'tip.dash.33': 'Ctrl+L efface la sortie de la Console JS sans fermer la fenêtre.',
      'tip.dash.34': 'Ctrl+Shift+L efface l\'onglet actif de chaque Console Output ouverte.',
      'tip.dash.35': 'Ctrl+Shift+H ouvre la fenêtre d\'aide de l\'API dans la Console Output.',
      'tip.dash.36': 'Dans l\'éditeur de code, Ctrl+Alt+→ et Ctrl+Alt+← passent au résultat de recherche suivant et précédent.',
      'tip.dash.37': 'La touche Échap efface le champ de recherche dans la Console Output.',
      'tip.dash.38': 'Un double-clic sur l\'en-tête d\'une fenêtre TEMP la bascule entre la taille par défaut et la dernière.',
      'tip.dash.39': 'Une fenêtre TEMP mémorise sa dernière taille par type et la conserve après un redémarrage du navigateur.',
      'tip.dash.40': 'Le défilement à la molette dans une fenêtre ne fait plus défiler la page en dessous.',
      'tip.dash.41': 'Chaque fenêtre a 8 poignées de redimensionnement — 4 aux coins et 4 sur les bords.',
      'tip.dash.42': 'Un clic droit sur l\'en-tête d\'une fenêtre ouvre un menu avec fermeture, déconnexion, police et taille.',
      'tip.dash.43': 'Faites glisser une fenêtre près d\'une autre — elles s\'aimantent en un seul groupe.',
      'tip.dash.44': 'La Console Output a 6 onglets : Tout, JZ, Console, Network, Errors et DataLayer.',
      'tip.dash.45': 'Dans l\'onglet Network, Alt+clic sur une ligne ajoute son domaine au filtre d\'inclusion.',
      'tip.dash.46': 'Alt+Shift+clic sur une ligne Network ajoute le domaine au filtre d\'exclusion.',
      'tip.dash.47': 'Chaque onglet de la Console Output garde son propre jeu de filtres indépendant.',
      'tip.dash.48': 'Dans l\'onglet DataLayer, faites glisser la barre entre les colonnes pour les redimensionner.',
      'tip.dash.49': 'En mode édition de barre, le bouton « + Étiquette » ajoute des libellés de texte statiques.',
      'tip.dash.50': 'Double-cliquez sur une étiquette de barre pour éditer son texte, sa police, sa taille et sa couleur.',
      'tip.dash.51': 'En mode édition, Shift+glisser sur une zone vide sélectionne les éléments avec un rectangle.',
      'tip.dash.52': 'En mode édition, Ctrl+clic ajoute ou retire un élément de la sélection.',
      'tip.dash.53': 'Cliquez sur le badge numéroté d\'un élément d\'action pour copier son ID stable dans le presse-papiers.',
      'tip.dash.54': 'Un jeu de règles peut réexécuter son JS lors des changements d\'interface sur les pages de type SPA.',
      'tip.dash.55': 'Au-delà de BUTTON, SELECT et INPUT, les actions ont aussi les types SLIDER, TEXTAREA et TOGGLE3.',
      'tip.dash.56': 'Le thème clair/sombre et la langue de l\'interface se synchronisent comme un réglage de compte partagé.',
      'tip.dash.57': 'Les statistiques d\'usage du Tableau de bord additionnent les données de tous les appareils appairés.',
      'tip.dash.58': 'La barre sous l\'éditeur de code peut être tirée vers le bas pour agrandir la zone de travail.',
      'tip.dash.59': 'La barre verticale entre la barre latérale et l\'éditeur replie le panneau de gauche pour plus de place.',
      'tip.dash.60': 'L\'icône maison dans l\'en-tête du Tableau de bord et dans le fil d\'Ariane y ramène directement.',
      // === Popup ===
      'popup.toggle.on.label':       'Extension activée',
      'popup.toggle.on.status':      'Injection active',
      'popup.toggle.off.label':      'Extension désactivée',
      'popup.toggle.off.status':     'Aucun CSS/JS n\'est injecté',
      'popup.editMode.label':        'Mode édition de la barre d\'actions',
      'popup.editMode.on':           'Activé — cliquer pour désactiver (Ctrl+Alt+S)',
      'popup.editMode.off':          'Désactivé — cliquer pour activer (Ctrl+Alt+S)',
      'popup.editMode.unavailable':  'Indisponible sur cette page',
      'popup.empty.title':           'Aucun dossier de règles ne correspond à cette page.',
      'popup.empty.hint':            'Ajoutez-les en mode édition.',
      'popup.empty.folderRules':     'Aucune règle active dans ce dossier',
      'popup.fallback.groupName':    'Groupe',
      'popup.fallback.unnamed':      '(sans nom)',
      'popup.restore':               'Restaurer le bouton flottant (:count)',
      'popup.editBtn':               'Panneau d\'édition',
      'popup.actionBarsTitle':       'Barres d\'actions',
      'popup.fallback.barName':      'Barre',
      'popup.hideForPage':           'Masquer le bouton flottant pour cette page',
      'popup.showForPage':           'Afficher le bouton flottant pour cette page',

      // === Options — page ===
      'options.title':                          'JustZix — Éditeur',

      // === Options — sidebar ===
      'sidebar.folders.title':                  'Dossiers',
      'sidebar.addFolder.label':                'Ajouter un dossier',
      'sidebar.search.placeholder':             'Rechercher (nom, tag, contenu du code)',
      'sidebar.clearSearch.label':              'Effacer',
      'sidebar.activeOnly.label':               'Actifs uniquement',
      'sidebar.activeOnly.title':               'Masquer les jeux de règles indépendants (marqués ○) — n\'afficher que les règles actives dans leur dossier (●)',
      'sidebar.expandAll.label':                'Déplier tout l\'arbre',
      'sidebar.collapseAll.label':              'Replier tout l\'arbre',
      'sidebar.toggle.title':                   'Replier/déplier le panneau des dossiers',

      // === Options — settings menu ===
      'settings.menu.label':                    'Paramètres',
      'settings.theme.label':                   'Thème',
      'settings.theme.title':                   'Choisir un thème : clair / sombre / auto',
      'settings.help.label':                    'Manuel d\'utilisation',
      'settings.tutorial.label':                'Tutoriel',
      'settings.export.label':                  'Exporter les paramètres',
      'settings.import.label':                  'Importer les paramètres',
      'settings.importUrl.label':               'Importer depuis une URL',
      'settings.importUrl.title':               'Importer un export depuis un lien JustZix (JZS-XXXX-XXXX-XXXX-XXXX)',
      'settings.sync.label':                    'Synchronisation cloud',
      'settings.shares.label':                  'Mes partages',
      'settings.shares.title':                  'Liste des liens publics à durée limitée émis vers des exports',
      'settings.projectSite.label':             'Site du projet',
      'settings.projectSite.title':             'Ouvrir justzix.com dans un nouvel onglet',
      'settings.lang.label':                    'Langue',
      'settings.lang.title':                    'Langue de l\'interface de l\'extension',
      'settings.lang.auto':                     'Auto',
      'settings.lang.autoTitle':                'Détecter depuis le navigateur (par défaut)',
      'toast.langChanged':                      'Langue définie : :lang',
      'lang.name.auto':                         'Auto',
      'lang.name.pl':                           'Polonais',
      'lang.name.en':                           'Anglais',
      'settings.version.label':                 'Version',
      'settings.version.checkTitle':            'Cliquer pour vérifier la dernière version (actualisation forcée, contourne le cache de 30 min)',
      'settings.version.checking':              'Vérification…',
      'settings.update.available':              'Nouvelle version disponible : v:version ⬇',
      'settings.update.notReady':               '(paquet pas encore publié)',
      'settings.syncStatusDot.title':           'Synchronisation cloud — non connecté',

      // === Options — folder editor ===
      'folder.icon.title':                      'Libellé du bouton flottant (max. 3 lettres, MAJUSCULES). Par défaut : ZIX',
      'folder.color.title':                     'Couleur du dossier (facultatif)',
      'folder.colorClear.title':                'Effacer la couleur',
      'folder.name.placeholder':                'Nom du dossier (ex. justzix.com)',
      'folder.url.placeholder':                 'Motif d\'URL (ex. https://justzix.com/* )',
      'folder.urls.label':                      'Motifs d\'URL du dossier :',
      'folder.urls.title':                      'Motifs d\'URL du dossier — au moins un doit correspondre pour qu\'une règle de ce dossier s\'exécute',
      'folder.urls.placeholder':                'https://justzix.com/*  (Entrée pour ajouter)',
      'folder.insertUrl.title':                 'Insérer l\'URL de l\'onglet actif',
      'folder.active.label':                    'Actif',
      'folder.active.title':                    'Actif — décoché, le dossier (et tous ses groupes/règles) est forcément désactivé et l\'interrupteur verrouillé',
      'folder.enabled.label':                   'Activé',
      'folder.enabled.title':                   'Activé — état actuel du dossier (se répercute sur les groupes et règles actifs)',
      'folder.actionsPanel.label':              'Panneau d\'actions',
      'folder.actionsPanel.title':              'Afficher le panneau flottant avec les boutons d\'action pour ce dossier',
      'folder.layout.label':                    'Disposition :',
      'folder.layout.title':                    'Disposition des boutons du panneau d\'actions',
      'folder.layout.vertical':                 'Verticale',
      'folder.layout.horizontal':               'Horizontale',
      'folder.addGroup.label':                  '+ Groupe',
      'folder.addGroup.title':                  'Ajouter un nouveau groupe dans ce dossier',
      'folder.export.label':                    'Exporter',
      'folder.export.title':                    'Exporter ce dossier',
      'folder.import.label':                    'Importer',
      'folder.import.title':                    'Importer un dossier depuis un fichier',
      'folder.delete.label':                    'Supprimer le dossier',

      // === Options — group editor ===
      'group.parentLabel':                      'Groupe dans le dossier :',
      'folder.label':                           'Dossier :',
      'group.label':                            'Groupe :',
      'group.name.placeholder':                 'Nom du groupe',
      'group.urls.label':                       'Motifs d\'URL du groupe :',
      'group.urls.title':                       'Motifs d\'URL supplémentaires du groupe — restriction additionnelle superposée au motif du dossier, s\'applique à toutes les règles du groupe',
      'group.urls.placeholder':                 'https://example.com/admin/*  (Tab = compléter depuis le dossier, Entrée = ajouter)',
      'group.active.label':                     'Actif',
      'group.active.title':                     'Actif — décoché, le groupe (et toutes ses règles) est forcément désactivé et l\'interrupteur verrouillé',
      'group.enabled.label':                    'Activé',
      'group.enabled.title':                    'Activé — état actuel du groupe (se répercute sur les règles actives)',
      'group.addRule.label':                    '+ Ajouter un jeu de règles',
      'group.export.title':                     'Exporter ce groupe',
      'group.import.title':                     'Importer des jeux de règles dans ce groupe',
      'group.move.label':                       'Déplacer',
      'group.move.title':                       'Déplacer le groupe vers un autre dossier',
      'group.duplicate.label':                  'Dupliquer',
      'group.duplicate.title':                  'Dupliquer le groupe avec ses jeux de règles',
      'group.delete.label':                     'Supprimer le groupe',

      // === Options — rule editor ===
      'rule.label':                             'Jeu de règles :',
      'rule.back.label':                        'Retour au groupe',
      'rule.export.title':                      'Exporter ce jeu de règles dans un fichier',
      'rule.share.label':                       'Partager l\'URL',
      'rule.share.title':                       'Partager ce jeu de règles via un lien public à durée limitée',
      'rule.import.title':                      'Importer un jeu de règles (remplacera l\'actuel)',

      // === Options — empty state ===
      'empty.title':                            'Sélectionnez ou ajoutez un dossier',
      'empty.body':                             'Chaque dossier regroupe des jeux de règles CSS/JS pour un motif d\'URL.',

      // === Default names for newly created entities (options.js) ===
      'default.groupName':                      'Par défaut',
      'default.folderName':                     'Nouveau dossier',
      'default.ruleName':                       'Nouveau jeu de règles',

      // === Plural noun forms used by toasts ===
      'plural.rule.one':                        'jeu de règles',
      'plural.rule.few':                        'jeux de règles',
      'plural.rule.many':                       'jeux de règles',
      'plural.rule.other':                      'jeux de règles',
      'plural.folder.one':                      'dossier',
      'plural.folder.few':                      'dossiers',
      'plural.folder.many':                     'dossiers',
      'plural.folder.other':                    'dossiers',
      'plural.group.one':                       'groupe',
      'plural.group.few':                       'groupes',
      'plural.group.many':                      'groupes',
      'plural.group.other':                     'groupes',
      'plural.action.one':                      'action',
      'plural.action.few':                      'actions',
      'plural.action.many':                     'actions',
      'plural.action.other':                    'actions',
      'plural.jsLine.one':                      'ligne JS',
      'plural.jsLine.few':                      'lignes JS',
      'plural.jsLine.many':                     'lignes JS',
      'plural.jsLine.other':                    'lignes JS',
      'toast.importedRulesToGroup':             ':count :word importés dans le groupe',
      'toast.exportedRules':                    ':count :word exportés',

      // === Sidebar — dynamic render ===
      'sidebar.empty.folders':                  'Aucun dossier',
      'sidebar.empty.activeFolders':            'Aucun dossier actif',
      'sidebar.folder.toggleHint':              'Activer/désactiver tout le dossier',
      'sidebar.folder.inactiveHint':            'Dossier inactif — déverrouillez-le dans l\'éditeur de dossier',
      'sidebar.group.toggleHint':               'Activer/désactiver le groupe',
      'sidebar.group.inactiveHint':             'Groupe inactif — déverrouillez-le dans l\'éditeur de groupe',
      'sidebar.rule.urlHint':                   'Restriction d\'URL au niveau du jeu de règles',
      'sidebar.rule.activeInFolder':            'Actif dans le dossier',
      'sidebar.rule.inactiveInFolder':          'Inactif dans le dossier',
      'sidebar.fallback.folderName':            '(dossier)',
      'sidebar.fallback.groupName':             '(groupe)',
      'sidebar.fallback.unnamed':               '(sans nom)',
      'sidebar.flag.css':                       'Contient du CSS',
      'sidebar.flag.js':                        'Contient du JavaScript',
      'sidebar.flag.actions':                   'Contient des actions',
      'sidebar.flag.groupUrls':                 'Le groupe a une restriction d\'URL',

      // === Folder/Group/Rule editor — dynamic ===
      'rules.empty.folder':                     'Aucun jeu de règles dans ce dossier. Ajoutez un groupe et créez-y le premier jeu de règles, ou cliquez sur un groupe dans la barre latérale.',
      'rules.empty.group':                      'Aucun jeu de règles dans ce groupe. Ajoutez le premier en cliquant sur « + Ajouter un jeu de règles ».',
      'rule.namePlaceholder':                   'Nom du jeu de règles',
      'rule.activeTooltip':                     'Actif — indique si cette règle est incluse lors du basculement de tout le dossier',
      'rule.urls.tooltip':                      'Motifs d\'URL supplémentaires — s\'ils sont définis, la règle ne s\'exécute que sur les sous-pages correspondant à au moins un motif (en plus du motif du dossier)',
      'rule.urls.placeholder.empty':            'https://example.com/admin/*  (Tab — insérer le motif du dossier, Entrée — ajouter)',

      // === Move menu ===
      'moveMenu.section.moveGroup':             'Déplacer le groupe vers',
      'moveMenu.section.moveRule':              'Déplacer vers le groupe',
      'moveMenu.section.copyRule':              'Copier vers le groupe',
      'moveMenu.empty.folders':                 'Aucun autre dossier',
      'moveMenu.empty.groups':                  'Aucun autre groupe',

      // === Confirm dialogs — dynamic ===
      'confirm.deleteFolderWithStats':          'Supprimer le dossier « :name » avec :groups et :rules ?',
      'confirm.deleteFolder':                   'Supprimer le dossier « :name » ?',
      'confirm.deleteGroupWithRules':           'Supprimer le groupe « :name » avec :count :word ?',
      'confirm.deleteGroup':                    'Supprimer le groupe « :name » ?',
      'confirm.replacePartial':                 'Remplacer :before actuel par une sauvegarde partielle (:after) ?\n\nToutes les données hors du fichier seront perdues. L\'opération est irréversible.',
      'confirm.overwriteAll':                   'Écraser :stats avec les données actuelles du fichier ?\n\nCette opération est irréversible.',

      // === Default name for newly created group ===
      'default.newGroupName':                   'Nouveau groupe',

      // === Export — summary + states ===
      'export.summary.full':                    ':icon Sauvegarde complète : :stats + paramètres globaux',
      'export.summary.partial':                 ':icon Sauvegarde partielle : :stats',
      'export.empty.folder':                    'Aucun dossier à exporter',
      'export.empty.group':                     'Le groupe est vide — aucun jeu de règles à exporter',
      'export.summary.toExport':                ':icon À exporter : :count :word',
      'export.summary.noneSelected':            ':icon Aucun jeu de règles sélectionné',

      // === Import detection (banners) ===
      'import.detection.invalid':               '<strong style="color:#dc2626;">:icon Format invalide :</strong> liste de dossiers manquante (<code>folders</code>).',
      'import.detection.partialContains':       'Contient : :stats',
      'import.detection.willReplace':           ':stats',

      // === Inline labels ===
      'inline.fileGroupLabel':                  'groupe',
      'shares.empty':                           'Vous n\'avez aucun lien actif.<br><br>Pour en créer un — ouvrez un export (complet / dossier / groupe / jeu de règles)<br>et cliquez sur <strong>« Partager l\'URL »</strong>.',

      // === Share preview / export types ===
      'shareType.folder':                       'Dossier unique',
      'shareType.group':                        'Groupe unique',
      'shareType.rule':                         'Jeu de règles unique',
      'shareType.full':                         'Export complet',

      // === Owner suffix when importing from share ===
      'shareImport.suffix.group':               'groupe',
      'shareImport.suffix.rule':                'jeu de règles',

      // === Rule import — prompt with options ===
      'prompt.ruleConflict':                    'Vous importez « :new ». Que faire du jeu de règles actuel « :current » ?\n\n  1 ou "replace"  — Remplacer le jeu de règles actuel\n  2 ou "copy"     — Ajouter comme nouveau jeu de règles à côté de l\'actuel (dans le même groupe)\n\nEntrez 1 ou 2 :',
      'prompt.folderConflicts':                 ':count :word détectés avec le même ID qu\'existants :\n:names\n\nEntrez l\'une des options :\n  1 ou "overwrite"  — Écraser les dossiers existants (supprime leurs groupes et jeux de règles actuels)\n  2 ou "copy"       — Importer comme copies (nouveaux IDs, « (import) » dans le nom du dossier)\n  Cancel            — Annuler l\'import\n\nChoix :',

      // === Group editor — additional ===
      'group.empty.noGroupsInFolder':           'Aucun groupe dans le dossier',

      // === Folder import — conflict messages ===
      'folderImport.conflict.sameId':           'Le dossier importé a le même ID que l\'existant « <strong>:name</strong> » (:count :word). Choisissez une action :',
      'folderImport.conflict.sameName':         'Un dossier nommé « <strong>:name</strong> » existe déjà (:count :word). Choisissez une action :',

      // === Dynamic button states ===
      'btn.shortcutPress':                      '⌨ appuyez sur Ctrl+Alt+Shift + une touche (Échap)',
      'btn.generating':                         'Génération…',
      'btn.importing':                          'Import…',
      'btn.showKey':                            'Afficher la clé',
      'btn.hideKey':                            'Masquer la clé',

      // === Rule editor — placeholders + tooltips ===
      'rule.editor.urlsPlaceholder':            'https://example.com/admin/*  (Tab — insérer le motif du dossier, Entrée — ajouter)',
      'rule.editor.urlsPlaceholderWithPattern': ':pattern  (Tab — insérer, Entrée — ajouter)',
      'rule.editor.toggleHint':                 'Activer/désactiver ce jeu de règles',
      'rule.editor.toggleLockedHint':           'Le jeu de règles est inactif — déverrouillez-le en cochant Actif',
      'sidebar.fallback.ruleName':              '(jeu de règles)',
      'sidebar.fallback.ruleUnnamed':           '(jeu de règles sans nom)',
      'confirm.deleteRule':                     'Supprimer le jeu de règles « :name » ?',

      // === Rule card (rendered by createRuleCard / renderActionsList) ===
      'rule.card.tagsTitle':                    'Tags (séparés par des virgules)',
      'rule.card.activeLabel':                  'Actif',
      'rule.card.enabledLabel':                 'Activé',
      'rule.card.history.title':                'Historique des modifications',
      'rule.card.history.label':                'Historique',
      'rule.card.move.label':                   'Déplacer',
      'rule.card.move.btnTitle':                'Déplacer le jeu de règles vers un autre groupe',
      'rule.card.copy.label':                   'Copier',
      'rule.card.copy.btnTitle':                'Copier le jeu de règles vers un autre groupe',
      'rule.card.duplicate.label':              'Dupliquer',
      'rule.card.delete.label':                 'Supprimer',
      'rule.card.urlsLabel':                    'Motifs d\'URL du jeu de règles :',
      'rule.card.tab.actions':                  'Actions',
      'rule.card.lineCount':                    'Nombre de lignes de code',
      'rule.card.js.runOnce.label':             'Exécuter le JS une seule fois',
      'rule.card.js.runOnce.title':             'Ne pas réexécuter le JS après une navigation SPA (changement d\'URL sans rechargement). Recommandé pour les scripts ajoutant des écouteurs globaux, des intervalles ou modifiant window.',
      'rule.card.actions.info':                 'Les boutons d\'action apparaissent dans le panneau flottant (interrupteur « Panneau d\'actions » dans le dossier). Raccourci de 4 caractères max., couleur personnalisée, le code JS s\'exécute au clic.',
      'rule.card.actions.add':                  '+ Ajouter un bouton d\'action',
      'rule.card.actions.addWithCount':         '+ Ajouter un bouton d\'action (:count)',
      'rule.card.action.visibleLabel':          'visible',
      'rule.card.duplicate.suffix':             '(copie)',
      'rule.card.action.codePreview':           '(:count :word) :first',
      'rule.card.action.fallback.unlabeled':    '(sans libellé)',
      'rule.card.fallbackName':                 'Jeu de règles',

      // === Folder export modal — additional ===
      'export.empty.foldersToExport':           'Aucun dossier à exporter',
      'export.summary.toExportFolders':         ':icon À exporter : :groupCount :groupWord, :ruleCount :ruleWord',
      'export.summary.invalidFolderFile':       'Format de fichier invalide — export de dossier unique attendu.',
      'export.summary.noConflicts':             ':icon Aucun conflit. Le dossier « <strong>:name</strong> » avec :count :word sera importé comme nouveau.',
      'export.summary.fileContains':            '<strong style="color:#374151;">:icon Contenu du fichier (:count :word) :</strong>',
      'export.summary.jsHeader':                ':icon JavaScript du jeu de règles :',
      'export.summary.emptyRule':               'jeu de règles vide',
      'toast.exportedFolder':                   'Dossier exporté (:groupCount :groupWord, :ruleCount :ruleWord)',
      'inline.fileFolderLabel':                 'dossier',
      'inline.fileRuleLabel':                   'jeu de règles',
      'default.newGroupNameInline':             'Nouveau groupe',

      // === Sync conflict modal counts ===
      'syncConflict.countLine':                 ':folders :foldersWord / :groups :groupsWord / :rules :rulesWord',

      // === Backup restore confirm ===
      'confirm.restoreBackup':                  'Restaurer la sauvegarde n°:id ?\n\nTOUS les dossiers/groupes/jeux de règles actuels seront remplacés par le contenu de la sauvegarde. Les autres appareils se synchroniseront au prochain pull.\n\nIrréversible (hormis la création d\'une nouvelle sauvegarde AVANT la restauration).',

      // === Loaders / placeholders ===
      'loading.shares':                         'Chargement…',
      'loading.generic':                        'Chargement…',

      // === Sidebar — drag & arrows tooltips ===
      'sidebar.dragHandle.title':               'Glisser pour réordonner',
      'sidebar.expandCollapse.title':           'Déplier/replier',
      'sidebar.folderActive.title':             'Actif',
      'sidebar.folderInactive.title':           'Inactif — forcément désactivé',
      'sidebar.groupActive.title':              'Actif',
      'sidebar.groupInactive.title':            'Inactif — forcément désactivé',
      'sidebar.searchNoResults':                'Aucun résultat pour « :query »',

      // === Theme ===
      'settings.theme.toggleToDark':            'Passer en mode sombre',
      'settings.theme.toggleToLight':           'Passer en mode clair',
      'settings.theme.labelDark':               'Mode sombre',
      'settings.theme.labelLight':              'Mode clair',
      'settings.theme.labelAuto':               'Auto (système)',
      'settings.theme.toggleToAuto':            'Passer en mode automatique',

      // === URL chip remove ===
      'btn.removeUrlChip':                      'Retirer',

      // === CSS validator ===
      'cssValidator.unclosedComment':           'Commentaire non fermé /* ... */',
      'cssValidator.missingCloseBraces':        'Accolades non fermées : il manque :count :word }',
      'plural.brace.one':                       'accolade fermante',
      'plural.brace.few':                       'accolades fermantes',
      'plural.brace.many':                      'accolades fermantes',
      'plural.brace.other':                     'accolades fermantes',
      'cssValidator.extraCloseBrace':           'Accolade fermante en trop } (ligne :line)',
      'jsValidator.unterminatedString':         'Littéral de chaîne non terminé (ligne :line)',
      'jsValidator.unclosedBracket':            'Parenthèse non fermée :bracket (ligne :line)',
      'jsValidator.extraBracket':               'Parenthèse fermante en trop :bracket (ligne :line)',

      // === Action buttons ===
      'action.shortcut.empty':                  'cliquer + appuyer sur le raccourci',
      'action.code.empty':                      'cliquer pour ajouter du code…',
      'action.stats.title':                     'Nombre de clics dans le panneau d\'actions',
      'action.visible.title':                   'Visible dans le panneau d\'actions (décoché = masqué, mais pas supprimé)',
      'action.shortcut.title':                  'Raccourci clavier — cliquez et appuyez sur une combinaison. Échap annule. Le raccourci fonctionne sur les pages correspondantes quelle que soit la visibilité du panneau.',
      'action.shortcut.titleAfterClick':        'Raccourci clavier. Cliquez et appuyez sur une combinaison pour modifier. Échap annule. × supprime.',
      'action.shortcut.removeBtn':              'Supprimer le raccourci clavier',
      'action.delete.btn':                      'Supprimer le bouton d\'action',
      'action.menu.btn.duplicate':              'Dupliquer l\'action ici',
      'action.menu.btn.move':                   'Déplacer l\'action vers un autre jeu de règles',
      'action.menu.btn.copy':                   'Copier l\'action vers un autre jeu de règles',
      'action.menu.section.moveTo':             'Déplacer vers le jeu de règles...',
      'action.menu.section.copyTo':             'Copier vers le jeu de règles...',
      'action.menu.empty.rules':                'Aucun autre jeu de règles',
      'action.menu.toast.moved':                'Action déplacée vers « :target »',
      'action.menu.toast.copied':               'Action copiée vers « :target »',
      'action.shortcut.conflictTitle':          'Conflit : ce raccourci est aussi utilisé par :count :word :\n:list\n\nÀ l\'appui, seul le premier dans l\'ordre de rendu se déclenchera.',
      'action.shortcut.conflictItem':           '• « :action » dans « :rule » (:folder)',
      'plural.actionAlt.one':                   'autre action',
      'plural.actionAlt.few':                   'autres actions',
      'plural.actionAlt.many':                  'autres actions',
      'plural.actionAlt.other':                 'autres actions',
      'plural.more.one':                        'autre',
      'plural.more.few':                        'autres',
      'plural.more.many':                       'autres',
      'plural.more.other':                      'autres',
      'action.shortcut.conflictMore':           ' (et :count :word)',
      'action.shortcut.conflictToast':          'Conflit : ce raccourci est déjà utilisé par l\'action « :actionLabel » dans « :ruleName »:more. Une seule d\'entre elles se déclenchera.',
      'confirm.deleteAction':                   'Supprimer le bouton d\'action « :label » ?',

      // === History ===
      'history.header':                         ':count dernières versions enregistrées. Cliquez sur « Restaurer » pour revenir à cette version (l\'actuelle sera enregistrée dans l\'historique).',
      'history.modalTitle':                     'Historique des modifications : « :name »',
      'history.version':                        'Version :n',
      'history.restore':                        'Restaurer',
      'history.close':                          'Fermer',

      // === Export — additional ===
      'export.summary.noElements':              ':icon Aucun élément sélectionné pour l\'export',
      'export.summary.toExportFolderShort':     ':icon À exporter : :groupCount :groupWord, :ruleCount :ruleWord',
      'export.summary.noneSelectedShort':       ':icon Aucun jeu de règles sélectionné',

      // === Import detection ===
      'import.detection.parseError':            '<strong style="color:#dc2626;">:icon Erreur d\'analyse JSON :</strong> :msg',
      'import.detection.partialBackup':         '<strong style="color:#5b21b6;">:icon Sauvegarde partielle (éléments sélectionnés)</strong><br>',
      'import.detection.fullBackup':            '<strong style="color:#006870;">:icon Sauvegarde complète des paramètres:legacy</strong><br>',
      'import.detection.legacySuffix':          ' (ancien format)',
      'import.detection.willOverwrite':         '<br><br><strong style="color:#b91c1c;">:icon Les paramètres actuels seront écrasés :</strong>',
      'import.detection.exportFirst':           '<br><span style="color:#7f1d1d;font-size:11px;">Recommandé : exportez <strong>avant</strong> d\'importer.</span>',
      'import.detection.folder':                '<strong style="color:#155e75;">:icon Export de dossier</strong> « <strong>:name</strong> »<br><span style="color:#155e75;">Contient : :stats</span>',
      'import.detection.group':                 '<strong style="color:#155e75;">:icon Export de groupe</strong> « <strong>:name</strong> »<br><span style="color:#155e75;">Contient : :stats</span>',
      'import.detection.rule':                  '<strong style="color:#155e75;">:icon Export de jeu de règles</strong> « <strong>:name</strong> »',

      // === Import — target selectors ===
      'import.target.folder.title':             'Où placer le dossier importé ?',
      'import.target.folder.newCopy':           'Ajouter comme nouveau dossier de premier niveau',
      'import.target.folder.overwrite':         'Écraser le dossier existant « :name » (conserve l\'ID — la synchro le voit comme une modification)',
      'import.target.folder.mergeInto':         'Fusionner les groupes/jeux de règles dans un dossier existant :',
      'import.target.group.title':              'Où placer le groupe importé ?',
      'import.target.group.existingFolder':     'Ajouter à un dossier existant :',
      'import.target.group.newFolder':          'Créer un nouveau dossier pour ce groupe',
      'import.target.rule.title':               'Où placer le jeu de règles importé ?',
      'import.target.rule.existingGroup':       'Ajouter à un groupe existant :',
      'import.target.rule.newFolder':           'Créer un nouveau dossier et un nouveau groupe pour ce jeu de règles',

      // === Share errors (create flow) ===
      'share.error.unknown':                    'Erreur inconnue',
      'share.error.shareFailed':                'Vous avez déjà 20 liens actifs ou dépassé 50 Mo. Révoquez les anciens dans « Mes partages ».',
      'share.error.badRequest':                 'Échec de la génération du lien — vérifiez que l\'export est valide. :msg',
      'share.error.rateLimited':                'Trop de liens créés dans la dernière heure (limite : 30). Réessayez dans un instant.',
      'share.error.invalidToken':               'Session expirée — reconnectez-vous dans le panneau de synchro et réessayez.',
      'share.error.serverError':                'Problème serveur. Réessayez dans un instant.',
      'share.error.fallback':                   'Erreur : :msg',
      'share.error.tooLarge':                   'Export trop volumineux — le backend autorise 5 Mo max. Essayez de partager une partie plus petite.',

      // === Share subject (create modal) ===
      'share.subject.partial':                  'Export partiel de la bibliothèque',
      'share.subject.full':                     'Export complet de la bibliothèque',

      // === Time relative formatting (fmtRelHuman) ===
      'rel.lessThanMinute':                     'moins d\'une minute',
      'rel.min':                                ':n min',
      'rel.hMin':                               ':h h :m min',
      'rel.h':                                  ':h h',
      'rel.dH':                                 ':d j :h h',
      'rel.d':                                  ':d j',

      // === Shares list ===
      'shares.expired':                         'expiré',
      'shares.fetchFailed':                     'Échec de la récupération de la liste : :msg',
      'shares.confirmRevoke':                   'Révoquer le partage n°:id ?\n\nLe lien cessera de fonctionner immédiatement — les destinataires obtiendront un 404 à la prochaine tentative.\n\nREMARQUE : si quelqu\'un a déjà téléchargé les données, il en aura une copie locale — la révocation ne récupère pas le contenu déjà téléchargé.',
      'shares.revoked':                         'Partage n°:id révoqué',

      // === Share preview details ===
      'sharePreview.senderLabel':               'Expéditeur :',
      'sharePreview.senderAnonymous':           'anonyme',
      'sharePreview.expiresLabel':              'Expire :',
      'sharePreview.expiresValue':              'dans :rel',
      'sharePreview.typeLabel':                 'Type :',
      'sharePreview.sizeLabel':                 'Taille :',
      'sharePreview.contentLabel':              'Contenu :',
      'sharePreview.cssLabel':                  'CSS :',
      'sharePreview.cssValueChars':             ':n caractères',
      'sharePreview.jsLabel':                   'JavaScript :',
      'sharePreview.downloadsLabel':            'Téléchargements :',
      'sharePreview.codePreviewTitle':          'Aperçu du code de l\'export partagé',

      // === Share import errors (preview) ===
      'shareImport.error.unknown':              'Erreur inconnue',
      'shareImport.error.badRequest':           'Le jeton est invalide. Vérifiez que vous avez copié le lien complet.',
      'shareImport.error.notFound':             'Ce lien a expiré ou a été révoqué par l\'expéditeur. Demandez-en un nouveau.',
      'shareImport.error.rateLimited':          'Trop de tentatives. Attendez un instant et réessayez.',
      'shareImport.error.serverError':          'Problème serveur. Réessayez.',
      'shareImport.error.fallback':             'Erreur : :msg',
      'shareImport.error.invalidTokenFormat':   'Format de jeton invalide',

      // === Rule import ===
      'ruleImport.unknownFormat':               'Format d\'export inconnu ou non pris en charge : :type',
      'prompt.ruleConflict.body':               'Vous importez « :new ». Que faire du jeu de règles actuel « :current » ?\n\n  1 ou "replace"  — Remplacer l\'actuel par le contenu du fichier\n  2 ou "copy"     — Ajouter comme nouveau jeu de règles à côté de l\'actuel (dans le même groupe)\n  Cancel          — Annuler l\'import\n\nChoix :',

      // === Folder export ===
      'folderExport.exportedToast':             'Dossier exporté (:groupCount :groupWord, :ruleCount :ruleWord)',

      // === Folder import — details ===
      'folderImport.parseError':                'Erreur d\'analyse JSON : :msg',
      'folderImport.invalidFormat':             'Format de fichier invalide — export de dossier unique attendu.',
      'folderImport.noConflicts':               ':icon Aucun conflit. Le dossier « <strong>:name</strong> » avec :count :word sera importé comme nouveau.',
      'folderImport.conflictSameId':            'Le dossier importé a le même ID que l\'existant « <strong>:name</strong> » (:count :word). Choisissez une action :',
      'folderImport.conflictSameName':          'Un dossier nommé « <strong>:name</strong> » existe déjà (:count :word). Choisissez une action :',
      'folderImport.fileContents':              '<strong style="color:#374151;">:icon Contenu du fichier (:count :word) :</strong>',
      'folderImport.jsHeader':                  ':icon JavaScript du jeu de règles :',
      'folderImport.codePreviewTitle':          'Aperçu du code : :name',
      'folderImport.previewJsBtn.title':        'Aperçu du code JS',

      // === Sync — toasts + status messages ===
      'sync.toast.synced':                      'Synchronisé : :applied modifications, :deletes suppressions',
      'sync.rel.justNow':                       'à l\'instant',
      'sync.tooltip.syncing':                   'Synchronisation…',
      'sync.tooltip.error':                     'Synchro : erreur. Dernière réussie : :last',
      'sync.tooltip.connected':                 'Synchronisation cloud — connecté. Dernière : :last',
      'sync.error.copyManually':                'Échec de la copie — sélectionnez et faites Ctrl+C manuellement.',
      'sync.error.registerFailed':              'Erreur d\'enregistrement : :msg',
      'sync.error.unknown':                     'inconnu',
      'sync.error.invalidKeyFormat':            'Format de clé invalide — attendu SYNC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX.',
      'sync.status.sendingLocal':               'Envoi de :count entités locales vers le compte.',
      'sync.status.cancelled':                  'Connexion annulée — déconnecté. Données locales intactes.',
      'sync.status.sendingAndDeleting':         'Envoi de :local entités locales, suppression de :remote du compte…',
      'sync.status.merged':                     'Fusionné. Les éléments uniquement locaux (:count éléments) iront au compte à la prochaine synchro.',
      'sync.error.firstSyncFailed':             'Connexion OK mais la première synchro a échoué : :msg',
      'sync.error.loginFailed':                 'Erreur de connexion : :msg',
      'sync.error.invalidEmail':                'Saisissez une adresse e-mail valide.',
      'sync.recovery.sent':                     'Si cette adresse e-mail est enregistrée, nous avons envoyé un lien de récupération. Consultez votre boîte de réception.',
      'sync.error.generic':                     'Erreur : :msg',
      'sync.account.thisDevice':                'cet appareil : :label',
      'sync.session.noLabel':                   '(sans libellé)',
      'sync.session.thisDeviceSuffix':          ' (cet appareil)',
      'sync.session.revoke':                    'Révoquer',
      'sync.error.revokeFailed':                'Erreur de révocation : :msg',
      'sync.session.fetchFailed':               'Échec de la récupération de la liste des sessions : :msg',
      'sync.key.copyManual':                    'Échec de la copie — cliquez sur « Afficher la clé » et faites Ctrl+C manuellement.',
      'sync.key.removed':                       'Clé retirée du stockage local.',
      'sync.backups.empty':                     'Aucune sauvegarde cloud. Créez la première en cliquant sur « Créer une sauvegarde maintenant ».',
      'sync.backups.fetchFailed':               'Échec de la récupération de la liste : :msg',
      'sync.backups.creating':                  'Création de la sauvegarde cloud…',
      'sync.backups.createFailed':              'Erreur de création de la sauvegarde : :msg',
      'sync.backups.restored':                  'Sauvegarde n°:id restaurée. :count entités remplacées. newCursor=:cursor.',
      'sync.backups.restoreFailed':             'Erreur de restauration : :msg',
      'sync.backups.downloadFailed':            'Erreur de téléchargement : :msg',
      'sync.backups.confirmDelete':             'Supprimer la sauvegarde n°:id du cloud ? Irréversible.',
      'sync.backups.deleted':                   'Sauvegarde n°:id supprimée.',
      'sync.backups.deleteFailed':              'Erreur de suppression : :msg',
      'sync.pushPullSummary':                   'Push : :pushedAccepted acceptées, :pushedRejected rejetées • Pull : :applied entités, :deletes suppressions.',
      'sync.returned':                          'La synchro a renvoyé : :result',
      'sync.snapshotFailed':                    'Erreur de snapshot : :msg',
      'sync.reconcile.running':                 'Resynchronisation complète avec le compte…',
      'sync.reconcile.okPushed':                'Resync OK — :count entités manquantes envoyées vers le compte.',
      'sync.reconcile.okInSync':                'Resync OK — la bibliothèque correspond au compte.',
      'sync.reconcile.failed':                  'Échec de la resynchronisation complète : :msg',
      'sync.email.saveButFailed':               'E-mail enregistré, mais l\'envoi du lien a échoué — réessayez.',
      'sync.email.verifyLinkSent':              'Lien de vérification envoyé à :email. Consultez votre boîte de réception et cliquez sur le lien.',
      'sync.email.removed':                     'E-mail supprimé.',

      // === Sync result modal — strategies ===
      'syncResult.merge.title':                 'Fusionné avec le cloud',
      'syncResult.merge.desc':                  'Les entités présentes aux deux endroits ont été écrasées par la version du compte. Les entités uniquement locales restent et iront au compte à la prochaine synchro.',
      'syncResult.merge.tableHeader':           '<table class="result-table"><thead><tr><th>Type</th><th>Du cloud : écrasées</th><th>Du cloud : nouvelles</th><th>Local conservé → push</th><th>Total après fusion</th></tr></thead><tbody>',
      'syncResult.useServer.title':             'Données locales remplacées par celles du cloud',
      'syncResult.useServer.desc':              'La bibliothèque locale a été entièrement remplacée par l\'état du compte. Les entités uniquement locales (le cas échéant) ont été perdues.',
      'syncResult.useServer.tableHeader':       '<table class="result-table"><thead><tr><th>Type</th><th>Récupérées du cloud</th><th>Local perdu</th><th>Communes (écrasées)</th><th>Total après synchro</th></tr></thead><tbody>',
      'syncResult.sendMine.title':              'Envoi des données locales vers le compte',
      'syncResult.sendMine.desc':               'La bibliothèque locale sera envoyée au compte. Les entités présentes dans le cloud mais absentes localement seront marquées comme supprimées (tombstone) sur le compte.',
      'syncResult.sendMine.tableHeader':        '<table class="result-table"><thead><tr><th>Type</th><th>Envoyées (push)</th><th>Écrasements sur le compte</th><th>Nouvelles sur le compte</th><th>Supprimées du compte</th></tr></thead><tbody>',
      'syncResult.fallback.title':              'Synchronisation terminée',
      'syncResult.empty':                       'Aucun détail.',
      'syncResult.label.folders':               'Dossiers',
      'syncResult.label.groups':                'Groupes',
      'syncResult.label.rules':                 'Jeux de règles',
      'syncResult.label.actions':               'Actions',

      // === Options — toasts (bottom notifications) ===
      'toast.saved':                            'Enregistré',
      'toast.cantDeleteLastGroup':              'Impossible de supprimer le dernier groupe d\'un dossier',
      'toast.noOtherTab':                       'Aucun autre onglet avec une URL de page',
      'toast.urlInserted':                      'URL de l\'onglet insérée',
      'toast.urlAlreadyAdded':                  'Motif déjà dans la liste : :pattern',
      'toast.error':                            'Erreur : :msg',
      'toast.shortcutMustIncludeMod':           'Raccourci autorisé : Ctrl+Alt+Shift + une lettre (sauf G H J K L), un chiffre ou un symbole (sauf \\).',
      'toast.shortcutCollidesChrome':           'Avertissement : le raccourci entre en conflit avec un raccourci Chrome (ex. nouvel onglet). Il peut ne pas fonctionner — choisissez-en un autre.',
      'toast.copied':                           'Copié : :text',
      'toast.copyFailed':                       'Échec de la copie',
      'toast.copyClipboard':                    'Copié dans le presse-papiers',
      'toast.copyLink':                         'Lien copié',
      'toast.copyToken':                        'Jeton copié',
      'toast.noHistory':                        'Aucun historique pour ce jeu de règles',
      'toast.versionRestored':                  'Version restaurée',
      'toast.fileNotFullImport':                'Le fichier ne convient pas à un import complet',
      'toast.unknownOption':                    'Option inconnue — import annulé',
      'toast.importedSettings':                 'Paramètres importés',
      'toast.parseError':                       'Erreur d\'analyse : :msg',
      'toast.invalidPartialFormat':             'Format invalide — export de groupe, dossier ou jeu de règles attendu',
      'toast.groupImport.folderRejected':       'Ceci est un export de dossier (plusieurs groupes). Importez-le via l\'import au niveau du dossier, pas du groupe.',
      'toast.exportedRule':                     'Jeu de règles exporté',
      'toast.signInFirst':                      'Connectez-vous d\'abord dans le panneau de synchro',
      'toast.exportNotReady':                   'L\'export n\'est pas prêt',
      'toast.noRulesSelected':                  'Aucun jeu de règles sélectionné',
      'toast.revokeError':                      'Erreur de révocation : :msg',
      'toast.importedFromLink':                 'Importé depuis le lien',
      'toast.invalidRuleFormat':                'Format de fichier invalide — export de jeu de règles attendu',
      'toast.importedReplaced':                 'Importé (remplacé)',
      'toast.importedAsCopy':                   'Importé comme copie',
      'toast.noImportData':                     'Aucune donnée à importer',
      'toast.importedFolder':                   'Dossier importé : :name',
      'toast.importedGroup':                    'Groupe importé : :name',
      'toast.importedRule':                     'Jeu de règles importé : :name',

      // === Options — native confirm() dialogs ===
      'confirm.restoreVersion':                 'Restaurer cette version ? Le contenu actuel sera ajouté à l\'historique comme nouveau snapshot.',
      'confirm.revokeSession':                  'Révoquer cette session ?',
      'confirm.removeKey':                      'Retirer la clé de cet appareil ?\n\nLa connexion fonctionne toujours (la session est indépendante de la clé), mais vous ne pourrez plus afficher ni copier la clé vers un autre appareil depuis cet ordinateur.\n\nAssurez-vous d\'avoir une copie de la clé enregistrée ailleurs (gestionnaire de mots de passe).',
      'confirm.snapshot':                       'Un snapshot complet télécharge toute la bibliothèque depuis le cloud et remplace l\'état local. Vos modifications en attente ne seront PAS perdues (elles restent dans la file). Continuer ?',
      'confirm.removeEmail':                    'Retirer l\'e-mail du compte ? Sans e-mail, vous ne pourrez pas récupérer en cas de perte de clé.',
      'confirm.signOut':                        'Déconnecter cet appareil ? La bibliothèque locale reste, mais cesse de se synchroniser.',

      // === Modal — full export ===
      'modal.export.title':                     'Exporter les paramètres',
      'modal.export.mode.all':                  'Tout',
      'modal.export.mode.select':               'Dossiers/règles sélectionnés',
      'modal.export.selectAll':                 'Tout sélectionner',
      'modal.export.deselectAll':               'Tout désélectionner',
      'modal.export.hint':                      'Vous pouvez copier le JSON dans le presse-papiers ou télécharger le fichier :',
      'modal.export.close':                     'Fermer',
      'modal.export.share':                     'Partager l\'URL',
      'modal.export.share.title':               'Partager cet export via un lien public à durée limitée',
      'modal.export.download':                  'Télécharger le fichier',
      'modal.export.copy':                      'Copier dans le presse-papiers',

      // === Modal — full import ===
      'modal.import.title':                     'Importer les paramètres',
      'modal.import.pickerHint':                'Collez le JSON de n\'importe quel export JustZix (sauvegarde complète, dossier, groupe ou jeu de règles) — le formulaire détectera le type et proposera des options de placement.',
      'modal.import.warningStrong':             'Avertissement :',
      'modal.import.warningRest':               ' un import complet écrasera tous les paramètres actuels.',
      'modal.import.placeholder':               'Collez le JSON ici ou choisissez un fichier...',
      'modal.import.cancel':                    'Annuler',
      'modal.import.import':                    'Importer',

      // === Modal — folder export ===
      'modal.folderExport.titlePrefix':         'Exporter le dossier :',
      'modal.folderExport.hint':                'Sélectionnez les groupes/règles à exporter.',
      'modal.folderExport.cancel':              'Annuler',
      'modal.folderExport.share':               'Partager l\'URL',
      'modal.folderExport.shareTitle':          'Partager ce dossier via un lien public à durée limitée',
      'modal.folderExport.download':            'Télécharger le fichier',

      // === Modal — group export ===
      'modal.groupExport.titlePrefix':          'Exporter le groupe :',
      'modal.groupExport.hint':                 'Sélectionnez les jeux de règles à exporter.',
      'modal.groupExport.cancel':               'Annuler',
      'modal.groupExport.share':                'Partager l\'URL',
      'modal.groupExport.shareTitle':           'Partager ce groupe via un lien public à durée limitée',
      'modal.groupExport.download':             'Télécharger le fichier',

      // === Modal — folder import ===
      'modal.folderImport.title':               'Importer un dossier',
      'modal.folderImport.placeholder':         'Collez le JSON du dossier ou choisissez un fichier...',
      'modal.folderImport.conflicts':           'Conflits détectés :',
      'modal.folderImport.action.rename':       'Importer comme nouveau dossier (avec le suffixe « (import) »)',
      'modal.folderImport.action.overwrite':    'Écraser le dossier existant',
      'modal.folderImport.action.merge':        'Fusionner — ajouter les nouveaux jeux de règles au dossier existant (en gardant les deux)',
      'modal.folderImport.cancel':              'Annuler',
      'modal.folderImport.import':              'Importer',

      // === Modal — action code ===
      // === Action bars (Phase 2 of multi-bars system, since v2.9) ===
      'actionBars.btn.label':                   'Barres d\'actions',
      'actionBars.btn.title':                   'Gérer les barres d\'actions attribuées à ce dossier',
      'actionBars.btn.titleGroup':              'Gérer les barres d\'actions attribuées à ce groupe',
      'actionBars.btn.titleRule':               'Gérer les barres d\'actions attribuées à ce jeu de règles',
      'actionBars.parentType.folder':           'Dossier',
      'actionBars.parentType.group':            'Groupe',
      'actionBars.parentType.rule':             'Jeu de règles',
      'actionBars.parentType.folder.short':     '[D]',
      'actionBars.parentType.group.short':      '[G]',
      'actionBars.parentType.rule.short':       '[R]',
      'actionBars.empty':                       'Aucune barre d\'actions à ce niveau. Cliquez sur « + Ajouter une nouvelle barre » pour créer la première.',
      'actionBars.section.owned':               'Barres à ce niveau',
      'actionBars.section.fromFolder':          '↑ Héritée du dossier « <strong>:name</strong> »',
      'actionBars.section.fromGroup':           '↑ Héritée du groupe « <strong>:name</strong> »',
      'actionBars.elementsCount':               'Éléments : :count',
      'actionBars.enabled':                     'Activée',
      'actionBars.name.placeholder':            'NOM',
      'actionBars.name.title':                  'Libellé au bord de la barre (max. 6 caractères, MAJUSCULES). Vide = pas de libellé.',
      'actionBars.color.title':                 'Couleur de fond de la barre',
      'actionBars.orientation.title':           'Cliquer pour basculer l\'orientation de la barre (verticale ↔ horizontale)',
      'actionBars.color.clear':                 'Effacer la couleur de fond',
      'actionBars.moveTo':                      'Déplacer vers :',
      'actionBars.delete':                      'Supprimer',
      'actionBars.confirmDelete':               'Supprimer cette barre ?',
      'actionBars.confirmDeleteWithElements':   'Supprimer cette barre ? Les éléments attribués (:count) garderont leurs définitions mais perdront l\'attribution de barre — vous devrez les attribuer manuellement à une autre barre.',
      'actionBars.deleteMigrate.message':       'Cette barre a :count éléments attribués. Choisissez où les déplacer avant la suppression (ou détachez-les de toute barre) :',
      'actionBars.deleteMigrate.detachOption':  '— détacher les éléments (aucune barre) —',
      'actionBars.deleteMigrate.confirm':       'Déplacer et supprimer',
      'actionBars.deleteMigrate.cancel':        'Annuler',
      'modal.actionBars.titlePrefix':           'Barres d\'actions —',
      'modal.actionBars.hint':                  'Barres attribuées à ce niveau. Les barres de dossier agrègent les éléments de tous les jeux de règles actifs du dossier, les barres de groupe — uniquement du groupe, les barres de jeu de règles — uniquement quand le jeu de règles est actif. Une barre sans élément visible ne sera pas rendue.',
      'modal.actionBars.add':                   '+ Ajouter une nouvelle barre',
      'modal.actionBars.close':                 'Fermer',

      // === Windows — dropdown type selector (since v2.13.41) ===
      'windows.btn.label':                      'Fenêtres',
      'windows.btn.title':                      'Gérer les fenêtres attribuées à ce dossier (CSS / JS / consoles)',
      'windows.btn.titleGroup':                 'Gérer les fenêtres attribuées à ce groupe',
      'windows.btn.titleRule':                  'Gérer les fenêtres attribuées à ce jeu de règles',
      'windows.menu.title':                     'Choisissez le type de fenêtre :',
      'windows.menu.comingSoon':                'bientôt disponible',
      'jsPanes.btn.label':                      'Panneaux JS',
      'jsConsoles.btn.label':                   'Consoles JS',
      'outputConsoles.btn.label':               'Consoles de sortie',

      // === CSS panes (Phase 2 "Windows on the front", since v2.13.40) ===
      'cssPanes.btn.label':                     'Panneaux CSS',
      'cssPanes.contextMenu.disable':           'Fermer le panneau CSS',
      'cssPanes.btn.title':                     'Gérer les panneaux CSS attribués à ce dossier',
      'cssPanes.btn.titleGroup':                'Gérer les panneaux CSS attribués à ce groupe',
      'cssPanes.btn.titleRule':                 'Gérer les panneaux CSS attribués à ce jeu de règles',
      'cssPanes.empty':                         'Aucun panneau CSS à ce niveau. Cliquez sur « + Ajouter un nouveau panneau CSS » pour créer le premier.',
      'cssPanes.section.owned':                 'Panneaux à ce niveau',
      'cssPanes.name.placeholder':              'Nom du panneau',
      'cssPanes.color.title':                   'Couleur du point d\'en-tête',
      'panes.style.dot':                        'Point',
      'panes.style.headerBg':                   'Fond d\'en-tête',
      'panes.style.headerText':                 'Texte d\'en-tête',
      'panes.style.bodyBg':                     'Fond du corps',
      'panes.style.bodyText':                   'Texte du corps',
      'panes.style.runBtn':                     'Bouton Exécuter',
      'panes.style.reset':                      'Effacer',
      'mcp.default.title':                      'Restaurer par défaut',
      'action.colors.bg':                       'Fond',
      'action.colors.text':                     'Texte',
      'action.colors.dropdownBg':               'Fond du menu',
      'action.colors.option':                   'Option',
      'action.colors.selected':                 'Sélectionné',
      'action.colors.hover':                    'Survol',
      'action.colors.label':                    'Libellé',
      'action.colors.value':                    'Valeur',
      'action.colors.placeholder':              'Espace réservé',
      'action.colors.bgInactive':               'Fond inactif',
      'action.colors.inactiveText':             'Texte inactif',
      'cssPanes.enabled.label':                 'Activé',
      'cssPanes.enabled.title':                 'Si le panneau doit apparaître sur les pages correspondantes',
      'cssPanes.delete.title':                  'Supprimer ce panneau',
      'cssPanes.delete.confirm':                'Supprimer le panneau CSS « :name » ? Le contenu de la zone de texte dans les onglets actifs sera perdu.',
      'modal.cssPanes.titlePrefix':             'Panneaux CSS —',
      'modal.cssPanes.hint':                    'Zone de texte flottante injectée sur les pages correspondant aux URLs de ce niveau. Le contenu est conservé dans sessionStorage par onglet (perdu à la fermeture de l\'onglet). Injection <style> en direct — les modifications CSS s\'appliquent instantanément.',
      'modal.cssPanes.add':                     '+ Ajouter un nouveau panneau CSS',
      'modal.cssPanes.close':                   'Fermer',
      'cssPane.frontend.close.title':           'Masquer le panneau (restera masqué jusqu\'à la fermeture de l\'onglet)',

      // === JS panes (Phase 2 cont., since v2.13.56) ===
      'jsPanes.contextMenu.disable':            'Fermer le panneau JS',
      'jsPanes.btn.title':                      'Gérer les panneaux JS attribués à ce dossier',
      'jsPanes.btn.titleGroup':                 'Gérer les panneaux JS attribués à ce groupe',
      'jsPanes.btn.titleRule':                  'Gérer les panneaux JS attribués à ce jeu de règles',
      'jsPanes.empty':                          'Aucun panneau JS à ce niveau. Cliquez sur « + Ajouter un nouveau panneau JS » pour créer le premier.',
      'jsPanes.section.owned':                  'Panneaux à ce niveau',
      'jsPanes.name.placeholder':               'Nom du panneau',
      'jsPanes.color.title':                    'Couleur du point d\'en-tête',
      'jsPanes.enabled.label':                  'Activé',
      'jsPanes.enabled.title':                  'Si le panneau doit apparaître sur les pages correspondantes',
      'jsPanes.delete.title':                   'Supprimer ce panneau',
      'jsPanes.delete.confirm':                 'Supprimer le panneau JS « :name » ? Le contenu de la zone de texte dans les onglets actifs sera perdu.',
      'modal.jsPanes.titlePrefix':              'Panneaux JS —',
      'modal.jsPanes.hint':                     'Zone de texte flottante exécutant du JS dans le contexte de la page (MAIN world, new Function) à chaque modification. Le contenu est conservé par onglet dans sessionStorage. Les erreurs d\'exécution sont affichées dans un overlay rouge sous le code.',
      'modal.jsPanes.add':                      '+ Ajouter un nouveau panneau JS',
      'modal.jsPanes.close':                    'Fermer',
      'jsPane.frontend.close.title':            'Masquer le panneau (restera masqué jusqu\'à la fermeture de l\'onglet)',

      // === JS Consoles (Phase 2 cont., since v2.13.66) ===
      'jsConsoles.contextMenu.disable':         'Fermer la console JS',
      'jsConsoles.empty':                       'Aucune console à ce niveau. Cliquez sur « + Ajouter une nouvelle console » pour créer la première.',
      'jsConsoles.section.owned':               'Consoles à ce niveau',
      'jsConsoles.name.placeholder':            'Nom de la console',
      'jsConsoles.enabled.label':               'Activée',
      'jsConsoles.enabled.title':               'Si la console doit apparaître sur les pages correspondantes',
      'jsConsoles.delete.title':                'Supprimer cette console',
      'jsConsoles.delete.confirm':              'Supprimer la console « :name » ? L\'historique de sortie dans les onglets actifs sera perdu.',
      'modal.jsConsoles.titlePrefix':           'Consoles JS —',
      'modal.jsConsoles.hint':                  'REPL interactive — Ctrl+Entrée exécute le code dans le MAIN world de la page, la sortie (ou l\'erreur) s\'ajoute à la zone de log sous la saisie. La sortie est conservée par onglet dans sessionStorage.',
      'modal.jsConsoles.add':                   '+ Ajouter une nouvelle console',
      'modal.jsConsoles.close':                 'Fermer',
      'jsConsole.frontend.close.title':         'Masquer la console (jusqu\'à la fermeture de l\'onglet)',
      'jsConsole.input.placeholder':            'Code à exécuter (Ctrl+Entrée pour exécuter, Ctrl+L pour effacer)',
      'jsConsole.clear.title':                  'Effacer la sortie (Ctrl+L)',

      // === Output Console (Phase 2 finish, since v2.13.72) — read-only log viewer. ===
      'panes.style.clearBtn':                   'Bouton Effacer',
      'panes.style.actionBtns':                 'Boutons d\'action',
      'outputConsoles.contextMenu.disable':     'Fermer la console de sortie',
      'outputConsoles.empty':                   'Aucune console de sortie à ce niveau. Cliquez sur « + Ajouter une nouvelle console de sortie » pour créer la première.',
      'outputConsoles.section.owned':           'Consoles de sortie à ce niveau',
      'outputConsoles.name.placeholder':        'Nom de la console de sortie',
      'outputConsoles.enabled.label':           'Activée',
      'outputConsoles.enabled.title':           'Si la console de sortie doit apparaître sur les pages correspondantes',
      'outputConsoles.delete.title':            'Supprimer cette console de sortie',
      'outputConsoles.delete.confirm':          'Supprimer la console de sortie « :name » ? L\'historique des logs dans les onglets actifs sera perdu.',
      'modal.outputConsoles.titlePrefix':       'Consoles de sortie —',
      'modal.outputConsoles.hint':              'Visionneuse de logs en lecture seule — capture console.log/warn/error/info de la page (y compris asynchrone). Effacer (⌫) n\'efface que la visionneuse, pas la console du navigateur.',
      'modal.outputConsoles.add':               '+ Ajouter une nouvelle console de sortie',
      'modal.outputConsoles.close':             'Fermer',
      'outputConsole.frontend.close.title':     'Masquer la console de sortie (jusqu\'à la fermeture de l\'onglet)',
      'outputConsole.clear.title':              'Effacer la visionneuse (Ctrl+Shift+L)',
      'outputConsole.autoscroll.title':         'Défilement automatique vers les derniers logs',
      'outputConsole.empty':                    '(aucun log — en attente de console.* / JZ.* depuis la page)',
      'outputConsole.mode.standard.title':      'Afficher uniquement console.* (clic / Ctrl+Shift+K → JZ.*)',
      'outputConsole.mode.jz.title':            'Afficher uniquement window.JZ.* (clic / Ctrl+Shift+K → les deux)',
      'outputConsole.mode.both.title':          'Afficher les deux : console.* + JZ.* (clic / Ctrl+Shift+K → console.* uniquement)',
      'outputConsole.filters.title':            'Filtres de niveau',
      'outputConsole.filters.level.log':        'log',
      'outputConsole.filters.level.info':       'info',
      'outputConsole.filters.level.warn':       'warn',
      'outputConsole.filters.level.error':      'error',
      'outputConsole.filters.level.debug':      'debug',
      'outputConsole.search.title':             'Rechercher dans les logs (Ctrl+Shift+S ; Échap efface)',
      'outputConsole.search.placeholder':       'Filtrer les entrées…',
      'outputConsole.tab.all':                  'Tout',
      'outputConsole.tab.jz':                   'JZconsole',
      'outputConsole.tab.console':              'Console',
      'outputConsole.tab.network':              'Network',
      'outputConsole.tab.errors':               'Erreurs',
      'outputConsole.tab.datalayer':            'DataLayer',
      'outputConsole.net.field.callTime':       'Heure d\'appel',
      'outputConsole.net.field.status':         'Statut',
      'outputConsole.net.field.type':           'Type',
      'outputConsole.net.field.method':         'Méthode',
      'outputConsole.net.field.domain':         'Domaine',
      'outputConsole.net.field.path':           'Chemin',
      'outputConsole.net.field.remote':         'Adresse distante',
      'outputConsole.net.field.initiator':      'Initiateur',
      'outputConsole.net.field.time':           'Durée',
      'outputConsole.net.field.size':           'Taille',
      'outputConsole.net.field.url':            'URL',
      'outputConsole.net.field.error':          'Erreur',
      'outputConsole.net.status.err':           'Erreurs/Autres',
      'outputConsole.filter.all':               'tout',
      'outputConsole.filter.none':              'aucun',
      'outputConsole.net.domainFilter':         'filtrer par domaine…',
      'outputConsole.net.domainsInclude':       'Domaines+',
      'outputConsole.net.domainsExclude':       'Domaines-',
      'outputConsole.dl.newPushes':             'Nouveaux pushes',
      'outputConsole.dl.currentState':          'État actuel',
      'outputConsole.dl.expandAll':             'tout déplier',
      'outputConsole.dl.collapseAll':           'tout replier',
      'outputConsole.net.size':                 'Taille',
      'outputConsole.net.time':                 'Durée',
      'outputConsole.err.field.type':           'Type',
      'outputConsole.err.field.location':       'Emplacement',
      'outputConsole.err.field.stack':          'Stack',
      'outputConsole.apiHelp.title': 'JustZix — raccourcis et fenêtres sur la page',
      'outputConsole.apiHelp.sec.global': 'Raccourcis globaux — fonctionnent sur toute page',
      'outputConsole.apiHelp.g.tempCss': 'Fenêtre TEMP CSS — ouvrir / fermer',
      'outputConsole.apiHelp.g.tempJs': 'Fenêtre TEMP JS — ouvrir / fermer',
      'outputConsole.apiHelp.g.tempJsc': 'Console TEMP JS — ouvrir / fermer',
      'outputConsole.apiHelp.g.tempOut': 'Console TEMP Output — ouvrir / fermer',
      'outputConsole.apiHelp.g.ai': 'AI Helper — ouvrir / fermer',
      'outputConsole.apiHelp.g.edit': 'Mode édition de barre d\'actions — activer / désactiver',
      'outputConsole.apiHelp.sec.window': 'Dans une fenêtre — quand elle a le focus',
      'outputConsole.apiHelp.w.run': 'Exécuter le code / envoyer le message — panneau JS, Console JS, AI Helper',
      'outputConsole.apiHelp.w.history': 'Historique des commandes saisies — Console JS',
      'outputConsole.apiHelp.w.clearConsole': 'Effacer la sortie — Console JS',
      'outputConsole.apiHelp.w.escSearch': 'Effacer le champ de recherche — Console Output',
      'outputConsole.apiHelp.sec.output': 'Console Output',
      'outputConsole.apiHelp.o.clear': 'Effacer l\'onglet actif de chaque Console Output',
      'outputConsole.apiHelp.o.help': 'Afficher / masquer cette aide',
      'outputConsole.apiHelp.o.tabs': '6 onglets : All, JZ, Console, Network, Errors, DataLayer — chacun avec son propre jeu de filtres.',
      'outputConsole.apiHelp.o.network': 'Network : Alt+clic sur une ligne ajoute son domaine au filtre d\'inclusion, Alt+Shift+clic au filtre d\'exclusion.',
      'outputConsole.apiHelp.o.datalayer': 'DataLayer : faites glisser la barre entre les colonnes pour les redimensionner.',
      'outputConsole.apiHelp.sec.mouse': 'Fenêtres sur la page — à la souris',
      'outputConsole.apiHelp.m.drag': 'Faites glisser une fenêtre par sa barre d\'en-tête pour la déplacer.',
      'outputConsole.apiHelp.m.resize': 'Redimensionnez une fenêtre en tirant l\'une de ses 8 poignées aux bords et aux coins.',
      'outputConsole.apiHelp.m.snap': 'Approchez une fenêtre d\'une autre — elles s\'aimantent en un groupe qui se déplace ensemble.',
      'outputConsole.apiHelp.m.context': 'Un clic droit sur la barre d\'en-tête ouvre un menu : fermer la fenêtre, déconnecter, police, taille du texte.',
      'outputConsole.apiHelp.m.tempDbl': 'Un double-clic sur la barre d\'en-tête d\'une fenêtre TEMP bascule entre la taille par défaut et la taille mémorisée.',
      'outputConsole.apiHelp.m.scroll': 'Le défilement à la molette dans une fenêtre ne fait défiler que son contenu, pas la page en dessous.',
      'outputConsole.apiHelp.sec.editmode': 'Mode édition de barre — après Ctrl+Alt+S',
      'outputConsole.apiHelp.em.undo': 'Annuler le dernier changement',
      'outputConsole.apiHelp.em.delete': 'Supprimer les étiquettes sélectionnées',
      'outputConsole.apiHelp.em.deselect': 'Désélectionner tous les éléments',
      'outputConsole.apiHelp.em.move': 'Faites glisser les éléments sélectionnés à la souris — plusieurs se déplacent ensemble.',
      'outputConsole.apiHelp.em.select': 'Un clic sélectionne un élément, Ctrl+clic l\'ajoute ou le retire de la sélection, Shift+glisser sur une zone vide sélectionne avec un rectangle.',
      'outputConsole.apiHelp.em.label': 'Le bouton « + Étiquette » ajoute un libellé de texte statique ; double-cliquez sur une étiquette pour ouvrir son éditeur.',
      'outputConsole.apiHelp.sec.logging': 'Journalisation vers la Console Output',
      'outputConsole.apiHelp.jzApi': 'Logger propre (kind=jz)',
      'outputConsole.apiHelp.aliases': 'Alias : window.__JUSTZIX__ (toujours), window.JZ (si non pris par la page).',
      'outputConsole.apiHelp.consoleApi': 'console.* standard (kind=console, capturé automatiquement)',
      'outputConsole.apiHelp.consoleNote': 'Chaque console.* de la page est transmis à la console d\'origine (les DevTools continuent de fonctionner) et envoyé à la Console Output.',
      'outputConsole.apiHelp.footer': 'Manuel complet (8 langues) : panneau JustZix → Aide.',
      // === Onboarding / tutoriel ===
      'content.exec.cspBlocked': 'Cette page bloque l\'exécution des scripts (CSP). Activez « Scripts utilisateur » pour JustZix dans les paramètres du navigateur.',
      'onboard.ui.back': 'Retour',
      'onboard.ui.next': 'Suivant',
      'onboard.ui.finish': 'Terminer',
      'onboard.ui.copy': 'Copier',
      'onboard.ui.copied': 'Copié',
      'onboard.ui.hint': 'Effectuez cette action pour continuer',
      'onboard.ui.close': 'Fermer',
      'onboard.front.title': 'Tutoriel front-end',
      'onboard.front.lead': 'Découvrez les outils JustZix disponibles directement sur la page — le bouton flottant, le panneau, les fenêtres de code et les barres d\'actions.',
      'onboard.front.titleFirst': 'JustZix sur la page',
      'onboard.front.leadFirst': 'JustZix fonctionne aussi directement sur la page. Ce tutoriel présente le bouton flottant, les fenêtres CSS/JS et les barres d\'actions — étape par étape.',
      'onboard.front.empty': 'Les tutoriels front-end arrivent bientôt.',
      'onboard.lr.titleFirst': 'Bienvenue dans JustZix',
      'onboard.lr.title': 'Centre de tutoriels',
      'onboard.lr.leadFirst': 'Première fois ici ? Le tutoriel interactif vous guide dans la création d\'une solution complète et fonctionnelle — étape par étape, directement dans le panneau.',
      'onboard.lr.lead': 'Lancez le guide interactif — il vous accompagne étape par étape dans la création d\'une solution finie.',
      'onboard.lr.scenarios': 'Tutoriels',
      'onboard.lr.start': 'Commencer',
      'onboard.lr.later': 'Plus tard',
      'onboard.lr.noAuto': 'Ne pas afficher au démarrage',
      'onboard.proj.name': 'Votre premier projet',
      'onboard.proj.desc': 'De zéro à une solution fonctionnelle : un dossier, une barre d\'actions, un groupe, un jeu de règles avec CSS et JS, et un bouton qui bascule l\'effet.',
      'onboard.proj.meta': '19 étapes · ~3 min',
      'onboard.proj.s1.title': 'Bienvenue dans JustZix',
      'onboard.proj.s1.body': 'JustZix injecte votre propre CSS et JavaScript dans les pages choisies. Nous allons maintenant construire une solution complète avec un bouton fonctionnel — étape par étape. Cela prend environ 3 minutes.',
      'onboard.proj.s2.title': 'Ajouter un dossier',
      'onboard.proj.s2.body': 'Un dossier regroupe les règles d\'un site. Cliquez le bouton <code>+</code> mis en évidence dans le panneau latéral.',
      'onboard.proj.s3.title': 'Nom du dossier',
      'onboard.proj.s3.body': 'Saisissez un nom, p. ex. <code>Mon premier projet</code> — c\'est une étiquette pour vous seul.',
      'onboard.proj.s4.title': 'Motif URL',
      'onboard.proj.s4.body': 'Il détermine sur quelles pages le dossier fonctionne. Saisissez une adresse avec un astérisque et appuyez sur Entrée, p. ex. <code>https://example.com/*</code>. Pour vous exercer sur n\'importe quelle page, saisissez simplement <code>*</code>.',
      'onboard.proj.s5.title': 'Étiquette du bouton',
      'onboard.proj.s5.body': 'Jusqu\'à 3 lettres — elles apparaissent sur le bouton flottant JustZix affiché sur la page.',
      'onboard.proj.s6.title': 'Barre d\'actions',
      'onboard.proj.s6.body': 'Une barre d\'actions est un panneau de boutons interactifs visible sur la page. Créons-en une d\'abord — cliquez le bouton des barres d\'actions mis en évidence.',
      'onboard.proj.s7.title': 'Ajouter une barre',
      'onboard.proj.s7.body': 'Cliquez pour créer une nouvelle barre d\'actions dans ce dossier.',
      'onboard.proj.s8.title': 'Fermer le gestionnaire',
      'onboard.proj.s8.body': 'La barre est prête. Fermez la fenêtre du gestionnaire de barres pour revenir au dossier.',
      'onboard.proj.s9.title': 'Ouvrir le groupe',
      'onboard.proj.s9.body': 'En créant le dossier, JustZix a déjà ajouté un premier groupe. Dans le panneau latéral, dépliez le dossier et cliquez ce groupe pour l\'ouvrir, puis cliquez "Suivant".',
      'onboard.proj.s10.title': 'Ajouter un jeu de règles',
      'onboard.proj.s10.body': 'Un jeu de règles est la règle proprement dite — une paire de code CSS et JavaScript. Cliquez le bouton d\'ajout de jeu de règles mis en évidence.',
      'onboard.proj.s11.title': 'Ouvrir le jeu de règles',
      'onboard.proj.s11.body': 'Le nouveau jeu de règles est apparu dans l\'arbre sous le groupe. Cliquez-le pour ouvrir l\'éditeur du jeu de règles, puis cliquez "Suivant".',
      'onboard.proj.s12.title': 'Code CSS',
      'onboard.proj.s12.body': 'L\'onglet CSS est déjà actif. Collez le code ci-dessous — il met toute la page en niveaux de gris et prépare le bouton de bascule de l\'effet.',
      'onboard.proj.s13.title': 'Onglet JavaScript',
      'onboard.proj.s13.body': 'Cliquez l\'onglet <code>JavaScript</code> pour ajouter du code JS au jeu de règles.',
      'onboard.proj.s14.title': 'Code JavaScript',
      'onboard.proj.s14.body': 'Le code JS s\'exécute à l\'ouverture de la page. Collez le code ci-dessous — il affiche un message confirmant que JustZix fonctionne. Le champ a une validation de syntaxe.',
      'onboard.proj.s15.title': 'Onglet Actions',
      'onboard.proj.s15.body': 'Nous allons maintenant ajouter un bouton interactif. Cliquez l\'onglet des actions dans l\'éditeur du jeu de règles.',
      'onboard.proj.s16.title': 'Ajouter un bouton',
      'onboard.proj.s16.body': 'Cliquez pour ajouter un élément d\'action. Par défaut, cela crée un bouton qui exécute du code à la demande.',
      'onboard.proj.s17.title': 'Code du bouton',
      'onboard.proj.s17.body': 'Chaque bouton a son propre code qui s\'exécute au clic sur la page. Cliquez le champ de code du nouveau bouton.',
      'onboard.proj.s18.title': 'Code de bascule',
      'onboard.proj.s18.body': 'Collez le code qui active et désactive les niveaux de gris définis en CSS. Enregistrez ensuite les modifications dans la fenêtre de l\'éditeur de code.',
      'onboard.proj.s19.title': 'Terminé !',
      'onboard.proj.s19.body': 'Vous avez une solution complète : un dossier, une barre d\'actions, un groupe, un jeu de règles avec du code CSS et JS, et un bouton fonctionnel. Ouvrez une page correspondant au motif URL — vous verrez le message, les niveaux de gris et le bouton qui les bascule. Vous pouvez relancer le tutoriel depuis le menu des réglages.',
      'panes.contextMenu.font':                 'Police',
      'panes.contextMenu.size':                 'Taille',
      'panes.contextMenu.defaultFont':          'Par défaut',
      'modal.confirmDismissDirty':              'Vous avez des modifications non enregistrées. Fermer et les perdre ?',
      'modal.confirmSaveBeforeClose':           'Vous avez des modifications non enregistrées.\n\nCliquez sur OK pour enregistrer et fermer.\nCliquez sur Annuler pour passer à la confirmation d\'abandon.',
      'modal.confirmDiscardChanges':            'Fermer SANS enregistrer ? Les modifications seront perdues.\n\nCliquez sur OK pour abandonner les modifications.\nCliquez sur Annuler pour continuer l\'édition.',
      'actionBars.moveLocked':                  'Déplacement impossible — la barre a des éléments qui se retrouveraient hors du nouveau périmètre. Réattribuez-les d\'abord.',
      'action.bar.noneOption':                  '— aucune barre —',
      'action.bar.unnamed':                     '(sans nom)',
      'action.bar.selectTitle':                 'Attribuer cette action à une barre. Les barres sont héritées vers le bas (dossier → groupe → règle).',
      'action.bar.noBarsHint':                  'Aucune barre d\'actions dans l\'arbre du jeu de règles — ajoutez une barre via le bouton « Barres d\'actions » dans le dossier/groupe/règle.',

      // === Typed actions (since v2.12.x) ===
      'action.color.title':                     'Couleur de fond (bouton/select/input/slider). Clic = sélecteur, ou saisissez l\'hex à côté.',
      'action.colorText.title':                 'Couleur du texte (et remplissage de la barre pour SLIDER). Blanc par défaut si non défini.',
      'action.colorHex.title':                  'Code hex — copier (Ctrl+C) ou coller (Ctrl+V) en texte brut. Accepte #RGB ou #RRGGBB.',
      'action.type.title':                      'Type d\'élément (bouton, menu déroulant SELECT, …)',
      'action.type.button':                     'Bouton',
      'action.type.select':                     'Menu déroulant (SELECT)',
      'action.type.input':                      'Champ de texte (INPUT)',
      'action.type.slider':                     'Curseur (SLIDER)',
      'action.type.textarea':                   'Champ multiligne (TEXTAREA)',
      'action.type.toggle3':                    'Bascule à 3 états (TOGGLE3)',

      'action.memory.label':                    'Mémoriser la valeur :',
      'action.memory.none':                     'Ne pas mémoriser (réinitialisé au rechargement)',
      'action.memory.page':                     'Par page (URL)',
      'action.memory.host':                     'Par domaine',
      'action.memory.ruleSet':                  'Au sein du jeu de règles (toutes les URLs où il s\'exécute)',
      'action.memory.global':                   'Globalement (toutes les pages, partagé)',
      'action.input.middleBtn':                 'INPUT · code :code',
      'action.slider.middleBtn':                'SLIDER · :min–:max pas :step :unit · code :code',
      'action.textarea.middleBtn':              'TEXTAREA · :rows lignes · code :code',
      'action.toggle3.middleBtn':               'TOGGLE3 · :filled/3 états · code :code',
      'action.toggle3.defaultState1':           'OFF',
      'action.toggle3.defaultState2':           'MID',
      'action.toggle3.defaultState3':           'ON',
      'action.toggle3.stateLabel.placeholder':  'libellé (max. 5)',
      'action.toggle3.stateValue.placeholder':  'valeur (vers JS)',
      'action.toggle3.defaultStateTitle':       'État par défaut (sans mémoire)',

      'action.select.middleBtn.staticEmpty':    'SELECT statique (aucune option)',
      'action.select.middleBtn.static':         'SELECT statique · :count options · code :code',
      'action.select.middleBtn.js':             'SELECT js · code :code',
      'action.select.empty':                    'Aucune option. Ajoutez la première avec le bouton ci-dessous.',
      'action.select.untitled':                 '(sans titre)',
      'action.select.optionNamePlaceholder':    'Nom (visible)',
      'action.select.optionValuePlaceholder':   'valeur (transmise au JS)',
      'action.select.deleteOption':             'Supprimer cette option',

      // === Modal — action code ===
      'modal.actionCode.titlePrefix':           'Code du bouton d\'action :',
      'modal.actionCode.body':                  'Code JavaScript exécuté lorsque le bouton d\'action est cliqué dans le panneau flottant. S\'exécute dans le contexte de la page.',
      'modal.actionCode.cancel':                'Annuler',
      'modal.actionCode.save':                  'Enregistrer',

      // === Modal — INPUT editor (since v2.13.0) ===
      'modal.inputEditor.titlePrefix':          'Éditeur INPUT :',
      'modal.inputEditor.body':                 'Champ de texte avec code JS facultatif déclenché à la modification (Entrée/perte de focus). La valeur est mémorisée selon la portée sélectionnée.',
      'modal.inputEditor.placeholder':          'Espace réservé :',
      'modal.inputEditor.defaultValue':         'Valeur par défaut :',
      'modal.inputEditor.colorPlaceholder':     'Couleur de l\'espace réservé :',
      'modal.inputEditor.codeLabel':            'Code JS (à la modification) :',
      'modal.inputEditor.hint':                 'Disponible dans le code : <code>value</code> (chaîne du champ), <code>$el</code> (HTMLInputElement), <code>$action</code> (id, label).',
      'modal.inputEditor.close':                'Fermer',
      'modal.colorClear':                       'Restaurer la couleur par défaut',
      'modal.sliderEditor.colorLabel':          'Couleur du libellé (gauche) :',
      'modal.sliderEditor.colorValue':          'Couleur de la valeur (droite) :',
      'modal.selectOptions.appearance.title':   'Apparence du menu déroulant :',
      'modal.selectOptions.appearance.hint':    'Pour la <strong>variante js</strong> : le fond du menu et la couleur des options fonctionnent dans tous les navigateurs. Les fonds de l\'option sélectionnée et du survol sont propres au navigateur (dépendent de l\'OS) — pour une personnalisation complète, utilisez la variante <em>statique</em>.',
      'modal.selectOptions.colorDropdownBg':    'Fond du menu déroulant :',
      'modal.selectOptions.colorOption':        'Couleur du texte des options :',
      'modal.selectOptions.colorSelected':      'Fond de l\'option sélectionnée :',
      'modal.selectOptions.colorHover':         'Fond au survol :',
      'modal.sliderEditor.titlePrefix':         'Éditeur SLIDER :',
      'modal.sliderEditor.body':                'Curseur avec code JS facultatif déclenché après que l\'utilisateur a relâché le curseur (`change`). La valeur est transmise comme Number.',
      'modal.sliderEditor.min':                 'Minimum :',
      'modal.sliderEditor.max':                 'Maximum :',
      'modal.sliderEditor.step':                'Pas :',
      'modal.sliderEditor.defaultValue':        'Valeur par défaut :',
      'modal.sliderEditor.unit':                'Unité (facultatif) :',
      'modal.sliderEditor.codeLabel':           'Code JS (à la modification) :',
      'modal.sliderEditor.hint':                'Disponible dans le code : <code>value</code> (Number), <code>$el</code> (HTMLInputElement type=range), <code>$action</code> (id, label).',
      'modal.sliderEditor.close':               'Fermer',
      'modal.sliderEditor.errMaxGtMin':         'Le maximum doit être supérieur au minimum.',
      'modal.sliderEditor.errStepPositive':     'Le pas doit être positif.',
      'modal.textareaEditor.titlePrefix':       'Éditeur TEXTAREA :',
      'modal.textareaEditor.body':              'Champ de texte multiligne avec code JS facultatif sur <code>change</code> (perte de focus). <strong>Entrée</strong> dans la zone de texte est un vrai saut de ligne — ne déclenche PAS le code (diffère de INPUT).',
      'modal.textareaEditor.placeholder':       'Espace réservé :',
      'modal.textareaEditor.defaultValue':      'Valeur par défaut :',
      'modal.textareaEditor.rows':              'Nombre de lignes :',
      'modal.textareaEditor.colorPlaceholder':  'Couleur de l\'espace réservé :',
      'modal.textareaEditor.codeLabel':         'Code JS (à la modification/perte de focus) :',
      'modal.textareaEditor.hint':              'Disponible dans le code : <code>value</code> (chaîne de la zone de texte, peut contenir <code>\\n</code>), <code>$el</code> (HTMLTextAreaElement), <code>$action</code> (id, label).',
      'modal.textareaEditor.close':             'Fermer',

      // === Modal — TOGGLE3 editor (od v2.13.25) ===
      'modal.toggle3Editor.titlePrefix':        'Éditeur TOGGLE3 :',
      'modal.toggle3Editor.body':               'Contrôle segmenté à 3 états. Cliquer sur un état inactif l\'active et exécute le code JS avec le <code>value</code> de l\'état choisi. La mémoire conserve l\'index courant (0-2) par action.',
      'modal.toggle3Editor.statesTitle':        'États (exactement 3, radio = par défaut) :',
      'modal.toggle3Editor.colorBg':            'Couleur de fond :',
      'modal.toggle3Editor.colorHover':         'Couleur au survol :',
      'modal.toggle3Editor.colorInactiveText':  'Couleur du texte inactif :',
      'modal.toggle3Editor.codeLabel':          'Code JS (au changement d\'état) :',
      'modal.toggle3Editor.hint':               'Disponible dans le code : <code>value</code> (state.value, retombe sur state.label), <code>stateIdx</code> (0-2), <code>stateLabel</code>, <code>$el</code> (div conteneur), <code>$action</code>.',
      'modal.toggle3Editor.close':              'Fermer',

      // === Modal — SELECT editor (since v2.12.1, redesign v2.12.2 — variants) ===
      'modal.selectOptions.titlePrefix':        'Éditeur SELECT :',
      'modal.selectOptions.idLabel':            'ID de l\'action :',
      'modal.selectOptions.selectorLabel':      'Sélecteur JS :',
      'modal.selectOptions.copyId':             'Copier l\'ID',
      'modal.selectOptions.copySelector':       'Copier le sélecteur JS',
      'modal.selectOptions.variant.static':     'Options statiques + un seul code JS',
      'modal.selectOptions.variant.js':         'Code JS uniquement (gère le menu déroulant)',
      'modal.selectOptions.static.body':        'Chaque option : <strong>nom</strong> (visible dans le menu) + <strong>valeur</strong> (transmise au code comme variable <code>value</code>). Un code partagé s\'exécute à la modification.',
      'modal.selectOptions.static.hint':        'Disponible dans le code : <code>value</code> (chaîne), <code>$el</code> (élément select), <code>$action</code> (id, label).',
      'modal.selectOptions.staticCodeLabel':    'Code JS (partagé) :',
      'modal.selectOptions.js.body':            'Votre code JS s\'exécute une fois après l\'insertion du menu déroulant dans la page. Il reçoit la référence <code>$el</code> vers un <code>&lt;select&gt;</code> vide — vous remplissez les options et gérez <code>change</code> vous-même.',
      'modal.selectOptions.js.hint':            'Disponible : <code>$el</code> (HTMLSelectElement), <code>$action</code> (id, label).',
      'modal.selectOptions.jsCodeLabel':        'Code JS :',
      'modal.selectOptions.addOption':          '+ Ajouter une option',
      'modal.selectOptions.close':              'Fermer',

      // === Modal — sync conflict ===
      'modal.syncConflict.title':               'Conflit de synchronisation',
      'modal.syncConflict.description':         'Vous vous êtes connecté à un compte qui possède déjà des données dans le cloud, et cet appareil a aussi une bibliothèque locale. Choisissez quoi faire — l\'opération est irréversible localement, mais les données locales peuvent être exportées avant de confirmer.',
      'modal.syncConflict.localLabel':          'Sur cet appareil, en local :',
      'modal.syncConflict.serverLabel':         'Sur le compte cloud :',
      'modal.syncConflict.useServer.title':     'Utiliser les données du compte cloud',
      'modal.syncConflict.useServer.body':      'Les dossiers/groupes/règles locaux seront remplacés par ceux du compte. Choisissez ceci si le compte a des données plus récentes et plus exactes que cet appareil.',
      'modal.syncConflict.sendMine.title':      'Envoyer mes données locales vers le compte',
      'modal.syncConflict.sendMine.body':       'L\'état du compte sera remplacé par ce que vous avez sur cet appareil. Choisissez ceci si cet appareil a des données plus récentes et plus exactes.',
      'modal.syncConflict.merge.title':         'Combiner les deux ensembles',
      'modal.syncConflict.merge.body':          'Les entités locales et cloud seront conservées ensemble. Si le même élément existe aux deux endroits (même ID), la version du compte l\'emporte. Les éléments uniquement locaux seront envoyés au compte à la prochaine synchro.',
      'modal.syncConflict.exportLocal':         'Exporter le local d\'abord',
      'modal.syncConflict.cancel':              'Annuler la connexion (se déconnecter)',

      // === Modal — share import ===
      'modal.shareImport.title':                'Importer depuis un lien JustZix',
      'modal.shareImport.body':                 'Collez le lien complet ou seulement le jeton <strong>JZS-XXXX-XXXX-XXXX-XXXX</strong> :',
      'modal.shareImport.placeholder':          'https://www.justzix.com/s/JZS-XXXX-XXXX-XXXX-XXXX',
      'modal.shareImport.cancel':               'Annuler',
      'modal.shareImport.fetch':                'Récupérer',
      'modal.shareImport.preview.title':        'Aperçu de l\'export partagé',
      'modal.shareImport.jsWarn':               '<strong>⚠ AVERTISSEMENT :</strong> L\'export contient du code JavaScript. Le code s\'exécutera sur les pages correspondant aux règles — vérifiez ce qu\'il fait avant d\'importer.',
      'modal.shareImport.showCode':             'Afficher le code JavaScript',
      'modal.shareImport.back':                 '← Retour',
      'modal.shareImport.confirm':              'Importer',

      // === Modal — my shares ===
      'modal.shares.title':                     'Mes liens partagés',
      'modal.shares.body':                      'Liens publics à durée limitée vers des exports. Le backend conserve les données jusqu\'à expiration, puis les supprime.',
      'modal.shares.howTo':                     'Pour créer un nouveau lien — ouvrez un export (complet / dossier / groupe / jeu de règles) et cliquez sur <strong>« Partager l\'URL »</strong>.',
      'modal.shares.refresh':                   'Actualiser',
      'modal.shares.close':                     'Fermer',

      // === Modal — share create ===
      'modal.shareCreate.title':                'Partager l\'export via un lien',
      'modal.shareCreate.activeFor':            'Le lien sera actif pendant :',
      'modal.shareCreate.ttl.1h':               '1 heure',
      'modal.shareCreate.ttl.6h':               '6 heures',
      'modal.shareCreate.ttl.12h':              '12 heures',
      'modal.shareCreate.ttl.24h':              '24 heures',
      'modal.shareCreate.ttl.default':          '(par défaut)',
      'modal.shareCreate.ttl.48h':              '48 heures',
      'modal.shareCreate.expireNote':           'Après expiration, le lien cessera de fonctionner. Vous pouvez le révoquer plus tôt depuis « Mes partages ».',
      'modal.shareCreate.cancel':               'Annuler',
      'modal.shareCreate.generate':             'Générer le lien',
      'modal.shareCreate.result.title':         'Lien généré',
      'modal.shareCreate.result.urlLabel':      'Envoyez ce lien au destinataire :',
      'modal.shareCreate.result.copyUrl':       'Copier le lien',
      'modal.shareCreate.result.tokenLabel':    'Ou seulement le jeton (à coller dans « Importer depuis une URL ») :',
      'modal.shareCreate.result.copyToken':     'Copier le jeton',
      'modal.shareCreate.result.goToMine':      'Voir « Mes partages »',
      'modal.shareCreate.result.close':         'Fermer',

      // === Modal — sync result ===
      'modal.syncResult.title':                 'Résultat de la synchronisation',
      'modal.syncResult.close':                 'OK, compris',

      // === Modal — cloud sync ===
      'modal.sync.title':                       'Synchronisation cloud',
      'modal.sync.signedOut.tab.register':      'Premier appareil',
      'modal.sync.signedOut.tab.login':         'J\'ai une clé',
      'modal.sync.signedOut.tab.recover':       'J\'ai perdu ma clé',
      'modal.sync.signedOut.register.body':     'Générez une nouvelle clé de synchro. <strong>Conservez-la en lieu sûr</strong> — le backend ne stocke qu\'un hachage. La clé permet de se connecter sur un deuxième appareil et de voir la même bibliothèque.',
      'modal.sync.signedOut.deviceLabel':       'Libellé de l\'appareil (facultatif) :',
      'modal.sync.signedOut.deviceLabel.placeholder1': 'ex. PC du bureau',
      'modal.sync.signedOut.deviceLabel.placeholder2': 'ex. Portable',
      'modal.sync.signedOut.yourKey':           'Votre clé de synchro :',
      'modal.sync.signedOut.regenerate':        'En générer une nouvelle',
      'modal.sync.signedOut.copyClipboard':     'Copier dans le presse-papiers',
      'modal.sync.signedOut.warnSave':          '⚠ <strong>Copiez et enregistrez la clé maintenant.</strong> La clé sera aussi disponible dans l\'onglet « Clé » après l\'enregistrement — mais il vaut mieux en garder une copie dans un gestionnaire de mots de passe.',
      'modal.sync.signedOut.login.body':        'Collez la clé que vous avez vue lors de l\'enregistrement du premier appareil. Format : <code>SYNC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX</code>.',
      'modal.sync.signedOut.keyLabel':          'Clé de synchro :',
      'modal.sync.signedOut.recover.body':      'Si une adresse e-mail <strong>vérifiée</strong> a été définie sur le compte — saisissez-la et envoyez un lien de récupération. Après avoir cliqué sur le lien dans l\'e-mail, vous recevrez une <strong>nouvelle clé</strong> (à usage unique). Toutes les sessions actives seront déconnectées.',
      'modal.sync.signedOut.email':             'E-mail du compte :',
      'modal.sync.signedOut.email.placeholder': 'quelquun@example.com',
      'modal.sync.signedOut.cancel':            'Annuler',
      'modal.sync.signedOut.regSubmit':         'Enregistrer l\'appareil',
      'modal.sync.signedOut.loginSubmit':       'Se connecter',
      'modal.sync.signedOut.recoverSubmit':     'Envoyer le lien de récupération',
      'modal.sync.signedIn.tab.status':         'Statut',
      'modal.sync.signedIn.tab.key':            'Clé d\'accès',
      'modal.sync.signedIn.tab.backups':        'Sauvegardes',
      'modal.sync.signedIn.tab.email':          'E-mail et récupération',
      'modal.sync.signedIn.status.account':     'Compte',
      'modal.sync.signedIn.status.pending':     'Modifications en attente :',
      'modal.sync.signedIn.status.cloudLib':    'Bibliothèque cloud',
      'modal.sync.signedIn.status.lastCursor':  'Dernier pull (curseur) :',
      'modal.sync.signedIn.status.syncNow':     'Synchroniser maintenant',
      'modal.sync.signedIn.status.snapshot':    'Snapshot complet',
      'modal.sync.signedIn.status.refresh':     'Actualiser les données du compte',
      'modal.sync.signedIn.status.reconcile':   'Resynchronisation complète avec le compte',
      'modal.sync.signedIn.status.sessions':    'Sessions actives (appareils)',
      'modal.sync.signedIn.status.history':     'Historique de synchronisation',
      'modal.sync.history.empty':               'Aucune synchronisation enregistrée.',
      'modal.sync.history.viewAll':             'Voir l\'historique complet',
      'modal.sync.history.changes':             'Modifications :',
      'modal.sync.history.device':              'Appareil :',
      'modal.sync.history.fromDevices':         'Récupéré de :',
      'modal.sync.history.stateAfter':          'État après synchronisation :',
      'modal.sync.history.noDetail':            'Aucun détail de modification (entrée ancienne).',
      'modal.sync.signedIn.key.body':           'Clé de synchro pour ce compte. Saisissez-la sur un deuxième appareil pour synchroniser la bibliothèque.',
      'modal.sync.signedIn.key.yourKey':        'Votre clé :',
      'modal.sync.signedIn.key.reveal':         'Afficher la clé',
      'modal.sync.signedIn.key.copy':           'Copier dans le presse-papiers',
      'modal.sync.signedIn.key.forget':         'Retirer la clé de cet appareil',
      'modal.sync.signedIn.key.warn':           '⚠ La clé donne un accès complet à la bibliothèque. Ne l\'affichez pas publiquement. Si vous ne souhaitez pas la conserver localement — cliquez sur <strong>Retirer la clé de cet appareil</strong> (gardez une copie dans un gestionnaire de mots de passe). La connexion fonctionne toujours, mais la clé ne pourra plus être affichée.',
      'modal.sync.signedIn.key.noKey':          'La clé n\'est pas stockée sur cet appareil (connexion avant l\'existence de cette fonction, ou retirée manuellement). Vous ne pouvez la récupérer que par e-mail de récupération ou si vous en avez une copie ailleurs.',
      'modal.sync.signedIn.backups.hint':       'Sauvegardes cloud — la restauration remplace toute la bibliothèque.',
      'modal.sync.signedIn.backups.label':      'Libellé de la nouvelle sauvegarde (facultatif) :',
      'modal.sync.signedIn.backups.placeholder':'ex. Avant un gros changement',
      'modal.sync.signedIn.backups.create':     'Créer une sauvegarde maintenant',
      'modal.sync.signedIn.backups.refresh':    'Actualiser la liste',
      'modal.sync.signedIn.email.body':         'L\'e-mail permet la <strong>récupération</strong> — si vous perdez la clé, vous pouvez en obtenir une nouvelle en cliquant sur un lien envoyé à cette adresse. Ajouter un e-mail nécessite une vérification (lien dans le courriel).',
      'modal.sync.signedIn.email.add':          'Ajouter un e-mail (récupération) :',
      'modal.sync.signedIn.email.placeholder':  'quelquun@example.com',
      'modal.sync.signedIn.email.submit':       'Envoyer le lien de vérification',
      'modal.sync.signedIn.email.remove':       'Retirer l\'e-mail',
      'modal.sync.signedIn.email.recoveryFlow': 'Procédure de récupération : sur un autre appareil (ou sans clé), sélectionnez l\'onglet <strong>« J\'ai perdu ma clé »</strong> dans le panneau de connexion, saisissez cet e-mail — le backend enverra un lien qui génère une nouvelle clé et invalide toutes les sessions actives.',
      'modal.sync.signedIn.logout':             'Déconnecter cet appareil',
      'modal.sync.signedIn.close':              'Fermer',

      // === Content script (floating button + actions panel) ===
      'content.float.titleGloballyOff':         'Extension désactivée globalement — cliquez sur l\'icône de l\'extension pour l\'activer',
      'content.float.titleNormal':              'Clic gauche : activer/désactiver le dossier • Clic droit : afficher la liste des jeux de règles • Glisser pour déplacer',
      'content.panel.empty':                    'Aucun jeu de règles actif',
      'content.panel.actionBarsTitle':          'Barres d\'actions',
      'content.panel.connectionsTitle':         'Connexions',
      'content.actionsPanel.dragHandle':        'Glisser pour déplacer',
      'content.editMode.banner':                'Mode édition de la barre — clic/Ctrl+clic sélectionne, glisser déplace · Ctrl+Z annule · Ctrl+Alt+S pour quitter',
      'content.editMode.undoEmpty':             'Rien à annuler',
      'content.editMode.addLabel':              '+ Label',
      'content.editMode.resetBar':              'Réinitialiser la barre',
      'content.editMode.resetConfirm':          'Réinitialiser la barre à la disposition par défaut ? Les positions, tailles et libellés personnalisés seront supprimés.',
      'content.labelEditor.text':               'Texte :',
      'content.labelEditor.font':               'Police :',
      'content.labelEditor.size':               'Taille :',
      'content.labelEditor.color':              'Couleur :',
      'content.labelEditor.delete':             'Supprimer',
      'content.labelEditor.close':              'Fermer',
      'content.labelContextMenu.delete':        'Supprimer le libellé',
      'content.bar.hideUntilReload':            'Fermer cette barre (jusqu\'au rechargement)',
      'content.bar.disconnectFrom':             'Déconnecter de : :name',
      'content.bar.disconnectAll':              'Déconnecter toutes les connexions',
      'content.bar.fallbackName':               'Barre',
      'content.float.label':                    '⊕ Bouton principal',
      'content.toast.noFoldersForPage':         'Aucun dossier ne correspond à cette page',
      'content.toast.floatVisible':             'Bouton flottant activé',
      'content.toast.floatHidden':              'Bouton flottant masqué',

      // === Background (context menu + JS error logger) ===
      'background.contextMenu.toggleFloat':     'Activer/désactiver le bouton flottant pour cette page',
      'background.contextMenu.editMode':        'Mode édition de la barre d\'actions (activer/désactiver)',
      'background.contextMenu.actionBars':      'Barres d\'actions',
      'background.contextMenu.actionBarsEmpty': '(aucune barre pour cette page)',
      'background.contextMenu.actionBarFallback': '(sans nom)',
      'background.contextMenu.cssPanes':         'Panneaux CSS',
      'background.contextMenu.cssPanesEmpty':    '(aucun panneau CSS pour cette page)',
      'background.contextMenu.cssPaneFallback':  '(sans nom)',
      'background.contextMenu.jsPanes':          'Panneaux JS',
      'background.contextMenu.jsPanesEmpty':     '(aucun panneau JS pour cette page)',
      'background.contextMenu.jsPaneFallback':   '(sans nom)',
      'background.contextMenu.jsConsoles':       'Consoles JS',
      'background.contextMenu.jsConsolesEmpty':  '(aucune console pour cette page)',
      'background.contextMenu.jsConsoleFallback':'(sans nom)',
      'background.contextMenu.outputConsoles':       'Consoles de sortie',
      'background.contextMenu.outputConsolesEmpty':  '(aucune console de sortie pour cette page)',
      'background.contextMenu.outputConsoleFallback':'(sans nom)',
      'background.contextMenu.tempPanes':            'Fenêtre temporaire (TEMP)',
      'background.contextMenu.tempCss':              'Panneau CSS TEMP (Ctrl+Alt+G)',
      'background.contextMenu.tempJs':               'Panneau JS TEMP (Ctrl+Alt+H)',
      'background.contextMenu.tempJsConsole':        'Console JS TEMP (Ctrl+Alt+J)',
      'background.contextMenu.tempOutput':           'Console de sortie TEMP (Ctrl+Alt+K)',
      'pane.collapse.title':                      'Réduire la fenêtre à la barre de titre',
      'pane.expand.title':                        'Agrandir la fenêtre',
      'aiHelper.form.submit':                     'OK',
      'aiHelper.form.cancel':                     'Annuler',
      'aiHelper.form.yes':                        'Oui',
      'aiHelper.form.no':                         'Non',
      'aiHelper.form.askTitle':                   'Question',
      'aiHelper.form.confirmTitle':               'Confirmation',
      'aiHelper.form.createConfirmTitle':         'Créer ?',
      'aiHelper.form.confirmed':                  'confirmé',
      'aiHelper.form.cancelled':                  'annulé',
      'tempPane.close.title':                        'Fermer la fenêtre temporaire (contenu conservé pour la session)',
      'tempPane.clear.title':                        'Effacer le contenu de la fenêtre',
      'background.jsError.prefix':              '[JustZix] Erreur JS dans le jeu de règles/l\'action ":id" :\n',
      // === AI Helper (od v2.13.127) ===
      'settings.aiHelper.label': 'AI Helper',
      'settings.aiHelper.title': 'Configurer les clés API pour AI Helper',
      'modal.aiHelper.title': 'AI Helper',
      'modal.aiHelper.intro': 'Saisis les clés API des fournisseurs que tu veux utiliser. Une seule suffit.',
      'modal.aiHelper.keyOpenai': 'Clé API — OpenAI',
      'modal.aiHelper.keyAnthropic': 'Clé API — Anthropic',
      'modal.aiHelper.keyGemini': 'Clé API — Gemini',
      'modal.aiHelper.defaultProvider': 'Fournisseur par défaut',
      'modal.aiHelper.defaultModel': 'Modèle par défaut',
      'modal.aiHelper.modelAuto': '— auto (le plus élevé) —',
      'modal.aiHelper.loadModels': 'Charger les modèles',
      'modal.aiHelper.securityNote': '<strong>Sécurité :</strong> par défaut, les clés API sont stockées uniquement dans le stockage local de ce navigateur (<code>chrome.storage.local</code>). Cocher « synchro » à côté d’une clé envoie cette clé via le cloud JustZix (celui que l’extension utilise pour synchroniser le reste de ses données) vers vos autres appareils. Les clés non cochées ne quittent jamais cet appareil.',
      'modal.aiHelper.syncKey':                'synchro',
      'modal.aiHelper.syncKeyTitle': 'Synchroniser cette clé entre vos appareils via le cloud JustZix',
      'modal.aiHelper.cancel': 'Annuler',
      'modal.aiHelper.save': 'Enregistrer',
      'modal.aiHelper.needKey': 'Saisis d’abord une clé API.',
      'modal.aiHelper.loadingModels': 'Chargement des modèles…',
      'modal.aiHelper.errModels': 'Échec du chargement des modèles',
      'modal.aiHelper.modelsLoaded': 'modèles disponibles',
      'aiHelper.win.title': 'AI Helper',
      'aiHelper.win.close': 'Fermer',
      'aiHelper.win.provider': 'Fournisseur',
      'aiHelper.win.model': 'Modèle',
      'aiHelper.win.clear': 'Effacer la conversation',
      'aiHelper.win.clearConfirm': 'Effacer toute la conversation et recommencer ?',
      'aiHelper.win.inputPlaceholder': 'Demande de l’aide pour du CSS, du JS ou une action…',
      'aiHelper.win.send': 'Envoyer (Ctrl+Entrée)',
      'aiHelper.win.welcome': 'Bonjour ! Je vais t’aider à préparer du CSS, du JavaScript ou des actions pour cette page. Décris ce que tu veux obtenir.',
      'aiHelper.win.you': 'Toi',
      'aiHelper.win.assistant': 'AI Helper',
      'aiHelper.win.thinking': 'Réflexion…',
      'aiHelper.win.error': 'Erreur de connexion à l’API',
      'aiHelper.win.noKeys': 'Aucune clé API. Ajoute une clé dans Paramètres → AI Helper.',
      'aiHelper.win.noModel': 'Sélectionne un modèle.',
      'aiHelper.win.loadingModels': 'Chargement…',
      'aiHelper.win.modelsError': 'Erreur de chargement des modèles',
      'background.contextMenu.aiHelper': 'AI Helper',
      'popup.aiHelper.label': 'AI Helper',
      'popup.aiHelper.on': 'Ouvert — cliquer pour fermer (Ctrl+Alt+\\)',
      'popup.aiHelper.off': 'Fermé — cliquer pour ouvrir (Ctrl+Alt+\\)',
      'popup.aiHelper.unavailable': 'Indisponible sur cette page',
      // === AI Helper etap 2 (od v2.13.128) ===
      'aiHelper.win.maxRounds': 'Limite d’étapes d’outils atteinte pour ce tour. Dis-moi comment continuer.',
      'aiHelper.win.copy':                        'Copier la conversation',
      'aiHelper.win.copy.all':                    'Copier la conversation entière',
      'aiHelper.win.copy.lastInput':              'Copier le dernier prompt API (système + historique + entrée)',
      'aiHelper.win.copyNoInput':                 'Rien à copier',
      'aiHelper.copy.lastInput.prefix':           '[JustZix → transférer à l\'API] L\'ENTIER dernier prompt envoyé au modèle a été copié (système + historique + dernier message). Envoie le bloc ci-dessous à l\'API comme PREMIER message user d\'une nouvelle conversation. SUPPRIME cet en-tête avant l\'envoi.',
      'aiHelper.win.copied':                      'Copié',
      'aiHelper.win.copyFailed':                  'Échec de la copie — vérifie les autorisations du presse-papiers.',
      'aiHelper.win.font':                        'Police',
      'aiHelper.win.fontSize':                    'Taille',
      'aiHelper.win.defaultFont':                 'Par défaut',
      'aiHelper.win.stop':                        'Arrêter l’agent',
      'aiHelper.win.stopped':                     'Interrompu par l’utilisateur.',
      'aiHelper.win.ctxUsage':                    'Utilisation du contexte du modèle',
      'aiHelper.win.ctxUsageDetail':              'Contexte',
      'aiHelper.win.compact':                     'Compacter la conversation (résumé LLM)',
      'aiHelper.win.compactNoop':                 'Historique trop court — rien à compacter.',
      'aiHelper.win.compactNoProvider':           'Sélectionnez d’abord un fournisseur et un modèle.',
      'aiHelper.win.compactErr':                  'Erreur de compactage',
      'aiHelper.win.compactEmpty':                'Le modèle a renvoyé un résultat vide.',
      'aiHelper.win.compactedPrefix':             'Résumé de',
      'aiHelper.win.compactedSuffix':             'messages précédents :',
      'aiHelper.win.telemetryTooltip':            'Temps et jetons de cette session',
      'aiHelper.win.telemetryTimers':             'Temps : depuis le dernier message (u) / depuis le dernier outil (t) / activité totale de l’agent (a)',
      'aiHelper.win.telemetryCurrent':            'Jetons de l’action en cours (↑ entrée / ↓ sortie)',
      'aiHelper.win.telemetrySession':            'Total des jetons de cette session',
      'aiHelper.win.msgTelem':                    'Jetons de ce tour et temps de réponse du modèle',
      'aiHelper.win.toolDur':                     'Temps d’exécution de l’outil',
      'aiHelper.win.stepIdBadge':                 'Numéro d’étape de l’historique',
      'aiHelper.form.freeText':                   'Ou répondre par texte',
      'aiHelper.form.freeTextOnly':               'Votre réponse',
      'aiHelper.form.freeTextPlaceholder':        'Saisissez votre réponse…',
      'aiHelper.win.turnLimit.body':              'Limite de 10 étapes d’outils atteinte pour ce tour. L’agent a fait une pause — vérifie le dernier outil et son résultat. Continuer ici avec la limite réinitialisée, ou arrêter ?',
      'aiHelper.win.turnLimit.lastTool':          'Dernier outil',
      'aiHelper.win.turnLimit.lastResult':        'Résultat',
      'aiHelper.win.turnLimit.continue':          'Continuer (réinitialiser la limite)',
      'aiHelper.win.turnLimit.stop':              'Arrêter',
      'aiHelper.win.turnLimit.stoppedByUser':     'Arrêté.',
      'aiHelper.copy.title':                      'Conversation JustZix AI Helper',
      'aiHelper.copy.toolResult':                 'Résultat de l’outil',
      'aiHelper.tool.label': 'outil',
      'aiHelper.tool.rejected': 'L’utilisateur a refusé l’opération.',
      'aiHelper.confirm.folder': 'AI Helper veut créer un DOSSIER :',
      'aiHelper.confirm.group': 'AI Helper veut créer un GROUPE :',
      'aiHelper.confirm.rule': 'AI Helper veut créer un JEU DE RÈGLES :',
      'aiHelper.confirm.create.cssPane': 'AI Helper veut créer une FENÊTRE CSS :',
      'aiHelper.confirm.create.jsPane': 'AI Helper veut créer une FENÊTRE JS :',
      'aiHelper.confirm.create.jsConsole': 'AI Helper veut créer une CONSOLE JS :',
      'aiHelper.confirm.create.outputConsole': 'AI Helper veut créer une CONSOLE DE SORTIE :',
      'aiHelper.confirm.setPaneCode': 'AI Helper veut insérer du code dans la fenêtre « :name » (:id).\nLongueur : actuellement :old car., nouveau :new car.\nNOTE : le code des fenêtres persistantes vit par onglet (sessionStorage), pas entre les sessions.',
      'aiHelper.confirm.bar': 'AI Helper veut créer une BARRE D\'ACTIONS :',
      'aiHelper.confirm.action': 'AI Helper veut créer une ACTION :',
      // === Wyszukiwanie w kodzie (od v2.13.130) ===
      'codeSearch.placeholder': 'Rechercher dans le code… (Ctrl+Alt+F)',
      'codeSearch.btn': 'Rechercher',
      'codeSearch.prev': 'Résultat précédent (Ctrl+Alt+←)',
      'codeSearch.next': 'Résultat suivant (Ctrl+Alt+→)',
      'codeSearch.noMatch': 'Aucun résultat',
    },

    it: {
      'dashboard.title': 'Cruscotto',
      'dashboard.subtitle': 'Una panoramica della tua libreria JustZix e scorciatoie rapide.',
      'dashboard.stats.title': 'Libreria',
      'dashboard.stats.folders': 'Cartelle',
      'dashboard.stats.groups': 'Gruppi',
      'dashboard.stats.rules': 'Set di regole',
      'dashboard.stats.actions': 'Azioni',
      'dashboard.stats.bars': 'Barre delle azioni',
      'dashboard.stats.windows': 'Finestre nella pagina',
      'dashboard.stats.withCss': 'Set con CSS',
      'dashboard.stats.withJs': 'Set con JS',
      'dashboard.stats.clicks': 'Clic sulle azioni',
      'dashboard.version.title': 'Versione dell\'estensione',
      'dashboard.version.checking': 'Verifica in corso…',
      'dashboard.version.current': 'Installata: :version',
      'dashboard.version.upToDate': 'Sei aggiornato',
      'dashboard.version.outdated': 'È disponibile una versione più recente: :version',
      'dashboard.storage.title': 'Archiviazione',
      'dashboard.backup.title': 'Ultimo backup',
      'dashboard.backup.signIn': 'Accedi alla sincronizzazione cloud per creare backup nel cloud.',
      'dashboard.backup.none': 'Nessun backup nel cloud finora.',
      'dashboard.backup.noLabel': '(senza etichetta)',
      'dashboard.backup.last': 'Ultimo backup: :date',
      'dashboard.recent.title': 'Set di regole modificati di recente',
      'dashboard.recent.empty': 'Nessun set di regole è stato ancora modificato.',
      'dashboard.examples.title': 'Ultimi esempi',
      'dashboard.posts.title': 'Dal blog',
      'dashboard.feed.loading': 'Caricamento…',
      'dashboard.feed.empty': 'Nessun elemento da mostrare.',
      'dashboard.home.title': 'Pannello',
      'dashboard.usage.title': 'Statistiche di utilizzo',
      'dashboard.usage.cssApplied': 'CSS applicati',
      'dashboard.usage.jsExecuted': 'JS eseguiti',
      'dashboard.usage.windowsShown': 'Finestre mostrate',
      'dashboard.usage.empty': 'Nessun dato — usa l\'estensione sulle pagine web.',
      'dashboard.tip.title': 'Suggerimento',
      'dashboard.tip.prev': 'Suggerimento precedente',
      'dashboard.tip.next': 'Suggerimento successivo',
      'dashboard.tip.manual': 'Apri il manuale completo',
      'tip.dash.1': 'Fai clic con il tasto destro sul pulsante fluttuante per aprire il pannello con l\'elenco delle cartelle e dei set di regole.',
      'tip.dash.2': 'Ctrl+Alt+S attiva o disattiva la modalità modifica della barra delle azioni — gli elementi possono essere posizionati e ridimensionati liberamente.',
      'tip.dash.3': 'Ogni set di regole può avere i propri pattern URL che lo restringono a sottopagine specifiche.',
      'tip.dash.4': 'Ctrl+Alt+\\ apre l\'AI Helper — un assistente che genera CSS/JS per un compito specifico.',
      'tip.dash.5': 'Gli editor di codice hanno una barra di ricerca — Ctrl+Alt+F sposta il focus su di essa.',
      'tip.dash.6': 'Facendo clic con il tasto sinistro sul pulsante fluttuante attivi o disattivi tutte le cartelle corrispondenti contemporaneamente.',
      'tip.dash.7': 'Un set di regole conserva nella cronologia le ultime 5 versioni di CSS e JS — puoi sempre annullare una modifica.',
      'tip.dash.8': 'Trascina un set di regole sul nome di un altro gruppo o cartella per spostarlo lì.',
      'tip.dash.9': 'Le finestre TEMP (Ctrl+Alt+G/H/J/K) sono pannelli CSS/JS e console usa e getta per prove rapide.',
      'tip.dash.10': 'Le azioni SELECT, INPUT, SLIDER e TOGGLE3 ricordano il loro valore — sopravvive al ricaricamento della pagina.',
      'tip.dash.11': 'La sincronizzazione cloud mantiene la tua libreria coerente su tutti i dispositivi.',
      'tip.dash.12': 'Invece di inviare un file JSON, condividi una cartella tramite un link (Share) con una scadenza.',
      'tip.dash.13': 'La barra di ricerca nella barra laterale trova cartelle, gruppi e set di regole per nome.',
      'tip.dash.14': 'Aggiungi tag ai set di regole e filtra la libreria per tag man mano che i progetti crescono.',
      'tip.dash.15': 'Un\'etichetta di tre lettere e un colore fanno risaltare il pulsante flottante di una cartella.',
      'tip.dash.16': 'Il pulsante "URL" inserisce l\'indirizzo della scheda attiva direttamente in un pattern URL.',
      'tip.dash.17': 'JustZix verifica al volo la validità dei pattern URL che digiti.',
      'tip.dash.18': 'L\'editor segnala gli errori di sintassi CSS prima che tu salvi un set di regole.',
      'tip.dash.19': 'Durante l\'importazione puoi vedere in anteprima il codice JavaScript prima di aggiungerlo.',
      'tip.dash.20': 'Disattivare una cartella spegne a cascata tutti i suoi gruppi e set di regole.',
      'tip.dash.21': 'Il filtro "Solo attivi" nasconde gli elementi disattivati quando la libreria diventa grande.',
      'tip.dash.22': 'Trascina una barra delle azioni accanto a un\'altra per agganciarle in un unico blocco.',
      'tip.dash.23': 'La finestra Console JS funziona come una REPL: prova frammenti di codice dal vivo sulla pagina.',
      'tip.dash.24': 'La finestra Output Console cattura le voci console.log della pagina e le mostra a schermo.',
      'tip.dash.25': 'Puoi esportare una singola cartella, un gruppo o un set di regole, non solo l\'intera libreria.',
      'tip.dash.26': 'JustZix aggiunge voci proprie al menu contestuale del browser (clic destro).',
      'tip.dash.27': 'Trascina con il mouse il divisorio tra i pannelli dell\'editor di codice per adattare la vista.',
      'tip.dash.28': 'L\'estensione ti avvisa da sola quando è disponibile una versione più recente.',
      'tip.dash.29': 'Un clic sull\'icona dell\'estensione apre un widget per attivare rapidamente cartelle e barre.',
      'tip.dash.30': 'Il JavaScript di un set di regole può attivare le azioni delle barre tramite l\'API di JustZix.',
      'tip.dash.31': 'Ctrl+Invio esegue il codice o invia il messaggio — nel pannello JS, nella Console JS e nell\'AI Helper.',
      'tip.dash.32': 'Nella Console JS le frecce ↑ / ↓ scorrono la cronologia dei comandi digitati.',
      'tip.dash.33': 'Ctrl+L svuota l\'output della Console JS senza chiudere la finestra.',
      'tip.dash.34': 'Ctrl+Shift+L svuota la scheda attiva di ogni Console Output aperta.',
      'tip.dash.35': 'Ctrl+Shift+H apre la finestra di aiuto dell\'API nella Console Output.',
      'tip.dash.36': 'Nell\'editor di codice Ctrl+Alt+→ e Ctrl+Alt+← saltano al risultato di ricerca successivo e precedente.',
      'tip.dash.37': 'Il tasto Esc svuota il campo di ricerca nella Console Output.',
      'tip.dash.38': 'Un doppio clic sull\'intestazione di una finestra TEMP la commuta tra la dimensione predefinita e l\'ultima.',
      'tip.dash.39': 'Una finestra TEMP ricorda l\'ultima dimensione per tipo e la mantiene dopo il riavvio del browser.',
      'tip.dash.40': 'Lo scorrimento con la rotellina dentro una finestra non scorre più la pagina sottostante.',
      'tip.dash.41': 'Ogni finestra ha 8 maniglie di ridimensionamento — 4 negli angoli e 4 sui bordi.',
      'tip.dash.42': 'Un clic destro sull\'intestazione di una finestra apre un menu con chiudi, scollega, font e dimensione.',
      'tip.dash.43': 'Trascina una finestra vicino a un\'altra — si agganciano magneticamente in un unico gruppo.',
      'tip.dash.44': 'La Console Output ha 6 schede: Tutto, JZ, Console, Network, Errors e DataLayer.',
      'tip.dash.45': 'Nella scheda Network, Alt+clic su una riga aggiunge il suo dominio al filtro di inclusione.',
      'tip.dash.46': 'Alt+Shift+clic su una riga Network aggiunge il dominio al filtro di esclusione.',
      'tip.dash.47': 'Ogni scheda della Console Output mantiene il proprio set di filtri indipendente.',
      'tip.dash.48': 'Nella scheda DataLayer trascina la barra tra le colonne per ridimensionarle.',
      'tip.dash.49': 'In modalità modifica barra il pulsante „+ Etichetta" aggiunge etichette di testo statiche.',
      'tip.dash.50': 'Doppio clic su un\'etichetta della barra apre l\'editor di testo, font, dimensione e colore.',
      'tip.dash.51': 'In modalità modifica, Shift+trascinamento su un\'area vuota seleziona gli elementi con un rettangolo.',
      'tip.dash.52': 'In modalità modifica, Ctrl+clic aggiunge o rimuove un elemento dalla selezione.',
      'tip.dash.53': 'Clicca sul badge numerico di un elemento azione per copiarne l\'ID stabile negli appunti.',
      'tip.dash.54': 'Un set di regole può rieseguire il suo JS ai cambiamenti dell\'interfaccia su pagine di tipo SPA.',
      'tip.dash.55': 'Oltre a BUTTON, SELECT e INPUT, le azioni hanno anche i tipi SLIDER, TEXTAREA e TOGGLE3.',
      'tip.dash.56': 'Il tema chiaro/scuro e la lingua dell\'interfaccia si sincronizzano come impostazione condivisa dell\'account.',
      'tip.dash.57': 'Le statistiche d\'uso nella Dashboard sommano i dati di tutti i dispositivi accoppiati.',
      'tip.dash.58': 'La barra sotto l\'editor di codice può essere trascinata verso il basso per ingrandire l\'area di lavoro.',
      'tip.dash.59': 'La barra verticale tra la sidebar e l\'editor comprime il pannello sinistro per avere più spazio.',
      'tip.dash.60': 'L\'icona casa nell\'intestazione della Dashboard e nel breadcrumb riporta direttamente alla Dashboard.',
      // === Popup ===
      'popup.toggle.on.label':       'Estensione attivata',
      'popup.toggle.on.status':      'Iniezione attiva',
      'popup.toggle.off.label':      'Estensione disattivata',
      'popup.toggle.off.status':     'Nessun CSS/JS viene iniettato',
      'popup.editMode.label':        'Modalità modifica della barra delle azioni',
      'popup.editMode.on':           'Attiva — clicca per disattivare (Ctrl+Alt+S)',
      'popup.editMode.off':          'Disattivata — clicca per attivare (Ctrl+Alt+S)',
      'popup.editMode.unavailable':  'Non disponibile su questa pagina',
      'popup.empty.title':           'Nessuna cartella di regole corrisponde a questa pagina.',
      'popup.empty.hint':            'Aggiungile in modalità modifica.',
      'popup.empty.folderRules':     'Nessuna regola attiva in questa cartella',
      'popup.fallback.groupName':    'Gruppo',
      'popup.fallback.unnamed':      '(senza nome)',
      'popup.restore':               'Ripristina il pulsante fluttuante (:count)',
      'popup.editBtn':               'Pannello di modifica',
      'popup.actionBarsTitle':       'Barre delle azioni',
      'popup.fallback.barName':      'Barra',
      'popup.hideForPage':           'Nascondi il pulsante fluttuante per questa pagina',
      'popup.showForPage':           'Mostra il pulsante fluttuante per questa pagina',

      // === Options — page ===
      'options.title':                          'JustZix — Editor',

      // === Options — sidebar ===
      'sidebar.folders.title':                  'Cartelle',
      'sidebar.addFolder.label':                'Aggiungi cartella',
      'sidebar.search.placeholder':             'Cerca (nome, tag, contenuto del codice)',
      'sidebar.clearSearch.label':              'Cancella',
      'sidebar.activeOnly.label':               'Solo attivi',
      'sidebar.activeOnly.title':               'Nascondi i set di regole indipendenti (contrassegnati con ○) — mostra solo le regole attive nella loro cartella (●)',
      'sidebar.expandAll.label':                'Espandi tutto l\'albero',
      'sidebar.collapseAll.label':              'Comprimi tutto l\'albero',
      'sidebar.toggle.title':                   'Comprimi/espandi il pannello delle cartelle',

      // === Options — settings menu ===
      'settings.menu.label':                    'Impostazioni',
      'settings.theme.label':                   'Tema',
      'settings.theme.title':                   'Scegli un tema: chiaro / scuro / auto',
      'settings.help.label':                    'Manuale utente',
      'settings.tutorial.label':                'Tutorial',
      'settings.export.label':                  'Esporta impostazioni',
      'settings.import.label':                  'Importa impostazioni',
      'settings.importUrl.label':               'Importa da URL',
      'settings.importUrl.title':               'Importa un export da un link JustZix (JZS-XXXX-XXXX-XXXX-XXXX)',
      'settings.sync.label':                    'Sincronizzazione cloud',
      'settings.shares.label':                  'Le mie condivisioni',
      'settings.shares.title':                  'Elenco dei link pubblici a tempo limitato emessi verso gli export',
      'settings.projectSite.label':             'Sito del progetto',
      'settings.projectSite.title':             'Apri justzix.com in una nuova scheda',
      'settings.lang.label':                    'Lingua',
      'settings.lang.title':                    'Lingua dell\'interfaccia dell\'estensione',
      'settings.lang.auto':                     'Auto',
      'settings.lang.autoTitle':                'Rileva dal browser (predefinito)',
      'toast.langChanged':                      'Lingua impostata: :lang',
      'lang.name.auto':                         'Auto',
      'lang.name.pl':                           'Polacco',
      'lang.name.en':                           'Inglese',
      'settings.version.label':                 'Versione',
      'settings.version.checkTitle':            'Clicca per verificare l\'ultima versione (aggiornamento forzato, ignora la cache di 30 min)',
      'settings.version.checking':              'Verifica in corso…',
      'settings.update.available':              'Nuova versione disponibile: v:version ⬇',
      'settings.update.notReady':               '(pacchetto non ancora pubblicato)',
      'settings.syncStatusDot.title':           'Sincronizzazione cloud — non connesso',

      // === Options — folder editor ===
      'folder.icon.title':                      'Etichetta del pulsante fluttuante (max 3 lettere, MAIUSCOLE). Predefinito: ZIX',
      'folder.color.title':                     'Colore della cartella (facoltativo)',
      'folder.colorClear.title':                'Rimuovi il colore',
      'folder.name.placeholder':                'Nome della cartella (es. justzix.com)',
      'folder.url.placeholder':                 'Modello di URL (es. https://justzix.com/* )',
      'folder.urls.label':                      'Modelli di URL della cartella:',
      'folder.urls.title':                      'Modelli di URL della cartella — almeno uno deve corrispondere perché una regola di questa cartella venga eseguita',
      'folder.urls.placeholder':                'https://justzix.com/*  (Invio per aggiungere)',
      'folder.insertUrl.title':                 'Inserisci l\'URL della scheda attiva',
      'folder.active.label':                    'Attiva',
      'folder.active.title':                    'Attiva — se deselezionata, la cartella (e tutti i suoi gruppi/regole) viene forzatamente disattivata e l\'interruttore bloccato',
      'folder.enabled.label':                   'Abilitata',
      'folder.enabled.title':                   'Abilitata — stato attuale della cartella (si propaga ai gruppi e alle regole attivi)',
      'folder.actionsPanel.label':              'Pannello azioni',
      'folder.actionsPanel.title':              'Mostra il pannello fluttuante con i pulsanti di azione per questa cartella',
      'folder.layout.label':                    'Disposizione:',
      'folder.layout.title':                    'Disposizione dei pulsanti del pannello azioni',
      'folder.layout.vertical':                 'Verticale',
      'folder.layout.horizontal':               'Orizzontale',
      'folder.addGroup.label':                  '+ Gruppo',
      'folder.addGroup.title':                  'Aggiungi un nuovo gruppo in questa cartella',
      'folder.export.label':                    'Esporta',
      'folder.export.title':                    'Esporta questa cartella',
      'folder.import.label':                    'Importa',
      'folder.import.title':                    'Importa una cartella da file',
      'folder.delete.label':                    'Elimina cartella',

      // === Options — group editor ===
      'group.parentLabel':                      'Gruppo nella cartella:',
      'folder.label':                           'Cartella:',
      'group.label':                            'Gruppo:',
      'group.name.placeholder':                 'Nome del gruppo',
      'group.urls.label':                       'Modelli di URL del gruppo:',
      'group.urls.title':                       'Modelli di URL aggiuntivi del gruppo — restrizione extra sovrapposta al modello della cartella, si applica a tutte le regole del gruppo',
      'group.urls.placeholder':                 'https://example.com/admin/*  (Tab = completa dalla cartella, Invio = aggiungi)',
      'group.active.label':                     'Attivo',
      'group.active.title':                     'Attivo — se deselezionato, il gruppo (e tutte le sue regole) viene forzatamente disattivato e l\'interruttore bloccato',
      'group.enabled.label':                    'Abilitato',
      'group.enabled.title':                    'Abilitato — stato attuale del gruppo (si propaga alle regole attive)',
      'group.addRule.label':                    '+ Aggiungi set di regole',
      'group.export.title':                     'Esporta questo gruppo',
      'group.import.title':                     'Importa set di regole in questo gruppo',
      'group.move.label':                       'Sposta',
      'group.move.title':                       'Sposta il gruppo in un\'altra cartella',
      'group.duplicate.label':                  'Duplica',
      'group.duplicate.title':                  'Duplica il gruppo con i set di regole',
      'group.delete.label':                     'Elimina gruppo',

      // === Options — rule editor ===
      'rule.label':                             'Set di regole:',
      'rule.back.label':                        'Torna al gruppo',
      'rule.export.title':                      'Esporta questo set di regole su file',
      'rule.share.label':                       'Condividi URL',
      'rule.share.title':                       'Condividi questo set di regole come link pubblico a tempo limitato',
      'rule.import.title':                      'Importa set di regole (sostituirà quello attuale)',

      // === Options — empty state ===
      'empty.title':                            'Seleziona o aggiungi una cartella',
      'empty.body':                             'Ogni cartella raggruppa set di regole CSS/JS per un modello di URL.',

      // === Default names for newly created entities (options.js) ===
      'default.groupName':                      'Predefinito',
      'default.folderName':                     'Nuova cartella',
      'default.ruleName':                       'Nuovo set di regole',

      // === Plural noun forms used by toasts ===
      'plural.rule.one':                        'set di regole',
      'plural.rule.few':                        'set di regole',
      'plural.rule.many':                       'set di regole',
      'plural.rule.other':                      'set di regole',
      'plural.folder.one':                      'cartella',
      'plural.folder.few':                      'cartelle',
      'plural.folder.many':                     'cartelle',
      'plural.folder.other':                    'cartelle',
      'plural.group.one':                       'gruppo',
      'plural.group.few':                       'gruppi',
      'plural.group.many':                      'gruppi',
      'plural.group.other':                     'gruppi',
      'plural.action.one':                      'azione',
      'plural.action.few':                      'azioni',
      'plural.action.many':                     'azioni',
      'plural.action.other':                    'azioni',
      'plural.jsLine.one':                      'riga JS',
      'plural.jsLine.few':                      'righe JS',
      'plural.jsLine.many':                     'righe JS',
      'plural.jsLine.other':                    'righe JS',
      'toast.importedRulesToGroup':             ':count :word importati nel gruppo',
      'toast.exportedRules':                    ':count :word esportati',

      // === Sidebar — dynamic render ===
      'sidebar.empty.folders':                  'Nessuna cartella',
      'sidebar.empty.activeFolders':            'Nessuna cartella attiva',
      'sidebar.folder.toggleHint':              'Attiva/disattiva l\'intera cartella',
      'sidebar.folder.inactiveHint':            'Cartella inattiva — sbloccala nell\'editor della cartella',
      'sidebar.group.toggleHint':               'Attiva/disattiva il gruppo',
      'sidebar.group.inactiveHint':             'Gruppo inattivo — sbloccalo nell\'editor del gruppo',
      'sidebar.rule.urlHint':                   'Restrizione di URL a livello di set di regole',
      'sidebar.rule.activeInFolder':            'Attivo nella cartella',
      'sidebar.rule.inactiveInFolder':          'Inattivo nella cartella',
      'sidebar.fallback.folderName':            '(cartella)',
      'sidebar.fallback.groupName':             '(gruppo)',
      'sidebar.fallback.unnamed':               '(nessun nome)',
      'sidebar.flag.css':                       'Contiene CSS',
      'sidebar.flag.js':                        'Contiene JavaScript',
      'sidebar.flag.actions':                   'Contiene azioni',
      'sidebar.flag.groupUrls':                 'Il gruppo ha una restrizione di URL',

      // === Folder/Group/Rule editor — dynamic ===
      'rules.empty.folder':                     'Nessun set di regole in questa cartella. Aggiungi un gruppo e creavi il primo set di regole, oppure clicca su un gruppo nella barra laterale.',
      'rules.empty.group':                      'Nessun set di regole in questo gruppo. Aggiungi il primo cliccando su "+ Aggiungi set di regole".',
      'rule.namePlaceholder':                   'Nome del set di regole',
      'rule.activeTooltip':                     'Attivo — se questa regola viene inclusa quando si commuta l\'intera cartella',
      'rule.urls.tooltip':                      'Modelli di URL aggiuntivi — se impostati, la regola viene eseguita solo sulle sottopagine che corrispondono ad almeno un modello (oltre al modello della cartella)',
      'rule.urls.placeholder.empty':            'https://example.com/admin/*  (Tab — inserisci il modello della cartella, Invio — aggiungi)',

      // === Move menu ===
      'moveMenu.section.moveGroup':             'Sposta il gruppo in',
      'moveMenu.section.moveRule':              'Sposta nel gruppo',
      'moveMenu.section.copyRule':              'Copia nel gruppo',
      'moveMenu.empty.folders':                 'Nessun\'altra cartella',
      'moveMenu.empty.groups':                  'Nessun altro gruppo',

      // === Confirm dialogs — dynamic ===
      'confirm.deleteFolderWithStats':          'Eliminare la cartella ":name" insieme a :groups e :rules?',
      'confirm.deleteFolder':                   'Eliminare la cartella ":name"?',
      'confirm.deleteGroupWithRules':           'Eliminare il gruppo ":name" insieme a :count :word?',
      'confirm.deleteGroup':                    'Eliminare il gruppo ":name"?',
      'confirm.replacePartial':                 'Sostituire :before attuale con un backup parziale (:after)?\n\nTutti i dati esterni al file andranno persi. L\'operazione è irreversibile.',
      'confirm.overwriteAll':                   'Sovrascrivere :stats con i dati attuali del file?\n\nQuesta operazione è irreversibile.',

      // === Default name for newly created group ===
      'default.newGroupName':                   'Nuovo gruppo',

      // === Export — summary + states ===
      'export.summary.full':                    ':icon Backup completo: :stats + impostazioni globali',
      'export.summary.partial':                 ':icon Backup parziale: :stats',
      'export.empty.folder':                    'Nessuna cartella da esportare',
      'export.empty.group':                     'Il gruppo è vuoto — nessun set di regole da esportare',
      'export.summary.toExport':                ':icon Da esportare: :count :word',
      'export.summary.noneSelected':            ':icon Nessun set di regole selezionato',

      // === Import detection (banners) ===
      'import.detection.invalid':               '<strong style="color:#dc2626;">:icon Formato non valido:</strong> elenco delle cartelle mancante (<code>folders</code>).',
      'import.detection.partialContains':       'Contiene: :stats',
      'import.detection.willReplace':           ':stats',

      // === Inline labels ===
      'inline.fileGroupLabel':                  'gruppo',
      'shares.empty':                           'Non hai link attivi.<br><br>Per crearne uno — apri un export (completo / cartella / gruppo / set di regole)<br>e clicca su <strong>"Condividi URL"</strong>.',

      // === Share preview / export types ===
      'shareType.folder':                       'Singola cartella',
      'shareType.group':                        'Singolo gruppo',
      'shareType.rule':                         'Singolo set di regole',
      'shareType.full':                         'Export completo',

      // === Owner suffix when importing from share ===
      'shareImport.suffix.group':               'gruppo',
      'shareImport.suffix.rule':                'set di regole',

      // === Rule import — prompt with options ===
      'prompt.ruleConflict':                    'Stai importando ":new". Cosa fare con il set di regole attuale ":current"?\n\n  1 o "replace"  — Sostituisci il set di regole attuale\n  2 o "copy"     — Aggiungi come nuovo set di regole accanto a quello attuale (nello stesso gruppo)\n\nInserisci 1 o 2:',
      'prompt.folderConflicts':                 'Rilevati :count :word con lo stesso ID di esistenti:\n:names\n\nInserisci una delle opzioni:\n  1 o "overwrite"  — Sovrascrivi le cartelle esistenti (elimina i loro gruppi e set di regole attuali)\n  2 o "copy"       — Importa come copie (nuovi ID, "(import)" nel nome della cartella)\n  Cancel           — Annulla l\'import\n\nScelta:',

      // === Group editor — additional ===
      'group.empty.noGroupsInFolder':           'Nessun gruppo nella cartella',

      // === Folder import — conflict messages ===
      'folderImport.conflict.sameId':           'La cartella importata ha lo stesso ID di quella esistente "<strong>:name</strong>" (:count :word). Scegli un\'azione:',
      'folderImport.conflict.sameName':         'Esiste già una cartella chiamata "<strong>:name</strong>" (:count :word). Scegli un\'azione:',

      // === Dynamic button states ===
      'btn.shortcutPress':                      '⌨ premi Ctrl+Alt+Shift + un tasto (Esc)',
      'btn.generating':                         'Generazione…',
      'btn.importing':                          'Importazione…',
      'btn.showKey':                            'Mostra chiave',
      'btn.hideKey':                            'Nascondi chiave',

      // === Rule editor — placeholders + tooltips ===
      'rule.editor.urlsPlaceholder':            'https://example.com/admin/*  (Tab — inserisci il modello della cartella, Invio — aggiungi)',
      'rule.editor.urlsPlaceholderWithPattern': ':pattern  (Tab — inserisci, Invio — aggiungi)',
      'rule.editor.toggleHint':                 'Attiva/disattiva questo set di regole',
      'rule.editor.toggleLockedHint':           'Il set di regole è inattivo — sbloccalo selezionando Attivo',
      'sidebar.fallback.ruleName':              '(set di regole)',
      'sidebar.fallback.ruleUnnamed':           '(set di regole senza nome)',
      'confirm.deleteRule':                     'Eliminare il set di regole ":name"?',

      // === Rule card (rendered by createRuleCard / renderActionsList) ===
      'rule.card.tagsTitle':                    'Tag (separati da virgola)',
      'rule.card.activeLabel':                  'Attivo',
      'rule.card.enabledLabel':                 'Abilitato',
      'rule.card.history.title':                'Cronologia delle modifiche',
      'rule.card.history.label':                'Cronologia',
      'rule.card.move.label':                   'Sposta',
      'rule.card.move.btnTitle':                'Sposta il set di regole in un altro gruppo',
      'rule.card.copy.label':                   'Copia',
      'rule.card.copy.btnTitle':                'Copia il set di regole in un altro gruppo',
      'rule.card.duplicate.label':              'Duplica',
      'rule.card.delete.label':                 'Elimina',
      'rule.card.urlsLabel':                    'Modelli di URL del set di regole:',
      'rule.card.tab.actions':                  'Azioni',
      'rule.card.lineCount':                    'Numero di righe di codice',
      'rule.card.js.runOnce.label':             'Esegui JS una sola volta',
      'rule.card.js.runOnce.title':             'Non rieseguire il JS dopo la navigazione SPA (cambio URL senza ricaricare la pagina). Consigliato per script che aggiungono listener globali, intervalli o modificano window.',
      'rule.card.actions.info':                 'I pulsanti di azione appaiono nel pannello fluttuante (interruttore "Pannello azioni" nella cartella). Scorciatoia di max 4 caratteri, colore personalizzato, il codice JS viene eseguito al clic.',
      'rule.card.actions.add':                  '+ Aggiungi pulsante di azione',
      'rule.card.actions.addWithCount':         '+ Aggiungi pulsante di azione (:count)',
      'rule.card.action.visibleLabel':          'visibile',
      'rule.card.duplicate.suffix':             '(copia)',
      'rule.card.action.codePreview':           '(:count :word) :first',
      'rule.card.action.fallback.unlabeled':    '(nessuna etichetta)',
      'rule.card.fallbackName':                 'Set di regole',

      // === Folder export modal — additional ===
      'export.empty.foldersToExport':           'Nessuna cartella da esportare',
      'export.summary.toExportFolders':         ':icon Da esportare: :groupCount :groupWord, :ruleCount :ruleWord',
      'export.summary.invalidFolderFile':       'Formato di file non valido — atteso un export di una singola cartella.',
      'export.summary.noConflicts':             ':icon Nessun conflitto. La cartella "<strong>:name</strong>" con :count :word sarà importata come nuova.',
      'export.summary.fileContains':            '<strong style="color:#374151;">:icon Contenuto del file (:count :word):</strong>',
      'export.summary.jsHeader':                ':icon JavaScript del set di regole:',
      'export.summary.emptyRule':               'set di regole vuoto',
      'toast.exportedFolder':                   'Cartella esportata (:groupCount :groupWord, :ruleCount :ruleWord)',
      'inline.fileFolderLabel':                 'cartella',
      'inline.fileRuleLabel':                   'set di regole',
      'default.newGroupNameInline':             'Nuovo gruppo',

      // === Sync conflict modal counts ===
      'syncConflict.countLine':                 ':folders :foldersWord / :groups :groupsWord / :rules :rulesWord',

      // === Backup restore confirm ===
      'confirm.restoreBackup':                  'Ripristinare il backup #:id?\n\nTUTTE le cartelle/gruppi/set di regole attuali verranno sostituiti con il contenuto del backup. Gli altri dispositivi si sincronizzeranno al prossimo pull.\n\nIrreversibile (a parte la creazione di un nuovo backup PRIMA del ripristino).',

      // === Loaders / placeholders ===
      'loading.shares':                         'Caricamento…',
      'loading.generic':                        'Caricamento…',

      // === Sidebar — drag & arrows tooltips ===
      'sidebar.dragHandle.title':               'Trascina per riordinare',
      'sidebar.expandCollapse.title':           'Espandi/comprimi',
      'sidebar.folderActive.title':             'Attiva',
      'sidebar.folderInactive.title':           'Inattiva — forzatamente disattivata',
      'sidebar.groupActive.title':              'Attivo',
      'sidebar.groupInactive.title':            'Inattivo — forzatamente disattivato',
      'sidebar.searchNoResults':                'Nessun risultato per ":query"',

      // === Theme ===
      'settings.theme.toggleToDark':            'Passa alla modalità scura',
      'settings.theme.toggleToLight':           'Passa alla modalità chiara',
      'settings.theme.labelDark':               'Modalità scura',
      'settings.theme.labelLight':              'Modalità chiara',
      'settings.theme.labelAuto':               'Auto (sistema)',
      'settings.theme.toggleToAuto':            'Passa alla modalità automatica',

      // === URL chip remove ===
      'btn.removeUrlChip':                      'Rimuovi',

      // === CSS validator ===
      'cssValidator.unclosedComment':           'Commento non chiuso /* ... */',
      'cssValidator.missingCloseBraces':        'Parentesi non chiuse: mancano :count :word }',
      'plural.brace.one':                       'parentesi graffa di chiusura',
      'plural.brace.few':                       'parentesi graffe di chiusura',
      'plural.brace.many':                      'parentesi graffe di chiusura',
      'plural.brace.other':                     'parentesi graffe di chiusura',
      'cssValidator.extraCloseBrace':           'Parentesi graffa di chiusura in eccesso } (riga :line)',
      'jsValidator.unterminatedString':         'Letterale stringa non terminato (riga :line)',
      'jsValidator.unclosedBracket':            'Parentesi non chiusa :bracket (riga :line)',
      'jsValidator.extraBracket':               'Parentesi di chiusura in eccesso :bracket (riga :line)',

      // === Action buttons ===
      'action.shortcut.empty':                  'clicca + premi la scorciatoia',
      'action.code.empty':                      'clicca per aggiungere codice…',
      'action.stats.title':                     'Numero di clic nel pannello azioni',
      'action.visible.title':                   'Visibile nel pannello azioni (deselezionato = nascosto, ma non rimosso)',
      'action.shortcut.title':                  'Scorciatoia da tastiera — clicca e premi una combinazione. Esc annulla. La scorciatoia funziona sulle pagine corrispondenti indipendentemente dalla visibilità del pannello.',
      'action.shortcut.titleAfterClick':        'Scorciatoia da tastiera. Clicca e premi una combinazione per modificarla. Esc annulla. × rimuove.',
      'action.shortcut.removeBtn':              'Rimuovi la scorciatoia da tastiera',
      'action.delete.btn':                      'Elimina pulsante di azione',
      'action.menu.btn.duplicate':              'Duplica l\'azione qui',
      'action.menu.btn.move':                   'Sposta l\'azione in un altro set di regole',
      'action.menu.btn.copy':                   'Copia l\'azione in un altro set di regole',
      'action.menu.section.moveTo':             'Sposta nel set di regole...',
      'action.menu.section.copyTo':             'Copia nel set di regole...',
      'action.menu.empty.rules':                'Nessun altro set di regole',
      'action.menu.toast.moved':                'Azione spostata in ":target"',
      'action.menu.toast.copied':               'Azione copiata in ":target"',
      'action.shortcut.conflictTitle':          'Conflitto: questa scorciatoia è usata anche da :count :word:\n:list\n\nAlla pressione, si attiverà solo la prima nell\'ordine di rendering.',
      'action.shortcut.conflictItem':           '• ":action" in ":rule" (:folder)',
      'plural.actionAlt.one':                   'altra azione',
      'plural.actionAlt.few':                   'altre azioni',
      'plural.actionAlt.many':                  'altre azioni',
      'plural.actionAlt.other':                 'altre azioni',
      'plural.more.one':                        'altra',
      'plural.more.few':                        'altre',
      'plural.more.many':                       'altre',
      'plural.more.other':                      'altre',
      'action.shortcut.conflictMore':           ' (e altre :count :word)',
      'action.shortcut.conflictToast':          'Conflitto: questa scorciatoia è già usata dall\'azione ":actionLabel" in ":ruleName":more. Si attiverà solo una di esse.',
      'confirm.deleteAction':                   'Eliminare il pulsante di azione ":label"?',

      // === History ===
      'history.header':                         'Ultime :count versioni salvate. Clicca su "Ripristina" per tornare a quella versione (quella attuale sarà salvata nella cronologia).',
      'history.modalTitle':                     'Cronologia delle modifiche: ":name"',
      'history.version':                        'Versione :n',
      'history.restore':                        'Ripristina',
      'history.close':                          'Chiudi',

      // === Export — additional ===
      'export.summary.noElements':              ':icon Nessun elemento selezionato per l\'export',
      'export.summary.toExportFolderShort':     ':icon Da esportare: :groupCount :groupWord, :ruleCount :ruleWord',
      'export.summary.noneSelectedShort':       ':icon Nessun set di regole selezionato',

      // === Import detection ===
      'import.detection.parseError':            '<strong style="color:#dc2626;">:icon Errore di analisi JSON:</strong> :msg',
      'import.detection.partialBackup':         '<strong style="color:#5b21b6;">:icon Backup parziale (elementi selezionati)</strong><br>',
      'import.detection.fullBackup':            '<strong style="color:#006870;">:icon Backup completo delle impostazioni:legacy</strong><br>',
      'import.detection.legacySuffix':          ' (formato precedente)',
      'import.detection.willOverwrite':         '<br><br><strong style="color:#b91c1c;">:icon Le impostazioni attuali verranno sovrascritte:</strong>',
      'import.detection.exportFirst':           '<br><span style="color:#7f1d1d;font-size:11px;">Consigliato: esporta <strong>prima</strong> di importare.</span>',
      'import.detection.folder':                '<strong style="color:#155e75;">:icon Export di cartella</strong> "<strong>:name</strong>"<br><span style="color:#155e75;">Contiene: :stats</span>',
      'import.detection.group':                 '<strong style="color:#155e75;">:icon Export di gruppo</strong> "<strong>:name</strong>"<br><span style="color:#155e75;">Contiene: :stats</span>',
      'import.detection.rule':                  '<strong style="color:#155e75;">:icon Export di set di regole</strong> "<strong>:name</strong>"',

      // === Import — target selectors ===
      'import.target.folder.title':             'Dove collocare la cartella importata?',
      'import.target.folder.newCopy':           'Aggiungi come nuova cartella di primo livello',
      'import.target.folder.overwrite':         'Sovrascrivi la cartella esistente ":name" (mantiene l\'ID — la sincro la vede come una modifica)',
      'import.target.folder.mergeInto':         'Unisci gruppi/set di regole in una cartella esistente:',
      'import.target.group.title':              'Dove collocare il gruppo importato?',
      'import.target.group.existingFolder':     'Aggiungi a una cartella esistente:',
      'import.target.group.newFolder':          'Crea una nuova cartella per questo gruppo',
      'import.target.rule.title':               'Dove collocare il set di regole importato?',
      'import.target.rule.existingGroup':       'Aggiungi a un gruppo esistente:',
      'import.target.rule.newFolder':           'Crea una nuova cartella e un nuovo gruppo per questo set di regole',

      // === Share errors (create flow) ===
      'share.error.unknown':                    'Errore sconosciuto',
      'share.error.shareFailed':                'Hai già 20 link attivi o hai superato i 50 MB. Revoca quelli vecchi in "Le mie condivisioni".',
      'share.error.badRequest':                 'Generazione del link non riuscita — verifica che l\'export sia valido. :msg',
      'share.error.rateLimited':                'Troppi link creati nell\'ultima ora (limite: 30). Riprova tra un momento.',
      'share.error.invalidToken':               'Sessione scaduta — accedi di nuovo nel pannello di sincronizzazione e riprova.',
      'share.error.serverError':                'Problema del server. Riprova tra un momento.',
      'share.error.fallback':                   'Errore: :msg',
      'share.error.tooLarge':                   'Export troppo grande — il backend consente max 5 MB. Prova a condividere una parte più piccola.',

      // === Share subject (create modal) ===
      'share.subject.partial':                  'Export parziale della libreria',
      'share.subject.full':                     'Export completo della libreria',

      // === Time relative formatting (fmtRelHuman) ===
      'rel.lessThanMinute':                     'meno di un minuto',
      'rel.min':                                ':n min',
      'rel.hMin':                               ':h h :m min',
      'rel.h':                                  ':h h',
      'rel.dH':                                 ':d g :h h',
      'rel.d':                                  ':d g',

      // === Shares list ===
      'shares.expired':                         'scaduto',
      'shares.fetchFailed':                     'Impossibile recuperare l\'elenco: :msg',
      'shares.confirmRevoke':                   'Revocare la condivisione #:id?\n\nIl link smetterà di funzionare immediatamente — i destinatari otterranno un 404 al prossimo tentativo di recupero.\n\nNOTA: se qualcuno ha già scaricato i dati, ne avrà una copia locale — la revoca non recupera i contenuti già scaricati.',
      'shares.revoked':                         'Condivisione #:id revocata',

      // === Share preview details ===
      'sharePreview.senderLabel':               'Mittente:',
      'sharePreview.senderAnonymous':           'anonimo',
      'sharePreview.expiresLabel':              'Scade:',
      'sharePreview.expiresValue':              'tra :rel',
      'sharePreview.typeLabel':                 'Tipo:',
      'sharePreview.sizeLabel':                 'Dimensione:',
      'sharePreview.contentLabel':              'Contenuto:',
      'sharePreview.cssLabel':                  'CSS:',
      'sharePreview.cssValueChars':             ':n caratteri',
      'sharePreview.jsLabel':                   'JavaScript:',
      'sharePreview.downloadsLabel':            'Download:',
      'sharePreview.codePreviewTitle':          'Anteprima del codice dall\'export condiviso',

      // === Share import errors (preview) ===
      'shareImport.error.unknown':              'Errore sconosciuto',
      'shareImport.error.badRequest':           'Il token non è valido. Verifica di aver copiato il link completo.',
      'shareImport.error.notFound':             'Questo link è scaduto o è stato revocato dal mittente. Chiedine uno nuovo.',
      'shareImport.error.rateLimited':          'Troppi tentativi. Attendi un momento e riprova.',
      'shareImport.error.serverError':          'Problema del server. Riprova.',
      'shareImport.error.fallback':             'Errore: :msg',
      'shareImport.error.invalidTokenFormat':   'Formato del token non valido',

      // === Rule import ===
      'ruleImport.unknownFormat':               'Formato di export sconosciuto o non supportato: :type',
      'prompt.ruleConflict.body':               'Stai importando ":new". Cosa fare con il set di regole attuale ":current"?\n\n  1 o "replace"  — Sostituisci l\'attuale con il contenuto del file\n  2 o "copy"     — Aggiungi come nuovo set di regole accanto a quello attuale (nello stesso gruppo)\n  Cancel         — Annulla l\'import\n\nScelta:',

      // === Folder export ===
      'folderExport.exportedToast':             'Cartella esportata (:groupCount :groupWord, :ruleCount :ruleWord)',

      // === Folder import — details ===
      'folderImport.parseError':                'Errore di analisi JSON: :msg',
      'folderImport.invalidFormat':             'Formato di file non valido — atteso un export di una singola cartella.',
      'folderImport.noConflicts':               ':icon Nessun conflitto. La cartella "<strong>:name</strong>" con :count :word sarà importata come nuova.',
      'folderImport.conflictSameId':            'La cartella importata ha lo stesso ID di quella esistente "<strong>:name</strong>" (:count :word). Scegli un\'azione:',
      'folderImport.conflictSameName':          'Esiste già una cartella chiamata "<strong>:name</strong>" (:count :word). Scegli un\'azione:',
      'folderImport.fileContents':              '<strong style="color:#374151;">:icon Contenuto del file (:count :word):</strong>',
      'folderImport.jsHeader':                  ':icon JavaScript del set di regole:',
      'folderImport.codePreviewTitle':          'Anteprima del codice: :name',
      'folderImport.previewJsBtn.title':        'Anteprima del codice JS',

      // === Sync — toasts + status messages ===
      'sync.toast.synced':                      'Sincronizzato: :applied modifiche, :deletes eliminazioni',
      'sync.rel.justNow':                       'proprio ora',
      'sync.tooltip.syncing':                   'Sincronizzazione…',
      'sync.tooltip.error':                     'Sincro: errore. Ultima riuscita: :last',
      'sync.tooltip.connected':                 'Sincronizzazione cloud — connesso. Ultima: :last',
      'sync.error.copyManually':                'Copia non riuscita — seleziona e premi Ctrl+C manualmente.',
      'sync.error.registerFailed':              'Errore di registrazione: :msg',
      'sync.error.unknown':                     'sconosciuto',
      'sync.error.invalidKeyFormat':            'Formato della chiave non valido — atteso SYNC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX.',
      'sync.status.sendingLocal':               'Invio di :count entità locali all\'account.',
      'sync.status.cancelled':                  'Accesso annullato — disconnesso. Dati locali intatti.',
      'sync.status.sendingAndDeleting':         'Invio di :local entità locali, eliminazione di :remote dall\'account…',
      'sync.status.merged':                     'Uniti. Le entità solo locali (:count elementi) andranno all\'account alla prossima sincro.',
      'sync.error.firstSyncFailed':             'Accesso OK ma la prima sincro è fallita: :msg',
      'sync.error.loginFailed':                 'Errore di accesso: :msg',
      'sync.error.invalidEmail':                'Inserisci un\'email valida.',
      'sync.recovery.sent':                     'Se questa email è registrata, abbiamo inviato un link di recupero. Controlla la posta in arrivo.',
      'sync.error.generic':                     'Errore: :msg',
      'sync.account.thisDevice':                'questo dispositivo: :label',
      'sync.session.noLabel':                   '(nessuna etichetta)',
      'sync.session.thisDeviceSuffix':          ' (questo dispositivo)',
      'sync.session.revoke':                    'Revoca',
      'sync.error.revokeFailed':                'Errore di revoca: :msg',
      'sync.session.fetchFailed':               'Impossibile recuperare l\'elenco delle sessioni: :msg',
      'sync.key.copyManual':                    'Copia non riuscita — clicca su "Mostra chiave" e premi Ctrl+C manualmente.',
      'sync.key.removed':                       'Chiave rimossa dall\'archiviazione locale.',
      'sync.backups.empty':                     'Nessun backup cloud. Crea il primo cliccando su "Crea backup ora".',
      'sync.backups.fetchFailed':               'Impossibile recuperare l\'elenco: :msg',
      'sync.backups.creating':                  'Creazione del backup cloud…',
      'sync.backups.createFailed':              'Errore nella creazione del backup: :msg',
      'sync.backups.restored':                  'Backup #:id ripristinato. Sostituite :count entità. newCursor=:cursor.',
      'sync.backups.restoreFailed':             'Errore di ripristino: :msg',
      'sync.backups.downloadFailed':            'Errore di download: :msg',
      'sync.backups.confirmDelete':             'Eliminare il backup #:id dal cloud? Irreversibile.',
      'sync.backups.deleted':                   'Backup #:id eliminato.',
      'sync.backups.deleteFailed':              'Errore di eliminazione: :msg',
      'sync.pushPullSummary':                   'Push: :pushedAccepted accettate, :pushedRejected rifiutate • Pull: :applied entità, :deletes eliminazioni.',
      'sync.returned':                          'La sincro ha restituito: :result',
      'sync.snapshotFailed':                    'Errore dello snapshot: :msg',
      'sync.reconcile.running':                 'Risincronizzazione completa con l\'account…',
      'sync.reconcile.okPushed':                'Resync OK — inviate :count entità mancanti all\'account.',
      'sync.reconcile.okInSync':                'Resync OK — la libreria corrisponde all\'account.',
      'sync.reconcile.failed':                  'Risincronizzazione completa non riuscita: :msg',
      'sync.email.saveButFailed':               'Email salvata, ma l\'invio del link è fallito — riprova.',
      'sync.email.verifyLinkSent':              'Link di verifica inviato a :email. Controlla la posta in arrivo e clicca sul link.',
      'sync.email.removed':                     'Email rimossa.',

      // === Sync result modal — strategies ===
      'syncResult.merge.title':                 'Unito con il cloud',
      'syncResult.merge.desc':                  'Le entità presenti in entrambi i posti sono state sovrascritte con la versione dell\'account. Quelle solo locali rimangono e andranno all\'account alla prossima sincro.',
      'syncResult.merge.tableHeader':           '<table class="result-table"><thead><tr><th>Tipo</th><th>Dal cloud: sovrascritte</th><th>Dal cloud: nuove</th><th>Locali mantenute → push</th><th>Totale dopo l\'unione</th></tr></thead><tbody>',
      'syncResult.useServer.title':             'Dati locali sostituiti con quelli del cloud',
      'syncResult.useServer.desc':              'La libreria locale è stata completamente sostituita con lo stato dell\'account. Le entità solo locali (se presenti) sono andate perse.',
      'syncResult.useServer.tableHeader':       '<table class="result-table"><thead><tr><th>Tipo</th><th>Recuperate dal cloud</th><th>Locali perse</th><th>Comuni (sovrascritte)</th><th>Totale dopo la sincro</th></tr></thead><tbody>',
      'syncResult.sendMine.title':              'Invio dei dati locali all\'account',
      'syncResult.sendMine.desc':               'La libreria locale verrà inviata all\'account. Le entità presenti nel cloud ma assenti localmente verranno contrassegnate come eliminate (tombstone) sull\'account.',
      'syncResult.sendMine.tableHeader':        '<table class="result-table"><thead><tr><th>Tipo</th><th>Inviate (push)</th><th>Sovrascritture sull\'account</th><th>Nuove sull\'account</th><th>Eliminate dall\'account</th></tr></thead><tbody>',
      'syncResult.fallback.title':              'Sincronizzazione completata',
      'syncResult.empty':                       'Nessun dettaglio.',
      'syncResult.label.folders':               'Cartelle',
      'syncResult.label.groups':                'Gruppi',
      'syncResult.label.rules':                 'Set di regole',
      'syncResult.label.actions':               'Azioni',

      // === Options — toasts (bottom notifications) ===
      'toast.saved':                            'Salvato',
      'toast.cantDeleteLastGroup':              'Impossibile eliminare l\'ultimo gruppo di una cartella',
      'toast.noOtherTab':                       'Nessun\'altra scheda con un URL di pagina',
      'toast.urlInserted':                      'URL della scheda inserito',
      'toast.urlAlreadyAdded':                  'Modello già nell\'elenco: :pattern',
      'toast.error':                            'Errore: :msg',
      'toast.shortcutMustIncludeMod':           'Scorciatoia consentita: Ctrl+Alt+Shift + una lettera (tranne G H J K L), una cifra o un simbolo (tranne \\).',
      'toast.shortcutCollidesChrome':           'Avviso: la scorciatoia entra in conflitto con una scorciatoia di Chrome (es. nuova scheda). Potrebbe non funzionare — scegline un\'altra.',
      'toast.copied':                           'Copiato: :text',
      'toast.copyFailed':                       'Copia non riuscita',
      'toast.copyClipboard':                    'Copiato negli appunti',
      'toast.copyLink':                         'Link copiato',
      'toast.copyToken':                        'Token copiato',
      'toast.noHistory':                        'Nessuna cronologia per questo set di regole',
      'toast.versionRestored':                  'Versione ripristinata',
      'toast.fileNotFullImport':                'Il file non è adatto a un import completo',
      'toast.unknownOption':                    'Opzione sconosciuta — import annullato',
      'toast.importedSettings':                 'Impostazioni importate',
      'toast.parseError':                       'Errore di analisi: :msg',
      'toast.invalidPartialFormat':             'Formato non valido — atteso un export di gruppo, cartella o set di regole',
      'toast.groupImport.folderRejected':       'Questo è un export di cartella (più gruppi). Importalo tramite l\'import a livello di cartella, non di gruppo.',
      'toast.exportedRule':                     'Set di regole esportato',
      'toast.signInFirst':                      'Accedi prima nel pannello di sincronizzazione',
      'toast.exportNotReady':                   'L\'export non è pronto',
      'toast.noRulesSelected':                  'Nessun set di regole selezionato',
      'toast.revokeError':                      'Errore di revoca: :msg',
      'toast.importedFromLink':                 'Importato dal link',
      'toast.invalidRuleFormat':                'Formato di file non valido — atteso un export di set di regole',
      'toast.importedReplaced':                 'Importato (sostituito)',
      'toast.importedAsCopy':                   'Importato come copia',
      'toast.noImportData':                     'Nessun dato da importare',
      'toast.importedFolder':                   'Cartella importata: :name',
      'toast.importedGroup':                    'Gruppo importato: :name',
      'toast.importedRule':                     'Set di regole importato: :name',

      // === Options — native confirm() dialogs ===
      'confirm.restoreVersion':                 'Ripristinare questa versione? Il contenuto attuale verrà aggiunto alla cronologia come nuovo snapshot.',
      'confirm.revokeSession':                  'Revocare questa sessione?',
      'confirm.removeKey':                      'Rimuovere la chiave da questo dispositivo?\n\nL\'accesso continua a funzionare (la sessione è indipendente dalla chiave), ma non potrai più visualizzare o copiare la chiave su un altro dispositivo da questo computer.\n\nAssicurati di avere una copia della chiave salvata altrove (gestore di password).',
      'confirm.snapshot':                       'Uno snapshot completo scarica l\'intera libreria dal cloud e sostituisce lo stato locale. Le modifiche in sospeso NON verranno perse (rimangono in coda). Continuare?',
      'confirm.removeEmail':                    'Rimuovere l\'email dall\'account? Senza email non potrai effettuare il recupero in caso di perdita della chiave.',
      'confirm.signOut':                        'Disconnettere questo dispositivo? La libreria locale rimane, ma smette di sincronizzarsi.',

      // === Modal — full export ===
      'modal.export.title':                     'Esporta impostazioni',
      'modal.export.mode.all':                  'Tutto',
      'modal.export.mode.select':               'Cartelle/regole selezionate',
      'modal.export.selectAll':                 'Seleziona tutto',
      'modal.export.deselectAll':               'Deseleziona tutto',
      'modal.export.hint':                      'Puoi copiare il JSON negli appunti o scaricare il file:',
      'modal.export.close':                     'Chiudi',
      'modal.export.share':                     'Condividi URL',
      'modal.export.share.title':               'Condividi questo export come link pubblico a tempo limitato',
      'modal.export.download':                  'Scarica il file',
      'modal.export.copy':                      'Copia negli appunti',

      // === Modal — full import ===
      'modal.import.title':                     'Importa impostazioni',
      'modal.import.pickerHint':                'Incolla il JSON di qualsiasi export JustZix (backup completo, cartella, gruppo o set di regole) — il modulo rileverà il tipo e offrirà opzioni di collocamento.',
      'modal.import.warningStrong':             'Avviso:',
      'modal.import.warningRest':               ' un import completo sovrascriverà tutte le impostazioni attuali.',
      'modal.import.placeholder':               'Incolla il JSON qui o scegli un file...',
      'modal.import.cancel':                    'Annulla',
      'modal.import.import':                    'Importa',

      // === Modal — folder export ===
      'modal.folderExport.titlePrefix':         'Esporta cartella:',
      'modal.folderExport.hint':                'Seleziona i gruppi/regole da esportare.',
      'modal.folderExport.cancel':              'Annulla',
      'modal.folderExport.share':               'Condividi URL',
      'modal.folderExport.shareTitle':          'Condividi questa cartella come link pubblico a tempo limitato',
      'modal.folderExport.download':            'Scarica il file',

      // === Modal — group export ===
      'modal.groupExport.titlePrefix':          'Esporta gruppo:',
      'modal.groupExport.hint':                 'Seleziona i set di regole da esportare.',
      'modal.groupExport.cancel':               'Annulla',
      'modal.groupExport.share':                'Condividi URL',
      'modal.groupExport.shareTitle':           'Condividi questo gruppo come link pubblico a tempo limitato',
      'modal.groupExport.download':             'Scarica il file',

      // === Modal — folder import ===
      'modal.folderImport.title':               'Importa cartella',
      'modal.folderImport.placeholder':         'Incolla il JSON della cartella o scegli un file...',
      'modal.folderImport.conflicts':           'Conflitti rilevati:',
      'modal.folderImport.action.rename':       'Importa come nuova cartella (con suffisso "(import)")',
      'modal.folderImport.action.overwrite':    'Sovrascrivi la cartella esistente',
      'modal.folderImport.action.merge':        'Unisci — aggiungi i nuovi set di regole alla cartella esistente (mantenendo entrambi)',
      'modal.folderImport.cancel':              'Annulla',
      'modal.folderImport.import':              'Importa',

      // === Modal — action code ===
      // === Action bars (Phase 2 of multi-bars system, since v2.9) ===
      'actionBars.btn.label':                   'Barre delle azioni',
      'actionBars.btn.title':                   'Gestisci le barre delle azioni assegnate a questa cartella',
      'actionBars.btn.titleGroup':              'Gestisci le barre delle azioni assegnate a questo gruppo',
      'actionBars.btn.titleRule':               'Gestisci le barre delle azioni assegnate a questo set di regole',
      'actionBars.parentType.folder':           'Cartella',
      'actionBars.parentType.group':            'Gruppo',
      'actionBars.parentType.rule':             'Set di regole',
      'actionBars.parentType.folder.short':     '[C]',
      'actionBars.parentType.group.short':      '[G]',
      'actionBars.parentType.rule.short':       '[R]',
      'actionBars.empty':                       'Nessuna barra delle azioni a questo livello. Clicca su "+ Aggiungi nuova barra" per crearne la prima.',
      'actionBars.section.owned':               'Barre a questo livello',
      'actionBars.section.fromFolder':          '↑ Ereditata dalla cartella "<strong>:name</strong>"',
      'actionBars.section.fromGroup':           '↑ Ereditata dal gruppo "<strong>:name</strong>"',
      'actionBars.elementsCount':               'Elementi: :count',
      'actionBars.enabled':                     'Abilitata',
      'actionBars.name.placeholder':            'NOME',
      'actionBars.name.title':                  'Etichetta sul bordo della barra (max 6 caratteri, MAIUSCOLE). Vuoto = nessuna etichetta.',
      'actionBars.color.title':                 'Colore di sfondo della barra',
      'actionBars.orientation.title':           'Clicca per commutare l\'orientamento della barra (verticale ↔ orizzontale)',
      'actionBars.color.clear':                 'Rimuovi il colore di sfondo',
      'actionBars.moveTo':                      'Sposta in:',
      'actionBars.delete':                      'Elimina',
      'actionBars.confirmDelete':               'Eliminare questa barra?',
      'actionBars.confirmDeleteWithElements':   'Eliminare questa barra? Gli elementi assegnati (:count) manterranno le loro definizioni ma perderanno l\'assegnazione alla barra — dovrai assegnarli manualmente a un\'altra barra.',
      'actionBars.deleteMigrate.message':       'Questa barra ha :count elementi assegnati. Scegli dove spostarli prima dell\'eliminazione (oppure scollegali da qualsiasi barra):',
      'actionBars.deleteMigrate.detachOption':  '— scollega gli elementi (nessuna barra) —',
      'actionBars.deleteMigrate.confirm':       'Sposta ed elimina',
      'actionBars.deleteMigrate.cancel':        'Annulla',
      'modal.actionBars.titlePrefix':           'Barre delle azioni —',
      'modal.actionBars.hint':                  'Barre assegnate a questo livello. Le barre della cartella aggregano gli elementi di tutti i set di regole attivi nella cartella, le barre del gruppo — solo dal gruppo, le barre del set di regole — solo quando il set di regole è attivo. Una barra senza elementi visibili non verrà renderizzata.',
      'modal.actionBars.add':                   '+ Aggiungi nuova barra',
      'modal.actionBars.close':                 'Chiudi',

      // === Windows — dropdown type selector (since v2.13.41) ===
      'windows.btn.label':                      'Finestre',
      'windows.btn.title':                      'Gestisci le finestre assegnate a questa cartella (CSS / JS / console)',
      'windows.btn.titleGroup':                 'Gestisci le finestre assegnate a questo gruppo',
      'windows.btn.titleRule':                  'Gestisci le finestre assegnate a questo set di regole',
      'windows.menu.title':                     'Scegli il tipo di finestra:',
      'windows.menu.comingSoon':                'in arrivo',
      'jsPanes.btn.label':                      'Pannelli JS',
      'jsConsoles.btn.label':                   'Console JS',
      'outputConsoles.btn.label':               'Console di output',

      // === CSS panes (Phase 2 "Windows on the front", since v2.13.40) ===
      'cssPanes.btn.label':                     'Pannelli CSS',
      'cssPanes.contextMenu.disable':           'Chiudi il pannello CSS',
      'cssPanes.btn.title':                     'Gestisci i pannelli CSS assegnati a questa cartella',
      'cssPanes.btn.titleGroup':                'Gestisci i pannelli CSS assegnati a questo gruppo',
      'cssPanes.btn.titleRule':                 'Gestisci i pannelli CSS assegnati a questo set di regole',
      'cssPanes.empty':                         'Nessun pannello CSS a questo livello. Clicca su "+ Aggiungi nuovo pannello CSS" per crearne il primo.',
      'cssPanes.section.owned':                 'Pannelli a questo livello',
      'cssPanes.name.placeholder':              'Nome del pannello',
      'cssPanes.color.title':                   'Colore del punto dell\'intestazione',
      'panes.style.dot':                        'Punto',
      'panes.style.headerBg':                   'Sfondo intestazione',
      'panes.style.headerText':                 'Testo intestazione',
      'panes.style.bodyBg':                     'Sfondo corpo',
      'panes.style.bodyText':                   'Testo corpo',
      'panes.style.runBtn':                     'Pulsante Esegui',
      'panes.style.reset':                      'Cancella',
      'mcp.default.title':                      'Ripristina predefinito',
      'action.colors.bg':                       'Sfondo',
      'action.colors.text':                     'Testo',
      'action.colors.dropdownBg':               'Sfondo del menu',
      'action.colors.option':                   'Opzione',
      'action.colors.selected':                 'Selezionato',
      'action.colors.hover':                    'Passaggio del mouse',
      'action.colors.label':                    'Etichetta',
      'action.colors.value':                    'Valore',
      'action.colors.placeholder':              'Segnaposto',
      'action.colors.bgInactive':               'Sfondo inattivo',
      'action.colors.inactiveText':             'Testo inattivo',
      'cssPanes.enabled.label':                 'Abilitato',
      'cssPanes.enabled.title':                 'Se il pannello deve apparire sulle pagine corrispondenti',
      'cssPanes.delete.title':                  'Elimina questo pannello',
      'cssPanes.delete.confirm':                'Eliminare il pannello CSS ":name"? Il contenuto dell\'area di testo nelle schede attive andrà perso.',
      'modal.cssPanes.titlePrefix':             'Pannelli CSS —',
      'modal.cssPanes.hint':                    'Area di testo fluttuante iniettata sulle pagine corrispondenti agli URL di questo livello. Il contenuto è conservato in sessionStorage per scheda (perso alla chiusura della scheda). Iniezione <style> in tempo reale — le modifiche CSS si applicano istantaneamente.',
      'modal.cssPanes.add':                     '+ Aggiungi nuovo pannello CSS',
      'modal.cssPanes.close':                   'Chiudi',
      'cssPane.frontend.close.title':           'Nascondi il pannello (resterà nascosto fino alla chiusura della scheda)',

      // === JS panes (Phase 2 cont., since v2.13.56) ===
      'jsPanes.contextMenu.disable':            'Chiudi il pannello JS',
      'jsPanes.btn.title':                      'Gestisci i pannelli JS assegnati a questa cartella',
      'jsPanes.btn.titleGroup':                 'Gestisci i pannelli JS assegnati a questo gruppo',
      'jsPanes.btn.titleRule':                  'Gestisci i pannelli JS assegnati a questo set di regole',
      'jsPanes.empty':                          'Nessun pannello JS a questo livello. Clicca su "+ Aggiungi nuovo pannello JS" per crearne il primo.',
      'jsPanes.section.owned':                  'Pannelli a questo livello',
      'jsPanes.name.placeholder':               'Nome del pannello',
      'jsPanes.color.title':                    'Colore del punto dell\'intestazione',
      'jsPanes.enabled.label':                  'Abilitato',
      'jsPanes.enabled.title':                  'Se il pannello deve apparire sulle pagine corrispondenti',
      'jsPanes.delete.title':                   'Elimina questo pannello',
      'jsPanes.delete.confirm':                 'Eliminare il pannello JS ":name"? Il contenuto dell\'area di testo nelle schede attive andrà perso.',
      'modal.jsPanes.titlePrefix':              'Pannelli JS —',
      'modal.jsPanes.hint':                     'Area di testo fluttuante che esegue JS nel contesto della pagina (MAIN world, new Function) a ogni modifica. Il contenuto è conservato per scheda in sessionStorage. Gli errori di esecuzione sono mostrati in un overlay rosso sotto il codice.',
      'modal.jsPanes.add':                      '+ Aggiungi nuovo pannello JS',
      'modal.jsPanes.close':                    'Chiudi',
      'jsPane.frontend.close.title':            'Nascondi il pannello (resterà nascosto fino alla chiusura della scheda)',

      // === JS Consoles (Phase 2 cont., since v2.13.66) ===
      'jsConsoles.contextMenu.disable':         'Chiudi la console JS',
      'jsConsoles.empty':                       'Nessuna console a questo livello. Clicca su "+ Aggiungi nuova console" per crearne la prima.',
      'jsConsoles.section.owned':               'Console a questo livello',
      'jsConsoles.name.placeholder':            'Nome della console',
      'jsConsoles.enabled.label':               'Abilitata',
      'jsConsoles.enabled.title':               'Se la console deve apparire sulle pagine corrispondenti',
      'jsConsoles.delete.title':                'Elimina questa console',
      'jsConsoles.delete.confirm':              'Eliminare la console ":name"? La cronologia di output nelle schede attive andrà persa.',
      'modal.jsConsoles.titlePrefix':           'Console JS —',
      'modal.jsConsoles.hint':                  'REPL interattiva — Ctrl+Invio esegue il codice nel MAIN world della pagina, l\'output (o l\'errore) viene aggiunto all\'area di log sotto l\'input. L\'output è conservato per scheda in sessionStorage.',
      'modal.jsConsoles.add':                   '+ Aggiungi nuova console',
      'modal.jsConsoles.close':                 'Chiudi',
      'jsConsole.frontend.close.title':         'Nascondi la console (fino alla chiusura della scheda)',
      'jsConsole.input.placeholder':            'Codice da eseguire (Ctrl+Invio per eseguire, Ctrl+L per cancellare)',
      'jsConsole.clear.title':                  'Cancella l\'output (Ctrl+L)',

      // === Output Console (Phase 2 finish, since v2.13.72) — read-only log viewer. ===
      'panes.style.clearBtn':                   'Pulsante Cancella',
      'panes.style.actionBtns':                 'Pulsanti di azione',
      'outputConsoles.contextMenu.disable':     'Chiudi la console di output',
      'outputConsoles.empty':                   'Nessuna console di output a questo livello. Clicca su "+ Aggiungi nuova console di output" per crearne la prima.',
      'outputConsoles.section.owned':           'Console di output a questo livello',
      'outputConsoles.name.placeholder':        'Nome della console di output',
      'outputConsoles.enabled.label':           'Abilitata',
      'outputConsoles.enabled.title':           'Se la console di output deve apparire sulle pagine corrispondenti',
      'outputConsoles.delete.title':            'Elimina questa console di output',
      'outputConsoles.delete.confirm':          'Eliminare la console di output ":name"? La cronologia dei log nelle schede attive andrà persa.',
      'modal.outputConsoles.titlePrefix':       'Console di output —',
      'modal.outputConsoles.hint':              'Visualizzatore di log in sola lettura — cattura console.log/warn/error/info dalla pagina (incluso l\'asincrono). Cancella (⌫) svuota solo il visualizzatore, non la console del browser.',
      'modal.outputConsoles.add':               '+ Aggiungi nuova console di output',
      'modal.outputConsoles.close':             'Chiudi',
      'outputConsole.frontend.close.title':     'Nascondi la console di output (fino alla chiusura della scheda)',
      'outputConsole.clear.title':              'Cancella il visualizzatore (Ctrl+Shift+L)',
      'outputConsole.autoscroll.title':         'Scorrimento automatico ai log più recenti',
      'outputConsole.empty':                    '(nessun log — in attesa di console.* / JZ.* dalla pagina)',
      'outputConsole.mode.standard.title':      'Mostra solo console.* (clic / Ctrl+Shift+K → JZ.*)',
      'outputConsole.mode.jz.title':            'Mostra solo window.JZ.* (clic / Ctrl+Shift+K → entrambi)',
      'outputConsole.mode.both.title':          'Mostra entrambi: console.* + JZ.* (clic / Ctrl+Shift+K → solo console.*)',
      'outputConsole.filters.title':            'Filtri per livello',
      'outputConsole.filters.level.log':        'log',
      'outputConsole.filters.level.info':       'info',
      'outputConsole.filters.level.warn':       'warn',
      'outputConsole.filters.level.error':      'error',
      'outputConsole.filters.level.debug':      'debug',
      'outputConsole.search.title':             'Cerca nei log (Ctrl+Shift+S; Esc cancella)',
      'outputConsole.search.placeholder':       'Filtra le voci…',
      'outputConsole.tab.all':                  'Tutto',
      'outputConsole.tab.jz':                   'JZconsole',
      'outputConsole.tab.console':              'Console',
      'outputConsole.tab.network':              'Network',
      'outputConsole.tab.errors':               'Errori',
      'outputConsole.tab.datalayer':            'DataLayer',
      'outputConsole.net.field.callTime':       'Ora chiamata',
      'outputConsole.net.field.status':         'Stato',
      'outputConsole.net.field.type':           'Tipo',
      'outputConsole.net.field.method':         'Metodo',
      'outputConsole.net.field.domain':         'Dominio',
      'outputConsole.net.field.path':           'Percorso',
      'outputConsole.net.field.remote':         'Indirizzo remoto',
      'outputConsole.net.field.initiator':      'Iniziatore',
      'outputConsole.net.field.time':           'Tempo',
      'outputConsole.net.field.size':           'Dimensione',
      'outputConsole.net.field.url':            'URL',
      'outputConsole.net.field.error':          'Errore',
      'outputConsole.net.status.err':           'Errori/Altri',
      'outputConsole.filter.all':               'tutti',
      'outputConsole.filter.none':              'nessuno',
      'outputConsole.net.domainFilter':         'filtra per dominio…',
      'outputConsole.net.domainsInclude':       'Domini+',
      'outputConsole.net.domainsExclude':       'Domini-',
      'outputConsole.dl.newPushes':             'Nuovi push',
      'outputConsole.dl.currentState':          'Stato attuale',
      'outputConsole.dl.expandAll':             'espandi tutto',
      'outputConsole.dl.collapseAll':           'comprimi tutto',
      'outputConsole.net.size':                 'Dimensione',
      'outputConsole.net.time':                 'Tempo',
      'outputConsole.err.field.type':           'Tipo',
      'outputConsole.err.field.location':       'Posizione',
      'outputConsole.err.field.stack':          'Stack',
      'outputConsole.apiHelp.title': 'JustZix — scorciatoie e finestre sulla pagina',
      'outputConsole.apiHelp.sec.global': 'Scorciatoie globali — funzionano su ogni pagina',
      'outputConsole.apiHelp.g.tempCss': 'Finestra TEMP CSS — apri / chiudi',
      'outputConsole.apiHelp.g.tempJs': 'Finestra TEMP JS — apri / chiudi',
      'outputConsole.apiHelp.g.tempJsc': 'Console TEMP JS — apri / chiudi',
      'outputConsole.apiHelp.g.tempOut': 'Console TEMP Output — apri / chiudi',
      'outputConsole.apiHelp.g.ai': 'AI Helper — apri / chiudi',
      'outputConsole.apiHelp.g.edit': 'Modalità modifica barra azioni — attiva / disattiva',
      'outputConsole.apiHelp.sec.window': 'Dentro una finestra — quando ha il focus',
      'outputConsole.apiHelp.w.run': 'Esegui il codice / invia il messaggio — pannello JS, Console JS, AI Helper',
      'outputConsole.apiHelp.w.history': 'Cronologia dei comandi digitati — Console JS',
      'outputConsole.apiHelp.w.clearConsole': 'Svuota l\'output — Console JS',
      'outputConsole.apiHelp.w.escSearch': 'Svuota il campo di ricerca — Console Output',
      'outputConsole.apiHelp.sec.output': 'Console Output',
      'outputConsole.apiHelp.o.clear': 'Svuota la scheda attiva di ogni Console Output',
      'outputConsole.apiHelp.o.help': 'Mostra / nascondi questa guida',
      'outputConsole.apiHelp.o.tabs': '6 schede: All, JZ, Console, Network, Errors, DataLayer — ognuna con il proprio set di filtri.',
      'outputConsole.apiHelp.o.network': 'Network: Alt+clic su una riga aggiunge il suo dominio al filtro di inclusione, Alt+Shift+clic al filtro di esclusione.',
      'outputConsole.apiHelp.o.datalayer': 'DataLayer: trascina la barra tra le colonne per ridimensionarle.',
      'outputConsole.apiHelp.sec.mouse': 'Finestre sulla pagina — con il mouse',
      'outputConsole.apiHelp.m.drag': 'Trascina una finestra dalla barra dell\'intestazione per spostarla.',
      'outputConsole.apiHelp.m.resize': 'Ridimensiona una finestra tirando una delle sue 8 maniglie su bordi e angoli.',
      'outputConsole.apiHelp.m.snap': 'Avvicina una finestra a un\'altra — si agganciano magneticamente in un gruppo che si sposta insieme.',
      'outputConsole.apiHelp.m.context': 'Un clic destro sulla barra dell\'intestazione apre un menu: chiudi finestra, scollega, font, dimensione testo.',
      'outputConsole.apiHelp.m.tempDbl': 'Un doppio clic sulla barra dell\'intestazione di una finestra TEMP commuta tra la dimensione predefinita e quella memorizzata.',
      'outputConsole.apiHelp.m.scroll': 'Lo scorrimento con la rotellina in una finestra scorre solo il suo contenuto, non la pagina sottostante.',
      'outputConsole.apiHelp.sec.editmode': 'Modalità modifica barra — dopo Ctrl+Alt+S',
      'outputConsole.apiHelp.em.undo': 'Annulla l\'ultima modifica',
      'outputConsole.apiHelp.em.delete': 'Elimina le etichette selezionate',
      'outputConsole.apiHelp.em.deselect': 'Deseleziona tutti gli elementi',
      'outputConsole.apiHelp.em.move': 'Trascina gli elementi selezionati con il mouse — più elementi si spostano insieme.',
      'outputConsole.apiHelp.em.select': 'Un clic seleziona un elemento, Ctrl+clic lo aggiunge o rimuove dalla selezione, Shift+trascinamento su un\'area vuota seleziona con un rettangolo.',
      'outputConsole.apiHelp.em.label': 'Il pulsante „+ Etichetta" aggiunge un\'etichetta di testo statica; doppio clic su un\'etichetta apre il suo editor.',
      'outputConsole.apiHelp.sec.logging': 'Logging nella Console Output',
      'outputConsole.apiHelp.jzApi': 'Logger proprio (kind=jz)',
      'outputConsole.apiHelp.aliases': 'Alias: window.__JUSTZIX__ (sempre), window.JZ (se non occupato dalla pagina).',
      'outputConsole.apiHelp.consoleApi': 'console.* standard (kind=console, catturato automaticamente)',
      'outputConsole.apiHelp.consoleNote': 'Ogni console.* della pagina passa alla console originale (i DevTools continuano a funzionare) e viene inviato alla Console Output.',
      'outputConsole.apiHelp.footer': 'Manuale completo (8 lingue): pannello JustZix → Guida.',
      // === Onboarding / tutorial ===
      'content.exec.cspBlocked': 'Questa pagina blocca l\'esecuzione degli script (CSP). Abilita «Script utente» per JustZix nelle impostazioni del browser.',
      'onboard.ui.back': 'Indietro',
      'onboard.ui.next': 'Avanti',
      'onboard.ui.finish': 'Fine',
      'onboard.ui.copy': 'Copia',
      'onboard.ui.copied': 'Copiato',
      'onboard.ui.hint': 'Esegui questa azione per continuare',
      'onboard.ui.close': 'Chiudi',
      'onboard.front.title': 'Tutorial front-end',
      'onboard.front.lead': 'Scopri gli strumenti JustZix disponibili direttamente nella pagina — il pulsante flottante, il pannello, le finestre di codice e le barre delle azioni.',
      'onboard.front.titleFirst': 'JustZix nella pagina',
      'onboard.front.leadFirst': 'JustZix funziona anche direttamente nella pagina. Questo tutorial mostra il pulsante flottante, le finestre CSS/JS e le barre delle azioni — passo dopo passo.',
      'onboard.front.empty': 'I tutorial front-end arriveranno presto.',
      'onboard.lr.titleFirst': 'Benvenuto in JustZix',
      'onboard.lr.title': 'Centro tutorial',
      'onboard.lr.leadFirst': 'Prima volta qui? Il tutorial interattivo ti guida nella creazione di una soluzione completa e funzionante — passo dopo passo, direttamente nel pannello.',
      'onboard.lr.lead': 'Avvia la guida interattiva — ti accompagna passo dopo passo nella creazione di una soluzione completa.',
      'onboard.lr.scenarios': 'Tutorial',
      'onboard.lr.start': 'Inizia',
      'onboard.lr.later': 'Forse più tardi',
      'onboard.lr.noAuto': 'Non mostrare all\'avvio',
      'onboard.proj.name': 'Il tuo primo progetto',
      'onboard.proj.desc': 'Da zero a una soluzione funzionante: una cartella, una barra delle azioni, un gruppo, un set di regole con CSS e JS e un pulsante che commuta l\'effetto.',
      'onboard.proj.meta': '19 passi · ~3 min',
      'onboard.proj.s1.title': 'Benvenuto in JustZix',
      'onboard.proj.s1.body': 'JustZix inietta il tuo CSS e JavaScript nelle pagine scelte. Ora costruiremo una soluzione completa con un pulsante funzionante — passo dopo passo. Richiede circa 3 minuti.',
      'onboard.proj.s2.title': 'Aggiungi una cartella',
      'onboard.proj.s2.body': 'Una cartella raggruppa le regole di un sito. Clicca il pulsante <code>+</code> evidenziato nel pannello laterale.',
      'onboard.proj.s3.title': 'Nome della cartella',
      'onboard.proj.s3.body': 'Inserisci un nome, es. <code>Il mio primo progetto</code> — è un\'etichetta solo per te.',
      'onboard.proj.s4.title': 'Schema URL',
      'onboard.proj.s4.body': 'Decide su quali pagine funziona la cartella. Digita un indirizzo con un asterisco e premi Invio, es. <code>https://example.com/*</code>. Per esercitarti su qualsiasi pagina, digita solo <code>*</code>.',
      'onboard.proj.s5.title': 'Etichetta del pulsante',
      'onboard.proj.s5.body': 'Massimo 3 lettere — appariranno sul pulsante flottante JustZix visibile sulla pagina.',
      'onboard.proj.s6.title': 'Barra delle azioni',
      'onboard.proj.s6.body': 'Una barra delle azioni è un pannello con pulsanti interattivi visibile sulla pagina. Creiamone prima una — clicca il pulsante delle barre delle azioni evidenziato.',
      'onboard.proj.s7.title': 'Aggiungi una barra',
      'onboard.proj.s7.body': 'Clicca per creare una nuova barra delle azioni in questa cartella.',
      'onboard.proj.s8.title': 'Chiudi il gestore',
      'onboard.proj.s8.body': 'La barra è pronta. Chiudi la finestra del gestore delle barre per tornare alla cartella.',
      'onboard.proj.s9.title': 'Apri il gruppo',
      'onboard.proj.s9.body': 'Creando la cartella, JustZix ha già aggiunto un primo gruppo. Nel pannello laterale espandi la cartella e clicca quel gruppo per aprirlo, poi clicca "Avanti".',
      'onboard.proj.s10.title': 'Aggiungi un set di regole',
      'onboard.proj.s10.body': 'Un set di regole è la regola vera e propria — una coppia di codice CSS e JavaScript. Clicca il pulsante di aggiunta set di regole evidenziato.',
      'onboard.proj.s11.title': 'Apri il set di regole',
      'onboard.proj.s11.body': 'Il nuovo set di regole è apparso nell\'albero sotto il gruppo. Cliccalo per aprire l\'editor del set di regole, poi clicca "Avanti".',
      'onboard.proj.s12.title': 'Codice CSS',
      'onboard.proj.s12.body': 'La scheda CSS è già attiva. Incolla il codice qui sotto — mette in scala di grigi tutta la pagina e prepara l\'interruttore dell\'effetto.',
      'onboard.proj.s13.title': 'Scheda JavaScript',
      'onboard.proj.s13.body': 'Clicca la scheda <code>JavaScript</code> per aggiungere codice JS al set di regole.',
      'onboard.proj.s14.title': 'Codice JavaScript',
      'onboard.proj.s14.body': 'Il codice JS viene eseguito all\'apertura della pagina. Incolla il codice qui sotto — mostra un messaggio che conferma il funzionamento di JustZix. Il campo ha la validazione della sintassi.',
      'onboard.proj.s15.title': 'Scheda Azioni',
      'onboard.proj.s15.body': 'Ora aggiungeremo un pulsante interattivo. Clicca la scheda delle azioni nell\'editor del set di regole.',
      'onboard.proj.s16.title': 'Aggiungi un pulsante',
      'onboard.proj.s16.body': 'Clicca per aggiungere un elemento azione. Per impostazione predefinita crea un pulsante che esegue codice su richiesta.',
      'onboard.proj.s17.title': 'Codice del pulsante',
      'onboard.proj.s17.body': 'Ogni pulsante ha il proprio codice eseguito al clic sulla pagina. Clicca il campo del codice del nuovo pulsante.',
      'onboard.proj.s18.title': 'Codice dell\'interruttore',
      'onboard.proj.s18.body': 'Incolla il codice che attiva e disattiva la scala di grigi impostata in CSS. Poi salva le modifiche nella finestra dell\'editor del codice.',
      'onboard.proj.s19.title': 'Fatto!',
      'onboard.proj.s19.body': 'Hai una soluzione completa: una cartella, una barra delle azioni, un gruppo, un set di regole con codice CSS e JS e un pulsante funzionante. Apri una pagina che corrisponde allo schema URL — vedrai il messaggio, la scala di grigi e il pulsante che la commuta. Puoi riavviare il tutorial dal menu delle impostazioni.',
      'panes.contextMenu.font':                 'Carattere',
      'panes.contextMenu.size':                 'Dimensione',
      'panes.contextMenu.defaultFont':          'Predefinito',
      'modal.confirmDismissDirty':              'Hai modifiche non salvate. Chiudere e perderle?',
      'modal.confirmSaveBeforeClose':           'Hai modifiche non salvate.\n\nClicca OK per salvare e chiudere.\nClicca Annulla per passare alla conferma di scarto.',
      'modal.confirmDiscardChanges':            'Chiudere SENZA salvare? Le modifiche andranno perse.\n\nClicca OK per scartare le modifiche.\nClicca Annulla per continuare la modifica.',
      'actionBars.moveLocked':                  'Spostamento impossibile — la barra ha elementi che si troverebbero fuori dal nuovo ambito. Riassegnali prima.',
      'action.bar.noneOption':                  '— nessuna barra —',
      'action.bar.unnamed':                     '(senza nome)',
      'action.bar.selectTitle':                 'Assegna questa azione a una barra. Le barre vengono ereditate verso il basso (cartella → gruppo → regola).',
      'action.bar.noBarsHint':                  'Nessuna barra delle azioni nell\'albero del set di regole — aggiungi una barra tramite il pulsante "Barre delle azioni" in cartella/gruppo/regola.',

      // === Typed actions (since v2.12.x) ===
      'action.color.title':                     'Colore di sfondo (pulsante/select/input/slider). Clic = selettore, oppure digita l\'hex accanto.',
      'action.colorText.title':                 'Colore del testo (e riempimento della barra per SLIDER). Bianco se non impostato.',
      'action.colorHex.title':                  'Codice hex — copia (Ctrl+C) o incolla (Ctrl+V) come testo semplice. Accetta #RGB o #RRGGBB.',
      'action.type.title':                      'Tipo di elemento (pulsante, menu a discesa SELECT, …)',
      'action.type.button':                     'Pulsante',
      'action.type.select':                     'Menu a discesa (SELECT)',
      'action.type.input':                      'Campo di testo (INPUT)',
      'action.type.slider':                     'Cursore (SLIDER)',
      'action.type.textarea':                   'Campo multilinea (TEXTAREA)',
      'action.type.toggle3':                    'Interruttore a 3 stati (TOGGLE3)',

      'action.memory.label':                    'Ricorda il valore:',
      'action.memory.none':                     'Non ricordare (si reimposta al ricaricamento)',
      'action.memory.page':                     'Per pagina (URL)',
      'action.memory.host':                     'Per dominio',
      'action.memory.ruleSet':                  'All\'interno del set di regole (tutti gli URL su cui viene eseguito)',
      'action.memory.global':                   'Globalmente (tutte le pagine, condiviso)',
      'action.input.middleBtn':                 'INPUT · codice :code',
      'action.slider.middleBtn':                'SLIDER · :min–:max passo :step :unit · codice :code',
      'action.textarea.middleBtn':              'TEXTAREA · :rows righe · codice :code',
      'action.toggle3.middleBtn':               'TOGGLE3 · :filled/3 stati · codice :code',
      'action.toggle3.defaultState1':           'OFF',
      'action.toggle3.defaultState2':           'MID',
      'action.toggle3.defaultState3':           'ON',
      'action.toggle3.stateLabel.placeholder':  'etichetta (max 5)',
      'action.toggle3.stateValue.placeholder':  'valore (a JS)',
      'action.toggle3.defaultStateTitle':       'Stato predefinito (quando non c\'è memoria)',

      'action.select.middleBtn.staticEmpty':    'SELECT statico (nessuna opzione)',
      'action.select.middleBtn.static':         'SELECT statico · :count opzioni · codice :code',
      'action.select.middleBtn.js':             'SELECT js · codice :code',
      'action.select.empty':                    'Nessuna opzione. Aggiungi la prima con il pulsante qui sotto.',
      'action.select.untitled':                 '(senza titolo)',
      'action.select.optionNamePlaceholder':    'Nome (visibile)',
      'action.select.optionValuePlaceholder':   'valore (passato a JS)',
      'action.select.deleteOption':             'Elimina questa opzione',

      // === Modal — action code ===
      'modal.actionCode.titlePrefix':           'Codice del pulsante di azione:',
      'modal.actionCode.body':                  'Codice JavaScript eseguito quando il pulsante di azione viene cliccato nel pannello fluttuante. Viene eseguito nel contesto della pagina.',
      'modal.actionCode.cancel':                'Annulla',
      'modal.actionCode.save':                  'Salva',

      // === Modal — INPUT editor (since v2.13.0) ===
      'modal.inputEditor.titlePrefix':          'Editor INPUT:',
      'modal.inputEditor.body':                 'Campo di testo con codice JS facoltativo attivato alla modifica (Invio/perdita di focus). Il valore viene ricordato in base all\'ambito selezionato.',
      'modal.inputEditor.placeholder':          'Segnaposto:',
      'modal.inputEditor.defaultValue':         'Valore predefinito:',
      'modal.inputEditor.colorPlaceholder':     'Colore del segnaposto:',
      'modal.inputEditor.codeLabel':            'Codice JS (alla modifica):',
      'modal.inputEditor.hint':                 'Disponibile nel codice: <code>value</code> (stringa dall\'input), <code>$el</code> (HTMLInputElement), <code>$action</code> (id, label).',
      'modal.inputEditor.close':                'Chiudi',
      'modal.colorClear':                       'Ripristina il colore predefinito',
      'modal.sliderEditor.colorLabel':          'Colore dell\'etichetta (sinistra):',
      'modal.sliderEditor.colorValue':          'Colore del valore (destra):',
      'modal.selectOptions.appearance.title':   'Aspetto del menu a discesa:',
      'modal.selectOptions.appearance.hint':    'Per la <strong>variante js</strong>: lo sfondo del menu e il colore delle opzioni funzionano in tutti i browser. Gli sfondi di opzione selezionata e passaggio del mouse sono specifici del browser (dipendono dall\'OS) — per la personalizzazione completa usa la variante <em>statica</em>.',
      'modal.selectOptions.colorDropdownBg':    'Sfondo del menu a discesa:',
      'modal.selectOptions.colorOption':        'Colore del testo delle opzioni:',
      'modal.selectOptions.colorSelected':      'Sfondo dell\'opzione selezionata:',
      'modal.selectOptions.colorHover':         'Sfondo al passaggio del mouse:',
      'modal.sliderEditor.titlePrefix':         'Editor SLIDER:',
      'modal.sliderEditor.body':                'Cursore con codice JS facoltativo attivato dopo che l\'utente rilascia il cursore (`change`). Il valore viene passato come Number.',
      'modal.sliderEditor.min':                 'Minimo:',
      'modal.sliderEditor.max':                 'Massimo:',
      'modal.sliderEditor.step':                'Passo:',
      'modal.sliderEditor.defaultValue':        'Valore predefinito:',
      'modal.sliderEditor.unit':                'Unità (facoltativa):',
      'modal.sliderEditor.codeLabel':           'Codice JS (alla modifica):',
      'modal.sliderEditor.hint':                'Disponibile nel codice: <code>value</code> (Number), <code>$el</code> (HTMLInputElement type=range), <code>$action</code> (id, label).',
      'modal.sliderEditor.close':               'Chiudi',
      'modal.sliderEditor.errMaxGtMin':         'Il massimo deve essere maggiore del minimo.',
      'modal.sliderEditor.errStepPositive':     'Il passo deve essere positivo.',
      'modal.textareaEditor.titlePrefix':       'Editor TEXTAREA:',
      'modal.textareaEditor.body':              'Campo di testo multilinea con codice JS facoltativo su <code>change</code> (perdita di focus). <strong>Invio</strong> nell\'area di testo è un vero a capo — NON attiva il codice (diversamente da INPUT).',
      'modal.textareaEditor.placeholder':       'Segnaposto:',
      'modal.textareaEditor.defaultValue':      'Valore predefinito:',
      'modal.textareaEditor.rows':              'Numero di righe:',
      'modal.textareaEditor.colorPlaceholder':  'Colore del segnaposto:',
      'modal.textareaEditor.codeLabel':         'Codice JS (alla modifica/perdita di focus):',
      'modal.textareaEditor.hint':              'Disponibile nel codice: <code>value</code> (stringa dall\'area di testo, può contenere <code>\\n</code>), <code>$el</code> (HTMLTextAreaElement), <code>$action</code> (id, label).',
      'modal.textareaEditor.close':             'Chiudi',

      // === Modal — TOGGLE3 editor (od v2.13.25) ===
      'modal.toggle3Editor.titlePrefix':        'Editor TOGGLE3:',
      'modal.toggle3Editor.body':               'Controllo segmentato a 3 stati. Cliccare su uno stato inattivo lo attiva ed esegue il codice JS con il <code>value</code> dello stato scelto. La memoria conserva l\'indice corrente (0-2) per azione.',
      'modal.toggle3Editor.statesTitle':        'Stati (esattamente 3, radio = predefinito):',
      'modal.toggle3Editor.colorBg':            'Colore di sfondo:',
      'modal.toggle3Editor.colorHover':         'Colore al passaggio del mouse:',
      'modal.toggle3Editor.colorInactiveText':  'Colore del testo inattivo:',
      'modal.toggle3Editor.codeLabel':          'Codice JS (al cambio di stato):',
      'modal.toggle3Editor.hint':               'Disponibile nel codice: <code>value</code> (state.value, ripiega su state.label), <code>stateIdx</code> (0-2), <code>stateLabel</code>, <code>$el</code> (div contenitore), <code>$action</code>.',
      'modal.toggle3Editor.close':              'Chiudi',

      // === Modal — SELECT editor (since v2.12.1, redesign v2.12.2 — variants) ===
      'modal.selectOptions.titlePrefix':        'Editor SELECT:',
      'modal.selectOptions.idLabel':            'ID azione:',
      'modal.selectOptions.selectorLabel':      'Selettore JS:',
      'modal.selectOptions.copyId':             'Copia ID',
      'modal.selectOptions.copySelector':       'Copia il selettore JS',
      'modal.selectOptions.variant.static':     'Opzioni statiche + un solo codice JS',
      'modal.selectOptions.variant.js':         'Solo codice JS (gestisce il menu a discesa)',
      'modal.selectOptions.static.body':        'Ogni opzione: <strong>nome</strong> (visibile nel menu) + <strong>valore</strong> (passato al codice come variabile <code>value</code>). Un codice condiviso viene eseguito alla modifica.',
      'modal.selectOptions.static.hint':        'Disponibile nel codice: <code>value</code> (stringa), <code>$el</code> (elemento select), <code>$action</code> (id, label).',
      'modal.selectOptions.staticCodeLabel':    'Codice JS (condiviso):',
      'modal.selectOptions.js.body':            'Il tuo codice JS viene eseguito una volta dopo l\'inserimento del menu a discesa nella pagina. Riceve il riferimento <code>$el</code> a un <code>&lt;select&gt;</code> vuoto — popoli le opzioni e gestisci <code>change</code> tu stesso.',
      'modal.selectOptions.js.hint':            'Disponibile: <code>$el</code> (HTMLSelectElement), <code>$action</code> (id, label).',
      'modal.selectOptions.jsCodeLabel':        'Codice JS:',
      'modal.selectOptions.addOption':          '+ Aggiungi opzione',
      'modal.selectOptions.close':              'Chiudi',

      // === Modal — sync conflict ===
      'modal.syncConflict.title':               'Conflitto di sincronizzazione',
      'modal.syncConflict.description':         'Hai effettuato l\'accesso a un account che ha già dati nel cloud, e anche questo dispositivo ha una libreria locale. Scegli cosa fare — l\'operazione è irreversibile localmente, ma i dati locali possono essere esportati prima di confermare.',
      'modal.syncConflict.localLabel':          'Su questo dispositivo in locale:',
      'modal.syncConflict.serverLabel':         'Sull\'account cloud:',
      'modal.syncConflict.useServer.title':     'Usa i dati dell\'account cloud',
      'modal.syncConflict.useServer.body':      'Le cartelle/gruppi/regole locali verranno sostituiti con quelli dell\'account. Scegli questo se l\'account ha dati più recenti e accurati di questo dispositivo.',
      'modal.syncConflict.sendMine.title':      'Invia i miei dati locali all\'account',
      'modal.syncConflict.sendMine.body':       'Lo stato dell\'account verrà sostituito con quello che hai su questo dispositivo. Scegli questo se questo dispositivo ha dati più recenti e accurati.',
      'modal.syncConflict.merge.title':         'Combina entrambi gli insiemi',
      'modal.syncConflict.merge.body':          'Le entità locali e cloud verranno mantenute insieme. Se lo stesso elemento esiste in entrambi i posti (stesso ID), vince la versione dell\'account. Gli elementi solo locali verranno inviati all\'account alla prossima sincro.',
      'modal.syncConflict.exportLocal':         'Esporta prima il locale',
      'modal.syncConflict.cancel':              'Annulla l\'accesso (disconnetti)',

      // === Modal — share import ===
      'modal.shareImport.title':                'Importa da un link JustZix',
      'modal.shareImport.body':                 'Incolla il link completo o solo il token <strong>JZS-XXXX-XXXX-XXXX-XXXX</strong>:',
      'modal.shareImport.placeholder':          'https://www.justzix.com/s/JZS-XXXX-XXXX-XXXX-XXXX',
      'modal.shareImport.cancel':               'Annulla',
      'modal.shareImport.fetch':                'Recupera',
      'modal.shareImport.preview.title':        'Anteprima dell\'export condiviso',
      'modal.shareImport.jsWarn':               '<strong>⚠ AVVISO:</strong> L\'export contiene codice JavaScript. Il codice verrà eseguito sulle pagine corrispondenti alle regole — controlla cosa fa prima di importare.',
      'modal.shareImport.showCode':             'Mostra il codice JavaScript',
      'modal.shareImport.back':                 '← Indietro',
      'modal.shareImport.confirm':              'Importa',

      // === Modal — my shares ===
      'modal.shares.title':                     'I miei link condivisi',
      'modal.shares.body':                      'Link pubblici a tempo limitato verso gli export. Il backend conserva i dati fino alla scadenza, poi li elimina.',
      'modal.shares.howTo':                     'Per creare un nuovo link — apri un export (completo / cartella / gruppo / set di regole) e clicca su <strong>"Condividi URL"</strong>.',
      'modal.shares.refresh':                   'Aggiorna',
      'modal.shares.close':                     'Chiudi',

      // === Modal — share create ===
      'modal.shareCreate.title':                'Condividi l\'export tramite link',
      'modal.shareCreate.activeFor':            'Il link sarà attivo per:',
      'modal.shareCreate.ttl.1h':               '1 ora',
      'modal.shareCreate.ttl.6h':               '6 ore',
      'modal.shareCreate.ttl.12h':              '12 ore',
      'modal.shareCreate.ttl.24h':              '24 ore',
      'modal.shareCreate.ttl.default':          '(predefinito)',
      'modal.shareCreate.ttl.48h':              '48 ore',
      'modal.shareCreate.expireNote':           'Dopo la scadenza il link smetterà di funzionare. Puoi revocarlo prima da "Le mie condivisioni".',
      'modal.shareCreate.cancel':               'Annulla',
      'modal.shareCreate.generate':             'Genera il link',
      'modal.shareCreate.result.title':         'Link generato',
      'modal.shareCreate.result.urlLabel':      'Invia questo link al destinatario:',
      'modal.shareCreate.result.copyUrl':       'Copia il link',
      'modal.shareCreate.result.tokenLabel':    'O solo il token (da incollare in "Importa da URL"):',
      'modal.shareCreate.result.copyToken':     'Copia il token',
      'modal.shareCreate.result.goToMine':      'Vedi "Le mie condivisioni"',
      'modal.shareCreate.result.close':         'Chiudi',

      // === Modal — sync result ===
      'modal.syncResult.title':                 'Risultato della sincronizzazione',
      'modal.syncResult.close':                 'OK, ho capito',

      // === Modal — cloud sync ===
      'modal.sync.title':                       'Sincronizzazione cloud',
      'modal.sync.signedOut.tab.register':      'Primo dispositivo',
      'modal.sync.signedOut.tab.login':         'Ho una chiave',
      'modal.sync.signedOut.tab.recover':       'Ho perso la chiave',
      'modal.sync.signedOut.register.body':     'Genera una nuova chiave di sincronizzazione. <strong>Conservala in un luogo sicuro</strong> — il backend memorizza solo un hash. La chiave consente di accedere su un secondo dispositivo e vedere la stessa libreria.',
      'modal.sync.signedOut.deviceLabel':       'Etichetta del dispositivo (facoltativa):',
      'modal.sync.signedOut.deviceLabel.placeholder1': 'es. PC di lavoro',
      'modal.sync.signedOut.deviceLabel.placeholder2': 'es. Portatile',
      'modal.sync.signedOut.yourKey':           'La tua chiave di sincronizzazione:',
      'modal.sync.signedOut.regenerate':        'Generane una nuova',
      'modal.sync.signedOut.copyClipboard':     'Copia negli appunti',
      'modal.sync.signedOut.warnSave':          '⚠ <strong>Copia e salva la chiave ora.</strong> La chiave sarà disponibile anche nella scheda "Chiave" dopo la registrazione — ma è meglio conservarne una copia in un gestore di password.',
      'modal.sync.signedOut.login.body':        'Incolla la chiave che hai visto durante la registrazione del primo dispositivo. Formato: <code>SYNC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX</code>.',
      'modal.sync.signedOut.keyLabel':          'Chiave di sincronizzazione:',
      'modal.sync.signedOut.recover.body':      'Se sull\'account è stata impostata un\'email <strong>verificata</strong> — inseriscila e invia un link di recupero. Dopo aver cliccato sul link nell\'email riceverai una <strong>nuova chiave</strong> (monouso). Tutte le sessioni attive verranno disconnesse.',
      'modal.sync.signedOut.email':             'Email dell\'account:',
      'modal.sync.signedOut.email.placeholder': 'qualcuno@example.com',
      'modal.sync.signedOut.cancel':            'Annulla',
      'modal.sync.signedOut.regSubmit':         'Registra il dispositivo',
      'modal.sync.signedOut.loginSubmit':       'Accedi',
      'modal.sync.signedOut.recoverSubmit':     'Invia il link di recupero',
      'modal.sync.signedIn.tab.status':         'Stato',
      'modal.sync.signedIn.tab.key':            'Chiave di accesso',
      'modal.sync.signedIn.tab.backups':        'Backup',
      'modal.sync.signedIn.tab.email':          'Email e recupero',
      'modal.sync.signedIn.status.account':     'Account',
      'modal.sync.signedIn.status.pending':     'Modifiche in sospeso:',
      'modal.sync.signedIn.status.cloudLib':    'Libreria nel cloud',
      'modal.sync.signedIn.status.lastCursor':  'Ultimo pull (cursore):',
      'modal.sync.signedIn.status.syncNow':     'Sincronizza ora',
      'modal.sync.signedIn.status.snapshot':    'Snapshot completo',
      'modal.sync.signedIn.status.refresh':     'Aggiorna i dati dell\'account',
      'modal.sync.signedIn.status.reconcile':   'Risincronizzazione completa con l\'account',
      'modal.sync.signedIn.status.sessions':    'Sessioni attive (dispositivi)',
      'modal.sync.signedIn.status.history':     'Cronologia sincronizzazione',
      'modal.sync.history.empty':               'Nessuna sincronizzazione registrata.',
      'modal.sync.history.viewAll':             'Mostra cronologia completa',
      'modal.sync.history.changes':             'Modifiche:',
      'modal.sync.history.device':              'Dispositivo:',
      'modal.sync.history.fromDevices':         'Recuperato da:',
      'modal.sync.history.stateAfter':          'Stato dopo la sincronizzazione:',
      'modal.sync.history.noDetail':            'Nessun dettaglio delle modifiche (voce precedente).',
      'modal.sync.signedIn.key.body':           'Chiave di sincronizzazione per questo account. Inseriscila su un secondo dispositivo per sincronizzare la libreria.',
      'modal.sync.signedIn.key.yourKey':        'La tua chiave:',
      'modal.sync.signedIn.key.reveal':         'Mostra la chiave',
      'modal.sync.signedIn.key.copy':           'Copia negli appunti',
      'modal.sync.signedIn.key.forget':         'Rimuovi la chiave da questo dispositivo',
      'modal.sync.signedIn.key.warn':           '⚠ La chiave dà accesso completo alla libreria. Non mostrarla pubblicamente. Se non vuoi conservarla localmente — clicca su <strong>Rimuovi la chiave da questo dispositivo</strong> (conserva una copia in un gestore di password). L\'accesso continua a funzionare, ma la chiave non potrà essere visualizzata di nuovo.',
      'modal.sync.signedIn.key.noKey':          'La chiave non è memorizzata su questo dispositivo (accesso effettuato prima dell\'esistenza di questa funzione, o rimossa manualmente). Puoi recuperarla solo tramite email di recupero o se ne hai una copia altrove.',
      'modal.sync.signedIn.backups.hint':       'Backup cloud — il ripristino sostituisce l\'intera libreria.',
      'modal.sync.signedIn.backups.label':      'Etichetta del nuovo backup (facoltativa):',
      'modal.sync.signedIn.backups.placeholder':'es. Prima di una grande modifica',
      'modal.sync.signedIn.backups.create':     'Crea backup ora',
      'modal.sync.signedIn.backups.refresh':    'Aggiorna l\'elenco',
      'modal.sync.signedIn.email.body':         'L\'email abilita il <strong>recupero</strong> — se perdi la chiave, puoi ottenerne una nuova cliccando su un link inviato a quell\'indirizzo. Aggiungere un\'email richiede una verifica (link nell\'email).',
      'modal.sync.signedIn.email.add':          'Aggiungi email (recupero):',
      'modal.sync.signedIn.email.placeholder':  'qualcuno@example.com',
      'modal.sync.signedIn.email.submit':       'Invia il link di verifica',
      'modal.sync.signedIn.email.remove':       'Rimuovi l\'email',
      'modal.sync.signedIn.email.recoveryFlow': 'Procedura di recupero: su un altro dispositivo (o in una situazione senza chiave) seleziona la scheda <strong>"Ho perso la chiave"</strong> nel pannello di accesso, inserisci questa email — il backend invierà un link che genera una nuova chiave e invalida tutte le sessioni attive.',
      'modal.sync.signedIn.logout':             'Disconnetti questo dispositivo',
      'modal.sync.signedIn.close':              'Chiudi',

      // === Content script (floating button + actions panel) ===
      'content.float.titleGloballyOff':         'Estensione disattivata globalmente — clicca sull\'icona dell\'estensione per attivarla',
      'content.float.titleNormal':              'Clic sinistro: attiva/disattiva la cartella • Clic destro: mostra l\'elenco dei set di regole • Trascina per spostare',
      'content.panel.empty':                    'Nessun set di regole attivo',
      'content.panel.actionBarsTitle':          'Barre delle azioni',
      'content.panel.connectionsTitle':         'Connessioni',
      'content.actionsPanel.dragHandle':        'Trascina per spostare',
      'content.editMode.banner':                'Modalità modifica della barra — clic/Ctrl+clic seleziona, trascina sposta · Ctrl+Z annulla · Ctrl+Alt+S per uscire',
      'content.editMode.undoEmpty':             'Niente da annullare',
      'content.editMode.addLabel':              '+ Label',
      'content.editMode.resetBar':              'Reimposta la barra',
      'content.editMode.resetConfirm':          'Reimpostare la barra alla disposizione predefinita? Le posizioni, le dimensioni e le etichette personalizzate verranno rimosse.',
      'content.labelEditor.text':               'Testo:',
      'content.labelEditor.font':               'Carattere:',
      'content.labelEditor.size':               'Dimensione:',
      'content.labelEditor.color':              'Colore:',
      'content.labelEditor.delete':             'Elimina',
      'content.labelEditor.close':              'Chiudi',
      'content.labelContextMenu.delete':        'Elimina etichetta',
      'content.bar.hideUntilReload':            'Chiudi questa barra (fino al ricaricamento)',
      'content.bar.disconnectFrom':             'Disconnetti da: :name',
      'content.bar.disconnectAll':              'Disconnetti tutte le connessioni',
      'content.bar.fallbackName':               'Barra',
      'content.float.label':                    '⊕ Pulsante principale',
      'content.toast.noFoldersForPage':         'Nessuna cartella corrisponde a questa pagina',
      'content.toast.floatVisible':             'Pulsante fluttuante attivato',
      'content.toast.floatHidden':              'Pulsante fluttuante nascosto',

      // === Background (context menu + JS error logger) ===
      'background.contextMenu.toggleFloat':     'Attiva/disattiva il pulsante fluttuante per questa pagina',
      'background.contextMenu.editMode':        'Modalità modifica della barra delle azioni (attiva/disattiva)',
      'background.contextMenu.actionBars':      'Barre delle azioni',
      'background.contextMenu.actionBarsEmpty': '(nessuna barra per questa pagina)',
      'background.contextMenu.actionBarFallback': '(senza nome)',
      'background.contextMenu.cssPanes':         'Pannelli CSS',
      'background.contextMenu.cssPanesEmpty':    '(nessun pannello CSS per questa pagina)',
      'background.contextMenu.cssPaneFallback':  '(senza nome)',
      'background.contextMenu.jsPanes':          'Pannelli JS',
      'background.contextMenu.jsPanesEmpty':     '(nessun pannello JS per questa pagina)',
      'background.contextMenu.jsPaneFallback':   '(senza nome)',
      'background.contextMenu.jsConsoles':       'Console JS',
      'background.contextMenu.jsConsolesEmpty':  '(nessuna console per questa pagina)',
      'background.contextMenu.jsConsoleFallback':'(senza nome)',
      'background.contextMenu.outputConsoles':       'Console di output',
      'background.contextMenu.outputConsolesEmpty':  '(nessuna console di output per questa pagina)',
      'background.contextMenu.outputConsoleFallback':'(senza nome)',
      'background.contextMenu.tempPanes':            'Finestra temporanea (TEMP)',
      'background.contextMenu.tempCss':              'Pannello CSS TEMP (Ctrl+Alt+G)',
      'background.contextMenu.tempJs':               'Pannello JS TEMP (Ctrl+Alt+H)',
      'background.contextMenu.tempJsConsole':        'Console JS TEMP (Ctrl+Alt+J)',
      'background.contextMenu.tempOutput':           'Console di output TEMP (Ctrl+Alt+K)',
      'pane.collapse.title':                      'Riduci la finestra alla barra del titolo',
      'pane.expand.title':                        'Espandi la finestra',
      'aiHelper.form.submit':                     'OK',
      'aiHelper.form.cancel':                     'Annulla',
      'aiHelper.form.yes':                        'Sì',
      'aiHelper.form.no':                         'No',
      'aiHelper.form.askTitle':                   'Domanda',
      'aiHelper.form.confirmTitle':               'Conferma',
      'aiHelper.form.updateConfirmTitle':               'Aggiornare?',
      'aiHelper.style.title':                           'Aspetto di AI Helper',
      'aiHelper.style.role.user':                       'I tuoi messaggi',
      'aiHelper.style.role.assistant':                  'Risposte dell’AI',
      'aiHelper.style.role.tool':                       'Risultati strumenti',
      'aiHelper.style.role.form':                       'Moduli',
      'aiHelper.style.font':                            'Carattere',
      'aiHelper.style.size':                            'Dimensione',
      'aiHelper.style.color':                           'Testo',
      'aiHelper.style.bg':                              'Sfondo',
      'aiHelper.style.reset':                           'Ripristina predefiniti',
      'aiHelper.style.resetAll':                        'Ripristina tutti i predefiniti',
      'aiHelper.confirm.update.folder':                 'Aggiornamento cartella',
      'aiHelper.confirm.update.group':                  'Aggiornamento gruppo',
      'aiHelper.confirm.update.rule':                   'Aggiornamento set',
      'aiHelper.confirm.update.bar':                    'Aggiornamento barra azioni',
      'aiHelper.confirm.update.action':                 'Aggiornamento azione',
      'aiHelper.confirm.update.cssPane':                'Aggiornamento finestra CSS',
      'aiHelper.confirm.update.jsPane':                 'Aggiornamento finestra JS',
      'aiHelper.confirm.update.jsConsole':             'Aggiornamento console JS',
      'aiHelper.confirm.update.outputConsole':         'Aggiornamento console di output',
      'aiHelper.form.createConfirmTitle':         'Creare?',
      'aiHelper.form.confirmed':                  'confermato',
      'aiHelper.form.cancelled':                  'annullato',
      'tempPane.close.title':                        'Chiudi la finestra temporanea (contenuto mantenuto per la sessione)',
      'tempPane.clear.title':                        'Cancella il contenuto della finestra',
      'background.jsError.prefix':              '[JustZix] Errore JS nel set di regole/azione ":id":\n',
      // === AI Helper (od v2.13.127) ===
      'settings.aiHelper.label': 'AI Helper',
      'settings.aiHelper.title': 'Configura le chiavi API per AI Helper',
      'modal.aiHelper.title': 'AI Helper',
      'modal.aiHelper.intro': 'Inserisci le chiavi API dei provider che vuoi usare. Ne basta una.',
      'modal.aiHelper.keyOpenai': 'Chiave API — OpenAI',
      'modal.aiHelper.keyAnthropic': 'Chiave API — Anthropic',
      'modal.aiHelper.keyGemini': 'Chiave API — Gemini',
      'modal.aiHelper.defaultProvider': 'Provider predefinito',
      'modal.aiHelper.defaultModel': 'Modello predefinito',
      'modal.aiHelper.modelAuto': '— auto (il più alto) —',
      'modal.aiHelper.loadModels': 'Carica i modelli',
      'modal.aiHelper.securityNote': '<strong>Sicurezza:</strong> per impostazione predefinita le chiavi API sono memorizzate solo nella memoria locale di questo browser (<code>chrome.storage.local</code>). Selezionando “sincronizza" accanto a una chiave, questa viene inviata tramite il cloud JustZix (lo stesso usato dall’estensione per sincronizzare gli altri dati) agli altri tuoi dispositivi. Le chiavi non selezionate non lasciano mai questo dispositivo.',
      'modal.aiHelper.syncKey':                'sincronizza',
      'modal.aiHelper.syncKeyTitle': 'Sincronizza questa chiave tra i tuoi dispositivi tramite il cloud JustZix',
      'modal.aiHelper.cancel': 'Annulla',
      'modal.aiHelper.save': 'Salva',
      'modal.aiHelper.needKey': 'Inserisci prima una chiave API.',
      'modal.aiHelper.loadingModels': 'Caricamento modelli…',
      'modal.aiHelper.errModels': 'Impossibile caricare i modelli',
      'modal.aiHelper.modelsLoaded': 'modelli disponibili',
      'aiHelper.win.title': 'AI Helper',
      'aiHelper.win.close': 'Chiudi',
      'aiHelper.win.provider': 'Provider',
      'aiHelper.win.model': 'Modello',
      'aiHelper.win.clear': 'Cancella la conversazione',
      'aiHelper.win.clearConfirm': 'Cancellare l’intera conversazione e ricominciare?',
      'aiHelper.win.inputPlaceholder': 'Chiedi aiuto con CSS, JS o un’azione…',
      'aiHelper.win.send': 'Invia (Ctrl+Invio)',
      'aiHelper.win.welcome': 'Ciao! Ti aiuto a preparare CSS, JavaScript o azioni per questa pagina. Descrivi cosa vuoi ottenere.',
      'aiHelper.win.you': 'Tu',
      'aiHelper.win.assistant': 'AI Helper',
      'aiHelper.win.thinking': 'Sto pensando…',
      'aiHelper.win.error': 'Errore di connessione all’API',
      'aiHelper.win.noKeys': 'Nessuna chiave API. Aggiungi una chiave in Impostazioni → AI Helper.',
      'aiHelper.win.noModel': 'Seleziona un modello.',
      'aiHelper.win.loadingModels': 'Caricamento…',
      'aiHelper.win.modelsError': 'Errore nel caricamento dei modelli',
      'background.contextMenu.aiHelper': 'AI Helper',
      'popup.aiHelper.label': 'AI Helper',
      'popup.aiHelper.on': 'Aperto — clicca per chiudere (Ctrl+Alt+\\)',
      'popup.aiHelper.off': 'Chiuso — clicca per aprire (Ctrl+Alt+\\)',
      'popup.aiHelper.unavailable': 'Non disponibile su questa pagina',
      // === AI Helper etap 2 (od v2.13.128) ===
      'aiHelper.win.maxRounds': 'Raggiunto il limite di passaggi degli strumenti per questo turno. Dimmi come continuare.',
      'aiHelper.win.copy':                        'Copia conversazione',
      'aiHelper.win.copy.all':                    'Copia conversazione intera',
      'aiHelper.win.copy.lastInput':              'Copia ultimo prompt API (sistema + cronologia + input)',
      'aiHelper.win.copyNoInput':                 'Niente da copiare',
      'aiHelper.copy.lastInput.prefix':           '[JustZix → inoltra all\'API] Copiato l\'INTERO ultimo prompt inviato al modello (sistema + cronologia + ultimo messaggio). Invia il blocco qui sotto all\'API come PRIMO messaggio user di una nuova conversazione. RIMUOVI questa intestazione prima dell\'invio.',
      'aiHelper.win.copied':                      'Copiato',
      'aiHelper.win.copyFailed':                  'Copia non riuscita — controlla i permessi degli appunti.',
      'aiHelper.win.font':                        'Carattere',
      'aiHelper.win.fontSize':                    'Dimensione',
      'aiHelper.win.defaultFont':                 'Predefinito',
      'aiHelper.win.stop':                        'Ferma l’agente',
      'aiHelper.win.stopped':                     'Interrotto dall’utente.',
      'aiHelper.win.ctxUsage':                    'Utilizzo del contesto del modello',
      'aiHelper.win.ctxUsageDetail':              'Contesto',
      'aiHelper.win.compact':                     'Compatta la conversazione (riassunto LLM)',
      'aiHelper.win.compactNoop':                 'Cronologia troppo breve — niente da compattare.',
      'aiHelper.win.compactNoProvider':           'Seleziona prima un provider e un modello.',
      'aiHelper.win.compactErr':                  'Errore di compattazione',
      'aiHelper.win.compactEmpty':                'Il modello ha restituito un risultato vuoto.',
      'aiHelper.win.compactedPrefix':             'Riassunti',
      'aiHelper.win.compactedSuffix':             'messaggi precedenti:',
      'aiHelper.win.telemetryTooltip':            'Tempi e token di questa sessione',
      'aiHelper.win.telemetryTimers':             'Tempo: dall’ultimo messaggio (u) / dall’ultimo strumento (t) / attività totale dell’agente (a)',
      'aiHelper.win.telemetryCurrent':            'Token nell’azione corrente (↑ input / ↓ output)',
      'aiHelper.win.telemetrySession':            'Token totali in questa sessione',
      'aiHelper.win.msgTelem':                    'Token di questo turno e tempo di risposta del modello',
      'aiHelper.win.toolDur':                     'Tempo di esecuzione dello strumento',
      'aiHelper.win.stepIdBadge':                 'Numero del passo nella cronologia',
      'aiHelper.form.freeText':                   'Oppure rispondi con testo',
      'aiHelper.form.freeTextOnly':               'La tua risposta',
      'aiHelper.form.freeTextPlaceholder':        'Scrivi la tua risposta…',
      'aiHelper.win.turnLimit.body':              'Raggiunto il limite di 10 passi degli strumenti in questo turno. L’agente è in pausa — controlla l’ultimo strumento e il suo risultato. Continuare da qui con il limite azzerato, o fermarsi?',
      'aiHelper.win.turnLimit.lastTool':          'Ultimo strumento',
      'aiHelper.win.turnLimit.lastResult':        'Risultato',
      'aiHelper.win.turnLimit.continue':          'Continua (azzera il limite)',
      'aiHelper.win.turnLimit.stop':              'Ferma',
      'aiHelper.win.turnLimit.stoppedByUser':     'Fermato.',
      'aiHelper.copy.title':                      'Conversazione JustZix AI Helper',
      'aiHelper.copy.toolResult':                 'Risultato dello strumento',
      'aiHelper.tool.label': 'strumento',
      'aiHelper.tool.rejected': 'L’utente ha rifiutato l’operazione.',
      'aiHelper.confirm.folder': 'AI Helper vuole creare una CARTELLA:',
      'aiHelper.confirm.group': 'AI Helper vuole creare un GRUPPO:',
      'aiHelper.confirm.rule': 'AI Helper vuole creare un SET DI REGOLE:',
      'aiHelper.confirm.create.cssPane': 'AI Helper vuole creare una FINESTRA CSS:',
      'aiHelper.confirm.create.jsPane': 'AI Helper vuole creare una FINESTRA JS:',
      'aiHelper.confirm.create.jsConsole': 'AI Helper vuole creare una CONSOLE JS:',
      'aiHelper.confirm.create.outputConsole': 'AI Helper vuole creare una CONSOLE DI OUTPUT:',
      'aiHelper.confirm.setPaneCode': 'AI Helper vuole inserire codice nella finestra ":name" (:id).\nLunghezza: attuale :old car., nuovo :new car.\nNOTA: il codice dei pannelli persistenti vive per scheda (sessionStorage), non tra le sessioni.',
      'aiHelper.confirm.bar': 'AI Helper vuole creare una BARRA AZIONI:',
      'aiHelper.confirm.action': 'AI Helper vuole creare un\'AZIONE:',
      // === Wyszukiwanie w kodzie (od v2.13.130) ===
      'codeSearch.placeholder': 'Cerca nel codice… (Ctrl+Alt+F)',
      'codeSearch.btn': 'Cerca',
      'codeSearch.prev': 'Risultato precedente (Ctrl+Alt+←)',
      'codeSearch.next': 'Risultato successivo (Ctrl+Alt+→)',
      'codeSearch.noMatch': 'Nessun risultato',
    },
    es: {
      'dashboard.title': 'Panel de control',
      'dashboard.subtitle': 'Un resumen de tu biblioteca de JustZix y atajos rápidos.',
      'dashboard.stats.title': 'Biblioteca',
      'dashboard.stats.folders': 'Carpetas',
      'dashboard.stats.groups': 'Grupos',
      'dashboard.stats.rules': 'Conjuntos de reglas',
      'dashboard.stats.actions': 'Acciones',
      'dashboard.stats.bars': 'Barras de acciones',
      'dashboard.stats.windows': 'Ventanas en la página',
      'dashboard.stats.withCss': 'Conjuntos con CSS',
      'dashboard.stats.withJs': 'Conjuntos con JS',
      'dashboard.stats.clicks': 'Clics en acciones',
      'dashboard.version.title': 'Versión de la extensión',
      'dashboard.version.checking': 'Comprobando…',
      'dashboard.version.current': 'Instalada: :version',
      'dashboard.version.upToDate': 'Estás al día',
      'dashboard.version.outdated': 'Hay una versión más reciente disponible: :version',
      'dashboard.storage.title': 'Almacenamiento',
      'dashboard.backup.title': 'Última copia de seguridad',
      'dashboard.backup.signIn': 'Inicia sesión en la sincronización en la nube para crear copias de seguridad en la nube.',
      'dashboard.backup.none': 'Aún no hay copias de seguridad en la nube.',
      'dashboard.backup.noLabel': '(sin etiqueta)',
      'dashboard.backup.last': 'Última copia de seguridad: :date',
      'dashboard.recent.title': 'Conjuntos de reglas editados recientemente',
      'dashboard.recent.empty': 'Aún no se ha editado ningún conjunto de reglas.',
      'dashboard.examples.title': 'Últimos ejemplos',
      'dashboard.posts.title': 'Del blog',
      'dashboard.feed.loading': 'Cargando…',
      'dashboard.feed.empty': 'No hay nada que mostrar.',
      'dashboard.home.title': 'Panel',
      'dashboard.usage.title': 'Estadísticas de uso',
      'dashboard.usage.cssApplied': 'CSS aplicado',
      'dashboard.usage.jsExecuted': 'JS ejecutado',
      'dashboard.usage.windowsShown': 'Ventanas mostradas',
      'dashboard.usage.empty': 'Sin datos todavía — usa la extensión en páginas web.',
      'dashboard.tip.title': 'Consejo',
      'dashboard.tip.prev': 'Consejo anterior',
      'dashboard.tip.next': 'Siguiente consejo',
      'dashboard.tip.manual': 'Abrir el manual completo',
      'tip.dash.1': 'Haz clic con el botón derecho en el botón flotante para abrir el panel con la lista de carpetas y conjuntos de reglas.',
      'tip.dash.2': 'Ctrl+Alt+S activa el modo de edición de la barra de acciones: los elementos se pueden posicionar y redimensionar libremente.',
      'tip.dash.3': 'Cada conjunto de reglas puede tener sus propios patrones de URL que lo limitan a subpáginas concretas.',
      'tip.dash.4': 'Ctrl+Alt+\\ abre el Asistente de IA: un asistente que genera CSS/JS para una tarea concreta.',
      'tip.dash.5': 'Los editores de código tienen una barra de búsqueda: Ctrl+Alt+F mueve el foco hacia ella.',
      'tip.dash.6': 'Al hacer clic con el botón izquierdo en el botón flotante se habilitan o deshabilitan todas las carpetas coincidentes a la vez.',
      'tip.dash.7': 'Un conjunto de reglas guarda en el historial sus últimas 5 versiones de CSS y JS: siempre puedes deshacer un cambio.',
      'tip.dash.8': 'Arrastra un conjunto de reglas sobre el nombre de otro grupo o carpeta para moverlo allí.',
      'tip.dash.9': 'Las ventanas TEMP (Ctrl+Alt+G/H/J/K) son paneles de CSS/JS y consolas desechables para pruebas rápidas.',
      'tip.dash.10': 'Las acciones SELECT, INPUT, SLIDER y TOGGLE3 recuerdan su valor: sobrevive a una recarga de la página.',
      'tip.dash.11': 'La sincronización en la nube mantiene tu biblioteca coherente en todos los dispositivos.',
      'tip.dash.12': 'En lugar de enviar un archivo JSON, comparte una carpeta mediante un enlace (Compartidos) con un tiempo de caducidad.',
      'tip.dash.13': 'El buscador de la barra lateral encuentra carpetas, grupos y conjuntos de reglas por su nombre.',
      'tip.dash.14': 'Añade etiquetas a los conjuntos de reglas y filtra tu biblioteca por etiqueta cuando crezcan los proyectos.',
      'tip.dash.15': 'Una etiqueta de tres letras y un color hacen destacar el botón flotante de una carpeta.',
      'tip.dash.16': 'El botón "URL" inserta la dirección de la pestaña activa directamente en un patrón de URL.',
      'tip.dash.17': 'JustZix valida sobre la marcha los patrones de URL que escribes.',
      'tip.dash.18': 'El editor avisa de errores de sintaxis CSS antes de que guardes un conjunto de reglas.',
      'tip.dash.19': 'Al importar, puedes previsualizar el código JavaScript antes de decidir añadirlo.',
      'tip.dash.20': 'Desactivar una carpeta apaga en cascada todos sus grupos y conjuntos de reglas.',
      'tip.dash.21': 'El filtro "Solo activos" oculta los elementos desactivados cuando la biblioteca crece.',
      'tip.dash.22': 'Arrastra una barra de acciones junto a otra para unirlas en un único bloque conectado.',
      'tip.dash.23': 'La ventana Consola JS funciona como un REPL: prueba fragmentos de código en vivo en la página.',
      'tip.dash.24': 'La ventana Output Console captura las entradas console.log de la página y las muestra en pantalla.',
      'tip.dash.25': 'Puedes exportar una sola carpeta, grupo o conjunto de reglas, no solo toda la biblioteca.',
      'tip.dash.26': 'JustZix añade sus propias entradas al menú contextual del navegador (clic derecho).',
      'tip.dash.27': 'Arrastra con el ratón el divisor entre los paneles del editor de código para ajustar la vista.',
      'tip.dash.28': 'La extensión te avisa por su cuenta cuando hay una versión más reciente disponible.',
      'tip.dash.29': 'Hacer clic en el icono de la extensión abre un widget para activar rápidamente carpetas y barras.',
      'tip.dash.30': 'El JavaScript de un conjunto de reglas puede disparar acciones de barra mediante la API de JustZix.',
      'tip.dash.31': 'Ctrl+Intro ejecuta el código o envía el mensaje — en el panel JS, la Consola JS y el AI Helper.',
      'tip.dash.32': 'En la Consola JS las flechas ↑ / ↓ recorren el historial de comandos escritos.',
      'tip.dash.33': 'Ctrl+L limpia la salida de la Consola JS sin cerrar la ventana.',
      'tip.dash.34': 'Ctrl+Shift+L limpia la pestaña activa de cada Consola Output abierta.',
      'tip.dash.35': 'Ctrl+Shift+H abre la ventana de ayuda de la API en la Consola Output.',
      'tip.dash.36': 'En el editor de código, Ctrl+Alt+→ y Ctrl+Alt+← saltan al resultado de búsqueda siguiente y anterior.',
      'tip.dash.37': 'La tecla Esc limpia el campo de búsqueda en la Consola Output.',
      'tip.dash.38': 'Un doble clic en la cabecera de una ventana TEMP la alterna entre el tamaño predeterminado y el último.',
      'tip.dash.39': 'Una ventana TEMP recuerda su último tamaño por tipo y lo conserva tras reiniciar el navegador.',
      'tip.dash.40': 'Desplazar con la rueda dentro de una ventana ya no desplaza la página de debajo.',
      'tip.dash.41': 'Cada ventana tiene 8 asas de redimensionado — 4 en las esquinas y 4 en los bordes.',
      'tip.dash.42': 'Un clic derecho en la cabecera de una ventana abre un menú con cerrar, desconectar, fuente y tamaño.',
      'tip.dash.43': 'Arrastra una ventana cerca de otra — se imantan en un único grupo.',
      'tip.dash.44': 'La Consola Output tiene 6 pestañas: Todo, JZ, Console, Network, Errors y DataLayer.',
      'tip.dash.45': 'En la pestaña Network, Alt+clic en una fila añade su dominio al filtro de inclusión.',
      'tip.dash.46': 'Alt+Shift+clic en una fila Network añade el dominio al filtro de exclusión.',
      'tip.dash.47': 'Cada pestaña de la Consola Output mantiene su propio conjunto de filtros independiente.',
      'tip.dash.48': 'En la pestaña DataLayer, arrastra la barra entre las columnas para redimensionarlas.',
      'tip.dash.49': 'En modo edición de barra, el botón „+ Etiqueta" añade rótulos de texto estáticos.',
      'tip.dash.50': 'Doble clic en una etiqueta de barra abre el editor de su texto, fuente, tamaño y color.',
      'tip.dash.51': 'En modo edición, Shift+arrastrar sobre un área vacía selecciona elementos con un rectángulo.',
      'tip.dash.52': 'En modo edición, Ctrl+clic añade o quita un elemento de la selección.',
      'tip.dash.53': 'Haz clic en la insignia numérica de un elemento de acción para copiar su ID estable al portapapeles.',
      'tip.dash.54': 'Un conjunto de reglas puede reejecutar su JS ante cambios de interfaz en páginas de tipo SPA.',
      'tip.dash.55': 'Además de BUTTON, SELECT e INPUT, las acciones tienen también los tipos SLIDER, TEXTAREA y TOGGLE3.',
      'tip.dash.56': 'El tema claro/oscuro y el idioma de la interfaz se sincronizan como un ajuste de cuenta compartido.',
      'tip.dash.57': 'Las estadísticas de uso del Panel suman los datos de todos los dispositivos emparejados.',
      'tip.dash.58': 'La barra bajo el editor de código se puede arrastrar hacia abajo para ampliar el área de trabajo.',
      'tip.dash.59': 'La barra vertical entre la barra lateral y el editor pliega el panel izquierdo para más espacio.',
      'tip.dash.60': 'El icono de inicio en la cabecera del Panel y en las migas de pan vuelve directo al Panel.',
      // === Popup ===
      'popup.toggle.on.label':       'Extensión activada',
      'popup.toggle.on.status':      'Inyección activa',
      'popup.toggle.off.label':      'Extensión desactivada',
      'popup.toggle.off.status':     'No se está inyectando CSS/JS',
      'popup.editMode.label':        'Modo de edición de la barra de acciones',
      'popup.editMode.on':           'Activado — clic para desactivar (Ctrl+Alt+S)',
      'popup.editMode.off':          'Desactivado — clic para activar (Ctrl+Alt+S)',
      'popup.editMode.unavailable':  'No disponible en esta página',
      'popup.empty.title':           'Ninguna carpeta de reglas coincide con esta página.',
      'popup.empty.hint':            'Añádelas en el modo de edición.',
      'popup.empty.folderRules':     'No hay reglas activas en esta carpeta',
      'popup.fallback.groupName':    'Grupo',
      'popup.fallback.unnamed':      '(sin nombre)',
      'popup.restore':               'Restaurar botón flotante (:count)',
      'popup.editBtn':               'Panel de edición',
      'popup.actionBarsTitle':       'Barras de acciones',
      'popup.fallback.barName':      'Barra',
      'popup.hideForPage':           'Ocultar el botón flotante en esta página',
      'popup.showForPage':           'Mostrar el botón flotante en esta página',

      // === Options — page ===
      'options.title':                          'JustZix — Editor',

      // === Options — sidebar ===
      'sidebar.folders.title':                  'Carpetas',
      'sidebar.addFolder.label':                'Añadir carpeta',
      'sidebar.search.placeholder':             'Buscar (nombre, etiqueta, contenido del código)',
      'sidebar.clearSearch.label':              'Borrar',
      'sidebar.activeOnly.label':               'Solo activas',
      'sidebar.activeOnly.title':               'Ocultar conjuntos de reglas independientes (marcados ○) — mostrar solo las reglas activas en su carpeta (●)',
      'sidebar.expandAll.label':                'Expandir todo el árbol',
      'sidebar.collapseAll.label':              'Contraer todo el árbol',
      'sidebar.toggle.title':                   'Contraer/expandir el panel de carpetas',

      // === Options — settings menu ===
      'settings.menu.label':                    'Ajustes',
      'settings.theme.label':                   'Tema',
      'settings.theme.title':                   'Elige un tema: claro / oscuro / auto',
      'settings.help.label':                    'Manual de usuario',
      'settings.tutorial.label':                'Tutorial',
      'settings.export.label':                  'Exportar ajustes',
      'settings.import.label':                  'Importar ajustes',
      'settings.importUrl.label':               'Importar desde URL',
      'settings.importUrl.title':               'Importar una exportación desde un enlace de JustZix (JZS-XXXX-XXXX-XXXX-XXXX)',
      'settings.sync.label':                    'Sincronización en la nube',
      'settings.shares.label':                  'Mis enlaces compartidos',
      'settings.shares.title':                  'Lista de enlaces públicos temporales emitidos para exportaciones',
      'settings.projectSite.label':             'Sitio web del proyecto',
      'settings.projectSite.title':             'Abrir justzix.com en una pestaña nueva',
      'settings.lang.label':                    'Idioma',
      'settings.lang.title':                    'Idioma de la interfaz de la extensión',
      'settings.lang.auto':                     'Automático',
      'settings.lang.autoTitle':                'Detectar desde el navegador (predeterminado)',
      'toast.langChanged':                      'Idioma establecido: :lang',
      'lang.name.auto':                         'Automático',
      'lang.name.pl':                           'Polaco',
      'lang.name.en':                           'Inglés',
      'settings.version.label':                 'Versión',
      'settings.version.checkTitle':            'Haz clic para buscar la última versión (recarga forzada, omite la caché de 30 min)',
      'settings.version.checking':              'Comprobando…',
      'settings.update.available':              'Nueva versión disponible: v:version ⬇',
      'settings.update.notReady':               '(el paquete aún no se ha publicado)',
      'settings.syncStatusDot.title':           'Sincronización en la nube — sin sesión iniciada',

      // === Options — folder editor ===
      'folder.icon.title':                      'Etiqueta del botón flotante (máx. 3 letras, MAYÚSCULAS). Predeterminado: ZIX',
      'folder.color.title':                     'Color de la carpeta (opcional)',
      'folder.colorClear.title':                'Quitar color',
      'folder.name.placeholder':                'Nombre de la carpeta (p. ej. justzix.com)',
      'folder.url.placeholder':                 'Patrón de URL (p. ej. https://justzix.com/* )',
      'folder.urls.label':                      'Patrones de URL de la carpeta:',
      'folder.urls.title':                      'Patrones de URL de la carpeta — al menos uno debe coincidir para que se ejecute cualquier regla de esta carpeta',
      'folder.urls.placeholder':                'https://justzix.com/*  (Enter para añadir)',
      'folder.insertUrl.title':                 'Insertar la URL de la pestaña activa',
      'folder.active.label':                    'Activa',
      'folder.active.title':                    'Activa — al desmarcarla, la carpeta (y todos sus grupos/reglas) se desactivan a la fuerza y el interruptor se bloquea',
      'folder.enabled.label':                   'Habilitada',
      'folder.enabled.title':                   'Habilitada — estado actual de la carpeta (se propaga a los grupos y reglas activos)',
      'folder.actionsPanel.label':              'Panel de acciones',
      'folder.actionsPanel.title':              'Mostrar un panel flotante con botones de acción para esta carpeta',
      'folder.layout.label':                    'Disposición:',
      'folder.layout.title':                    'Disposición de los botones del panel de acciones',
      'folder.layout.vertical':                 'Vertical',
      'folder.layout.horizontal':               'Horizontal',
      'folder.addGroup.label':                  '+ Grupo',
      'folder.addGroup.title':                  'Añadir un grupo nuevo en esta carpeta',
      'folder.export.label':                    'Exportar',
      'folder.export.title':                    'Exportar esta carpeta',
      'folder.import.label':                    'Importar',
      'folder.import.title':                    'Importar una carpeta desde un archivo',
      'folder.delete.label':                    'Eliminar carpeta',

      // === Options — group editor ===
      'group.parentLabel':                      'Grupo en la carpeta:',
      'folder.label':                           'Carpeta:',
      'group.label':                            'Grupo:',
      'group.name.placeholder':                 'Nombre del grupo',
      'group.urls.label':                       'Patrones de URL del grupo:',
      'group.urls.title':                       'Patrones de URL adicionales del grupo — restricción extra sobre el patrón de la carpeta, se aplica a todas las reglas del grupo',
      'group.urls.placeholder':                 'https://example.com/admin/*  (Tab = autocompletar desde la carpeta, Enter = añadir)',
      'group.active.label':                     'Activo',
      'group.active.title':                     'Activo — al desmarcarlo, el grupo (y todas sus reglas) se desactivan a la fuerza y el interruptor se bloquea',
      'group.enabled.label':                    'Habilitado',
      'group.enabled.title':                    'Habilitado — estado actual del grupo (se propaga a las reglas activas)',
      'group.addRule.label':                    '+ Añadir conjunto de reglas',
      'group.export.title':                     'Exportar este grupo',
      'group.import.title':                     'Importar conjuntos de reglas a este grupo',
      'group.move.label':                       'Mover',
      'group.move.title':                       'Mover el grupo a otra carpeta',
      'group.duplicate.label':                  'Duplicar',
      'group.duplicate.title':                  'Duplicar el grupo con sus conjuntos de reglas',
      'group.delete.label':                     'Eliminar grupo',

      // === Options — rule editor ===
      'rule.label':                             'Conjunto de reglas:',
      'rule.back.label':                        'Volver al grupo',
      'rule.export.title':                      'Exportar este conjunto de reglas a un archivo',
      'rule.share.label':                       'Compartir URL',
      'rule.share.title':                       'Compartir este conjunto de reglas como un enlace público temporal',
      'rule.import.title':                      'Importar conjunto de reglas (reemplazará el actual)',

      // === Options — empty state ===
      'empty.title':                            'Selecciona o añade una carpeta',
      'empty.body':                             'Cada carpeta agrupa conjuntos de reglas CSS/JS para un patrón de URL.',

      // === Default names for newly created entities (options.js) ===
      'default.groupName':                      'Predeterminado',
      'default.folderName':                     'Carpeta nueva',
      'default.ruleName':                       'Conjunto de reglas nuevo',

      // === Plural noun forms used by toasts ===
      // EN has 2 forms (one/other), PL has 4 — both filled, Intl.PluralRules picks correctly.
      'plural.rule.one':                        'conjunto de reglas',
      'plural.rule.few':                        'conjuntos de reglas',
      'plural.rule.many':                       'conjuntos de reglas',
      'plural.rule.other':                      'conjuntos de reglas',
      'plural.folder.one':                      'carpeta',
      'plural.folder.few':                      'carpetas',
      'plural.folder.many':                     'carpetas',
      'plural.folder.other':                    'carpetas',
      'plural.group.one':                       'grupo',
      'plural.group.few':                       'grupos',
      'plural.group.many':                      'grupos',
      'plural.group.other':                     'grupos',
      'plural.action.one':                      'acción',
      'plural.action.few':                      'acciones',
      'plural.action.many':                     'acciones',
      'plural.action.other':                    'acciones',
      'plural.jsLine.one':                      'línea de JS',
      'plural.jsLine.few':                      'líneas de JS',
      'plural.jsLine.many':                     'líneas de JS',
      'plural.jsLine.other':                    'líneas de JS',
      'toast.importedRulesToGroup':             'Se importaron :count :word al grupo',
      'toast.exportedRules':                    'Se exportaron :count :word',

      // === Sidebar — dynamic render ===
      'sidebar.empty.folders':                  'Sin carpetas',
      'sidebar.empty.activeFolders':            'Sin carpetas activas',
      'sidebar.folder.toggleHint':              'Habilitar/deshabilitar toda la carpeta',
      'sidebar.folder.inactiveHint':            'Carpeta inactiva — desbloquéala en el editor de carpetas',
      'sidebar.group.toggleHint':               'Habilitar/deshabilitar el grupo',
      'sidebar.group.inactiveHint':             'Grupo inactivo — desbloquéalo en el editor de grupos',
      'sidebar.rule.urlHint':                   'Restricción de URL a nivel del conjunto de reglas',
      'sidebar.rule.activeInFolder':            'Activo en la carpeta',
      'sidebar.rule.inactiveInFolder':          'Inactivo en la carpeta',
      'sidebar.fallback.folderName':            '(carpeta)',
      'sidebar.fallback.groupName':             '(grupo)',
      'sidebar.fallback.unnamed':               '(sin nombre)',
      'sidebar.flag.css':                       'Contiene CSS',
      'sidebar.flag.js':                        'Contiene JavaScript',
      'sidebar.flag.actions':                   'Contiene acciones',
      'sidebar.flag.groupUrls':                 'El grupo tiene restricción de URL',

      // === Folder/Group/Rule editor — dynamic ===
      'rules.empty.folder':                     'No hay conjuntos de reglas en esta carpeta. Añade un grupo y crea en él el primer conjunto de reglas, o haz clic en un grupo de la barra lateral.',
      'rules.empty.group':                      'No hay conjuntos de reglas en este grupo. Añade el primero haciendo clic en "+ Añadir conjunto de reglas".',
      'rule.namePlaceholder':                   'Nombre del conjunto de reglas',
      'rule.activeTooltip':                     'Activo — indica si esta regla se incluye al conmutar toda la carpeta',
      'rule.urls.tooltip':                      'Patrones de URL adicionales — cuando se definen, la regla se ejecuta solo en subpáginas que coincidan con al menos un patrón (además del patrón de la carpeta)',
      'rule.urls.placeholder.empty':            'https://example.com/admin/*  (Tab — insertar patrón de la carpeta, Enter — añadir)',

      // === Move menu ===
      'moveMenu.section.moveGroup':             'Mover grupo a',
      'moveMenu.section.moveRule':              'Mover al grupo',
      'moveMenu.section.copyRule':              'Copiar al grupo',
      'moveMenu.empty.folders':                 'No hay otras carpetas',
      'moveMenu.empty.groups':                  'No hay otros grupos',

      // === Confirm dialogs — dynamic ===
      'confirm.deleteFolderWithStats':          '¿Eliminar la carpeta ":name" junto con :groups y :rules?',
      'confirm.deleteFolder':                   '¿Eliminar la carpeta ":name"?',
      'confirm.deleteGroupWithRules':           '¿Eliminar el grupo ":name" junto con :count :word?',
      'confirm.deleteGroup':                    '¿Eliminar el grupo ":name"?',
      'confirm.replacePartial':                 '¿Reemplazar el/la :before actual con la copia de seguridad parcial (:after)?\n\nSe perderán todos los datos que no estén en el archivo. La operación es irreversible.',
      'confirm.overwriteAll':                   '¿Sobrescribir :stats con los datos actuales del archivo?\n\nEsta operación es irreversible.',

      // === Default name for newly created group ===
      'default.newGroupName':                   'Grupo nuevo',

      // === Export — summary + states ===
      'export.summary.full':                    ':icon Copia de seguridad completa: :stats + ajustes globales',
      'export.summary.partial':                 ':icon Copia de seguridad parcial: :stats',
      'export.empty.folder':                    'No hay carpetas para exportar',
      'export.empty.group':                     'El grupo está vacío — no hay conjuntos de reglas para exportar',
      'export.summary.toExport':                ':icon Para exportar: :count :word',
      'export.summary.noneSelected':            ':icon No hay conjuntos de reglas seleccionados',

      // === Import detection (banners) ===
      'import.detection.invalid':               '<strong style="color:#dc2626;">:icon Formato no válido:</strong> falta la lista de carpetas (<code>folders</code>).',
      'import.detection.partialContains':       'Contiene: :stats',
      'import.detection.willReplace':           ':stats',

      // === Inline labels ===
      'inline.fileGroupLabel':                  'grupo',
      'shares.empty':                           'No tienes enlaces activos.<br><br>Para crear uno — abre una exportación (completa / carpeta / grupo / conjunto de reglas)<br>y haz clic en <strong>"Compartir URL"</strong>.',

      // === Share preview / export types ===
      'shareType.folder':                       'Una sola carpeta',
      'shareType.group':                        'Un solo grupo',
      'shareType.rule':                         'Un solo conjunto de reglas',
      'shareType.full':                         'Exportación completa',

      // === Owner suffix when importing from share ===
      'shareImport.suffix.group':               'grupo',
      'shareImport.suffix.rule':                'conjunto de reglas',

      // === Rule import — prompt with options ===
      'prompt.ruleConflict':                    'Estás importando ":new". ¿Qué hacer con el conjunto de reglas actual ":current"?\n\n  1 o "replace"  — Reemplazar el conjunto de reglas actual\n  2 o "copy"     — Añadir como un conjunto de reglas nuevo junto al actual (en el mismo grupo)\n\nIntroduce 1 o 2:',
      'prompt.folderConflicts':                 'Se detectaron :count :word con el mismo ID que las existentes:\n:names\n\nIntroduce una opción:\n  1 o "overwrite"  — Sobrescribir las carpetas existentes (elimina sus grupos y conjuntos de reglas actuales)\n  2 o "copy"       — Importar como copias (IDs nuevos, "(import)" en el nombre de la carpeta)\n  Cancelar         — Cancelar la importación\n\nElección:',

      // === Group editor — additional ===
      'group.empty.noGroupsInFolder':           'No hay grupos en la carpeta',

      // === Folder import — conflict messages ===
      'folderImport.conflict.sameId':           'La carpeta importada tiene el mismo ID que la existente "<strong>:name</strong>" (:count :word). Elige una acción:',
      'folderImport.conflict.sameName':         'Ya existe una carpeta llamada "<strong>:name</strong>" (:count :word). Elige una acción:',

      // === Dynamic button states ===
      'btn.shortcutPress':                      '⌨ pulsa Ctrl+Alt+Shift + una tecla (Esc)',
      'btn.generating':                         'Generando…',
      'btn.importing':                          'Importando…',
      'btn.showKey':                            'Mostrar clave',
      'btn.hideKey':                            'Ocultar clave',

      // === Rule editor — placeholders + tooltips ===
      'rule.editor.urlsPlaceholder':            'https://example.com/admin/*  (Tab — insertar patrón de la carpeta, Enter — añadir)',
      'rule.editor.urlsPlaceholderWithPattern': ':pattern  (Tab — insertar, Enter — añadir)',
      'rule.editor.toggleHint':                 'Habilitar/deshabilitar este conjunto de reglas',
      'rule.editor.toggleLockedHint':           'El conjunto de reglas está inactivo — desbloquéalo marcando Activo',
      'sidebar.fallback.ruleName':              '(conjunto de reglas)',
      'sidebar.fallback.ruleUnnamed':           '(conjunto de reglas sin nombre)',
      'confirm.deleteRule':                     '¿Eliminar el conjunto de reglas ":name"?',

      // === Rule card (rendered by createRuleCard / renderActionsList) ===
      'rule.card.tagsTitle':                    'Etiquetas (separadas por comas)',
      'rule.card.activeLabel':                  'Activo',
      'rule.card.enabledLabel':                 'Habilitado',
      'rule.card.history.title':                'Historial de cambios',
      'rule.card.history.label':                'Historial',
      'rule.card.move.label':                   'Mover',
      'rule.card.move.btnTitle':                'Mover el conjunto de reglas a otro grupo',
      'rule.card.copy.label':                   'Copiar',
      'rule.card.copy.btnTitle':                'Copiar el conjunto de reglas a otro grupo',
      'rule.card.duplicate.label':              'Duplicar',
      'rule.card.delete.label':                 'Eliminar',
      'rule.card.urlsLabel':                    'Patrones de URL del conjunto de reglas:',
      'rule.card.tab.actions':                  'Acciones',
      'rule.card.lineCount':                    'Número de líneas de código',
      'rule.card.js.runOnce.label':             'Ejecutar JS solo una vez',
      'rule.card.js.runOnce.title':             'No volver a ejecutar el JS tras la navegación SPA (cambio de URL sin recargar la página). Recomendado para scripts que añaden listeners globales, intervalos o modifican window.',
      'rule.card.actions.info':                 'Los botones de acción aparecen en el panel flotante (interruptor "Panel de acciones" en la carpeta). Atajo de máx. 4 caracteres, color personalizado, el código JS se ejecuta al hacer clic.',
      'rule.card.actions.add':                  '+ Añadir botón de acción',
      'rule.card.actions.addWithCount':         '+ Añadir botón de acción (:count)',
      'rule.card.action.visibleLabel':          'visible',
      'rule.card.duplicate.suffix':             '(copia)',
      'rule.card.action.codePreview':           '(:count :word) :first',
      'rule.card.action.fallback.unlabeled':    '(sin etiqueta)',
      'rule.card.fallbackName':                 'Conjunto de reglas',

      // === Folder export modal — additional ===
      'export.empty.foldersToExport':           'No hay carpetas para exportar',
      'export.summary.toExportFolders':         ':icon Para exportar: :groupCount :groupWord, :ruleCount :ruleWord',
      'export.summary.invalidFolderFile':       'Formato de archivo no válido — se esperaba la exportación de una sola carpeta.',
      'export.summary.noConflicts':             ':icon Sin conflictos. La carpeta "<strong>:name</strong>" con :count :word se importará como nueva.',
      'export.summary.fileContains':            '<strong style="color:#374151;">:icon Contenido del archivo (:count :word):</strong>',
      'export.summary.jsHeader':                ':icon JavaScript del conjunto de reglas:',
      'export.summary.emptyRule':               'conjunto de reglas vacío',
      'toast.exportedFolder':                   'Carpeta exportada (:groupCount :groupWord, :ruleCount :ruleWord)',
      'inline.fileFolderLabel':                 'carpeta',
      'inline.fileRuleLabel':                   'conjunto de reglas',
      'default.newGroupNameInline':             'Grupo nuevo',

      // === Sync conflict modal counts ===
      'syncConflict.countLine':                 ':folders :foldersWord / :groups :groupsWord / :rules :rulesWord',

      // === Backup restore confirm ===
      'confirm.restoreBackup':                  '¿Restaurar la copia de seguridad n.º :id?\n\nTODAS las carpetas/grupos/conjuntos de reglas actuales se reemplazarán por lo que haya en la copia de seguridad. Otros dispositivos se sincronizarán en la próxima descarga.\n\nIrreversible (salvo por la creación de una copia de seguridad ANTES de restaurar).',

      // === Loaders / placeholders ===
      'loading.shares':                         'Cargando…',
      'loading.generic':                        'Cargando…',

      // === Sidebar — drag & arrows tooltips ===
      'sidebar.dragHandle.title':               'Arrastra para reordenar',
      'sidebar.expandCollapse.title':           'Expandir/contraer',
      'sidebar.folderActive.title':             'Activa',
      'sidebar.folderInactive.title':           'Inactiva — desactivada a la fuerza',
      'sidebar.groupActive.title':              'Activo',
      'sidebar.groupInactive.title':            'Inactivo — desactivado a la fuerza',
      'sidebar.searchNoResults':                'Sin resultados para ":query"',

      // === Theme ===
      'settings.theme.toggleToDark':            'Cambiar al modo oscuro',
      'settings.theme.toggleToLight':           'Cambiar al modo claro',
      'settings.theme.labelDark':               'Modo oscuro',
      'settings.theme.labelLight':              'Modo claro',
      'settings.theme.labelAuto':               'Auto (sistema)',
      'settings.theme.toggleToAuto':            'Cambiar al modo automático',

      // === URL chip remove ===
      'btn.removeUrlChip':                      'Quitar',

      // === CSS validator ===
      'cssValidator.unclosedComment':           'Comentario sin cerrar /* ... */',
      'cssValidator.missingCloseBraces':        'Llaves sin cerrar: faltan :count :word }',
      'plural.brace.one':                       'llave de cierre',
      'plural.brace.few':                       'llaves de cierre',
      'plural.brace.many':                      'llaves de cierre',
      'plural.brace.other':                     'llaves de cierre',
      'cssValidator.extraCloseBrace':           'Llave de cierre sobrante } (línea :line)',
      'jsValidator.unterminatedString':         'Literal de cadena sin terminar (línea :line)',
      'jsValidator.unclosedBracket':            'Paréntesis sin cerrar :bracket (línea :line)',
      'jsValidator.extraBracket':               'Paréntesis de cierre sobrante :bracket (línea :line)',

      // === Action buttons ===
      'action.shortcut.empty':                  'haz clic + pulsa el atajo',
      'action.code.empty':                      'haz clic para añadir código…',
      'action.stats.title':                     'Número de clics en el panel de acciones',
      'action.visible.title':                   'Visible en el panel de acciones (sin marcar = oculto, pero no eliminado)',
      'action.shortcut.title':                  'Atajo de teclado — haz clic y pulsa una combinación. Esc cancela. El atajo funciona en las páginas que coincidan independientemente de la visibilidad del panel.',
      'action.shortcut.titleAfterClick':        'Atajo de teclado. Haz clic y pulsa una combinación para cambiarlo. Esc cancela. × lo elimina.',
      'action.shortcut.removeBtn':              'Quitar el atajo de teclado',
      'action.delete.btn':                      'Eliminar el botón de acción',
      'action.menu.btn.duplicate':              'Duplicar la acción aquí',
      'action.menu.btn.move':                   'Mover la acción a otro conjunto de reglas',
      'action.menu.btn.copy':                   'Copiar la acción a otro conjunto de reglas',
      'action.menu.section.moveTo':             'Mover al conjunto de reglas...',
      'action.menu.section.copyTo':             'Copiar al conjunto de reglas...',
      'action.menu.empty.rules':                'No hay otros conjuntos de reglas',
      'action.menu.toast.moved':                'Acción movida a ":target"',
      'action.menu.toast.copied':               'Acción copiada a ":target"',
      'action.shortcut.conflictTitle':          'Conflicto: este atajo también lo usa(n) :count :word:\n:list\n\nAl pulsarlo, solo se ejecutará el primero en el orden de renderizado.',
      'action.shortcut.conflictItem':           '• ":action" en ":rule" (:folder)',
      'plural.actionAlt.one':                   'otra acción',
      'plural.actionAlt.few':                   'otras acciones',
      'plural.actionAlt.many':                  'otras acciones',
      'plural.actionAlt.other':                 'otras acciones',
      'plural.more.one':                        'más',
      'plural.more.few':                        'más',
      'plural.more.many':                       'más',
      'plural.more.other':                      'más',
      'action.shortcut.conflictMore':           ' (y :count :word)',
      'action.shortcut.conflictToast':          'Conflicto: este atajo ya lo usa la acción ":actionLabel" en ":ruleName":more. Solo se ejecutará una de ellas.',
      'confirm.deleteAction':                   '¿Eliminar el botón de acción ":label"?',

      // === History ===
      'history.header':                         'Últimas :count versiones guardadas. Haz clic en "Restaurar" para volver a esa versión (la actual se guardará en el historial).',
      'history.modalTitle':                     'Historial de cambios: ":name"',
      'history.version':                        'Versión :n',
      'history.restore':                        'Restaurar',
      'history.close':                          'Cerrar',

      // === Export — additional ===
      'export.summary.noElements':              ':icon No hay elementos seleccionados para exportar',
      'export.summary.toExportFolderShort':     ':icon Para exportar: :groupCount :groupWord, :ruleCount :ruleWord',
      'export.summary.noneSelectedShort':       ':icon No hay conjuntos de reglas seleccionados',

      // === Import detection ===
      'import.detection.parseError':            '<strong style="color:#dc2626;">:icon Error al analizar el JSON:</strong> :msg',
      'import.detection.partialBackup':         '<strong style="color:#5b21b6;">:icon Copia de seguridad parcial (elementos seleccionados)</strong><br>',
      'import.detection.fullBackup':            '<strong style="color:#006870;">:icon Copia de seguridad completa de los ajustes:legacy</strong><br>',
      'import.detection.legacySuffix':          ' (formato antiguo)',
      'import.detection.willOverwrite':         '<br><br><strong style="color:#b91c1c;">:icon Los ajustes actuales se sobrescribirán:</strong>',
      'import.detection.exportFirst':           '<br><span style="color:#7f1d1d;font-size:11px;">Recomendado: exporta <strong>antes</strong> de importar.</span>',
      'import.detection.folder':                '<strong style="color:#155e75;">:icon Exportación de carpeta</strong> "<strong>:name</strong>"<br><span style="color:#155e75;">Contiene: :stats</span>',
      'import.detection.group':                 '<strong style="color:#155e75;">:icon Exportación de grupo</strong> "<strong>:name</strong>"<br><span style="color:#155e75;">Contiene: :stats</span>',
      'import.detection.rule':                  '<strong style="color:#155e75;">:icon Exportación de conjunto de reglas</strong> "<strong>:name</strong>"',

      // === Import — target selectors ===
      'import.target.folder.title':             '¿Dónde colocar la carpeta importada?',
      'import.target.folder.newCopy':           'Añadir como una nueva carpeta de nivel superior',
      'import.target.folder.overwrite':         'Sobrescribir la carpeta existente ":name" (conserva el ID — la sincronización lo ve como una edición)',
      'import.target.folder.mergeInto':         'Fusionar grupos/conjuntos de reglas en una carpeta existente:',
      'import.target.group.title':              '¿Dónde colocar el grupo importado?',
      'import.target.group.existingFolder':     'Añadir a una carpeta existente:',
      'import.target.group.newFolder':          'Crear una carpeta nueva para este grupo',
      'import.target.rule.title':               '¿Dónde colocar el conjunto de reglas importado?',
      'import.target.rule.existingGroup':       'Añadir a un grupo existente:',
      'import.target.rule.newFolder':           'Crear una carpeta y un grupo nuevos para este conjunto de reglas',

      // === Share errors (create flow) ===
      'share.error.unknown':                    'Error desconocido',
      'share.error.shareFailed':                'Ya tienes 20 enlaces activos o has superado los 50 MB. Revoca los antiguos en "Mis enlaces compartidos".',
      'share.error.badRequest':                 'No se pudo generar el enlace — comprueba que la exportación sea válida. :msg',
      'share.error.rateLimited':                'Se han creado demasiados enlaces en la última hora (límite: 30). Inténtalo de nuevo en un momento.',
      'share.error.invalidToken':               'La sesión ha caducado — vuelve a iniciar sesión en el panel de sincronización e inténtalo otra vez.',
      'share.error.serverError':                'Problema del servidor. Inténtalo de nuevo en un momento.',
      'share.error.fallback':                   'Error: :msg',
      'share.error.tooLarge':                   'La exportación es demasiado grande — el servidor admite un máximo de 5 MB. Intenta compartir una parte más pequeña.',

      // === Share subject (create modal) ===
      'share.subject.partial':                  'Exportación parcial de la biblioteca',
      'share.subject.full':                     'Exportación completa de la biblioteca',

      // === Time relative formatting (fmtRelHuman) ===
      'rel.lessThanMinute':                     'menos de un minuto',
      'rel.min':                                ':n min',
      'rel.hMin':                               ':h h :m min',
      'rel.h':                                  ':h h',
      'rel.dH':                                 ':d d :h h',
      'rel.d':                                  ':d d',

      // === Shares list ===
      'shares.expired':                         'caducado',
      'shares.fetchFailed':                     'No se pudo obtener la lista: :msg',
      'shares.confirmRevoke':                   '¿Revocar el enlace compartido n.º :id?\n\nEl enlace dejará de funcionar de inmediato — los destinatarios recibirán un error 404 en el próximo intento de descarga.\n\nNOTA: si alguien ya ha descargado el contenido, tendrá una copia local — la revocación no recupera el contenido ya descargado.',
      'shares.revoked':                         'Enlace compartido n.º :id revocado',

      // === Share preview details ===
      'sharePreview.senderLabel':               'Remitente:',
      'sharePreview.senderAnonymous':           'anónimo',
      'sharePreview.expiresLabel':              'Caduca:',
      'sharePreview.expiresValue':              'en :rel',
      'sharePreview.typeLabel':                 'Tipo:',
      'sharePreview.sizeLabel':                 'Tamaño:',
      'sharePreview.contentLabel':              'Contenido:',
      'sharePreview.cssLabel':                  'CSS:',
      'sharePreview.cssValueChars':             ':n caracteres',
      'sharePreview.jsLabel':                   'JavaScript:',
      'sharePreview.downloadsLabel':            'Descargas:',
      'sharePreview.codePreviewTitle':          'Vista previa del código de la exportación compartida',

      // === Share import errors (preview) ===
      'shareImport.error.unknown':              'Error desconocido',
      'shareImport.error.badRequest':           'El token no es válido. Comprueba que has copiado el enlace completo.',
      'shareImport.error.notFound':             'Este enlace ha caducado o fue revocado por el remitente. Pide uno nuevo.',
      'shareImport.error.rateLimited':          'Demasiados intentos. Espera un momento e inténtalo de nuevo.',
      'shareImport.error.serverError':          'Problema del servidor. Inténtalo de nuevo.',
      'shareImport.error.fallback':             'Error: :msg',
      'shareImport.error.invalidTokenFormat':   'Formato de token no válido',

      // === Rule import ===
      'ruleImport.unknownFormat':               'Formato de exportación desconocido o no compatible: :type',
      'prompt.ruleConflict.body':               'Estás importando ":new". ¿Qué hacer con el conjunto de reglas actual ":current"?\n\n  1 o "replace"  — Reemplazar el actual con el contenido del archivo\n  2 o "copy"     — Añadir como un conjunto de reglas nuevo junto al actual (en el mismo grupo)\n  Cancelar       — Cancelar la importación\n\nElección:',

      // === Folder export ===
      'folderExport.exportedToast':             'Carpeta exportada (:groupCount :groupWord, :ruleCount :ruleWord)',

      // === Folder import — details ===
      'folderImport.parseError':                'Error al analizar el JSON: :msg',
      'folderImport.invalidFormat':             'Formato de archivo no válido — se esperaba la exportación de una sola carpeta.',
      'folderImport.noConflicts':               ':icon Sin conflictos. La carpeta "<strong>:name</strong>" con :count :word se importará como nueva.',
      'folderImport.conflictSameId':            'La carpeta importada tiene el mismo ID que la existente "<strong>:name</strong>" (:count :word). Elige una acción:',
      'folderImport.conflictSameName':          'Ya existe una carpeta llamada "<strong>:name</strong>" (:count :word). Elige una acción:',
      'folderImport.fileContents':              '<strong style="color:#374151;">:icon Contenido del archivo (:count :word):</strong>',
      'folderImport.jsHeader':                  ':icon JavaScript del conjunto de reglas:',
      'folderImport.codePreviewTitle':          'Vista previa del código: :name',
      'folderImport.previewJsBtn.title':        'Vista previa del código JS',

      // === Sync — toasts + status messages ===
      'sync.toast.synced':                      'Sincronizado: :applied cambios, :deletes eliminaciones',
      'sync.rel.justNow':                       'ahora mismo',
      'sync.tooltip.syncing':                   'Sincronizando…',
      'sync.tooltip.error':                     'Sincronización: error. Última correcta: :last',
      'sync.tooltip.connected':                 'Sincronización en la nube — conectado. Última: :last',
      'sync.error.copyManually':                'No se pudo copiar — selecciona y pulsa Ctrl+C manualmente.',
      'sync.error.registerFailed':              'Error de registro: :msg',
      'sync.error.unknown':                     'desconocido',
      'sync.error.invalidKeyFormat':            'Formato de clave no válido — se espera SYNC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX.',
      'sync.status.sendingLocal':               'Enviando :count entidades locales a la cuenta.',
      'sync.status.cancelled':                  'Inicio de sesión cancelado — sesión cerrada. Los datos locales no se han modificado.',
      'sync.status.sendingAndDeleting':         'Enviando :local entidades locales, eliminando :remote de la cuenta…',
      'sync.status.merged':                     'Fusionado. Las entidades exclusivamente locales (:count elementos) se enviarán a la cuenta en la próxima sincronización.',
      'sync.error.firstSyncFailed':             'Inicio de sesión correcto, pero la primera sincronización falló: :msg',
      'sync.error.loginFailed':                 'Error de inicio de sesión: :msg',
      'sync.error.invalidEmail':                'Introduce un correo electrónico válido.',
      'sync.recovery.sent':                     'Si este correo está registrado, hemos enviado un enlace de recuperación. Revisa tu bandeja de entrada.',
      'sync.error.generic':                     'Error: :msg',
      'sync.account.thisDevice':                'este dispositivo: :label',
      'sync.session.noLabel':                   '(sin etiqueta)',
      'sync.session.thisDeviceSuffix':          ' (este dispositivo)',
      'sync.session.revoke':                    'Revocar',
      'sync.error.revokeFailed':                'Error al revocar: :msg',
      'sync.session.fetchFailed':               'No se pudo obtener la lista de sesiones: :msg',
      'sync.key.copyManual':                    'No se pudo copiar — haz clic en "Mostrar clave" y pulsa Ctrl+C manualmente.',
      'sync.key.removed':                       'Clave eliminada del almacenamiento local.',
      'sync.backups.empty':                     'No hay copias de seguridad en la nube. Crea la primera haciendo clic en "Crear copia de seguridad ahora".',
      'sync.backups.fetchFailed':               'No se pudo obtener la lista: :msg',
      'sync.backups.creating':                  'Creando copia de seguridad en la nube…',
      'sync.backups.createFailed':              'Error al crear la copia de seguridad: :msg',
      'sync.backups.restored':                  'Copia de seguridad n.º :id restaurada. Se reemplazaron :count entidades. newCursor=:cursor.',
      'sync.backups.restoreFailed':             'Error al restaurar: :msg',
      'sync.backups.downloadFailed':            'Error al descargar: :msg',
      'sync.backups.confirmDelete':             '¿Eliminar la copia de seguridad n.º :id de la nube? Irreversible.',
      'sync.backups.deleted':                   'Copia de seguridad n.º :id eliminada.',
      'sync.backups.deleteFailed':              'Error al eliminar: :msg',
      'sync.pushPullSummary':                   'Envío: :pushedAccepted aceptados, :pushedRejected rechazados • Descarga: :applied entidades, :deletes eliminaciones.',
      'sync.returned':                          'La sincronización devolvió: :result',
      'sync.snapshotFailed':                    'Error de instantánea: :msg',
      'sync.reconcile.running':                 'Resincronización completa con la cuenta…',
      'sync.reconcile.okPushed':                'Resync OK — :count entidades faltantes enviadas a la cuenta.',
      'sync.reconcile.okInSync':                'Resync OK — la biblioteca coincide con la cuenta.',
      'sync.reconcile.failed':                  'La resincronización completa falló: :msg',
      'sync.email.saveButFailed':               'Correo guardado, pero falló el envío del enlace — inténtalo de nuevo.',
      'sync.email.verifyLinkSent':              'Enlace de verificación enviado a :email. Revisa tu bandeja de entrada y haz clic en el enlace.',
      'sync.email.removed':                     'Correo electrónico eliminado.',

      // === Sync result modal — strategies ===
      'syncResult.merge.title':                 'Fusionado con la nube',
      'syncResult.merge.desc':                  'Las entidades presentes en ambos sitios se sobrescribieron con la versión de la cuenta. Las exclusivamente locales se conservan y se enviarán a la cuenta en la próxima sincronización.',
      'syncResult.merge.tableHeader':           '<table class="result-table"><thead><tr><th>Tipo</th><th>Desde la nube: sobrescritas</th><th>Desde la nube: nuevas</th><th>Locales conservadas → envío</th><th>Total tras la fusión</th></tr></thead><tbody>',
      'syncResult.useServer.title':             'Datos locales reemplazados por los de la nube',
      'syncResult.useServer.desc':              'La biblioteca local se reemplazó por completo con el estado de la cuenta. Las entidades exclusivamente locales (si las había) se perdieron.',
      'syncResult.useServer.tableHeader':       '<table class="result-table"><thead><tr><th>Tipo</th><th>Obtenidas de la nube</th><th>Locales perdidas</th><th>Comunes (sobrescritas)</th><th>Total tras la sincronización</th></tr></thead><tbody>',
      'syncResult.sendMine.title':              'Enviando datos locales a la cuenta',
      'syncResult.sendMine.desc':               'La biblioteca local se enviará a la cuenta. Las entidades presentes en la nube pero ausentes localmente se marcarán como eliminadas (tombstone) en la cuenta.',
      'syncResult.sendMine.tableHeader':        '<table class="result-table"><thead><tr><th>Tipo</th><th>Enviadas (push)</th><th>Sobrescrituras en la cuenta</th><th>Nuevas en la cuenta</th><th>Eliminadas de la cuenta</th></tr></thead><tbody>',
      'syncResult.fallback.title':              'Sincronización completada',
      'syncResult.empty':                       'Sin detalles.',
      'syncResult.label.folders':               'Carpetas',
      'syncResult.label.groups':                'Grupos',
      'syncResult.label.rules':                 'Conjuntos de reglas',
      'syncResult.label.actions':               'Acciones',

      // === Options — toasts (bottom notifications) ===
      'toast.saved':                            'Guardado',
      'toast.cantDeleteLastGroup':              'No se puede eliminar el último grupo de una carpeta',
      'toast.noOtherTab':                       'No hay otra pestaña con la URL de una página',
      'toast.urlInserted':                      'URL insertada desde la pestaña',
      'toast.urlAlreadyAdded':                  'El patrón ya está en la lista: :pattern',
      'toast.error':                            'Error: :msg',
      'toast.shortcutMustIncludeMod':           'Atajo permitido: Ctrl+Alt+Shift + una letra (excepto G H J K L), un dígito o un símbolo (excepto \\).',
      'toast.shortcutCollidesChrome':           'Aviso: el atajo coincide con un atajo de Chrome (p. ej. pestaña nueva). Puede que no funcione — elige otro.',
      'toast.copied':                           'Copiado: :text',
      'toast.copyFailed':                       'Error al copiar',
      'toast.copyClipboard':                    'Copiado al portapapeles',
      'toast.copyLink':                         'Enlace copiado',
      'toast.copyToken':                        'Token copiado',
      'toast.noHistory':                        'No hay historial para este conjunto de reglas',
      'toast.versionRestored':                  'Versión restaurada',
      'toast.fileNotFullImport':                'El archivo no es apto para una importación completa',
      'toast.unknownOption':                    'Opción desconocida — importación cancelada',
      'toast.importedSettings':                 'Ajustes importados',
      'toast.parseError':                       'Error al analizar: :msg',
      'toast.invalidPartialFormat':             'Formato no válido — se esperaba la exportación de un grupo, una carpeta o un conjunto de reglas',
      'toast.groupImport.folderRejected':       'Esto es una exportación de carpeta (varios grupos). Impórtala mediante la importación a nivel de carpeta, no de grupo.',
      'toast.exportedRule':                     'Conjunto de reglas exportado',
      'toast.signInFirst':                      'Inicia sesión primero en el panel de sincronización',
      'toast.exportNotReady':                   'La exportación no está lista',
      'toast.noRulesSelected':                  'No hay conjuntos de reglas seleccionados',
      'toast.revokeError':                      'Error al revocar: :msg',
      'toast.importedFromLink':                 'Importado desde el enlace',
      'toast.invalidRuleFormat':                'Formato de archivo no válido — se esperaba la exportación de un conjunto de reglas',
      'toast.importedReplaced':                 'Importado (reemplazado)',
      'toast.importedAsCopy':                   'Importado como copia',
      'toast.noImportData':                     'No hay datos para importar',
      'toast.importedFolder':                   'Carpeta importada: :name',
      'toast.importedGroup':                    'Grupo importado: :name',
      'toast.importedRule':                     'Conjunto de reglas importado: :name',

      // === Options — native confirm() dialogs ===
      'confirm.restoreVersion':                 '¿Restaurar esta versión? El contenido actual se añadirá al historial como una nueva instantánea.',
      'confirm.revokeSession':                  '¿Revocar esta sesión?',
      'confirm.removeKey':                      '¿Quitar la clave de este dispositivo?\n\nEl inicio de sesión seguirá funcionando (la sesión es independiente de la clave), pero no podrás ver ni copiar la clave a otro dispositivo desde este ordenador.\n\nAsegúrate de tener una copia de la clave guardada en otro lugar (un gestor de contraseñas).',
      'confirm.snapshot':                       'La instantánea completa descarga toda la biblioteca de la nube y reemplaza el estado local. Tus cambios pendientes NO se perderán (permanecen en la cola). ¿Continuar?',
      'confirm.removeEmail':                    '¿Quitar el correo electrónico de la cuenta? Sin correo, no podrás recuperar el acceso si pierdes la clave.',
      'confirm.signOut':                        '¿Cerrar sesión en este dispositivo? La biblioteca local se mantiene, pero deja de sincronizarse.',

      // === Modal — full export ===
      'modal.export.title':                     'Exportar ajustes',
      'modal.export.mode.all':                  'Todo',
      'modal.export.mode.select':               'Carpetas/reglas seleccionadas',
      'modal.export.selectAll':                 'Seleccionar todo',
      'modal.export.deselectAll':               'Deseleccionar todo',
      'modal.export.hint':                      'Puedes copiar el JSON al portapapeles o descargar el archivo:',
      'modal.export.close':                     'Cerrar',
      'modal.export.share':                     'Compartir URL',
      'modal.export.share.title':               'Compartir esta exportación como un enlace público temporal',
      'modal.export.download':                  'Descargar archivo',
      'modal.export.copy':                      'Copiar al portapapeles',

      // === Modal — full import ===
      'modal.import.title':                     'Importar ajustes',
      'modal.import.pickerHint':                'Pega el JSON de cualquier exportación de JustZix (copia de seguridad completa, carpeta, grupo o conjunto de reglas) — el formulario detectará el tipo y ofrecerá opciones de ubicación.',
      'modal.import.warningStrong':             'Aviso:',
      'modal.import.warningRest':               ' una importación completa sobrescribirá todos los ajustes actuales.',
      'modal.import.placeholder':               'Pega el JSON aquí o elige un archivo...',
      'modal.import.cancel':                    'Cancelar',
      'modal.import.import':                    'Importar',

      // === Modal — folder export ===
      'modal.folderExport.titlePrefix':         'Exportar carpeta:',
      'modal.folderExport.hint':                'Selecciona los grupos/reglas para exportar.',
      'modal.folderExport.cancel':              'Cancelar',
      'modal.folderExport.share':               'Compartir URL',
      'modal.folderExport.shareTitle':          'Compartir esta carpeta como un enlace público temporal',
      'modal.folderExport.download':            'Descargar archivo',

      // === Modal — group export ===
      'modal.groupExport.titlePrefix':          'Exportar grupo:',
      'modal.groupExport.hint':                 'Selecciona los conjuntos de reglas para exportar.',
      'modal.groupExport.cancel':               'Cancelar',
      'modal.groupExport.share':                'Compartir URL',
      'modal.groupExport.shareTitle':           'Compartir este grupo como un enlace público temporal',
      'modal.groupExport.download':             'Descargar archivo',

      // === Modal — folder import ===
      'modal.folderImport.title':               'Importar carpeta',
      'modal.folderImport.placeholder':         'Pega el JSON de la carpeta o elige un archivo...',
      'modal.folderImport.conflicts':           'Conflictos detectados:',
      'modal.folderImport.action.rename':       'Importar como una carpeta nueva (con el sufijo "(import)")',
      'modal.folderImport.action.overwrite':    'Sobrescribir la carpeta existente',
      'modal.folderImport.action.merge':        'Fusionar — añadir los conjuntos de reglas nuevos a la carpeta existente (conservando ambos)',
      'modal.folderImport.cancel':              'Cancelar',
      'modal.folderImport.import':              'Importar',

      // === Modal — action code ===
      // === Action bars (Phase 2 of multi-bars system, since v2.9) ===
      'actionBars.btn.label':                   'Barras de acciones',
      'actionBars.btn.title':                   'Gestionar las barras de acciones asignadas a esta carpeta',
      'actionBars.btn.titleGroup':              'Gestionar las barras de acciones asignadas a este grupo',
      'actionBars.btn.titleRule':               'Gestionar las barras de acciones asignadas a este conjunto de reglas',
      'actionBars.parentType.folder':           'Carpeta',
      'actionBars.parentType.group':            'Grupo',
      'actionBars.parentType.rule':             'Conjunto de reglas',
      'actionBars.parentType.folder.short':     '[C]',
      'actionBars.parentType.group.short':      '[G]',
      'actionBars.parentType.rule.short':       '[R]',
      'actionBars.empty':                       'No hay barras de acciones en este nivel. Haz clic en "+ Añadir barra nueva" para crear la primera.',
      'actionBars.section.owned':               'Barras en este nivel',
      'actionBars.section.fromFolder':          '↑ Heredada de la carpeta "<strong>:name</strong>"',
      'actionBars.section.fromGroup':           '↑ Heredada del grupo "<strong>:name</strong>"',
      'actionBars.elementsCount':               'Elementos: :count',
      'actionBars.enabled':                     'Habilitada',
      'actionBars.name.placeholder':            'NOMBRE',
      'actionBars.name.title':                  'Etiqueta en el borde de la barra (máx. 6 caracteres, MAYÚSCULAS). Vacío = sin etiqueta.',
      'actionBars.color.title':                 'Color de fondo de la barra',
      'actionBars.orientation.title':           'Haz clic para alternar la orientación de la barra (vertical ↔ horizontal)',
      'actionBars.color.clear':                 'Quitar el color de fondo',
      'actionBars.moveTo':                      'Mover a:',
      'actionBars.delete':                      'Eliminar',
      'actionBars.confirmDelete':               '¿Eliminar esta barra?',
      'actionBars.confirmDeleteWithElements':   '¿Eliminar esta barra? Los elementos asignados (:count) conservarán sus definiciones pero perderán la asignación de barra — tendrás que asignarlos a otra barra manualmente.',
      'actionBars.deleteMigrate.message':       'Esta barra tiene :count elementos asignados. Elige a dónde moverlos antes de eliminarla (o sepáralos de cualquier barra):',
      'actionBars.deleteMigrate.detachOption':  '— separar los elementos (sin barra) —',
      'actionBars.deleteMigrate.confirm':       'Mover y eliminar',
      'actionBars.deleteMigrate.cancel':        'Cancelar',
      'modal.actionBars.titlePrefix':           'Barras de acciones —',
      'modal.actionBars.hint':                  'Barras asignadas a este nivel. Las barras de carpeta agregan elementos de todos los conjuntos de reglas activos de la carpeta, las de grupo — solo del grupo, las de conjunto de reglas — solo cuando el conjunto está activo. Una barra sin elementos visibles no se renderizará.',
      'modal.actionBars.add':                   '+ Añadir barra nueva',
      'modal.actionBars.close':                 'Cerrar',

      // === Windows — dropdown type selector (since v2.13.41) ===
      'windows.btn.label':                      'Ventanas',
      'windows.btn.title':                      'Gestionar las ventanas asignadas a esta carpeta (CSS / JS / consolas)',
      'windows.btn.titleGroup':                 'Gestionar las ventanas asignadas a este grupo',
      'windows.btn.titleRule':                  'Gestionar las ventanas asignadas a este conjunto de reglas',
      'windows.menu.title':                     'Elige el tipo de ventana:',
      'windows.menu.comingSoon':                'próximamente',
      'jsPanes.btn.label':                      'Paneles JS',
      'jsConsoles.btn.label':                   'Consolas JS',
      'outputConsoles.btn.label':               'Consolas de salida',

      // === CSS panes (Phase 2 "Windows on the front", since v2.13.40) ===
      'cssPanes.btn.label':                     'Paneles CSS',
      'cssPanes.contextMenu.disable':           'Cerrar panel CSS',
      'cssPanes.btn.title':                     'Gestionar los paneles CSS asignados a esta carpeta',
      'cssPanes.btn.titleGroup':                'Gestionar los paneles CSS asignados a este grupo',
      'cssPanes.btn.titleRule':                 'Gestionar los paneles CSS asignados a este conjunto de reglas',
      'cssPanes.empty':                         'No hay paneles CSS en este nivel. Haz clic en "+ Añadir panel CSS nuevo" para crear el primero.',
      'cssPanes.section.owned':                 'Paneles en este nivel',
      'cssPanes.name.placeholder':              'Nombre del panel',
      'cssPanes.color.title':                   'Color del punto del encabezado',
      // Custom pane colors (since v2.13.58) — shared CSS / JS pane.
      'panes.style.dot':                        'Punto',
      'panes.style.headerBg':                   'Fondo del encabezado',
      'panes.style.headerText':                 'Texto del encabezado',
      'panes.style.bodyBg':                     'Fondo del cuerpo',
      'panes.style.bodyText':                   'Texto del cuerpo',
      'panes.style.runBtn':                     'Botón de ejecutar',
      'panes.style.reset':                      'Borrar',
      'mcp.default.title':                      'Restaurar predeterminado',
      // Action color labels (since v2.13.60) — short, no instructions.
      'action.colors.bg':                       'Fondo',
      'action.colors.text':                     'Texto',
      'action.colors.dropdownBg':               'Fondo del desplegable',
      'action.colors.option':                   'Opción',
      'action.colors.selected':                 'Seleccionada',
      'action.colors.hover':                    'Cursor encima',
      'action.colors.label':                    'Etiqueta',
      'action.colors.value':                    'Valor',
      'action.colors.placeholder':              'Marcador de posición',
      'action.colors.bgInactive':               'Fondo inactivo',
      'action.colors.inactiveText':             'Texto inactivo',
      'cssPanes.enabled.label':                 'Habilitado',
      'cssPanes.enabled.title':                 'Indica si el panel debe aparecer en las páginas que coincidan',
      'cssPanes.delete.title':                  'Eliminar este panel',
      'cssPanes.delete.confirm':                '¿Eliminar el panel CSS ":name"? Se perderá el contenido del área de texto en las pestañas activas.',
      'modal.cssPanes.titlePrefix':             'Paneles CSS —',
      'modal.cssPanes.hint':                    'Área de texto flotante inyectada en las páginas que coincidan con las URLs de este nivel. El contenido se guarda en sessionStorage por pestaña (se pierde al cerrar la pestaña). Inyección de <style> en vivo — los cambios de CSS se aplican al instante.',
      'modal.cssPanes.add':                     '+ Añadir panel CSS nuevo',
      'modal.cssPanes.close':                   'Cerrar',
      'cssPane.frontend.close.title':           'Ocultar el panel (permanecerá oculto hasta cerrar la pestaña)',

      // === JS panes (Phase 2 cont., since v2.13.56) ===
      'jsPanes.contextMenu.disable':            'Cerrar panel JS',
      'jsPanes.btn.title':                      'Gestionar los paneles JS asignados a esta carpeta',
      'jsPanes.btn.titleGroup':                 'Gestionar los paneles JS asignados a este grupo',
      'jsPanes.btn.titleRule':                  'Gestionar los paneles JS asignados a este conjunto de reglas',
      'jsPanes.empty':                          'No hay paneles JS en este nivel. Haz clic en "+ Añadir panel JS nuevo" para crear el primero.',
      'jsPanes.section.owned':                  'Paneles en este nivel',
      'jsPanes.name.placeholder':               'Nombre del panel',
      'jsPanes.color.title':                    'Color del punto del encabezado',
      'jsPanes.enabled.label':                  'Habilitado',
      'jsPanes.enabled.title':                  'Indica si el panel debe aparecer en las páginas que coincidan',
      'jsPanes.delete.title':                   'Eliminar este panel',
      'jsPanes.delete.confirm':                 '¿Eliminar el panel JS ":name"? Se perderá el contenido del área de texto en las pestañas activas.',
      'modal.jsPanes.titlePrefix':              'Paneles JS —',
      'modal.jsPanes.hint':                     'Área de texto flotante que ejecuta JS en el contexto de la página (mundo MAIN, new Function) con cada cambio. El contenido se guarda por pestaña en sessionStorage. Los errores de ejecución se muestran en una superposición roja debajo del código.',
      'modal.jsPanes.add':                      '+ Añadir panel JS nuevo',
      'modal.jsPanes.close':                    'Cerrar',
      'jsPane.frontend.close.title':            'Ocultar el panel (permanecerá oculto hasta cerrar la pestaña)',

      // === JS Consoles (Phase 2 cont., since v2.13.66) ===
      'jsConsoles.contextMenu.disable':         'Cerrar consola JS',
      'jsConsoles.empty':                       'No hay consolas en este nivel. Haz clic en "+ Añadir consola nueva" para crear la primera.',
      'jsConsoles.section.owned':               'Consolas en este nivel',
      'jsConsoles.name.placeholder':            'Nombre de la consola',
      'jsConsoles.enabled.label':               'Habilitada',
      'jsConsoles.enabled.title':               'Indica si la consola debe aparecer en las páginas que coincidan',
      'jsConsoles.delete.title':                'Eliminar esta consola',
      'jsConsoles.delete.confirm':              '¿Eliminar la consola ":name"? Se perderá el historial de salida en las pestañas activas.',
      'modal.jsConsoles.titlePrefix':           'Consolas JS —',
      'modal.jsConsoles.hint':                  'REPL interactivo — Ctrl+Enter ejecuta el código en el mundo MAIN de la página, la salida (o el error) se añade al área de registro debajo de la entrada. La salida se guarda por pestaña en sessionStorage.',
      'modal.jsConsoles.add':                   '+ Añadir consola nueva',
      'modal.jsConsoles.close':                 'Cerrar',
      'jsConsole.frontend.close.title':         'Ocultar la consola (hasta cerrar la pestaña)',
      'jsConsole.input.placeholder':            'Código a ejecutar (Ctrl+Enter para ejecutar, Ctrl+L para borrar)',
      'jsConsole.clear.title':                  'Borrar la salida (Ctrl+L)',

      // === Output Console (Phase 2 finish, since v2.13.72) — read-only log viewer. ===
      'panes.style.clearBtn':                   'Botón de borrar',
      'panes.style.actionBtns':                 'Botones de acción',
      'outputConsoles.contextMenu.disable':     'Cerrar consola de salida',
      'outputConsoles.empty':                   'No hay consolas de salida en este nivel. Haz clic en "+ Añadir consola de salida nueva" para crear la primera.',
      'outputConsoles.section.owned':           'Consolas de salida en este nivel',
      'outputConsoles.name.placeholder':        'Nombre de la consola de salida',
      'outputConsoles.enabled.label':           'Habilitada',
      'outputConsoles.enabled.title':           'Indica si la consola de salida debe aparecer en las páginas que coincidan',
      'outputConsoles.delete.title':            'Eliminar esta consola de salida',
      'outputConsoles.delete.confirm':          '¿Eliminar la consola de salida ":name"? Se perderá el historial de registro en las pestañas activas.',
      'modal.outputConsoles.titlePrefix':       'Consolas de salida —',
      'modal.outputConsoles.hint':              'Visor de registro de solo lectura — captura console.log/warn/error/info de la página (incluido el código asíncrono). Borrar (⌫) solo limpia el visor, no la consola del navegador.',
      'modal.outputConsoles.add':               '+ Añadir consola de salida nueva',
      'modal.outputConsoles.close':             'Cerrar',
      'outputConsole.frontend.close.title':     'Ocultar la consola de salida (hasta cerrar la pestaña)',
      'outputConsole.clear.title':              'Borrar el visor (Ctrl+Shift+L)',
      'outputConsole.autoscroll.title':         'Desplazamiento automático hasta los registros más recientes',
      'outputConsole.empty':                    '(sin registros — esperando console.* / JZ.* de la página)',
      // viewMode + filters + search (since v2.13.73, per spec section 6).
      'outputConsole.mode.standard.title':      'Mostrar solo console.* (clic / Ctrl+Shift+K → JZ.*)',
      'outputConsole.mode.jz.title':            'Mostrar solo window.JZ.* (clic / Ctrl+Shift+K → ambos)',
      'outputConsole.mode.both.title':          'Mostrar ambos: console.* + JZ.* (clic / Ctrl+Shift+K → solo console.*)',
      'outputConsole.filters.title':            'Filtros de nivel',
      'outputConsole.filters.level.log':        'log',
      'outputConsole.filters.level.info':       'info',
      'outputConsole.filters.level.warn':       'warn',
      'outputConsole.filters.level.error':      'error',
      'outputConsole.filters.level.debug':      'debug',
      'outputConsole.search.title':             'Buscar en los registros (Ctrl+Shift+S; Esc borra)',
      'outputConsole.search.placeholder':       'Filtrar entradas…',
      'outputConsole.tab.all':                  'Todo',
      'outputConsole.tab.jz':                   'JZconsole',
      'outputConsole.tab.console':              'Consola',
      'outputConsole.tab.network':              'Red',
      'outputConsole.tab.errors':               'Errores',
      'outputConsole.tab.datalayer':            'DataLayer',
      'outputConsole.net.field.callTime':       'Hora de la llamada',
      'outputConsole.net.field.status':         'Estado',
      'outputConsole.net.field.type':           'Tipo',
      'outputConsole.net.field.method':         'Método',
      'outputConsole.net.field.domain':         'Dominio',
      'outputConsole.net.field.path':           'Ruta',
      'outputConsole.net.field.remote':         'Dirección remota',
      'outputConsole.net.field.initiator':      'Iniciador',
      'outputConsole.net.field.time':           'Tiempo',
      'outputConsole.net.field.size':           'Tamaño',
      'outputConsole.net.field.url':            'URL',
      'outputConsole.net.field.error':          'Error',
      'outputConsole.net.status.err':           'Errores/Otros',
      'outputConsole.filter.all':               'todo',
      'outputConsole.filter.none':              'ninguno',
      'outputConsole.net.domainFilter':         'filtrar por dominio…',
      'outputConsole.net.domainsInclude':       'Dominios+',
      'outputConsole.net.domainsExclude':       'Dominios-',
      'outputConsole.dl.newPushes':             'Nuevos pushes',
      'outputConsole.dl.currentState':          'Estado actual',
      'outputConsole.dl.expandAll':             'expandir todo',
      'outputConsole.dl.collapseAll':           'contraer todo',
      'outputConsole.net.size':                 'Tamaño',
      'outputConsole.net.time':                 'Tiempo',
      'outputConsole.err.field.type':           'Tipo',
      'outputConsole.err.field.location':       'Ubicación',
      'outputConsole.err.field.stack':          'Pila',
      // API help popup (since v2.13.78, Ctrl+Shift+H).
      'outputConsole.apiHelp.title': 'JustZix — atajos y ventanas en la página',
      'outputConsole.apiHelp.sec.global': 'Atajos globales — funcionan en cualquier página',
      'outputConsole.apiHelp.g.tempCss': 'Ventana TEMP CSS — abrir / cerrar',
      'outputConsole.apiHelp.g.tempJs': 'Ventana TEMP JS — abrir / cerrar',
      'outputConsole.apiHelp.g.tempJsc': 'Consola TEMP JS — abrir / cerrar',
      'outputConsole.apiHelp.g.tempOut': 'Consola TEMP Output — abrir / cerrar',
      'outputConsole.apiHelp.g.ai': 'AI Helper — abrir / cerrar',
      'outputConsole.apiHelp.g.edit': 'Modo edición de barra de acciones — activar / desactivar',
      'outputConsole.apiHelp.sec.window': 'Dentro de una ventana — cuando tiene el foco',
      'outputConsole.apiHelp.w.run': 'Ejecutar el código / enviar el mensaje — panel JS, Consola JS, AI Helper',
      'outputConsole.apiHelp.w.history': 'Historial de comandos escritos — Consola JS',
      'outputConsole.apiHelp.w.clearConsole': 'Limpiar la salida — Consola JS',
      'outputConsole.apiHelp.w.escSearch': 'Limpiar el campo de búsqueda — Consola Output',
      'outputConsole.apiHelp.sec.output': 'Consola Output',
      'outputConsole.apiHelp.o.clear': 'Limpiar la pestaña activa de cada Consola Output',
      'outputConsole.apiHelp.o.help': 'Mostrar / ocultar esta ayuda',
      'outputConsole.apiHelp.o.tabs': '6 pestañas: All, JZ, Console, Network, Errors, DataLayer — cada una con su propio conjunto de filtros.',
      'outputConsole.apiHelp.o.network': 'Network: Alt+clic en una fila añade su dominio al filtro de inclusión, Alt+Shift+clic al filtro de exclusión.',
      'outputConsole.apiHelp.o.datalayer': 'DataLayer: arrastra la barra entre las columnas para redimensionarlas.',
      'outputConsole.apiHelp.sec.mouse': 'Ventanas en la página — con el ratón',
      'outputConsole.apiHelp.m.drag': 'Arrastra una ventana por su barra de cabecera para reposicionarla.',
      'outputConsole.apiHelp.m.resize': 'Redimensiona una ventana tirando de una de sus 8 asas en los bordes y las esquinas.',
      'outputConsole.apiHelp.m.snap': 'Acerca una ventana a otra — se imantan en un grupo que se mueve junto.',
      'outputConsole.apiHelp.m.context': 'Un clic derecho en la barra de cabecera abre un menú: cerrar ventana, desconectar, fuente, tamaño del texto.',
      'outputConsole.apiHelp.m.tempDbl': 'Un doble clic en la barra de cabecera de una ventana TEMP alterna entre el tamaño predeterminado y el recordado.',
      'outputConsole.apiHelp.m.scroll': 'Desplazar con la rueda dentro de una ventana desplaza solo su contenido, no la página de debajo.',
      'outputConsole.apiHelp.sec.editmode': 'Modo edición de barra — tras Ctrl+Alt+S',
      'outputConsole.apiHelp.em.undo': 'Deshacer el último cambio',
      'outputConsole.apiHelp.em.delete': 'Eliminar las etiquetas seleccionadas',
      'outputConsole.apiHelp.em.deselect': 'Deseleccionar todos los elementos',
      'outputConsole.apiHelp.em.move': 'Arrastra los elementos seleccionados con el ratón — varios se mueven juntos.',
      'outputConsole.apiHelp.em.select': 'Un clic selecciona un elemento, Ctrl+clic lo añade o quita de la selección, Shift+arrastrar sobre un área vacía selecciona con un rectángulo.',
      'outputConsole.apiHelp.em.label': 'El botón „+ Etiqueta" añade un rótulo de texto estático; doble clic en una etiqueta abre su editor.',
      'outputConsole.apiHelp.sec.logging': 'Registro en la Consola Output',
      'outputConsole.apiHelp.jzApi': 'Logger propio (kind=jz)',
      'outputConsole.apiHelp.aliases': 'Alias: window.__JUSTZIX__ (siempre), window.JZ (si la página no lo ocupa).',
      'outputConsole.apiHelp.consoleApi': 'console.* estándar (kind=console, capturado automáticamente)',
      'outputConsole.apiHelp.consoleNote': 'Cada console.* de la página se transfiere a la consola original (las DevTools siguen funcionando) y se envía a la Consola Output.',
      'outputConsole.apiHelp.footer': 'Manual completo (8 idiomas): panel JustZix → Ayuda.',
      // === Onboarding / tutorial ===
      'content.exec.cspBlocked': 'Esta página bloquea la ejecución de scripts (CSP). Activa «Scripts de usuario» para JustZix en los ajustes del navegador.',
      'onboard.ui.back': 'Atrás',
      'onboard.ui.next': 'Siguiente',
      'onboard.ui.finish': 'Finalizar',
      'onboard.ui.copy': 'Copiar',
      'onboard.ui.copied': 'Copiado',
      'onboard.ui.hint': 'Realiza esta acción para continuar',
      'onboard.ui.close': 'Cerrar',
      'onboard.front.title': 'Tutorial del front-end',
      'onboard.front.lead': 'Descubre las herramientas de JustZix disponibles directamente en la página: el botón flotante, el panel, las ventanas de código y las barras de acciones.',
      'onboard.front.titleFirst': 'JustZix en la página',
      'onboard.front.leadFirst': 'JustZix también funciona directamente en la página. Este tutorial muestra el botón flotante, las ventanas CSS/JS y las barras de acciones, paso a paso.',
      'onboard.front.empty': 'Los tutoriales del front-end llegarán pronto.',
      'onboard.lr.titleFirst': 'Te damos la bienvenida a JustZix',
      'onboard.lr.title': 'Centro de tutoriales',
      'onboard.lr.leadFirst': '¿Primera vez aquí? El tutorial interactivo te guía en la creación de una solución completa y funcional — paso a paso, directamente en el panel.',
      'onboard.lr.lead': 'Inicia la guía interactiva — te acompaña paso a paso en la creación de una solución terminada.',
      'onboard.lr.scenarios': 'Tutoriales',
      'onboard.lr.start': 'Empezar',
      'onboard.lr.later': 'Quizás más tarde',
      'onboard.lr.noAuto': 'No mostrar al iniciar',
      'onboard.proj.name': 'Tu primer proyecto',
      'onboard.proj.desc': 'De cero a una solución funcional: una carpeta, una barra de acciones, un grupo, un conjunto de reglas con CSS y JS y un botón que alterna el efecto.',
      'onboard.proj.meta': '19 pasos · ~3 min',
      'onboard.proj.s1.title': 'Te damos la bienvenida a JustZix',
      'onboard.proj.s1.body': 'JustZix inyecta tu propio CSS y JavaScript en las páginas elegidas. Ahora construiremos una solución completa con un botón funcional — paso a paso. Lleva unos 3 minutos.',
      'onboard.proj.s2.title': 'Añadir una carpeta',
      'onboard.proj.s2.body': 'Una carpeta agrupa las reglas de un sitio. Haz clic en el botón <code>+</code> resaltado del panel lateral.',
      'onboard.proj.s3.title': 'Nombre de la carpeta',
      'onboard.proj.s3.body': 'Escribe un nombre, p. ej. <code>Mi primer proyecto</code> — es una etiqueta solo para ti.',
      'onboard.proj.s4.title': 'Patrón de URL',
      'onboard.proj.s4.body': 'Decide en qué páginas funciona la carpeta. Escribe una dirección con asterisco y pulsa Intro, p. ej. <code>https://example.com/*</code>. Para practicar en cualquier página, escribe solo <code>*</code>.',
      'onboard.proj.s5.title': 'Etiqueta del botón',
      'onboard.proj.s5.body': 'Hasta 3 letras — aparecerán en el botón flotante de JustZix visible en la página.',
      'onboard.proj.s6.title': 'Barra de acciones',
      'onboard.proj.s6.body': 'Una barra de acciones es un panel con botones interactivos visible en la página. Creemos una primero — haz clic en el botón de barras de acciones resaltado.',
      'onboard.proj.s7.title': 'Añadir una barra',
      'onboard.proj.s7.body': 'Haz clic para crear una nueva barra de acciones en esta carpeta.',
      'onboard.proj.s8.title': 'Cerrar el gestor',
      'onboard.proj.s8.body': 'La barra está lista. Cierra la ventana del gestor de barras para volver a la carpeta.',
      'onboard.proj.s9.title': 'Abrir el grupo',
      'onboard.proj.s9.body': 'Al crear la carpeta, JustZix ya añadió un primer grupo. En el panel lateral despliega la carpeta y haz clic en ese grupo para abrirlo, luego pulsa "Siguiente".',
      'onboard.proj.s10.title': 'Añadir un conjunto de reglas',
      'onboard.proj.s10.body': 'Un conjunto de reglas es la regla en sí — un par de código CSS y JavaScript. Haz clic en el botón de añadir conjunto de reglas resaltado.',
      'onboard.proj.s11.title': 'Abrir el conjunto de reglas',
      'onboard.proj.s11.body': 'El nuevo conjunto de reglas apareció en el árbol bajo el grupo. Haz clic en él para abrir el editor del conjunto de reglas, luego pulsa "Siguiente".',
      'onboard.proj.s12.title': 'Código CSS',
      'onboard.proj.s12.body': 'La pestaña CSS ya está activa. Pega el código de abajo — pone toda la página en escala de grises y prepara el interruptor del efecto.',
      'onboard.proj.s13.title': 'Pestaña JavaScript',
      'onboard.proj.s13.body': 'Haz clic en la pestaña <code>JavaScript</code> para añadir código JS al conjunto de reglas.',
      'onboard.proj.s14.title': 'Código JavaScript',
      'onboard.proj.s14.body': 'El código JS se ejecuta al abrir la página. Pega el código de abajo — muestra un mensaje que confirma que JustZix funciona. El campo tiene validación de sintaxis.',
      'onboard.proj.s15.title': 'Pestaña Acciones',
      'onboard.proj.s15.body': 'Ahora añadiremos un botón interactivo. Haz clic en la pestaña de acciones del editor del conjunto de reglas.',
      'onboard.proj.s16.title': 'Añadir un botón',
      'onboard.proj.s16.body': 'Haz clic para añadir un elemento de acción. Por defecto crea un botón que ejecuta código a demanda.',
      'onboard.proj.s17.title': 'Código del botón',
      'onboard.proj.s17.body': 'Cada botón tiene su propio código que se ejecuta al hacer clic en la página. Haz clic en el campo de código del nuevo botón.',
      'onboard.proj.s18.title': 'Código del interruptor',
      'onboard.proj.s18.body': 'Pega el código que activa y desactiva la escala de grises definida en CSS. Después guarda los cambios en la ventana del editor de código.',
      'onboard.proj.s19.title': '¡Listo!',
      'onboard.proj.s19.body': 'Tienes una solución completa: una carpeta, una barra de acciones, un grupo, un conjunto de reglas con código CSS y JS y un botón funcional. Abre una página que coincida con el patrón de URL — verás el mensaje, la escala de grises y el botón que la alterna. Puedes reiniciar el tutorial desde el menú de ajustes.',
      // Font + size context menu (since v2.13.70) — shared by CSS/JS pane + JS Console.
      'panes.contextMenu.font':                 'Fuente',
      'panes.contextMenu.size':                 'Tamaño',
      'panes.contextMenu.defaultFont':          'Predeterminada',
      'modal.confirmDismissDirty':              'Tienes cambios sin guardar. ¿Cerrar y perderlos?',
      'modal.confirmSaveBeforeClose':           'Tienes cambios sin guardar.\n\nHaz clic en Aceptar para guardar y cerrar.\nHaz clic en Cancelar para pasar a la confirmación de descarte.',
      'modal.confirmDiscardChanges':            '¿Cerrar SIN guardar? Se perderán los cambios.\n\nHaz clic en Aceptar para descartar los cambios.\nHaz clic en Cancelar para seguir editando.',
      'actionBars.moveLocked':                  'No se puede mover — la barra tiene elementos que quedarían fuera del nuevo ámbito. Reasígnalos primero.',
      'action.bar.noneOption':                  '— sin barra —',
      'action.bar.unnamed':                     '(sin nombre)',
      'action.bar.selectTitle':                 'Asignar esta acción a una barra. Las barras se heredan hacia abajo (carpeta → grupo → regla).',
      'action.bar.noBarsHint':                  'No hay barras de acciones en el árbol del conjunto de reglas — añade una barra con el botón "Barras de acciones" en la carpeta/grupo/regla.',

      // === Typed actions (since v2.12.x) ===
      'action.color.title':                     'Color de fondo (botón/select/input/control deslizante). Clic = selector, o escribe el hex al lado.',
      'action.colorText.title':                 'Color del texto (y relleno de la barra para SLIDER). Predeterminado a blanco si no se establece.',
      'action.colorHex.title':                  'Código hex — copia (Ctrl+C) o pega (Ctrl+V) como texto sin formato. Acepta #RGB o #RRGGBB.',
      'action.type.title':                      'Tipo de elemento (botón, desplegable SELECT, …)',
      'action.type.button':                     'Botón',
      'action.type.select':                     'Desplegable (SELECT)',
      'action.type.input':                      'Campo de texto (INPUT)',
      'action.type.slider':                     'Control deslizante de rango (SLIDER)',
      'action.type.textarea':                   'Campo multilínea (TEXTAREA)',
      'action.type.toggle3':                    'Conmutador de 3 estados (TOGGLE3)',

      // Memory scope (since v2.13.0; ruleSet added v2.13.4) — shared across state-bearing types
      'action.memory.label':                    'Recordar el valor:',
      'action.memory.none':                     'No recordar (se restablece al recargar)',
      'action.memory.page':                     'Por página (URL)',
      'action.memory.host':                     'Por dominio',
      'action.memory.ruleSet':                  'Dentro del conjunto de reglas (todas las URLs donde se ejecuta)',
      'action.memory.global':                   'Globalmente (todas las páginas, compartido)',
      'action.input.middleBtn':                 'INPUT · código :code',
      'action.slider.middleBtn':                'SLIDER · :min–:max paso :step :unit · código :code',
      'action.textarea.middleBtn':              'TEXTAREA · :rows filas · código :code',
      'action.toggle3.middleBtn':               'TOGGLE3 · :filled/3 estados · código :code',
      'action.toggle3.defaultState1':           'OFF',
      'action.toggle3.defaultState2':           'MID',
      'action.toggle3.defaultState3':           'ON',
      'action.toggle3.stateLabel.placeholder':  'etiqueta (máx. 5)',
      'action.toggle3.stateValue.placeholder':  'valor (para JS)',
      'action.toggle3.defaultStateTitle':       'Estado predeterminado (cuando no hay memoria)',

      'action.select.middleBtn.staticEmpty':    'SELECT estático (sin opciones)',
      'action.select.middleBtn.static':         'SELECT estático · :count opciones · código :code',
      'action.select.middleBtn.js':             'SELECT js · código :code',
      'action.select.empty':                    'No hay opciones. Añade la primera con el botón de abajo.',
      'action.select.untitled':                 '(sin título)',
      'action.select.optionNamePlaceholder':    'Nombre (visible)',
      'action.select.optionValuePlaceholder':   'valor (pasado a JS)',
      'action.select.deleteOption':             'Eliminar esta opción',

      // === Modal — action code ===
      'modal.actionCode.titlePrefix':           'Código del botón de acción:',
      'modal.actionCode.body':                  'Código JavaScript que se ejecuta al hacer clic en el botón de acción del panel flotante. Se ejecuta en el contexto de la página.',
      'modal.actionCode.cancel':                'Cancelar',
      'modal.actionCode.save':                  'Guardar',

      // === Modal — INPUT editor (since v2.13.0) ===
      'modal.inputEditor.titlePrefix':          'Editor de INPUT:',
      'modal.inputEditor.body':                 'Campo de texto con código JS opcional que se activa al cambiar (Enter/desenfoque). El valor se recuerda según el ámbito seleccionado.',
      'modal.inputEditor.placeholder':          'Marcador de posición:',
      'modal.inputEditor.defaultValue':         'Valor predeterminado:',
      'modal.inputEditor.colorPlaceholder':     'Color del marcador de posición:',
      'modal.inputEditor.codeLabel':            'Código JS (al cambiar):',
      'modal.inputEditor.hint':                 'Disponible en el código: <code>value</code> (cadena del input), <code>$el</code> (HTMLInputElement), <code>$action</code> (id, label).',
      'modal.inputEditor.close':                'Cerrar',
      'modal.colorClear':                       'Restaurar el color predeterminado',
      'modal.sliderEditor.colorLabel':          'Color de la etiqueta (izquierda):',
      'modal.sliderEditor.colorValue':          'Color del valor (derecha):',
      'modal.selectOptions.appearance.title':   'Apariencia del desplegable:',
      'modal.selectOptions.appearance.hint':    'Para la <strong>variante js</strong>: el fondo del desplegable y el color de las opciones funcionan en todos los navegadores. Los fondos de selección y de cursor encima dependen del navegador (del SO) — para una personalización completa usa la variante <em>estática</em>.',
      'modal.selectOptions.colorDropdownBg':    'Fondo del desplegable:',
      'modal.selectOptions.colorOption':        'Color del texto de las opciones:',
      'modal.selectOptions.colorSelected':      'Fondo de la opción seleccionada:',
      'modal.selectOptions.colorHover':         'Fondo al pasar el cursor:',
      'modal.sliderEditor.titlePrefix':         'Editor de SLIDER:',
      'modal.sliderEditor.body':                'Control deslizante de rango con código JS opcional que se activa después de que el usuario suelta el control (`change`). El valor se pasa como un Number.',
      'modal.sliderEditor.min':                 'Mínimo:',
      'modal.sliderEditor.max':                 'Máximo:',
      'modal.sliderEditor.step':                'Paso:',
      'modal.sliderEditor.defaultValue':        'Valor predeterminado:',
      'modal.sliderEditor.unit':                'Unidad (opcional):',
      'modal.sliderEditor.codeLabel':           'Código JS (al cambiar):',
      'modal.sliderEditor.hint':                'Disponible en el código: <code>value</code> (Number), <code>$el</code> (HTMLInputElement type=range), <code>$action</code> (id, label).',
      'modal.sliderEditor.close':               'Cerrar',
      'modal.sliderEditor.errMaxGtMin':         'El máximo debe ser mayor que el mínimo.',
      'modal.sliderEditor.errStepPositive':     'El paso debe ser positivo.',
      'modal.textareaEditor.titlePrefix':       'Editor de TEXTAREA:',
      'modal.textareaEditor.body':              'Campo de texto multilínea con código JS opcional al cambiar (<code>change</code>, desenfoque). <strong>Enter</strong> en el textarea es un salto de línea legítimo — NO activa el código (a diferencia de INPUT).',
      'modal.textareaEditor.placeholder':       'Marcador de posición:',
      'modal.textareaEditor.defaultValue':      'Valor predeterminado:',
      'modal.textareaEditor.rows':              'Número de filas:',
      'modal.textareaEditor.colorPlaceholder':  'Color del marcador de posición:',
      'modal.textareaEditor.codeLabel':         'Código JS (al cambiar/desenfocar):',
      'modal.textareaEditor.hint':              'Disponible en el código: <code>value</code> (cadena del textarea, puede contener <code>\\n</code>), <code>$el</code> (HTMLTextAreaElement), <code>$action</code> (id, label).',
      'modal.textareaEditor.close':             'Cerrar',

      // === Modal — TOGGLE3 editor (od v2.13.25) ===
      'modal.toggle3Editor.titlePrefix':        'Editor de TOGGLE3:',
      'modal.toggle3Editor.body':               'Control segmentado de 3 estados. Al hacer clic en un estado inactivo se activa y ejecuta el código JS con el <code>value</code> del estado elegido. La memoria conserva el índice actual (0-2) por acción.',
      'modal.toggle3Editor.statesTitle':        'Estados (exactamente 3, el radio = predeterminado):',
      'modal.toggle3Editor.colorBg':            'Color de fondo:',
      'modal.toggle3Editor.colorHover':         'Color al pasar el cursor:',
      'modal.toggle3Editor.colorInactiveText':  'Color del texto inactivo:',
      'modal.toggle3Editor.codeLabel':          'Código JS (al cambiar de estado):',
      'modal.toggle3Editor.hint':               'Disponible en el código: <code>value</code> (state.value, recurre a state.label), <code>stateIdx</code> (0-2), <code>stateLabel</code>, <code>$el</code> (div contenedor), <code>$action</code>.',
      'modal.toggle3Editor.close':              'Cerrar',

      // === Modal — SELECT editor (since v2.12.1, redesign v2.12.2 — variants) ===
      'modal.selectOptions.titlePrefix':        'Editor de SELECT:',
      'modal.selectOptions.idLabel':            'ID de la acción:',
      'modal.selectOptions.selectorLabel':      'Selector JS:',
      'modal.selectOptions.copyId':             'Copiar ID',
      'modal.selectOptions.copySelector':       'Copiar selector JS',
      'modal.selectOptions.variant.static':     'Opciones estáticas + un único código JS',
      'modal.selectOptions.variant.js':         'Solo código JS (gestiona el desplegable)',
      'modal.selectOptions.static.body':        'Cada opción: <strong>nombre</strong> (visible en el desplegable) + <strong>valor</strong> (pasado al código como la variable <code>value</code>). Un único código compartido se ejecuta al cambiar.',
      'modal.selectOptions.static.hint':        'Disponible en el código: <code>value</code> (cadena), <code>$el</code> (elemento select), <code>$action</code> (id, label).',
      'modal.selectOptions.staticCodeLabel':    'Código JS (compartido):',
      'modal.selectOptions.js.body':            'Tu código JS se ejecuta una vez después de insertar el desplegable en la página. Recibe la referencia <code>$el</code> a un <code>&lt;select&gt;</code> vacío — tú rellenas las opciones y gestionas <code>change</code> por tu cuenta.',
      'modal.selectOptions.js.hint':            'Disponible: <code>$el</code> (HTMLSelectElement), <code>$action</code> (id, label).',
      'modal.selectOptions.jsCodeLabel':        'Código JS:',
      'modal.selectOptions.addOption':          '+ Añadir opción',
      'modal.selectOptions.close':              'Cerrar',

      // === Modal — sync conflict ===
      'modal.syncConflict.title':               'Conflicto de sincronización',
      'modal.syncConflict.description':         'Has iniciado sesión en una cuenta que ya tiene datos en la nube, y este dispositivo también tiene una biblioteca local. Elige qué hacer — la operación es irreversible localmente, pero puedes exportar los datos locales antes de confirmar.',
      'modal.syncConflict.localLabel':          'En este dispositivo, localmente:',
      'modal.syncConflict.serverLabel':         'En la cuenta de la nube:',
      'modal.syncConflict.useServer.title':     'Usar los datos de la cuenta de la nube',
      'modal.syncConflict.useServer.body':      'Las carpetas/grupos/reglas locales se reemplazarán por los de la cuenta. Elige esta opción si la cuenta tiene datos más recientes y precisos que este dispositivo.',
      'modal.syncConflict.sendMine.title':      'Enviar mis datos locales a la cuenta',
      'modal.syncConflict.sendMine.body':       'El estado de la cuenta se reemplazará por lo que tienes en este dispositivo. Elige esta opción si este dispositivo tiene datos más recientes y precisos.',
      'modal.syncConflict.merge.title':         'Combinar ambos conjuntos',
      'modal.syncConflict.merge.body':          'Las entidades locales y de la nube se conservarán juntas. Si el mismo elemento existe en ambos sitios (mismo ID), gana la versión de la cuenta. Los elementos exclusivamente locales se enviarán a la cuenta en la próxima sincronización.',
      'modal.syncConflict.exportLocal':         'Exportar los datos locales primero',
      'modal.syncConflict.cancel':              'Cancelar el inicio de sesión (cerrar sesión)',

      // === Modal — share import ===
      'modal.shareImport.title':                'Importar desde un enlace de JustZix',
      'modal.shareImport.body':                 'Pega el enlace completo o solo el token <strong>JZS-XXXX-XXXX-XXXX-XXXX</strong>:',
      'modal.shareImport.placeholder':          'https://www.justzix.com/s/JZS-XXXX-XXXX-XXXX-XXXX',
      'modal.shareImport.cancel':               'Cancelar',
      'modal.shareImport.fetch':                'Obtener',
      'modal.shareImport.preview.title':        'Vista previa de la exportación compartida',
      'modal.shareImport.jsWarn':               '<strong>⚠ ADVERTENCIA:</strong> La exportación contiene código JavaScript. El código se ejecutará en las páginas que coincidan con las reglas — revisa lo que hace antes de importar.',
      'modal.shareImport.showCode':             'Mostrar el código JavaScript',
      'modal.shareImport.back':                 '← Atrás',
      'modal.shareImport.confirm':              'Importar',

      // === Modal — my shares ===
      'modal.shares.title':                     'Mis enlaces compartidos',
      'modal.shares.body':                      'Enlaces públicos temporales a exportaciones. El servidor conserva el contenido hasta la caducidad y después lo elimina.',
      'modal.shares.howTo':                     'Para crear un enlace nuevo — abre una exportación (completa / carpeta / grupo / conjunto de reglas) y haz clic en <strong>"Compartir URL"</strong>.',
      'modal.shares.refresh':                   'Actualizar',
      'modal.shares.close':                     'Cerrar',

      // === Modal — share create ===
      'modal.shareCreate.title':                'Compartir la exportación mediante un enlace',
      'modal.shareCreate.activeFor':            'El enlace estará activo durante:',
      'modal.shareCreate.ttl.1h':               '1 hora',
      'modal.shareCreate.ttl.6h':               '6 horas',
      'modal.shareCreate.ttl.12h':              '12 horas',
      'modal.shareCreate.ttl.24h':              '24 horas',
      'modal.shareCreate.ttl.default':          '(predeterminado)',
      'modal.shareCreate.ttl.48h':              '48 horas',
      'modal.shareCreate.expireNote':           'Tras la caducidad, el enlace dejará de funcionar. Puedes revocarlo antes desde "Mis enlaces compartidos".',
      'modal.shareCreate.cancel':               'Cancelar',
      'modal.shareCreate.generate':             'Generar enlace',
      'modal.shareCreate.result.title':         'Enlace generado',
      'modal.shareCreate.result.urlLabel':      'Envía este enlace al destinatario:',
      'modal.shareCreate.result.copyUrl':       'Copiar enlace',
      'modal.shareCreate.result.tokenLabel':    'O solo el token (para pegarlo en "Importar desde URL"):',
      'modal.shareCreate.result.copyToken':     'Copiar token',
      'modal.shareCreate.result.goToMine':      'Ver "Mis enlaces compartidos"',
      'modal.shareCreate.result.close':         'Cerrar',

      // === Modal — sync result ===
      'modal.syncResult.title':                 'Resultado de la sincronización',
      'modal.syncResult.close':                 'Entendido',

      // === Modal — cloud sync ===
      'modal.sync.title':                       'Sincronización en la nube',
      'modal.sync.signedOut.tab.register':      'Primer dispositivo',
      'modal.sync.signedOut.tab.login':         'Tengo una clave',
      'modal.sync.signedOut.tab.recover':       'Perdí mi clave',
      'modal.sync.signedOut.register.body':     'Genera una nueva clave de sincronización. <strong>Guárdala en un lugar seguro</strong> — el servidor solo almacena un hash. La clave permite iniciar sesión en un segundo dispositivo y ver la misma biblioteca.',
      'modal.sync.signedOut.deviceLabel':       'Etiqueta del dispositivo (opcional):',
      'modal.sync.signedOut.deviceLabel.placeholder1': 'p. ej. PC del trabajo',
      'modal.sync.signedOut.deviceLabel.placeholder2': 'p. ej. Portátil',
      'modal.sync.signedOut.yourKey':           'Tu clave de sincronización:',
      'modal.sync.signedOut.regenerate':        'Generar nueva',
      'modal.sync.signedOut.copyClipboard':     'Copiar al portapapeles',
      'modal.sync.signedOut.warnSave':          '⚠ <strong>Copia y guarda la clave ahora.</strong> La clave también estará disponible en la pestaña "Clave" tras el registro — pero es mejor guardar una copia en un gestor de contraseñas.',
      'modal.sync.signedOut.login.body':        'Pega la clave que viste al registrar el primer dispositivo. Formato: <code>SYNC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX</code>.',
      'modal.sync.signedOut.keyLabel':          'Clave de sincronización:',
      'modal.sync.signedOut.recover.body':      'Si se estableció un correo electrónico <strong>verificado</strong> en la cuenta — introdúcelo y envía un enlace de recuperación. Tras hacer clic en el enlace del correo recibirás una <strong>clave nueva</strong> (de un solo uso). Se cerrarán todas las sesiones activas.',
      'modal.sync.signedOut.email':             'Correo electrónico de la cuenta:',
      'modal.sync.signedOut.email.placeholder': 'alguien@ejemplo.com',
      'modal.sync.signedOut.cancel':            'Cancelar',
      'modal.sync.signedOut.regSubmit':         'Registrar dispositivo',
      'modal.sync.signedOut.loginSubmit':       'Iniciar sesión',
      'modal.sync.signedOut.recoverSubmit':     'Enviar enlace de recuperación',
      'modal.sync.signedIn.tab.status':         'Estado',
      'modal.sync.signedIn.tab.key':            'Clave de acceso',
      'modal.sync.signedIn.tab.backups':        'Copias de seguridad',
      'modal.sync.signedIn.tab.email':          'Correo y recuperación',
      'modal.sync.signedIn.status.account':     'Cuenta',
      'modal.sync.signedIn.status.pending':     'Cambios pendientes:',
      'modal.sync.signedIn.status.cloudLib':    'Biblioteca en la nube',
      'modal.sync.signedIn.status.lastCursor':  'Última descarga (cursor):',
      'modal.sync.signedIn.status.syncNow':     'Sincronizar ahora',
      'modal.sync.signedIn.status.snapshot':    'Instantánea completa',
      'modal.sync.signedIn.status.refresh':     'Actualizar los datos de la cuenta',
      'modal.sync.signedIn.status.reconcile':   'Resincronización completa con la cuenta',
      'modal.sync.signedIn.status.sessions':    'Sesiones activas (dispositivos)',
      'modal.sync.signedIn.status.history':     'Historial de sincronización',
      'modal.sync.history.empty':               'No hay sincronizaciones registradas.',
      'modal.sync.history.viewAll':             'Ver historial completo',
      'modal.sync.history.changes':             'Cambios:',
      'modal.sync.history.device':              'Dispositivo:',
      'modal.sync.history.fromDevices':         'Obtenido de:',
      'modal.sync.history.stateAfter':          'Estado tras la sincronización:',
      'modal.sync.history.noDetail':            'Sin detalles de cambios (entrada antigua).',
      'modal.sync.signedIn.key.body':           'Clave de sincronización de esta cuenta. Introdúcela en un segundo dispositivo para sincronizar la biblioteca.',
      'modal.sync.signedIn.key.yourKey':        'Tu clave:',
      'modal.sync.signedIn.key.reveal':         'Mostrar clave',
      'modal.sync.signedIn.key.copy':           'Copiar al portapapeles',
      'modal.sync.signedIn.key.forget':         'Quitar la clave de este dispositivo',
      'modal.sync.signedIn.key.warn':           '⚠ La clave otorga acceso completo a la biblioteca. No la muestres en público. Si no quieres conservarla localmente — haz clic en <strong>Quitar la clave de este dispositivo</strong> (guarda una copia en un gestor de contraseñas). El inicio de sesión seguirá funcionando, pero la clave no podrá volver a verse.',
      'modal.sync.signedIn.key.noKey':          'La clave no está almacenada en este dispositivo (se inició sesión antes de que existiera esta función, o se eliminó manualmente). Solo puedes recuperarla mediante el correo de recuperación o si tienes una copia guardada en otro lugar.',
      'modal.sync.signedIn.backups.hint':       'Copias de seguridad en la nube — restaurar reemplaza toda la biblioteca.',
      'modal.sync.signedIn.backups.label':      'Etiqueta de la nueva copia de seguridad (opcional):',
      'modal.sync.signedIn.backups.placeholder':'p. ej. Antes de un gran cambio',
      'modal.sync.signedIn.backups.create':     'Crear copia de seguridad ahora',
      'modal.sync.signedIn.backups.refresh':    'Actualizar lista',
      'modal.sync.signedIn.email.body':         'El correo electrónico permite la <strong>recuperación</strong> — cuando pierdes la clave, puedes obtener una nueva haciendo clic en un enlace enviado a esa dirección. Añadir un correo requiere verificación (enlace en el mensaje).',
      'modal.sync.signedIn.email.add':          'Añadir correo (recuperación):',
      'modal.sync.signedIn.email.placeholder':  'alguien@ejemplo.com',
      'modal.sync.signedIn.email.submit':       'Enviar enlace de verificación',
      'modal.sync.signedIn.email.remove':       'Quitar correo',
      'modal.sync.signedIn.email.recoveryFlow': 'Flujo de recuperación: en otro dispositivo (o sin clave) selecciona la pestaña <strong>"Perdí mi clave"</strong> en el panel de inicio de sesión, introduce este correo — el servidor enviará un enlace que genera una clave nueva e invalida todas las sesiones activas.',
      'modal.sync.signedIn.logout':             'Cerrar sesión en este dispositivo',
      'modal.sync.signedIn.close':              'Cerrar',

      // === Content script (floating button + actions panel) ===
      'content.float.titleGloballyOff':         'Extensión desactivada globalmente — haz clic en el icono de la extensión para activarla',
      'content.float.titleNormal':              'Clic izquierdo: activar/desactivar la carpeta • Clic derecho: mostrar la lista de conjuntos de reglas • Arrastra para mover',
      'content.panel.empty':                    'No hay conjuntos de reglas activos',
      'content.panel.actionBarsTitle':          'Barras de acciones',
      'content.panel.connectionsTitle':         'Conexiones',
      'content.actionsPanel.dragHandle':        'Arrastra para mover',
      'content.editMode.banner':                'Modo de edición de la barra — clic/Ctrl+clic selecciona, arrastrar mueve · Ctrl+Z deshace · Ctrl+Alt+S para salir',
      'content.editMode.undoEmpty':             'Nada que deshacer',
      'content.editMode.addLabel':              '+ Etiqueta',
      'content.editMode.resetBar':              'Restablecer barra',
      'content.editMode.resetConfirm':          '¿Restablecer la barra a la disposición predeterminada? Se eliminarán las posiciones, tamaños y etiquetas personalizados.',
      'content.labelEditor.text':               'Texto:',
      'content.labelEditor.font':               'Fuente:',
      'content.labelEditor.size':               'Tamaño:',
      'content.labelEditor.color':              'Color:',
      'content.labelEditor.delete':             'Eliminar',
      'content.labelEditor.close':              'Cerrar',
      'content.labelContextMenu.delete':        'Eliminar etiqueta',
      'content.bar.hideUntilReload':            'Cerrar esta barra (hasta recargar)',
      'content.bar.disconnectFrom':             'Desconectar de: :name',
      'content.bar.disconnectAll':              'Desconectar todas las conexiones',
      'content.bar.fallbackName':               'Barra',
      'content.float.label':                    '⊕ Botón principal',
      'content.toast.noFoldersForPage':         'Ninguna carpeta coincide con esta página',
      'content.toast.floatVisible':             'Botón flotante activado',
      'content.toast.floatHidden':              'Botón flotante oculto',

      // === Background (context menu + JS error logger) ===
      'background.contextMenu.toggleFloat':     'Activar/desactivar el botón flotante para esta página',
      'background.contextMenu.editMode':        'Modo de edición de la barra de acciones (activar/desactivar)',
      'background.contextMenu.actionBars':      'Barras de acciones',
      'background.contextMenu.actionBarsEmpty': '(no hay barras para esta página)',
      'background.contextMenu.actionBarFallback': '(sin nombre)',
      'background.contextMenu.cssPanes':         'Paneles CSS',
      'background.contextMenu.cssPanesEmpty':    '(no hay paneles CSS para esta página)',
      'background.contextMenu.cssPaneFallback':  '(sin nombre)',
      'background.contextMenu.jsPanes':          'Paneles JS',
      'background.contextMenu.jsPanesEmpty':     '(no hay paneles JS para esta página)',
      'background.contextMenu.jsPaneFallback':   '(sin nombre)',
      'background.contextMenu.jsConsoles':       'Consolas JS',
      'background.contextMenu.jsConsolesEmpty':  '(no hay consolas para esta página)',
      'background.contextMenu.jsConsoleFallback':'(sin nombre)',
      'background.contextMenu.outputConsoles':       'Consolas de salida',
      'background.contextMenu.outputConsolesEmpty':  '(no hay consolas de salida para esta página)',
      'background.contextMenu.outputConsoleFallback':'(sin nombre)',
      // TEMP panes (since v2.13.81).
      'background.contextMenu.tempPanes':            'Ventana temporal (TEMP)',
      'background.contextMenu.tempCss':              'Panel CSS TEMP (Ctrl+Alt+G)',
      'background.contextMenu.tempJs':               'Panel JS TEMP (Ctrl+Alt+H)',
      'background.contextMenu.tempJsConsole':        'Consola JS TEMP (Ctrl+Alt+J)',
      'background.contextMenu.tempOutput':           'Consola de salida TEMP (Ctrl+Alt+K)',
      'pane.collapse.title':                      'Contraer la ventana a la barra de título',
      'pane.expand.title':                        'Expandir la ventana',
      'aiHelper.form.submit':                     'OK',
      'aiHelper.form.cancel':                     'Cancelar',
      'aiHelper.form.yes':                        'Sí',
      'aiHelper.form.no':                         'No',
      'aiHelper.form.askTitle':                   'Pregunta',
      'aiHelper.form.confirmTitle':               'Confirmación',
      'aiHelper.form.updateConfirmTitle':               '¿Actualizar?',
      'aiHelper.style.title':                           'Apariencia de AI Helper',
      'aiHelper.style.role.user':                       'Tus mensajes',
      'aiHelper.style.role.assistant':                  'Respuestas de IA',
      'aiHelper.style.role.tool':                       'Resultados de herramientas',
      'aiHelper.style.role.form':                       'Formularios',
      'aiHelper.style.font':                            'Fuente',
      'aiHelper.style.size':                            'Tamaño',
      'aiHelper.style.color':                           'Texto',
      'aiHelper.style.bg':                              'Fondo',
      'aiHelper.style.reset':                           'Restaurar predeterminados',
      'aiHelper.style.resetAll':                        'Restaurar todos los predeterminados',
      'aiHelper.confirm.update.folder':                 'Actualización del catálogo',
      'aiHelper.confirm.update.group':                  'Actualización del grupo',
      'aiHelper.confirm.update.rule':                   'Actualización del conjunto',
      'aiHelper.confirm.update.bar':                    'Actualización de la barra de acciones',
      'aiHelper.confirm.update.action':                 'Actualización de la acción',
      'aiHelper.confirm.update.cssPane':                'Actualización de la ventana CSS',
      'aiHelper.confirm.update.jsPane':                 'Actualización de la ventana JS',
      'aiHelper.confirm.update.jsConsole':             'Actualización de la consola JS',
      'aiHelper.confirm.update.outputConsole':         'Actualización de la consola de salida',
      'aiHelper.form.createConfirmTitle':         '¿Crear?',
      'aiHelper.form.confirmed':                  'confirmado',
      'aiHelper.form.cancelled':                  'cancelado',
      'tempPane.close.title':                        'Cerrar la ventana temporal (contenido conservado durante la sesión)',
      'tempPane.clear.title':                        'Borrar el contenido de la ventana',
      'background.jsError.prefix':              '[JustZix] Error de JS en el conjunto de reglas/acción ":id":\n',
      // === AI Helper (od v2.13.127) ===
      'settings.aiHelper.label': 'AI Helper',
      'settings.aiHelper.title': 'Configurar las claves API para AI Helper',
      'modal.aiHelper.title': 'AI Helper',
      'modal.aiHelper.intro': 'Introduce las claves API de los proveedores que quieras usar. Basta con una.',
      'modal.aiHelper.keyOpenai': 'Clave API — OpenAI',
      'modal.aiHelper.keyAnthropic': 'Clave API — Anthropic',
      'modal.aiHelper.keyGemini': 'Clave API — Gemini',
      'modal.aiHelper.defaultProvider': 'Proveedor predeterminado',
      'modal.aiHelper.defaultModel': 'Modelo predeterminado',
      'modal.aiHelper.modelAuto': '— auto (el más alto) —',
      'modal.aiHelper.loadModels': 'Cargar modelos',
      'modal.aiHelper.securityNote': '<strong>Seguridad:</strong> de forma predeterminada las claves API se guardan únicamente en el almacenamiento local de este navegador (<code>chrome.storage.local</code>). Marcar “sincronizar" junto a una clave envía esa clave a través de la nube de JustZix (la misma que la extensión usa para sincronizar el resto de sus datos) a tus otros dispositivos. Las claves sin marcar nunca salen de este dispositivo.',
      'modal.aiHelper.syncKey':                'sincronizar',
      'modal.aiHelper.syncKeyTitle': 'Sincronizar esta clave entre tus dispositivos mediante la nube de JustZix',
      'modal.aiHelper.cancel': 'Cancelar',
      'modal.aiHelper.save': 'Guardar',
      'modal.aiHelper.needKey': 'Introduce primero una clave API.',
      'modal.aiHelper.loadingModels': 'Cargando modelos…',
      'modal.aiHelper.errModels': 'No se pudieron cargar los modelos',
      'modal.aiHelper.modelsLoaded': 'modelos disponibles',
      'aiHelper.win.title': 'AI Helper',
      'aiHelper.win.close': 'Cerrar',
      'aiHelper.win.provider': 'Proveedor',
      'aiHelper.win.model': 'Modelo',
      'aiHelper.win.clear': 'Borrar la conversación',
      'aiHelper.win.clearConfirm': '¿Borrar toda la conversación y empezar de nuevo?',
      'aiHelper.win.inputPlaceholder': 'Pide ayuda con CSS, JS o una acción…',
      'aiHelper.win.send': 'Enviar (Ctrl+Intro)',
      'aiHelper.win.welcome': '¡Hola! Te ayudaré a preparar CSS, JavaScript o acciones para esta página. Describe qué quieres conseguir.',
      'aiHelper.win.you': 'Tú',
      'aiHelper.win.assistant': 'AI Helper',
      'aiHelper.win.thinking': 'Pensando…',
      'aiHelper.win.error': 'Error de conexión con la API',
      'aiHelper.win.noKeys': 'No hay claves API. Añade una clave en Ajustes → AI Helper.',
      'aiHelper.win.noModel': 'Selecciona un modelo.',
      'aiHelper.win.loadingModels': 'Cargando…',
      'aiHelper.win.modelsError': 'Error al cargar los modelos',
      'background.contextMenu.aiHelper': 'AI Helper',
      'popup.aiHelper.label': 'AI Helper',
      'popup.aiHelper.on': 'Abierto — haz clic para cerrar (Ctrl+Alt+\\)',
      'popup.aiHelper.off': 'Cerrado — haz clic para abrir (Ctrl+Alt+\\)',
      'popup.aiHelper.unavailable': 'No disponible en esta página',
      // === AI Helper etap 2 (od v2.13.128) ===
      'aiHelper.win.maxRounds': 'Se alcanzó el límite de pasos de herramientas en este turno. Dime cómo continuar.',
      'aiHelper.win.copy':                        'Copiar conversación',
      'aiHelper.win.copy.all':                    'Copiar conversación completa',
      'aiHelper.win.copy.lastInput':              'Copiar último prompt API (sistema + historial + entrada)',
      'aiHelper.win.copyNoInput':                 'Nada para copiar',
      'aiHelper.copy.lastInput.prefix':           '[JustZix → reenviar a la API] Copiado el ENTERO último prompt enviado al modelo (sistema + historial + último mensaje). Envía el bloque a continuación a la API como PRIMER user-message de una nueva conversación. ELIMINA este encabezado antes de enviar.',
      'aiHelper.win.copied':                      'Copiado',
      'aiHelper.win.copyFailed':                  'No se pudo copiar — comprueba los permisos del portapapeles.',
      'aiHelper.win.font':                        'Fuente',
      'aiHelper.win.fontSize':                    'Tamaño',
      'aiHelper.win.defaultFont':                 'Predeterminado',
      'aiHelper.win.stop':                        'Detener al agente',
      'aiHelper.win.stopped':                     'Interrumpido por el usuario.',
      'aiHelper.win.ctxUsage':                    'Uso del contexto del modelo',
      'aiHelper.win.ctxUsageDetail':              'Contexto',
      'aiHelper.win.compact':                     'Compactar conversación (resumen LLM)',
      'aiHelper.win.compactNoop':                 'Historial demasiado corto — nada que compactar.',
      'aiHelper.win.compactNoProvider':           'Selecciona primero un proveedor y un modelo.',
      'aiHelper.win.compactErr':                  'Error de compactación',
      'aiHelper.win.compactEmpty':                'El modelo devolvió un resultado vacío.',
      'aiHelper.win.compactedPrefix':             'Resumidos',
      'aiHelper.win.compactedSuffix':             'mensajes anteriores:',
      'aiHelper.win.telemetryTooltip':            'Tiempos y tokens de esta sesión',
      'aiHelper.win.telemetryTimers':             'Tiempo: desde el último mensaje (u) / desde la última herramienta (t) / actividad total del agente (a)',
      'aiHelper.win.telemetryCurrent':            'Tokens en la acción actual (↑ entrada / ↓ salida)',
      'aiHelper.win.telemetrySession':            'Tokens totales en esta sesión',
      'aiHelper.win.msgTelem':                    'Tokens de esta ronda y tiempo de respuesta del modelo',
      'aiHelper.win.toolDur':                     'Tiempo de ejecución de la herramienta',
      'aiHelper.win.stepIdBadge':                 'Número de paso del historial',
      'aiHelper.form.freeText':                   'O responde con texto',
      'aiHelper.form.freeTextOnly':               'Tu respuesta',
      'aiHelper.form.freeTextPlaceholder':        'Escribe tu respuesta…',
      'aiHelper.win.turnLimit.body':              'Se alcanzó el límite de 10 pasos de herramientas en este turno. El agente se pausó — revisa la última herramienta y su resultado. ¿Continuar desde aquí con el límite reiniciado o detener?',
      'aiHelper.win.turnLimit.lastTool':          'Última herramienta',
      'aiHelper.win.turnLimit.lastResult':        'Resultado',
      'aiHelper.win.turnLimit.continue':          'Continuar (reiniciar límite)',
      'aiHelper.win.turnLimit.stop':              'Detener',
      'aiHelper.win.turnLimit.stoppedByUser':     'Detenido.',
      'aiHelper.copy.title':                      'Conversación de JustZix AI Helper',
      'aiHelper.copy.toolResult':                 'Resultado de la herramienta',
      'aiHelper.tool.label': 'herramienta',
      'aiHelper.tool.rejected': 'El usuario rechazó la operación.',
      'aiHelper.confirm.folder': 'AI Helper quiere crear una CARPETA:',
      'aiHelper.confirm.group': 'AI Helper quiere crear un GRUPO:',
      'aiHelper.confirm.rule': 'AI Helper quiere crear un CONJUNTO DE REGLAS:',
      'aiHelper.confirm.create.cssPane': 'AI Helper quiere crear una VENTANA CSS:',
      'aiHelper.confirm.create.jsPane': 'AI Helper quiere crear una VENTANA JS:',
      'aiHelper.confirm.create.jsConsole': 'AI Helper quiere crear una CONSOLA JS:',
      'aiHelper.confirm.create.outputConsole': 'AI Helper quiere crear una CONSOLA DE SALIDA:',
      'aiHelper.confirm.setPaneCode': 'AI Helper quiere insertar código en la ventana ":name" (:id).\nLongitud: actual :old car., nuevo :new car.\nNOTA: el código de los paneles persistentes vive por pestaña (sessionStorage), no entre sesiones.',
      'aiHelper.confirm.bar': 'AI Helper quiere crear una BARRA DE ACCIONES:',
      'aiHelper.confirm.action': 'AI Helper quiere crear una ACCIÓN:',
      // === Wyszukiwanie w kodzie (od v2.13.130) ===
      'codeSearch.placeholder': 'Buscar en el código… (Ctrl+Alt+F)',
      'codeSearch.btn': 'Buscar',
      'codeSearch.prev': 'Resultado anterior (Ctrl+Alt+←)',
      'codeSearch.next': 'Resultado siguiente (Ctrl+Alt+→)',
      'codeSearch.noMatch': 'Sin resultados',
    },
    ru: {
      'dashboard.title': 'Панель управления',
      'dashboard.subtitle': 'Обзор вашей библиотеки JustZix и быстрые ярлыки.',
      'dashboard.stats.title': 'Библиотека',
      'dashboard.stats.folders': 'Папки',
      'dashboard.stats.groups': 'Группы',
      'dashboard.stats.rules': 'Наборы правил',
      'dashboard.stats.actions': 'Действия',
      'dashboard.stats.bars': 'Панели действий',
      'dashboard.stats.windows': 'Окна на странице',
      'dashboard.stats.withCss': 'Наборы с CSS',
      'dashboard.stats.withJs': 'Наборы с JS',
      'dashboard.stats.clicks': 'Клики по действиям',
      'dashboard.version.title': 'Версия расширения',
      'dashboard.version.checking': 'Проверка…',
      'dashboard.version.current': 'Установлено: :version',
      'dashboard.version.upToDate': 'У вас актуальная версия',
      'dashboard.version.outdated': 'Доступна новая версия: :version',
      'dashboard.storage.title': 'Хранилище',
      'dashboard.backup.title': 'Последняя резервная копия',
      'dashboard.backup.signIn': 'Войдите в облачную синхронизацию, чтобы создавать облачные резервные копии.',
      'dashboard.backup.none': 'Облачных резервных копий пока нет.',
      'dashboard.backup.noLabel': '(без названия)',
      'dashboard.backup.last': 'Последняя резервная копия: :date',
      'dashboard.recent.title': 'Недавно изменённые наборы правил',
      'dashboard.recent.empty': 'Ни один набор правил ещё не редактировался.',
      'dashboard.examples.title': 'Последние примеры',
      'dashboard.posts.title': 'Из блога',
      'dashboard.feed.loading': 'Загрузка…',
      'dashboard.feed.empty': 'Пока нечего показать.',
      'dashboard.home.title': 'Панель',
      'dashboard.usage.title': 'Статистика использования',
      'dashboard.usage.cssApplied': 'Применений CSS',
      'dashboard.usage.jsExecuted': 'Запусков JS',
      'dashboard.usage.windowsShown': 'Показов окон',
      'dashboard.usage.empty': 'Пока нет данных — используйте расширение на сайтах.',
      'dashboard.tip.title': 'Совет',
      'dashboard.tip.prev': 'Предыдущий совет',
      'dashboard.tip.next': 'Следующий совет',
      'dashboard.tip.manual': 'Открыть полное руководство',
      'tip.dash.1': 'Щёлкните правой кнопкой по плавающей кнопке, чтобы открыть панель со списком папок и наборов правил.',
      'tip.dash.2': 'Ctrl+Alt+S переключает режим редактирования панели действий — элементы можно свободно располагать и менять их размер.',
      'tip.dash.3': 'У каждого набора правил могут быть собственные шаблоны URL, ограничивающие его конкретными подстраницами.',
      'tip.dash.4': 'Ctrl+Alt+\\ открывает AI Helper — ассистента, который генерирует CSS/JS для конкретной задачи.',
      'tip.dash.5': 'В редакторах кода есть строка поиска — Ctrl+Alt+F переводит на неё фокус.',
      'tip.dash.6': 'Щелчок левой кнопкой по плавающей кнопке включает или отключает сразу все подходящие папки.',
      'tip.dash.7': 'Набор правил хранит последние 5 версий CSS и JS в истории — изменение всегда можно отменить.',
      'tip.dash.8': 'Перетащите набор правил на имя другой группы или папки, чтобы переместить его туда.',
      'tip.dash.9': 'Окна TEMP (Ctrl+Alt+G/H/J/K) — одноразовые панели CSS/JS и консоли для быстрых проверок.',
      'tip.dash.10': 'Действия SELECT, INPUT, SLIDER и TOGGLE3 запоминают своё значение — оно сохраняется после перезагрузки страницы.',
      'tip.dash.11': 'Облачная синхронизация поддерживает согласованность вашей библиотеки на всех устройствах.',
      'tip.dash.12': 'Вместо отправки файла JSON поделитесь папкой по ссылке (Мои ссылки) с указанием срока действия.',
      'tip.dash.13': 'Поиск в боковой панели быстро находит папку, группу или набор правил по имени.',
      'tip.dash.14': 'Добавляйте теги к наборам правил и фильтруйте библиотеку по тегам, когда проектов много.',
      'tip.dash.15': 'Трёхбуквенная метка и цвет выделяют плавающую кнопку папки на странице.',
      'tip.dash.16': 'Кнопка «URL» вставляет адрес активной вкладки прямо в шаблон URL — без копирования.',
      'tip.dash.17': 'JustZix на лету проверяет правильность вводимых шаблонов URL.',
      'tip.dash.18': 'Редактор предупреждает об ошибках синтаксиса CSS ещё до сохранения набора правил.',
      'tip.dash.19': 'При импорте можно просмотреть код JavaScript, прежде чем решить его добавить.',
      'tip.dash.20': 'Отключение папки каскадно отключает все её группы и наборы правил.',
      'tip.dash.21': 'Фильтр «Только активные» скрывает отключённые элементы, когда библиотека разрастается.',
      'tip.dash.22': 'Перетащите панель действий к другой, чтобы соединить их в один блок.',
      'tip.dash.23': 'Окно JS-консоли работает как REPL — проверяйте фрагменты кода прямо на странице.',
      'tip.dash.24': 'Окно Output Console перехватывает записи console.log страницы и показывает их на экране.',
      'tip.dash.25': 'Можно экспортировать отдельную папку, группу или набор правил, а не только всю библиотеку.',
      'tip.dash.26': 'JustZix добавляет свои пункты в контекстное меню браузера (правый клик).',
      'tip.dash.27': 'Перетащите мышью границу между панелями редактора кода, подгоняя вид под себя.',
      'tip.dash.28': 'Расширение само уведомит вас, когда выйдет более новая версия.',
      'tip.dash.29': 'Клик по значку расширения открывает виджет для быстрого включения папок и панелей.',
      'tip.dash.30': 'JavaScript набора правил может вызывать действия панелей через встроенный API JustZix.',
      'tip.dash.31': 'Ctrl+Enter выполняет код или отправляет сообщение — в окне JS, консоли JS и AI Helper.',
      'tip.dash.32': 'В консоли JS стрелки ↑ / ↓ листают историю введённых команд.',
      'tip.dash.33': 'Ctrl+L очищает вывод консоли JS, не закрывая окно.',
      'tip.dash.34': 'Ctrl+Shift+L очищает активную вкладку каждой открытой консоли Output.',
      'tip.dash.35': 'Ctrl+Shift+H открывает окно справки по API в консоли Output.',
      'tip.dash.36': 'В редакторе кода Ctrl+Alt+→ и Ctrl+Alt+← переходят к следующему и предыдущему результату поиска.',
      'tip.dash.37': 'Клавиша Esc очищает поле поиска в консоли Output.',
      'tip.dash.38': 'Двойной клик по заголовку окна TEMP переключает его между размером по умолчанию и последним.',
      'tip.dash.39': 'Окно TEMP запоминает последний размер для каждого типа и сохраняет его после перезапуска браузера.',
      'tip.dash.40': 'Прокрутка колёсиком внутри окна больше не прокручивает страницу под ним.',
      'tip.dash.41': 'У каждого окна 8 маркеров изменения размера — 4 в углах и 4 на краях.',
      'tip.dash.42': 'Правый клик на заголовке окна открывает меню с закрытием, отсоединением, шрифтом и размером.',
      'tip.dash.43': 'Перетащите окно ближе к другому — они магнитно склеятся в одну группу.',
      'tip.dash.44': 'У консоли Output 6 вкладок: Всё, JZ, Console, Network, Errors и DataLayer.',
      'tip.dash.45': 'На вкладке Network консоли Output Alt+клик по строке добавляет её домен в фильтр включения.',
      'tip.dash.46': 'Alt+Shift+клик по строке Network добавляет домен в фильтр исключения.',
      'tip.dash.47': 'У каждой вкладки консоли Output свой независимый набор фильтров.',
      'tip.dash.48': 'На вкладке DataLayer перетащите полосу между столбцами, чтобы изменить их ширину.',
      'tip.dash.49': 'В режиме редактирования панели кнопка „+ Метка" добавляет статические текстовые подписи.',
      'tip.dash.50': 'Двойной клик по метке панели открывает редактор её текста, шрифта, размера и цвета.',
      'tip.dash.51': 'В режиме редактирования Shift+перетаскивание по пустой области выделяет элементы прямоугольником.',
      'tip.dash.52': 'В режиме редактирования Ctrl+клик добавляет или убирает элемент из выделения.',
      'tip.dash.53': 'Клик по числовому бейджу элемента действия копирует его стабильный ID в буфер обмена.',
      'tip.dash.54': 'Набор правил можно настроить на повторное выполнение JS при изменениях UI на SPA-страницах.',
      'tip.dash.55': 'Помимо BUTTON, SELECT и INPUT, у действий есть типы SLIDER, TEXTAREA и TOGGLE3.',
      'tip.dash.56': 'Светлая/тёмная тема и язык интерфейса синхронизируются как общая настройка аккаунта.',
      'tip.dash.57': 'Статистика использования на Панели суммирует данные со всех сопряжённых устройств.',
      'tip.dash.58': 'Полосу под редактором кода можно потянуть вниз, чтобы увеличить рабочую область.',
      'tip.dash.59': 'Вертикальная полоса между сайдбаром и редактором сворачивает левую панель для большего пространства.',
      'tip.dash.60': 'Иконка дома в шапке Панели и в навигационных крошках возвращает прямо на Панель.',
      // === Popup ===
      'popup.toggle.on.label':       'Расширение включено',
      'popup.toggle.on.status':      'Внедрение активно',
      'popup.toggle.off.label':      'Расширение отключено',
      'popup.toggle.off.status':     'CSS/JS не внедряется',
      'popup.editMode.label':        'Режим редактирования панели действий',
      'popup.editMode.on':           'Вкл — нажмите, чтобы выключить (Ctrl+Alt+S)',
      'popup.editMode.off':          'Выкл — нажмите, чтобы включить (Ctrl+Alt+S)',
      'popup.editMode.unavailable':  'Недоступно на этой странице',
      'popup.empty.title':           'Нет папок с правилами для этой страницы.',
      'popup.empty.hint':            'Добавьте их в режиме редактирования.',
      'popup.empty.folderRules':     'Нет активных правил в этой папке',
      'popup.fallback.groupName':    'Группа',
      'popup.fallback.unnamed':      '(без названия)',
      'popup.restore':               'Восстановить плавающую кнопку (:count)',
      'popup.editBtn':               'Панель редактирования',
      'popup.actionBarsTitle':       'Панели действий',
      'popup.fallback.barName':      'Панель',
      'popup.hideForPage':           'Скрыть плавающую кнопку для этой страницы',
      'popup.showForPage':           'Показать плавающую кнопку для этой страницы',

      // === Options — page ===
      'options.title':                          'JustZix — Редактор',

      // === Options — sidebar ===
      'sidebar.folders.title':                  'Папки',
      'sidebar.addFolder.label':                'Добавить папку',
      'sidebar.search.placeholder':             'Поиск (имя, тег, содержимое кода)',
      'sidebar.clearSearch.label':              'Очистить',
      'sidebar.activeOnly.label':                'Только активные',
      'sidebar.activeOnly.title':               'Скрыть независимые наборы правил (отмечены ○) — показывать только правила, активные в своей папке (●)',
      'sidebar.expandAll.label':                'Развернуть всё дерево',
      'sidebar.collapseAll.label':              'Свернуть всё дерево',
      'sidebar.toggle.title':                   'Свернуть/развернуть панель папок',

      // === Options — settings menu ===
      'settings.menu.label':                    'Настройки',
      'settings.theme.label':                   'Тема',
      'settings.theme.title':                   'Выбрать тему: светлая / тёмная / авто',
      'settings.help.label':                    'Руководство пользователя',
      'settings.tutorial.label':                'Обучение',
      'settings.export.label':                  'Экспорт настроек',
      'settings.import.label':                  'Импорт настроек',
      'settings.importUrl.label':               'Импорт по ссылке',
      'settings.importUrl.title':               'Импортировать экспорт по ссылке JustZix (JZS-XXXX-XXXX-XXXX-XXXX)',
      'settings.sync.label':                    'Облачная синхронизация',
      'settings.shares.label':                  'Мои ссылки',
      'settings.shares.title':                  'Список выданных временных публичных ссылок на экспорты',
      'settings.projectSite.label':             'Сайт проекта',
      'settings.projectSite.title':             'Открыть justzix.com в новой вкладке',
      'settings.lang.label':                    'Язык',
      'settings.lang.title':                    'Язык интерфейса расширения',
      'settings.lang.auto':                     'Авто',
      'settings.lang.autoTitle':                'Определять по браузеру (по умолчанию)',
      'toast.langChanged':                      'Язык установлен: :lang',
      'lang.name.auto':                         'Авто',
      'lang.name.pl':                           'Польский',
      'lang.name.en':                           'Английский',
      'settings.version.label':                 'Версия',
      'settings.version.checkTitle':            'Нажмите, чтобы проверить последнюю версию (принудительное обновление, минуя 30-минутный кэш)',
      'settings.version.checking':              'Проверка…',
      'settings.update.available':              'Доступна новая версия: v:version ⬇',
      'settings.update.notReady':               '(пакет ещё не выпущен)',
      'settings.syncStatusDot.title':           'Облачная синхронизация — вход не выполнен',

      // === Options — folder editor ===
      'folder.icon.title':                      'Метка плавающей кнопки (макс. 3 буквы, ЗАГЛАВНЫЕ). По умолчанию: ZIX',
      'folder.color.title':                     'Цвет папки (необязательно)',
      'folder.colorClear.title':                'Сбросить цвет',
      'folder.name.placeholder':                'Имя папки (напр. justzix.com)',
      'folder.url.placeholder':                 'Шаблон URL (напр. https://justzix.com/* )',
      'folder.urls.label':                      'Шаблоны URL папки:',
      'folder.urls.title':                      'Шаблоны URL папки — хотя бы один должен совпасть, чтобы любое правило в папке выполнялось',
      'folder.urls.placeholder':                'https://justzix.com/*  (Enter — добавить)',
      'folder.insertUrl.title':                 'Вставить URL активной вкладки',
      'folder.active.label':                    'Активна',
      'folder.active.title':                    'Активна — если снять флажок, папка (и все её группы/правила) принудительно отключаются, а переключатель блокируется',
      'folder.enabled.label':                   'Включена',
      'folder.enabled.title':                   'Включена — текущее состояние папки (каскадно для активных групп и правил)',
      'folder.actionsPanel.label':              'Панель действий',
      'folder.actionsPanel.title':              'Показывать плавающую панель с кнопками действий для этой папки',
      'folder.layout.label':                    'Расположение:',
      'folder.layout.title':                    'Расположение кнопок панели действий',
      'folder.layout.vertical':                 'Вертикально',
      'folder.layout.horizontal':               'Горизонтально',
      'folder.addGroup.label':                  '+ Группа',
      'folder.addGroup.title':                  'Добавить новую группу в эту папку',
      'folder.export.label':                    'Экспорт',
      'folder.export.title':                    'Экспортировать эту папку',
      'folder.import.label':                    'Импорт',
      'folder.import.title':                    'Импортировать папку из файла',
      'folder.delete.label':                    'Удалить папку',

      // === Options — group editor ===
      'group.parentLabel':                      'Группа в папке:',
      'folder.label':                           'Папка:',
      'group.label':                            'Группа:',
      'group.name.placeholder':                 'Имя группы',
      'group.urls.label':                       'Шаблоны URL группы:',
      'group.urls.title':                       'Дополнительные шаблоны URL группы — ограничение поверх шаблона папки, применяется ко всем правилам группы',
      'group.urls.placeholder':                 'https://example.com/admin/*  (Tab — автодополнение из папки, Enter — добавить)',
      'group.active.label':                     'Активна',
      'group.active.title':                     'Активна — если снять флажок, группа (и все её правила) принудительно отключаются, а переключатель блокируется',
      'group.enabled.label':                    'Включена',
      'group.enabled.title':                    'Включена — текущее состояние группы (каскадно для активных правил)',
      'group.addRule.label':                    '+ Добавить набор правил',
      'group.export.title':                     'Экспортировать эту группу',
      'group.import.title':                     'Импортировать наборы правил в эту группу',
      'group.move.label':                       'Переместить',
      'group.move.title':                       'Переместить группу в другую папку',
      'group.duplicate.label':                  'Дублировать',
      'group.duplicate.title':                  'Дублировать группу с наборами правил',
      'group.delete.label':                     'Удалить группу',

      // === Options — rule editor ===
      'rule.label':                             'Набор правил:',
      'rule.back.label':                        'Назад к группе',
      'rule.export.title':                      'Экспортировать этот набор правил в файл',
      'rule.share.label':                       'Поделиться ссылкой',
      'rule.share.title':                       'Поделиться этим набором правил как временной публичной ссылкой',
      'rule.import.title':                      'Импортировать набор правил (заменит текущий)',

      // === Options — empty state ===
      'empty.title':                            'Выберите или добавьте папку',
      'empty.body':                             'Каждая папка группирует наборы правил CSS/JS для одного шаблона URL.',

      // === Default names for newly created entities (options.js) ===
      'default.groupName':                      'По умолчанию',
      'default.folderName':                     'Новая папка',
      'default.ruleName':                       'Новый набор правил',

      // === Plural noun forms used by toasts ===
      // EN has 2 forms (one/other), PL has 4 — both filled, Intl.PluralRules picks correctly.
      'plural.rule.one':                        'набор правил',
      'plural.rule.few':                        'набора правил',
      'plural.rule.many':                       'наборов правил',
      'plural.rule.other':                      'наборов правил',
      'plural.folder.one':                      'папка',
      'plural.folder.few':                      'папки',
      'plural.folder.many':                     'папок',
      'plural.folder.other':                    'папок',
      'plural.group.one':                       'группа',
      'plural.group.few':                       'группы',
      'plural.group.many':                      'групп',
      'plural.group.other':                     'групп',
      'plural.action.one':                      'действие',
      'plural.action.few':                      'действия',
      'plural.action.many':                     'действий',
      'plural.action.other':                    'действий',
      'plural.jsLine.one':                      'строка JS',
      'plural.jsLine.few':                      'строки JS',
      'plural.jsLine.many':                     'строк JS',
      'plural.jsLine.other':                    'строк JS',
      'toast.importedRulesToGroup':             'Импортировано :count :word в группу',
      'toast.exportedRules':                    'Экспортировано :count :word',

      // === Sidebar — dynamic render ===
      'sidebar.empty.folders':                  'Нет папок',
      'sidebar.empty.activeFolders':            'Нет активных папок',
      'sidebar.folder.toggleHint':              'Включить/отключить всю папку',
      'sidebar.folder.inactiveHint':            'Папка неактивна — разблокируйте её в редакторе папки',
      'sidebar.group.toggleHint':               'Включить/отключить группу',
      'sidebar.group.inactiveHint':             'Группа неактивна — разблокируйте её в редакторе группы',
      'sidebar.rule.urlHint':                   'Ограничение по URL на уровне набора правил',
      'sidebar.rule.activeInFolder':            'Активен в папке',
      'sidebar.rule.inactiveInFolder':          'Неактивен в папке',
      'sidebar.fallback.folderName':            '(папка)',
      'sidebar.fallback.groupName':             '(группа)',
      'sidebar.fallback.unnamed':               '(без названия)',
      'sidebar.flag.css':                       'Содержит CSS',
      'sidebar.flag.js':                        'Содержит JavaScript',
      'sidebar.flag.actions':                   'Содержит действия',
      'sidebar.flag.groupUrls':                 'У группы есть ограничение по URL',

      // === Folder/Group/Rule editor — dynamic ===
      'rules.empty.folder':                     'Нет наборов правил в этой папке. Добавьте группу и создайте в ней первый набор правил, либо щёлкните группу на боковой панели.',
      'rules.empty.group':                      'Нет наборов правил в этой группе. Добавьте первый, нажав «+ Добавить набор правил».',
      'rule.namePlaceholder':                   'Имя набора правил',
      'rule.activeTooltip':                     'Активен — учитывается ли это правило при переключении всей папки',
      'rule.urls.tooltip':                      'Дополнительные шаблоны URL — если заданы, правило срабатывает только на подстраницах, совпадающих хотя бы с одним шаблоном (в дополнение к шаблону папки)',
      'rule.urls.placeholder.empty':            'https://example.com/admin/*  (Tab — вставить шаблон папки, Enter — добавить)',

      // === Move menu ===
      'moveMenu.section.moveGroup':             'Переместить группу в',
      'moveMenu.section.moveRule':              'Переместить в группу',
      'moveMenu.section.copyRule':              'Скопировать в группу',
      'moveMenu.empty.folders':                 'Нет других папок',
      'moveMenu.empty.groups':                  'Нет других групп',

      // === Confirm dialogs — dynamic ===
      'confirm.deleteFolderWithStats':          'Удалить папку «:name» вместе с :groups и :rules?',
      'confirm.deleteFolder':                   'Удалить папку «:name»?',
      'confirm.deleteGroupWithRules':           'Удалить группу «:name» вместе с :count :word?',
      'confirm.deleteGroup':                    'Удалить группу «:name»?',
      'confirm.replacePartial':                 'Заменить текущие :before частичной резервной копией (:after)?\n\nВсе данные вне файла будут потеряны. Операция необратима.',
      'confirm.overwriteAll':                   'Перезаписать :stats текущими данными из файла?\n\nЭта операция необратима.',

      // === Default name for newly created group ===
      'default.newGroupName':                   'Новая группа',

      // === Export — summary + states ===
      'export.summary.full':                    ':icon Полная резервная копия: :stats + глобальные настройки',
      'export.summary.partial':                 ':icon Частичная резервная копия: :stats',
      'export.empty.folder':                    'Нет папок для экспорта',
      'export.empty.group':                     'Группа пуста — нет наборов правил для экспорта',
      'export.summary.toExport':                ':icon К экспорту: :count :word',
      'export.summary.noneSelected':            ':icon Наборы правил не выбраны',

      // === Import detection (banners) ===
      'import.detection.invalid':               '<strong style="color:#dc2626;">:icon Неверный формат:</strong> отсутствует список папок (<code>folders</code>).',
      'import.detection.partialContains':       'Содержит: :stats',
      'import.detection.willReplace':           ':stats',

      // === Inline labels ===
      'inline.fileGroupLabel':                  'группа',
      'shares.empty':                           'У вас нет активных ссылок.<br><br>Чтобы создать — откройте экспорт (полный / папка / группа / набор правил)<br>и нажмите <strong>«Поделиться ссылкой»</strong>.',

      // === Share preview / export types ===
      'shareType.folder':                       'Одна папка',
      'shareType.group':                        'Одна группа',
      'shareType.rule':                         'Один набор правил',
      'shareType.full':                         'Полный экспорт',

      // === Owner suffix when importing from share ===
      'shareImport.suffix.group':               'группа',
      'shareImport.suffix.rule':                'набор правил',

      // === Rule import — prompt with options ===
      'prompt.ruleConflict':                    'Вы импортируете «:new». Что сделать с текущим набором правил «:current»?\n\n  1 или «replace»  — Заменить текущий набор правил\n  2 или «copy»     — Добавить как новый набор правил рядом с текущим (в той же группе)\n\nВведите 1 или 2:',
      'prompt.folderConflicts':                 'Обнаружено :count :word с тем же ID, что и существующие:\n:names\n\nВведите одно из:\n  1 или «overwrite»  — Перезаписать существующие папки (удаляет их текущие группы и наборы правил)\n  2 или «copy»       — Импортировать как копии (новые ID, «(импорт)» в имени папки)\n  Cancel             — Отменить импорт\n\nВыбор:',

      // === Group editor — additional ===
      'group.empty.noGroupsInFolder':           'Нет групп в папке',

      // === Folder import — conflict messages ===
      'folderImport.conflict.sameId':           'У импортируемой папки тот же ID, что и у существующей «<strong>:name</strong>» (:count :word). Выберите действие:',
      'folderImport.conflict.sameName':         'Папка с именем «<strong>:name</strong>» уже существует (:count :word). Выберите действие:',

      // === Dynamic button states ===
      'btn.shortcutPress':                      '⌨ нажмите Ctrl+Alt+Shift + клавишу (Esc)',
      'btn.generating':                         'Генерация…',
      'btn.importing':                          'Импорт…',
      'btn.showKey':                            'Показать ключ',
      'btn.hideKey':                            'Скрыть ключ',

      // === Rule editor — placeholders + tooltips ===
      'rule.editor.urlsPlaceholder':            'https://example.com/admin/*  (Tab — вставить шаблон папки, Enter — добавить)',
      'rule.editor.urlsPlaceholderWithPattern': ':pattern  (Tab — вставить, Enter — добавить)',
      'rule.editor.toggleHint':                 'Включить/отключить этот набор правил',
      'rule.editor.toggleLockedHint':           'Набор правил неактивен — разблокируйте, установив флажок «Активен»',
      'sidebar.fallback.ruleName':              '(набор правил)',
      'sidebar.fallback.ruleUnnamed':           '(набор правил без названия)',
      'confirm.deleteRule':                     'Удалить набор правил «:name»?',

      // === Rule card (rendered by createRuleCard / renderActionsList) ===
      'rule.card.tagsTitle':                    'Теги (через запятую)',
      'rule.card.activeLabel':                  'Активен',
      'rule.card.enabledLabel':                 'Включён',
      'rule.card.history.title':                'История изменений',
      'rule.card.history.label':                'История',
      'rule.card.move.label':                   'Переместить',
      'rule.card.move.btnTitle':                'Переместить набор правил в другую группу',
      'rule.card.copy.label':                   'Копировать',
      'rule.card.copy.btnTitle':                'Скопировать набор правил в другую группу',
      'rule.card.duplicate.label':              'Дублировать',
      'rule.card.delete.label':                 'Удалить',
      'rule.card.urlsLabel':                    'Шаблоны URL набора правил:',
      'rule.card.tab.actions':                  'Действия',
      'rule.card.lineCount':                    'Количество строк кода',
      'rule.card.js.runOnce.label':             'Выполнять JS только один раз',
      'rule.card.js.runOnce.title':             'Не запускать JS повторно после SPA-навигации (смена URL без перезагрузки страницы). Рекомендуется для скриптов с глобальными слушателями, интервалами или патчами window.',
      'rule.card.actions.info':                 'Кнопки действий появляются на плавающей панели (переключатель «Панель действий» в папке). Макс. 4 символа в ярлыке, свой цвет, JS-код выполняется по клику.',
      'rule.card.actions.add':                  '+ Добавить кнопку действия',
      'rule.card.actions.addWithCount':         '+ Добавить кнопку действия (:count)',
      'rule.card.action.visibleLabel':          'видима',
      'rule.card.duplicate.suffix':             '(копия)',
      'rule.card.action.codePreview':           '(:count :word) :first',
      'rule.card.action.fallback.unlabeled':    '(без ярлыка)',
      'rule.card.fallbackName':                 'Набор правил',

      // === Folder export modal — additional ===
      'export.empty.foldersToExport':           'Нет папок для экспорта',
      'export.summary.toExportFolders':         ':icon К экспорту: :groupCount :groupWord, :ruleCount :ruleWord',
      'export.summary.invalidFolderFile':       'Неверный формат файла — ожидался экспорт одной папки.',
      'export.summary.noConflicts':             ':icon Конфликтов нет. Папка «<strong>:name</strong>» с :count :word будет импортирована как новая.',
      'export.summary.fileContains':            '<strong style="color:#374151;">:icon Содержимое файла (:count :word):</strong>',
      'export.summary.jsHeader':                ':icon JavaScript набора правил:',
      'export.summary.emptyRule':               'пустой набор правил',
      'toast.exportedFolder':                   'Папка экспортирована (:groupCount :groupWord, :ruleCount :ruleWord)',
      'inline.fileFolderLabel':                 'папка',
      'inline.fileRuleLabel':                   'набор правил',
      'default.newGroupNameInline':             'Новая группа',

      // === Sync conflict modal counts ===
      'syncConflict.countLine':                 ':folders :foldersWord / :groups :groupsWord / :rules :rulesWord',

      // === Backup restore confirm ===
      'confirm.restoreBackup':                  'Восстановить резервную копию #:id?\n\nВСЕ текущие папки/группы/наборы правил будут заменены тем, что в резервной копии. Другие устройства синхронизируются при следующей загрузке.\n\nНеобратимо (кроме создания новой резервной копии ПЕРЕД восстановлением).',

      // === Loaders / placeholders ===
      'loading.shares':                         'Загрузка…',
      'loading.generic':                        'Загрузка…',

      // === Sidebar — drag & arrows tooltips ===
      'sidebar.dragHandle.title':               'Перетащите для изменения порядка',
      'sidebar.expandCollapse.title':           'Развернуть/свернуть',
      'sidebar.folderActive.title':             'Активна',
      'sidebar.folderInactive.title':           'Неактивна — принудительно отключена',
      'sidebar.groupActive.title':              'Активна',
      'sidebar.groupInactive.title':            'Неактивна — принудительно отключена',
      'sidebar.searchNoResults':                'Нет результатов для «:query»',

      // === Theme ===
      'settings.theme.toggleToDark':            'Переключить на тёмную тему',
      'settings.theme.toggleToLight':           'Переключить на светлую тему',
      'settings.theme.labelDark':               'Тёмная тема',
      'settings.theme.labelLight':              'Светлая тема',
      'settings.theme.labelAuto':               'Авто (система)',
      'settings.theme.toggleToAuto':            'Переключить на авто-режим',

      // === URL chip remove ===
      'btn.removeUrlChip':                      'Удалить',

      // === CSS validator ===
      'cssValidator.unclosedComment':           'Незакрытый комментарий /* ... */',
      'cssValidator.missingCloseBraces':        'Незакрытые скобки: не хватает :count :word }',
      'plural.brace.one':                       'закрывающей скобки',
      'plural.brace.few':                       'закрывающих скобок',
      'plural.brace.many':                      'закрывающих скобок',
      'plural.brace.other':                     'закрывающих скобок',
      'cssValidator.extraCloseBrace':           'Лишняя закрывающая скобка } (строка :line)',
      'jsValidator.unterminatedString':         'Незавершённый строковый литерал (строка :line)',
      'jsValidator.unclosedBracket':            'Незакрытая скобка :bracket (строка :line)',
      'jsValidator.extraBracket':               'Лишняя закрывающая скобка :bracket (строка :line)',

      // === Action buttons ===
      'action.shortcut.empty':                  'нажмите + введите комбинацию',
      'action.code.empty':                      'нажмите, чтобы добавить код…',
      'action.stats.title':                     'Счётчик кликов в панели действий',
      'action.visible.title':                   'Видна в панели действий (без флажка = скрыта, но не удалена)',
      'action.shortcut.title':                  'Сочетание клавиш — нажмите и введите комбинацию. Esc — отмена. Сочетание работает на подходящих страницах независимо от видимости панели.',
      'action.shortcut.titleAfterClick':        'Сочетание клавиш. Нажмите и введите комбинацию, чтобы изменить. Esc — отмена. × — удалить.',
      'action.shortcut.removeBtn':              'Удалить сочетание клавиш',
      'action.delete.btn':                      'Удалить кнопку действия',
      'action.menu.btn.duplicate':              'Дублировать действие здесь',
      'action.menu.btn.move':                   'Переместить действие в другой набор правил',
      'action.menu.btn.copy':                   'Скопировать действие в другой набор правил',
      'action.menu.section.moveTo':             'Переместить в набор правил...',
      'action.menu.section.copyTo':             'Скопировать в набор правил...',
      'action.menu.empty.rules':                'Нет других наборов правил',
      'action.menu.toast.moved':                'Действие перемещено в «:target»',
      'action.menu.toast.copied':               'Действие скопировано в «:target»',
      'action.shortcut.conflictTitle':          'Конфликт: это сочетание также используют :count :word:\n:list\n\nПри нажатии сработает только первое в порядке отрисовки.',
      'action.shortcut.conflictItem':           '• «:action» в «:rule» (:folder)',
      'plural.actionAlt.one':                   'другое действие',
      'plural.actionAlt.few':                   'других действия',
      'plural.actionAlt.many':                  'других действий',
      'plural.actionAlt.other':                 'других действий',
      'plural.more.one':                        'другое',
      'plural.more.few':                        'других',
      'plural.more.many':                       'ещё',
      'plural.more.other':                      'ещё',
      'action.shortcut.conflictMore':           ' (и ещё :count :word)',
      'action.shortcut.conflictToast':          'Конфликт: это сочетание уже используется действием «:actionLabel» в «:ruleName»:more. Сработает только одно из них.',
      'confirm.deleteAction':                   'Удалить кнопку действия «:label»?',

      // === History ===
      'history.header':                         'Последние :count сохранённых версий. Нажмите «Восстановить», чтобы вернуться к этой версии (текущая будет сохранена в историю).',
      'history.modalTitle':                     'История изменений: «:name»',
      'history.version':                        'Версия :n',
      'history.restore':                        'Восстановить',
      'history.close':                          'Закрыть',

      // === Export — additional ===
      'export.summary.noElements':              ':icon Не выбраны элементы для экспорта',
      'export.summary.toExportFolderShort':     ':icon К экспорту: :groupCount :groupWord, :ruleCount :ruleWord',
      'export.summary.noneSelectedShort':       ':icon Наборы правил не выбраны',

      // === Import detection ===
      'import.detection.parseError':            '<strong style="color:#dc2626;">:icon Ошибка разбора JSON:</strong> :msg',
      'import.detection.partialBackup':         '<strong style="color:#5b21b6;">:icon Частичная резервная копия (выбранные элементы)</strong><br>',
      'import.detection.fullBackup':            '<strong style="color:#006870;">:icon Полная резервная копия настроек:legacy</strong><br>',
      'import.detection.legacySuffix':          ' (устаревший формат)',
      'import.detection.willOverwrite':         '<br><br><strong style="color:#b91c1c;">:icon Текущие настройки будут перезаписаны:</strong>',
      'import.detection.exportFirst':           '<br><span style="color:#7f1d1d;font-size:11px;">Рекомендуется: экспортировать <strong>перед</strong> импортом.</span>',
      'import.detection.folder':                '<strong style="color:#155e75;">:icon Экспорт папки</strong> «<strong>:name</strong>»<br><span style="color:#155e75;">Содержит: :stats</span>',
      'import.detection.group':                 '<strong style="color:#155e75;">:icon Экспорт группы</strong> «<strong>:name</strong>»<br><span style="color:#155e75;">Содержит: :stats</span>',
      'import.detection.rule':                  '<strong style="color:#155e75;">:icon Экспорт набора правил</strong> «<strong>:name</strong>»',

      // === Import — target selectors ===
      'import.target.folder.title':             'Куда поместить импортированную папку?',
      'import.target.folder.newCopy':           'Добавить как новую папку верхнего уровня',
      'import.target.folder.overwrite':         'Перезаписать существующую папку «:name» (сохраняет ID — синхронизация считает это правкой)',
      'import.target.folder.mergeInto':         'Объединить группы/наборы правил с существующей папкой:',
      'import.target.group.title':              'Куда поместить импортированную группу?',
      'import.target.group.existingFolder':     'Добавить в существующую папку:',
      'import.target.group.newFolder':          'Создать новую папку для этой группы',
      'import.target.rule.title':               'Куда поместить импортированный набор правил?',
      'import.target.rule.existingGroup':       'Добавить в существующую группу:',
      'import.target.rule.newFolder':           'Создать новую папку и группу для этого набора правил',

      // === Share errors (create flow) ===
      'share.error.unknown':                    'Неизвестная ошибка',
      'share.error.shareFailed':                'У вас уже 20 активных ссылок или превышен лимит 50 МБ. Отзовите старые в разделе «Мои ссылки».',
      'share.error.badRequest':                 'Не удалось сгенерировать ссылку — проверьте, что экспорт корректен. :msg',
      'share.error.rateLimited':                'Слишком много ссылок создано за последний час (лимит: 30). Попробуйте через некоторое время.',
      'share.error.invalidToken':               'Сессия истекла — войдите снова в панели синхронизации и повторите попытку.',
      'share.error.serverError':                'Проблема на сервере. Попробуйте через некоторое время.',
      'share.error.fallback':                   'Ошибка: :msg',
      'share.error.tooLarge':                   'Экспорт слишком большой — бэкенд допускает максимум 5 МБ. Попробуйте поделиться меньшей частью.',

      // === Share subject (create modal) ===
      'share.subject.partial':                  'Частичный экспорт библиотеки',
      'share.subject.full':                     'Полный экспорт библиотеки',

      // === Time relative formatting (fmtRelHuman) ===
      'rel.lessThanMinute':                     'менее минуты',
      'rel.min':                                ':n мин',
      'rel.hMin':                               ':h ч :m мин',
      'rel.h':                                  ':h ч',
      'rel.dH':                                 ':d дн :h ч',
      'rel.d':                                  ':d дн',

      // === Shares list ===
      'shares.expired':                         'истекла',
      'shares.fetchFailed':                     'Не удалось получить список: :msg',
      'shares.confirmRevoke':                   'Отозвать ссылку #:id?\n\nСсылка перестанет работать немедленно — получатели получат 404 при следующей попытке загрузки.\n\nПРИМЕЧАНИЕ: если кто-то уже скачал данные, у него останется локальная копия — отзыв не возвращает уже загруженное содержимое.',
      'shares.revoked':                         'Ссылка #:id отозвана',

      // === Share preview details ===
      'sharePreview.senderLabel':               'Отправитель:',
      'sharePreview.senderAnonymous':           'аноним',
      'sharePreview.expiresLabel':              'Истекает:',
      'sharePreview.expiresValue':              'через :rel',
      'sharePreview.typeLabel':                 'Тип:',
      'sharePreview.sizeLabel':                 'Размер:',
      'sharePreview.contentLabel':              'Содержимое:',
      'sharePreview.cssLabel':                  'CSS:',
      'sharePreview.cssValueChars':             ':n симв.',
      'sharePreview.jsLabel':                   'JavaScript:',
      'sharePreview.downloadsLabel':            'Загрузки:',
      'sharePreview.codePreviewTitle':          'Предпросмотр кода из общего экспорта',

      // === Share import errors (preview) ===
      'shareImport.error.unknown':              'Неизвестная ошибка',
      'shareImport.error.badRequest':           'Токен недействителен. Проверьте, что вы скопировали ссылку полностью.',
      'shareImport.error.notFound':             'Эта ссылка истекла или была отозвана отправителем. Запросите новую.',
      'shareImport.error.rateLimited':          'Слишком много попыток. Подождите немного и повторите.',
      'shareImport.error.serverError':          'Проблема на сервере. Попробуйте снова.',
      'shareImport.error.fallback':             'Ошибка: :msg',
      'shareImport.error.invalidTokenFormat':   'Неверный формат токена',

      // === Rule import ===
      'ruleImport.unknownFormat':               'Неизвестный или неподдерживаемый формат экспорта: :type',
      'prompt.ruleConflict.body':               'Вы импортируете «:new». Что сделать с текущим набором правил «:current»?\n\n  1 или «replace»  — Заменить текущий содержимым файла\n  2 или «copy»     — Добавить как новый набор правил рядом с текущим (в той же группе)\n  Cancel           — Отменить импорт\n\nВыбор:',

      // === Folder export ===
      'folderExport.exportedToast':             'Папка экспортирована (:groupCount :groupWord, :ruleCount :ruleWord)',

      // === Folder import — details ===
      'folderImport.parseError':                'Ошибка разбора JSON: :msg',
      'folderImport.invalidFormat':             'Неверный формат файла — ожидался экспорт одной папки.',
      'folderImport.noConflicts':               ':icon Конфликтов нет. Папка «<strong>:name</strong>» с :count :word будет импортирована как новая.',
      'folderImport.conflictSameId':            'У импортируемой папки тот же ID, что и у существующей «<strong>:name</strong>» (:count :word). Выберите действие:',
      'folderImport.conflictSameName':          'Папка с именем «<strong>:name</strong>» уже существует (:count :word). Выберите действие:',
      'folderImport.fileContents':              '<strong style="color:#374151;">:icon Содержимое файла (:count :word):</strong>',
      'folderImport.jsHeader':                  ':icon JavaScript набора правил:',
      'folderImport.codePreviewTitle':          'Предпросмотр кода: :name',
      'folderImport.previewJsBtn.title':        'Предпросмотр JS-кода',

      // === Sync — toasts + status messages ===
      'sync.toast.synced':                      'Синхронизировано: :applied изменений, :deletes удалений',
      'sync.rel.justNow':                       'только что',
      'sync.tooltip.syncing':                   'Синхронизация…',
      'sync.tooltip.error':                     'Синхронизация: ошибка. Последняя успешная: :last',
      'sync.tooltip.connected':                 'Облачная синхронизация — подключено. Последняя: :last',
      'sync.error.copyManually':                'Не удалось скопировать — выделите и Ctrl+C вручную.',
      'sync.error.registerFailed':              'Ошибка регистрации: :msg',
      'sync.error.unknown':                     'неизвестно',
      'sync.error.invalidKeyFormat':            'Неверный формат ключа — ожидается SYNC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX.',
      'sync.status.sendingLocal':               'Отправка :count локальных сущностей в аккаунт.',
      'sync.status.cancelled':                  'Вход отменён — выполнен выход. Локальные данные не тронуты.',
      'sync.status.sendingAndDeleting':         'Отправка :local локальных сущностей, удаление :remote из аккаунта…',
      'sync.status.merged':                     'Объединено. Уникальные локальные (:count элементов) попадут в аккаунт при следующей синхронизации.',
      'sync.error.firstSyncFailed':             'Вход выполнен, но первая синхронизация не удалась: :msg',
      'sync.error.loginFailed':                 'Ошибка входа: :msg',
      'sync.error.invalidEmail':                'Введите корректный email.',
      'sync.recovery.sent':                     'Если этот email зарегистрирован, мы отправили ссылку для восстановления. Проверьте почту.',
      'sync.error.generic':                     'Ошибка: :msg',
      'sync.account.thisDevice':                'это устройство: :label',
      'sync.session.noLabel':                   '(без метки)',
      'sync.session.thisDeviceSuffix':          ' (это устройство)',
      'sync.session.revoke':                    'Отозвать',
      'sync.error.revokeFailed':                'Ошибка отзыва: :msg',
      'sync.session.fetchFailed':               'Не удалось получить список сессий: :msg',
      'sync.key.copyManual':                    'Не удалось скопировать — нажмите «Показать ключ» и Ctrl+C вручную.',
      'sync.key.removed':                       'Ключ удалён из локального хранилища.',
      'sync.backups.empty':                     'Нет облачных резервных копий. Создайте первую, нажав «Создать резервную копию сейчас».',
      'sync.backups.fetchFailed':               'Не удалось получить список: :msg',
      'sync.backups.creating':                  'Создание облачной резервной копии…',
      'sync.backups.createFailed':              'Ошибка создания резервной копии: :msg',
      'sync.backups.restored':                  'Восстановлена резервная копия #:id. Заменено :count сущностей. newCursor=:cursor.',
      'sync.backups.restoreFailed':             'Ошибка восстановления: :msg',
      'sync.backups.downloadFailed':            'Ошибка загрузки: :msg',
      'sync.backups.confirmDelete':             'Удалить резервную копию #:id из облака? Необратимо.',
      'sync.backups.deleted':                   'Резервная копия #:id удалена.',
      'sync.backups.deleteFailed':              'Ошибка удаления: :msg',
      'sync.pushPullSummary':                   'Отправка: :pushedAccepted принято, :pushedRejected отклонено • Загрузка: :applied сущностей, :deletes удалений.',
      'sync.returned':                          'Синхронизация вернула: :result',
      'sync.snapshotFailed':                    'Ошибка снимка: :msg',
      'sync.reconcile.running':                 'Полная ресинхронизация с аккаунтом…',
      'sync.reconcile.okPushed':                'Ресинхронизация OK — отправлено :count недостающих сущностей в аккаунт.',
      'sync.reconcile.okInSync':                'Ресинхронизация OK — библиотека совпадает с аккаунтом.',
      'sync.reconcile.failed':                  'Полная ресинхронизация не удалась: :msg',
      'sync.email.saveButFailed':               'Email сохранён, но отправка ссылки не удалась — попробуйте снова.',
      'sync.email.verifyLinkSent':              'Ссылка для подтверждения отправлена на :email. Проверьте почту и перейдите по ссылке.',
      'sync.email.removed':                     'Email удалён.',

      // === Sync result modal — strategies ===
      'syncResult.merge.title':                 'Объединено с облаком',
      'syncResult.merge.desc':                  'Сущности, присутствующие в обоих местах, перезаписаны версией из аккаунта. Уникальные локальные остаются и попадут в аккаунт при следующей синхронизации.',
      'syncResult.merge.tableHeader':           '<table class="result-table"><thead><tr><th>Тип</th><th>Из облака: перезаписано</th><th>Из облака: новые</th><th>Локальные сохранены → отправка</th><th>Всего после объединения</th></tr></thead><tbody>',
      'syncResult.useServer.title':             'Локальные данные заменены данными из облака',
      'syncResult.useServer.desc':              'Локальная библиотека полностью заменена состоянием аккаунта. Уникальные локальные сущности (если были) потеряны.',
      'syncResult.useServer.tableHeader':       '<table class="result-table"><thead><tr><th>Тип</th><th>Получено из облака</th><th>Локальные потеряны</th><th>Общие (перезаписано)</th><th>Всего после синхронизации</th></tr></thead><tbody>',
      'syncResult.sendMine.title':              'Отправка локальных данных в аккаунт',
      'syncResult.sendMine.desc':               'Локальная библиотека будет отправлена в аккаунт. Сущности, присутствующие в облаке, но отсутствующие локально, будут помечены как удалённые (tombstone) в аккаунте.',
      'syncResult.sendMine.tableHeader':        '<table class="result-table"><thead><tr><th>Тип</th><th>Отправлено</th><th>Перезаписей в аккаунте</th><th>Новых в аккаунте</th><th>Удалено из аккаунта</th></tr></thead><tbody>',
      'syncResult.fallback.title':              'Синхронизация завершена',
      'syncResult.empty':                       'Нет подробностей.',
      'syncResult.label.folders':               'Папки',
      'syncResult.label.groups':                'Группы',
      'syncResult.label.rules':                 'Наборы правил',
      'syncResult.label.actions':               'Действия',

      // === Options — toasts (bottom notifications) ===
      'toast.saved':                            'Сохранено',
      'toast.cantDeleteLastGroup':              'Нельзя удалить последнюю группу в папке',
      'toast.noOtherTab':                       'Нет другой вкладки с URL страницы',
      'toast.urlInserted':                      'Вставлен URL из вкладки',
      'toast.urlAlreadyAdded':                  'Шаблон уже в списке: :pattern',
      'toast.error':                            'Ошибка: :msg',
      'toast.shortcutMustIncludeMod':           'Допустимое сочетание: Ctrl+Alt+Shift + буква (кроме G H J K L), цифра или символ (кроме \\).',
      'toast.shortcutCollidesChrome':           'Предупреждение: сочетание конфликтует с сочетанием Chrome (напр. новая вкладка). Оно может не работать — выберите другое.',
      'toast.copied':                           'Скопировано: :text',
      'toast.copyFailed':                       'Не удалось скопировать',
      'toast.copyClipboard':                    'Скопировано в буфер обмена',
      'toast.copyLink':                         'Ссылка скопирована',
      'toast.copyToken':                        'Токен скопирован',
      'toast.noHistory':                        'Нет истории для этого набора правил',
      'toast.versionRestored':                  'Версия восстановлена',
      'toast.fileNotFullImport':                'Файл не подходит для полного импорта',
      'toast.unknownOption':                    'Неизвестный вариант — импорт отменён',
      'toast.importedSettings':                 'Настройки импортированы',
      'toast.parseError':                       'Ошибка разбора: :msg',
      'toast.invalidPartialFormat':             'Неверный формат — ожидался экспорт группы, папки или набора правил',
      'toast.groupImport.folderRejected':       'Это экспорт папки (несколько групп). Импортируйте его через импорт на уровне папки, а не группы.',
      'toast.exportedRule':                     'Набор правил экспортирован',
      'toast.signInFirst':                      'Сначала войдите в панели синхронизации',
      'toast.exportNotReady':                   'Экспорт не готов',
      'toast.noRulesSelected':                  'Наборы правил не выбраны',
      'toast.revokeError':                      'Ошибка отзыва: :msg',
      'toast.importedFromLink':                 'Импортировано по ссылке',
      'toast.invalidRuleFormat':                'Неверный формат файла — ожидался экспорт набора правил',
      'toast.importedReplaced':                 'Импортировано (заменено)',
      'toast.importedAsCopy':                   'Импортировано как копия',
      'toast.noImportData':                     'Нет данных для импорта',
      'toast.importedFolder':                   'Папка импортирована: :name',
      'toast.importedGroup':                    'Группа импортирована: :name',
      'toast.importedRule':                     'Набор правил импортирован: :name',

      // === Options — native confirm() dialogs ===
      'confirm.restoreVersion':                 'Восстановить эту версию? Текущее содержимое будет добавлено в историю как новый снимок.',
      'confirm.revokeSession':                  'Отозвать эту сессию?',
      'confirm.removeKey':                      'Удалить ключ с этого устройства?\n\nВход по-прежнему работает (сессия не зависит от ключа), но вы не сможете просмотреть или скопировать ключ на другое устройство с этого компьютера.\n\nУбедитесь, что у вас есть сохранённая копия ключа в другом месте (менеджер паролей).',
      'confirm.snapshot':                       'Полный снимок загружает всю библиотеку из облака и заменяет локальное состояние. Ваши ожидающие изменения НЕ будут потеряны (они остаются в очереди). Продолжить?',
      'confirm.removeEmail':                    'Удалить email из аккаунта? Без email вы не сможете восстановить доступ при потере ключа.',
      'confirm.signOut':                        'Выйти на этом устройстве? Локальная библиотека остаётся, но синхронизация прекращается.',

      // === Modal — full export ===
      'modal.export.title':                     'Экспорт настроек',
      'modal.export.mode.all':                  'Всё',
      'modal.export.mode.select':               'Выбранные папки/правила',
      'modal.export.selectAll':                 'Выбрать все',
      'modal.export.deselectAll':               'Снять выделение',
      'modal.export.hint':                      'Вы можете скопировать JSON в буфер обмена или скачать файл:',
      'modal.export.close':                     'Закрыть',
      'modal.export.share':                     'Поделиться ссылкой',
      'modal.export.share.title':               'Поделиться этим экспортом как временной публичной ссылкой',
      'modal.export.download':                  'Скачать файл',
      'modal.export.copy':                      'Копировать в буфер обмена',

      // === Modal — full import ===
      'modal.import.title':                     'Импорт настроек',
      'modal.import.pickerHint':                'Вставьте JSON из любого экспорта JustZix (полная резервная копия, папка, группа или набор правил) — форма определит тип и предложит варианты размещения.',
      'modal.import.warningStrong':             'Внимание:',
      'modal.import.warningRest':               ' полный импорт перезапишет все текущие настройки.',
      'modal.import.placeholder':               'Вставьте JSON сюда или выберите файл...',
      'modal.import.cancel':                    'Отмена',
      'modal.import.import':                    'Импорт',

      // === Modal — folder export ===
      'modal.folderExport.titlePrefix':         'Экспорт папки:',
      'modal.folderExport.hint':                'Выберите группы/правила для экспорта.',
      'modal.folderExport.cancel':              'Отмена',
      'modal.folderExport.share':               'Поделиться ссылкой',
      'modal.folderExport.shareTitle':          'Поделиться этой папкой как временной публичной ссылкой',
      'modal.folderExport.download':            'Скачать файл',

      // === Modal — group export ===
      'modal.groupExport.titlePrefix':          'Экспорт группы:',
      'modal.groupExport.hint':                 'Выберите наборы правил для экспорта.',
      'modal.groupExport.cancel':               'Отмена',
      'modal.groupExport.share':                'Поделиться ссылкой',
      'modal.groupExport.shareTitle':           'Поделиться этой группой как временной публичной ссылкой',
      'modal.groupExport.download':             'Скачать файл',

      // === Modal — folder import ===
      'modal.folderImport.title':               'Импорт папки',
      'modal.folderImport.placeholder':         'Вставьте JSON папки или выберите файл...',
      'modal.folderImport.conflicts':           'Обнаружены конфликты:',
      'modal.folderImport.action.rename':       'Импортировать как новую папку (с суффиксом «(импорт)»)',
      'modal.folderImport.action.overwrite':    'Перезаписать существующую папку',
      'modal.folderImport.action.merge':        'Объединить — добавить новые наборы правил в существующую папку (сохраняя оба)',
      'modal.folderImport.cancel':              'Отмена',
      'modal.folderImport.import':              'Импорт',

      // === Modal — action code ===
      // === Action bars (Phase 2 of multi-bars system, since v2.9) ===
      'actionBars.btn.label':                   'Панели действий',
      'actionBars.btn.title':                   'Управление панелями действий, назначенными этой папке',
      'actionBars.btn.titleGroup':              'Управление панелями действий, назначенными этой группе',
      'actionBars.btn.titleRule':               'Управление панелями действий, назначенными этому набору правил',
      'actionBars.parentType.folder':           'Папка',
      'actionBars.parentType.group':            'Группа',
      'actionBars.parentType.rule':             'Набор правил',
      'actionBars.parentType.folder.short':     '[П]',
      'actionBars.parentType.group.short':      '[Г]',
      'actionBars.parentType.rule.short':       '[Н]',
      'actionBars.empty':                       'На этом уровне нет панелей действий. Нажмите «+ Добавить новую панель», чтобы создать первую.',
      'actionBars.section.owned':               'Панели на этом уровне',
      'actionBars.section.fromFolder':          '↑ Унаследовано от папки «<strong>:name</strong>»',
      'actionBars.section.fromGroup':           '↑ Унаследовано от группы «<strong>:name</strong>»',
      'actionBars.elementsCount':               'Элементов: :count',
      'actionBars.enabled':                     'Включена',
      'actionBars.name.placeholder':            'ИМЯ',
      'actionBars.name.title':                  'Метка на краю панели (макс. 6 символов, ЗАГЛАВНЫЕ). Пусто = без метки.',
      'actionBars.color.title':                 'Цвет фона панели',
      'actionBars.orientation.title':           'Нажмите для смены ориентации панели (вертикальная ↔ горизонтальная)',
      'actionBars.color.clear':                 'Сбросить цвет фона',
      'actionBars.moveTo':                      'Переместить в:',
      'actionBars.delete':                      'Удалить',
      'actionBars.confirmDelete':               'Удалить эту панель?',
      'actionBars.confirmDeleteWithElements':   'Удалить эту панель? Назначенные элементы (:count) сохранят свои определения, но потеряют привязку к панели — вам нужно будет назначить их другой панели вручную.',
      'actionBars.deleteMigrate.message':       'У этой панели :count назначенных элементов. Выберите, куда переместить их перед удалением (или отвяжите от любой панели):',
      'actionBars.deleteMigrate.detachOption':  '— отвязать элементы (без панели) —',
      'actionBars.deleteMigrate.confirm':       'Переместить и удалить',
      'actionBars.deleteMigrate.cancel':        'Отмена',
      'modal.actionBars.titlePrefix':           'Панели действий —',
      'modal.actionBars.hint':                  'Панели, назначенные этому уровню. Панели папки агрегируют элементы из всех активных наборов правил папки, панели группы — только из группы, панели набора правил — только когда набор активен. Панель без видимых элементов не отрисовывается.',
      'modal.actionBars.add':                   '+ Добавить новую панель',
      'modal.actionBars.close':                 'Закрыть',

      // === Windows — dropdown type selector (since v2.13.41) ===
      'windows.btn.label':                      'Окна',
      'windows.btn.title':                      'Управление окнами, назначенными этой папке (CSS / JS / консоли)',
      'windows.btn.titleGroup':                 'Управление окнами, назначенными этой группе',
      'windows.btn.titleRule':                  'Управление окнами, назначенными этому набору правил',
      'windows.menu.title':                     'Выберите тип окна:',
      'windows.menu.comingSoon':                'скоро',
      'jsPanes.btn.label':                      'JS-панели',
      'jsConsoles.btn.label':                   'JS-консоли',
      'outputConsoles.btn.label':               'Консоли вывода',

      // === CSS panes (Phase 2 "Windows on the front", since v2.13.40) ===
      'cssPanes.btn.label':                     'CSS-панели',
      'cssPanes.contextMenu.disable':           'Закрыть CSS-панель',
      'cssPanes.btn.title':                     'Управление CSS-панелями, назначенными этой папке',
      'cssPanes.btn.titleGroup':                'Управление CSS-панелями, назначенными этой группе',
      'cssPanes.btn.titleRule':                 'Управление CSS-панелями, назначенными этому набору правил',
      'cssPanes.empty':                         'На этом уровне нет CSS-панелей. Нажмите «+ Добавить новую CSS-панель», чтобы создать первую.',
      'cssPanes.section.owned':                 'Панели на этом уровне',
      'cssPanes.name.placeholder':              'Имя панели',
      'cssPanes.color.title':                   'Цвет точки в заголовке',
      // Custom pane colors (since v2.13.58) — shared CSS / JS pane.
      'panes.style.dot':                        'Точка',
      'panes.style.headerBg':                   'Фон заголовка',
      'panes.style.headerText':                 'Текст заголовка',
      'panes.style.bodyBg':                     'Фон тела',
      'panes.style.bodyText':                   'Текст тела',
      'panes.style.runBtn':                     'Кнопка запуска',
      'panes.style.reset':                      'Очистить',
      'mcp.default.title':                      'Восстановить по умолчанию',
      // Action color labels (since v2.13.60) — short, no instructions.
      'action.colors.bg':                       'Фон',
      'action.colors.text':                     'Текст',
      'action.colors.dropdownBg':               'Фон списка',
      'action.colors.option':                   'Вариант',
      'action.colors.selected':                 'Выбранный',
      'action.colors.hover':                    'Наведение',
      'action.colors.label':                    'Метка',
      'action.colors.value':                    'Значение',
      'action.colors.placeholder':              'Подсказка',
      'action.colors.bgInactive':               'Фон неактивного',
      'action.colors.inactiveText':             'Текст неактивного',
      'cssPanes.enabled.label':                 'Включена',
      'cssPanes.enabled.title':                 'Должна ли панель появляться на подходящих страницах',
      'cssPanes.delete.title':                  'Удалить эту панель',
      'cssPanes.delete.confirm':                'Удалить CSS-панель «:name»? Содержимое текстового поля в активных вкладках будет потеряно.',
      'modal.cssPanes.titlePrefix':             'CSS-панели —',
      'modal.cssPanes.hint':                    'Плавающее текстовое поле, внедряемое на страницы, совпадающие с URL этого уровня. Содержимое хранится в sessionStorage для каждой вкладки (теряется при закрытии вкладки). Живое внедрение <style> — изменения CSS применяются мгновенно.',
      'modal.cssPanes.add':                     '+ Добавить новую CSS-панель',
      'modal.cssPanes.close':                   'Закрыть',
      'cssPane.frontend.close.title':           'Скрыть панель (останется скрытой до закрытия вкладки)',

      // === JS panes (Phase 2 cont., since v2.13.56) ===
      'jsPanes.contextMenu.disable':            'Закрыть JS-панель',
      'jsPanes.btn.title':                      'Управление JS-панелями, назначенными этой папке',
      'jsPanes.btn.titleGroup':                 'Управление JS-панелями, назначенными этой группе',
      'jsPanes.btn.titleRule':                  'Управление JS-панелями, назначенными этому набору правил',
      'jsPanes.empty':                          'На этом уровне нет JS-панелей. Нажмите «+ Добавить новую JS-панель», чтобы создать первую.',
      'jsPanes.section.owned':                  'Панели на этом уровне',
      'jsPanes.name.placeholder':               'Имя панели',
      'jsPanes.color.title':                    'Цвет точки в заголовке',
      'jsPanes.enabled.label':                  'Включена',
      'jsPanes.enabled.title':                  'Должна ли панель появляться на подходящих страницах',
      'jsPanes.delete.title':                   'Удалить эту панель',
      'jsPanes.delete.confirm':                 'Удалить JS-панель «:name»? Содержимое текстового поля в активных вкладках будет потеряно.',
      'modal.jsPanes.titlePrefix':              'JS-панели —',
      'modal.jsPanes.hint':                     'Плавающее текстовое поле, выполняющее JS в контексте страницы (мир MAIN, new Function) при каждом изменении. Содержимое хранится для каждой вкладки в sessionStorage. Ошибки выполнения отображаются в красном слое под кодом.',
      'modal.jsPanes.add':                      '+ Добавить новую JS-панель',
      'modal.jsPanes.close':                    'Закрыть',
      'jsPane.frontend.close.title':            'Скрыть панель (останется скрытой до закрытия вкладки)',

      // === JS Consoles (Phase 2 cont., since v2.13.66) ===
      'jsConsoles.contextMenu.disable':         'Закрыть JS-консоль',
      'jsConsoles.empty':                       'На этом уровне нет консолей. Нажмите «+ Добавить новую консоль», чтобы создать первую.',
      'jsConsoles.section.owned':               'Консоли на этом уровне',
      'jsConsoles.name.placeholder':            'Имя консоли',
      'jsConsoles.enabled.label':               'Включена',
      'jsConsoles.enabled.title':               'Должна ли консоль появляться на подходящих страницах',
      'jsConsoles.delete.title':                'Удалить эту консоль',
      'jsConsoles.delete.confirm':              'Удалить консоль «:name»? История вывода в активных вкладках будет потеряна.',
      'modal.jsConsoles.titlePrefix':           'JS-консоли —',
      'modal.jsConsoles.hint':                  'Интерактивный REPL — Ctrl+Enter выполняет код в мире MAIN страницы, вывод (или ошибка) добавляется в область журнала под полем ввода. Вывод хранится для каждой вкладки в sessionStorage.',
      'modal.jsConsoles.add':                   '+ Добавить новую консоль',
      'modal.jsConsoles.close':                 'Закрыть',
      'jsConsole.frontend.close.title':         'Скрыть консоль (до закрытия вкладки)',
      'jsConsole.input.placeholder':            'Код для выполнения (Ctrl+Enter — выполнить, Ctrl+L — очистить)',
      'jsConsole.clear.title':                  'Очистить вывод (Ctrl+L)',

      // === Output Console (Phase 2 finish, since v2.13.72) — read-only log viewer. ===
      'panes.style.clearBtn':                   'Кнопка очистки',
      'panes.style.actionBtns':                 'Кнопки действий',
      'outputConsoles.contextMenu.disable':     'Закрыть консоль вывода',
      'outputConsoles.empty':                   'На этом уровне нет консолей вывода. Нажмите «+ Добавить новую консоль вывода», чтобы создать первую.',
      'outputConsoles.section.owned':           'Консоли вывода на этом уровне',
      'outputConsoles.name.placeholder':        'Имя консоли вывода',
      'outputConsoles.enabled.label':           'Включена',
      'outputConsoles.enabled.title':           'Должна ли консоль вывода появляться на подходящих страницах',
      'outputConsoles.delete.title':            'Удалить эту консоль вывода',
      'outputConsoles.delete.confirm':          'Удалить консоль вывода «:name»? История журнала в активных вкладках будет потеряна.',
      'modal.outputConsoles.titlePrefix':       'Консоли вывода —',
      'modal.outputConsoles.hint':              'Просмотрщик журнала только для чтения — перехватывает console.log/warn/error/info со страницы (включая асинхронные). Очистка (⌫) очищает только просмотрщик, не консоль браузера.',
      'modal.outputConsoles.add':               '+ Добавить новую консоль вывода',
      'modal.outputConsoles.close':             'Закрыть',
      'outputConsole.frontend.close.title':     'Скрыть консоль вывода (до закрытия вкладки)',
      'outputConsole.clear.title':              'Очистить просмотрщик (Ctrl+Shift+L)',
      'outputConsole.autoscroll.title':         'Автопрокрутка к последним записям',
      'outputConsole.empty':                    '(нет записей — ожидание console.* / JZ.* со страницы)',
      // viewMode + filters + search (since v2.13.73, per spec section 6).
      'outputConsole.mode.standard.title':      'Показывать только console.* (клик / Ctrl+Shift+K → JZ.*)',
      'outputConsole.mode.jz.title':            'Показывать только window.JZ.* (клик / Ctrl+Shift+K → оба)',
      'outputConsole.mode.both.title':          'Показывать оба: console.* + JZ.* (клик / Ctrl+Shift+K → только console.*)',
      'outputConsole.filters.title':            'Фильтры по уровню',
      'outputConsole.filters.level.log':        'log',
      'outputConsole.filters.level.info':       'info',
      'outputConsole.filters.level.warn':       'warn',
      'outputConsole.filters.level.error':      'error',
      'outputConsole.filters.level.debug':      'debug',
      'outputConsole.search.title':             'Поиск в записях (Ctrl+Shift+S; Esc — очистить)',
      'outputConsole.search.placeholder':       'Фильтровать записи…',
      'outputConsole.tab.all':                  'Все',
      'outputConsole.tab.jz':                   'JZconsole',
      'outputConsole.tab.console':              'Консоль',
      'outputConsole.tab.network':              'Сеть',
      'outputConsole.tab.errors':               'Ошибки',
      'outputConsole.tab.datalayer':            'DataLayer',
      'outputConsole.net.field.callTime':       'Время вызова',
      'outputConsole.net.field.status':         'Статус',
      'outputConsole.net.field.type':           'Тип',
      'outputConsole.net.field.method':         'Метод',
      'outputConsole.net.field.domain':         'Домен',
      'outputConsole.net.field.path':           'Путь',
      'outputConsole.net.field.remote':         'Удалённый адрес',
      'outputConsole.net.field.initiator':      'Инициатор',
      'outputConsole.net.field.time':           'Время',
      'outputConsole.net.field.size':           'Размер',
      'outputConsole.net.field.url':            'URL',
      'outputConsole.net.field.error':          'Ошибка',
      'outputConsole.net.status.err':           'Ошибки/Прочее',
      'outputConsole.filter.all':               'все',
      'outputConsole.filter.none':              'нет',
      'outputConsole.net.domainFilter':         'фильтр по домену…',
      'outputConsole.net.domainsInclude':       'Домены+',
      'outputConsole.net.domainsExclude':       'Домены-',
      'outputConsole.dl.newPushes':             'Новые push-ы',
      'outputConsole.dl.currentState':          'Текущее состояние',
      'outputConsole.dl.expandAll':             'развернуть всё',
      'outputConsole.dl.collapseAll':           'свернуть всё',
      'outputConsole.net.size':                 'Размер',
      'outputConsole.net.time':                 'Время',
      'outputConsole.err.field.type':           'Тип',
      'outputConsole.err.field.location':       'Расположение',
      'outputConsole.err.field.stack':          'Стек',
      // API help popup (since v2.13.78, Ctrl+Shift+H).
      'outputConsole.apiHelp.title': 'JustZix — сочетания клавиш и окна на странице',
      'outputConsole.apiHelp.sec.global': 'Глобальные сочетания — работают на любой странице',
      'outputConsole.apiHelp.g.tempCss': 'Окно TEMP CSS — открыть / закрыть',
      'outputConsole.apiHelp.g.tempJs': 'Окно TEMP JS — открыть / закрыть',
      'outputConsole.apiHelp.g.tempJsc': 'Окно TEMP консоль JS — открыть / закрыть',
      'outputConsole.apiHelp.g.tempOut': 'Окно TEMP Output Console — открыть / закрыть',
      'outputConsole.apiHelp.g.ai': 'AI Helper — открыть / закрыть',
      'outputConsole.apiHelp.g.edit': 'Режим редактирования панели действий — вкл / выкл',
      'outputConsole.apiHelp.sec.window': 'Внутри окна — когда оно в фокусе',
      'outputConsole.apiHelp.w.run': 'Выполнить код / отправить сообщение — окно JS, консоль JS, AI Helper',
      'outputConsole.apiHelp.w.history': 'История введённых команд — консоль JS',
      'outputConsole.apiHelp.w.clearConsole': 'Очистить вывод — консоль JS',
      'outputConsole.apiHelp.w.escSearch': 'Очистить поле поиска — Output Console',
      'outputConsole.apiHelp.sec.output': 'Output Console',
      'outputConsole.apiHelp.o.clear': 'Очистить активную вкладку каждой консоли Output',
      'outputConsole.apiHelp.o.help': 'Показать / скрыть эту справку',
      'outputConsole.apiHelp.o.tabs': '6 вкладок: All, JZ, Console, Network, Errors, DataLayer — у каждой свой набор фильтров.',
      'outputConsole.apiHelp.o.network': 'Network: Alt+клик по строке добавляет её домен в фильтр включения, Alt+Shift+клик — в фильтр исключения.',
      'outputConsole.apiHelp.o.datalayer': 'DataLayer: перетащите полосу между столбцами, чтобы изменить их ширину.',
      'outputConsole.apiHelp.sec.mouse': 'Окна на странице — мышью',
      'outputConsole.apiHelp.m.drag': 'Перетащите окно за полосу заголовка, чтобы переместить его.',
      'outputConsole.apiHelp.m.resize': 'Измените размер окна, потянув за один из 8 маркеров на краях и в углах.',
      'outputConsole.apiHelp.m.snap': 'Поднесите окно к другому — они магнитно склеятся в группу, которая двигается вместе.',
      'outputConsole.apiHelp.m.context': 'Правый клик на полосе заголовка открывает меню: закрыть окно, отсоединить, шрифт, размер текста.',
      'outputConsole.apiHelp.m.tempDbl': 'Двойной клик по полосе заголовка окна TEMP переключает между размером по умолчанию и запомненным.',
      'outputConsole.apiHelp.m.scroll': 'Прокрутка колёсиком внутри окна прокручивает только его содержимое, а не страницу под ним.',
      'outputConsole.apiHelp.sec.editmode': 'Режим редактирования панели — после Ctrl+Alt+S',
      'outputConsole.apiHelp.em.undo': 'Отменить последнее изменение',
      'outputConsole.apiHelp.em.delete': 'Удалить выделенные метки',
      'outputConsole.apiHelp.em.deselect': 'Снять выделение со всех элементов',
      'outputConsole.apiHelp.em.move': 'Перетаскивайте выделенные элементы мышью — несколько двигаются вместе.',
      'outputConsole.apiHelp.em.select': 'Клик выделяет элемент, Ctrl+клик добавляет или убирает его из выделения, Shift+перетаскивание по пустой области выделяет прямоугольником.',
      'outputConsole.apiHelp.em.label': 'Кнопка „+ Метка" добавляет статическую текстовую подпись; двойной клик по метке открывает её редактор.',
      'outputConsole.apiHelp.sec.logging': 'Логирование в Output Console',
      'outputConsole.apiHelp.jzApi': 'Собственный логгер (kind=jz)',
      'outputConsole.apiHelp.aliases': 'Псевдонимы: window.__JUSTZIX__ (всегда), window.JZ (если не занят страницей).',
      'outputConsole.apiHelp.consoleApi': 'Стандартный console.* (kind=console, перехватывается автоматически)',
      'outputConsole.apiHelp.consoleNote': 'Каждый console.* страницы передаётся в оригинальную консоль (DevTools продолжают работать) и направляется в Output Console.',
      'outputConsole.apiHelp.footer': 'Полная инструкция (8 языков): панель JustZix → Помощь.',
      // === Onboarding / обучение ===
      'content.exec.cspBlocked': 'Эта страница блокирует выполнение скриптов (CSP). Включите «Пользовательские скрипты» для JustZix в настройках браузера.',
      'onboard.ui.back': 'Назад',
      'onboard.ui.next': 'Далее',
      'onboard.ui.finish': 'Завершить',
      'onboard.ui.copy': 'Копировать',
      'onboard.ui.copied': 'Скопировано',
      'onboard.ui.hint': 'Выполните это действие, чтобы продолжить',
      'onboard.ui.close': 'Закрыть',
      'onboard.front.title': 'Обучение на странице',
      'onboard.front.lead': 'Познакомьтесь с инструментами JustZix прямо на странице — плавающая кнопка, панель, окна кода и панели действий.',
      'onboard.front.titleFirst': 'JustZix на странице',
      'onboard.front.leadFirst': 'JustZix работает и прямо на странице. Это обучение покажет плавающую кнопку, окна CSS/JS и панели действий — шаг за шагом.',
      'onboard.front.empty': 'Обучающие сценарии для страницы появятся скоро.',
      'onboard.lr.titleFirst': 'Добро пожаловать в JustZix',
      'onboard.lr.title': 'Центр обучения',
      'onboard.lr.leadFirst': 'Впервые здесь? Интерактивное обучение проведёт вас через создание полного, работающего решения — шаг за шагом, прямо в панели.',
      'onboard.lr.lead': 'Запустите интерактивное руководство — оно шаг за шагом проведёт вас через создание готового решения.',
      'onboard.lr.scenarios': 'Уроки',
      'onboard.lr.start': 'Начать',
      'onboard.lr.later': 'Может быть позже',
      'onboard.lr.noAuto': 'Не показывать при запуске',
      'onboard.proj.name': 'Ваш первый проект',
      'onboard.proj.desc': 'С нуля до рабочего решения: каталог, панель действий, группа, набор правил с CSS и JS и кнопка, переключающая эффект.',
      'onboard.proj.meta': '19 шагов · ~3 мин',
      'onboard.proj.s1.title': 'Добро пожаловать в JustZix',
      'onboard.proj.s1.body': 'JustZix внедряет ваш собственный CSS и JavaScript на выбранные страницы. Сейчас мы построим полное решение с работающей кнопкой — шаг за шагом. Это займёт около 3 минут.',
      'onboard.proj.s2.title': 'Добавьте каталог',
      'onboard.proj.s2.body': 'Каталог объединяет правила для одного сайта. Нажмите подсвеченную кнопку <code>+</code> в боковой панели.',
      'onboard.proj.s3.title': 'Название каталога',
      'onboard.proj.s3.body': 'Введите название, например <code>Мой первый проект</code> — это метка только для вас.',
      'onboard.proj.s4.title': 'Шаблон URL',
      'onboard.proj.s4.body': 'Определяет, на каких страницах работает каталог. Введите адрес со звёздочкой и нажмите Enter, например <code>https://example.com/*</code>. Чтобы тренироваться на любой странице, введите просто <code>*</code>.',
      'onboard.proj.s5.title': 'Метка кнопки',
      'onboard.proj.s5.body': 'Максимум 3 буквы — они появятся на плавающей кнопке JustZix на странице.',
      'onboard.proj.s6.title': 'Панель действий',
      'onboard.proj.s6.body': 'Панель действий — это панель с интерактивными кнопками на странице. Сначала создадим её — нажмите подсвеченную кнопку панелей действий.',
      'onboard.proj.s7.title': 'Добавьте панель',
      'onboard.proj.s7.body': 'Нажмите, чтобы создать новую панель действий в этом каталоге.',
      'onboard.proj.s8.title': 'Закройте менеджер',
      'onboard.proj.s8.body': 'Панель готова. Закройте окно менеджера панелей, чтобы вернуться к каталогу.',
      'onboard.proj.s9.title': 'Откройте группу',
      'onboard.proj.s9.body': 'При создании каталога JustZix уже добавил первую группу. В боковой панели разверните каталог и нажмите на эту группу, чтобы открыть её, затем нажмите «Далее».',
      'onboard.proj.s10.title': 'Добавьте набор правил',
      'onboard.proj.s10.body': 'Набор правил — это собственно правило: пара кода CSS и JavaScript. Нажмите подсвеченную кнопку добавления набора правил.',
      'onboard.proj.s11.title': 'Откройте набор правил',
      'onboard.proj.s11.body': 'Новый набор правил появился в дереве под группой. Нажмите на него, чтобы открыть редактор набора правил, затем нажмите «Далее».',
      'onboard.proj.s12.title': 'Код CSS',
      'onboard.proj.s12.body': 'Вкладка CSS уже активна. Вставьте код ниже — он обесцвечивает всю страницу и готовит переключатель эффекта.',
      'onboard.proj.s13.title': 'Вкладка JavaScript',
      'onboard.proj.s13.body': 'Нажмите вкладку <code>JavaScript</code>, чтобы добавить код JS в набор правил.',
      'onboard.proj.s14.title': 'Код JavaScript',
      'onboard.proj.s14.body': 'Код JS выполняется при открытии страницы. Вставьте код ниже — он покажет сообщение, подтверждающее, что JustZix работает. У поля есть проверка синтаксиса.',
      'onboard.proj.s15.title': 'Вкладка Действия',
      'onboard.proj.s15.body': 'Теперь добавим интерактивную кнопку. Нажмите вкладку действий в редакторе набора правил.',
      'onboard.proj.s16.title': 'Добавьте кнопку',
      'onboard.proj.s16.body': 'Нажмите, чтобы добавить элемент действия. По умолчанию создаётся кнопка, выполняющая код по запросу.',
      'onboard.proj.s17.title': 'Код кнопки',
      'onboard.proj.s17.body': 'У каждой кнопки свой код, выполняемый при клике на странице. Нажмите поле кода новой кнопки.',
      'onboard.proj.s18.title': 'Код переключателя',
      'onboard.proj.s18.body': 'Вставьте код, который включает и выключает обесцвечивание, заданное в CSS. Затем сохраните изменения в окне редактора кода.',
      'onboard.proj.s19.title': 'Готово!',
      'onboard.proj.s19.body': 'У вас полное решение: каталог, панель действий, группа, набор правил с кодом CSS и JS и работающая кнопка. Откройте страницу, соответствующую шаблону URL — вы увидите сообщение, обесцвечивание и кнопку, которая его переключает. Обучение можно перезапустить из меню настроек.',
      // Font + size context menu (since v2.13.70) — shared by CSS/JS pane + JS Console.
      'panes.contextMenu.font':                 'Шрифт',
      'panes.contextMenu.size':                 'Размер',
      'panes.contextMenu.defaultFont':          'По умолчанию',
      'modal.confirmDismissDirty':              'У вас есть несохранённые изменения. Закрыть и потерять их?',
      'modal.confirmSaveBeforeClose':           'У вас есть несохранённые изменения.\n\nНажмите OK, чтобы сохранить и закрыть.\nНажмите Отмена, чтобы перейти к подтверждению отмены.',
      'modal.confirmDiscardChanges':            'Закрыть БЕЗ сохранения? Изменения будут потеряны.\n\nНажмите OK, чтобы отменить изменения.\nНажмите Отмена, чтобы продолжить редактирование.',
      'actionBars.moveLocked':                  'Нельзя переместить — у панели есть элементы, которые выпали бы за пределы новой области. Сначала переназначьте их.',
      'action.bar.noneOption':                  '— без панели —',
      'action.bar.unnamed':                     '(без названия)',
      'action.bar.selectTitle':                 'Назначить это действие панели. Панели наследуются вниз (папка → группа → правило).',
      'action.bar.noBarsHint':                  'Нет панелей действий в дереве набора правил — добавьте панель кнопкой «Панели действий» в папке/группе/правиле.',

      // === Typed actions (since v2.12.x) ===
      'action.color.title':                     'Цвет фона (кнопка/select/input/слайдер). Клик = палитра, либо введите hex рядом.',
      'action.colorText.title':                 'Цвет текста (и заливка полосы для SLIDER). По умолчанию белый, если не задан.',
      'action.colorHex.title':                  'Hex-код — копировать (Ctrl+C) или вставить (Ctrl+V) как обычный текст. Принимает #RGB или #RRGGBB.',
      'action.type.title':                      'Тип элемента (кнопка, выпадающий SELECT, …)',
      'action.type.button':                     'Кнопка',
      'action.type.select':                     'Выпадающий список (SELECT)',
      'action.type.input':                      'Текстовое поле (INPUT)',
      'action.type.slider':                     'Ползунок диапазона (SLIDER)',
      'action.type.textarea':                   'Многострочное поле (TEXTAREA)',
      'action.type.toggle3':                    'Переключатель на 3 состояния (TOGGLE3)',

      // Memory scope (since v2.13.0; ruleSet added v2.13.4) — shared across state-bearing types
      'action.memory.label':                    'Запоминать значение:',
      'action.memory.none':                     'Не запоминать (сбрасывается при перезагрузке)',
      'action.memory.page':                     'Для страницы (URL)',
      'action.memory.host':                     'Для домена',
      'action.memory.ruleSet':                  'В пределах набора правил (все URL, где он работает)',
      'action.memory.global':                   'Глобально (все страницы, общее)',
      'action.input.middleBtn':                 'INPUT · код :code',
      'action.slider.middleBtn':                'SLIDER · :min–:max шаг :step :unit · код :code',
      'action.textarea.middleBtn':              'TEXTAREA · :rows строк · код :code',
      'action.toggle3.middleBtn':               'TOGGLE3 · :filled/3 состояний · код :code',
      'action.toggle3.defaultState1':           'ВЫКЛ',
      'action.toggle3.defaultState2':           'СРЕД',
      'action.toggle3.defaultState3':           'ВКЛ',
      'action.toggle3.stateLabel.placeholder':  'метка (макс. 5)',
      'action.toggle3.stateValue.placeholder':  'значение (для JS)',
      'action.toggle3.defaultStateTitle':       'Состояние по умолчанию (когда нет памяти)',

      'action.select.middleBtn.staticEmpty':    'SELECT статический (без вариантов)',
      'action.select.middleBtn.static':         'SELECT статический · :count вариантов · код :code',
      'action.select.middleBtn.js':             'SELECT js · код :code',
      'action.select.empty':                    'Нет вариантов. Добавьте первый кнопкой ниже.',
      'action.select.untitled':                 '(без названия)',
      'action.select.optionNamePlaceholder':    'Имя (видимое)',
      'action.select.optionValuePlaceholder':   'значение (передаётся в JS)',
      'action.select.deleteOption':             'Удалить этот вариант',

      // === Modal — action code ===
      'modal.actionCode.titlePrefix':           'Код кнопки действия:',
      'modal.actionCode.body':                  'Код JavaScript, выполняемый при нажатии кнопки действия на плавающей панели. Выполняется в контексте страницы.',
      'modal.actionCode.cancel':                'Отмена',
      'modal.actionCode.save':                  'Сохранить',

      // === Modal — INPUT editor (since v2.13.0) ===
      'modal.inputEditor.titlePrefix':          'Редактор INPUT:',
      'modal.inputEditor.body':                 'Текстовое поле с необязательным JS-кодом, запускаемым при изменении (Enter/потеря фокуса). Значение запоминается согласно выбранной области.',
      'modal.inputEditor.placeholder':          'Подсказка:',
      'modal.inputEditor.defaultValue':         'Значение по умолчанию:',
      'modal.inputEditor.colorPlaceholder':     'Цвет подсказки:',
      'modal.inputEditor.codeLabel':            'JS-код (при изменении):',
      'modal.inputEditor.hint':                 'Доступно в коде: <code>value</code> (строка из поля), <code>$el</code> (HTMLInputElement), <code>$action</code> (id, label).',
      'modal.inputEditor.close':                'Закрыть',
      'modal.colorClear':                       'Восстановить цвет по умолчанию',
      'modal.sliderEditor.colorLabel':          'Цвет метки (слева):',
      'modal.sliderEditor.colorValue':          'Цвет значения (справа):',
      'modal.selectOptions.appearance.title':   'Внешний вид списка:',
      'modal.selectOptions.appearance.hint':    'Для <strong>варианта js</strong>: фон списка и цвет варианта работают во всех браузерах. Фон выбранного варианта и при наведении зависят от браузера (зависят от ОС) — для полной настройки используйте <em>статический</em> вариант.',
      'modal.selectOptions.colorDropdownBg':    'Фон списка:',
      'modal.selectOptions.colorOption':        'Цвет текста варианта:',
      'modal.selectOptions.colorSelected':      'Фон выбранного варианта:',
      'modal.selectOptions.colorHover':         'Фон при наведении:',
      'modal.sliderEditor.titlePrefix':         'Редактор SLIDER:',
      'modal.sliderEditor.body':                'Ползунок диапазона с необязательным JS-кодом, запускаемым после отпускания ползунка (`change`). Значение передаётся как Number.',
      'modal.sliderEditor.min':                 'Минимум:',
      'modal.sliderEditor.max':                 'Максимум:',
      'modal.sliderEditor.step':                'Шаг:',
      'modal.sliderEditor.defaultValue':        'Значение по умолчанию:',
      'modal.sliderEditor.unit':                'Единица (необязательно):',
      'modal.sliderEditor.codeLabel':           'JS-код (при изменении):',
      'modal.sliderEditor.hint':                'Доступно в коде: <code>value</code> (Number), <code>$el</code> (HTMLInputElement type=range), <code>$action</code> (id, label).',
      'modal.sliderEditor.close':               'Закрыть',
      'modal.sliderEditor.errMaxGtMin':         'Максимум должен быть больше минимума.',
      'modal.sliderEditor.errStepPositive':     'Шаг должен быть положительным.',
      'modal.textareaEditor.titlePrefix':       'Редактор TEXTAREA:',
      'modal.textareaEditor.body':              'Многострочное текстовое поле с необязательным JS-кодом при <code>change</code> (потеря фокуса). <strong>Enter</strong> в textarea — настоящий перенос строки, НЕ запускает код (в отличие от INPUT).',
      'modal.textareaEditor.placeholder':       'Подсказка:',
      'modal.textareaEditor.defaultValue':      'Значение по умолчанию:',
      'modal.textareaEditor.rows':              'Число строк:',
      'modal.textareaEditor.colorPlaceholder':  'Цвет подсказки:',
      'modal.textareaEditor.codeLabel':         'JS-код (при изменении/потере фокуса):',
      'modal.textareaEditor.hint':              'Доступно в коде: <code>value</code> (строка из textarea, может содержать <code>\\n</code>), <code>$el</code> (HTMLTextAreaElement), <code>$action</code> (id, label).',
      'modal.textareaEditor.close':             'Закрыть',

      // === Modal — TOGGLE3 editor (od v2.13.25) ===
      'modal.toggle3Editor.titlePrefix':        'Редактор TOGGLE3:',
      'modal.toggle3Editor.body':               'Сегментированный переключатель на 3 состояния. Нажатие на неактивное состояние активирует его и запускает JS-код с <code>value</code> выбранного состояния. Память сохраняет текущий индекс (0-2) для каждого действия.',
      'modal.toggle3Editor.statesTitle':        'Состояния (ровно 3, radio = по умолчанию):',
      'modal.toggle3Editor.colorBg':            'Цвет фона:',
      'modal.toggle3Editor.colorHover':         'Цвет при наведении:',
      'modal.toggle3Editor.colorInactiveText':  'Цвет текста неактивного:',
      'modal.toggle3Editor.codeLabel':          'JS-код (при смене состояния):',
      'modal.toggle3Editor.hint':               'Доступно в коде: <code>value</code> (state.value, при отсутствии — state.label), <code>stateIdx</code> (0-2), <code>stateLabel</code>, <code>$el</code> (контейнер div), <code>$action</code>.',
      'modal.toggle3Editor.close':              'Закрыть',

      // === Modal — SELECT editor (since v2.12.1, redesign v2.12.2 — variants) ===
      'modal.selectOptions.titlePrefix':        'Редактор SELECT:',
      'modal.selectOptions.idLabel':            'ID действия:',
      'modal.selectOptions.selectorLabel':      'JS-селектор:',
      'modal.selectOptions.copyId':             'Копировать ID',
      'modal.selectOptions.copySelector':       'Копировать JS-селектор',
      'modal.selectOptions.variant.static':     'Статические варианты + один JS-код',
      'modal.selectOptions.variant.js':         'Только JS-код (управляет списком)',
      'modal.selectOptions.static.body':        'Каждый вариант: <strong>имя</strong> (видимо в списке) + <strong>значение</strong> (передаётся в код как переменная <code>value</code>). При изменении выполняется один общий код.',
      'modal.selectOptions.static.hint':        'В коде доступно: <code>value</code> (строка), <code>$el</code> (элемент select), <code>$action</code> (id, label).',
      'modal.selectOptions.staticCodeLabel':    'JS-код (общий):',
      'modal.selectOptions.js.body':            'Ваш JS-код выполняется один раз после вставки списка на страницу. Получает ссылку <code>$el</code> на пустой <code>&lt;select&gt;</code> — варианты и обработку <code>change</code> вы реализуете сами.',
      'modal.selectOptions.js.hint':            'Доступно: <code>$el</code> (HTMLSelectElement), <code>$action</code> (id, label).',
      'modal.selectOptions.jsCodeLabel':        'JS-код:',
      'modal.selectOptions.addOption':          '+ Добавить вариант',
      'modal.selectOptions.close':              'Закрыть',

      // === Modal — sync conflict ===
      'modal.syncConflict.title':               'Конфликт синхронизации',
      'modal.syncConflict.description':         'Вы вошли в аккаунт, в котором уже есть данные в облаке, и на этом устройстве также есть локальная библиотека. Выберите, что делать — операция необратима локально, но локальные данные можно экспортировать перед подтверждением.',
      'modal.syncConflict.localLabel':          'На этом устройстве локально:',
      'modal.syncConflict.serverLabel':         'В облачном аккаунте:',
      'modal.syncConflict.useServer.title':     'Использовать данные из облачного аккаунта',
      'modal.syncConflict.useServer.body':      'Локальные папки/группы/правила будут заменены данными из аккаунта. Выберите это, если в аккаунте более новые и точные данные, чем на этом устройстве.',
      'modal.syncConflict.sendMine.title':      'Отправить мои локальные данные в аккаунт',
      'modal.syncConflict.sendMine.body':       'Состояние аккаунта будет заменено тем, что есть на этом устройстве. Выберите это, если на этом устройстве более новые и точные данные.',
      'modal.syncConflict.merge.title':         'Объединить оба набора',
      'modal.syncConflict.merge.body':          'Локальные и облачные сущности будут сохранены вместе. Если один и тот же элемент существует в обоих местах (тот же ID), побеждает версия аккаунта. Только локальные элементы будут отправлены в аккаунт при следующей синхронизации.',
      'modal.syncConflict.exportLocal':         'Сначала экспортировать локальные',
      'modal.syncConflict.cancel':              'Отменить вход (выйти)',

      // === Modal — share import ===
      'modal.shareImport.title':                'Импорт по ссылке JustZix',
      'modal.shareImport.body':                 'Вставьте полную ссылку или только токен <strong>JZS-XXXX-XXXX-XXXX-XXXX</strong>:',
      'modal.shareImport.placeholder':          'https://www.justzix.com/s/JZS-XXXX-XXXX-XXXX-XXXX',
      'modal.shareImport.cancel':               'Отмена',
      'modal.shareImport.fetch':                'Получить',
      'modal.shareImport.preview.title':        'Предпросмотр общего экспорта',
      'modal.shareImport.jsWarn':               '<strong>⚠ ВНИМАНИЕ:</strong> Экспорт содержит код JavaScript. Код будет выполняться на страницах, совпадающих с правилами — проверьте, что он делает, перед импортом.',
      'modal.shareImport.showCode':             'Показать код JavaScript',
      'modal.shareImport.back':                 '← Назад',
      'modal.shareImport.confirm':              'Импорт',

      // === Modal — my shares ===
      'modal.shares.title':                     'Мои ссылки',
      'modal.shares.body':                      'Временные публичные ссылки на экспорты. Бэкенд хранит данные до истечения срока, затем удаляет их.',
      'modal.shares.howTo':                     'Чтобы создать новую ссылку — откройте экспорт (полный / папка / группа / набор правил) и нажмите <strong>«Поделиться ссылкой»</strong>.',
      'modal.shares.refresh':                   'Обновить',
      'modal.shares.close':                     'Закрыть',

      // === Modal — share create ===
      'modal.shareCreate.title':                'Поделиться экспортом по ссылке',
      'modal.shareCreate.activeFor':            'Ссылка будет активна:',
      'modal.shareCreate.ttl.1h':               '1 час',
      'modal.shareCreate.ttl.6h':               '6 часов',
      'modal.shareCreate.ttl.12h':              '12 часов',
      'modal.shareCreate.ttl.24h':              '24 часа',
      'modal.shareCreate.ttl.default':          '(по умолчанию)',
      'modal.shareCreate.ttl.48h':              '48 часов',
      'modal.shareCreate.expireNote':           'После истечения срока ссылка перестанет работать. Вы можете отозвать её раньше в разделе «Мои ссылки».',
      'modal.shareCreate.cancel':               'Отмена',
      'modal.shareCreate.generate':             'Сгенерировать ссылку',
      'modal.shareCreate.result.title':         'Ссылка сгенерирована',
      'modal.shareCreate.result.urlLabel':      'Отправьте эту ссылку получателю:',
      'modal.shareCreate.result.copyUrl':       'Копировать ссылку',
      'modal.shareCreate.result.tokenLabel':    'Или только токен (для вставки в «Импорт по ссылке»):',
      'modal.shareCreate.result.copyToken':     'Копировать токен',
      'modal.shareCreate.result.goToMine':      'Открыть «Мои ссылки»',
      'modal.shareCreate.result.close':         'Закрыть',

      // === Modal — sync result ===
      'modal.syncResult.title':                 'Результат синхронизации',
      'modal.syncResult.close':                 'ОК, понятно',

      // === Modal — cloud sync ===
      'modal.sync.title':                       'Облачная синхронизация',
      'modal.sync.signedOut.tab.register':      'Первое устройство',
      'modal.sync.signedOut.tab.login':         'У меня есть ключ',
      'modal.sync.signedOut.tab.recover':       'Я потерял ключ',
      'modal.sync.signedOut.register.body':     'Сгенерируйте новый ключ синхронизации. <strong>Сохраните его в надёжном месте</strong> — бэкенд хранит только хеш. Ключ позволяет войти на втором устройстве и видеть ту же библиотеку.',
      'modal.sync.signedOut.deviceLabel':       'Метка устройства (необязательно):',
      'modal.sync.signedOut.deviceLabel.placeholder1': 'напр. Рабочий ПК',
      'modal.sync.signedOut.deviceLabel.placeholder2': 'напр. Ноутбук',
      'modal.sync.signedOut.yourKey':           'Ваш ключ синхронизации:',
      'modal.sync.signedOut.regenerate':        'Сгенерировать новый',
      'modal.sync.signedOut.copyClipboard':     'Копировать в буфер обмена',
      'modal.sync.signedOut.warnSave':          '⚠ <strong>Скопируйте и сохраните ключ сейчас.</strong> Ключ также будет доступен во вкладке «Ключ» после регистрации — но лучше держать копию в менеджере паролей.',
      'modal.sync.signedOut.login.body':        'Вставьте ключ, который вы видели при регистрации первого устройства. Формат: <code>SYNC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX</code>.',
      'modal.sync.signedOut.keyLabel':          'Ключ синхронизации:',
      'modal.sync.signedOut.recover.body':      'Если в аккаунте был задан <strong>подтверждённый</strong> email — введите его и отправьте ссылку для восстановления. После перехода по ссылке в письме вы получите <strong>новый ключ</strong> (одноразовый). Все активные сессии будут завершены.',
      'modal.sync.signedOut.email':             'Email аккаунта:',
      'modal.sync.signedOut.email.placeholder': 'someone@example.com',
      'modal.sync.signedOut.cancel':            'Отмена',
      'modal.sync.signedOut.regSubmit':         'Зарегистрировать устройство',
      'modal.sync.signedOut.loginSubmit':       'Войти',
      'modal.sync.signedOut.recoverSubmit':     'Отправить ссылку для восстановления',
      'modal.sync.signedIn.tab.status':         'Статус',
      'modal.sync.signedIn.tab.key':            'Ключ доступа',
      'modal.sync.signedIn.tab.backups':        'Резервные копии',
      'modal.sync.signedIn.tab.email':          'Email и восстановление',
      'modal.sync.signedIn.status.account':     'Аккаунт',
      'modal.sync.signedIn.status.pending':     'Ожидающие изменения:',
      'modal.sync.signedIn.status.cloudLib':    'Библиотека в облаке',
      'modal.sync.signedIn.status.lastCursor':  'Последняя загрузка (cursor):',
      'modal.sync.signedIn.status.syncNow':     'Синхронизировать сейчас',
      'modal.sync.signedIn.status.snapshot':    'Полный снимок',
      'modal.sync.signedIn.status.refresh':     'Обновить данные аккаунта',
      'modal.sync.signedIn.status.reconcile':   'Полная пересинхронизация с аккаунтом',
      'modal.sync.signedIn.status.sessions':    'Активные сессии (устройства)',
      'modal.sync.signedIn.status.history':     'История синхронизаций',
      'modal.sync.history.empty':               'Синхронизаций пока нет.',
      'modal.sync.history.viewAll':             'Показать всю историю',
      'modal.sync.history.changes':             'Изменения:',
      'modal.sync.history.device':              'Устройство:',
      'modal.sync.history.fromDevices':         'Получено с:',
      'modal.sync.history.stateAfter':          'Состояние после синхронизации:',
      'modal.sync.history.noDetail':            'Нет деталей изменений (старая запись).',
      'modal.sync.signedIn.key.body':           'Ключ синхронизации для этого аккаунта. Введите его на втором устройстве для синхронизации библиотеки.',
      'modal.sync.signedIn.key.yourKey':        'Ваш ключ:',
      'modal.sync.signedIn.key.reveal':         'Показать ключ',
      'modal.sync.signedIn.key.copy':           'Копировать в буфер обмена',
      'modal.sync.signedIn.key.forget':         'Удалить ключ с этого устройства',
      'modal.sync.signedIn.key.warn':           '⚠ Ключ даёт полный доступ к библиотеке. Не показывайте его публично. Если не хотите хранить его локально — нажмите <strong>Удалить ключ с этого устройства</strong> (сохраните копию в менеджере паролей). Вход по-прежнему работает, но ключ нельзя будет просмотреть снова.',
      'modal.sync.signedIn.key.noKey':          'Ключ не хранится на этом устройстве (вход выполнен до появления этой функции или ключ удалён вручную). Восстановить его можно только через письмо восстановления или если у вас есть копия в другом месте.',
      'modal.sync.signedIn.backups.hint':       'Облачные резервные копии — восстановление заменяет всю библиотеку.',
      'modal.sync.signedIn.backups.label':      'Метка новой резервной копии (необязательно):',
      'modal.sync.signedIn.backups.placeholder':'напр. Перед большим изменением',
      'modal.sync.signedIn.backups.create':     'Создать резервную копию сейчас',
      'modal.sync.signedIn.backups.refresh':    'Обновить список',
      'modal.sync.signedIn.email.body':         'Email включает <strong>восстановление</strong> — при потере ключа вы можете получить новый, перейдя по ссылке, отправленной на этот адрес. Добавление email требует подтверждения (ссылка в письме).',
      'modal.sync.signedIn.email.add':          'Добавить email (восстановление):',
      'modal.sync.signedIn.email.placeholder':  'someone@example.com',
      'modal.sync.signedIn.email.submit':       'Отправить ссылку для подтверждения',
      'modal.sync.signedIn.email.remove':       'Удалить email',
      'modal.sync.signedIn.email.recoveryFlow': 'Процесс восстановления: на другом устройстве (или в ситуации без ключа) выберите вкладку <strong>«Я потерял ключ»</strong> в панели входа, введите этот email — бэкенд отправит ссылку, которая сгенерирует новый ключ и завершит все активные сессии.',
      'modal.sync.signedIn.logout':             'Выйти на этом устройстве',
      'modal.sync.signedIn.close':              'Закрыть',

      // === Content script (floating button + actions panel) ===
      'content.float.titleGloballyOff':         'Расширение глобально отключено — нажмите значок расширения, чтобы включить',
      'content.float.titleNormal':              'Левый клик: включить/отключить папку • Правый клик: показать список наборов правил • Перетащите для перемещения',
      'content.panel.empty':                    'Нет активных наборов правил',
      'content.panel.actionBarsTitle':          'Панели действий',
      'content.panel.connectionsTitle':         'Соединения',
      'content.actionsPanel.dragHandle':        'Перетащите для перемещения',
      'content.editMode.banner':                'Режим редактирования панели — клик/Ctrl+клик выделяет, перетаскивание перемещает · Ctrl+Z отмена · Ctrl+Alt+S для выхода',
      'content.editMode.undoEmpty':             'Нечего отменять',
      'content.editMode.addLabel':              '+ Метка',
      'content.editMode.resetBar':              'Сбросить панель',
      'content.editMode.resetConfirm':          'Сбросить панель к расположению по умолчанию? Пользовательские позиции, размеры и метки будут удалены.',
      'content.labelEditor.text':               'Текст:',
      'content.labelEditor.font':               'Шрифт:',
      'content.labelEditor.size':               'Размер:',
      'content.labelEditor.color':              'Цвет:',
      'content.labelEditor.delete':             'Удалить',
      'content.labelEditor.close':              'Закрыть',
      'content.labelContextMenu.delete':        'Удалить метку',
      'content.bar.hideUntilReload':            'Закрыть эту панель (до перезагрузки)',
      'content.bar.disconnectFrom':             'Отсоединить от: :name',
      'content.bar.disconnectAll':              'Отсоединить все соединения',
      'content.bar.fallbackName':               'Панель',
      'content.float.label':                    '⊕ Главная кнопка',
      'content.toast.noFoldersForPage':         'Нет папок для этой страницы',
      'content.toast.floatVisible':             'Плавающая кнопка включена',
      'content.toast.floatHidden':              'Плавающая кнопка скрыта',

      // === Background (context menu + JS error logger) ===
      'background.contextMenu.toggleFloat':     'Включить/отключить плавающую кнопку для этой страницы',
      'background.contextMenu.editMode':        'Режим редактирования панели действий (вкл/выкл)',
      'background.contextMenu.actionBars':      'Панели действий',
      'background.contextMenu.actionBarsEmpty': '(нет панелей для этой страницы)',
      'background.contextMenu.actionBarFallback': '(без названия)',
      'background.contextMenu.cssPanes':         'CSS-панели',
      'background.contextMenu.cssPanesEmpty':    '(нет CSS-панелей для этой страницы)',
      'background.contextMenu.cssPaneFallback':  '(без названия)',
      'background.contextMenu.jsPanes':          'JS-панели',
      'background.contextMenu.jsPanesEmpty':     '(нет JS-панелей для этой страницы)',
      'background.contextMenu.jsPaneFallback':   '(без названия)',
      'background.contextMenu.jsConsoles':       'JS-консоли',
      'background.contextMenu.jsConsolesEmpty':  '(нет консолей для этой страницы)',
      'background.contextMenu.jsConsoleFallback':'(без названия)',
      'background.contextMenu.outputConsoles':       'Консоли вывода',
      'background.contextMenu.outputConsolesEmpty':  '(нет консолей вывода для этой страницы)',
      'background.contextMenu.outputConsoleFallback':'(без названия)',
      // TEMP panes (since v2.13.81).
      'background.contextMenu.tempPanes':            'Временное окно (TEMP)',
      'background.contextMenu.tempCss':              'Временная CSS-панель (Ctrl+Alt+G)',
      'background.contextMenu.tempJs':               'Временная JS-панель (Ctrl+Alt+H)',
      'background.contextMenu.tempJsConsole':        'Временная JS-консоль (Ctrl+Alt+J)',
      'background.contextMenu.tempOutput':           'Временная консоль вывода (Ctrl+Alt+K)',
      'pane.collapse.title':                      'Свернуть окно до строки заголовка',
      'pane.expand.title':                        'Развернуть окно',
      'aiHelper.form.submit':                     'OK',
      'aiHelper.form.cancel':                     'Отмена',
      'aiHelper.form.yes':                        'Да',
      'aiHelper.form.no':                         'Нет',
      'aiHelper.form.askTitle':                   'Вопрос',
      'aiHelper.form.confirmTitle':               'Подтверждение',
      'aiHelper.form.updateConfirmTitle':               'Обновить?',
      'aiHelper.style.title':                           'Внешний вид AI Helper',
      'aiHelper.style.role.user':                       'Ваши сообщения',
      'aiHelper.style.role.assistant':                  'Ответы AI',
      'aiHelper.style.role.tool':                       'Результаты инструментов',
      'aiHelper.style.role.form':                       'Формы',
      'aiHelper.style.font':                            'Шрифт',
      'aiHelper.style.size':                            'Размер',
      'aiHelper.style.color':                           'Текст',
      'aiHelper.style.bg':                              'Фон',
      'aiHelper.style.reset':                           'Сбросить к умолчанию',
      'aiHelper.style.resetAll':                        'Сбросить всё к умолчанию',
      'aiHelper.confirm.update.folder':                 'Обновление каталога',
      'aiHelper.confirm.update.group':                  'Обновление группы',
      'aiHelper.confirm.update.rule':                   'Обновление набора',
      'aiHelper.confirm.update.bar':                    'Обновление панели действий',
      'aiHelper.confirm.update.action':                 'Обновление действия',
      'aiHelper.confirm.update.cssPane':                'Обновление окна CSS',
      'aiHelper.confirm.update.jsPane':                 'Обновление окна JS',
      'aiHelper.confirm.update.jsConsole':             'Обновление консоли JS',
      'aiHelper.confirm.update.outputConsole':         'Обновление Output Console',
      'aiHelper.form.createConfirmTitle':         'Создать?',
      'aiHelper.form.confirmed':                  'подтверждено',
      'aiHelper.form.cancelled':                  'отменено',
      'tempPane.close.title':                        'Закрыть временное окно (содержимое сохраняется в сессии)',
      'tempPane.clear.title':                        'Очистить содержимое окна',
      'background.jsError.prefix':              '[JustZix] Ошибка JS в наборе правил/действии «:id»:\n',
      // === AI Helper (od v2.13.127) ===
      'settings.aiHelper.label': 'AI Helper',
      'settings.aiHelper.title': 'Настроить API-ключи для AI Helper',
      'modal.aiHelper.title': 'AI Helper',
      'modal.aiHelper.intro': 'Введите API-ключи провайдеров, которыми хотите пользоваться. Достаточно одного.',
      'modal.aiHelper.keyOpenai': 'API-ключ — OpenAI',
      'modal.aiHelper.keyAnthropic': 'API-ключ — Anthropic',
      'modal.aiHelper.keyGemini': 'API-ключ — Gemini',
      'modal.aiHelper.defaultProvider': 'Провайдер по умолчанию',
      'modal.aiHelper.defaultModel': 'Модель по умолчанию',
      'modal.aiHelper.modelAuto': '— авто (высший) —',
      'modal.aiHelper.loadModels': 'Загрузить модели',
      'modal.aiHelper.securityNote': '<strong>Безопасность:</strong> по умолчанию API-ключи хранятся только в локальном хранилище этого браузера (<code>chrome.storage.local</code>). Флажок «синхр.» рядом с ключом отправляет этот ключ через облако JustZix (то же, через которое расширение синхронизирует остальные данные) на ваши другие устройства. Неотмеченные ключи никогда не покидают это устройство.',
      'modal.aiHelper.syncKey':                'синхр.',
      'modal.aiHelper.syncKeyTitle': 'Синхронизировать этот ключ между устройствами через облако JustZix',
      'modal.aiHelper.cancel': 'Отмена',
      'modal.aiHelper.save': 'Сохранить',
      'modal.aiHelper.needKey': 'Сначала введите API-ключ.',
      'modal.aiHelper.loadingModels': 'Загрузка моделей…',
      'modal.aiHelper.errModels': 'Не удалось загрузить модели',
      'modal.aiHelper.modelsLoaded': 'моделей доступно',
      'aiHelper.win.title': 'AI Helper',
      'aiHelper.win.close': 'Закрыть',
      'aiHelper.win.provider': 'Провайдер',
      'aiHelper.win.model': 'Модель',
      'aiHelper.win.clear': 'Очистить разговор',
      'aiHelper.win.clearConfirm': 'Очистить весь разговор и начать заново?',
      'aiHelper.win.inputPlaceholder': 'Спросите о помощи с CSS, JS или действием…',
      'aiHelper.win.send': 'Отправить (Ctrl+Enter)',
      'aiHelper.win.welcome': 'Привет! Я помогу подготовить CSS, JavaScript или действия для этой страницы. Опишите, чего вы хотите добиться.',
      'aiHelper.win.you': 'Вы',
      'aiHelper.win.assistant': 'AI Helper',
      'aiHelper.win.thinking': 'Думаю…',
      'aiHelper.win.error': 'Ошибка соединения с API',
      'aiHelper.win.noKeys': 'Нет API-ключей. Добавьте ключ в Настройках → AI Helper.',
      'aiHelper.win.noModel': 'Выберите модель.',
      'aiHelper.win.loadingModels': 'Загрузка…',
      'aiHelper.win.modelsError': 'Ошибка загрузки моделей',
      'background.contextMenu.aiHelper': 'AI Helper',
      'popup.aiHelper.label': 'AI Helper',
      'popup.aiHelper.on': 'Открыт — нажмите, чтобы закрыть (Ctrl+Alt+\\)',
      'popup.aiHelper.off': 'Закрыт — нажмите, чтобы открыть (Ctrl+Alt+\\)',
      'popup.aiHelper.unavailable': 'Недоступно на этой странице',
      // === AI Helper etap 2 (od v2.13.128) ===
      'aiHelper.win.maxRounds': 'Достигнут лимит шагов инструментов за один ход. Напишите, как продолжить.',
      'aiHelper.win.copy':                        'Скопировать диалог',
      'aiHelper.win.copy.all':                    'Скопировать весь диалог',
      'aiHelper.win.copy.lastInput':              'Скопировать последний промпт API (system + история + ввод)',
      'aiHelper.win.copyNoInput':                 'Нечего копировать',
      'aiHelper.copy.lastInput.prefix':           '[JustZix → переслать в API] Скопирован ВЕСЬ последний промпт, отправленный модели (system + история + последнее сообщение). Отправь блок ниже в API как ПЕРВОЕ сообщение user в новом диалоге. УДАЛИ этот заголовок перед отправкой.',
      'aiHelper.win.copied':                      'Скопировано',
      'aiHelper.win.copyFailed':                  'Не удалось скопировать — проверьте права доступа к буферу обмена.',
      'aiHelper.win.font':                        'Шрифт',
      'aiHelper.win.fontSize':                    'Размер',
      'aiHelper.win.defaultFont':                 'По умолчанию',
      'aiHelper.win.stop':                        'Остановить агента',
      'aiHelper.win.stopped':                     'Прервано пользователем.',
      'aiHelper.win.ctxUsage':                    'Использование контекста модели',
      'aiHelper.win.ctxUsageDetail':              'Контекст',
      'aiHelper.win.compact':                     'Сжать разговор (резюме LLM)',
      'aiHelper.win.compactNoop':                 'История слишком короткая — нечего сжимать.',
      'aiHelper.win.compactNoProvider':           'Сначала выберите провайдера и модель.',
      'aiHelper.win.compactErr':                  'Ошибка сжатия',
      'aiHelper.win.compactEmpty':                'Модель вернула пустой результат.',
      'aiHelper.win.compactedPrefix':             'Суммировано',
      'aiHelper.win.compactedSuffix':             'предыдущих сообщений:',
      'aiHelper.win.telemetryTooltip':            'Время и токены этой сессии',
      'aiHelper.win.telemetryTimers':             'Время: с последнего сообщения (u) / с последнего инструмента (t) / общая активность агента (a)',
      'aiHelper.win.telemetryCurrent':            'Токены в текущем действии (↑ ввод / ↓ вывод)',
      'aiHelper.win.telemetrySession':            'Всего токенов за сессию',
      'aiHelper.win.msgTelem':                    'Токены этого раунда и время ответа модели',
      'aiHelper.win.toolDur':                     'Время выполнения инструмента',
      'aiHelper.win.stepIdBadge':                 'Номер шага в истории',
      'aiHelper.form.freeText':                   'Или ответьте текстом',
      'aiHelper.form.freeTextOnly':               'Ваш ответ',
      'aiHelper.form.freeTextPlaceholder':        'Введите ответ…',
      'aiHelper.win.turnLimit.body':              'Достигнут лимит 10 шагов инструментов в этом ходе. Агент остановился — проверьте последнее использованное средство и его результат. Продолжить отсюда со сбросом лимита или остановиться?',
      'aiHelper.win.turnLimit.lastTool':          'Последний инструмент',
      'aiHelper.win.turnLimit.lastResult':        'Результат',
      'aiHelper.win.turnLimit.continue':          'Продолжить (сброс лимита)',
      'aiHelper.win.turnLimit.stop':              'Остановить',
      'aiHelper.win.turnLimit.stoppedByUser':     'Остановлено.',
      'aiHelper.copy.title':                      'Диалог JustZix AI Helper',
      'aiHelper.copy.toolResult':                 'Результат инструмента',
      'aiHelper.tool.label': 'инструмент',
      'aiHelper.tool.rejected': 'Пользователь отклонил операцию.',
      'aiHelper.confirm.folder': 'AI Helper хочет создать ПАПКУ:',
      'aiHelper.confirm.group': 'AI Helper хочет создать ГРУППУ:',
      'aiHelper.confirm.rule': 'AI Helper хочет создать НАБОР:',
      'aiHelper.confirm.create.cssPane': 'AI Helper хочет создать ОКНО CSS:',
      'aiHelper.confirm.create.jsPane': 'AI Helper хочет создать ОКНО JS:',
      'aiHelper.confirm.create.jsConsole': 'AI Helper хочет создать КОНСОЛЬ JS:',
      'aiHelper.confirm.create.outputConsole': 'AI Helper хочет создать OUTPUT-КОНСОЛЬ:',
      'aiHelper.confirm.setPaneCode': 'AI Helper хочет вставить код в окно «:name» (:id).\nДлина: сейчас :old симв., новый :new симв.\nВНИМАНИЕ: код постоянного окна живёт в пределах вкладки (sessionStorage), не между сессиями.',
      'aiHelper.confirm.bar': 'AI Helper хочет создать ПАНЕЛЬ ДЕЙСТВИЙ:',
      'aiHelper.confirm.action': 'AI Helper хочет создать ДЕЙСТВИЕ:',
      // === Wyszukiwanie w kodzie (od v2.13.130) ===
      'codeSearch.placeholder': 'Поиск в коде… (Ctrl+Alt+F)',
      'codeSearch.btn': 'Искать',
      'codeSearch.prev': 'Предыдущее совпадение (Ctrl+Alt+←)',
      'codeSearch.next': 'Следующее совпадение (Ctrl+Alt+→)',
      'codeSearch.noMatch': 'Ничего не найдено',
    },
    zh: {
      'dashboard.title': '仪表板',
      'dashboard.subtitle': '您的 JustZix 库概览和快捷方式。',
      'dashboard.stats.title': '库',
      'dashboard.stats.folders': '文件夹',
      'dashboard.stats.groups': '分组',
      'dashboard.stats.rules': '规则集',
      'dashboard.stats.actions': '操作',
      'dashboard.stats.bars': '操作栏',
      'dashboard.stats.windows': '页内窗口',
      'dashboard.stats.withCss': '含 CSS 的规则集',
      'dashboard.stats.withJs': '含 JS 的规则集',
      'dashboard.stats.clicks': '操作点击次数',
      'dashboard.version.title': '扩展版本',
      'dashboard.version.checking': '检查中…',
      'dashboard.version.current': '已安装::version',
      'dashboard.version.upToDate': '已是最新版本',
      'dashboard.version.outdated': '有新版本可用::version',
      'dashboard.storage.title': '存储',
      'dashboard.backup.title': '上次备份',
      'dashboard.backup.signIn': '登录云同步以创建云备份。',
      'dashboard.backup.none': '尚无云备份。',
      'dashboard.backup.noLabel': '(无标签)',
      'dashboard.backup.last': '上次备份::date',
      'dashboard.recent.title': '最近编辑的规则集',
      'dashboard.recent.empty': '尚未编辑任何规则集。',
      'dashboard.examples.title': '最新示例',
      'dashboard.posts.title': '来自博客',
      'dashboard.feed.loading': '加载中…',
      'dashboard.feed.empty': '暂无内容。',
      'dashboard.home.title': '仪表板',
      'dashboard.usage.title': '使用统计',
      'dashboard.usage.cssApplied': 'CSS 应用次数',
      'dashboard.usage.jsExecuted': 'JS 执行次数',
      'dashboard.usage.windowsShown': '窗口显示次数',
      'dashboard.usage.empty': '暂无数据 — 请在网页上使用扩展。',
      'dashboard.tip.title': '提示',
      'dashboard.tip.prev': '上一条提示',
      'dashboard.tip.next': '下一条提示',
      'dashboard.tip.manual': '打开完整用户手册',
      'tip.dash.1': '右键点击悬浮按钮可打开包含文件夹和规则集列表的面板。',
      'tip.dash.2': 'Ctrl+Alt+S 可切换操作栏编辑模式 — 元素可自由定位和调整大小。',
      'tip.dash.3': '每个规则集都可以拥有自己的 URL 模式,将其限定到特定的子页面。',
      'tip.dash.4': 'Ctrl+Alt+\ 可打开 AI 助手 — 一个为特定任务生成 CSS/JS 的助手。',
      'tip.dash.5': '代码编辑器带有搜索栏 — Ctrl+Alt+F 可将焦点移至该处。',
      'tip.dash.6': '左键点击悬浮按钮可一次性启用或禁用所有匹配的文件夹。',
      'tip.dash.7': '规则集会在历史记录中保留最近 5 个 CSS 和 JS 版本 — 您始终可以撤销更改。',
      'tip.dash.8': '将规则集拖到另一个分组或文件夹名称上即可将其移动到那里。',
      'tip.dash.9': 'TEMP 窗口 (Ctrl+Alt+G/H/J/K) 是用于快速测试的一次性 CSS/JS 面板和控制台。',
      'tip.dash.10': 'SELECT、INPUT、SLIDER 和 TOGGLE3 操作会记住其值 — 页面重新加载后依然保留。',
      'tip.dash.11': '云同步可使您的库在多台设备间保持一致。',
      'tip.dash.12': '与其发送 JSON 文件,不如通过带有效期的链接(分享)来共享文件夹。',
      'tip.dash.13': '侧边栏的搜索框可按名称快速找到目录、分组或规则集。',
      'tip.dash.14': '为规则集添加标签,项目变多时可按标签筛选整个库。',
      'tip.dash.15': '三个字母的标签和颜色让目录的悬浮按钮在页面上更醒目。',
      'tip.dash.16': '“URL”按钮可将当前标签页的地址直接填入 URL 匹配规则,无需复制粘贴。',
      'tip.dash.17': 'JustZix 会实时校验你输入的 URL 匹配规则是否有效。',
      'tip.dash.18': '在保存规则集之前,编辑器会提示 CSS 语法错误。',
      'tip.dash.19': '导入时可先预览 JavaScript 代码,再决定是否添加。',
      'tip.dash.20': '停用某个目录会级联关闭其下所有分组和规则集。',
      'tip.dash.21': '库变大时,“仅活动”筛选器可隐藏已停用的项目。',
      'tip.dash.22': '将一个操作栏拖到另一个旁边,即可吸附拼接成一个整体。',
      'tip.dash.23': 'JS 控制台窗口就像一个 REPL,可在页面上实时测试代码片段。',
      'tip.dash.24': 'Output Console 窗口会捕获页面的 console.log 输出并直接显示出来。',
      'tip.dash.25': '你可以单独导出某个目录、分组或规则集,而不必导出整个库。',
      'tip.dash.26': 'JustZix 会在浏览器的右键菜单中添加自己的菜单项。',
      'tip.dash.27': '用鼠标拖动代码编辑器面板之间的分隔线,可按需调整视图。',
      'tip.dash.28': '有新版本可用时,扩展会主动通知你。',
      'tip.dash.29': '点击扩展图标会打开一个小部件,用于快速开关目录和操作栏。',
      'tip.dash.30': '规则集的 JavaScript 可通过 JustZix 内置 API 触发操作栏的动作。',
      'tip.dash.31': 'Ctrl+Enter 执行代码或发送消息 —— 适用于 JS 窗口、JS Console 和 AI Helper。',
      'tip.dash.32': '在 JS Console 中，↑ / ↓ 方向键可翻阅已输入命令的历史。',
      'tip.dash.33': 'Ctrl+L 清空 JS Console 的输出而不关闭窗口。',
      'tip.dash.34': 'Ctrl+Shift+L 清空每个已打开的 Output Console 的当前标签页。',
      'tip.dash.35': 'Ctrl+Shift+H 打开 Output Console 中的 API 帮助窗口。',
      'tip.dash.36': '在代码编辑器中，Ctrl+Alt+→ 和 Ctrl+Alt+← 跳到下一个和上一个搜索结果。',
      'tip.dash.37': 'Esc 键清空 Output Console 中的搜索框。',
      'tip.dash.38': '双击 TEMP 窗口的标题栏会在默认尺寸与上次尺寸之间切换。',
      'tip.dash.39': 'TEMP 窗口按每种类型记住上次尺寸，并在浏览器重启后保留。',
      'tip.dash.40': '在前端窗口内用滚轮滚动不再滚动下方的页面。',
      'tip.dash.41': '每个前端窗口有 8 个调整大小的句柄 —— 4 个在角上，4 个在边缘。',
      'tip.dash.42': '右键点击窗口标题会打开含关闭、断开连接、字体和大小选项的菜单。',
      'tip.dash.43': '把一个窗口拖到另一个附近 —— 它们会磁性吸附成一个组。',
      'tip.dash.44': 'Output Console 有 6 个标签页：全部、JZ、Console、Network、Errors 和 DataLayer。',
      'tip.dash.45': '在 Output Console 的 Network 标签页，Alt+点击某行会把它的域名加入包含过滤器。',
      'tip.dash.46': 'Alt+Shift+点击 Network 行会把域名加入排除过滤器。',
      'tip.dash.47': 'Output Console 的每个标签页都有自己独立的一套过滤器。',
      'tip.dash.48': '在 DataLayer 标签页，拖动列之间的分隔条可调整它们的宽度。',
      'tip.dash.49': '在操作栏编辑模式下，「+ 标签」按钮可添加静态文本标签。',
      'tip.dash.50': '双击操作栏标签会打开其文本、字体、大小和颜色的编辑器。',
      'tip.dash.51': '在编辑模式下，Shift+在空白区域拖动可用矩形框选元素。',
      'tip.dash.52': '在编辑模式下，Ctrl+点击可把元素加入或移出选择。',
      'tip.dash.53': '点击操作元素的数字徽章可把它的稳定 ID 复制到剪贴板。',
      'tip.dash.54': '规则集可设置为在 SPA 类页面的 UI 变化时重新执行其 JS。',
      'tip.dash.55': '除了 BUTTON、SELECT 和 INPUT，操作还有 SLIDER、TEXTAREA 和 TOGGLE3 类型。',
      'tip.dash.56': '明亮/暗黑主题与界面语言作为共享的账户设置同步。',
      'tip.dash.57': '仪表板上的使用统计会汇总所有已配对设备的数据。',
      'tip.dash.58': '代码编辑器下方的横条可向下拖动以扩大工作区域。',
      'tip.dash.59': '侧边栏与编辑器之间的竖条可折叠左侧面板以获得更多空间。',
      'tip.dash.60': '仪表板标题和面包屑中的主页图标可直接返回仪表板。',
      // === Popup ===
      'popup.toggle.on.label':       '插件已启用',
      'popup.toggle.on.status':      '注入运行中',
      'popup.toggle.off.label':      '插件已禁用',
      'popup.toggle.off.status':     '未注入任何 CSS/JS',
      'popup.editMode.label':        '操作栏编辑模式',
      'popup.editMode.on':           '已开启 — 点击关闭 (Ctrl+Alt+S)',
      'popup.editMode.off':          '已关闭 — 点击开启 (Ctrl+Alt+S)',
      'popup.editMode.unavailable':  '此页面不可用',
      'popup.empty.title':           '没有与此页面匹配的规则文件夹。',
      'popup.empty.hint':            '在编辑模式下添加它们。',
      'popup.empty.folderRules':     '此文件夹中没有活动规则',
      'popup.fallback.groupName':    '分组',
      'popup.fallback.unnamed':      '(未命名)',
      'popup.restore':               '恢复悬浮按钮 (:count)',
      'popup.editBtn':               '编辑面板',
      'popup.actionBarsTitle':       '操作栏',
      'popup.fallback.barName':      '操作栏',
      'popup.hideForPage':           '在此页面隐藏悬浮按钮',
      'popup.showForPage':           '在此页面显示悬浮按钮',

      // === Options — page ===
      'options.title':                          'JustZix — 编辑器',

      // === Options — sidebar ===
      'sidebar.folders.title':                  '文件夹',
      'sidebar.addFolder.label':                '添加文件夹',
      'sidebar.search.placeholder':             '搜索(名称、标签、代码内容)',
      'sidebar.clearSearch.label':              '清除',
      'sidebar.activeOnly.label':               '仅活动',
      'sidebar.activeOnly.title':               '隐藏独立规则集(标记为 ○)— 仅显示在其文件夹中活动的规则(●)',
      'sidebar.expandAll.label':                '展开整个树',
      'sidebar.collapseAll.label':              '折叠整个树',
      'sidebar.toggle.title':                   '折叠/展开文件夹面板',

      // === Options — settings menu ===
      'settings.menu.label':                    '设置',
      'settings.theme.label':                   '主题',
      'settings.theme.title':                   '选择主题：浅色 / 深色 / 自动',
      'settings.help.label':                    '用户手册',
      'settings.tutorial.label':                '教程',
      'settings.export.label':                  '导出设置',
      'settings.import.label':                  '导入设置',
      'settings.importUrl.label':               '从 URL 导入',
      'settings.importUrl.title':               '从 JustZix 链接导入导出文件 (JZS-XXXX-XXXX-XXXX-XXXX)',
      'settings.sync.label':                    '云同步',
      'settings.shares.label':                  '我的分享',
      'settings.shares.title':                  '已发布的限时公开导出链接列表',
      'settings.projectSite.label':             '项目网站',
      'settings.projectSite.title':             '在新标签页中打开 justzix.com',
      'settings.lang.label':                    '语言',
      'settings.lang.title':                    '扩展界面语言',
      'settings.lang.auto':                     '自动',
      'settings.lang.autoTitle':                '根据浏览器检测(默认)',
      'toast.langChanged':                      '语言已设置::lang',
      'lang.name.auto':                         '自动',
      'lang.name.pl':                           '波兰语',
      'lang.name.en':                           '英语',
      'settings.version.label':                 '版本',
      'settings.version.checkTitle':            '点击检查最新版本(强制刷新,绕过 30 分钟缓存)',
      'settings.version.checking':              '检查中…',
      'settings.update.available':              '有新版本可用：v:version ⬇',
      'settings.update.notReady':               '(安装包尚未发布)',
      'settings.syncStatusDot.title':           '云同步 — 未登录',

      // === Options — folder editor ===
      'folder.icon.title':                      '悬浮按钮标签(最多 3 个字母,大写)。默认:ZIX',
      'folder.color.title':                     '文件夹颜色(可选)',
      'folder.colorClear.title':                '清除颜色',
      'folder.name.placeholder':                '文件夹名称(例如 justzix.com)',
      'folder.url.placeholder':                 'URL 模式(例如 https://justzix.com/* )',
      'folder.urls.label':                      '文件夹 URL 模式:',
      'folder.urls.title':                      '文件夹 URL 模式 — 至少需匹配一个,此文件夹中的任何规则才会运行',
      'folder.urls.placeholder':                'https://justzix.com/*  (按 Enter 添加)',
      'folder.insertUrl.title':                 '插入当前标签页 URL',
      'folder.active.label':                    '激活',
      'folder.active.title':                    '激活 — 取消勾选时,该文件夹(及其所有分组/规则)将被强制禁用,开关被锁定',
      'folder.enabled.label':                   '已启用',
      'folder.enabled.title':                   '已启用 — 文件夹的当前状态(向下级联到活动分组和规则)',
      'folder.actionsPanel.label':              '操作面板',
      'folder.actionsPanel.title':              '显示此文件夹的操作按钮悬浮面板',
      'folder.layout.label':                    '布局:',
      'folder.layout.title':                    '操作面板按钮布局',
      'folder.layout.vertical':                 '垂直',
      'folder.layout.horizontal':               '水平',
      'folder.addGroup.label':                  '+ 分组',
      'folder.addGroup.title':                  '在此文件夹中添加新分组',
      'folder.export.label':                    '导出',
      'folder.export.title':                    '导出此文件夹',
      'folder.import.label':                    '导入',
      'folder.import.title':                    '从文件导入文件夹',
      'folder.delete.label':                    '删除文件夹',

      // === Options — group editor ===
      'group.parentLabel':                      '所属文件夹的分组:',
      'folder.label':                           '文件夹:',
      'group.label':                            '分组:',
      'group.name.placeholder':                 '分组名称',
      'group.urls.label':                       '分组 URL 模式:',
      'group.urls.title':                       '附加分组 URL 模式 — 在文件夹模式之上叠加的额外限制,适用于分组中的所有规则',
      'group.urls.placeholder':                 'https://example.com/admin/*  (Tab = 从文件夹自动补全,Enter = 添加)',
      'group.active.label':                     '激活',
      'group.active.title':                     '激活 — 取消勾选时,该分组(及其所有规则)将被强制禁用,开关被锁定',
      'group.enabled.label':                    '已启用',
      'group.enabled.title':                    '已启用 — 分组的当前状态(向下级联到活动规则)',
      'group.addRule.label':                    '+ 添加规则集',
      'group.export.title':                     '导出此分组',
      'group.import.title':                     '将规则集导入此分组',
      'group.move.label':                       '移动',
      'group.move.title':                       '将分组移动到其他文件夹',
      'group.duplicate.label':                  '复制',
      'group.duplicate.title':                  '复制分组及其规则集',
      'group.delete.label':                     '删除分组',

      // === Options — rule editor ===
      'rule.label':                             '规则集:',
      'rule.back.label':                        '返回分组',
      'rule.export.title':                      '将此规则集导出到文件',
      'rule.share.label':                       '分享 URL',
      'rule.share.title':                       '将此规则集分享为限时公开链接',
      'rule.import.title':                      '导入规则集(将替换当前规则集)',

      // === Options — empty state ===
      'empty.title':                            '选择或添加文件夹',
      'empty.body':                             '每个文件夹按一个 URL 模式对 CSS/JS 规则集进行分组。',

      // === Default names for newly created entities (options.js) ===
      'default.groupName':                      '默认',
      'default.folderName':                     '新建文件夹',
      'default.ruleName':                       '新建规则集',

      // === Plural noun forms used by toasts ===
      // EN has 2 forms (one/other), PL has 4 — both filled, Intl.PluralRules picks correctly.
      'plural.rule.one':                        '个规则集',
      'plural.rule.few':                        '个规则集',
      'plural.rule.many':                       '个规则集',
      'plural.rule.other':                      '个规则集',
      'plural.folder.one':                      '个文件夹',
      'plural.folder.few':                      '个文件夹',
      'plural.folder.many':                     '个文件夹',
      'plural.folder.other':                    '个文件夹',
      'plural.group.one':                       '个分组',
      'plural.group.few':                       '个分组',
      'plural.group.many':                      '个分组',
      'plural.group.other':                     '个分组',
      'plural.action.one':                      '个操作',
      'plural.action.few':                      '个操作',
      'plural.action.many':                     '个操作',
      'plural.action.other':                    '个操作',
      'plural.jsLine.one':                      '行 JS',
      'plural.jsLine.few':                      '行 JS',
      'plural.jsLine.many':                     '行 JS',
      'plural.jsLine.other':                    '行 JS',
      'toast.importedRulesToGroup':             '已将 :count :word 导入到分组',
      'toast.exportedRules':                    '已导出 :count :word',

      // === Sidebar — dynamic render ===
      'sidebar.empty.folders':                  '无文件夹',
      'sidebar.empty.activeFolders':            '无活动文件夹',
      'sidebar.folder.toggleHint':              '启用/禁用整个文件夹',
      'sidebar.folder.inactiveHint':            '文件夹未激活 — 在文件夹编辑器中解锁',
      'sidebar.group.toggleHint':               '启用/禁用分组',
      'sidebar.group.inactiveHint':             '分组未激活 — 在分组编辑器中解锁',
      'sidebar.rule.urlHint':                   '规则集级别的 URL 限制',
      'sidebar.rule.activeInFolder':            '在文件夹中活动',
      'sidebar.rule.inactiveInFolder':          '在文件夹中非活动',
      'sidebar.fallback.folderName':            '(文件夹)',
      'sidebar.fallback.groupName':             '(分组)',
      'sidebar.fallback.unnamed':               '(无名称)',
      'sidebar.flag.css':                       '包含 CSS',
      'sidebar.flag.js':                        '包含 JavaScript',
      'sidebar.flag.actions':                   '包含操作',
      'sidebar.flag.groupUrls':                 '分组有 URL 限制',

      // === Folder/Group/Rule editor — dynamic ===
      'rules.empty.folder':                     '此文件夹中没有规则集。请添加一个分组并在其中创建第一个规则集,或在侧栏中点击某个分组。',
      'rules.empty.group':                      '此分组中没有规则集。点击"+ 添加规则集"添加第一个。',
      'rule.namePlaceholder':                   '规则集名称',
      'rule.activeTooltip':                     '激活 — 在切换整个文件夹时是否包含此规则',
      'rule.urls.tooltip':                      '附加 URL 模式 — 设置后,该规则仅在匹配至少一个模式的子页面上触发(在文件夹模式之外)',
      'rule.urls.placeholder.empty':            'https://example.com/admin/*  (Tab — 插入文件夹模式,Enter — 添加)',

      // === Move menu ===
      'moveMenu.section.moveGroup':             '将分组移动到',
      'moveMenu.section.moveRule':              '移动到分组',
      'moveMenu.section.copyRule':              '复制到分组',
      'moveMenu.empty.folders':                 '没有其他文件夹',
      'moveMenu.empty.groups':                  '没有其他分组',

      // === Confirm dialogs — dynamic ===
      'confirm.deleteFolderWithStats':          '删除文件夹 ":name" 以及 :groups 和 :rules?',
      'confirm.deleteFolder':                   '删除文件夹 ":name"?',
      'confirm.deleteGroupWithRules':           '删除分组 ":name" 以及 :count :word?',
      'confirm.deleteGroup':                    '删除分组 ":name"?',
      'confirm.replacePartial':                 '用部分备份(:after)替换当前的 :before?\n\n文件之外的所有数据将丢失。此操作不可撤销。',
      'confirm.overwriteAll':                   '用文件中的当前数据覆盖 :stats?\n\n此操作不可撤销。',

      // === Default name for newly created group ===
      'default.newGroupName':                   '新建分组',

      // === Export — summary + states ===
      'export.summary.full':                    ':icon 完整备份::stats + 全局设置',
      'export.summary.partial':                 ':icon 部分备份::stats',
      'export.empty.folder':                    '没有可导出的文件夹',
      'export.empty.group':                     '分组为空 — 没有可导出的规则集',
      'export.summary.toExport':                ':icon 待导出::count :word',
      'export.summary.noneSelected':            ':icon 未选择规则集',

      // === Import detection (banners) ===
      'import.detection.invalid':               '<strong style="color:#dc2626;">:icon 格式无效:</strong> 缺少文件夹列表 (<code>folders</code>)。',
      'import.detection.partialContains':       '包含::stats',
      'import.detection.willReplace':           ':stats',

      // === Inline labels ===
      'inline.fileGroupLabel':                  '分组',
      'shares.empty':                           '您没有活动的链接。<br><br>要创建链接 — 打开一个导出文件(完整 / 文件夹 / 分组 / 规则集)<br>并点击 <strong>"分享 URL"</strong>。',

      // === Share preview / export types ===
      'shareType.folder':                       '单个文件夹',
      'shareType.group':                        '单个分组',
      'shareType.rule':                         '单个规则集',
      'shareType.full':                         '完整导出',

      // === Owner suffix when importing from share ===
      'shareImport.suffix.group':               '分组',
      'shareImport.suffix.rule':                '规则集',

      // === Rule import — prompt with options ===
      'prompt.ruleConflict':                    '您正在导入 ":new"。对当前规则集 ":current" 如何处理?\n\n  1 或 "replace"  — 替换当前规则集\n  2 或 "copy"     — 在当前规则集旁添加为新规则集(在同一分组中)\n\n请输入 1 或 2:',
      'prompt.folderConflicts':                 '检测到 :count :word 与现有项 ID 相同:\n:names\n\n请输入以下选项之一:\n  1 或 "overwrite"  — 覆盖现有文件夹(删除其当前分组和规则集)\n  2 或 "copy"       — 导入为副本(新 ID,文件夹名称带 "(import)")\n  Cancel            — 中止导入\n\n选择:',

      // === Group editor — additional ===
      'group.empty.noGroupsInFolder':           '文件夹中没有分组',

      // === Folder import — conflict messages ===
      'folderImport.conflict.sameId':           '导入的文件夹与现有 "<strong>:name</strong>" (:count :word) ID 相同。请选择操作:',
      'folderImport.conflict.sameName':         '名为 "<strong>:name</strong>" 的文件夹已存在 (:count :word)。请选择操作:',

      // === Dynamic button states ===
      'btn.shortcutPress':                      '⌨ 按下 Ctrl+Alt+Shift + 按键 (Esc)',
      'btn.generating':                         '生成中…',
      'btn.importing':                          '导入中…',
      'btn.showKey':                            '显示密钥',
      'btn.hideKey':                            '隐藏密钥',

      // === Rule editor — placeholders + tooltips ===
      'rule.editor.urlsPlaceholder':            'https://example.com/admin/*  (Tab — 插入文件夹模式,Enter — 添加)',
      'rule.editor.urlsPlaceholderWithPattern': ':pattern  (Tab — 插入,Enter — 添加)',
      'rule.editor.toggleHint':                 '启用/禁用此规则集',
      'rule.editor.toggleLockedHint':           '规则集未激活 — 勾选"激活"以解锁',
      'sidebar.fallback.ruleName':              '(规则集)',
      'sidebar.fallback.ruleUnnamed':           '(未命名规则集)',
      'confirm.deleteRule':                     '删除规则集 ":name"?',

      // === Rule card (rendered by createRuleCard / renderActionsList) ===
      'rule.card.tagsTitle':                    '标签(以逗号分隔)',
      'rule.card.activeLabel':                  '激活',
      'rule.card.enabledLabel':                 '已启用',
      'rule.card.history.title':                '更改历史',
      'rule.card.history.label':                '历史',
      'rule.card.move.label':                   '移动',
      'rule.card.move.btnTitle':                '将规则集移动到其他分组',
      'rule.card.copy.label':                   '复制',
      'rule.card.copy.btnTitle':                '将规则集复制到其他分组',
      'rule.card.duplicate.label':              '副本',
      'rule.card.delete.label':                 '删除',
      'rule.card.urlsLabel':                    '规则集 URL 模式:',
      'rule.card.tab.actions':                  '操作',
      'rule.card.lineCount':                    '代码行数',
      'rule.card.js.runOnce.label':             '仅执行一次 JS',
      'rule.card.js.runOnce.title':             'SPA 导航后(URL 改变但不重新加载页面)不再重复执行 JS。建议用于添加全局监听器、定时器或修改 window 的脚本。',
      'rule.card.actions.info':                 '操作按钮显示在悬浮面板中(文件夹中的"操作面板"开关)。快捷方式最多 4 个字符,可自定义颜色,JS 代码在点击时运行。',
      'rule.card.actions.add':                  '+ 添加操作按钮',
      'rule.card.actions.addWithCount':         '+ 添加操作按钮 (:count)',
      'rule.card.action.visibleLabel':          '可见',
      'rule.card.duplicate.suffix':             '(副本)',
      'rule.card.action.codePreview':           '(:count :word) :first',
      'rule.card.action.fallback.unlabeled':    '(无标签)',
      'rule.card.fallbackName':                 '规则集',

      // === Folder export modal — additional ===
      'export.empty.foldersToExport':           '没有可导出的文件夹',
      'export.summary.toExportFolders':         ':icon 待导出::groupCount :groupWord、:ruleCount :ruleWord',
      'export.summary.invalidFolderFile':       '文件格式无效 — 预期为单个文件夹导出。',
      'export.summary.noConflicts':             ':icon 无冲突。文件夹 "<strong>:name</strong>"(含 :count :word)将作为新文件夹导入。',
      'export.summary.fileContains':            '<strong style="color:#374151;">:icon 文件内容 (:count :word):</strong>',
      'export.summary.jsHeader':                ':icon 规则集 JavaScript:',
      'export.summary.emptyRule':               '空规则集',
      'toast.exportedFolder':                   '已导出文件夹 (:groupCount :groupWord、:ruleCount :ruleWord)',
      'inline.fileFolderLabel':                 '文件夹',
      'inline.fileRuleLabel':                   '规则集',
      'default.newGroupNameInline':             '新建分组',

      // === Sync conflict modal counts ===
      'syncConflict.countLine':                 ':folders :foldersWord / :groups :groupsWord / :rules :rulesWord',

      // === Backup restore confirm ===
      'confirm.restoreBackup':                  '恢复备份 #:id?\n\n当前所有文件夹/分组/规则集将被备份中的内容替换。其他设备将在下次拉取时同步。\n\n不可撤销(恢复前会创建一个新备份除外)。',

      // === Loaders / placeholders ===
      'loading.shares':                         '加载中…',
      'loading.generic':                        '加载中…',

      // === Sidebar — drag & arrows tooltips ===
      'sidebar.dragHandle.title':               '拖动以重新排序',
      'sidebar.expandCollapse.title':           '展开/折叠',
      'sidebar.folderActive.title':             '激活',
      'sidebar.folderInactive.title':           '未激活 — 已强制禁用',
      'sidebar.groupActive.title':              '激活',
      'sidebar.groupInactive.title':            '未激活 — 已强制禁用',
      'sidebar.searchNoResults':                '没有 ":query" 的结果',

      // === Theme ===
      'settings.theme.toggleToDark':            '切换到深色模式',
      'settings.theme.toggleToLight':           '切换到浅色模式',
      'settings.theme.labelDark':               '深色模式',
      'settings.theme.labelLight':              '浅色模式',
      'settings.theme.labelAuto':               '自动（系统）',
      'settings.theme.toggleToAuto':            '切换到自动模式',

      // === URL chip remove ===
      'btn.removeUrlChip':                      '移除',

      // === CSS validator ===
      'cssValidator.unclosedComment':           '未闭合的注释 /* ... */',
      'cssValidator.missingCloseBraces':        '括号未闭合:缺少 :count :word }',
      'plural.brace.one':                       '个右花括号',
      'plural.brace.few':                       '个右花括号',
      'plural.brace.many':                      '个右花括号',
      'plural.brace.other':                     '个右花括号',
      'cssValidator.extraCloseBrace':           '多余的右花括号 }(第 :line 行)',
      'jsValidator.unterminatedString':         '未结束的字符串字面量（第 :line 行）',
      'jsValidator.unclosedBracket':            '未闭合的括号 :bracket（第 :line 行）',
      'jsValidator.extraBracket':               '多余的右括号 :bracket（第 :line 行）',

      // === Action buttons ===
      'action.shortcut.empty':                  '点击并按下快捷键',
      'action.code.empty':                      '点击以添加代码…',
      'action.stats.title':                     '操作面板中的点击次数',
      'action.visible.title':                   '在操作面板中可见(取消勾选 = 隐藏,但不移除)',
      'action.shortcut.title':                  '键盘快捷键 — 点击并按下组合键。Esc 取消。无论面板是否可见,快捷键都会在匹配的页面上生效。',
      'action.shortcut.titleAfterClick':        '键盘快捷键。点击并按下组合键以更改。Esc 取消。× 移除。',
      'action.shortcut.removeBtn':              '移除键盘快捷键',
      'action.delete.btn':                      '删除操作按钮',
      'action.menu.btn.duplicate':              '在此处复制操作',
      'action.menu.btn.move':                   '将操作移动到其他规则集',
      'action.menu.btn.copy':                   '将操作复制到其他规则集',
      'action.menu.section.moveTo':             '移动到规则集...',
      'action.menu.section.copyTo':             '复制到规则集...',
      'action.menu.empty.rules':                '没有其他规则集',
      'action.menu.toast.moved':                '操作已移动到 ":target"',
      'action.menu.toast.copied':               '操作已复制到 ":target"',
      'action.shortcut.conflictTitle':          '冲突:此快捷键还被 :count :word 使用:\n:list\n\n按下时,只有渲染顺序中的第一个会触发。',
      'action.shortcut.conflictItem':           '• ":rule" 中的 ":action" (:folder)',
      'plural.actionAlt.one':                   '个其他操作',
      'plural.actionAlt.few':                   '个其他操作',
      'plural.actionAlt.many':                  '个其他操作',
      'plural.actionAlt.other':                 '个其他操作',
      'plural.more.one':                        '个其他',
      'plural.more.few':                        '个其他',
      'plural.more.many':                       '个其他',
      'plural.more.other':                      '个其他',
      'action.shortcut.conflictMore':           '(以及另外 :count :word)',
      'action.shortcut.conflictToast':          '冲突:此快捷键已被 ":ruleName" 中的操作 ":actionLabel" 使用:more。它们中只有一个会触发。',
      'confirm.deleteAction':                   '删除操作按钮 ":label"?',

      // === History ===
      'history.header':                         '最近 :count 个已保存版本。点击"恢复"以还原到该版本(当前版本将保存到历史记录)。',
      'history.modalTitle':                     '更改历史:":name"',
      'history.version':                        '版本 :n',
      'history.restore':                        '恢复',
      'history.close':                          '关闭',

      // === Export — additional ===
      'export.summary.noElements':              ':icon 未选择要导出的元素',
      'export.summary.toExportFolderShort':     ':icon 待导出::groupCount :groupWord、:ruleCount :ruleWord',
      'export.summary.noneSelectedShort':       ':icon 未选择规则集',

      // === Import detection ===
      'import.detection.parseError':            '<strong style="color:#dc2626;">:icon JSON 解析错误:</strong> :msg',
      'import.detection.partialBackup':         '<strong style="color:#5b21b6;">:icon 部分备份(选定的元素)</strong><br>',
      'import.detection.fullBackup':            '<strong style="color:#006870;">:icon 完整设置备份:legacy</strong><br>',
      'import.detection.legacySuffix':          ' (旧格式)',
      'import.detection.willOverwrite':         '<br><br><strong style="color:#b91c1c;">:icon 当前设置将被覆盖:</strong>',
      'import.detection.exportFirst':           '<br><span style="color:#7f1d1d;font-size:11px;">建议:导入<strong>之前</strong>先导出。</span>',
      'import.detection.folder':                '<strong style="color:#155e75;">:icon 文件夹导出</strong> "<strong>:name</strong>"<br><span style="color:#155e75;">包含::stats</span>',
      'import.detection.group':                 '<strong style="color:#155e75;">:icon 分组导出</strong> "<strong>:name</strong>"<br><span style="color:#155e75;">包含::stats</span>',
      'import.detection.rule':                  '<strong style="color:#155e75;">:icon 规则集导出</strong> "<strong>:name</strong>"',

      // === Import — target selectors ===
      'import.target.folder.title':             '将导入的文件夹放在哪里?',
      'import.target.folder.newCopy':           '添加为新的顶级文件夹',
      'import.target.folder.overwrite':         '覆盖现有文件夹 ":name"(保留 ID — 同步时视为一次编辑)',
      'import.target.folder.mergeInto':         '将分组/规则集合并到现有文件夹:',
      'import.target.group.title':              '将导入的分组放在哪里?',
      'import.target.group.existingFolder':     '添加到现有文件夹:',
      'import.target.group.newFolder':          '为此分组创建新文件夹',
      'import.target.rule.title':               '将导入的规则集放在哪里?',
      'import.target.rule.existingGroup':       '添加到现有分组:',
      'import.target.rule.newFolder':           '为此规则集创建新文件夹和分组',

      // === Share errors (create flow) ===
      'share.error.unknown':                    '未知错误',
      'share.error.shareFailed':                '您已有 20 个活动链接或超出 50 MB 限制。请在"我的分享"中撤销旧链接。',
      'share.error.badRequest':                 '生成链接失败 — 请检查导出文件是否有效。:msg',
      'share.error.rateLimited':                '过去一小时创建的链接过多(上限:30)。请稍后再试。',
      'share.error.invalidToken':               '会话已过期 — 请在同步面板中重新登录后再试一次。',
      'share.error.serverError':                '服务器出现问题。请稍后再试。',
      'share.error.fallback':                   '错误::msg',
      'share.error.tooLarge':                   '导出文件过大 — 后端最多允许 5 MB。请尝试分享较小的部分。',

      // === Share subject (create modal) ===
      'share.subject.partial':                  '部分库导出',
      'share.subject.full':                     '完整库导出',

      // === Time relative formatting (fmtRelHuman) ===
      'rel.lessThanMinute':                     '不到一分钟',
      'rel.min':                                ':n 分钟',
      'rel.hMin':                               ':h 小时 :m 分钟',
      'rel.h':                                  ':h 小时',
      'rel.dH':                                 ':d 天 :h 小时',
      'rel.d':                                  ':d 天',

      // === Shares list ===
      'shares.expired':                         '已过期',
      'shares.fetchFailed':                     '获取列表失败::msg',
      'shares.confirmRevoke':                   '撤销分享 #:id?\n\n该链接将立即停止工作 — 接收者下次尝试获取时会收到 404。\n\n注意:如果有人已下载内容,他们将拥有本地副本 — 撤销无法收回已下载的内容。',
      'shares.revoked':                         '分享 #:id 已撤销',

      // === Share preview details ===
      'sharePreview.senderLabel':               '发送者:',
      'sharePreview.senderAnonymous':           '匿名',
      'sharePreview.expiresLabel':              '过期时间:',
      'sharePreview.expiresValue':              ':rel 后',
      'sharePreview.typeLabel':                 '类型:',
      'sharePreview.sizeLabel':                 '大小:',
      'sharePreview.contentLabel':              '内容:',
      'sharePreview.cssLabel':                  'CSS:',
      'sharePreview.cssValueChars':             ':n 个字符',
      'sharePreview.jsLabel':                   'JavaScript:',
      'sharePreview.downloadsLabel':            '下载次数:',
      'sharePreview.codePreviewTitle':          '来自共享导出的代码预览',

      // === Share import errors (preview) ===
      'shareImport.error.unknown':              '未知错误',
      'shareImport.error.badRequest':           '令牌无效。请检查您是否复制了完整的链接。',
      'shareImport.error.notFound':             '此链接已过期或被发送者撤销。请索取新链接。',
      'shareImport.error.rateLimited':          '尝试次数过多。请稍候片刻后重试。',
      'shareImport.error.serverError':          '服务器出现问题。请重试。',
      'shareImport.error.fallback':             '错误::msg',
      'shareImport.error.invalidTokenFormat':   '令牌格式无效',

      // === Rule import ===
      'ruleImport.unknownFormat':               '未知或不支持的导出格式::type',
      'prompt.ruleConflict.body':               '您正在导入 ":new"。对当前规则集 ":current" 如何处理?\n\n  1 或 "replace"  — 用文件内容替换当前规则集\n  2 或 "copy"     — 在当前规则集旁添加为新规则集(在同一分组中)\n  Cancel          — 中止导入\n\n选择:',

      // === Folder export ===
      'folderExport.exportedToast':             '已导出文件夹 (:groupCount :groupWord、:ruleCount :ruleWord)',

      // === Folder import — details ===
      'folderImport.parseError':                'JSON 解析错误::msg',
      'folderImport.invalidFormat':             '文件格式无效 — 预期为单个文件夹导出。',
      'folderImport.noConflicts':               ':icon 无冲突。文件夹 "<strong>:name</strong>"(含 :count :word)将作为新文件夹导入。',
      'folderImport.conflictSameId':            '导入的文件夹与现有 "<strong>:name</strong>" (:count :word) ID 相同。请选择操作:',
      'folderImport.conflictSameName':          '名为 "<strong>:name</strong>" 的文件夹已存在 (:count :word)。请选择操作:',
      'folderImport.fileContents':              '<strong style="color:#374151;">:icon 文件内容 (:count :word):</strong>',
      'folderImport.jsHeader':                  ':icon 规则集 JavaScript:',
      'folderImport.codePreviewTitle':          '代码预览::name',
      'folderImport.previewJsBtn.title':        '预览 JS 代码',

      // === Sync — toasts + status messages ===
      'sync.toast.synced':                      '已同步::applied 项更改,:deletes 项删除',
      'sync.rel.justNow':                       '刚刚',
      'sync.tooltip.syncing':                   '同步中…',
      'sync.tooltip.error':                     '同步:错误。上次成功::last',
      'sync.tooltip.connected':                 '云同步 — 已连接。上次::last',
      'sync.error.copyManually':                '复制失败 — 请手动选中并按 Ctrl+C。',
      'sync.error.registerFailed':              '注册错误::msg',
      'sync.error.unknown':                     '未知',
      'sync.error.invalidKeyFormat':            '密钥格式无效 — 预期为 SYNC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX。',
      'sync.status.sendingLocal':               '正在将 :count 个本地实体发送到账户。',
      'sync.status.cancelled':                  '登录已取消 — 已登出。本地数据未受影响。',
      'sync.status.sendingAndDeleting':         '正在发送 :local 个本地实体,从账户删除 :remote 个…',
      'sync.status.merged':                     '已合并。本地独有的(:count 项)将在下次同步时发送到账户。',
      'sync.error.firstSyncFailed':             '登录成功但首次同步失败::msg',
      'sync.error.loginFailed':                 '登录错误::msg',
      'sync.error.invalidEmail':                '请输入有效的电子邮件地址。',
      'sync.recovery.sent':                     '如果此邮箱已注册,我们已发送恢复链接。请检查您的收件箱。',
      'sync.error.generic':                     '错误::msg',
      'sync.account.thisDevice':                '此设备::label',
      'sync.session.noLabel':                   '(无标签)',
      'sync.session.thisDeviceSuffix':          ' (此设备)',
      'sync.session.revoke':                    '撤销',
      'sync.error.revokeFailed':                '撤销错误::msg',
      'sync.session.fetchFailed':               '获取会话列表失败::msg',
      'sync.key.copyManual':                    '复制失败 — 请点击"显示密钥"并手动按 Ctrl+C。',
      'sync.key.removed':                       '密钥已从本地存储中移除。',
      'sync.backups.empty':                     '没有云备份。点击"立即创建备份"创建第一个。',
      'sync.backups.fetchFailed':               '获取列表失败::msg',
      'sync.backups.creating':                  '正在创建云备份…',
      'sync.backups.createFailed':              '创建备份错误::msg',
      'sync.backups.restored':                  '已恢复备份 #:id。替换了 :count 个实体。newCursor=:cursor。',
      'sync.backups.restoreFailed':             '恢复错误::msg',
      'sync.backups.downloadFailed':            '下载错误::msg',
      'sync.backups.confirmDelete':             '从云端删除备份 #:id?不可撤销。',
      'sync.backups.deleted':                   '备份 #:id 已删除。',
      'sync.backups.deleteFailed':              '删除错误::msg',
      'sync.pushPullSummary':                   '推送::pushedAccepted 项接受,:pushedRejected 项拒绝 • 拉取::applied 个实体,:deletes 项删除。',
      'sync.returned':                          '同步返回::result',
      'sync.snapshotFailed':                    '快照错误::msg',
      'sync.reconcile.running':                 '与账户完整重新同步…',
      'sync.reconcile.okPushed':                '重新同步完成 — 已向账户推送 :count 个缺失实体。',
      'sync.reconcile.okInSync':                '重新同步完成 — 库与账户一致。',
      'sync.reconcile.failed':                  '完整重新同步失败::msg',
      'sync.email.saveButFailed':               '邮箱已保存,但链接发送失败 — 请重试。',
      'sync.email.verifyLinkSent':              '验证链接已发送至 :email。请检查收件箱并点击链接。',
      'sync.email.removed':                     '邮箱已移除。',

      // === Sync result modal — strategies ===
      'syncResult.merge.title':                 '已与云端合并',
      'syncResult.merge.desc':                  '两处都存在的实体已被账户版本覆盖。本地独有的实体保留,并将在下次同步时发送到账户。',
      'syncResult.merge.tableHeader':           '<table class="result-table"><thead><tr><th>类型</th><th>来自云端:已覆盖</th><th>来自云端:新增</th><th>本地保留 → 推送</th><th>合并后总计</th></tr></thead><tbody>',
      'syncResult.useServer.title':             '已用云端数据替换本地',
      'syncResult.useServer.desc':              '本地库已被账户状态完全替换。本地独有的实体(如有)已丢失。',
      'syncResult.useServer.tableHeader':       '<table class="result-table"><thead><tr><th>类型</th><th>从云端获取</th><th>本地丢失</th><th>共有(已覆盖)</th><th>同步后总计</th></tr></thead><tbody>',
      'syncResult.sendMine.title':              '正在将本地发送到账户',
      'syncResult.sendMine.desc':               '本地库将被推送到账户。云端存在但本地缺失的实体 — 将在账户上标记为已删除(墓碑标记)。',
      'syncResult.sendMine.tableHeader':        '<table class="result-table"><thead><tr><th>类型</th><th>已发送(推送)</th><th>账户上的覆盖</th><th>账户上的新增</th><th>从账户删除</th></tr></thead><tbody>',
      'syncResult.fallback.title':              '同步完成',
      'syncResult.empty':                       '无详细信息。',
      'syncResult.label.folders':               '文件夹',
      'syncResult.label.groups':                '分组',
      'syncResult.label.rules':                 '规则集',
      'syncResult.label.actions':               '操作',

      // === Options — toasts (bottom notifications) ===
      'toast.saved':                            '已保存',
      'toast.cantDeleteLastGroup':              '无法删除文件夹中的最后一个分组',
      'toast.noOtherTab':                       '没有其他带页面 URL 的标签页',
      'toast.urlInserted':                      '已从标签页插入 URL',
      'toast.urlAlreadyAdded':                  '模式已在列表中::pattern',
      'toast.error':                            '错误::msg',
      'toast.shortcutMustIncludeMod':           '允许的快捷键:Ctrl+Alt+Shift + 字母(G H J K L 除外)、数字或符号(\\ 除外)。',
      'toast.shortcutCollidesChrome':           '警告:快捷键与 Chrome 快捷键冲突(例如新建标签页)。它可能无法工作 — 请选择其他快捷键。',
      'toast.copied':                           '已复制::text',
      'toast.copyFailed':                       '复制失败',
      'toast.copyClipboard':                    '已复制到剪贴板',
      'toast.copyLink':                         '链接已复制',
      'toast.copyToken':                        '令牌已复制',
      'toast.noHistory':                        '此规则集没有历史记录',
      'toast.versionRestored':                  '版本已恢复',
      'toast.fileNotFullImport':                '文件不适合完整导入',
      'toast.unknownOption':                    '未知选项 — 导入已取消',
      'toast.importedSettings':                 '设置已导入',
      'toast.parseError':                       '解析错误::msg',
      'toast.invalidPartialFormat':             '格式无效 — 预期为分组、文件夹或规则集导出',
      'toast.groupImport.folderRejected':       '这是文件夹导出(包含多个分组)。请通过文件夹级别的导入,而非分组级别。',
      'toast.exportedRule':                     '规则集已导出',
      'toast.signInFirst':                      '请先在同步面板中登录',
      'toast.exportNotReady':                   '导出尚未就绪',
      'toast.noRulesSelected':                  '未选择规则集',
      'toast.revokeError':                      '撤销错误::msg',
      'toast.importedFromLink':                 '已从链接导入',
      'toast.invalidRuleFormat':                '文件格式无效 — 预期为规则集导出',
      'toast.importedReplaced':                 '已导入(已替换)',
      'toast.importedAsCopy':                   '已导入为副本',
      'toast.noImportData':                     '没有可导入的数据',
      'toast.importedFolder':                   '文件夹已导入::name',
      'toast.importedGroup':                    '分组已导入::name',
      'toast.importedRule':                     '规则集已导入::name',

      // === Options — native confirm() dialogs ===
      'confirm.restoreVersion':                 '恢复此版本?当前内容将作为新快照添加到历史记录。',
      'confirm.revokeSession':                  '撤销此会话?',
      'confirm.removeKey':                      '从此设备移除密钥?\n\n登录仍然有效(会话独立于密钥),但您将无法从这台电脑查看或将密钥复制到另一台设备。\n\n请确保您在别处保存了密钥副本(密码管理器)。',
      'confirm.snapshot':                       '完整快照会从云端下载整个库并替换本地状态。您待处理的更改不会丢失(它们保留在队列中)。是否继续?',
      'confirm.removeEmail':                    '从账户移除邮箱?没有邮箱,您将无法在丢失密钥后恢复。',
      'confirm.signOut':                        '登出此设备?本地库会保留,但停止同步。',

      // === Modal — full export ===
      'modal.export.title':                     '导出设置',
      'modal.export.mode.all':                  '全部',
      'modal.export.mode.select':               '选定的文件夹/规则',
      'modal.export.selectAll':                 '全选',
      'modal.export.deselectAll':               '取消全选',
      'modal.export.hint':                      '您可以将 JSON 复制到剪贴板或下载文件:',
      'modal.export.close':                     '关闭',
      'modal.export.share':                     '分享 URL',
      'modal.export.share.title':               '将此导出分享为限时公开链接',
      'modal.export.download':                  '下载文件',
      'modal.export.copy':                      '复制到剪贴板',

      // === Modal — full import ===
      'modal.import.title':                     '导入设置',
      'modal.import.pickerHint':                '粘贴任何 JustZix 导出的 JSON(完整备份、文件夹、分组或规则集)— 表单将检测类型并提供放置选项。',
      'modal.import.warningStrong':             '警告:',
      'modal.import.warningRest':               ' 完整导入将覆盖所有当前设置。',
      'modal.import.placeholder':               '在此粘贴 JSON 或选择文件...',
      'modal.import.cancel':                    '取消',
      'modal.import.import':                    '导入',

      // === Modal — folder export ===
      'modal.folderExport.titlePrefix':         '导出文件夹:',
      'modal.folderExport.hint':                '选择要导出的分组/规则。',
      'modal.folderExport.cancel':              '取消',
      'modal.folderExport.share':               '分享 URL',
      'modal.folderExport.shareTitle':          '将此文件夹分享为限时公开链接',
      'modal.folderExport.download':            '下载文件',

      // === Modal — group export ===
      'modal.groupExport.titlePrefix':          '导出分组:',
      'modal.groupExport.hint':                 '选择要导出的规则集。',
      'modal.groupExport.cancel':               '取消',
      'modal.groupExport.share':                '分享 URL',
      'modal.groupExport.shareTitle':           '将此分组分享为限时公开链接',
      'modal.groupExport.download':             '下载文件',

      // === Modal — folder import ===
      'modal.folderImport.title':               '导入文件夹',
      'modal.folderImport.placeholder':         '粘贴文件夹 JSON 或选择文件...',
      'modal.folderImport.conflicts':           '检测到冲突:',
      'modal.folderImport.action.rename':       '导入为新文件夹(带 "(import)" 后缀)',
      'modal.folderImport.action.overwrite':    '覆盖现有文件夹',
      'modal.folderImport.action.merge':        '合并 — 将新规则集添加到现有文件夹(两者都保留)',
      'modal.folderImport.cancel':              '取消',
      'modal.folderImport.import':              '导入',

      // === Modal — action code ===
      // === Action bars (Phase 2 of multi-bars system, since v2.9) ===
      'actionBars.btn.label':                   '操作栏',
      'actionBars.btn.title':                   '管理分配给此文件夹的操作栏',
      'actionBars.btn.titleGroup':              '管理分配给此分组的操作栏',
      'actionBars.btn.titleRule':               '管理分配给此规则集的操作栏',
      'actionBars.parentType.folder':           '文件夹',
      'actionBars.parentType.group':            '分组',
      'actionBars.parentType.rule':             '规则集',
      'actionBars.parentType.folder.short':     '[F]',
      'actionBars.parentType.group.short':      '[G]',
      'actionBars.parentType.rule.short':       '[R]',
      'actionBars.empty':                       '此级别没有操作栏。点击"+ 添加新操作栏"创建第一个。',
      'actionBars.section.owned':               '此级别的操作栏',
      'actionBars.section.fromFolder':          '↑ 继承自文件夹 "<strong>:name</strong>"',
      'actionBars.section.fromGroup':           '↑ 继承自分组 "<strong>:name</strong>"',
      'actionBars.elementsCount':               '元素::count',
      'actionBars.enabled':                     '已启用',
      'actionBars.name.placeholder':            '名称',
      'actionBars.name.title':                  '操作栏边缘的标签(最多 6 个字符,大写)。留空 = 无标签。',
      'actionBars.color.title':                 '操作栏背景颜色',
      'actionBars.orientation.title':           '点击切换操作栏方向(垂直 ↔ 水平)',
      'actionBars.color.clear':                 '清除背景颜色',
      'actionBars.moveTo':                      '移动到:',
      'actionBars.delete':                      '删除',
      'actionBars.confirmDelete':               '删除此操作栏?',
      'actionBars.confirmDeleteWithElements':   '删除此操作栏?已分配的元素 (:count) 将保留其定义,但失去操作栏分配 — 您需要手动将它们分配到其他操作栏。',
      'actionBars.deleteMigrate.message':       '此操作栏有 :count 个已分配元素。请在删除前选择将它们移动到何处(或将其从任何操作栏分离):',
      'actionBars.deleteMigrate.detachOption':  '— 分离元素(无操作栏)—',
      'actionBars.deleteMigrate.confirm':       '移动并删除',
      'actionBars.deleteMigrate.cancel':        '取消',
      'modal.actionBars.titlePrefix':           '操作栏 —',
      'modal.actionBars.hint':                  '分配给此级别的操作栏。文件夹操作栏汇总文件夹中所有活动规则集的元素,分组操作栏 — 仅来自该分组,规则集操作栏 — 仅在规则集活动时。没有可见元素的操作栏将不会渲染。',
      'modal.actionBars.add':                   '+ 添加新操作栏',
      'modal.actionBars.close':                 '关闭',

      // === Windows — dropdown type selector (since v2.13.41) ===
      'windows.btn.label':                      '窗口',
      'windows.btn.title':                      '管理分配给此文件夹的窗口(CSS / JS / 控制台)',
      'windows.btn.titleGroup':                 '管理分配给此分组的窗口',
      'windows.btn.titleRule':                  '管理分配给此规则集的窗口',
      'windows.menu.title':                     '选择窗口类型:',
      'windows.menu.comingSoon':                '即将推出',
      'jsPanes.btn.label':                      'JS 窗格',
      'jsConsoles.btn.label':                   'JS 控制台',
      'outputConsoles.btn.label':               '输出控制台',

      // === CSS panes (Phase 2 "Windows on the front", since v2.13.40) ===
      'cssPanes.btn.label':                     'CSS 窗格',
      'cssPanes.contextMenu.disable':           '关闭 CSS 窗格',
      'cssPanes.btn.title':                     '管理分配给此文件夹的 CSS 窗格',
      'cssPanes.btn.titleGroup':                '管理分配给此分组的 CSS 窗格',
      'cssPanes.btn.titleRule':                 '管理分配给此规则集的 CSS 窗格',
      'cssPanes.empty':                         '此级别没有 CSS 窗格。点击"+ 添加新 CSS 窗格"创建第一个。',
      'cssPanes.section.owned':                 '此级别的窗格',
      'cssPanes.name.placeholder':              '窗格名称',
      'cssPanes.color.title':                   '标题圆点颜色',
      // Custom pane colors (since v2.13.58) — shared CSS / JS pane.
      'panes.style.dot':                        '圆点',
      'panes.style.headerBg':                   '标题背景',
      'panes.style.headerText':                 '标题文字',
      'panes.style.bodyBg':                     '正文背景',
      'panes.style.bodyText':                   '正文文字',
      'panes.style.runBtn':                     '运行按钮',
      'panes.style.reset':                      '清除',
      'mcp.default.title':                      '恢复默认',
      // Action color labels (since v2.13.60) — short, no instructions.
      'action.colors.bg':                       '背景',
      'action.colors.text':                     '文字',
      'action.colors.dropdownBg':               '下拉背景',
      'action.colors.option':                   '选项',
      'action.colors.selected':                 '已选中',
      'action.colors.hover':                    '悬停',
      'action.colors.label':                    '标签',
      'action.colors.value':                    '值',
      'action.colors.placeholder':              '占位符',
      'action.colors.bgInactive':               '非活动背景',
      'action.colors.inactiveText':             '非活动文字',
      'cssPanes.enabled.label':                 '已启用',
      'cssPanes.enabled.title':                 '窗格是否应在匹配的页面上显示',
      'cssPanes.delete.title':                  '删除此窗格',
      'cssPanes.delete.confirm':                '删除 CSS 窗格 ":name"?活动标签页中的文本框内容将丢失。',
      'modal.cssPanes.titlePrefix':             'CSS 窗格 —',
      'modal.cssPanes.hint':                    '注入到匹配此级别 URL 页面上的悬浮文本框。内容按标签页保存在 sessionStorage 中(标签页关闭时丢失)。实时 <style> 注入 — CSS 更改即时生效。',
      'modal.cssPanes.add':                     '+ 添加新 CSS 窗格',
      'modal.cssPanes.close':                   '关闭',
      'cssPane.frontend.close.title':           '隐藏窗格(在标签页关闭前保持隐藏)',

      // === JS panes (Phase 2 cont., since v2.13.56) ===
      'jsPanes.contextMenu.disable':            '关闭 JS 窗格',
      'jsPanes.btn.title':                      '管理分配给此文件夹的 JS 窗格',
      'jsPanes.btn.titleGroup':                 '管理分配给此分组的 JS 窗格',
      'jsPanes.btn.titleRule':                  '管理分配给此规则集的 JS 窗格',
      'jsPanes.empty':                          '此级别没有 JS 窗格。点击"+ 添加新 JS 窗格"创建第一个。',
      'jsPanes.section.owned':                  '此级别的窗格',
      'jsPanes.name.placeholder':               '窗格名称',
      'jsPanes.color.title':                    '标题圆点颜色',
      'jsPanes.enabled.label':                  '已启用',
      'jsPanes.enabled.title':                  '窗格是否应在匹配的页面上显示',
      'jsPanes.delete.title':                   '删除此窗格',
      'jsPanes.delete.confirm':                 '删除 JS 窗格 ":name"?活动标签页中的文本框内容将丢失。',
      'modal.jsPanes.titlePrefix':              'JS 窗格 —',
      'modal.jsPanes.hint':                     '在页面上下文(MAIN world,new Function)中每次更改时执行 JS 的悬浮文本框。内容按标签页保存在 sessionStorage 中。执行错误显示在代码下方的红色覆盖层中。',
      'modal.jsPanes.add':                      '+ 添加新 JS 窗格',
      'modal.jsPanes.close':                    '关闭',
      'jsPane.frontend.close.title':            '隐藏窗格(在标签页关闭前保持隐藏)',

      // === JS Consoles (Phase 2 cont., since v2.13.66) ===
      'jsConsoles.contextMenu.disable':         '关闭 JS 控制台',
      'jsConsoles.empty':                       '此级别没有控制台。点击"+ 添加新控制台"创建第一个。',
      'jsConsoles.section.owned':               '此级别的控制台',
      'jsConsoles.name.placeholder':            '控制台名称',
      'jsConsoles.enabled.label':               '已启用',
      'jsConsoles.enabled.title':               '控制台是否应在匹配的页面上显示',
      'jsConsoles.delete.title':                '删除此控制台',
      'jsConsoles.delete.confirm':              '删除控制台 ":name"?活动标签页中的输出历史将丢失。',
      'modal.jsConsoles.titlePrefix':           'JS 控制台 —',
      'modal.jsConsoles.hint':                  '交互式 REPL — Ctrl+Enter 在页面 MAIN world 中运行代码,输出(或错误)追加到输入下方的日志区域。输出按标签页保存在 sessionStorage 中。',
      'modal.jsConsoles.add':                   '+ 添加新控制台',
      'modal.jsConsoles.close':                 '关闭',
      'jsConsole.frontend.close.title':         '隐藏控制台(直到标签页关闭)',
      'jsConsole.input.placeholder':            '要运行的代码(Ctrl+Enter 执行,Ctrl+L 清除)',
      'jsConsole.clear.title':                  '清除输出 (Ctrl+L)',

      // === Output Console (Phase 2 finish, since v2.13.72) — read-only log viewer. ===
      'panes.style.clearBtn':                   '清除按钮',
      'panes.style.actionBtns':                 '操作按钮',
      'outputConsoles.contextMenu.disable':     '关闭输出控制台',
      'outputConsoles.empty':                   '此级别没有输出控制台。点击"+ 添加新输出控制台"创建第一个。',
      'outputConsoles.section.owned':           '此级别的输出控制台',
      'outputConsoles.name.placeholder':        '输出控制台名称',
      'outputConsoles.enabled.label':           '已启用',
      'outputConsoles.enabled.title':           '输出控制台是否应在匹配的页面上显示',
      'outputConsoles.delete.title':            '删除此输出控制台',
      'outputConsoles.delete.confirm':          '删除输出控制台 ":name"?活动标签页中的日志历史将丢失。',
      'modal.outputConsoles.titlePrefix':       '输出控制台 —',
      'modal.outputConsoles.hint':              '只读日志查看器 — 捕获页面的 console.log/warn/error/info(包括异步)。清除 (⌫) 仅清除查看器,不清除浏览器控制台。',
      'modal.outputConsoles.add':               '+ 添加新输出控制台',
      'modal.outputConsoles.close':             '关闭',
      'outputConsole.frontend.close.title':     '隐藏输出控制台(直到标签页关闭)',
      'outputConsole.clear.title':              '清除查看器 (Ctrl+Shift+L)',
      'outputConsole.autoscroll.title':         '自动滚动到最新日志',
      'outputConsole.empty':                    '(无日志 — 等待页面的 console.* / JZ.*)',
      // viewMode + filters + search (since v2.13.73, per spec section 6).
      'outputConsole.mode.standard.title':      '仅显示 console.*(点击 / Ctrl+Shift+K → JZ.*)',
      'outputConsole.mode.jz.title':            '仅显示 window.JZ.*(点击 / Ctrl+Shift+K → 两者)',
      'outputConsole.mode.both.title':          '同时显示:console.* + JZ.*(点击 / Ctrl+Shift+K → 仅 console.*)',
      'outputConsole.filters.title':            '级别过滤器',
      'outputConsole.filters.level.log':        'log',
      'outputConsole.filters.level.info':       'info',
      'outputConsole.filters.level.warn':       'warn',
      'outputConsole.filters.level.error':      'error',
      'outputConsole.filters.level.debug':      'debug',
      'outputConsole.search.title':             '在日志中搜索 (Ctrl+Shift+S;Esc 清除)',
      'outputConsole.search.placeholder':       '过滤条目…',
      'outputConsole.tab.all':                  '全部',
      'outputConsole.tab.jz':                   'JZconsole',
      'outputConsole.tab.console':              'Console',
      'outputConsole.tab.network':              '网络',
      'outputConsole.tab.errors':               '错误',
      'outputConsole.tab.datalayer':            'DataLayer',
      'outputConsole.net.field.callTime':       '调用时间',
      'outputConsole.net.field.status':         '状态',
      'outputConsole.net.field.type':           '类型',
      'outputConsole.net.field.method':         '方法',
      'outputConsole.net.field.domain':         '域名',
      'outputConsole.net.field.path':           '路径',
      'outputConsole.net.field.remote':         '远程地址',
      'outputConsole.net.field.initiator':      '发起者',
      'outputConsole.net.field.time':           '时间',
      'outputConsole.net.field.size':           '大小',
      'outputConsole.net.field.url':            'URL',
      'outputConsole.net.field.error':          '错误',
      'outputConsole.net.status.err':           '错误/其他',
      'outputConsole.filter.all':               '全部',
      'outputConsole.filter.none':              '无',
      'outputConsole.net.domainFilter':         '按域名过滤…',
      'outputConsole.net.domainsInclude':       '域名+',
      'outputConsole.net.domainsExclude':       '域名-',
      'outputConsole.dl.newPushes':             '新推送',
      'outputConsole.dl.currentState':          '当前状态',
      'outputConsole.dl.expandAll':             '全部展开',
      'outputConsole.dl.collapseAll':           '全部折叠',
      'outputConsole.net.size':                 '大小',
      'outputConsole.net.time':                 '时间',
      'outputConsole.err.field.type':           '类型',
      'outputConsole.err.field.location':       '位置',
      'outputConsole.err.field.stack':          '堆栈',
      // API help popup (since v2.13.78, Ctrl+Shift+H).
      'outputConsole.apiHelp.title': 'JustZix — 快捷键与页面内窗口',
      'outputConsole.apiHelp.sec.global': '全局快捷键 —— 在任意页面上有效',
      'outputConsole.apiHelp.g.tempCss': 'TEMP CSS 窗口 —— 打开 / 关闭',
      'outputConsole.apiHelp.g.tempJs': 'TEMP JS 窗口 —— 打开 / 关闭',
      'outputConsole.apiHelp.g.tempJsc': 'TEMP JS Console —— 打开 / 关闭',
      'outputConsole.apiHelp.g.tempOut': 'TEMP Output Console —— 打开 / 关闭',
      'outputConsole.apiHelp.g.ai': 'AI Helper —— 打开 / 关闭',
      'outputConsole.apiHelp.g.edit': '操作栏编辑模式 —— 开 / 关',
      'outputConsole.apiHelp.sec.window': '窗口内 —— 当它获得焦点时',
      'outputConsole.apiHelp.w.run': '执行代码 / 发送消息 —— JS 窗口、JS Console、AI Helper',
      'outputConsole.apiHelp.w.history': '已输入命令的历史 —— JS Console',
      'outputConsole.apiHelp.w.clearConsole': '清空输出 —— JS Console',
      'outputConsole.apiHelp.w.escSearch': '清空搜索框 —— Output Console',
      'outputConsole.apiHelp.sec.output': 'Output Console',
      'outputConsole.apiHelp.o.clear': '清空每个 Output Console 的当前标签页',
      'outputConsole.apiHelp.o.help': '显示 / 隐藏此帮助',
      'outputConsole.apiHelp.o.tabs': '6 个标签页：All、JZ、Console、Network、Errors、DataLayer —— 每个都有自己独立的一套过滤器。',
      'outputConsole.apiHelp.o.network': 'Network：Alt+点击某行会把它的域名加入包含过滤器，Alt+Shift+点击加入排除过滤器。',
      'outputConsole.apiHelp.o.datalayer': 'DataLayer：拖动列之间的分隔条可调整它们的宽度。',
      'outputConsole.apiHelp.sec.mouse': '页面内窗口 —— 鼠标操作',
      'outputConsole.apiHelp.m.drag': '拖动窗口的标题栏即可移动它。',
      'outputConsole.apiHelp.m.resize': '拖动窗口边缘和角上的 8 个句柄之一即可调整大小。',
      'outputConsole.apiHelp.m.snap': '把一个窗口移到另一个附近 —— 它们会磁性吸附成一个一起移动的组。',
      'outputConsole.apiHelp.m.context': '右键点击标题栏会打开菜单：关闭窗口、断开连接、字体、文本大小。',
      'outputConsole.apiHelp.m.tempDbl': '双击 TEMP 窗口的标题栏会在默认尺寸与记住的尺寸之间切换。',
      'outputConsole.apiHelp.m.scroll': '在窗口内用滚轮滚动只会滚动其内容，而不是下方的页面。',
      'outputConsole.apiHelp.sec.editmode': '操作栏编辑模式 —— 按 Ctrl+Alt+S 后',
      'outputConsole.apiHelp.em.undo': '撤销上一次更改',
      'outputConsole.apiHelp.em.delete': '删除选中的标签',
      'outputConsole.apiHelp.em.deselect': '取消选择所有元素',
      'outputConsole.apiHelp.em.move': '用鼠标拖动选中的元素 —— 多个会一起移动。',
      'outputConsole.apiHelp.em.select': '点击选择一个元素，Ctrl+点击把它加入或移出选择，Shift+在空白区域拖动可用矩形框选。',
      'outputConsole.apiHelp.em.label': '「+ 标签」按钮可添加静态文本标签；双击标签会打开其编辑器。',
      'outputConsole.apiHelp.sec.logging': '记录到 Output Console',
      'outputConsole.apiHelp.jzApi': '自有日志器（kind=jz）',
      'outputConsole.apiHelp.aliases': '别名：window.__JUSTZIX__（始终），window.JZ（若未被页面占用）。',
      'outputConsole.apiHelp.consoleApi': '标准 console.*（kind=console，自动捕获）',
      'outputConsole.apiHelp.consoleNote': '页面的每个 console.* 都会透传到原始 console（DevTools 仍正常工作）并发送到 Output Console。',
      'outputConsole.apiHelp.footer': '完整手册（8 种语言）：JustZix 面板 → 帮助。',
      // === Onboarding / 教程 ===
      'content.exec.cspBlocked': '此页面阻止脚本执行（CSP）。请在浏览器设置中为 JustZix 启用"用户脚本"。',
      'onboard.ui.back': '上一步',
      'onboard.ui.next': '下一步',
      'onboard.ui.finish': '完成',
      'onboard.ui.copy': '复制',
      'onboard.ui.copied': '已复制',
      'onboard.ui.hint': '执行此操作以继续',
      'onboard.ui.close': '关闭',
      'onboard.front.title': '前端教程',
      'onboard.front.lead': '了解可直接在页面上使用的 JustZix 工具——悬浮按钮、面板、代码窗口和操作栏。',
      'onboard.front.titleFirst': '页面上的 JustZix',
      'onboard.front.leadFirst': 'JustZix 也可直接在页面上使用。本教程将逐步介绍悬浮按钮、CSS/JS 窗口和操作栏。',
      'onboard.front.empty': '前端教程即将推出。',
      'onboard.lr.titleFirst': '欢迎使用 JustZix',
      'onboard.lr.title': '教程中心',
      'onboard.lr.leadFirst': '第一次来？交互式教程将带你一步步构建一个完整、可用的方案 —— 就在面板中。',
      'onboard.lr.lead': '启动交互式向导 —— 它会带你一步步构建一个完整的方案。',
      'onboard.lr.scenarios': '教程',
      'onboard.lr.start': '开始',
      'onboard.lr.later': '以后再说',
      'onboard.lr.noAuto': '启动时不显示',
      'onboard.proj.name': '你的第一个项目',
      'onboard.proj.desc': '从零到可用方案：一个 folder、一个操作栏、一个 group、一个含 CSS 与 JS 的 rule 集，以及一个切换效果的按钮。',
      'onboard.proj.meta': '19 步 · 约 3 分钟',
      'onboard.proj.s1.title': '欢迎使用 JustZix',
      'onboard.proj.s1.body': 'JustZix 把你自己的 CSS 和 JavaScript 注入到选定的页面。现在我们将一步步构建一个带可用按钮的完整方案，约需 3 分钟。',
      'onboard.proj.s2.title': '添加 folder',
      'onboard.proj.s2.body': 'folder 把一个网站的规则归在一起。点击侧边栏中高亮的 <code>+</code> 按钮。',
      'onboard.proj.s3.title': 'folder 名称',
      'onboard.proj.s3.body': '输入一个名称，例如 <code>我的第一个项目</code> —— 这只是给你看的标签。',
      'onboard.proj.s4.title': 'URL 匹配模式',
      'onboard.proj.s4.body': '它决定 folder 在哪些页面生效。输入带星号的网址并按 Enter，例如 <code>https://example.com/*</code>。若想在任意页面练习，只需输入 <code>*</code>。',
      'onboard.proj.s5.title': '按钮标签',
      'onboard.proj.s5.body': '最多 3 个字母 —— 会显示在页面上 JustZix 的浮动按钮上。',
      'onboard.proj.s6.title': '操作栏',
      'onboard.proj.s6.body': '操作栏是页面上一个含交互按钮的面板。我们先创建一个 —— 点击高亮的操作栏按钮。',
      'onboard.proj.s7.title': '添加操作栏',
      'onboard.proj.s7.body': '点击以在此 folder 中创建一个新的操作栏。',
      'onboard.proj.s8.title': '关闭管理器',
      'onboard.proj.s8.body': '操作栏已就绪。关闭操作栏管理器窗口以返回 folder。',
      'onboard.proj.s9.title': '打开 group',
      'onboard.proj.s9.body': '创建 folder 时，JustZix 已自动添加了第一个 group。在侧边栏展开 folder 并点击该 group 将其打开，然后点击「下一步」。',
      'onboard.proj.s10.title': '添加 rule 集',
      'onboard.proj.s10.body': 'rule 集就是真正的规则 —— 一对 CSS 与 JavaScript 代码。点击高亮的添加 rule 集按钮。',
      'onboard.proj.s11.title': '打开 rule 集',
      'onboard.proj.s11.body': '新的 rule 集已出现在树中 group 下方。点击它以打开 rule 集编辑器，然后点击「下一步」。',
      'onboard.proj.s12.title': 'CSS 代码',
      'onboard.proj.s12.body': 'CSS 标签页已激活。粘贴下面的代码 —— 它会把整个页面变灰，并为效果开关做准备。',
      'onboard.proj.s13.title': 'JavaScript 标签页',
      'onboard.proj.s13.body': '点击 <code>JavaScript</code> 标签页，为 rule 集添加 JS 代码。',
      'onboard.proj.s14.title': 'JavaScript 代码',
      'onboard.proj.s14.body': 'JS 代码在你打开页面时运行。粘贴下面的代码 —— 它会显示一条确认 JustZix 正常工作的消息。该字段带语法校验。',
      'onboard.proj.s15.title': '操作标签页',
      'onboard.proj.s15.body': '现在我们来添加一个交互按钮。点击 rule 集编辑器中的操作标签页。',
      'onboard.proj.s16.title': '添加按钮',
      'onboard.proj.s16.body': '点击以添加一个操作元素。默认会创建一个按需运行代码的按钮。',
      'onboard.proj.s17.title': '按钮代码',
      'onboard.proj.s17.body': '每个按钮都有自己的代码，在页面上被点击时运行。点击新按钮的代码字段。',
      'onboard.proj.s18.title': '开关代码',
      'onboard.proj.s18.body': '粘贴用于开启和关闭 CSS 中所设灰度的代码。然后在代码编辑器窗口中保存更改。',
      'onboard.proj.s19.title': '完成！',
      'onboard.proj.s19.body': '你已拥有一个完整方案：folder、操作栏、group、含 CSS 与 JS 代码的 rule 集，以及一个可用的按钮。打开一个匹配 URL 模式的页面 —— 你会看到消息、灰度效果，以及切换它的按钮。你可以从设置菜单重新启动教程。',
      // Font + size context menu (since v2.13.70) — shared by CSS/JS pane + JS Console.
      'panes.contextMenu.font':                 '字体',
      'panes.contextMenu.size':                 '大小',
      'panes.contextMenu.defaultFont':          '默认',
      'modal.confirmDismissDirty':              '您有未保存的更改。关闭并放弃它们?',
      'modal.confirmSaveBeforeClose':           '您有未保存的更改。\n\n点击"确定"保存并关闭。\n点击"取消"进入放弃确认。',
      'modal.confirmDiscardChanges':            '关闭且不保存?更改将丢失。\n\n点击"确定"放弃更改。\n点击"取消"继续编辑。',
      'actionBars.moveLocked':                  '无法移动 — 操作栏中有元素将落在新范围之外。请先重新分配它们。',
      'action.bar.noneOption':                  '— 无操作栏 —',
      'action.bar.unnamed':                     '(未命名)',
      'action.bar.selectTitle':                 '将此操作分配到操作栏。操作栏向下继承(文件夹 → 分组 → 规则)。',
      'action.bar.noBarsHint':                  '规则集树中没有操作栏 — 通过文件夹/分组/规则中的"操作栏"按钮添加操作栏。',

      // === Typed actions (since v2.12.x) ===
      'action.color.title':                     '背景颜色(按钮/选择框/输入框/滑块)。点击 = 拾色器,或在旁边输入十六进制值。',
      'action.colorText.title':                 '文字颜色(以及 SLIDER 的填充条)。未设置时默认为白色。',
      'action.colorHex.title':                  '十六进制代码 — 以纯文本形式复制 (Ctrl+C) 或粘贴 (Ctrl+V)。接受 #RGB 或 #RRGGBB。',
      'action.type.title':                      '元素类型(按钮、下拉 SELECT、…)',
      'action.type.button':                     '按钮',
      'action.type.select':                     '下拉框 (SELECT)',
      'action.type.input':                      '文本框 (INPUT)',
      'action.type.slider':                     '范围滑块 (SLIDER)',
      'action.type.textarea':                   '多行文本框 (TEXTAREA)',
      'action.type.toggle3':                    '三态切换 (TOGGLE3)',

      // Memory scope (since v2.13.0; ruleSet added v2.13.4) — shared across state-bearing types
      'action.memory.label':                    '记住值:',
      'action.memory.none':                     '不记住(重新加载时重置)',
      'action.memory.page':                     '按页面 (URL)',
      'action.memory.host':                     '按域名',
      'action.memory.ruleSet':                  '在规则集内(其运行的所有 URL)',
      'action.memory.global':                   '全局(所有页面,共享)',
      'action.input.middleBtn':                 'INPUT · 代码 :code',
      'action.slider.middleBtn':                'SLIDER · :min–:max 步长 :step :unit · 代码 :code',
      'action.textarea.middleBtn':              'TEXTAREA · :rows 行 · 代码 :code',
      'action.toggle3.middleBtn':               'TOGGLE3 · :filled/3 个状态 · 代码 :code',
      'action.toggle3.defaultState1':           'OFF',
      'action.toggle3.defaultState2':           'MID',
      'action.toggle3.defaultState3':           'ON',
      'action.toggle3.stateLabel.placeholder':  '标签(最多 5 个)',
      'action.toggle3.stateValue.placeholder':  '值(传给 JS)',
      'action.toggle3.defaultStateTitle':       '默认状态(无记忆时)',

      'action.select.middleBtn.staticEmpty':    'SELECT 静态(无选项)',
      'action.select.middleBtn.static':         'SELECT 静态 · :count 个选项 · 代码 :code',
      'action.select.middleBtn.js':             'SELECT js · 代码 :code',
      'action.select.empty':                    '没有选项。用下方的按钮添加第一个。',
      'action.select.untitled':                 '(无标题)',
      'action.select.optionNamePlaceholder':    '名称(可见)',
      'action.select.optionValuePlaceholder':   '值(传给 JS)',
      'action.select.deleteOption':             '删除此选项',

      // === Modal — action code ===
      'modal.actionCode.titlePrefix':           '操作按钮代码:',
      'modal.actionCode.body':                  '在悬浮面板中点击操作按钮时执行的 JavaScript 代码。在页面上下文中运行。',
      'modal.actionCode.cancel':                '取消',
      'modal.actionCode.save':                  '保存',

      // === Modal — INPUT editor (since v2.13.0) ===
      'modal.inputEditor.titlePrefix':          'INPUT 编辑器:',
      'modal.inputEditor.body':                 '带可选 JS 代码的文本框,在更改时触发(Enter/失去焦点)。值按所选范围记住。',
      'modal.inputEditor.placeholder':          '占位符:',
      'modal.inputEditor.defaultValue':         '默认值:',
      'modal.inputEditor.colorPlaceholder':     '占位符颜色:',
      'modal.inputEditor.codeLabel':            'JS 代码(更改时):',
      'modal.inputEditor.hint':                 '代码中可用:<code>value</code>(输入框的字符串)、<code>$el</code> (HTMLInputElement)、<code>$action</code>(id、label)。',
      'modal.inputEditor.close':                '关闭',
      'modal.colorClear':                       '恢复默认颜色',
      'modal.sliderEditor.colorLabel':          '标签颜色(左):',
      'modal.sliderEditor.colorValue':          '值颜色(右):',
      'modal.selectOptions.appearance.title':   '下拉框外观:',
      'modal.selectOptions.appearance.hint':    '对于 <strong>js 变体</strong>:下拉背景和选项颜色在所有浏览器中都生效。已选中和悬停背景因浏览器而异(取决于操作系统)— 要完全自定义请使用 <em>静态</em> 变体。',
      'modal.selectOptions.colorDropdownBg':    '下拉背景:',
      'modal.selectOptions.colorOption':        '选项文字颜色:',
      'modal.selectOptions.colorSelected':      '已选中选项背景:',
      'modal.selectOptions.colorHover':         '悬停背景:',
      'modal.sliderEditor.titlePrefix':         'SLIDER 编辑器:',
      'modal.sliderEditor.body':                '带可选 JS 代码的范围滑块,在用户释放滑块后触发(`change`)。值以 Number 形式传递。',
      'modal.sliderEditor.min':                 '最小值:',
      'modal.sliderEditor.max':                 '最大值:',
      'modal.sliderEditor.step':                '步长:',
      'modal.sliderEditor.defaultValue':        '默认值:',
      'modal.sliderEditor.unit':                '单位(可选):',
      'modal.sliderEditor.codeLabel':           'JS 代码(更改时):',
      'modal.sliderEditor.hint':                '代码中可用:<code>value</code> (Number)、<code>$el</code> (HTMLInputElement type=range)、<code>$action</code>(id、label)。',
      'modal.sliderEditor.close':               '关闭',
      'modal.sliderEditor.errMaxGtMin':         '最大值必须大于最小值。',
      'modal.sliderEditor.errStepPositive':     '步长必须为正数。',
      'modal.textareaEditor.titlePrefix':       'TEXTAREA 编辑器:',
      'modal.textareaEditor.body':              '带可选 JS 代码的多行文本框,在 <code>change</code>(失去焦点)时触发。textarea 中的 <strong>Enter</strong> 是正常换行 — 不会触发代码(与 INPUT 不同)。',
      'modal.textareaEditor.placeholder':       '占位符:',
      'modal.textareaEditor.defaultValue':      '默认值:',
      'modal.textareaEditor.rows':              '行数:',
      'modal.textareaEditor.colorPlaceholder':  '占位符颜色:',
      'modal.textareaEditor.codeLabel':         'JS 代码(更改/失去焦点时):',
      'modal.textareaEditor.hint':              '代码中可用:<code>value</code>(textarea 的字符串,可能包含 <code>\\n</code>)、<code>$el</code> (HTMLTextAreaElement)、<code>$action</code>(id、label)。',
      'modal.textareaEditor.close':             '关闭',

      // === Modal — TOGGLE3 editor (od v2.13.25) ===
      'modal.toggle3Editor.titlePrefix':        'TOGGLE3 编辑器:',
      'modal.toggle3Editor.body':               '三态分段控件。点击非活动状态会将其激活,并以所选状态的 <code>value</code> 运行 JS 代码。记忆功能按操作保存当前索引 (0-2)。',
      'modal.toggle3Editor.statesTitle':        '状态(正好 3 个,单选 = 默认):',
      'modal.toggle3Editor.colorBg':            '背景颜色:',
      'modal.toggle3Editor.colorHover':         '悬停颜色:',
      'modal.toggle3Editor.colorInactiveText':  '非活动文字颜色:',
      'modal.toggle3Editor.codeLabel':          'JS 代码(状态更改时):',
      'modal.toggle3Editor.hint':               '代码中可用:<code>value</code>(state.value,回退到 state.label)、<code>stateIdx</code> (0-2)、<code>stateLabel</code>、<code>$el</code>(容器 div)、<code>$action</code>。',
      'modal.toggle3Editor.close':              '关闭',

      // === Modal — SELECT editor (since v2.12.1, redesign v2.12.2 — variants) ===
      'modal.selectOptions.titlePrefix':        'SELECT 编辑器:',
      'modal.selectOptions.idLabel':            '操作 ID:',
      'modal.selectOptions.selectorLabel':      'JS 选择器:',
      'modal.selectOptions.copyId':             '复制 ID',
      'modal.selectOptions.copySelector':       '复制 JS 选择器',
      'modal.selectOptions.variant.static':     '静态选项 + 单个 JS 代码',
      'modal.selectOptions.variant.js':         '仅 JS 代码(管理下拉框)',
      'modal.selectOptions.static.body':        '每个选项:<strong>名称</strong>(在下拉框中可见)+ <strong>值</strong>(作为 <code>value</code> 变量传给代码)。一段共享代码在更改时运行。',
      'modal.selectOptions.static.hint':        '代码中可用:<code>value</code>(字符串)、<code>$el</code>(select 元素)、<code>$action</code>(id、label)。',
      'modal.selectOptions.staticCodeLabel':    'JS 代码(共享):',
      'modal.selectOptions.js.body':            '您的 JS 代码在下拉框插入页面后运行一次。接收对空 <code>&lt;select&gt;</code> 的 <code>$el</code> 引用 — 您自行填充选项并处理 <code>change</code>。',
      'modal.selectOptions.js.hint':            '可用:<code>$el</code> (HTMLSelectElement)、<code>$action</code>(id、label)。',
      'modal.selectOptions.jsCodeLabel':        'JS 代码:',
      'modal.selectOptions.addOption':          '+ 添加选项',
      'modal.selectOptions.close':              '关闭',

      // === Modal — sync conflict ===
      'modal.syncConflict.title':               '同步冲突',
      'modal.syncConflict.description':         '您登录的账户在云端已有数据,而此设备也有本地库。请选择要做什么 — 此操作在本地不可撤销,但确认前可以先导出本地数据。',
      'modal.syncConflict.localLabel':          '此设备本地:',
      'modal.syncConflict.serverLabel':         '云端账户:',
      'modal.syncConflict.useServer.title':     '使用云端账户的数据',
      'modal.syncConflict.useServer.body':      '本地文件夹/分组/规则将被账户中的内容替换。如果账户的数据比此设备更新更准确,请选择此项。',
      'modal.syncConflict.sendMine.title':      '将我的本地数据发送到账户',
      'modal.syncConflict.sendMine.body':       '账户状态将被此设备上的内容替换。如果此设备的数据更新更准确,请选择此项。',
      'modal.syncConflict.merge.title':         '合并两组数据',
      'modal.syncConflict.merge.body':          '本地和云端实体将一起保留。如果同一项在两处都存在(相同 ID),以账户版本为准。仅本地的项将在下次同步时发送到账户。',
      'modal.syncConflict.exportLocal':         '先导出本地',
      'modal.syncConflict.cancel':              '取消登录(登出)',

      // === Modal — share import ===
      'modal.shareImport.title':                '从 JustZix 链接导入',
      'modal.shareImport.body':                 '粘贴完整链接或仅令牌 <strong>JZS-XXXX-XXXX-XXXX-XXXX</strong>:',
      'modal.shareImport.placeholder':          'https://www.justzix.com/s/JZS-XXXX-XXXX-XXXX-XXXX',
      'modal.shareImport.cancel':               '取消',
      'modal.shareImport.fetch':                '获取',
      'modal.shareImport.preview.title':        '共享导出预览',
      'modal.shareImport.jsWarn':               '<strong>⚠ 警告:</strong> 此导出包含 JavaScript 代码。代码将在匹配规则的页面上运行 — 导入前请检查它的行为。',
      'modal.shareImport.showCode':             '显示 JavaScript 代码',
      'modal.shareImport.back':                 '← 返回',
      'modal.shareImport.confirm':              '导入',

      // === Modal — my shares ===
      'modal.shares.title':                     '我的共享链接',
      'modal.shares.body':                      '导出文件的限时公开链接。后端会保留内容直到过期,然后删除。',
      'modal.shares.howTo':                     '要创建新链接 — 打开一个导出文件(完整 / 文件夹 / 分组 / 规则集)并点击 <strong>"分享 URL"</strong>。',
      'modal.shares.refresh':                   '刷新',
      'modal.shares.close':                     '关闭',

      // === Modal — share create ===
      'modal.shareCreate.title':                '通过链接分享导出',
      'modal.shareCreate.activeFor':            '链接有效期为:',
      'modal.shareCreate.ttl.1h':               '1 小时',
      'modal.shareCreate.ttl.6h':               '6 小时',
      'modal.shareCreate.ttl.12h':              '12 小时',
      'modal.shareCreate.ttl.24h':              '24 小时',
      'modal.shareCreate.ttl.default':          '(默认)',
      'modal.shareCreate.ttl.48h':              '48 小时',
      'modal.shareCreate.expireNote':           '过期后链接将停止工作。您可以在"我的分享"中提前撤销它。',
      'modal.shareCreate.cancel':               '取消',
      'modal.shareCreate.generate':             '生成链接',
      'modal.shareCreate.result.title':         '链接已生成',
      'modal.shareCreate.result.urlLabel':      '将此链接发送给接收者:',
      'modal.shareCreate.result.copyUrl':       '复制链接',
      'modal.shareCreate.result.tokenLabel':    '或仅令牌(用于粘贴到"从 URL 导入"):',
      'modal.shareCreate.result.copyToken':     '复制令牌',
      'modal.shareCreate.result.goToMine':      '查看"我的分享"',
      'modal.shareCreate.result.close':         '关闭',

      // === Modal — sync result ===
      'modal.syncResult.title':                 '同步结果',
      'modal.syncResult.close':                 '好的,知道了',

      // === Modal — cloud sync ===
      'modal.sync.title':                       '云同步',
      'modal.sync.signedOut.tab.register':      '首台设备',
      'modal.sync.signedOut.tab.login':         '我有密钥',
      'modal.sync.signedOut.tab.recover':       '我丢失了密钥',
      'modal.sync.signedOut.register.body':     '生成新的同步密钥。<strong>将其保存在安全的地方</strong> — 后端只存储哈希值。该密钥可用于在第二台设备上登录并查看同一个库。',
      'modal.sync.signedOut.deviceLabel':       '设备标签(可选):',
      'modal.sync.signedOut.deviceLabel.placeholder1': '例如 办公电脑',
      'modal.sync.signedOut.deviceLabel.placeholder2': '例如 笔记本电脑',
      'modal.sync.signedOut.yourKey':           '您的同步密钥:',
      'modal.sync.signedOut.regenerate':        '重新生成',
      'modal.sync.signedOut.copyClipboard':     '复制到剪贴板',
      'modal.sync.signedOut.warnSave':          '⚠ <strong>立即复制并保存密钥。</strong> 注册后密钥也会在"密钥"标签页中提供 — 但最好在密码管理器中保留一份副本。',
      'modal.sync.signedOut.login.body':        '粘贴您在注册首台设备时看到的密钥。格式:<code>SYNC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX</code>。',
      'modal.sync.signedOut.keyLabel':          '同步密钥:',
      'modal.sync.signedOut.recover.body':      '如果账户上设置了<strong>已验证的</strong>邮箱 — 输入它并发送恢复链接。点击邮件中的链接后,您将收到<strong>新密钥</strong>(一次性)。所有活动会话都将被登出。',
      'modal.sync.signedOut.email':             '账户邮箱:',
      'modal.sync.signedOut.email.placeholder': 'someone@example.com',
      'modal.sync.signedOut.cancel':            '取消',
      'modal.sync.signedOut.regSubmit':         '注册设备',
      'modal.sync.signedOut.loginSubmit':       '登录',
      'modal.sync.signedOut.recoverSubmit':     '发送恢复链接',
      'modal.sync.signedIn.tab.status':         '状态',
      'modal.sync.signedIn.tab.key':            '访问密钥',
      'modal.sync.signedIn.tab.backups':        '备份',
      'modal.sync.signedIn.tab.email':          '邮箱与恢复',
      'modal.sync.signedIn.status.account':     '账户',
      'modal.sync.signedIn.status.pending':     '待处理的更改:',
      'modal.sync.signedIn.status.cloudLib':    '云端库',
      'modal.sync.signedIn.status.lastCursor':  '上次拉取(游标):',
      'modal.sync.signedIn.status.syncNow':     '立即同步',
      'modal.sync.signedIn.status.snapshot':    '完整快照',
      'modal.sync.signedIn.status.refresh':     '刷新账户数据',
      'modal.sync.signedIn.status.reconcile':   '与账户完整重新同步',
      'modal.sync.signedIn.status.sessions':    '活动会话(设备)',
      'modal.sync.signedIn.status.history':     '同步历史',
      'modal.sync.history.empty':               '暂无同步记录。',
      'modal.sync.history.viewAll':             '查看完整历史',
      'modal.sync.history.changes':             '更改：',
      'modal.sync.history.device':              '设备：',
      'modal.sync.history.fromDevices':         '拉取自：',
      'modal.sync.history.stateAfter':          '同步后状态：',
      'modal.sync.history.noDetail':            '无更改详情（较早的记录）。',
      'modal.sync.signedIn.key.body':           '此账户的同步密钥。在第二台设备上输入它以同步库。',
      'modal.sync.signedIn.key.yourKey':        '您的密钥:',
      'modal.sync.signedIn.key.reveal':         '显示密钥',
      'modal.sync.signedIn.key.copy':           '复制到剪贴板',
      'modal.sync.signedIn.key.forget':         '从此设备移除密钥',
      'modal.sync.signedIn.key.warn':           '⚠ 密钥提供对库的完全访问权限。请勿公开展示。如果您不想在本地保留它 — 请点击 <strong>从此设备移除密钥</strong>(在密码管理器中保留一份副本)。登录仍然有效,但密钥无法再次查看。',
      'modal.sync.signedIn.key.noKey':          '此设备未存储密钥(在此功能存在之前登录,或已手动移除)。您只能通过恢复邮件,或如果在别处保存了副本来恢复它。',
      'modal.sync.signedIn.backups.hint':       '云备份 — 恢复会替换整个库。',
      'modal.sync.signedIn.backups.label':      '新备份标签(可选):',
      'modal.sync.signedIn.backups.placeholder':'例如 大改动之前',
      'modal.sync.signedIn.backups.create':     '立即创建备份',
      'modal.sync.signedIn.backups.refresh':    '刷新列表',
      'modal.sync.signedIn.email.body':         '邮箱可启用<strong>恢复</strong>功能 — 丢失密钥时,您可以通过点击发送到该地址的链接来获取新密钥。添加邮箱需要验证(邮件中的链接)。',
      'modal.sync.signedIn.email.add':          '添加邮箱(恢复):',
      'modal.sync.signedIn.email.placeholder':  'someone@example.com',
      'modal.sync.signedIn.email.submit':       '发送验证链接',
      'modal.sync.signedIn.email.remove':       '移除邮箱',
      'modal.sync.signedIn.email.recoveryFlow': '恢复流程:在另一台设备上(或无密钥情况下)选择登录面板中的 <strong>"我丢失了密钥"</strong> 标签页,输入此邮箱 — 后端将发送一个链接,生成新密钥并使所有活动会话失效。',
      'modal.sync.signedIn.logout':             '登出此设备',
      'modal.sync.signedIn.close':              '关闭',

      // === Content script (floating button + actions panel) ===
      'content.float.titleGloballyOff':         '插件已全局禁用 — 点击扩展图标以启用',
      'content.float.titleNormal':              '左键点击:启用/禁用文件夹 • 右键点击:显示规则集列表 • 拖动以移动',
      'content.panel.empty':                    '没有活动的规则集',
      'content.panel.actionBarsTitle':          '操作栏',
      'content.panel.connectionsTitle':         '连接',
      'content.actionsPanel.dragHandle':        '拖动以移动',
      'content.editMode.banner':                '操作栏编辑模式 — 点击/Ctrl+点击选择,拖动移动 · Ctrl+Z 撤销 · Ctrl+Alt+S 退出',
      'content.editMode.undoEmpty':             '没有可撤销的操作',
      'content.editMode.addLabel':              '+ 标签',
      'content.editMode.resetBar':              '重置操作栏',
      'content.editMode.resetConfirm':          '将操作栏重置为默认布局?自定义位置、大小和标签将被移除。',
      'content.labelEditor.text':               '文本:',
      'content.labelEditor.font':               '字体:',
      'content.labelEditor.size':               '大小:',
      'content.labelEditor.color':              '颜色:',
      'content.labelEditor.delete':             '删除',
      'content.labelEditor.close':              '关闭',
      'content.labelContextMenu.delete':        '删除标签',
      'content.bar.hideUntilReload':            '关闭此操作栏(直到重新加载)',
      'content.bar.disconnectFrom':             '断开与::name 的连接',
      'content.bar.disconnectAll':              '断开所有连接',
      'content.bar.fallbackName':               '操作栏',
      'content.float.label':                    '⊕ 主按钮',
      'content.toast.noFoldersForPage':         '没有与此页面匹配的文件夹',
      'content.toast.floatVisible':             '悬浮按钮已启用',
      'content.toast.floatHidden':              '悬浮按钮已隐藏',

      // === Background (context menu + JS error logger) ===
      'background.contextMenu.toggleFloat':     '启用/禁用此页面的悬浮按钮',
      'background.contextMenu.editMode':        '操作栏编辑模式(开启/关闭切换)',
      'background.contextMenu.actionBars':      '操作栏',
      'background.contextMenu.actionBarsEmpty': '(此页面没有操作栏)',
      'background.contextMenu.actionBarFallback': '(未命名)',
      'background.contextMenu.cssPanes':         'CSS 窗格',
      'background.contextMenu.cssPanesEmpty':    '(此页面没有 CSS 窗格)',
      'background.contextMenu.cssPaneFallback':  '(未命名)',
      'background.contextMenu.jsPanes':          'JS 窗格',
      'background.contextMenu.jsPanesEmpty':     '(此页面没有 JS 窗格)',
      'background.contextMenu.jsPaneFallback':   '(未命名)',
      'background.contextMenu.jsConsoles':       'JS 控制台',
      'background.contextMenu.jsConsolesEmpty':  '(此页面没有控制台)',
      'background.contextMenu.jsConsoleFallback':'(未命名)',
      'background.contextMenu.outputConsoles':       '输出控制台',
      'background.contextMenu.outputConsolesEmpty':  '(此页面没有输出控制台)',
      'background.contextMenu.outputConsoleFallback':'(未命名)',
      // TEMP panes (since v2.13.81).
      'background.contextMenu.tempPanes':            '临时窗口 (TEMP)',
      'background.contextMenu.tempCss':              'TEMP CSS 窗格 (Ctrl+Alt+G)',
      'background.contextMenu.tempJs':               'TEMP JS 窗格 (Ctrl+Alt+H)',
      'background.contextMenu.tempJsConsole':        'TEMP JS 控制台 (Ctrl+Alt+J)',
      'background.contextMenu.tempOutput':           'TEMP 输出控制台 (Ctrl+Alt+K)',
      'pane.collapse.title':                      '将窗口折叠到标题栏',
      'pane.expand.title':                        '展开窗口',
      'aiHelper.form.submit':                     '确定',
      'aiHelper.form.cancel':                     '取消',
      'aiHelper.form.yes':                        '是',
      'aiHelper.form.no':                         '否',
      'aiHelper.form.askTitle':                   '提问',
      'aiHelper.form.confirmTitle':               '确认',
      'aiHelper.form.updateConfirmTitle':               '是否更新？',
      'aiHelper.style.title':                           'AI Helper 外观',
      'aiHelper.style.role.user':                       '您的消息',
      'aiHelper.style.role.assistant':                  'AI 回复',
      'aiHelper.style.role.tool':                       '工具结果',
      'aiHelper.style.role.form':                       '表单',
      'aiHelper.style.font':                            '字体',
      'aiHelper.style.size':                            '大小',
      'aiHelper.style.color':                           '文字',
      'aiHelper.style.bg':                              '背景',
      'aiHelper.style.reset':                           '恢复默认',
      'aiHelper.style.resetAll':                        '全部恢复默认',
      'aiHelper.confirm.update.folder':                 '更新目录',
      'aiHelper.confirm.update.group':                  '更新分组',
      'aiHelper.confirm.update.rule':                   '更新集合',
      'aiHelper.confirm.update.bar':                    '更新操作栏',
      'aiHelper.confirm.update.action':                 '更新操作',
      'aiHelper.confirm.update.cssPane':                '更新 CSS 窗口',
      'aiHelper.confirm.update.jsPane':                 '更新 JS 窗口',
      'aiHelper.confirm.update.jsConsole':             '更新 JS 控制台',
      'aiHelper.confirm.update.outputConsole':         '更新输出控制台',
      'aiHelper.form.createConfirmTitle':         '是否创建？',
      'aiHelper.form.confirmed':                  '已确认',
      'aiHelper.form.cancelled':                  '已取消',
      'tempPane.close.title':                        '关闭临时窗口（内容在会话期间保留）',
      'tempPane.clear.title':                        '清除窗口内容',
      'background.jsError.prefix':              '[JustZix] 规则集/操作 ":id" 中的 JS 错误:\n',
      // === AI Helper (od v2.13.127) ===
      'settings.aiHelper.label': 'AI Helper',
      'settings.aiHelper.title': '配置 AI Helper 的 API 密钥',
      'modal.aiHelper.title': 'AI Helper',
      'modal.aiHelper.intro': '输入你想使用的服务商的 API 密钥。一个即可。',
      'modal.aiHelper.keyOpenai': 'API 密钥 — OpenAI',
      'modal.aiHelper.keyAnthropic': 'API 密钥 — Anthropic',
      'modal.aiHelper.keyGemini': 'API 密钥 — Gemini',
      'modal.aiHelper.defaultProvider': '默认服务商',
      'modal.aiHelper.defaultModel': '默认模型',
      'modal.aiHelper.modelAuto': '— 自动（最高）—',
      'modal.aiHelper.loadModels': '加载模型',
      'modal.aiHelper.securityNote': '<strong>安全性：</strong>API 密钥默认仅存储在此浏览器的本地存储中（<code>chrome.storage.local</code>）。勾选密钥旁的“同步"会通过 JustZix 云（与扩展同步其余数据所用的同一云）将该密钥发送到你的其他设备。未勾选的密钥永远不会离开此设备。',
      'modal.aiHelper.syncKey':                '同步',
      'modal.aiHelper.syncKeyTitle': '通过 JustZix 云在你的设备之间同步此密钥',
      'modal.aiHelper.cancel': '取消',
      'modal.aiHelper.save': '保存',
      'modal.aiHelper.needKey': '请先输入 API 密钥。',
      'modal.aiHelper.loadingModels': '正在加载模型…',
      'modal.aiHelper.errModels': '加载模型失败',
      'modal.aiHelper.modelsLoaded': '个模型可用',
      'aiHelper.win.title': 'AI Helper',
      'aiHelper.win.close': '关闭',
      'aiHelper.win.provider': '服务商',
      'aiHelper.win.model': '模型',
      'aiHelper.win.clear': '清空对话',
      'aiHelper.win.clearConfirm': '清空整个对话并重新开始？',
      'aiHelper.win.inputPlaceholder': '询问有关 CSS、JS 或操作的帮助…',
      'aiHelper.win.send': '发送（Ctrl+Enter）',
      'aiHelper.win.welcome': '你好！我会帮你为此页面准备 CSS、JavaScript 或操作。请描述你想实现的目标。',
      'aiHelper.win.you': '你',
      'aiHelper.win.assistant': 'AI Helper',
      'aiHelper.win.thinking': '思考中…',
      'aiHelper.win.error': 'API 连接错误',
      'aiHelper.win.noKeys': '没有 API 密钥。请在「设置 → AI Helper」中添加密钥。',
      'aiHelper.win.noModel': '请选择模型。',
      'aiHelper.win.loadingModels': '加载中…',
      'aiHelper.win.modelsError': '加载模型出错',
      'background.contextMenu.aiHelper': 'AI Helper',
      'popup.aiHelper.label': 'AI Helper',
      'popup.aiHelper.on': '已打开 — 点击关闭（Ctrl+Alt+\\）',
      'popup.aiHelper.off': '已关闭 — 点击打开（Ctrl+Alt+\\）',
      'popup.aiHelper.unavailable': '在此页面不可用',
      // === AI Helper etap 2 (od v2.13.128) ===
      'aiHelper.win.maxRounds': '已达到本轮工具步骤上限。请告诉我如何继续。',
      'aiHelper.win.copy':                        '复制对话',
      'aiHelper.win.copy.all':                    '复制整个对话',
      'aiHelper.win.copy.lastInput':              '复制最后一次 API 提示（system + 历史 + 输入）',
      'aiHelper.win.copyNoInput':                 '无内容可复制',
      'aiHelper.copy.lastInput.prefix':           '[JustZix → 转发到 API] 已复制发送给模型的完整最后提示（system + 历史 + 最后消息）。将下面的块作为新对话的第一条 user 消息发送到 API。发送前请删除此头部。',
      'aiHelper.win.copied':                      '已复制',
      'aiHelper.win.copyFailed':                  '复制失败——请检查剪贴板权限。',
      'aiHelper.win.font':                        '字体',
      'aiHelper.win.fontSize':                    '大小',
      'aiHelper.win.defaultFont':                 '默认',
      'aiHelper.win.stop':                        '停止代理',
      'aiHelper.win.stopped':                     '已由用户中断。',
      'aiHelper.win.ctxUsage':                    '模型上下文用量',
      'aiHelper.win.ctxUsageDetail':              '上下文',
      'aiHelper.win.compact':                     '压缩对话（LLM 摘要）',
      'aiHelper.win.compactNoop':                 '历史记录太短，无需压缩。',
      'aiHelper.win.compactNoProvider':           '请先选择提供商和模型。',
      'aiHelper.win.compactErr':                  '压缩错误',
      'aiHelper.win.compactEmpty':                '模型返回了空结果。',
      'aiHelper.win.compactedPrefix':             '已汇总',
      'aiHelper.win.compactedSuffix':             '条较早的消息：',
      'aiHelper.win.telemetryTooltip':            '本次会话的时间与令牌',
      'aiHelper.win.telemetryTimers':             '时间：自上条消息 (u) / 自上次工具 (t) / 代理总活动 (a)',
      'aiHelper.win.telemetryCurrent':            '当前动作的令牌（↑ 输入 / ↓ 输出）',
      'aiHelper.win.telemetrySession':            '本次会话的令牌总数',
      'aiHelper.win.msgTelem':                    '本轮令牌与模型响应时间',
      'aiHelper.win.toolDur':                     '工具执行时间',
      'aiHelper.win.stepIdBadge':                 '历史步骤编号',
      'aiHelper.form.freeText':                   '或用文字回复',
      'aiHelper.form.freeTextOnly':               '你的回复',
      'aiHelper.form.freeTextPlaceholder':        '输入你的回复…',
      'aiHelper.win.turnLimit.body':              '本轮工具步骤已达到 10 次上限。代理已暂停——请检查最后一次使用的工具及其结果。是否在此处继续（重置限制）或停止？',
      'aiHelper.win.turnLimit.lastTool':          '最后工具',
      'aiHelper.win.turnLimit.lastResult':        '结果',
      'aiHelper.win.turnLimit.continue':          '继续（重置限制）',
      'aiHelper.win.turnLimit.stop':              '停止',
      'aiHelper.win.turnLimit.stoppedByUser':     '已停止。',
      'aiHelper.copy.title':                      'JustZix AI Helper 对话',
      'aiHelper.copy.toolResult':                 '工具结果',
      'aiHelper.tool.label': '工具',
      'aiHelper.tool.rejected': '用户拒绝了该操作。',
      'aiHelper.confirm.folder': 'AI Helper 想要创建文件夹：',
      'aiHelper.confirm.group': 'AI Helper 想要创建分组：',
      'aiHelper.confirm.rule': 'AI Helper 想要创建规则集：',
      'aiHelper.confirm.create.cssPane': 'AI Helper 想要创建 CSS 窗口：',
      'aiHelper.confirm.create.jsPane': 'AI Helper 想要创建 JS 窗口：',
      'aiHelper.confirm.create.jsConsole': 'AI Helper 想要创建 JS 控制台：',
      'aiHelper.confirm.create.outputConsole': 'AI Helper 想要创建输出控制台：',
      'aiHelper.confirm.setPaneCode': 'AI Helper 想要向窗口 ":name"(:id)插入代码。\n长度：当前 :old 字符，新 :new 字符。\n注意：持久窗口代码按标签页存储（sessionStorage），不跨会话保留。',
      'aiHelper.confirm.bar': 'AI Helper 想要创建操作栏：',
      'aiHelper.confirm.action': 'AI Helper 想要创建操作动作：',
      // === Wyszukiwanie w kodzie (od v2.13.130) ===
      'codeSearch.placeholder': '在代码中搜索…（Ctrl+Alt+F）',
      'codeSearch.btn': '搜索',
      'codeSearch.prev': '上一个匹配（Ctrl+Alt+←）',
      'codeSearch.next': '下一个匹配（Ctrl+Alt+→）',
      'codeSearch.noMatch': '无匹配',
    },
  };

  let currentLang = DEFAULT;
  let userPref = 'auto'; // 'auto' | 'pl' | 'en' — co siedzi w storage.userLang
  let readyResolve;
  const ready = new Promise(r => { readyResolve = r; });

  function detectChromeUiLang() {
    try {
      const ui = chrome.i18n && chrome.i18n.getUILanguage ? chrome.i18n.getUILanguage() : '';
      if (!ui) return DEFAULT;
      const short = String(ui).toLowerCase().split('-')[0];
      return SUPPORTED.includes(short) ? short : DEFAULT;
    } catch (e) { return DEFAULT; }
  }

  function resolve(pref) {
    return (pref && pref !== 'auto' && SUPPORTED.includes(pref)) ? pref : detectChromeUiLang();
  }

  async function init() {
    try {
      const data = await chrome.storage.local.get(['userLang']);
      userPref = data.userLang || 'auto';
    } catch (e) {
      userPref = 'auto';
    }
    currentLang = resolve(userPref);
    readyResolve();
    return currentLang;
  }

  function t(key, vars) {
    const dict = STRINGS[currentLang] || STRINGS[DEFAULT];
    let str = dict[key];
    if (str == null) {
      // Fallback do PL gdy klucz brakuje w bieżącym języku
      str = (STRINGS[DEFAULT] || {})[key];
    }
    if (str == null) return key; // ostatni fallback — pokaż surowy klucz, ułatwia diagnostykę
    if (vars) {
      // Klucze podstawiane malejąco po długości — inaczej podstawienie krótszego
      // klucza (`:rule`) zepsułoby dłuższy o tym samym prefiksie (`:ruleWord`).
      const keys = Object.keys(vars)
        .filter(k => Object.prototype.hasOwnProperty.call(vars, k))
        .sort((a, b) => b.length - a.length);
      for (const k of keys) {
        str = str.split(':' + k).join(String(vars[k]));
      }
    }
    return str;
  }

  async function setLang(pref) {
    if (pref !== 'auto' && !SUPPORTED.includes(pref)) return currentLang;
    userPref = pref;
    currentLang = resolve(userPref);
    try {
      await chrome.storage.local.set({ userLang: pref });
    } catch (e) {}
    return currentLang;
  }

  // Aplikuje tłumaczenia do DOM:
  //   data-i18n="key"        → element.textContent = t(key)
  //   data-i18n-html="key"   → element.innerHTML = t(key) (gdy treść zawiera HTML — używaj ostrożnie)
  //   data-i18n-attr="placeholder:key,title:key2"  → element.setAttribute(attr, t(key))
  function applyToDOM(root) {
    const r = root || document;
    r.querySelectorAll('[data-i18n]').forEach(el => {
      const key = el.getAttribute('data-i18n');
      if (key) el.textContent = t(key);
    });
    r.querySelectorAll('[data-i18n-html]').forEach(el => {
      const key = el.getAttribute('data-i18n-html');
      if (key) el.innerHTML = t(key);
    });
    r.querySelectorAll('[data-i18n-attr]').forEach(el => {
      const spec = el.getAttribute('data-i18n-attr');
      if (!spec) return;
      spec.split(',').forEach(pair => {
        const [attr, key] = pair.split(':').map(s => (s || '').trim());
        if (attr && key) el.setAttribute(attr, t(key));
      });
    });
    if (r === document) {
      try { document.documentElement.lang = currentLang; } catch (e) {}
    }
  }

  // plural(n, baseKey) → t(baseKey + '.' + form)
  // Form to jedna z 'one' | 'few' | 'many' | 'other' (Intl.PluralRules wybiera odpowiednią dla bieżącego języka).
  // Przykład: plural(5, 'plural.rule') zwraca 'zestawów' dla PL, 'rule sets' dla EN.
  function plural(n, baseKey) {
    let form = 'other';
    try {
      form = new Intl.PluralRules(currentLang).select(Number(n));
    } catch (e) {}
    return t(baseKey + '.' + form);
  }

  // Auto-reaguje na zmianę języka w innym kontekście (np. user zmienia język w options,
  // background.js i content scripts dowiadują się przez storage.onChanged i odświeżają lokalny stan).
  try {
    if (chrome.storage && chrome.storage.onChanged) {
      chrome.storage.onChanged.addListener((changes, area) => {
        if (area === 'local' && changes.userLang) {
          userPref = changes.userLang.newValue || 'auto';
          currentLang = resolve(userPref);
          if (typeof _root.dispatchEvent === 'function' && typeof CustomEvent !== 'undefined') {
            try { _root.dispatchEvent(new CustomEvent('jz-lang-changed', { detail: { lang: currentLang, pref: userPref } })); } catch (e) {}
          }
        }
      });
    }
  } catch (e) {}

  return {
    t,
    plural,
    setLang,
    applyToDOM,
    init,
    ready,
    SUPPORTED,
    DEFAULT,
    get lang() { return currentLang; },
    get pref() { return userPref; },
    detectChromeUiLang,
  };
})();
})();
