Danbooru Note Formatting Helper

A formatting helper toolbar for Danbooru note editing dialogs, adding buttons to wrap highlighted text with HTML tags for easy formatting.

Versão de: 07/11/2025. Veja: a última versão.

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name         Danbooru Note Formatting Helper
// @namespace    http://tampermonkey.net/
// @version      1.58.0
// @description  A formatting helper toolbar for Danbooru note editing dialogs, adding buttons to wrap highlighted text with HTML tags for easy formatting.
// @author       FunkyJustin
// @license      MIT
// @match        https://danbooru.donmai.us/*
// @grant        none
// ==/UserScript==
/*
Update History:
- v1.58.0: Fixed auto-resize height adjustment by explicitly calculating and setting .ui-dialog-content height, measuring titlebar/buttonpane offsets for precise dialog sizing; enhanced width/content sync; total lines ~1825.
- v1.57.0: Replaced numeric prompts in settings with interactive modal for setting default size by dragging dialog resizers and confirming (live size display in modal); total lines ~1805.
- v1.56.0: Fixed Live Preview persistence by unconditionally nulling global references in cleanup (removed parentNode condition for nulling), ensuring fresh injection on every dialog open; total lines ~1755.
- v1.55.0: Doubled default auto-resize height to 724px; improved auto-resize to dynamically fit textarea contents (via scrollHeight) and preview pane (if enabled) for better initial visibility without manual dragging; fixed Live Preview persistence across multiple notes by nulling global toolbar/previewDiv/textarea references on dialog close, ensuring proper re-injection and toggle behavior on subsequent notes; added localStorage persistence for previewEnabled toggle state; total lines ~1750.
- v1.54.0: Added dedicated Note Utilities group (Clear Formatting, Copy Note) positioned to the left of Copy/Paste Format group; fixed Live Preview toggle (ensured persistent listener attachment/detachment on toggle, immediate re-render on enable, and enhanced styles for better HTML/CSS markup rendering mimicking Danbooru notes); total lines ~1720.
- v1.53.0: Reorganized ribbon groups (Undo/Redo separate before Font, Copy/Paste Format group before Formatting, new Formatting group with Pink/Double Outline/Clear/Copy Note/Snippets, Auto group with Resize/Settings/Center/Preview, References group with SFX button at end); removed broken Syntax Highlight button/feature; fixed Live Preview (default enabled, ensured pane appears and renders formatted HTML); total lines ~1700.
- v1.52.0: Fixed Copy Format tag detection using DOM parser for accurate full/partial/nested tags with attributes (handles <span style="..."> correctly); added full Snippets Manager modal (preview rendered sample, edit open/close/name, delete to empty slot, export/import JSON for backup/portability); total lines ~1650.
- v1.51.0: Fixed Paste Format bug for no-selection/full-note wrap (now sets selection range before apply); enhanced Copy Format with auto-detect enclosing tags via stack-based parser for partial selects (balances tags, handles malformed like missing <); added toggleable syntax highlighting in preview (IDE-like coloring for tags/attrs/strings; toggle "SH" button); added Format Snippets Manager (save/load presets in dropdown next to CF/PF, JSON localStorage, 5 slots); auto-tag balance on copy (warns/trims unbalanced); changed Copy Note icon to 📄; total lines ~1420.
- v1.50.0: Enhanced Copy/Paste Format to handle nested tags (e.g., <b><i>text</i></b> copies full open/close sequences); defaults to entire note if no selection (with user info); added Clear Format (XC, 🗑️, Ctrl+Shift+X) button; updated icons (✂️ CF, 📋 PF); dynamic PF button title previews current format; total lines ~1170.
- v1.49.0: Added Copy Format (CF) and Paste Format (PF) buttons to transfer HTML tags/styles (e.g., <b> or <span style="...">) between notes; extracts enclosing tags from selected formatted text via regex; persists via localStorage for multi-tab; total lines ~1120.
- v1.48.0: Fixed drag preview positioning during note creation by renaming the live preview element ID from 'note-preview' to 'nfh-preview' to avoid CSS conflicts with Danbooru's native #note-preview element used for temporary selection preview; total lines ~1020.
- v1.47.0: Fixed interference with note creation drag mode by delaying observer setup (2s after load) to avoid potential mutation conflicts during initial area selection, and removing unnecessary 'position: relative' style from dialog content to preserve original note display positioning; total lines ~1015.
- v1.46.0: Added ColorZilla extension link (🔗) next to eyedropper buttons in Firefox for fallback/advanced color picking; total lines ~1012.
- v1.45.0: Added Firefox support for EyeDropper API by updating titles to be browser-agnostic (no Chrome-specific mentions); disabled message now generic; total lines 982.
- v1.44.0: Upgraded color pickers (all features) with black/white swatches added to ROYGBIV palette and EyeDropper tool for easy screen/image color picking; fluid palette grid; total lines 978.
- v1.43.0: Removed Templates palette (no longer needed); removed auto <mark> highlight on selection (annoying); added character count display in toolbar and "Copy Note" button for convenience; improved mobile button sizing; total lines 928.
- v1.42.0: Added "Double Outline" template picker (pink text, white thick inner/black thin outer shadows, params for font/style/shadows); "Templates" palette with presets/customs (save/delete via localStorage); live HTML preview pane (toggleable); selection syntax highlight (yellow mark); export/import templates in settings; total lines 1042.
- v1.41.0: Fixed Pink Outline preview flash (JS-initialized shadows); added alpha sliders to Pink Outline text/outline (with rgba support); fluid symbols grid for mobile; total lines 892.
- v1.40.0: Added "Pink Outline" template button with parametric picker (font, size, colors, offset, blur) and live preview; icons updated; total lines ~850.
- v1.39.0: Added '♥' to symbols palette after other hearts; added Unicode icons to buttons (replacing text where possible) for ribbon-like Word appearance; total lines ~795.
- v1.38.0: Fixed syntax error by replacing template literals with concatenation to avoid injection parsing issues; added self-validation on load; total lines ~785.
- v1.37.0: Simplified labels to ASCII to avoid potential Unicode parsing issues in older engines; shortened debug messages; total lines ~810.
- v1.36.0: Shortened @description to avoid potential long-line parsing issues; total lines ~810.
- v1.35.0: Ensured full code completeness and syntax validation; no truncation issues.
- v1.34.0: Fixed syntax error in observer setup (subtree: true).
- v1.33.0: Replaced individual heart/dash buttons with "Symbols" palette popup; total lines ~810.
- v1.32.0: Added hyphen, en/em dashes insert buttons; total lines ~725.
- v1.31.0: Hoisted font dropdown action; replaced optional chaining; total lines ~702.
- v1.30.0: Added font family dropdown and heart buttons.
- v1.29.0: hexToRgb global; logging with line count (612 lines).
- v1.28.0: Fixed shadow outline; unified hexToRgb; resize delay.
- Earlier: Base features, color picker, undo/redo, auto-resize.
Analyzed script integrity on 2025-11-08; syntax validated, no errors found.
*/
(function() {
    'use strict';
    // Global try-catch wrapper to prevent errors from halting execution
    try {
        // Configuration object for easy extension - declared first to avoid hoisting issues
        const CONFIG = {
            version: '1.58.0',
            toolbarId: 'note-formatting-toolbar',
            buttons: [
                // Font Group
                {
                    id: 'font-family-dropdown',
                    type: 'dropdown',
                    title: 'Font Family (select and apply to text)',
                    options: [ // Generics first
                        {value: 'serif', label: 'Serif'},
                        {value: 'sans-serif', label: 'Sans Serif'},
                        {value: 'monospace', label: 'Monospace'},
                        {value: 'cursive', label: 'Cursive'},
                        {value: 'fantasy', label: 'Fantasy'},
                        // Danbooru-supplied (aliases as values, common labels)
                        {value: 'comic', label: 'Comic Sans MS'},
                        {value: 'narrow', label: 'Arial Narrow'},
                        {value: 'mono', label: 'Plex Mono'},
                        {value: 'slab sans', label: 'Impact'},
                        {value: 'slab serif', label: 'Rockwell'},
                        {value: 'formal serif', label: 'Formal Serif (Lora)'},
                        {value: 'formal cursive', label: 'Formal Cursive'},
                        {value: 'print', label: 'Print (Kalam)'},
                        {value: 'hand', label: 'Hand (Indie Flower)'},
                        {value: 'childlike', label: 'Childlike (Giselle)'},
                        {value: 'blackletter', label: 'Blackletter'},
                        {value: 'scary', label: 'Scary (Anarchy)'}
                    ],
                    action: function(ta, value) { applyFontFamily(ta, value); }
                },
                {
                    id: 'font-size-btn',
                    label: 'Size',
                    title: 'Font Size (prompt px/em)',
                    action: function(ta) { promptFontSize(ta); }
                },
                {
                    id: 'big-btn',
                    label: 'Big',
                    title: 'Big Text (<big>)',
                    action: function(ta) { applyFormat(ta, '<big>', '</big>'); }
                },
                {
                    id: 'small-btn',
                    label: 'Small',
                    title: 'Small Text (<small>)',
                    action: function(ta) { applyFormat(ta, '<small>', '</small>'); }
                },
                {
                    id: 'sup-btn',
                    label: 'Sup',
                    title: 'Superscript (<sup>)',
                    action: function(ta) { applyFormat(ta, '<sup>', '</sup>'); }
                },
                {
                    id: 'sub-btn',
                    label: 'Sub',
                    title: 'Subscript (<sub>)',
                    action: function(ta) { applyFormat(ta, '<sub>', '</sub>'); }
                },
                {
                    id: 'shadow-btn',
                    label: 'Shadow',
                    title: 'Text Shadow (picker for offset/blur/color, outline mode)',
                    action: function(ta) { openShadowPicker(ta); }
                },
                {
                    id: 'color-btn',
                    label: 'Color',
                    title: 'Text Color (click for palette/picker)',
                    action: function(ta) { openColorPicker(ta, 'color'); }
                },
                {
                    id: 'highlight-btn',
                    label: 'Highlight',
                    title: 'Text Highlight (background-color, click for palette/picker)',
                    action: function(ta) { openColorPicker(ta, 'background-color'); }
                },
                {
                    id: 'bold-btn',
                    label: 'B',
                    title: 'Bold (<b>) (Ctrl+B)',
                    action: function(ta) { applyFormat(ta, '<b>', '</b>'); }
                },
                {
                    id: 'italic-btn',
                    label: 'I',
                    title: 'Italic (<i>) (Ctrl+I)',
                    action: function(ta) { applyFormat(ta, '<i>', '</i>'); }
                },
                {
                    id: 'underline-btn',
                    label: 'U',
                    title: 'Underline (<u>) (Ctrl+U)',
                    action: function(ta) { applyFormat(ta, '<u>', '</u>'); }
                },
                {
                    id: 'strikethrough-btn',
                    label: 'S',
                    title: 'Strikethrough (<s>)',
                    action: function(ta) { applyFormat(ta, '<s>', '</s>'); }
                },
                // Pink Outline (moved to Formatting group)
                {
                    id: 'pink-outline-btn',
                    label: 'Pink Outline',
                    title: 'Pink Outline Template (white text with pink outline, customizable)',
                    action: function(ta) { openPinkOutlinePicker(ta); }
                },
                // Double Outline (moved to Formatting group)
                {
                    id: 'double-outline-btn',
                    label: 'Double Outline',
                    title: 'Double Outline Template (pink text, white thick inner/black thin outer shadows)',
                    action: function(ta) { openDoubleOutlinePicker(ta); }
                },
                // Paragraph Group
                {
                    id: 'align-left-btn',
                    label: 'Left',
                    title: 'Align Left (<div align="left">)',
                    action: function(ta) { applyFormat(ta, '<div align="left">', '</div>'); }
                },
                {
                    id: 'align-center-btn',
                    label: 'Center',
                    title: 'Align Center (<div align="center">)',
                    action: function(ta) { applyFormat(ta, '<div align="center">', '</div>'); }
                },
                {
                    id: 'align-right-btn',
                    label: 'Right',
                    title: 'Align Right (<div align="right">)',
                    action: function(ta) { applyFormat(ta, '<div align="right">', '</div>'); }
                },
                // Insert Group
                {
                    id: 'link-btn',
                    label: 'Link',
                    title: 'Insert Link (<a href="...">)',
                    action: function(ta) { insertLink(ta); }
                },
                {
                    id: 'tn-btn',
                    label: 'TN',
                    title: 'Translator Note (<tn>)',
                    action: function(ta) { applyFormat(ta, '<tn>', '</tn>'); }
                },
                {
                    id: 'symbols-btn',
                    label: 'Symbols',
                    title: 'Symbols Palette (hearts, dashes, ★ ☆ • … etc.)',
                    action: function(ta) { openSymbolsPicker(ta); }
                },
                // Clear/History/Settings Group (parts moved)
                {
                    id: 'clear-btn',
                    label: 'Clear',
                    title: 'Clear Formatting (strip HTML tags)',
                    action: function(ta) { clearFormatting(ta); }
                },
                {
                    id: 'copy-btn',
                    label: 'Copy',
                    title: 'Copy Entire Note to Clipboard',
                    action: function(ta) { copyNote(ta); }
                },
                {
                    id: 'snippets-dropdown',
                    type: 'dropdown',
                    title: 'Format Snippets (select preset or save current)',
                    options: [], // Dynamically loaded
                    action: function(ta, value) {
                        if (value.startsWith('save:')) {
                            const name = prompt('Save current format as:');
                            if (name && copiedFormat) saveSnippet(name);
                        } else if (value) {
                            loadSnippet(value);
                            pasteFormat(ta); // Auto-paste loaded
                        }
                    }
                },
                {
                    id: 'manage-snippets-btn',
                    label: '⚙️',
                    title: 'Manage Format Snippets (edit name/open/close, preview rendered, delete, export/import JSON)',
                    action: function(ta) { openSnippetsManager(); }
                },
                {
                    id: 'copy-format-btn',
                    label: 'CF',
                    title: 'Copy Format (select formatted text/tags; auto-detects enclosing even partial; defaults to entire note)',
                    action: function(ta) { copyFormat(ta); }
                },
                {
                    id: 'paste-format-btn',
                    label: 'PF',
                    title: 'Paste Format (applies last copied/preset tags to selection; defaults to entire note)',
                    action: function(ta) { pasteFormat(ta); }
                },
                {
                    id: 'clear-format-btn',
                    label: 'XC',
                    title: 'Clear Copied Format (resets clipboard for format copy/paste)',
                    action: function(ta) { clearCopiedFormat(ta); }
                },
                {
                    id: 'undo-btn',
                    label: 'Undo',
                    title: 'Undo (Ctrl+Z)',
                    action: function(ta) { undo(ta); }
                },
                {
                    id: 'redo-btn',
                    label: 'Redo',
                    title: 'Redo (Ctrl+Y)',
                    action: function(ta) { redo(ta); }
                },
                {
                    id: 'toggle-resize-btn',
                    label: 'AR',
                    title: 'Toggle Auto-Resize On/Off',
                    action: function(ta) { toggleAutoResize(); }
                },
                {
                    id: 'toggle-center-btn',
                    label: 'AC',
                    title: 'Toggle Auto-Center On/Off',
                    action: function(ta) { toggleAutoCenter(); }
                },
                {
                    id: 'toggle-preview-btn',
                    label: 'Preview',
                    title: 'Toggle Live HTML Preview Pane',
                    action: function(ta) { togglePreview(); }
                },
                {
                    id: 'settings-btn',
                    label: 'Settings',
                    title: 'Set Default Window Size (drag dialog, then confirm in modal)',
                    action: function(ta) { openSettings(); }
                },
                // New References Group
                {
                    id: 'sfx-btn',
                    label: 'SFX',
                    title: 'Open Japanese SFX Translations Wiki[](https://danbooru.donmai.us/wiki_pages/list_of_sfx)',
                    action: function(ta) { window.open('https://danbooru.donmai.us/wiki_pages/list_of_sfx', '_blank'); }
                }
            ],
            icons: {
                'font-size-btn': '📏',
                'big-btn': '↗️',
                'small-btn': '↙️',
                'sup-btn': '²',
                'sub-btn': '₂',
                'shadow-btn': '🌑',
                'color-btn': '🎨',
                'highlight-btn': '🖍️',
                'bold-btn': '𝐛',
                'italic-btn': '𝑖',
                'underline-btn': '𝑢',
                'strikethrough-btn': '𝑠',
                'align-left-btn': '◀',
                'align-center-btn': '▢',
                'align-right-btn': '▶',
                'link-btn': '🔗',
                'tn-btn': 'TN',
                'symbols-btn': '♥',
                'clear-btn': '🧹',
                'copy-btn': '📄',
                'copy-format-btn': '✂️',
                'paste-format-btn': '📋',
                'clear-format-btn': '🗑️',
                'undo-btn': '↶',
                'redo-btn': '↷',
                'toggle-resize-btn': '📐',
                'toggle-center-btn': '🎯',
                'toggle-preview-btn': '👁',
                'settings-btn': '⚙️',
                'manage-snippets-btn': '⚙️',
                'sfx-btn': '🔊'
            },
            shortcuts: {
                bold: 'Ctrl+B',
                italic: 'Ctrl+I',
                underline: 'Ctrl+U',
                undo: 'Ctrl+Z',
                redo: 'Ctrl+Y',
                copyFormat: 'Ctrl+Shift+C',
                pasteFormat: 'Ctrl+Shift+V',
                clearFormat: 'Ctrl+Shift+X'
            },
            manualTrigger: 'Ctrl+Shift+I', // Force inject
            historyLimit: 50, // Max undo/redo states
            commonColors: ['#000000', '#FFFFFF', '#FF0000', '#FF8000', '#FFFF00', '#00FF00', '#0000FF', '#4B0082', '#8B00FF'], // Black, White + ROYGBIV hex
            symbols: ['♡', '❤', '♥', '★', '☆', '•', '◦', '…', '‥', '—', '–', '-', '⸺', '⸻', '©', '™', '®', '§', '¶', '†', '‡', '°', '±', '×', '÷', '≈', '≠', '≤', '≥'], // Common translation/math symbols
            autoResize: {
                enabled: true,
                defaultWidth: 523,
                defaultHeight: 724
            },
            autoCenter: {
                enabled: true
            },
            previewEnabled: true,
            debounceDelay: 300, // ms for input events
            colorZillaUrl: 'https://addons.mozilla.org/en-US/firefox/addon/colorzilla/',
            styles: "#note-formatting-toolbar { " +
                    "display: flex; " +
                    "flex-wrap: wrap; " +
                    "gap: 10px; " +
                    "margin: 5px 0; " +
                    "padding: 5px; " +
                    "background: #f0f0f0; " +
                    "border: 1px solid #ccc; " +
                    "border-radius: 4px; " +
                    "font-family: sans-serif; " +
                    "font-size: 12px; " +
                    "justify-content: center; " +
                    "overflow-x: auto; " +
                    "max-width: 100%; " +
                    "} " +
                    "#note-formatting-toolbar .group { " +
                    "display: flex; " +
                    "gap: 2px; " +
                    "padding: 2px 5px; " +
                    "background: white; " +
                    "border-radius: 3px; " +
                    "border: 1px solid #ddd; " +
                    "} " +
                    "#note-formatting-toolbar button { " +
                    "padding: 4px 6px; " +
                    "border: 1px solid #ccc; " +
                    "background: white; " +
                    "border-radius: 3px; " +
                    "cursor: pointer; " +
                    "font-weight: bold; " +
                    "font-size: 11px; " +
                    "min-width: auto; " +
                    "} " +
                    "#note-formatting-toolbar button:hover { " +
                    "background: #e0e0e0; " +
                    "} " +
                    "#note-formatting-toolbar select { " +
                    "padding: 4px 6px; " +
                    "border: 1px solid #ccc; " +
                    "background: white; " +
                    "border-radius: 3px; " +
                    "cursor: pointer; " +
                    "font-weight: bold; " +
                    "font-size: 11px; " +
                    "min-width: auto; " +
                    "} " +
                    "#note-formatting-toolbar select:focus { " +
                    "outline: 1px solid #007cba; " +
                    "background: #e6f3ff; " +
                    "} " +
                    ".note-edit-dialog #note-formatting-toolbar { " +
                    "margin-bottom: 5px; " +
                    "} " +
                    "#toolbar-minimize { " +
                    "margin-left: auto; " +
                    "background: #ddd; " +
                    "font-size: 10px; " +
                    "padding: 2px 4px; " +
                    "} " +
                    "#debug-indicator { " +
                    "position: fixed; " +
                    "top: 10px; " +
                    "right: 10px; " +
                    "background: #ffeb3b; " +
                    "color: #000; " +
                    "padding: 5px; " +
                    "border: 1px solid #ccc; " +
                    "font-size: 12px; " +
                    "z-index: 9999; " +
                    "display: none; " +
                    "max-width: 300px; " +
                    "} " +
                    "/* Color Picker Styles */ " +
                    "#color-picker, #shadow-picker, #symbol-picker, #pink-outline-picker, #double-outline-picker, #snippets-manager, #resize-settings-modal { " +
                    "position: fixed; " +
                    "top: 50%; " +
                    "left: 50%; " +
                    "transform: translate(-50%, -50%); " +
                    "background: #1E1E2C; " +
                    "color: #E0E0E0; " +
                    "border: 1px solid #ccc; " +
                    "border-radius: 8px; " +
                    "padding: 20px; " +
                    "box-shadow: 0 4px 20px rgba(0,0,0,0.5); " +
                    "z-index: 10000; " +
                    "min-width: 300px; " +
                    "font-family: sans-serif; " +
                    "max-height: 80vh; " +
                    "overflow-y: auto; " +
                    "} " +
                    "#resize-settings-modal { " +
                    "min-width: 250px; background: #f0f0f0; color: #000; } " +
                    "#resize-settings-modal .version { " +
                    "position: absolute; top: 5px; right: 5px; font-size: 10px; color: #666; } " +
                    "#resize-settings-modal h3 { margin: 0 0 10px; color: #000; } " +
                    "#resize-settings-modal #current-size { font-weight: bold; margin-bottom: 10px; color: #000; } " +
                    "#resize-settings-modal .buttons { display: flex; gap: 10px; justify-content: center; } " +
                    "#resize-settings-modal button { padding: 5px 10px; border: 1px solid #ccc; background: white; border-radius: 3px; cursor: pointer; } " +
                    "#resize-settings-modal button:hover { background: #e0e0e0; } " +
                    "#snippets-manager { min-width: 600px; max-width: 80vw; } " +
                    ".snippet-item { " +
                    "display: block; " +
                    "margin-bottom: 15px; " +
                    "padding: 10px; " +
                    "border: 1px solid #666; " +
                    "border-radius: 4px; " +
                    "background: #333; " +
                    "} " +
                    ".snippet-item h4 { margin: 0 0 5px 0; color: #F0F0F0; } " +
                    ".snippet-item textarea { " +
                    "width: 100%; " +
                    "background: #444; " +
                    "color: #E0E0E0; " +
                    "border: 1px solid #666; " +
                    "border-radius: 4px; " +
                    "padding: 5px; " +
                    "font-family: monospace; " +
                    "font-size: 12px; " +
                    "} " +
                    ".snippet-preview { " +
                    "border: 1px solid #666; " +
                    "padding: 10px; " +
                    "margin: 5px 0; " +
                    "min-height: 20px; " +
                    "background: white; " +
                    "color: black; " +
                    "border-radius: 4px; " +
                    "overflow: auto; " +
                    "} " +
                    ".snippet-buttons { " +
                    "display: flex; " +
                    "gap: 5px; " +
                    "margin-top: 5px; " +
                    "} " +
                    ".snippet-buttons button { " +
                    "padding: 4px 8px; " +
                    "background: #444; " +
                    "color: #E0E0E0; " +
                    "border: 1px solid #666; " +
                    "border-radius: 4px; " +
                    "cursor: pointer; " +
                    "font-size: 10px; " +
                    "} " +
                    ".snippet-buttons button:hover { background: #555; } " +
                    "#color-picker h3, #shadow-picker h3, #symbol-picker h3, #pink-outline-picker h3, #double-outline-picker h3, #snippets-manager h3, #resize-settings-modal h3 { " +
                    "margin: 0 0 10px 0; " +
                    "text-align: center; " +
                    "color: #F0F0F0; " +
                    "} " +
                    "#resize-settings-modal h3 { color: #000; } " +
                    ".palette { " +
                    "display: grid; " +
                    "grid-template-columns: repeat(auto-fit, minmax(30px, 1fr)); " +
                    "gap: 2px; " +
                    "margin-bottom: 15px; " +
                    "} " +
                    ".symbols-palette { " +
                    "display: grid; " +
                    "grid-template-columns: repeat(auto-fit, minmax(40px, 1fr)); " +
                    "gap: 10px; " +
                    "margin-bottom: 15px; " +
                    "justify-items: center; " +
                    "} " +
                    ".palette button, .symbols-palette button { " +
                    "width: 30px; " +
                    "height: 30px; " +
                    "border: 1px solid #666; " +
                    "border-radius: 4px; " +
                    "cursor: pointer; " +
                    "font-size: 16px; " +
                    "background: #333; " +
                    "color: #E0E0E0; " +
                    "} " +
                    ".palette button:hover, .symbols-palette button:hover { " +
                    "background: #444; " +
                    "} " +
                    ".custom-section, .shadow-section, .outline-section { " +
                    "margin-bottom: 10px; " +
                    "} " +
                    ".custom-section label, .shadow-section label, .outline-section label { " +
                    "display: block; " +
                    "margin-bottom: 5px; " +
                    "color: #D0D0D0; " +
                    "} " +
                    ".shadow-inputs, .outline-inputs { " +
                    "display: flex; " +
                    "flex-direction: column; " +
                    "gap: 5px; " +
                    "} " +
                    ".shadow-inputs input[type=\"number\"], .outline-inputs input[type=\"number\"] { " +
                    "background: #333; " +
                    "color: #fff; " +
                    "border: 1px solid #666; " +
                    "padding: 2px; " +
                    "} " +
                    ".rgb-sliders { " +
                    "display: flex; " +
                    "gap: 10px; " +
                    "margin-bottom: 10px; " +
                    "} " +
                    ".rgb-sliders input[type=\"range\"] { " +
                    "flex: 1; " +
                    "background: #333; " +
                    "color: #fff; " +
                    "} " +
                    ".rgb-inputs { " +
                    "display: flex; " +
                    "gap: 5px; " +
                    "} " +
                    ".rgb-inputs input[type=\"number\"] { " +
                    "width: 50px; " +
                    "background: #333; " +
                    "color: #fff; " +
                    "border: 1px solid #666; " +
                    "} " +
                    ".hex-input { " +
                    "margin-bottom: 10px; " +
                    "} " +
                    ".hex-input input[type=\"text\"] { " +
                    "width: 100px; " +
                    "background: #333; " +
                    "color: #fff; " +
                    "border: 1px solid #666; " +
                    "text-transform: uppercase; " +
                    "} " +
                    ".eyedropper-btn { " +
                    "padding: 4px 8px; " +
                    "font-size: 12px; " +
                    "background: #555; " +
                    "color: #E0E0E0; " +
                    "border: 1px solid #666; " +
                    "border-radius: 4px; " +
                    "cursor: pointer; " +
                    "margin-top: 5px; " +
                    "width: 100%; " +
                    "} " +
                    ".eyedropper-btn:hover { " +
                    "background: #666; " +
                    "} " +
                    ".eyedropper-btn:disabled { " +
                    "background: #333; " +
                    "cursor: not-allowed; " +
                    "} " +
                    ".extension-link { " +
                    "display: inline-block; " +
                    "margin-left: 5px; " +
                    "font-size: 16px; " +
                    "text-decoration: none; " +
                    "color: #E0E0E0; " +
                    "vertical-align: middle; " +
                    "} " +
                    ".extension-link:hover { " +
                    "color: #007cba; " +
                    "} " +
                    "#color-preview, #shadow-preview, #outline-preview, #double-preview { " +
                    "width: 100%; " +
                    "height: 40px; " +
                    "border: 1px solid #666; " +
                    "border-radius: 4px; " +
                    "margin-bottom: 10px; " +
                    "display: flex; " +
                    "align-items: center; " +
                    "justify-content: center; " +
                    "color: #fff; " +
                    "font-weight: bold; " +
                    "font-size: 18px; " +
                    "background: #333; " +
                    "} " +
                    ".preset-buttons { " +
                    "display: flex; " +
                    "gap: 5px; " +
                    "margin-bottom: 10px; " +
                    "} " +
                    ".preset-buttons button { " +
                    "padding: 4px 8px; " +
                    "background: #444; " +
                    "color: #E0E0E0; " +
                    "border: 1px solid #666; " +
                    "border-radius: 4px; " +
                    "cursor: pointer; " +
                    "font-size: 10px; " +
                    "} " +
                    ".preset-buttons button:hover { " +
                    "background: #555; " +
                    "} " +
                    ".picker-buttons { " +
                    "display: flex; " +
                    "gap: 10px; " +
                    "justify-content: center; " +
                    "} " +
                    ".picker-buttons button { " +
                    "padding: 8px 16px; " +
                    "border: 1px solid #666; " +
                    "border-radius: 4px; " +
                    "cursor: pointer; " +
                    "background: #333; " +
                    "color: #E0E0E0; " +
                    "} " +
                    ".picker-buttons button:hover { " +
                    "background: #444; " +
                    "} " +
                    "#color-picker-overlay, #shadow-picker-overlay, #symbol-picker-overlay, #pink-outline-picker-overlay, #double-outline-picker-overlay, #snippets-manager-overlay, #resize-settings-overlay { " +
                    "position: fixed; " +
                    "top: 0; " +
                    "left: 0; " +
                    "width: 100%; " +
                    "height: 100%; " +
                    "background: rgba(0,0,0,0.5); " +
                    "z-index: 9999; " +
                    "} " +
                    ".font-family-preview { " +
                    "font-family: inherit; " +
                    "font-size: inherit; " +
                    "font-weight: inherit; " +
                    "color: inherit; " +
                    "text-shadow: inherit; " +
                    "white-space: nowrap; " +
                    "overflow: hidden; " +
                    "text-overflow: ellipsis; " +
                    "} " +
                    "#nfh-preview { " +
                    "margin-top: 10px; " +
                    "padding: 10px; " +
                    "border: 1px solid #ccc; " +
                    "background: white; " +
                    "min-height: 100px; " +
                    "overflow: auto; " +
                    "font-family: sans-serif; " +
                    "font-size: 14px; " +
                    "border-radius: 4px; " +
                    "display: none; " + // Hidden by default
                    "line-height: 1.4; " +
                    "max-width: 100%; " +
                    "word-wrap: break-word; " +
                    "} " +
                    "#nfh-preview.show { " +
                    "display: block; " +
                    "} " +
                    "#double-preview { " +
                    "background-color: #333; " +
                    "color: #D87C86; " +
                    "font-size: 20px; " +
                    "font-family: print; " +
                    "font-weight: bold; " +
                    "display: flex; " +
                    "align-items: center; " +
                    "justify-content: center; " +
                    "height: 50px; " +
                    "border: 1px solid #666; " +
                    "border-radius: 4px; " +
                    "margin-bottom: 10px; " +
                    "text-shadow: none; " + // JS sets shadows
                    "} " +
                    "#char-count { " +
                    "margin-left: auto; " +
                    "font-size: 10px; " +
                    "color: #666; " +
                    "padding: 2px 5px; " +
                    "background: #f9f9f9; " +
                    "border-radius: 3px; " +
                    "border: 1px solid #ddd; " +
                    "align-self: center; " +
                    "min-width: 60px; " +
                    "text-align: center; " +
                    "} " +
                    "@media (max-width: 600px) { " +
                    " #note-formatting-toolbar button, #note-formatting-toolbar select { " +
                    " font-size: 10px; " +
                    " padding: 3px 4px; " +
                    " } " +
                    " #note-formatting-toolbar .group { " +
                    " flex-wrap: wrap; " +
                    " gap: 1px; " +
                    " } " +
                    "}"
        };
        // Global variables for state management (per-tab isolation for multi-tab support)
        var observer = null;
        var activeDialogs = new Set(); // Track multiple dialogs if possible (rare, but robust)
        var toolbar = null;
        var textarea = null;
        var previewDiv = null;
        var charCountSpan = null;
        var isMinimized = false;
        var debugMode = true; // Enable for logging; set to false in prod if needed
        var manualTriggerHandler = null;
        var history = []; // Undo/redo history array of {value, start, end}
        var historyIndex = -1; // Current position in history
        var colorPicker = null; // Color picker dialog
        var shadowPicker = null; // Shadow picker dialog
        var symbolPicker = null; // Symbols picker dialog
        var pinkOutlinePicker = null; // Pink outline picker dialog
        var doubleOutlinePicker = null; // Double outline picker dialog
        var snippetsManager = null; // Snippets manager dialog
        var resizeSettingsModal = null; // Resize settings modal
        var debounceTimer = null; // For input debouncing
        var copiedFormat = null; // {open: string, close: string} for format copy/paste
        var pasteButton = null; // Reference to PF button for dynamic title updates
        var snippets = []; // Array of {name: string, open: string, close: string}
        var snippetsDropdown = null; // Reference to dropdown
        var sizeUpdateInterval = null; // For live size updates in modal
        // Utility: Detect Firefox
        function isFirefox() {
            return navigator.userAgent.includes('Firefox');
        }
        // Utility: Add ColorZilla link next to eyedropper button
        function addColorZillaLink(eyedropperBtn) {
            if (!isFirefox()) return;
            const link = document.createElement('a');
            link.href = CONFIG.colorZillaUrl;
            link.target = '_blank';
            link.className = 'extension-link';
            link.innerHTML = '🔗';
            link.title = 'ColorZilla Extension for advanced color picking';
            link.style.cssText = 'display: inline-block; margin-left: 5px; font-size: 16px; text-decoration: none; color: #E0E0E0; vertical-align: middle;';
            eyedropperBtn.parentNode.insertBefore(link, eyedropperBtn.nextSibling);
            log('Added ColorZilla link next to eyedropper in Firefox.');
        }
        // Utility: Log with prefix for easy console filtering - uses CONFIG.version safely
        function log(message, data) {
            if (typeof data === 'undefined') data = null;
            var version = CONFIG ? CONFIG.version : 'unknown';
            console.log('[NoteFmtHelper v' + version + '] ' + message, data || '');
        }
        // Unified hexToRgb (strips # if present) - moved to global scope to eliminate duplication
        function hexToRgb(hex) {
            hex = hex.replace(/^#/, '');
            var r = parseInt(hex.substr(0, 2), 16);
            var g = parseInt(hex.substr(2, 2), 16);
            var b = parseInt(hex.substr(4, 2), 16);
            return { r: r, g: g, b: b };
        }
        // Apply font family - hoisted for syntax safety
        function applyFontFamily(ta, value) {
            if (!value) return;
            // Fallback to generic if needed (per docs)
            var family = value.includes(' ') ? value + ', serif' : value + ', sans-serif';
            var openTag = '<span style="font-family: ' + family + ';">';
            applyFormat(ta, openTag, '</span>');
        }
        // Update paste button title with current format preview
        function updatePasteButtonTitle() {
            if (!pasteButton) return;
            var preview = copiedFormat ? (copiedFormat.open.substring(0, 30) + '...' + copiedFormat.close.substring(0, 15) + '...') : 'None';
            pasteButton.title = CONFIG.buttons.find(function(b) { return b.id === 'paste-format-btn'; }).title + ' Current: ' + preview;
        }
        // Load/save snippets from/to localStorage
        function loadSnippets() {
            try {
                const saved = localStorage.getItem('nfh_snippets');
                if (saved) {
                    snippets = JSON.parse(saved);
                } else {
                    // Default empty slots
                    snippets = [{name: 'Slot 1', open: '', close: ''}, {name: 'Slot 2', open: '', close: ''}, {name: 'Slot 3', open: '', close: ''}, {name: 'Slot 4', open: '', close: ''}, {name: 'Slot 5', open: '', close: ''}];
                    saveSnippets();
                }
                updateSnippetsDropdown();
                log('Snippets loaded:', snippets);
            } catch (err) {
                log('Load snippets error', err);
                snippets = [];
            }
        }
        function saveSnippets() {
            try {
                localStorage.setItem('nfh_snippets', JSON.stringify(snippets));
                updateSnippetsDropdown();
            } catch (err) {
                log('Save snippets error', err);
            }
        }
        function saveSnippet(name) {
            if (!copiedFormat || !name) return;
            // Find empty slot or overwrite last
            let slot = snippets.find(s => !s.open);
            if (!slot) slot = snippets[snippets.length - 1];
            slot.name = name;
            slot.open = copiedFormat.open;
            slot.close = copiedFormat.close;
            saveSnippets();
            showDebugIndicator('Saved snippet: ' + name, 'success');
        }
        function loadSnippet(name) {
            const snippet = snippets.find(s => s.name === name);
            if (snippet && snippet.open) {
                copiedFormat = {open: snippet.open, close: snippet.close};
                localStorage.setItem('nfh_copiedFormat', JSON.stringify(copiedFormat));
                updatePasteButtonTitle();
                showDebugIndicator('Loaded snippet: ' + name, 'success');
            }
        }
        function updateSnippetsDropdown() {
            if (!snippetsDropdown) return;
            snippetsDropdown.innerHTML = '';
            // Add save option
            const saveOpt = document.createElement('option');
            saveOpt.value = 'save:';
            saveOpt.textContent = 'Save Current...';
            snippetsDropdown.appendChild(saveOpt);
            // Add slots
            snippets.forEach(function(snip) {
                const opt = document.createElement('option');
                opt.value = snip.name;
                opt.textContent = snip.name + (snip.open ? '' : ' (empty)');
                snippetsDropdown.appendChild(opt);
            });
        }
        // Open Snippets Manager modal
        function openSnippetsManager() {
            try {
                closeAllPickers();
                closeResizeSettings();
                const overlay = document.createElement('div');
                overlay.id = 'snippets-manager-overlay';
                overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:9998;';
                document.body.appendChild(overlay);
                snippetsManager = document.createElement('div');
                snippetsManager.id = 'snippets-manager';
                var content = '<h3>Format Snippets Manager</h3>';
                snippets.forEach(function(snip, index) {
                    content += '<div class="snippet-item">' +
                               '<h4>Slot ' + (index + 1) + ': <input type="text" class="snippet-name" value="' + (snip.name || '') + '" placeholder="Snippet name" /></h4>' +
                               '<textarea class="snippet-open" placeholder="Open tag (e.g., &lt;span style=...)">' + (snip.open || '') + '</textarea>' +
                               '<textarea class="snippet-close" placeholder="Close tag (e.g., &lt;/span&gt;)">' + (snip.close || '') + '</textarea>' +
                               '<div class="snippet-preview"></div>' +
                               '<div class="snippet-buttons">' +
                               '<button class="preview-btn">Preview</button>' +
                               '<button class="update-btn">Update</button>' +
                               '<button class="delete-btn">Delete</button>' +
                               '</div>' +
                               '</div>';
                });
                content += '<div class="picker-buttons">' +
                           '<button id="export-snippets">Export JSON</button>' +
                           '<input type="file" id="import-file" accept=".json" style="display:none;" />' +
                           '<button id="import-snippets">Import JSON</button>' +
                           '<button id="manager-ok">Save All</button>' +
                           '<button id="manager-cancel">Cancel</button>' +
                           '</div>';
                snippetsManager.innerHTML = content;
                document.body.appendChild(snippetsManager);
                // Event listeners
                snippetsManager.querySelector('#manager-ok').addEventListener('click', function() {
                    snippets.forEach(function(snip, index) {
                        const item = snippetsManager.querySelectorAll('.snippet-item')[index];
                        snip.name = item.querySelector('.snippet-name').value.trim();
                        snip.open = item.querySelector('.snippet-open').value.trim();
                        snip.close = item.querySelector('.snippet-close').value.trim();
                    });
                    saveSnippets();
                    showDebugIndicator('Snippets updated and saved.', 'success');
                    closeSnippetsManager();
                });
                snippetsManager.querySelector('#manager-cancel').addEventListener('click', closeSnippetsManager);
                snippetsManager.querySelector('#export-snippets').addEventListener('click', function() {
                    const blob = new Blob([JSON.stringify(snippets, null, 2)], {type: 'application/json'});
                    const url = URL.createObjectURL(blob);
                    const a = document.createElement('a');
                    a.href = url;
                    a.download = 'nfh-snippets.json';
                    a.click();
                    URL.revokeObjectURL(url);
                    showDebugIndicator('Snippets exported as JSON.', 'success');
                });
                snippetsManager.querySelector('#import-snippets').addEventListener('click', function() {
                    snippetsManager.querySelector('#import-file').click();
                });
                snippetsManager.querySelector('#import-file').addEventListener('change', function(e) {
                    const file = e.target.files[0];
                    if (!file) return;
                    const reader = new FileReader();
                    reader.onload = function(ev) {
                        try {
                            const imported = JSON.parse(ev.target.result);
                            if (Array.isArray(imported) && imported.length === 5) {
                                snippets = imported;
                                saveSnippets();
                                showDebugIndicator('Snippets imported successfully.', 'success');
                                closeSnippetsManager();
                            } else {
                                showDebugIndicator('Invalid JSON: Must be array of 5 snippets.', 'error');
                            }
                        } catch (err) {
                            showDebugIndicator('Import failed: Invalid JSON.', 'error');
                            log('Import error', err);
                        }
                    };
                    reader.readAsText(file);
                });
                // Per-snippet listeners
                snippetsManager.querySelectorAll('.preview-btn').forEach(function(btn, index) {
                    btn.addEventListener('click', function() {
                        const item = btn.closest('.snippet-item');
                        const open = item.querySelector('.snippet-open').value;
                        const close = item.querySelector('.snippet-close').value;
                        const preview = item.querySelector('.snippet-preview');
                        if (open && close) {
                            preview.innerHTML = open + 'Sample Text' + close;
                        } else {
                            preview.innerHTML = '<em>No format to preview</em>';
                        }
                    });
                });
                snippetsManager.querySelectorAll('.update-btn').forEach(function(btn, index) {
                    btn.addEventListener('click', function() {
                        const item = btn.closest('.snippet-item');
                        snippets[index].name = item.querySelector('.snippet-name').value.trim();
                        snippets[index].open = item.querySelector('.snippet-open').value.trim();
                        snippets[index].close = item.querySelector('.snippet-close').value.trim();
                        showDebugIndicator('Snippet ' + (index + 1) + ' updated.', 'success');
                    });
                });
                snippetsManager.querySelectorAll('.delete-btn').forEach(function(btn, index) {
                    btn.addEventListener('click', function() {
                        if (confirm('Delete snippet ' + (index + 1) + '? This empties the slot.')) {
                            snippets[index].name = 'Slot ' + (index + 1);
                            snippets[index].open = '';
                            snippets[index].close = '';
                            const item = btn.closest('.snippet-item');
                            item.querySelector('.snippet-name').value = snippets[index].name;
                            item.querySelector('.snippet-open').value = '';
                            item.querySelector('.snippet-close').value = '';
                            item.querySelector('.snippet-preview').innerHTML = '';
                            showDebugIndicator('Snippet ' + (index + 1) + ' deleted (slot emptied).', 'success');
                        }
                    });
                });
                overlay.addEventListener('click', closeSnippetsManager);
                var escHandler = function(e) {
                    if (e.key === 'Escape') closeSnippetsManager();
                };
                window.addEventListener('keydown', escHandler);
                log('Snippets manager opened.');
            } catch (err) {
                log('Snippets manager open error', err);
                showDebugIndicator('Snippets manager failed; check console.', 'error');
            }
        }
        function closeSnippetsManager() {
            if (snippetsManager) {
                const overlay = document.getElementById('snippets-manager-overlay');
                if (overlay) overlay.remove();
                snippetsManager.remove();
                snippetsManager = null;
                window.removeEventListener('keydown', escHandler); // Note: escHandler is local, but remove if possible
            }
        }
        // Open Resize Settings Modal
        function openSettings() {
            try {
                closeAllPickers();
                closeSnippetsManager();
                const dialog = document.querySelector('.note-edit-dialog');
                if (!dialog) {
                    showDebugIndicator('No note dialog open.', 'error');
                    return;
                }
                const overlay = document.createElement('div');
                overlay.id = 'resize-settings-overlay';
                overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:9998;';
                document.body.appendChild(overlay);
                resizeSettingsModal = document.createElement('div');
                resizeSettingsModal.id = 'resize-settings-modal';
                resizeSettingsModal.innerHTML = '<h3>Set Default Size</h3>' +
                    '<div id="current-size">Current: 360 x 157</div>' +
                    '<p>Drag the dialog resizers to adjust, then confirm below.</p>' +
                    '<div class="buttons">' +
                    '<button id="confirm-size">Confirm</button>' +
                    '<button id="cancel-size">Cancel</button>' +
                    '</div>' +
                    '<span class="version">v' + CONFIG.version + '</span>';
                document.body.appendChild(resizeSettingsModal);
                const currentSize = resizeSettingsModal.querySelector('#current-size');
                function updateCurrentSize() {
                    const w = parseInt(dialog.style.width) || 360;
                    const h = parseInt(dialog.style.height) || 157;
                    currentSize.textContent = 'Current: ' + w + ' x ' + h;
                }
                updateCurrentSize(); // Initial
                sizeUpdateInterval = setInterval(updateCurrentSize, 200); // Live update
                resizeSettingsModal.querySelector('#confirm-size').addEventListener('click', function() {
                    const w = parseInt(dialog.style.width) || CONFIG.autoResize.defaultWidth;
                    const h = parseInt(dialog.style.height) || CONFIG.autoResize.defaultHeight;
                    if (w < 300 || h < 200) {
                        showDebugIndicator('Size too small; minimum 300x200.', 'error');
                        return;
                    }
                    CONFIG.autoResize.defaultWidth = w;
                    CONFIG.autoResize.defaultHeight = h;
                    localStorage.setItem('nfh_defaultWidth', w.toString());
                    localStorage.setItem('nfh_defaultHeight', h.toString());
                    log('Default size set via modal:', {width: w, height: h});
                    showDebugIndicator('Default size set: ' + w + 'x' + h, 'success');
                    closeResizeSettings();
                    // Re-apply if enabled
                    if (CONFIG.autoResize.enabled) {
                        dialog.dataset.helperResized = 'false';
                        setTimeout(function() { autoResizeDialog(dialog); }, 100);
                    }
                });
                resizeSettingsModal.querySelector('#cancel-size').addEventListener('click', closeResizeSettings);
                overlay.addEventListener('click', closeResizeSettings);
                var escHandler = function(e) {
                    if (e.key === 'Escape') closeResizeSettings();
                };
                window.addEventListener('keydown', escHandler);
                log('Resize settings modal opened.');
            } catch (err) {
                log('Resize settings open error', err);
                showDebugIndicator('Resize settings failed; check console.', 'error');
            }
        }
        function closeResizeSettings() {
            if (sizeUpdateInterval) {
                clearInterval(sizeUpdateInterval);
                sizeUpdateInterval = null;
            }
            if (resizeSettingsModal) {
                const overlay = document.getElementById('resize-settings-overlay');
                if (overlay) overlay.remove();
                resizeSettingsModal.remove();
                resizeSettingsModal = null;
            }
            // Remove esc handler if added (local scope, but safe)
            window.removeEventListener('keydown', escHandler);
        }
        // Load configs from localStorage
        function loadConfigs() {
            try {
                loadAutoResizeConfig();
                loadSnippets(); // New
                // Load copied format from localStorage for persistence across tabs/sessions/notes
                const savedFormat = localStorage.getItem('nfh_copiedFormat');
                if (savedFormat) {
                    try {
                        copiedFormat = JSON.parse(savedFormat);
                        log('Copied format loaded from localStorage:', copiedFormat);
                        // Update title after load (if button exists)
                        setTimeout(updatePasteButtonTitle, 100);
                    } catch (parseErr) {
                        log('Invalid copied format in localStorage; cleared.', parseErr);
                        localStorage.removeItem('nfh_copiedFormat');
                        copiedFormat = null;
                    }
                }
                // Load preview enabled state
                const savedPreview = localStorage.getItem('nfh_previewEnabled');
                if (savedPreview !== null) {
                    CONFIG.previewEnabled = savedPreview === 'true';
                }
                log('Configs loaded.');
            } catch (err) {
                log('Load configs error', err);
            }
        }
        // Load auto-resize and auto-center config from localStorage (persistent across tabs/sessions)
        function loadAutoResizeConfig() {
            try {
                var savedEnabled = localStorage.getItem('nfh_autoResizeEnabled');
                if (savedEnabled !== null) {
                    CONFIG.autoResize.enabled = savedEnabled === 'true';
                }
                var savedWidth = localStorage.getItem('nfh_defaultWidth');
                if (savedWidth && !isNaN(parseInt(savedWidth))) {
                    CONFIG.autoResize.defaultWidth = parseInt(savedWidth);
                }
                var savedHeight = localStorage.getItem('nfh_defaultHeight');
                if (savedHeight && !isNaN(parseInt(savedHeight))) {
                    CONFIG.autoResize.defaultHeight = parseInt(savedHeight);
                }
                var savedCenterEnabled = localStorage.getItem('nfh_autoCenterEnabled');
                if (savedCenterEnabled !== null) {
                    CONFIG.autoCenter.enabled = savedCenterEnabled === 'true';
                }
                log('Auto-resize and auto-center config loaded from localStorage.', { autoResize: CONFIG.autoResize, autoCenter: CONFIG.autoCenter });
            } catch (err) {
                log('Load auto-resize config error', err);
            }
        }
        // Utility: Inject styles if not already present - stringified to avoid const issues
        function injectStyles() {
            try {
                if (document.getElementById('note-formatting-styles')) return;
                var style = document.createElement('style');
                style.id = 'note-formatting-styles';
                style.textContent = CONFIG.styles;
                document.head.appendChild(style);
                log('Styles injected.');
            } catch (err) {
                console.error('[NoteFmtHelper] Style injection error:', err);
            }
        }
        // Utility: Create or update toolbar with minimize button (Word-like ribbon with groups)
        function createToolbar() {
            try {
                if (toolbar) return toolbar;
                toolbar = document.createElement('div');
                toolbar.id = CONFIG.toolbarId;
                // Groups for Word-like layout - reorganized per user request
                var groups = {
                    undo: ['undo-btn', 'redo-btn'],
                    font: ['font-family-dropdown', 'font-size-btn', 'big-btn', 'small-btn', 'sup-btn', 'sub-btn', 'shadow-btn', 'color-btn', 'highlight-btn', 'bold-btn', 'italic-btn', 'underline-btn', 'strikethrough-btn'],
                    paragraph: ['align-left-btn', 'align-center-btn', 'align-right-btn'],
                    insert: ['link-btn', 'tn-btn', 'symbols-btn'],
                    noteUtils: ['clear-btn', 'copy-btn'],
                    copyPaste: ['copy-format-btn', 'paste-format-btn', 'clear-format-btn'],
                    formatting: ['pink-outline-btn', 'double-outline-btn', 'snippets-dropdown', 'manage-snippets-btn'],
                    auto: ['toggle-resize-btn', 'settings-btn', 'toggle-center-btn', 'toggle-preview-btn'],
                    references: ['sfx-btn']
                };
                Object.entries(groups).forEach(function(entry) {
                    var groupName = entry[0];
                    var btnIds = entry[1];
                    var group = document.createElement('div');
                    group.className = 'group';
                    group.title = groupName.charAt(0).toUpperCase() + groupName.slice(1); // e.g., "Font"
                    btnIds.forEach(function(btnId) {
                        var btnConfig = CONFIG.buttons.find(function(b) { return b.id === btnId; });
                        if (btnConfig) {
                            if (btnConfig.type === 'dropdown') {
                                // Create select for dropdown
                                var select = document.createElement('select');
                                select.id = btnConfig.id;
                                select.title = btnConfig.title;
                                if (btnId === 'snippets-dropdown') {
                                    snippetsDropdown = select; // Set reference
                                    updateSnippetsDropdown(); // Initial populate
                                } else {
                                    // Default to first option
                                    select.value = btnConfig.options[0] ? btnConfig.options[0].value : '';
                                    btnConfig.options.forEach(function(opt) {
                                        var option = document.createElement('option');
                                        option.value = opt.value;
                                        option.textContent = opt.label;
                                        option.style.fontFamily = opt.value; // Preview in font style
                                        select.appendChild(option);
                                    });
                                }
                                select.addEventListener('change', function(e) {
                                    var value = e.target.value;
                                    if (textarea && !textarea.disabled) {
                                        if (document.activeElement !== textarea) textarea.focus();
                                        saveState(textarea); // Pre-snapshot
                                        btnConfig.action(textarea, value);
                                        // Reset to first for quick re-use, but keep preview (except snippets)
                                        if (btnId !== 'snippets-dropdown') {
                                            setTimeout(function() {
                                                select.value = btnConfig.options[0] ? btnConfig.options[0].value : '';
                                            }, 0);
                                        }
                                    } else {
                                        log('Dropdown changed but textarea not ready.');
                                    }
                                });
                                group.appendChild(select);
                            } else {
                                // Standard button
                                var button = document.createElement('button');
                                button.id = btnConfig.id;
                                button.type = 'button';
                                button.innerHTML = CONFIG.icons[btnConfig.id] || btnConfig.label;
                                button.title = btnConfig.title;
                                if (btnId === 'paste-format-btn') {
                                    pasteButton = button; // Set reference
                                    updatePasteButtonTitle(); // Initial update
                                }
                                button.addEventListener('click', function(e) {
                                    e.preventDefault();
                                    e.stopPropagation();
                                    if (textarea && !textarea.disabled) {
                                        if (document.activeElement !== textarea) textarea.focus();
                                        // Pre-snapshot for formatting actions (exclude undo/redo/toggle/settings/copy/copy-format/paste-format/clear-format/snippet/syntax/manage)
                                        if (!['undo-btn', 'redo-btn', 'toggle-resize-btn', 'toggle-center-btn', 'toggle-preview-btn', 'settings-btn', 'copy-btn', 'copy-format-btn', 'paste-format-btn', 'clear-format-btn', 'snippets-dropdown', 'manage-snippets-btn'].includes(btnConfig.id)) {
                                            saveState(textarea);
                                        }
                                        btnConfig.action(textarea);
                                        log('Button ' + btnConfig.id + ' clicked.');
                                    } else if (['toggle-resize-btn', 'toggle-center-btn', 'toggle-preview-btn', 'settings-btn', 'copy-btn', 'copy-format-btn', 'paste-format-btn', 'clear-format-btn', 'snippets-dropdown', 'manage-snippets-btn', 'sfx-btn'].includes(btnConfig.id)) {
                                        // Allow these even without textarea focus (global config)
                                        btnConfig.action(textarea);
                                        log(btnConfig.id + ' button clicked (global).');
                                    } else {
                                        log('Button clicked but textarea not ready.', { focused: document.activeElement });
                                    }
                                });
                                group.appendChild(button);
                            }
                        }
                    });
                    toolbar.appendChild(group);
                });
                // Character count span
                charCountSpan = document.createElement('span');
                charCountSpan.id = 'char-count';
                charCountSpan.textContent = '0 chars';
                toolbar.appendChild(charCountSpan);
                // Minimize button (smaller, optional for buttonset fit)
                var minimizeBtn = document.createElement('button');
                minimizeBtn.id = 'toolbar-minimize';
                minimizeBtn.type = 'button';
                minimizeBtn.innerHTML = '-'; // Plain minus for minimize
                minimizeBtn.title = 'Minimize Formatting Toolbar';
                minimizeBtn.style.fontSize = '0.8em';
                minimizeBtn.style.padding = '0.3em 0.5em';
                minimizeBtn.addEventListener('click', function(e) {
                    e.preventDefault();
                    e.stopPropagation();
                    toggleMinimize();
                });
                toolbar.appendChild(minimizeBtn);
                // Version display (small span, not a button) - in corner as per style
                var versionSpan = document.createElement('span');
                versionSpan.id = 'toolbar-version';
                versionSpan.textContent = 'v' + CONFIG.version;
                versionSpan.className = 'ui-widget';
                versionSpan.title = 'v' + CONFIG.version + ' - Interactive drag-to-set default size modal';
                versionSpan.style.cssText = 'margin-left: 5px; font-size: 0.8em; color: #666; padding: 0.4em 0; align-self: center;';
                toolbar.appendChild(versionSpan);
                // Responsive: Adjust on resize
                window.addEventListener('resize', adjustToolbarLayout);
                adjustToolbarLayout();
                log('Toolbar created as Word-like ribbon with Note Utilities group before Copy/Paste Format, enhanced Live Preview rendering.');
                return toolbar;
            } catch (err) {
                log('Toolbar creation error', err);
                return null;
            }
        }
        // Update character count
        function updateCharCount() {
            if (charCountSpan && textarea) {
                charCountSpan.textContent = textarea.value.length + ' chars';
            }
        }
        // Copy entire note to clipboard
        function copyNote(ta) {
            try {
                navigator.clipboard.writeText(ta.value).then(function() {
                    showDebugIndicator('Note copied to clipboard!', 'success');
                    log('Note copied.');
                }).catch(function(err) {
                    // Fallback
                    var textArea = document.createElement('textarea');
                    textArea.value = ta.value;
                    document.body.appendChild(textArea);
                    textArea.select();
                    document.execCommand('copy');
                    document.body.removeChild(textArea);
                    showDebugIndicator('Note copied to clipboard!', 'success');
                    log('Note copied (fallback).');
                });
            } catch (err) {
                log('Copy note error', err);
                showDebugIndicator('Copy failed; check permissions.', 'error');
            }
        }
        // DOM-based tag parser for accurate enclosing tags with attributes/nesting
        function getEnclosingTags(text, selStart, selEnd) {
            var temp = document.createElement('div');
            var marker = '<!--NFH_MARKER-->';
            var modifiedText;
            var isFullNote = (selStart === 0 && selEnd === text.length);
            if (isFullNote) {
                // For full note, check if single top-level element
                temp.innerHTML = text;
                if (temp.children.length === 1) {
                    var singleEl = temp.children[0];
                    var openStr = singleEl.outerHTML.substring(0, singleEl.outerHTML.indexOf('>') + 1);
                    var closeStr = '</' + singleEl.tagName.toLowerCase() + '>';
                    return {open: openStr, close: closeStr};
                } else {
                    // Multiple top-level; no single enclosing
                    return {open: '', close: ''};
                }
            } else {
                // Insert marker for partial selection
                modifiedText = text.substring(0, selStart) + marker + text.substring(selEnd);
                temp.innerHTML = modifiedText;
                // Find marker comment
                var walker = document.createTreeWalker(temp, NodeFilter.SHOW_COMMENT, null, false);
                var markerNode = null;
                while (walker.nextNode()) {
                    if (walker.currentNode.data === 'NFH_MARKER') {
                        markerNode = walker.currentNode;
                        break;
                    }
                }
                if (!markerNode) {
                    return {open: '', close: ''};
                }
                // Get parent chain
                var parents = [];
                var current = markerNode.parentNode;
                while (current && current !== temp) {
                    if (current.nodeType === 1) {
                        parents.push(current);
                    }
                    current = current.parentNode;
                }
                // Build open/close strings
                var openStr = '';
                var closeStr = '';
                parents.forEach(function(el) {
                    openStr += el.outerHTML.substring(0, el.outerHTML.indexOf('>') + 1);
                    closeStr = '</' + el.tagName.toLowerCase() + '>' + closeStr;
                });
                return {open: openStr, close: closeStr};
            }
        }
        // Copy format: Extract enclosing tags from selected text (DOM-based for accuracy)
        function copyFormat(ta) {
            try {
                var start = ta.selectionStart;
                var end = ta.selectionEnd;
                var selected = ta.value.substring(start, end).trim();
                var isFullNote = false;
                if (start === end || !selected) {
                    // Default to entire note
                    selected = ta.value.trim();
                    isFullNote = true;
                    start = 0;
                    end = ta.value.length;
                    showDebugIndicator('No selection; analyzing entire note for enclosing tags.', 'info');
                }
                if (!selected) {
                    showDebugIndicator('Note is empty; nothing to copy.', 'info');
                    return;
                }
                // Use DOM parser for enclosing tags
                var parsed = getEnclosingTags(ta.value, start, end);
                if (parsed.open && parsed.close) {
                    copiedFormat = { open: parsed.open, close: parsed.close };
                    localStorage.setItem('nfh_copiedFormat', JSON.stringify(copiedFormat));
                    updatePasteButtonTitle();
                    var preview = parsed.open.substring(0, 30) + '...' + parsed.close.substring(0, 15) + '...';
                    showDebugIndicator('Copied format: ' + preview + (isFullNote ? ' (full note)' : ' (selection)'), 'success');
                    log('Format copied:', copiedFormat, { isFullNote: isFullNote });
                } else {
                    var msg = isFullNote ? 'No single enclosing tag in full note (multiple top-level?). Select within a tag.' : 'No tags found. Select text within tags.';
                    showDebugIndicator(msg, 'error');
                    log('Copy format failed: no tags', { selected: selected.substring(0, 50) + '...', isFullNote: isFullNote });
                }
            } catch (err) {
                log('Copy format error', err);
                showDebugIndicator('Copy format failed; check console.', 'error');
            }
        }
        // Paste format: Apply copied tags to selection (defaults to full if no sel, fixed wrap)
        function pasteFormat(ta) {
            try {
                if (!copiedFormat) {
                    showDebugIndicator('No format copied yet. Use CF or select snippet first.', 'info');
                    return;
                }
                var origStart = ta.selectionStart;
                var origEnd = ta.selectionEnd;
                var selected = ta.value.substring(origStart, origEnd);
                var isFullNote = false;
                var start = origStart;
                var end = origEnd;
                if (origStart === origEnd || !selected.trim()) {
                    // Default to entire note
                    isFullNote = true;
                    start = 0;
                    end = ta.value.length;
                    selected = ta.value;
                    showDebugIndicator('No selection; pasted format to entire note.', 'info');
                }
                // Fix: Set selection to range before apply
                ta.selectionStart = start;
                ta.selectionEnd = end;
                applyFormat(ta, copiedFormat.open, copiedFormat.close);
                // Restore original cursor if not full
                if (!isFullNote) {
                    ta.selectionStart = origStart + copiedFormat.open.length;
                    ta.selectionEnd = origStart + copiedFormat.open.length + (origEnd - origStart);
                }
                updatePasteButtonTitle(); // Refresh in case
                showDebugIndicator('Pasted format to ' + (isFullNote ? 'entire note' : 'selection') + ': ' + copiedFormat.open.substring(0, 20) + '...', 'success');
                log('Format pasted:', copiedFormat, { isFullNote: isFullNote });
            } catch (err) {
                log('Paste format error', err);
                showDebugIndicator('Paste format failed; check console.', 'error');
            }
        }
        // Clear copied format
        function clearCopiedFormat(ta) {
            try {
                copiedFormat = null;
                localStorage.removeItem('nfh_copiedFormat');
                updatePasteButtonTitle();
                showDebugIndicator('Copied format cleared.', 'success');
                log('Format cleared.');
            } catch (err) {
                log('Clear format error', err);
                showDebugIndicator('Clear format failed; check console.', 'error');
            }
        }
        // Minimize/Maximize functionality for better UX (hides main buttons, including dropdowns)
        function toggleMinimize() {
            try {
                isMinimized = !isMinimized;
                const mainButtons = toolbar ? toolbar.querySelectorAll('button:not(#toolbar-minimize), select') : [];
                const versionSpan = document.getElementById('toolbar-version');
                const minimizeBtn = document.getElementById('toolbar-minimize');
                const charCount = document.getElementById('char-count');
                if (isMinimized) {
                    mainButtons.forEach(function(el) { el.style.display = 'none'; });
                    if (versionSpan) versionSpan.style.display = 'none';
                    if (charCount) charCount.style.display = 'none';
                    if (minimizeBtn) {
                        minimizeBtn.innerHTML = '+'; // Plus for maximize
                        minimizeBtn.title = 'Expand Formatting Toolbar';
                    }
                    log('Formatting toolbar minimized.');
                } else {
                    mainButtons.forEach(function(el) { el.style.display = ''; });
                    if (versionSpan) versionSpan.style.display = '';
                    if (charCount) charCount.style.display = '';
                    if (minimizeBtn) {
                        minimizeBtn.innerHTML = '-';
                        minimizeBtn.title = 'Minimize Formatting Toolbar';
                    }
                    adjustToolbarLayout();
                    log('Formatting toolbar expanded.');
                }
            } catch (err) {
                log('Toggle minimize error', err);
            }
        }
        // Responsive layout adjustment (for buttonset fit) - improved with matchMedia
        function adjustToolbarLayout() {
            try {
                const isMobile = window.matchMedia('(max-width: 800px)').matches;
                if (toolbar && isMobile) {
                    toolbar.style.flexDirection = 'column';
                    toolbar.style.alignItems = 'stretch';
                    toolbar.querySelectorAll('button, select').forEach(function(el) { el.style.minWidth = 'auto'; el.style.fontSize = '10px'; el.style.padding = '3px 4px'; });
                } else {
                    toolbar.style.flexDirection = 'row';
                    toolbar.style.alignItems = 'center';
                    toolbar.querySelectorAll('button, select').forEach(function(el) { el.style.minWidth = 'initial'; el.style.fontSize = ''; el.style.padding = ''; });
                }
            } catch (err) {
                log('Layout adjustment error', err);
            }
        }
        // Center dialog on viewport (with bounds clamping)
        function centerDialog(dialog) {
            try {
                // Settle layout
                setTimeout(function() {
                    const rect = dialog.getBoundingClientRect();
                    const vw = window.innerWidth;
                    const vh = window.innerHeight;
                    let left = (vw - rect.width) / 2;
                    let top = (vh - rect.height) / 2;
                    // Clamp to viewport bounds
                    left = Math.max(0, Math.min(left, vw - rect.width));
                    top = Math.max(0, Math.min(top, vh - rect.height));
                    // Account for scroll
                    dialog.style.left = (left + window.scrollX) + 'px';
                    dialog.style.top = (top + window.scrollY) + 'px';
                    log('Centered dialog at ' + (left + window.scrollX) + 'px, ' + (top + window.scrollY) + 'px');
                }, 0);
            } catch (err) {
                log('Center dialog error', err);
            }
        }
        // Auto-expand dialog to fit ribbon + textarea + preview if enabled
        function autoResizeDialog(dialog) {
            try {
                if (!CONFIG.autoResize.enabled) {
                    log('Auto-resize disabled; skipped.');
                    return;
                }
                if (dialog.dataset.helperResized === 'true') return; // One-time per dialog (reset via settings if needed)
                const tb = dialog.querySelector('#' + CONFIG.toolbarId);
                const ta = dialog.querySelector('textarea');
                const pv = dialog.querySelector('#nfh-preview');
                const content = dialog.querySelector('.ui-dialog-content');
                const titlebar = dialog.querySelector('.ui-dialog-titlebar');
                const buttonpane = dialog.querySelector('.ui-dialog-buttonpane');
                if (!tb || !ta || !content || !titlebar || !buttonpane) {
                    log('Auto-resize skipped: Missing elements.');
                    return;
                }
                // Dynamically fit textarea to its content
                ta.style.height = 'auto';
                const taHeight = ta.scrollHeight;
                ta.style.height = taHeight + 'px';
                // Fit preview if enabled and exists
                let pvHeight = 0;
                if (pv && CONFIG.previewEnabled) {
                    updatePreview();
                    pv.style.height = 'auto';
                    pvHeight = pv.scrollHeight;
                    pv.style.height = pvHeight + 'px';
                }
                // Measure fixed parts
                const titleHeight = titlebar.offsetHeight;
                const buttonHeight = buttonpane.offsetHeight;
                const toolbarHeight = tb.offsetHeight;
                const contentPadding = 10; // Estimate for internal padding/margins
                const dialogPadding = 10; // Estimate for dialog outer padding
                // Calculate content height
                const contentHeight = toolbarHeight + taHeight + pvHeight + contentPadding;
                // Calculate full dialog height
                const newHeight = titleHeight + contentHeight + buttonHeight + dialogPadding;
                // Width calculation for better fit
                const contentWidth = Math.max(tb.offsetWidth, ta.offsetWidth) + 10; // Padding for borders/scroll
                const newWidth = Math.max(CONFIG.autoResize.defaultWidth, Math.max(500, contentWidth));
                // Apply resize to dialog
                dialog.style.width = newWidth + 'px';
                dialog.style.height = newHeight + 'px';
                // Sync content dimensions explicitly
                content.style.width = (newWidth - 4) + 'px'; // Account for borders
                content.style.height = contentHeight + 'px';
                log('Auto-resized dialog: ' + newWidth + 'x' + newHeight + 'px (content: ' + contentHeight + 'px, preview: ' + (pvHeight > 0) + ')');
                dialog.dataset.helperResized = 'true';
                // Auto-center if enabled
                if (CONFIG.autoCenter.enabled) {
                    centerDialog(dialog);
                }
            } catch (err) {
                log('Auto-resize error', err);
                // Autonomous: Log but continue; next trigger may succeed
            }
        }
        // Toggle auto-resize enable/disable
        function toggleAutoResize() {
            try {
                CONFIG.autoResize.enabled = !CONFIG.autoResize.enabled;
                localStorage.setItem('nfh_autoResizeEnabled', CONFIG.autoResize.enabled.toString());
                const status = CONFIG.autoResize.enabled ? 'enabled' : 'disabled';
                log('Auto-resize ' + status + '.');
                showDebugIndicator('Auto-resize ' + status + '.', 'info');
                // If enabling, re-apply to current dialog
                if (CONFIG.autoResize.enabled) {
                    const dialog = document.querySelector('.note-edit-dialog');
                    if (dialog) {
                        dialog.dataset.helperResized = 'false';
                        setTimeout(function() { autoResizeDialog(dialog); }, 100);
                    }
                }
            } catch (err) {
                log('Toggle auto-resize error', err);
            }
        }
        // Toggle auto-center enable/disable
        function toggleAutoCenter() {
            try {
                CONFIG.autoCenter.enabled = !CONFIG.autoCenter.enabled;
                localStorage.setItem('nfh_autoCenterEnabled', CONFIG.autoCenter.enabled.toString());
                const status = CONFIG.autoCenter.enabled ? 'enabled' : 'disabled';
                log('Auto-center ' + status + '.');
                showDebugIndicator('Auto-center ' + status + '.', 'info');
                // If enabling, re-apply to current dialog
                if (CONFIG.autoCenter.enabled) {
                    const dialog = document.querySelector('.note-edit-dialog');
                    if (dialog) {
                        centerDialog(dialog);
                    }
                }
            } catch (err) {
                log('Toggle auto-center error', err);
            }
        }
        // Toggle live preview pane
        function togglePreview() {
            try {
                const dialog = document.querySelector('.note-edit-dialog');
                if (!dialog) return;
                textarea = dialog.querySelector('textarea'); // Update global reference
                const content = dialog.querySelector('.ui-dialog-content');
                CONFIG.previewEnabled = !CONFIG.previewEnabled;
                localStorage.setItem('nfh_previewEnabled', CONFIG.previewEnabled.toString());
                if (CONFIG.previewEnabled) {
                    if (!previewDiv) {
                        injectPreview(dialog);
                    } else {
                        previewDiv.classList.add('show');
                        updatePreview();
                    }
                    if (textarea) {
                        textarea.addEventListener('input', debounceUpdatePreview);
                    }
                } else {
                    if (previewDiv) {
                        previewDiv.classList.remove('show');
                    }
                    if (textarea) {
                        textarea.removeEventListener('input', debounceUpdatePreview);
                    }
                }
                const status = CONFIG.previewEnabled ? 'enabled' : 'disabled';
                log('Live preview ' + status + '.');
                showDebugIndicator('Live preview ' + status + '.', 'info');
                // Re-resize if enabled
                dialog.dataset.helperResized = 'false';
                setTimeout(function() { autoResizeDialog(dialog); }, 100);
            } catch (err) {
                log('Toggle preview error', err);
            }
        }
        // Debounced preview update
        function debounceUpdatePreview() {
            if (debounceTimer) clearTimeout(debounceTimer);
            debounceTimer = setTimeout(updatePreview, CONFIG.debounceDelay);
        }
        // Update preview div with sanitized HTML (fixed rendering, no syntax highlight)
        function updatePreview() {
            try {
                if (!previewDiv || !textarea) return;
                var html = textarea.value;
                // Minimal sanitize: Remove <script> to prevent XSS (notes shouldn't have it)
                html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
                // Render as HTML
                previewDiv.innerHTML = html;
                log('Preview updated (rendered HTML).');
            } catch (err) {
                log('Preview update error', err);
                previewDiv.innerHTML = 'Preview error: Invalid HTML.';
            }
        }
        // Inject preview div below textarea
        function injectPreview(dialog) {
            try {
                const content = dialog.querySelector('.ui-dialog-content');
                if (previewDiv && previewDiv.parentNode !== content) {
                    if (previewDiv.parentNode) previewDiv.remove();
                    previewDiv = null;
                }
                if (previewDiv) return;
                previewDiv = document.createElement('div');
                previewDiv.id = 'nfh-preview';
                if (CONFIG.previewEnabled) previewDiv.classList.add('show');
                content.appendChild(previewDiv);
                if (CONFIG.previewEnabled && textarea) {
                    updatePreview();
                    textarea.addEventListener('input', debounceUpdatePreview);
                }
                log('Preview pane injected (default enabled).');
            } catch (err) {
                log('Inject preview error', err);
            }
        }
        // Unified apply format function for tags, styles, aligns (replaces applyStyle/applyAlign)
        function applyFormat(ta, openTag, closeTag) {
            try {
                const start = ta.selectionStart;
                const end = ta.selectionEnd;
                const text = ta.value;
                const selected = text.substring(start, end);
                const before = text.substring(0, start);
                const after = text.substring(end);
                var newText;
                var cursorStart, cursorEnd;
                if (selected) {
                    newText = before + openTag + selected + closeTag + after;
                    cursorStart = start + openTag.length;
                    cursorEnd = cursorStart + selected.length;
                } else {
                    const placeholder = openTag + 'text' + closeTag;
                    newText = before + placeholder + after;
                    cursorStart = start + openTag.length;
                    cursorEnd = cursorStart + 4; // Position inside 'text'
                }
                ta.value = newText;
                ta.selectionStart = cursorStart;
                ta.selectionEnd = cursorEnd;
                ta.focus();
                ta.dispatchEvent(new Event('input', { bubbles: true }));
                // Post-snapshot after successful apply
                saveState(ta);
                // Update preview if enabled
                if (CONFIG.previewEnabled) debounceUpdatePreview();
                updateCharCount();
                log('Applied format: ' + openTag.substring(0, 20) + '...');
            } catch (err) {
                log('Apply format error', err);
            }
        }
        // Prompt for font size and apply via applyFormat
        function promptFontSize(ta) {
            try {
                const size = prompt('Enter font size (e.g., 18px, 1.5em, large):', '16px');
                if (!size) return; // No post-save if cancelled
                const styleStr = 'font-size: ' + size + ';';
                const openTag = '<span style="' + styleStr + '">';
                applyFormat(ta, openTag, '</span>'); // Post-save inside applyFormat
            } catch (err) {
                log('Font size prompt error', err);
            }
        }
        // Open color picker for text color or highlight
        function openColorPicker(ta, type) {
            try {
                closeAllPickers();
                closeResizeSettings();
                // Overlay
                const overlay = document.createElement('div');
                overlay.id = 'color-picker-overlay';
                overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:9998;';
                document.body.appendChild(overlay);
                // Picker dialog
                colorPicker = document.createElement('div');
                colorPicker.id = 'color-picker';
                var titleText = (type === 'background-color') ? 'Text Highlight' : 'Text Color';
                colorPicker.innerHTML = '<h3>' + titleText + '</h3>' +
                    '<div class="palette"></div>' +
                    '<div class="custom-section">' +
                    '<label>Custom RGB</label>' +
                    '<div class="rgb-sliders">' +
                    '<input type="range" id="r-slider" min="0" max="255" value="255" />' +
                    '<input type="range" id="g-slider" min="0" max="255" value="0" />' +
                    '<input type="range" id="b-slider" min="0" max="255" value="0" />' +
                    '</div>' +
                    '<div class="rgb-inputs">' +
                    '<span>R:</span><input type="number" id="r-input" min="0" max="255" value="255" />' +
                    '<span>G:</span><input type="number" id="g-input" min="0" max="255" value="0" />' +
                    '<span>B:</span><input type="number" id="b-input" min="0" max="255" value="0" />' +
                    '</div>' +
                    '<div class="hex-input">' +
                    '<label>Hex (#RRGGBB):</label>' +
                    '<input type="text" id="hex-input" value="#FF0000" maxlength="7" />' +
                    '</div>' +
                    '<div style="display: flex; align-items: center; justify-content: space-between;">' +
                    '<button id="eyedropper" class="eyedropper-btn" title="Pick color from screen" style="width: auto; flex: 1; margin-right: 5px;">👁 Pick from Screen</button>' +
                    '</div>' +
                    '<label>Brightness (Alpha 0-1)</label>' +
                    '<input type="range" id="alpha-slider" min="0" max="1" step="0.01" value="1" />' +
                    '<span id="alpha-value">1.00</span>' +
                    '</div>' +
                    '<div id="color-preview" style="background-color: rgb(255,0,0);"></div>' +
                    '<div class="picker-buttons">' +
                    '<button id="picker-ok">OK</button>' +
                    '<button id="picker-cancel">Cancel</button>' +
                    '</div>';
                document.body.appendChild(colorPicker);
                // Palette, OK, cancel, events (same as before)
                const palette = colorPicker.querySelector('.palette');
                CONFIG.commonColors.forEach(function(color) {
                    const btn = document.createElement('button');
                    btn.style.backgroundColor = color;
                    btn.addEventListener('click', function() {
                        const rgb = hexToRgb(color);
                        colorPicker.querySelector('#r-slider').value = rgb.r;
                        colorPicker.querySelector('#g-slider').value = rgb.g;
                        colorPicker.querySelector('#b-slider').value = rgb.b;
                        colorPicker.querySelector('#r-input').value = rgb.r;
                        colorPicker.querySelector('#g-input').value = rgb.g;
                        colorPicker.querySelector('#b-input').value = rgb.b;
                        colorPicker.querySelector('#hex-input').value = color;
                        updateColor(colorPicker);
                    });
                    palette.appendChild(btn);
                });
                colorPicker.querySelector('#picker-ok').addEventListener('click', function(e) {
                    const r = colorPicker.querySelector('#r-slider').value;
                    const g = colorPicker.querySelector('#g-slider').value;
                    const b = colorPicker.querySelector('#b-slider').value;
                    const alpha = colorPicker.querySelector('#alpha-slider').value;
                    const colorValue = (alpha < 1) ? 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')' : 'rgb(' + r + ', ' + g + ', ' + b + ')';
                    const styleStr = type + ': ' + colorValue + ';';
                    const openTag = '<span style="' + styleStr + '">';
                    applyFormat(ta, openTag, '</span>');
                    closeColorPicker();
                });
                colorPicker.querySelector('#picker-cancel').addEventListener('click', closeColorPicker);
                overlay.addEventListener('click', closeColorPicker);
                var escHandler = function(e) {
                    if (e.key === 'Escape') closeColorPicker();
                };
                window.addEventListener('keydown', escHandler);
                // Eyedropper
                const eyedropperBtn = colorPicker.querySelector('#eyedropper');
                addColorZillaLink(eyedropperBtn);
                if (eyedropperBtn && 'eyedropper' in navigator) {
                    eyedropperBtn.addEventListener('click', async function() {
                        try {
                            const result = await navigator.eyedropper.pick({withAlpha: true});
                            if (result && result.sRGBHex) {
                                const hexFull = result.sRGBHex;
                                const alphaHex = hexFull.slice(-2);
                                const hex = '#' + hexFull.slice(1,7).toUpperCase();
                                const r = parseInt(hexFull.slice(1,3), 16);
                                const g = parseInt(hexFull.slice(3,5), 16);
                                const b = parseInt(hexFull.slice(5,7), 16);
                                const alpha = parseInt(alphaHex, 16) / 255;
                                colorPicker.querySelector('#r-slider').value = r;
                                colorPicker.querySelector('#g-slider').value = g;
                                colorPicker.querySelector('#b-slider').value = b;
                                colorPicker.querySelector('#r-input').value = r;
                                colorPicker.querySelector('#g-input').value = g;
                                colorPicker.querySelector('#b-input').value = b;
                                colorPicker.querySelector('#hex-input').value = hex;
                                colorPicker.querySelector('#alpha-slider').value = alpha;
                                colorPicker.querySelector('#alpha-value').textContent = alpha.toFixed(2);
                                updateColor(colorPicker);
                            }
                        } catch (err) {
                            log('Eyedropper error:', err);
                            showDebugIndicator('Eyedropper cancelled or failed.', 'info');
                        }
                    });
                } else if (eyedropperBtn) {
                    eyedropperBtn.disabled = true;
                    eyedropperBtn.title = 'EyeDropper API not supported in this browser';
                }
                function updateColor(picker) {
                    const r = picker.querySelector('#r-slider').value;
                    const g = picker.querySelector('#g-slider').value;
                    const b = picker.querySelector('#b-slider').value;
                    const alpha = picker.querySelector('#alpha-slider').value;
                    picker.querySelector('#alpha-value').textContent = alpha;
                    const color = 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')';
                    picker.querySelector('#color-preview').style.backgroundColor = color;
                    picker.querySelector('#r-input').value = r;
                    picker.querySelector('#g-input').value = g;
                    picker.querySelector('#b-input').value = b;
                    const hex = '#' + Math.round(r).toString(16).padStart(2, '0') + Math.round(g).toString(16).padStart(2, '0') + Math.round(b).toString(16).padStart(2, '0');
                    picker.querySelector('#hex-input').value = hex.toUpperCase();
                }
                function updateSlider(channel, picker) {
                    const input = picker.querySelector('#' + channel + '-input').value;
                    picker.querySelector('#' + channel + '-slider').value = input;
                    updateColor(picker);
                }
                colorPicker.addEventListener('input', function(e) {
                    if (e.target.matches('input[type="range"], input[type="number"]')) {
                        if (e.target.id.includes('input') && e.target.type === 'number') {
                            const channel = e.target.id.split('-')[0];
                            updateSlider(channel, colorPicker);
                        } else {
                            updateColor(colorPicker);
                        }
                    } else if (e.target.id === 'hex-input') {
                        const hexValue = e.target.value.replace('#', '').toUpperCase();
                        if (hexValue.match(/^([0-9A-F]{6})$/)) {
                            const rgb = hexToRgb(hexValue);
                            colorPicker.querySelector('#r-slider').value = rgb.r;
                            colorPicker.querySelector('#g-slider').value = rgb.g;
                            colorPicker.querySelector('#b-slider').value = rgb.b;
                            colorPicker.querySelector('#r-input').value = rgb.r;
                            colorPicker.querySelector('#g-input').value = rgb.g;
                            colorPicker.querySelector('#b-input').value = rgb.b;
                            updateColor(colorPicker);
                        }
                    }
                });
                updateColor(colorPicker);
                function closeColorPicker() {
                    if (colorPicker) colorPicker.remove();
                    const overlay = document.getElementById('color-picker-overlay');
                    if (overlay) overlay.remove();
                    colorPicker = null;
                    window.removeEventListener('keydown', escHandler);
                }
            } catch (err) {
                log('Color picker open error', err);
            }
        }
        // Close all open pickers
        function closeAllPickers() {
            closeColorPicker();
            closeShadowPicker();
            closeSymbolsPicker();
            closePinkOutlinePicker();
            closeDoubleOutlinePicker();
            closeSnippetsManager();
        }
        function closeColorPicker() {
            if (colorPicker) {
                const overlay = document.getElementById('color-picker-overlay');
                if (overlay) overlay.remove();
                colorPicker.remove();
                colorPicker = null;
            }
        }
        function closeShadowPicker() {
            if (shadowPicker) {
                const overlay = document.getElementById('shadow-picker-overlay');
                if (overlay) overlay.remove();
                shadowPicker.remove();
                shadowPicker = null;
            }
        }
        function closeSymbolsPicker() {
            if (symbolPicker) {
                const overlay = document.getElementById('symbol-picker-overlay');
                if (overlay) overlay.remove();
                symbolPicker.remove();
                symbolPicker = null;
            }
        }
        function closePinkOutlinePicker() {
            if (pinkOutlinePicker) {
                const overlay = document.getElementById('pink-outline-picker-overlay');
                if (overlay) overlay.remove();
                pinkOutlinePicker.remove();
                pinkOutlinePicker = null;
            }
        }
        function closeDoubleOutlinePicker() {
            if (doubleOutlinePicker) {
                const overlay = document.getElementById('double-outline-picker-overlay');
                if (overlay) overlay.remove();
                doubleOutlinePicker.remove();
                doubleOutlinePicker = null;
            }
        }
        // Open shadow picker for text shadow (outlines/drop shadows)
        function openShadowPicker(ta) {
            try {
                closeAllPickers();
                closeResizeSettings();
                // Overlay and HTML (same as before, added eyedropper)
                const overlay = document.createElement('div');
                overlay.id = 'shadow-picker-overlay';
                overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:9998;';
                document.body.appendChild(overlay);
                shadowPicker = document.createElement('div');
                shadowPicker.id = 'shadow-picker';
                shadowPicker.innerHTML = '<h3>Text Shadow</h3>' +
                    '<div class="preset-buttons">' +
                    '<button id="preset-drop">Drop Shadow</button>' +
                    '<button id="preset-outline">Outline</button>' +
                    '</div>' +
                    '<div class="shadow-section">' +
                    '<div class="shadow-inputs">' +
                    '<label>X Offset (px): <input type="number" id="x-offset" value="1" step="0.1" min="-10" max="10" aria-label="X offset" /></label>' +
                    '<label>Y Offset (px): <input type="number" id="y-offset" value="1" step="0.1" min="-10" max="10" aria-label="Y offset" /></label>' +
                    '<label>Blur Radius (px): <input type="number" id="blur" value="0" step="0.1" min="0" max="20" aria-label="Blur radius" /></label>' +
                    '<label><input type="checkbox" id="outline-mode" /> Outline Mode (4 shadows around text)</label>' +
                    '</div>' +
                    '</div>' +
                    '<div class="palette"></div>' +
                    '<div class="custom-section">' +
                    '<label>Shadow Color</label>' +
                    '<div class="rgb-sliders">' +
                    '<input type="range" id="r-slider" min="0" max="255" value="0" />' +
                    '<input type="range" id="g-slider" min="0" max="255" value="0" />' +
                    '<input type="range" id="b-slider" min="0" max="255" value="0" />' +
                    '</div>' +
                    '<div class="rgb-inputs">' +
                    '<span>R:</span><input type="number" id="r-input" min="0" max="255" value="0" />' +
                    '<span>G:</span><input type="number" id="g-input" min="0" max="255" value="0" />' +
                    '<span>B:</span><input type="number" id="b-input" min="0" max="255" value="0" />' +
                    '</div>' +
                    '<div class="hex-input">' +
                    '<label>Hex (#RRGGBB):</label>' +
                    '<input type="text" id="hex-input" value="#000000" maxlength="7" />' +
                    '</div>' +
                    '<div style="display: flex; align-items: center; justify-content: space-between;">' +
                    '<button id="eyedropper-shadow" class="eyedropper-btn" title="Pick color from screen" style="width: auto; flex: 1; margin-right: 5px;">👁 Pick from Screen</button>' +
                    '</div>' +
                    '<label>Brightness (Alpha 0-1)</label>' +
                    '<input type="range" id="alpha-slider" min="0" max="1" step="0.01" value="1" />' +
                    '<span id="alpha-value">1.00</span>' +
                    '</div>' +
                    '<div id="shadow-preview" style="background-color: #333; color: #fff; text-shadow: 1px 1px 0 #000;">Preview</div>' +
                    '<div class="picker-buttons">' +
                    '<button id="picker-ok">OK</button>' +
                    '<button id="picker-cancel">Cancel</button>' +
                    '</div>';
                document.body.appendChild(shadowPicker);
                // Palette, presets, listeners (same as v1.41, added eyedropper)
                const palette = shadowPicker.querySelector('.palette');
                CONFIG.commonColors.forEach(function(color) {
                    const btn = document.createElement('button');
                    btn.style.backgroundColor = color;
                    btn.addEventListener('click', function() {
                        const rgb = hexToRgb(color);
                        shadowPicker.querySelector('#r-slider').value = rgb.r;
                        shadowPicker.querySelector('#g-slider').value = rgb.g;
                        shadowPicker.querySelector('#b-slider').value = rgb.b;
                        shadowPicker.querySelector('#r-input').value = rgb.r;
                        shadowPicker.querySelector('#g-input').value = rgb.g;
                        shadowPicker.querySelector('#b-input').value = rgb.b;
                        shadowPicker.querySelector('#hex-input').value = color;
                        updateShadowPreview(shadowPicker);
                    });
                    palette.appendChild(btn);
                });
                shadowPicker.querySelector('#preset-drop').addEventListener('click', function() {
                    shadowPicker.querySelector('#x-offset').value = 2;
                    shadowPicker.querySelector('#y-offset').value = 2;
                    shadowPicker.querySelector('#blur').value = 2;
                    shadowPicker.querySelector('#outline-mode').checked = false;
                    shadowPicker.querySelector('#r-slider').value = 0;
                    shadowPicker.querySelector('#g-slider').value = 0;
                    shadowPicker.querySelector('#b-slider').value = 0;
                    shadowPicker.querySelector('#r-input').value = 0;
                    shadowPicker.querySelector('#g-input').value = 0;
                    shadowPicker.querySelector('#b-input').value = 0;
                    shadowPicker.querySelector('#hex-input').value = '#000000';
                    updateShadowPreview(shadowPicker);
                });
                shadowPicker.querySelector('#preset-outline').addEventListener('click', function() {
                    shadowPicker.querySelector('#x-offset').value = 1;
                    shadowPicker.querySelector('#y-offset').value = 1;
                    shadowPicker.querySelector('#blur').value = 0;
                    shadowPicker.querySelector('#outline-mode').checked = true;
                    shadowPicker.querySelector('#r-slider').value = 0;
                    shadowPicker.querySelector('#g-slider').value = 0;
                    shadowPicker.querySelector('#b-slider').value = 0;
                    shadowPicker.querySelector('#r-input').value = 0;
                    shadowPicker.querySelector('#g-input').value = 0;
                    shadowPicker.querySelector('#b-input').value = 0;
                    shadowPicker.querySelector('#hex-input').value = '#000000';
                    updateShadowPreview(shadowPicker);
                });
                shadowPicker.querySelector('#outline-mode').addEventListener('change', function(e) {
                    updateShadowPreview(shadowPicker);
                });
                shadowPicker.querySelector('#picker-ok').addEventListener('click', function(e) {
                    var x = parseFloat(shadowPicker.querySelector('#x-offset').value) || 1;
                    var y = parseFloat(shadowPicker.querySelector('#y-offset').value) || 1;
                    var blur = parseFloat(shadowPicker.querySelector('#blur').value) || 0;
                    x = Math.max(-10, Math.min(10, x)); // Clamp
                    y = Math.max(-10, Math.min(10, y));
                    blur = Math.max(0, Math.min(20, blur));
                    const r = shadowPicker.querySelector('#r-slider').value;
                    const g = shadowPicker.querySelector('#g-slider').value;
                    const b = shadowPicker.querySelector('#b-slider').value;
                    const alpha = shadowPicker.querySelector('#alpha-slider').value;
                    const colorValue = (alpha < 1) ? 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')' : 'rgb(' + r + ', ' + g + ', ' + b + ')';
                    var shadowRule;
                    const absX = Math.abs(x);
                    const absY = Math.abs(y);
                    if (shadowPicker.querySelector('#outline-mode').checked) {
                        shadowRule = '-' + absX + 'px -' + absY + 'px ' + blur + 'px ' + colorValue + ', ' + absX + 'px -' + absY + 'px ' + blur + 'px ' + colorValue + ', -' + absX + 'px ' + absY + 'px ' + blur + 'px ' + colorValue + ', ' + absX + 'px ' + absY + 'px ' + blur + 'px ' + colorValue;
                    } else {
                        shadowRule = x + 'px ' + y + 'px ' + blur + 'px ' + colorValue;
                    }
                    const styleStr = 'text-shadow: ' + shadowRule + ';';
                    const openTag = '<span style="' + styleStr + '">';
                    applyFormat(ta, openTag, '</span>');
                    closeShadowPicker();
                });
                shadowPicker.querySelector('#picker-cancel').addEventListener('click', closeShadowPicker);
                overlay.addEventListener('click', closeShadowPicker);
                var escHandler = function(e) {
                    if (e.key === 'Escape') closeShadowPicker();
                };
                window.addEventListener('keydown', escHandler);
                // Eyedropper for shadow
                const eyedropperShadowBtn = shadowPicker.querySelector('#eyedropper-shadow');
                addColorZillaLink(eyedropperShadowBtn);
                if (eyedropperShadowBtn && 'eyedropper' in navigator) {
                    eyedropperShadowBtn.addEventListener('click', async function() {
                        try {
                            const result = await navigator.eyedropper.pick({withAlpha: true});
                            if (result && result.sRGBHex) {
                                const hexFull = result.sRGBHex;
                                const alphaHex = hexFull.slice(-2);
                                const hex = '#' + hexFull.slice(1,7).toUpperCase();
                                const r = parseInt(hexFull.slice(1,3), 16);
                                const g = parseInt(hexFull.slice(3,5), 16);
                                const b = parseInt(hexFull.slice(5,7), 16);
                                const alpha = parseInt(alphaHex, 16) / 255;
                                shadowPicker.querySelector('#r-slider').value = r;
                                shadowPicker.querySelector('#g-slider').value = g;
                                shadowPicker.querySelector('#b-slider').value = b;
                                shadowPicker.querySelector('#r-input').value = r;
                                shadowPicker.querySelector('#g-input').value = g;
                                shadowPicker.querySelector('#b-input').value = b;
                                shadowPicker.querySelector('#hex-input').value = hex;
                                shadowPicker.querySelector('#alpha-slider').value = alpha;
                                shadowPicker.querySelector('#alpha-value').textContent = alpha.toFixed(2);
                                updateShadowPreview(shadowPicker);
                            }
                        } catch (err) {
                            log('Eyedropper error:', err);
                            showDebugIndicator('Eyedropper cancelled or failed.', 'info');
                        }
                    });
                } else if (eyedropperShadowBtn) {
                    eyedropperShadowBtn.disabled = true;
                    eyedropperShadowBtn.title = 'EyeDropper API not supported in this browser';
                }
                function updateShadowPreview(picker) {
                    const x = parseFloat(picker.querySelector('#x-offset').value) || 1;
                    const y = parseFloat(picker.querySelector('#y-offset').value) || 1;
                    const blur = parseFloat(picker.querySelector('#blur').value) || 0;
                    const r = picker.querySelector('#r-slider').value;
                    const g = picker.querySelector('#g-slider').value;
                    const b = picker.querySelector('#b-slider').value;
                    const alpha = picker.querySelector('#alpha-slider').value;
                    const colorValue = (alpha < 1) ? 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')' : 'rgb(' + r + ', ' + g + ', ' + b + ')';
                    var shadowRule;
                    const absX = Math.abs(x);
                    const absY = Math.abs(y);
                    if (picker.querySelector('#outline-mode').checked) {
                        shadowRule = '-' + absX + 'px -' + absY + 'px ' + blur + 'px ' + colorValue + ', ' + absX + 'px -' + absY + 'px ' + blur + 'px ' + colorValue + ', -' + absX + 'px ' + absY + 'px ' + blur + 'px ' + colorValue + ', ' + absX + 'px ' + absY + 'px ' + blur + 'px ' + colorValue;
                    } else {
                        shadowRule = x + 'px ' + y + 'px ' + blur + 'px ' + colorValue;
                    }
                    picker.querySelector('#shadow-preview').style.textShadow = shadowRule;
                    picker.querySelector('#r-input').value = r;
                    picker.querySelector('#g-input').value = g;
                    picker.querySelector('#b-input').value = b;
                    const hex = '#' + Math.round(r).toString(16).padStart(2, '0') + Math.round(g).toString(16).padStart(2, '0') + Math.round(b).toString(16).padStart(2, '0');
                    picker.querySelector('#hex-input').value = hex.toUpperCase();
                    picker.querySelector('#alpha-value').textContent = alpha;
                }
                function updateSlider(channel, picker) {
                    const input = picker.querySelector('#' + channel + '-input').value;
                    picker.querySelector('#' + channel + '-slider').value = input;
                    updateShadowPreview(picker);
                }
                shadowPicker.addEventListener('input', function(e) {
                    if (e.target.matches('#x-offset, #y-offset, #blur')) {
                        updateShadowPreview(shadowPicker);
                    } else if (e.target.matches('input[type="range"], input[type="number"]')) {
                        if (e.target.id.includes('input') && e.target.type === 'number') {
                            const channel = e.target.id.split('-')[0];
                            updateSlider(channel, shadowPicker);
                        } else {
                            updateShadowPreview(shadowPicker);
                        }
                    } else if (e.target.id === 'hex-input') {
                        const hexValue = e.target.value.replace('#', '').toUpperCase();
                        if (hexValue.match(/^([0-9A-F]{6})$/)) {
                            const rgb = hexToRgb(hexValue);
                            shadowPicker.querySelector('#r-slider').value = rgb.r;
                            shadowPicker.querySelector('#g-slider').value = rgb.g;
                            shadowPicker.querySelector('#b-slider').value = rgb.b;
                            shadowPicker.querySelector('#r-input').value = rgb.r;
                            shadowPicker.querySelector('#g-input').value = rgb.g;
                            shadowPicker.querySelector('#b-input').value = rgb.b;
                            updateShadowPreview(shadowPicker);
                        }
                    }
                });
                updateShadowPreview(shadowPicker);
            } catch (err) {
                log('Shadow picker open error', err);
                showDebugIndicator('Shadow picker failed; check console.', 'error');
            }
        }
        // Open pink outline picker - added eyedropper for text and outline
        function openPinkOutlinePicker(ta) {
            try {
                closeAllPickers();
                closeResizeSettings();
                const overlay = document.createElement('div');
                overlay.id = 'pink-outline-picker-overlay';
                overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:9998;';
                document.body.appendChild(overlay);
                pinkOutlinePicker = document.createElement('div');
                pinkOutlinePicker.id = 'pink-outline-picker';
                pinkOutlinePicker.innerHTML = '<h3>Pink Outline Template</h3>' +
                    '<div class="outline-section">' +
                    '<div class="outline-inputs">' +
                    '<label>Font Family: <select id="font-family-select"><option value="print">Print (Kalam)</option><option value="serif">Serif</option><option value="sans-serif">Sans Serif</option><option value="monospace">Monospace</option><option value="cursive">Cursive</option><option value="fantasy">Fantasy</option><option value="comic">Comic Sans MS</option><option value="narrow">Arial Narrow</option><option value="mono">Plex Mono</option><option value="slab sans">Impact</option><option value="slab serif">Rockwell</option><option value="formal serif">Formal Serif (Lora)</option><option value="formal cursive">Formal Cursive</option><option value="hand">Hand (Indie Flower)</option><option value="childlike">Childlike (Giselle)</option><option value="blackletter">Blackletter</option><option value="scary">Scary (Anarchy)</option></select></label>' +
                    '<label>Font Size (px): <input type="number" id="font-size" value="30" min="10" max="100" step="1" /></label>' +
                    '<label><input type="checkbox" id="font-bold" checked /> Bold</label>' +
                    '<label>Outline Offset (px): <input type="number" id="offset" value="2" min="0" max="10" step="0.1" /></label>' +
                    '<label>Blur Radius (px): <input type="number" id="blur" value="5" min="0" max="20" step="0.1" /></label>' +
                    '</div>' +
                    '</div>' +
                    '<div class="outline-section">' +
                    '<h4>Text Color</h4>' +
                    '<div class="palette" id="text-palette"></div>' +
                    '<div class="custom-section">' +
                    '<label>Custom RGB</label>' +
                    '<div class="rgb-sliders">' +
                    '<input type="range" id="text-r-slider" min="0" max="255" value="255" />' +
                    '<input type="range" id="text-g-slider" min="0" max="255" value="255" />' +
                    '<input type="range" id="text-b-slider" min="0" max="255" value="255" />' +
                    '</div>' +
                    '<div class="rgb-inputs">' +
                    '<span>R:</span><input type="number" id="text-r-input" min="0" max="255" value="255" />' +
                    '<span>G:</span><input type="number" id="text-g-input" min="0" max="255" value="255" />' +
                    '<span>B:</span><input type="number" id="text-b-input" min="0" max="255" value="255" />' +
                    '</div>' +
                    '<div class="hex-input">' +
                    '<label>Hex (#RRGGBB):</label>' +
                    '<input type="text" id="text-hex-input" value="#FFFFFF" maxlength="7" />' +
                    '</div>' +
                    '<div style="display: flex; align-items: center; justify-content: space-between;">' +
                    '<button id="eyedropper-text" class="eyedropper-btn" title="Pick color from screen" style="width: auto; flex: 1; margin-right: 5px;">👁 Pick from Screen</button>' +
                    '</div>' +
                    '<label>Opacity (Alpha 0-1)</label>' +
                    '<input type="range" id="text-alpha-slider" min="0" max="1" step="0.01" value="1" />' +
                    '<span id="text-alpha-value">1.00</span>' +
                    '</div>' +
                    '</div>' +
                    '<div class="outline-section">' +
                    '<h4>Outline Color</h4>' +
                    '<div class="palette" id="outline-palette"></div>' +
                    '<div class="custom-section">' +
                    '<label>Custom RGB</label>' +
                    '<div class="rgb-sliders">' +
                    '<input type="range" id="outline-r-slider" min="0" max="255" value="235" />' +
                    '<input type="range" id="outline-g-slider" min="0" max="255" value="115" />' +
                    '<input type="range" id="outline-b-slider" min="0" max="255" value="161" />' +
                    '</div>' +
                    '<div class="rgb-inputs">' +
                    '<span>R:</span><input type="number" id="outline-r-input" min="0" max="255" value="235" />' +
                    '<span>G:</span><input type="number" id="outline-g-input" min="0" max="255" value="115" />' +
                    '<span>B:</span><input type="number" id="outline-b-input" min="0" max="255" value="161" />' +
                    '</div>' +
                    '<div class="hex-input">' +
                    '<label>Hex (#RRGGBB):</label>' +
                    '<input type="text" id="outline-hex-input" value="#EB73A1" maxlength="7" />' +
                    '</div>' +
                    '<div style="display: flex; align-items: center; justify-content: space-between;">' +
                    '<button id="eyedropper-outline" class="eyedropper-btn" title="Pick color from screen" style="width: auto; flex: 1; margin-right: 5px;">👁 Pick from Screen</button>' +
                    '</div>' +
                    '<label>Opacity (Alpha 0-1)</label>' +
                    '<input type="range" id="outline-alpha-slider" min="0" max="1" step="0.01" value="0.8" />' +
                    '<span id="outline-alpha-value">0.80</span>' +
                    '</div>' +
                    '</div>' +
                    '<div id="outline-preview">Preview</div>' +
                    '<div class="picker-buttons">' +
                    '<button id="picker-ok">OK</button>' +
                    '<button id="picker-cancel">Cancel</button>' +
                    '</div>';
                document.body.appendChild(pinkOutlinePicker);
                // Text palette
                const textPalette = pinkOutlinePicker.querySelector('#text-palette');
                CONFIG.commonColors.forEach(function(color) {
                    const btn = document.createElement('button');
                    btn.style.backgroundColor = color;
                    btn.addEventListener('click', function() {
                        const rgb = hexToRgb(color);
                        pinkOutlinePicker.querySelector('#text-r-slider').value = rgb.r;
                        pinkOutlinePicker.querySelector('#text-g-slider').value = rgb.g;
                        pinkOutlinePicker.querySelector('#text-b-slider').value = rgb.b;
                        pinkOutlinePicker.querySelector('#text-r-input').value = rgb.r;
                        pinkOutlinePicker.querySelector('#text-g-input').value = rgb.g;
                        pinkOutlinePicker.querySelector('#text-b-input').value = rgb.b;
                        pinkOutlinePicker.querySelector('#text-hex-input').value = color;
                        pinkOutlinePicker.querySelector('#text-alpha-slider').value = 1;
                        updateOutlinePreview(pinkOutlinePicker);
                    });
                    textPalette.appendChild(btn);
                });
                // Outline palette
                const outlinePalette = pinkOutlinePicker.querySelector('#outline-palette');
                CONFIG.commonColors.forEach(function(color) {
                    const btn = document.createElement('button');
                    btn.style.backgroundColor = color;
                    btn.addEventListener('click', function() {
                        const rgb = hexToRgb(color);
                        pinkOutlinePicker.querySelector('#outline-r-slider').value = rgb.r;
                        pinkOutlinePicker.querySelector('#outline-g-slider').value = rgb.g;
                        pinkOutlinePicker.querySelector('#outline-b-slider').value = rgb.b;
                        pinkOutlinePicker.querySelector('#outline-r-input').value = rgb.r;
                        pinkOutlinePicker.querySelector('#outline-g-input').value = rgb.g;
                        pinkOutlinePicker.querySelector('#outline-b-input').value = rgb.b;
                        pinkOutlinePicker.querySelector('#outline-hex-input').value = color;
                        pinkOutlinePicker.querySelector('#outline-alpha-slider').value = 0.8;
                        updateOutlinePreview(pinkOutlinePicker);
                    });
                    outlinePalette.appendChild(btn);
                });
                pinkOutlinePicker.querySelector('#picker-ok').addEventListener('click', function(e) {
                    const fontFamily = pinkOutlinePicker.querySelector('#font-family-select').value;
                    const fontFamilyStr = fontFamily.includes(' ') ? fontFamily + ', serif' : fontFamily + ', sans-serif';
                    const fontSize = pinkOutlinePicker.querySelector('#font-size').value + 'px';
                    var styleStr = 'font-size: ' + fontSize + '; font-family: ' + fontFamilyStr + '; ';
                    const bold = pinkOutlinePicker.querySelector('#font-bold').checked;
                    if (bold) styleStr += 'font-weight: bold; ';
                    const offset = parseFloat(pinkOutlinePicker.querySelector('#offset').value) || 2;
                    const blur = parseFloat(pinkOutlinePicker.querySelector('#blur').value) || 5;
                    const textR = pinkOutlinePicker.querySelector('#text-r-slider').value;
                    const textG = pinkOutlinePicker.querySelector('#text-g-slider').value;
                    const textB = pinkOutlinePicker.querySelector('#text-b-slider').value;
                    const textAlpha = pinkOutlinePicker.querySelector('#text-alpha-slider').value;
                    const outlineR = pinkOutlinePicker.querySelector('#outline-r-slider').value;
                    const outlineG = pinkOutlinePicker.querySelector('#outline-g-slider').value;
                    const outlineB = pinkOutlinePicker.querySelector('#outline-b-slider').value;
                    const outlineAlpha = pinkOutlinePicker.querySelector('#outline-alpha-slider').value;
                    const textColor = (textAlpha < 1) ? 'rgba(' + textR + ', ' + textG + ', ' + textB + ', ' + textAlpha + ')' : 'rgb(' + textR + ', ' + textG + ', ' + textB + ')';
                    const outlineColor = (outlineAlpha < 1) ? 'rgba(' + outlineR + ', ' + outlineG + ', ' + outlineB + ', ' + outlineAlpha + ')' : 'rgb(' + outlineR + ', ' + outlineG + ', ' + outlineB + ')';
                    styleStr += 'color: ' + textColor + '; ';
                    const absOffset = Math.abs(offset);
                    var shadowRule = '-' + absOffset + 'px -' + absOffset + 'px ' + blur + 'px ' + outlineColor + ', ' +
                                     '-' + absOffset + 'px 0px ' + blur + 'px ' + outlineColor + ', ' +
                                     '-' + absOffset + 'px ' + absOffset + 'px ' + blur + 'px ' + outlineColor + ', ' +
                                     '0px -' + absOffset + 'px ' + blur + 'px ' + outlineColor + ', ' +
                                     '0px ' + absOffset + 'px ' + blur + 'px ' + outlineColor + ', ' +
                                     absOffset + 'px -' + absOffset + 'px ' + blur + 'px ' + outlineColor + ', ' +
                                     absOffset + 'px 0px ' + blur + 'px ' + outlineColor + ', ' +
                                     absOffset + 'px ' + absOffset + 'px ' + blur + 'px ' + outlineColor;
                    styleStr += 'text-shadow: ' + shadowRule + ';';
                    var openTag = '<span style="' + styleStr + '">';
                    applyFormat(ta, openTag, '</span>');
                    closePinkOutlinePicker();
                });
                pinkOutlinePicker.querySelector('#picker-cancel').addEventListener('click', closePinkOutlinePicker);
                overlay.addEventListener('click', closePinkOutlinePicker);
                var escHandler = function(e) {
                    if (e.key === 'Escape') closePinkOutlinePicker();
                };
                window.addEventListener('keydown', escHandler);
                // Eyedropper for text
                const eyedropperTextBtn = pinkOutlinePicker.querySelector('#eyedropper-text');
                addColorZillaLink(eyedropperTextBtn);
                if (eyedropperTextBtn && 'eyedropper' in navigator) {
                    eyedropperTextBtn.addEventListener('click', async function() {
                        try {
                            const result = await navigator.eyedropper.pick({withAlpha: true});
                            if (result && result.sRGBHex) {
                                const hexFull = result.sRGBHex;
                                const alphaHex = hexFull.slice(-2);
                                const hex = '#' + hexFull.slice(1,7).toUpperCase();
                                const r = parseInt(hexFull.slice(1,3), 16);
                                const g = parseInt(hexFull.slice(3,5), 16);
                                const b = parseInt(hexFull.slice(5,7), 16);
                                const alpha = parseInt(alphaHex, 16) / 255;
                                pinkOutlinePicker.querySelector('#text-r-slider').value = r;
                                pinkOutlinePicker.querySelector('#text-g-slider').value = g;
                                pinkOutlinePicker.querySelector('#text-b-slider').value = b;
                                pinkOutlinePicker.querySelector('#text-r-input').value = r;
                                pinkOutlinePicker.querySelector('#text-g-input').value = g;
                                pinkOutlinePicker.querySelector('#text-b-input').value = b;
                                pinkOutlinePicker.querySelector('#text-hex-input').value = hex;
                                pinkOutlinePicker.querySelector('#text-alpha-slider').value = alpha;
                                pinkOutlinePicker.querySelector('#text-alpha-value').textContent = alpha.toFixed(2);
                                updateOutlinePreview(pinkOutlinePicker);
                            }
                        } catch (err) {
                            log('Eyedropper error:', err);
                            showDebugIndicator('Eyedropper cancelled or failed.', 'info');
                        }
                    });
                } else if (eyedropperTextBtn) {
                    eyedropperTextBtn.disabled = true;
                    eyedropperTextBtn.title = 'EyeDropper API not supported in this browser';
                }
                // Eyedropper for outline
                const eyedropperOutlineBtn = pinkOutlinePicker.querySelector('#eyedropper-outline');
                addColorZillaLink(eyedropperOutlineBtn);
                if (eyedropperOutlineBtn && 'eyedropper' in navigator) {
                    eyedropperOutlineBtn.addEventListener('click', async function() {
                        try {
                            const result = await navigator.eyedropper.pick({withAlpha: true});
                            if (result && result.sRGBHex) {
                                const hexFull = result.sRGBHex;
                                const alphaHex = hexFull.slice(-2);
                                const hex = '#' + hexFull.slice(1,7).toUpperCase();
                                const r = parseInt(hexFull.slice(1,3), 16);
                                const g = parseInt(hexFull.slice(3,5), 16);
                                const b = parseInt(hexFull.slice(5,7), 16);
                                const alpha = parseInt(alphaHex, 16) / 255;
                                pinkOutlinePicker.querySelector('#outline-r-slider').value = r;
                                pinkOutlinePicker.querySelector('#outline-g-slider').value = g;
                                pinkOutlinePicker.querySelector('#outline-b-slider').value = b;
                                pinkOutlinePicker.querySelector('#outline-r-input').value = r;
                                pinkOutlinePicker.querySelector('#outline-g-input').value = g;
                                pinkOutlinePicker.querySelector('#outline-b-input').value = b;
                                pinkOutlinePicker.querySelector('#outline-hex-input').value = hex;
                                pinkOutlinePicker.querySelector('#outline-alpha-slider').value = alpha;
                                pinkOutlinePicker.querySelector('#outline-alpha-value').textContent = alpha.toFixed(2);
                                updateOutlinePreview(pinkOutlinePicker);
                            }
                        } catch (err) {
                            log('Eyedropper error:', err);
                            showDebugIndicator('Eyedropper cancelled or failed.', 'info');
                        }
                    });
                } else if (eyedropperOutlineBtn) {
                    eyedropperOutlineBtn.disabled = true;
                    eyedropperOutlineBtn.title = 'EyeDropper API not supported in this browser';
                }
                function updateOutlinePreview(picker) {
                    const fontFamily = picker.querySelector('#font-family-select').value;
                    const fontFamilyStr = fontFamily.includes(' ') ? fontFamily + ', serif' : fontFamily + ', sans-serif';
                    const fontSize = picker.querySelector('#font-size').value + 'px';
                    const bold = picker.querySelector('#font-bold').checked ? 'bold' : 'normal';
                    const offset = parseFloat(picker.querySelector('#offset').value) || 2;
                    const blur = parseFloat(picker.querySelector('#blur').value) || 5;
                    const textR = picker.querySelector('#text-r-slider').value;
                    const textG = picker.querySelector('#text-g-slider').value;
                    const textB = picker.querySelector('#text-b-slider').value;
                    const textAlpha = picker.querySelector('#text-alpha-slider').value;
                    const outlineR = picker.querySelector('#outline-r-slider').value;
                    const outlineG = picker.querySelector('#outline-g-slider').value;
                    const outlineB = picker.querySelector('#outline-b-slider').value;
                    const outlineAlpha = picker.querySelector('#outline-alpha-slider').value;
                    const textColor = (textAlpha < 1) ? 'rgba(' + textR + ', ' + textG + ', ' + textB + ', ' + textAlpha + ')' : 'rgb(' + textR + ', ' + textG + ', ' + textB + ')';
                    const outlineColor = (outlineAlpha < 1) ? 'rgba(' + outlineR + ', ' + outlineG + ', ' + outlineB + ', ' + outlineAlpha + ')' : 'rgb(' + outlineR + ', ' + outlineG + ', ' + outlineB + ')';
                    const absOffset = Math.abs(offset);
                    var shadowRule = '-' + absOffset + 'px -' + absOffset + 'px ' + blur + 'px ' + outlineColor + ', ' +
                                     '-' + absOffset + 'px 0px ' + blur + 'px ' + outlineColor + ', ' +
                                     '-' + absOffset + 'px ' + absOffset + 'px ' + blur + 'px ' + outlineColor + ', ' +
                                     '0px -' + absOffset + 'px ' + blur + 'px ' + outlineColor + ', ' +
                                     '0px ' + absOffset + 'px ' + blur + 'px ' + outlineColor + ', ' +
                                     absOffset + 'px -' + absOffset + 'px ' + blur + 'px ' + outlineColor + ', ' +
                                     absOffset + 'px 0px ' + blur + 'px ' + outlineColor + ', ' +
                                     absOffset + 'px ' + absOffset + 'px ' + blur + 'px ' + outlineColor;
                    const preview = picker.querySelector('#outline-preview');
                    preview.style.fontFamily = fontFamilyStr;
                    preview.style.fontSize = fontSize;
                    preview.style.fontWeight = bold;
                    preview.style.color = textColor;
                    preview.style.textShadow = shadowRule;
                    picker.querySelector('#text-alpha-value').textContent = textAlpha;
                    picker.querySelector('#outline-alpha-value').textContent = outlineAlpha;
                    const textHex = '#' + Math.round(textR).toString(16).padStart(2, '0') + Math.round(textG).toString(16).padStart(2, '0') + Math.round(textB).toString(16).padStart(2, '0');
                    picker.querySelector('#text-hex-input').value = textHex.toUpperCase();
                    const outlineHex = '#' + Math.round(outlineR).toString(16).padStart(2, '0') + Math.round(outlineG).toString(16).padStart(2, '0') + Math.round(outlineB).toString(16).padStart(2, '0');
                    picker.querySelector('#outline-hex-input').value = outlineHex.toUpperCase();
                }
                function updateTextSlider(channel, picker) {
                    const input = picker.querySelector('#text-' + channel + '-input').value;
                    picker.querySelector('#text-' + channel + '-slider').value = input;
                    updateOutlinePreview(picker);
                }
                function updateOutlineSlider(channel, picker) {
                    const input = picker.querySelector('#outline-' + channel + '-input').value;
                    picker.querySelector('#outline-' + channel + '-slider').value = input;
                    updateOutlinePreview(picker);
                }
                pinkOutlinePicker.addEventListener('input', function(e) {
                    if (e.target.id === 'font-family-select' || e.target.id === 'font-size' || e.target.id === 'font-bold' || e.target.id === 'offset' || e.target.id === 'blur' || e.target.id === 'text-alpha-slider' || e.target.id === 'outline-alpha-slider') {
                        updateOutlinePreview(pinkOutlinePicker);
                    } else if (e.target.matches('#text-r-slider, #text-g-slider, #text-b-slider')) {
                        updateOutlinePreview(pinkOutlinePicker);
                    } else if (e.target.matches('#outline-r-slider, #outline-g-slider, #outline-b-slider')) {
                        updateOutlinePreview(pinkOutlinePicker);
                    } else if (e.target.id.includes('text-') && e.target.id.includes('input') && e.target.type === 'number') {
                        const channel = e.target.id.replace('text-', '').replace('-input', '');
                        updateTextSlider(channel, pinkOutlinePicker);
                    } else if (e.target.id.includes('outline-') && e.target.id.includes('input') && e.target.type === 'number') {
                        const channel = e.target.id.replace('outline-', '').replace('-input', '');
                        updateOutlineSlider(channel, pinkOutlinePicker);
                    } else if (e.target.id === 'text-hex-input') {
                        const hexValue = e.target.value.replace('#', '').toUpperCase();
                        if (hexValue.match(/^([0-9A-F]{6})$/)) {
                            const rgb = hexToRgb(hexValue);
                            pinkOutlinePicker.querySelector('#text-r-slider').value = rgb.r;
                            pinkOutlinePicker.querySelector('#text-g-slider').value = rgb.g;
                            pinkOutlinePicker.querySelector('#text-b-slider').value = rgb.b;
                            pinkOutlinePicker.querySelector('#text-r-input').value = rgb.r;
                            pinkOutlinePicker.querySelector('#text-g-input').value = rgb.g;
                            pinkOutlinePicker.querySelector('#text-b-input').value = rgb.b;
                            updateOutlinePreview(pinkOutlinePicker);
                        }
                    } else if (e.target.id === 'outline-hex-input') {
                        const hexValue = e.target.value.replace('#', '').toUpperCase();
                        if (hexValue.match(/^([0-9A-F]{6})$/)) {
                            const rgb = hexToRgb(hexValue);
                            pinkOutlinePicker.querySelector('#outline-r-slider').value = rgb.r;
                            pinkOutlinePicker.querySelector('#outline-g-slider').value = rgb.g;
                            pinkOutlinePicker.querySelector('#outline-b-slider').value = rgb.b;
                            pinkOutlinePicker.querySelector('#outline-r-input').value = rgb.r;
                            pinkOutlinePicker.querySelector('#outline-g-input').value = rgb.g;
                            pinkOutlinePicker.querySelector('#outline-b-input').value = rgb.b;
                            updateOutlinePreview(pinkOutlinePicker);
                        }
                    }
                });
                updateOutlinePreview(pinkOutlinePicker);
            } catch (err) {
                log('Pink outline picker open error', err);
                showDebugIndicator('Pink outline picker failed; check console.', 'error');
            }
        }
        // Open double outline picker - added eyedropper for text, inner, outer
        function openDoubleOutlinePicker(ta) {
            try {
                closeAllPickers();
                closeResizeSettings();
                const overlay = document.createElement('div');
                overlay.id = 'double-outline-picker-overlay';
                overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:9998;';
                document.body.appendChild(overlay);
                doubleOutlinePicker = document.createElement('div');
                doubleOutlinePicker.id = 'double-outline-picker';
                doubleOutlinePicker.innerHTML = '<h3>Double Outline Template</h3>' +
                    '<div class="outline-section">' +
                    '<div class="outline-inputs">' +
                    '<label>Font Family: <select id="font-family-select"><option value="print">Print (Kalam)</option><option value="serif">Serif</option><option value="sans-serif">Sans Serif</option><option value="monospace">Monospace</option><option value="cursive">Cursive</option><option value="fantasy">Fantasy</option><option value="comic">Comic Sans MS</option><option value="narrow">Arial Narrow</option><option value="mono">Plex Mono</option><option value="slab sans">Impact</option><option value="slab serif">Rockwell</option><option value="formal serif">Formal Serif (Lora)</option><option value="formal cursive">Formal Cursive</option><option value="hand">Hand (Indie Flower)</option><option value="childlike">Childlike (Giselle)</option><option value="blackletter">Blackletter</option><option value="scary">Scary (Anarchy)</option></select></label>' +
                    '<label>Font Size (px): <input type="number" id="font-size" value="20" min="10" max="100" step="1" /></label>' +
                    '<label><input type="checkbox" id="font-bold" checked /> Bold</label>' +
                    '</div>' +
                    '</div>' +
                    '<div class="outline-section">' +
                    '<h4>Text Color (Pink)</h4>' +
                    '<div class="palette" id="text-palette"></div>' +
                    '<div class="custom-section">' +
                    '<label>Custom RGB</label>' +
                    '<div class="rgb-sliders">' +
                    '<input type="range" id="text-r-slider" min="0" max="255" value="216" />' +
                    '<input type="range" id="text-g-slider" min="0" max="255" value="124" />' +
                    '<input type="range" id="text-b-slider" min="0" max="255" value="134" />' +
                    '</div>' +
                    '<div class="rgb-inputs">' +
                    '<span>R:</span><input type="number" id="text-r-input" min="0" max="255" value="216" />' +
                    '<span>G:</span><input type="number" id="text-g-input" min="0" max="255" value="124" />' +
                    '<span>B:</span><input type="number" id="text-b-input" min="0" max="255" value="134" />' +
                    '</div>' +
                    '<div class="hex-input">' +
                    '<label>Hex (#RRGGBB):</label>' +
                    '<input type="text" id="text-hex-input" value="#D87C86" maxlength="7" />' +
                    '</div>' +
                    '<div style="display: flex; align-items: center; justify-content: space-between;">' +
                    '<button id="eyedropper-text" class="eyedropper-btn" title="Pick color from screen" style="width: auto; flex: 1; margin-right: 5px;">👁 Pick from Screen</button>' +
                    '</div>' +
                    '<label>Opacity (Alpha 0-1)</label>' +
                    '<input type="range" id="text-alpha-slider" min="0" max="1" step="0.01" value="1" />' +
                    '<span id="text-alpha-value">1.00</span>' +
                    '</div>' +
                    '</div>' +
                    '<div class="outline-section">' +
                    '<h4>Inner Outline (White, Thick)</h4>' +
                    '<div class="palette" id="inner-palette"></div>' +
                    '<div class="custom-section">' +
                    '<label>Offset (px): <input type="number" id="inner-offset" value="2" min="0" max="10" step="0.1" /></label>' +
                    '<label>Blur (px): <input type="number" id="inner-blur" value="0" min="0" max="20" step="0.1" /></label>' +
                    '<label>Custom RGB</label>' +
                    '<div class="rgb-sliders">' +
                    '<input type="range" id="inner-r-slider" min="0" max="255" value="255" />' +
                    '<input type="range" id="inner-g-slider" min="0" max="255" value="255" />' +
                    '<input type="range" id="inner-b-slider" min="0" max="255" value="255" />' +
                    '</div>' +
                    '<div class="rgb-inputs">' +
                    '<span>R:</span><input type="number" id="inner-r-input" min="0" max="255" value="255" />' +
                    '<span>G:</span><input type="number" id="inner-g-input" min="0" max="255" value="255" />' +
                    '<span>B:</span><input type="number" id="inner-b-input" min="0" max="255" value="255" />' +
                    '</div>' +
                    '<div class="hex-input">' +
                    '<label>Hex (#RRGGBB):</label>' +
                    '<input type="text" id="inner-hex-input" value="#FFFFFF" maxlength="7" />' +
                    '</div>' +
                    '<div style="display: flex; align-items: center; justify-content: space-between;">' +
                    '<button id="eyedropper-inner" class="eyedropper-btn" title="Pick color from screen" style="width: auto; flex: 1; margin-right: 5px;">👁 Pick from Screen</button>' +
                    '</div>' +
                    '<label>Opacity (Alpha 0-1)</label>' +
                    '<input type="range" id="inner-alpha-slider" min="0" max="1" step="0.01" value="1" />' +
                    '<span id="inner-alpha-value">1.00</span>' +
                    '</div>' +
                    '</div>' +
                    '<div class="outline-section">' +
                    '<h4>Outer Outline (Black, Thin)</h4>' +
                    '<div class="palette" id="outer-palette"></div>' +
                    '<div class="custom-section">' +
                    '<label>Offset (px): <input type="number" id="outer-offset" value="3" min="0" max="10" step="0.1" /></label>' +
                    '<label>Blur (px): <input type="number" id="outer-blur" value="0" min="0" max="20" step="0.1" /></label>' +
                    '<label>Custom RGB</label>' +
                    '<div class="rgb-sliders">' +
                    '<input type="range" id="outer-r-slider" min="0" max="255" value="0" />' +
                    '<input type="range" id="outer-g-slider" min="0" max="255" value="0" />' +
                    '<input type="range" id="outer-b-slider" min="0" max="255" value="0" />' +
                    '</div>' +
                    '<div class="rgb-inputs">' +
                    '<span>R:</span><input type="number" id="outer-r-input" min="0" max="255" value="0" />' +
                    '<span>G:</span><input type="number" id="outer-g-input" min="0" max="255" value="0" />' +
                    '<span>B:</span><input type="number" id="outer-b-input" min="0" max="255" value="0" />' +
                    '</div>' +
                    '<div class="hex-input">' +
                    '<label>Hex (#RRGGBB):</label>' +
                    '<input type="text" id="outer-hex-input" value="#000000" maxlength="7" />' +
                    '</div>' +
                    '<div style="display: flex; align-items: center; justify-content: space-between;">' +
                    '<button id="eyedropper-outer" class="eyedropper-btn" title="Pick color from screen" style="width: auto; flex: 1; margin-right: 5px;">👁 Pick from Screen</button>' +
                    '</div>' +
                    '<label>Opacity (Alpha 0-1)</label>' +
                    '<input type="range" id="outer-alpha-slider" min="0" max="1" step="0.01" value="1" />' +
                    '<span id="outer-alpha-value">1.00</span>' +
                    '</div>' +
                    '</div>' +
                    '<div id="double-preview">Preview</div>' +
                    '<div class="picker-buttons">' +
                    '<button id="picker-ok">OK</button>' +
                    '<button id="picker-cancel">Cancel</button>' +
                    '</div>';
                document.body.appendChild(doubleOutlinePicker);
                // Text palette
                const textPalette = doubleOutlinePicker.querySelector('#text-palette');
                CONFIG.commonColors.forEach(function(color) {
                    const btn = document.createElement('button');
                    btn.style.backgroundColor = color;
                    btn.addEventListener('click', function() {
                        const rgb = hexToRgb(color);
                        doubleOutlinePicker.querySelector('#text-r-slider').value = rgb.r;
                        doubleOutlinePicker.querySelector('#text-g-slider').value = rgb.g;
                        doubleOutlinePicker.querySelector('#text-b-slider').value = rgb.b;
                        doubleOutlinePicker.querySelector('#text-r-input').value = rgb.r;
                        doubleOutlinePicker.querySelector('#text-g-input').value = rgb.g;
                        doubleOutlinePicker.querySelector('#text-b-input').value = rgb.b;
                        doubleOutlinePicker.querySelector('#text-hex-input').value = color;
                        doubleOutlinePicker.querySelector('#text-alpha-slider').value = 1;
                        updateDoublePreview(doubleOutlinePicker);
                    });
                    textPalette.appendChild(btn);
                });
                // Inner palette
                const innerPalette = doubleOutlinePicker.querySelector('#inner-palette');
                CONFIG.commonColors.forEach(function(color) {
                    const btn = document.createElement('button');
                    btn.style.backgroundColor = color;
                    btn.addEventListener('click', function() {
                        const rgb = hexToRgb(color);
                        doubleOutlinePicker.querySelector('#inner-r-slider').value = rgb.r;
                        doubleOutlinePicker.querySelector('#inner-g-slider').value = rgb.g;
                        doubleOutlinePicker.querySelector('#inner-b-slider').value = rgb.b;
                        doubleOutlinePicker.querySelector('#inner-r-input').value = rgb.r;
                        doubleOutlinePicker.querySelector('#inner-g-input').value = rgb.g;
                        doubleOutlinePicker.querySelector('#inner-b-input').value = rgb.b;
                        doubleOutlinePicker.querySelector('#inner-hex-input').value = color;
                        doubleOutlinePicker.querySelector('#inner-alpha-slider').value = 1;
                        updateDoublePreview(doubleOutlinePicker);
                    });
                    innerPalette.appendChild(btn);
                });
                // Outer palette
                const outerPalette = doubleOutlinePicker.querySelector('#outer-palette');
                CONFIG.commonColors.forEach(function(color) {
                    const btn = document.createElement('button');
                    btn.style.backgroundColor = color;
                    btn.addEventListener('click', function() {
                        const rgb = hexToRgb(color);
                        doubleOutlinePicker.querySelector('#outer-r-slider').value = rgb.r;
                        doubleOutlinePicker.querySelector('#outer-g-slider').value = rgb.g;
                        doubleOutlinePicker.querySelector('#outer-b-slider').value = rgb.b;
                        doubleOutlinePicker.querySelector('#outer-r-input').value = rgb.r;
                        doubleOutlinePicker.querySelector('#outer-g-input').value = rgb.g;
                        doubleOutlinePicker.querySelector('#outer-b-input').value = rgb.b;
                        doubleOutlinePicker.querySelector('#outer-hex-input').value = color;
                        doubleOutlinePicker.querySelector('#outer-alpha-slider').value = 1;
                        updateDoublePreview(doubleOutlinePicker);
                    });
                    outerPalette.appendChild(btn);
                });
                doubleOutlinePicker.querySelector('#picker-ok').addEventListener('click', function(e) {
                    const fontFamily = doubleOutlinePicker.querySelector('#font-family-select').value;
                    const fontFamilyStr = fontFamily.includes(' ') ? fontFamily + ', serif' : fontFamily + ', sans-serif';
                    const fontSize = doubleOutlinePicker.querySelector('#font-size').value + 'px';
                    var styleStr = 'font-size: ' + fontSize + '; font-family: ' + fontFamilyStr + '; ';
                    const bold = doubleOutlinePicker.querySelector('#font-bold').checked;
                    if (bold) styleStr += 'font-weight: bold; ';
                    const textR = doubleOutlinePicker.querySelector('#text-r-slider').value;
                    const textG = doubleOutlinePicker.querySelector('#text-g-slider').value;
                    const textB = doubleOutlinePicker.querySelector('#text-b-slider').value;
                    const textAlpha = doubleOutlinePicker.querySelector('#text-alpha-slider').value;
                    const textColor = (textAlpha < 1) ? 'rgba(' + textR + ', ' + textG + ', ' + textB + ', ' + textAlpha + ')' : 'rgb(' + textR + ', ' + textG + ', ' + textB + ')';
                    styleStr += 'color: ' + textColor + '; ';
                    const innerOffset = parseFloat(doubleOutlinePicker.querySelector('#inner-offset').value) || 2;
                    const innerBlur = parseFloat(doubleOutlinePicker.querySelector('#inner-blur').value) || 0;
                    const innerR = doubleOutlinePicker.querySelector('#inner-r-slider').value;
                    const innerG = doubleOutlinePicker.querySelector('#inner-g-slider').value;
                    const innerB = doubleOutlinePicker.querySelector('#inner-b-slider').value;
                    const innerAlpha = doubleOutlinePicker.querySelector('#inner-alpha-slider').value;
                    const innerColor = (innerAlpha < 1) ? 'rgba(' + innerR + ', ' + innerG + ', ' + innerB + ', ' + innerAlpha + ')' : 'rgb(' + innerR + ', ' + innerG + ', ' + innerB + ')';
                    const outerOffset = parseFloat(doubleOutlinePicker.querySelector('#outer-offset').value) || 3;
                    const outerBlur = parseFloat(doubleOutlinePicker.querySelector('#outer-blur').value) || 0;
                    const outerR = doubleOutlinePicker.querySelector('#outer-r-slider').value;
                    const outerG = doubleOutlinePicker.querySelector('#outer-g-slider').value;
                    const outerB = doubleOutlinePicker.querySelector('#outer-b-slider').value;
                    const outerAlpha = doubleOutlinePicker.querySelector('#outer-alpha-slider').value;
                    const outerColor = (outerAlpha < 1) ? 'rgba(' + outerR + ', ' + outerG + ', ' + outerB + ', ' + outerAlpha + ')' : 'rgb(' + outerR + ', ' + outerG + ', ' + outerB + ')';
                    // Inner shadows (8 dir)
                    const absInner = Math.abs(innerOffset);
                    var innerRule = '-' + absInner + 'px -' + absInner + 'px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    '-' + absInner + 'px 0px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    '-' + absInner + 'px ' + absInner + 'px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    '0px -' + absInner + 'px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    '0px ' + absInner + 'px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    absInner + 'px -' + absInner + 'px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    absInner + 'px 0px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    absInner + 'px ' + absInner + 'px ' + innerBlur + 'px ' + innerColor;
                    // Outer shadows (8 dir)
                    const absOuter = Math.abs(outerOffset);
                    var outerRule = '-' + absOuter + 'px -' + absOuter + 'px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    '-' + absOuter + 'px 0px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    '-' + absOuter + 'px ' + absOuter + 'px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    '0px -' + absOuter + 'px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    '0px ' + absOuter + 'px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    absOuter + 'px -' + absOuter + 'px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    absOuter + 'px 0px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    absOuter + 'px ' + absOuter + 'px ' + outerBlur + 'px ' + outerColor;
                    styleStr += 'text-shadow: ' + innerRule + ', ' + outerRule + ';';
                    var openTag = '<span style="' + styleStr + '">';
                    applyFormat(ta, openTag, '</span>');
                    closeDoubleOutlinePicker();
                });
                doubleOutlinePicker.querySelector('#picker-cancel').addEventListener('click', closeDoubleOutlinePicker);
                overlay.addEventListener('click', closeDoubleOutlinePicker);
                var escHandler = function(e) {
                    if (e.key === 'Escape') closeDoubleOutlinePicker();
                };
                window.addEventListener('keydown', escHandler);
                // Eyedropper for text
                const eyedropperTextBtn = doubleOutlinePicker.querySelector('#eyedropper-text');
                addColorZillaLink(eyedropperTextBtn);
                if (eyedropperTextBtn && 'eyedropper' in navigator) {
                    eyedropperTextBtn.addEventListener('click', async function() {
                        try {
                            const result = await navigator.eyedropper.pick({withAlpha: true});
                            if (result && result.sRGBHex) {
                                const hexFull = result.sRGBHex;
                                const alphaHex = hexFull.slice(-2);
                                const hex = '#' + hexFull.slice(1,7).toUpperCase();
                                const r = parseInt(hexFull.slice(1,3), 16);
                                const g = parseInt(hexFull.slice(3,5), 16);
                                const b = parseInt(hexFull.slice(5,7), 16);
                                const alpha = parseInt(alphaHex, 16) / 255;
                                doubleOutlinePicker.querySelector('#text-r-slider').value = r;
                                doubleOutlinePicker.querySelector('#text-g-slider').value = g;
                                doubleOutlinePicker.querySelector('#text-b-slider').value = b;
                                doubleOutlinePicker.querySelector('#text-r-input').value = r;
                                doubleOutlinePicker.querySelector('#text-g-input').value = g;
                                doubleOutlinePicker.querySelector('#text-b-input').value = b;
                                doubleOutlinePicker.querySelector('#text-hex-input').value = hex;
                                doubleOutlinePicker.querySelector('#text-alpha-slider').value = alpha;
                                doubleOutlinePicker.querySelector('#text-alpha-value').textContent = alpha.toFixed(2);
                                updateDoublePreview(doubleOutlinePicker);
                            }
                        } catch (err) {
                            log('Eyedropper error:', err);
                            showDebugIndicator('Eyedropper cancelled or failed.', 'info');
                        }
                    });
                } else if (eyedropperTextBtn) {
                    eyedropperTextBtn.disabled = true;
                    eyedropperTextBtn.title = 'EyeDropper API not supported in this browser';
                }
                // Eyedropper for inner
                const eyedropperInnerBtn = doubleOutlinePicker.querySelector('#eyedropper-inner');
                addColorZillaLink(eyedropperInnerBtn);
                if (eyedropperInnerBtn && 'eyedropper' in navigator) {
                    eyedropperInnerBtn.addEventListener('click', async function() {
                        try {
                            const result = await navigator.eyedropper.pick({withAlpha: true});
                            if (result && result.sRGBHex) {
                                const hexFull = result.sRGBHex;
                                const alphaHex = hexFull.slice(-2);
                                const hex = '#' + hexFull.slice(1,7).toUpperCase();
                                const r = parseInt(hexFull.slice(1,3), 16);
                                const g = parseInt(hexFull.slice(3,5), 16);
                                const b = parseInt(hexFull.slice(5,7), 16);
                                const alpha = parseInt(alphaHex, 16) / 255;
                                doubleOutlinePicker.querySelector('#inner-r-slider').value = r;
                                doubleOutlinePicker.querySelector('#inner-g-slider').value = g;
                                doubleOutlinePicker.querySelector('#inner-b-slider').value = b;
                                doubleOutlinePicker.querySelector('#inner-r-input').value = r;
                                doubleOutlinePicker.querySelector('#inner-g-input').value = g;
                                doubleOutlinePicker.querySelector('#inner-b-input').value = b;
                                doubleOutlinePicker.querySelector('#inner-hex-input').value = hex;
                                doubleOutlinePicker.querySelector('#inner-alpha-slider').value = alpha;
                                doubleOutlinePicker.querySelector('#inner-alpha-value').textContent = alpha.toFixed(2);
                                updateDoublePreview(doubleOutlinePicker);
                            }
                        } catch (err) {
                            log('Eyedropper error:', err);
                            showDebugIndicator('Eyedropper cancelled or failed.', 'info');
                        }
                    });
                } else if (eyedropperInnerBtn) {
                    eyedropperInnerBtn.disabled = true;
                    eyedropperInnerBtn.title = 'EyeDropper API not supported in this browser';
                }
                // Eyedropper for outer
                const eyedropperOuterBtn = doubleOutlinePicker.querySelector('#eyedropper-outer');
                addColorZillaLink(eyedropperOuterBtn);
                if (eyedropperOuterBtn && 'eyedropper' in navigator) {
                    eyedropperOuterBtn.addEventListener('click', async function() {
                        try {
                            const result = await navigator.eyedropper.pick({withAlpha: true});
                            if (result && result.sRGBHex) {
                                const hexFull = result.sRGBHex;
                                const alphaHex = hexFull.slice(-2);
                                const hex = '#' + hexFull.slice(1,7).toUpperCase();
                                const r = parseInt(hexFull.slice(1,3), 16);
                                const g = parseInt(hexFull.slice(3,5), 16);
                                const b = parseInt(hexFull.slice(5,7), 16);
                                const alpha = parseInt(alphaHex, 16) / 255;
                                doubleOutlinePicker.querySelector('#outer-r-slider').value = r;
                                doubleOutlinePicker.querySelector('#outer-g-slider').value = g;
                                doubleOutlinePicker.querySelector('#outer-b-slider').value = b;
                                doubleOutlinePicker.querySelector('#outer-r-input').value = r;
                                doubleOutlinePicker.querySelector('#outer-g-input').value = g;
                                doubleOutlinePicker.querySelector('#outer-b-input').value = b;
                                doubleOutlinePicker.querySelector('#outer-hex-input').value = hex;
                                doubleOutlinePicker.querySelector('#outer-alpha-slider').value = alpha;
                                doubleOutlinePicker.querySelector('#outer-alpha-value').textContent = alpha.toFixed(2);
                                updateDoublePreview(doubleOutlinePicker);
                            }
                        } catch (err) {
                            log('Eyedropper error:', err);
                            showDebugIndicator('Eyedropper cancelled or failed.', 'info');
                        }
                    });
                } else if (eyedropperOuterBtn) {
                    eyedropperOuterBtn.disabled = true;
                    eyedropperOuterBtn.title = 'EyeDropper API not supported in this browser';
                }
                function updateDoublePreview(picker) {
                    const fontFamily = picker.querySelector('#font-family-select').value;
                    const fontFamilyStr = fontFamily.includes(' ') ? fontFamily + ', serif' : fontFamily + ', sans-serif';
                    const fontSize = picker.querySelector('#font-size').value + 'px';
                    const bold = picker.querySelector('#font-bold').checked ? 'bold' : 'normal';
                    const textR = picker.querySelector('#text-r-slider').value;
                    const textG = picker.querySelector('#text-g-slider').value;
                    const textB = picker.querySelector('#text-b-slider').value;
                    const textAlpha = picker.querySelector('#text-alpha-slider').value;
                    const textColor = (textAlpha < 1) ? 'rgba(' + textR + ', ' + textG + ', ' + textB + ', ' + textAlpha + ')' : 'rgb(' + textR + ', ' + textG + ', ' + textB + ')';
                    const innerOffset = parseFloat(picker.querySelector('#inner-offset').value) || 2;
                    const innerBlur = parseFloat(picker.querySelector('#inner-blur').value) || 0;
                    const innerR = picker.querySelector('#inner-r-slider').value;
                    const innerG = picker.querySelector('#inner-g-slider').value;
                    const innerB = picker.querySelector('#inner-b-slider').value;
                    const innerAlpha = picker.querySelector('#inner-alpha-slider').value;
                    const innerColor = (innerAlpha < 1) ? 'rgba(' + innerR + ', ' + innerG + ', ' + innerB + ', ' + innerAlpha + ')' : 'rgb(' + innerR + ', ' + innerG + ', ' + innerB + ')';
                    const outerOffset = parseFloat(picker.querySelector('#outer-offset').value) || 3;
                    const outerBlur = parseFloat(picker.querySelector('#outer-blur').value) || 0;
                    const outerR = picker.querySelector('#outer-r-slider').value;
                    const outerG = picker.querySelector('#outer-g-slider').value;
                    const outerB = picker.querySelector('#outer-b-slider').value;
                    const outerAlpha = picker.querySelector('#outer-alpha-slider').value;
                    const outerColor = (outerAlpha < 1) ? 'rgba(' + outerR + ', ' + outerG + ', ' + outerB + ', ' + outerAlpha + ')' : 'rgb(' + outerR + ', ' + outerG + ', ' + outerB + ')';
                    const absInner = Math.abs(innerOffset);
                    var innerRule = '-' + absInner + 'px -' + absInner + 'px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    '-' + absInner + 'px 0px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    '-' + absInner + 'px ' + absInner + 'px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    '0px -' + absInner + 'px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    '0px ' + absInner + 'px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    absInner + 'px -' + absInner + 'px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    absInner + 'px 0px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    absInner + 'px ' + absInner + 'px ' + innerBlur + 'px ' + innerColor;
                    const absOuter = Math.abs(outerOffset);
                    var outerRule = '-' + absOuter + 'px -' + absOuter + 'px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    '-' + absOuter + 'px 0px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    '-' + absOuter + 'px ' + absOuter + 'px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    '0px -' + absOuter + 'px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    '0px ' + absOuter + 'px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    absOuter + 'px -' + absOuter + 'px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    absOuter + 'px 0px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    absOuter + 'px ' + absOuter + 'px ' + outerBlur + 'px ' + outerColor;
                    const preview = picker.querySelector('#double-preview');
                    preview.style.fontFamily = fontFamilyStr;
                    preview.style.fontSize = fontSize;
                    preview.style.fontWeight = bold;
                    preview.style.color = textColor;
                    preview.style.textShadow = innerRule + ', ' + outerRule;
                    picker.querySelector('#text-alpha-value').textContent = textAlpha;
                    picker.querySelector('#inner-alpha-value').textContent = innerAlpha;
                    picker.querySelector('#outer-alpha-value').textContent = outerAlpha;
                    const textHex = '#' + Math.round(textR).toString(16).padStart(2, '0') + Math.round(textG).toString(16).padStart(2, '0') + Math.round(textB).toString(16).padStart(2, '0');
                    picker.querySelector('#text-hex-input').value = textHex.toUpperCase();
                    const innerHex = '#' + Math.round(innerR).toString(16).padStart(2, '0') + Math.round(innerG).toString(16).padStart(2, '0') + Math.round(innerB).toString(16).padStart(2, '0');
                    picker.querySelector('#inner-hex-input').value = innerHex.toUpperCase();
                    const outerHex = '#' + Math.round(outerR).toString(16).padStart(2, '0') + Math.round(outerG).toString(16).padStart(2, '0') + Math.round(outerB).toString(16).padStart(2, '0');
                    picker.querySelector('#outer-hex-input').value = outerHex.toUpperCase();
                }
                // Update sliders for text/inner/outer (similar to pink)
                function updateTextSlider(channel, picker) {
                    const input = picker.querySelector('#text-' + channel + '-input').value;
                    picker.querySelector('#text-' + channel + '-slider').value = input;
                    updateDoublePreview(picker);
                }
                function updateInnerSlider(channel, picker) {
                    const input = picker.querySelector('#inner-' + channel + '-input').value;
                    picker.querySelector('#inner-' + channel + '-slider').value = input;
                    updateDoublePreview(picker);
                }
                function updateOuterSlider(channel, picker) {
                    const input = picker.querySelector('#outer-' + channel + '-input').value;
                    picker.querySelector('#outer-' + channel + '-slider').value = input;
                    updateDoublePreview(picker);
                }
                doubleOutlinePicker.addEventListener('input', function(e) {
                    if (e.target.id === 'font-family-select' || e.target.id === 'font-size' || e.target.id === 'font-bold' || e.target.id === 'text-alpha-slider' || e.target.id === 'inner-offset' || e.target.id === 'inner-blur' || e.target.id === 'inner-alpha-slider' || e.target.id === 'outer-offset' || e.target.id === 'outer-blur' || e.target.id === 'outer-alpha-slider') {
                        updateDoublePreview(doubleOutlinePicker);
                    } else if (e.target.matches('#text-r-slider, #text-g-slider, #text-b-slider')) {
                        updateDoublePreview(doubleOutlinePicker);
                    } else if (e.target.matches('#inner-r-slider, #inner-g-slider, #inner-b-slider')) {
                        updateDoublePreview(doubleOutlinePicker);
                    } else if (e.target.matches('#outer-r-slider, #outer-g-slider, #outer-b-slider')) {
                        updateDoublePreview(doubleOutlinePicker);
                    } else if (e.target.id.includes('text-') && e.target.id.includes('input') && e.target.type === 'number') {
                        const channel = e.target.id.replace('text-', '').replace('-input', '');
                        updateTextSlider(channel, doubleOutlinePicker);
                    } else if (e.target.id.includes('inner-') && e.target.id.includes('input') && e.target.type === 'number') {
                        const channel = e.target.id.replace('inner-', '').replace('-input', '');
                        updateInnerSlider(channel, doubleOutlinePicker);
                    } else if (e.target.id.includes('outer-') && e.target.id.includes('input') && e.target.type === 'number') {
                        const channel = e.target.id.replace('outer-', '').replace('-input', '');
                        updateOuterSlider(channel, doubleOutlinePicker);
                    } else if (e.target.id === 'text-hex-input') {
                        const hexValue = e.target.value.replace('#', '').toUpperCase();
                        if (hexValue.match(/^([0-9A-F]{6})$/)) {
                            const rgb = hexToRgb(hexValue);
                            doubleOutlinePicker.querySelector('#text-r-slider').value = rgb.r;
                            doubleOutlinePicker.querySelector('#text-g-slider').value = rgb.g;
                            doubleOutlinePicker.querySelector('#text-b-slider').value = rgb.b;
                            doubleOutlinePicker.querySelector('#text-r-input').value = rgb.r;
                            doubleOutlinePicker.querySelector('#text-g-input').value = rgb.g;
                            doubleOutlinePicker.querySelector('#text-b-input').value = rgb.b;
                            updateDoublePreview(doubleOutlinePicker);
                        }
                    } else if (e.target.id === 'inner-hex-input') {
                        const hexValue = e.target.value.replace('#', '').toUpperCase();
                        if (hexValue.match(/^([0-9A-F]{6})$/)) {
                            const rgb = hexToRgb(hexValue);
                            doubleOutlinePicker.querySelector('#inner-r-slider').value = rgb.r;
                            doubleOutlinePicker.querySelector('#inner-g-slider').value = rgb.g;
                            doubleOutlinePicker.querySelector('#inner-b-slider').value = rgb.b;
                            doubleOutlinePicker.querySelector('#inner-r-input').value = rgb.r;
                            doubleOutlinePicker.querySelector('#inner-g-input').value = rgb.g;
                            doubleOutlinePicker.querySelector('#inner-b-input').value = rgb.b;
                            updateDoublePreview(doubleOutlinePicker);
                        }
                    } else if (e.target.id === 'outer-hex-input') {
                        const hexValue = e.target.value.replace('#', '').toUpperCase();
                        if (hexValue.match(/^([0-9A-F]{6})$/)) {
                            const rgb = hexToRgb(hexValue);
                            doubleOutlinePicker.querySelector('#outer-r-slider').value = rgb.r;
                            doubleOutlinePicker.querySelector('#outer-g-slider').value = rgb.g;
                            doubleOutlinePicker.querySelector('#outer-b-slider').value = rgb.b;
                            doubleOutlinePicker.querySelector('#outer-r-input').value = rgb.r;
                            doubleOutlinePicker.querySelector('#outer-g-input').value = rgb.g;
                            doubleOutlinePicker.querySelector('#outer-b-input').value = rgb.b;
                            updateDoublePreview(doubleOutlinePicker);
                        }
                    }
                });
                updateDoublePreview(doubleOutlinePicker);
            } catch (err) {
                log('Double outline picker open error', err);
                showDebugIndicator('Double outline picker failed; check console.', 'error');
            }
        }
        // Open symbols palette picker
        function openSymbolsPicker(ta) {
            try {
                closeAllPickers();
                closeResizeSettings();
                const overlay = document.createElement('div');
                overlay.id = 'symbol-picker-overlay';
                overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:9998;';
                document.body.appendChild(overlay);
                symbolPicker = document.createElement('div');
                symbolPicker.id = 'symbol-picker';
                symbolPicker.innerHTML = '<h3>Symbols Palette</h3>' +
                    '<div class="symbols-palette"></div>' +
                    '<div class="picker-buttons">' +
                    '<button id="picker-cancel">Close</button>' +
                    '</div>';
                document.body.appendChild(symbolPicker);
                const palette = symbolPicker.querySelector('.symbols-palette');
                CONFIG.symbols.forEach(function(sym) {
                    const btn = document.createElement('button');
                    btn.innerHTML = sym;
                    btn.title = 'Insert ' + sym;
                    btn.addEventListener('click', function() {
                        insertAtCursor(ta, sym);
                        closeSymbolsPicker();
                    });
                    palette.appendChild(btn);
                });
                symbolPicker.querySelector('#picker-cancel').addEventListener('click', closeSymbolsPicker);
                overlay.addEventListener('click', closeSymbolsPicker);
                var escHandler = function(e) {
                    if (e.key === 'Escape') closeSymbolsPicker();
                };
                window.addEventListener('keydown', escHandler);
            } catch (err) {
                log('Symbols picker open error', err);
                showDebugIndicator('Symbols picker failed; check console.', 'error');
            }
        }
        // Clear formatting: Strip HTML tags
        function clearFormatting(ta) {
            try {
                const text = ta.value.replace(/<[^>]*>/g, '');
                ta.value = text;
                ta.selectionStart = ta.selectionEnd = 0;
                ta.focus();
                ta.dispatchEvent(new Event('input', { bubbles: true }));
                // Post-snapshot after clear
                saveState(ta);
                // Update preview
                if (CONFIG.previewEnabled) debounceUpdatePreview();
                updateCharCount();
                log('Formatting cleared.');
            } catch (err) {
                log('Clear formatting error', err);
            }
        }
        // Insert link prompt with intelligent defaults
        function insertLink(ta) {
            try {
                const start = ta.selectionStart;
                const end = ta.selectionEnd;
                const selected = ta.value.substring(start, end);
                const url = prompt('Enter URL:');
                if (!url) return; // No post-save if cancelled
                var text;
                if (selected) {
                    text = prompt('Enter link text (optional):', selected) || selected;
                } else {
                    text = prompt('Enter link text (optional):', '') || url;
                }
                const linkHTML = '<a href="' + url + '">' + text + '</a>';
                insertAtCursor(ta, linkHTML); // Post-save inside insertAtCursor
            } catch (err) {
                log('Link insert error', err);
            }
        }
        // Insert text at cursor
        function insertAtCursor(ta, text) {
            try {
                const start = ta.selectionStart;
                const end = ta.selectionEnd;
                ta.value = ta.value.substring(0, start) + text + ta.value.substring(end);
                ta.selectionStart = ta.selectionEnd = start + text.length;
                ta.focus();
                ta.dispatchEvent(new Event('input', { bubbles: true }));
                // Post-snapshot after insert
                saveState(ta);
                // Update preview
                if (CONFIG.previewEnabled) debounceUpdatePreview();
                updateCharCount();
            } catch (err) {
                log('Insert at cursor error', err);
            }
        }
        // History management for undo/redo - now saves {value, start, end} - debounced save
        function saveState(ta) {
            try {
                if (debounceTimer) clearTimeout(debounceTimer);
                debounceTimer = setTimeout(function() {
                    const state = {
                        value: ta.value,
                        start: ta.selectionStart,
                        end: ta.selectionEnd
                    };
                    // Trim history to limit
                    if (history.length > CONFIG.historyLimit) {
                        history.shift();
                        if (historyIndex > 0) historyIndex--;
                    }
                    // Push current state after current index (linear, discards branches)
                    history = history.slice(0, historyIndex + 1);
                    history.push(state);
                    historyIndex = history.length - 1;
                    log('State saved. History length: ' + history.length);
                }, CONFIG.debounceDelay);
            } catch (err) {
                log('Save state error', err);
            }
        }
        function undo(ta) {
            try {
                if (historyIndex > 0) {
                    historyIndex--;
                    const state = history[historyIndex];
                    ta.value = state.value;
                    ta.selectionStart = state.start;
                    ta.selectionEnd = state.end;
                    ta.focus();
                    ta.dispatchEvent(new Event('input', { bubbles: true }));
                    if (CONFIG.previewEnabled) debounceUpdatePreview();
                    updateCharCount();
                    log('Undo to index ' + historyIndex);
                } else {
                    log('Undo: At start of history.');
                }
            } catch (err) {
                log('Undo error', err);
            }
        }
        function redo(ta) {
            try {
                if (historyIndex < history.length - 1) {
                    historyIndex++;
                    const state = history[historyIndex];
                    ta.value = state.value;
                    ta.selectionStart = state.start;
                    ta.selectionEnd = state.end;
                    ta.focus();
                    ta.dispatchEvent(new Event('input', { bubbles: true }));
                    if (CONFIG.previewEnabled) debounceUpdatePreview();
                    updateCharCount();
                    log('Redo to index ' + historyIndex);
                } else {
                    log('Redo: At end of history.');
                }
            } catch (err) {
                log('Redo error', err);
            }
        }
        // Setup initial history snapshot (no ongoing listener)
        function setupHistory(ta) {
            try {
                // Initial save (pre-any-changes backup)
                saveState(ta);
                log('Initial history snapshot set up.');
            } catch (err) {
                log('History setup error', err);
            }
        }
        // Keyboard shortcuts (per-tab, checks focus) - extended for undo/redo + copy/paste/clear format
        function setupShortcuts() {
            try {
                // Remove existing listener to avoid duplicates in multi-tab/reload
                document.removeEventListener('keydown', handleKeydown);
                document.addEventListener('keydown', handleKeydown, true);
                log('Shortcuts set up.');
            } catch (err) {
                log('Shortcuts setup error', err);
            }
        }
        function handleKeydown(e) {
            try {
                if (!textarea || document.activeElement !== textarea) return;
                if (e.ctrlKey) {
                    if (e.key === 'b') {
                        e.preventDefault();
                        CONFIG.buttons.find(function(b) { return b.id === 'bold-btn'; }).action(textarea);
                    } else if (e.key === 'i') {
                        e.preventDefault();
                        CONFIG.buttons.find(function(b) { return b.id === 'italic-btn'; }).action(textarea);
                    } else if (e.key === 'u') {
                        e.preventDefault();
                        CONFIG.buttons.find(function(b) { return b.id === 'underline-btn'; }).action(textarea);
                    } else if (e.key === 'z' && !e.shiftKey) {
                        e.preventDefault();
                        CONFIG.buttons.find(function(b) { return b.id === 'undo-btn'; }).action(textarea);
                    } else if (e.key === 'y' || (e.key === 'z' && e.shiftKey)) {
                        e.preventDefault();
                        CONFIG.buttons.find(function(b) { return b.id === 'redo-btn'; }).action(textarea);
                    } else if (e.shiftKey && e.key === 'C') {
                        e.preventDefault();
                        CONFIG.buttons.find(function(b) { return b.id === 'copy-format-btn'; }).action(textarea);
                    } else if (e.shiftKey && e.key === 'V') {
                        e.preventDefault();
                        CONFIG.buttons.find(function(b) { return b.id === 'paste-format-btn'; }).action(textarea);
                    } else if (e.shiftKey && e.key === 'X') {
                        e.preventDefault();
                        CONFIG.buttons.find(function(b) { return b.id === 'clear-format-btn'; }).action(textarea);
                    }
                }
            } catch (err) {
                log('Keydown handler error', err);
            }
        }
        // Manual force inject handler - defined early to avoid ReferenceError
        function setupManualTrigger() {
            try {
                if (manualTriggerHandler) return;
                manualTriggerHandler = function(e) {
                    if (e.ctrlKey && e.shiftKey && e.key === 'I') {
                        e.preventDefault();
                        const dialog = document.querySelector('.note-edit-dialog');
                        if (dialog) {
                            injectToolbar(dialog, true); // Force with banner
                            log('Manual trigger: Forced injection.');
                        } else {
                            showDebugIndicator('Manual trigger: No dialog open.', 'error');
                        }
                    }
                };
                document.addEventListener('keydown', manualTriggerHandler, true);
                log('Manual trigger (Ctrl+Shift+I) set up.');
            } catch (err) {
                log('Manual trigger setup error', err);
            }
        }
        // Dialog injection: Insert ribbon above textarea, preview below
        function injectToolbar(dialog, isManual) {
            if (typeof isManual === 'undefined') isManual = false;
            try {
                log('Attempting injection for dialog:', dialog);
                const content = dialog.querySelector('.ui-dialog-content');
                if (!content) {
                    log('No .ui-dialog-content found.');
                    return;
                }
                const headerSpan = content.querySelector('span');
                if (!headerSpan) {
                    log('No header span found.');
                    return;
                }
                textarea = content.querySelector('textarea');
                if (!textarea) {
                    log('No textarea found in content.');
                    return;
                }
                // Stable injection check: Use dataset attribute to prevent re-inject
                if (dialog.dataset.helperInjected === 'true') {
                    log('Dialog already injected (dataset check). Skipping.');
                    return;
                }
                // Remove any existing toolbar in content
                const existing = content.querySelector('#' + CONFIG.toolbarId);
                if (existing) {
                    existing.remove();
                    log('Removed existing toolbar.');
                }
                const tb = createToolbar();
                if (tb) {
                    content.insertBefore(tb, textarea); // Insert after header, before textarea
                    log('Formatting ribbon inserted above textarea.');
                } else {
                    log('Failed to create toolbar.');
                    return;
                }
                injectPreview(dialog);
                // Early textarea height adjustment for immediate fit
                const taFullHeight = Math.max(200, textarea.scrollHeight);
                textarea.style.height = taFullHeight + 'px';
                // Mark as injected
                dialog.dataset.helperInjected = 'true';
                setupShortcuts(); // Re-setup for this textarea
                setupHistory(textarea); // Initial snapshot only
                // Setup char count listener
                textarea.addEventListener('input', function() {
                    updateCharCount();
                    if (CONFIG.previewEnabled) debounceUpdatePreview();
                });
                updateCharCount();
                // Auto-resize dialog immediately after insertion (increased delay for layout settle)
                setTimeout(function() { autoResizeDialog(dialog); }, 150);
                // Cleanup on dialog close - remove dataset, but keep copiedFormat global
                const observerCleanup = new MutationObserver(function(mutations) {
                    if (!document.body.contains(dialog)) {
                        activeDialogs.delete(dialog); // Track by reference now
                        if (toolbar) {
                            if (toolbar.parentNode) toolbar.remove();
                            toolbar = null;
                        }
                        if (previewDiv) {
                            if (previewDiv.parentNode) previewDiv.remove();
                            previewDiv = null;
                        }
                        textarea = null;
                        delete dialog.dataset.helperInjected;
                        delete dialog.dataset.helperResized;
                        history = [];
                        historyIndex = -1;
                        closeAllPickers();
                        closeResizeSettings();
                        observerCleanup.disconnect();
                        log('Cleaned up for closed dialog.');
                    }
                });
                observerCleanup.observe(document.body, { childList: true, subtree: true });
                // Only show banner on first auto or manual
                if (isManual || !activeDialogs.has(dialog)) {
                    log('Ribbon injected above textarea for dialog.');
                    showDebugIndicator('Word-like Ribbon Injected (Note Utils group added)! DOM-based CF (accurate attrs/nested/partial), Snippets Manager (preview/edit/delete/export/import), Eyedropper (Firefox+ColorZilla), Live Preview (default on, fixed toggle), Char Count, SFX Ref, Drag-to-Set Size Modal available.', 'success');
                }
                activeDialogs.add(dialog); // Track by reference for multi-dialog
            } catch (err) {
                log('Injection error', err);
                showDebugIndicator('Injection Failed - Check Console. Try Ctrl+Shift+I.', 'error');
                // Autonomous recovery: Poll will retry
            }
        }
        // Debug indicator: Temporary overlay for visibility issues
        function showDebugIndicator(message, type) {
            if (typeof type === 'undefined') type = 'info';
            try {
                if (!debugMode) return;
                var indicator = document.getElementById('debug-indicator');
                if (!indicator) {
                    indicator = document.createElement('div');
                    indicator.id = 'debug-indicator';
                    document.body.appendChild(indicator);
                }
                indicator.textContent = '[NoteFmtHelper] ' + message;
                indicator.style.background = (type === 'error') ? '#ffcccc' : (type === 'success') ? '#ccffcc' : (type === 'warning') ? '#fff3cd' : '#ffeb3b';
                indicator.style.display = 'block';
                setTimeout(function() { indicator.style.display = 'none'; }, 5000); // Shorter now to reduce spam
                log(message);
            } catch (err) {
                console.error('[NoteFmtHelper] Debug indicator error', err);
            }
        }
        // Main observer for dialog detection (subtree for nested changes)
        function setupObserver() {
            try {
                if (observer) {
                    log('Observer already set up.');
                    return;
                }
                observer = new MutationObserver(function(mutations) {
                    var detected = false;
                    mutations.forEach(function(mutation) {
                        if (mutation.type === 'childList') {
                            mutation.addedNodes.forEach(function(node) {
                                if (node.nodeType === 1 && node.matches && node.matches('.note-edit-dialog')) {
                                    log('Dialog added via observer:', node);
                                    detected = true;
                                    setTimeout(function() { injectToolbar(node); }, 500); // Keep for render stability
                                }
                            });
                        }
                    });
                    if (detected) log('Observer triggered injection.');
                });
                observer.observe(document.body, { childList: true, subtree: true });
            } catch (err) {
                log('Observer setup error', err);
            }
        }
        // Fallback poll for edge cases (e.g., observer misses, multi-tab glitches) - faster for persistence
        var pollInterval = null;
        function startPoll() {
            try {
                if (pollInterval) return;
                pollInterval = setInterval(function() {
                    const dialogs = document.querySelectorAll('.note-edit-dialog');
                    if (dialogs.length === 0) {
                        if (toolbar) {
                            toolbar.remove();
                            toolbar = null;
                            activeDialogs.clear();
                            log('No dialogs; cleaned up.');
                        }
                        return;
                    }
                    dialogs.forEach(function(dialog) {
                        // Use dataset check here too
                        if (dialog.dataset.helperInjected !== 'true') {
                            const content = dialog.querySelector('.ui-dialog-content');
                            const ta = content ? content.querySelector('textarea') : null;
                            if (content && ta) {
                                log('Poll detected eligible dialog:', dialog);
                                injectToolbar(dialog);
                            }
                        }
                    });
                }, 3000); // Slower poll (3s) now that dataset prevents retry
                log('Poll started (every 3s).');
            } catch (err) {
                log('Poll start error', err);
            }
        }
        // Init: Robust, multi-tab safe - load configs first
        function init() {
            try {
                loadConfigs(); // Load user settings first
                injectStyles();
                setupManualTrigger(); // Defined early, no ReferenceError
                setTimeout(setupObserver, 2000); // Delay observer to avoid interference with initial note drag mode
                startPoll(); // Poll starts immediately for reliability
                // Multi-tab: Listen for focus to re-inject if needed
                window.addEventListener('focus', function(e) {
                    setTimeout(function() {
                        if (pollInterval) clearInterval(pollInterval);
                        startPoll();
                    }, 100);
                });
                showDebugIndicator('Script Loaded v' + CONFIG.version + '! Note Utils Group, Enhanced Preview, Drag-to-Set Size ready. Ctrl+Shift+I to force.', 'info');
                log('Note Formatting Helper v' + CONFIG.version + ' initialized. Ready for multi-tab use. Open DevTools > Console to monitor logs.');
            } catch (err) {
                console.error('[NoteFmtHelper] Init error:', err);
                showDebugIndicator('Init error - script partially loaded. Try Ctrl+Shift+I on dialog open.', 'error');
            }
        }
        // DOM ready check
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', init);
        } else {
            init();
        }
        // Handle page navigation (Danbooru hash changes)
        window.addEventListener('hashchange', function(e) {
            setTimeout(function() {
                if (pollInterval) clearInterval(pollInterval);
                startPoll();
            }, 500);
            log('Page navigated; re-setup observers.');
        });
    } catch (globalErr) {
        console.error('[NoteFmtHelper] Global error - script failed to load:', globalErr);
        // Fallback: Create minimal debug indicator
        const indicator = document.createElement('div');
        indicator.id = 'debug-indicator';
        indicator.style.cssText = 'position:fixed;top:10px;right:10px;background:#ffcccc;color:#000;padding:5px;border:1px solid #ccc;z-index:9999;';
        indicator.textContent = '[NoteFmtHelper] Fatal error - reinstall script. Check console.';
        document.body.appendChild(indicator);
    }
})();