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.

// ==UserScript==
// @name         Danbooru Note Formatting Helper
// @namespace    http://tampermonkey.net/
// @version      1.62.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.62.0: Fixed Change Case marker removal by collecting comment nodes first before deleting to avoid TreeWalker iteration issues during DOM mutation; ensures clean output without leftover <!--NFH_END-->; total lines ~1980.
- v1.61.0: Fixed Copy Format for full-note scenarios by replacing DOM outerHTML truncation with regex extraction of full nested opening tag prefix ((?:<[^>\/]+>)+) and closing suffix ((?:<\/[^>]+>)+), ensuring balanced count and content presence; captures complete nesting like <b><i><u><s>...</s></u></i></b> even without selection; total lines ~1975.
- v1.60.0: Fixed ReferenceError in modal close functions by replacing local escHandler with global shared escHandler for Escape key, ensuring proper event listener removal across all modals without scope issues; total lines ~1965.
- v1.59.0: Fixed Copy/Paste Format nested tag order by reversing parents for open tags and adjusting close concatenation (now captures <span><b> correctly); added Change Case button/modal to right of Font dropdown (Sentence/Lower/Upper/Toggle/Capitalize options via DOM text node traversal, preserves HTML/tags, auto-applies to full note if no selection); total lines ~1960.
- 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.62.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: 'change-case-btn',
                    label: 'Aa',
                    title: 'Change Case (Sentence, Lower, Upper, Toggle, Capitalize Each Word)',
                    action: function(ta) { openChangeCaseModal(ta); }
                },
                {
                    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': '🔊',
                'change-case-btn': 'Aa'
            },
            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, #change-case-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; " +
                    "} " +
                    "#change-case-modal { min-width: 250px; } " +
                    "#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, #change-case-modal h3 { " +
                    "margin: 0 0 10px 0; " +
                    "text-align: center; " +
                    "color: #F0F0F0; " +
                    "} " +
                    "#resize-settings-modal h3 { color: #000; } " +
                    "#change-case-modal h3 { color: #F0F0F0; } " +
                    ".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, #change-case-modal-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; " +
                    "} " +
                    "#change-case-modal .option { " +
                    "display: flex; " +
                    "align-items: center; " +
                    "gap: 10px; " +
                    "margin-bottom: 10px; " +
                    "} " +
                    "#change-case-modal input[type=\"radio\"] { " +
                    "margin: 0; " +
                    "} " +
                    "#change-case-modal label { " +
                    "cursor: pointer; " +
                    "color: #E0E0E0; " +
                    "} " +
                    "@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 changeCaseModal = null; // Change case 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
        var globalEscHandler = function(e) {
            if (e.key === 'Escape') {
                closeAllPickers();
                closeResizeSettings();
            }
        };
        // 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);
                window.addEventListener('keydown', globalEscHandler);
                log('Snippets manager opened.');
            } catch (err) {
                log('Snippets manager open error', err);
                showDebugIndicator('Snippets manager failed; check console.', 'error');
            }
        }
        function closeSnippetsManager() {
            window.removeEventListener('keydown', globalEscHandler);
            if (snippetsManager) {
                const overlay = document.getElementById('snippets-manager-overlay');
                if (overlay) overlay.remove();
                snippetsManager.remove();
                snippetsManager = null;
            }
        }
        // Open Change Case Modal
        function openChangeCaseModal(ta) {
            try {
                closeAllPickers();
                closeResizeSettings();
                const overlay = document.createElement('div');
                overlay.id = 'change-case-modal-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);
                changeCaseModal = document.createElement('div');
                changeCaseModal.id = 'change-case-modal';
                changeCaseModal.innerHTML = '<h3>Change Case</h3>' +
                    '<div class="options">' +
                    '<div class="option"><input type="radio" name="case" value="sentence" id="case-sentence" checked><label for="case-sentence">Sentence Case</label></div>' +
                    '<div class="option"><input type="radio" name="case" value="lower" id="case-lower"><label for="case-lower">Lower Case</label></div>' +
                    '<div class="option"><input type="radio" name="case" value="upper" id="case-upper"><label for="case-upper">Upper Case</label></div>' +
                    '<div class="option"><input type="radio" name="case" value="toggle" id="case-toggle"><label for="case-toggle">Toggle Case</label></div>' +
                    '<div class="option"><input type="radio" name="case" value="capitalize" id="case-capitalize"><label for="case-capitalize">Capitalize Each Word</label></div>' +
                    '</div>' +
                    '<div class="picker-buttons">' +
                    '<button id="case-ok">OK</button>' +
                    '<button id="case-cancel">Cancel</button>' +
                    '</div>';
                document.body.appendChild(changeCaseModal);
                changeCaseModal.querySelector('#case-ok').addEventListener('click', function() {
                    const option = changeCaseModal.querySelector('input[name="case"]:checked').value;
                    changeCaseInRange(ta, option);
                    closeChangeCaseModal();
                });
                changeCaseModal.querySelector('#case-cancel').addEventListener('click', closeChangeCaseModal);
                overlay.addEventListener('click', closeChangeCaseModal);
                window.addEventListener('keydown', globalEscHandler);
                log('Change case modal opened.');
            } catch (err) {
                log('Change case modal open error', err);
                showDebugIndicator('Change case modal failed; check console.', 'error');
            }
        }
        function closeChangeCaseModal() {
            window.removeEventListener('keydown', globalEscHandler);
            if (changeCaseModal) {
                const overlay = document.getElementById('change-case-modal-overlay');
                if (overlay) overlay.remove();
                changeCaseModal.remove();
                changeCaseModal = null;
            }
        }
        // Change case in selection range via DOM text nodes
        function changeCaseInRange(ta, option) {
            try {
                var start = ta.selectionStart;
                var end = ta.selectionEnd;
                var isFull = false;
                if (start === end) {
                    start = 0;
                    end = ta.value.length;
                    isFull = true;
                    showDebugIndicator('No selection; changing case for entire note.', 'info');
                }
                var markerStart = '<!--NFH_START-->';
                var markerEnd = '<!--NFH_END-->';
                var modifiedText = ta.value.substring(0, start) + markerStart + ta.value.substring(start, end) + markerEnd + ta.value.substring(end);
                var temp = document.createElement('div');
                temp.innerHTML = modifiedText;
                // Find markers
                var walker = document.createTreeWalker(temp, NodeFilter.SHOW_COMMENT, null, false);
                var startNode = null;
                var endNode = null;
                while (walker.nextNode()) {
                    if (walker.currentNode.data === 'NFH_START') startNode = walker.currentNode;
                    if (walker.currentNode.data === 'NFH_END') endNode = walker.currentNode;
                }
                if (!startNode || !endNode) {
                    showDebugIndicator('Failed to locate range for case change.', 'error');
                    return;
                }
                // Create range and get text nodes
                var range = document.createRange();
                range.setStartAfter(startNode);
                range.setEndBefore(endNode);
                var textNodes = [];
                var nodeIterator = document.createNodeIterator(range.commonAncestorContainer, NodeFilter.SHOW_TEXT, null, false);
                var node;
                while (node = nodeIterator.nextNode()) {
                    textNodes.push(node);
                }
                // Apply case change
                textNodes.forEach(function(node) {
                    var text = node.textContent;
                    switch (option) {
                        case 'lower':
                            node.textContent = text.toLowerCase();
                            break;
                        case 'upper':
                            node.textContent = text.toUpperCase();
                            break;
                        case 'toggle':
                            node.textContent = text.split('').map(function(c, i) { return i % 2 === 0 ? c.toUpperCase() : c.toLowerCase(); }).join('');
                            break;
                        case 'capitalize':
                            node.textContent = text.toLowerCase().replace(/\b\w/g, function(c) { return c.toUpperCase(); });
                            break;
                        case 'sentence':
                            var lowered = text.toLowerCase();
                            var sentences = lowered.split(/(?=[.!?]\s+)/);
                            var capitalized = sentences.map(function(s, i) {
                                if (i === 0 || /[.?!]\s*$/.test(sentences[i - 1])) {
                                    return s.replace(/^\s*(\w)/, function(match, c) { return match.toUpperCase(); });
                                }
                                return s;
                            }).join('');
                            node.textContent = capitalized;
                            break;
                    }
                });
                // Remove markers - FIXED: Collect first to avoid TreeWalker mutation issues
                var markersToRemove = [];
                walker = document.createTreeWalker(temp, NodeFilter.SHOW_COMMENT, null, false);
                while (walker.nextNode()) {
                    if (walker.currentNode.data.includes('NFH')) {
                        markersToRemove.push(walker.currentNode);
                    }
                }
                markersToRemove.forEach(function(node) {
                    if (node.parentNode) {
                        node.parentNode.removeChild(node);
                    }
                });
                // Update textarea
                ta.value = temp.innerHTML;
                ta.selectionStart = start;
                ta.selectionEnd = end;
                ta.dispatchEvent(new Event('input', { bubbles: true }));
                saveState(ta);
                if (CONFIG.previewEnabled) debounceUpdatePreview();
                updateCharCount();
                showDebugIndicator('Case changed to ' + option + (isFull ? ' (full note)' : ' (selection)'), 'success');
                log('Case changed:', { option: option, isFull: isFull, nodesChanged: textNodes.length });
            } catch (err) {
                log('Change case error', err);
                showDebugIndicator('Case change failed; check console.', 'error');
            }
        }
        // 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);
                window.addEventListener('keydown', globalEscHandler);
                log('Resize settings modal opened.');
            } catch (err) {
                log('Resize settings open error', err);
                showDebugIndicator('Resize settings failed; check console.', 'error');
            }
        }
        function closeResizeSettings() {
            window.removeEventListener('keydown', globalEscHandler);
            if (sizeUpdateInterval) {
                clearInterval(sizeUpdateInterval);
                sizeUpdateInterval = null;
            }
            if (resizeSettingsModal) {
                const overlay = document.getElementById('resize-settings-overlay');
                if (overlay) overlay.remove();
                resizeSettingsModal.remove();
                resizeSettingsModal = null;
            }
        }
        // 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', 'change-case-btn', '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, use regex to extract nested opening prefix and closing suffix
                var fullHtml = temp.innerHTML = text.trim();
                if (fullHtml) {
                    var openMatch = fullHtml.match(/^((?:<[^>\/]+>)+)/);
                    var closeMatch = fullHtml.match(/((?:<\/[^>]+>)+)$/);
                    var openStr = openMatch ? openMatch[1] : '';
                    var closeStr = closeMatch ? closeMatch[1] : '';
                    // Validate: balanced counts and has content
                    var openCount = (openStr.match(/<[^>\/]+>/g) || []).length;
                    var closeCount = (closeStr.match(/<\/[^>]+>/g) || []).length;
                    if (openStr && closeStr && openCount === closeCount && openStr.length + closeStr.length < fullHtml.length) {
                        return {open: openStr, close: closeStr};
                    }
                }
                // Fallback for invalid full note
                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 - FIXED: reverse for open (outer first), append for close (inner first)
                var openStr = '';
                var closeStr = '';
                var reversedParents = parents.slice().reverse();
                reversedParents.forEach(function(el) {
                    openStr += el.outerHTML.substring(0, el.outerHTML.indexOf('>') + 1);
                });
                parents.forEach(function(el) {
                    closeStr += '</' + el.tagName.toLowerCase() + '>';
                });
                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);
                window.addEventListener('keydown', globalEscHandler);
                // 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() {
                    window.removeEventListener('keydown', globalEscHandler);
                    if (colorPicker) colorPicker.remove();
                    const overlay = document.getElementById('color-picker-overlay');
                    if (overlay) overlay.remove();
                    colorPicker = null;
                }
            } catch (err) {
                log('Color picker open error', err);
            }
        }
        // Close all open pickers
        function closeAllPickers() {
            closeColorPicker();
            closeShadowPicker();
            closeSymbolsPicker();
            closePinkOutlinePicker();
            closeDoubleOutlinePicker();
            closeSnippetsManager();
            closeChangeCaseModal();
            window.removeEventListener('keydown', globalEscHandler);
        }
        function closeColorPicker() {
            window.removeEventListener('keydown', globalEscHandler);
            if (colorPicker) {
                const overlay = document.getElementById('color-picker-overlay');
                if (overlay) overlay.remove();
                colorPicker.remove();
                colorPicker = null;
            }
        }
        function closeShadowPicker() {
            window.removeEventListener('keydown', globalEscHandler);
            if (shadowPicker) {
                const overlay = document.getElementById('shadow-picker-overlay');
                if (overlay) overlay.remove();
                shadowPicker.remove();
                shadowPicker = null;
            }
        }
        function closeSymbolsPicker() {
            window.removeEventListener('keydown', globalEscHandler);
            if (symbolPicker) {
                const overlay = document.getElementById('symbol-picker-overlay');
                if (overlay) overlay.remove();
                symbolPicker.remove();
                symbolPicker = null;
            }
        }
        function closePinkOutlinePicker() {
            window.removeEventListener('keydown', globalEscHandler);
            if (pinkOutlinePicker) {
                const overlay = document.getElementById('pink-outline-picker-overlay');
                if (overlay) overlay.remove();
                pinkOutlinePicker.remove();
                pinkOutlinePicker = null;
            }
        }
        function closeDoubleOutlinePicker() {
            window.removeEventListener('keydown', globalEscHandler);
            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);
                window.addEventListener('keydown', globalEscHandler);
                // 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);
                window.addEventListener('keydown', globalEscHandler);
                // 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);
                window.addEventListener('keydown', globalEscHandler);
                // 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);
                window.addEventListener('keydown', globalEscHandler);
            } 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, Change Case 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, Change Case 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);
    }
})();