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. Now with Word-like ribbon layout, advanced markup buttons, upgraded color picker with eyedropper and expanded palette, symbols palette, undo/redo, auto-resize, live preview, and character count.

// ==UserScript==
// @name         Danbooru Note Formatting Helper
// @namespace    http://tampermonkey.net/
// @version      1.48.0
// @description  A formatting helper toolbar for Danbooru note editing dialogs, adding buttons to wrap highlighted text with HTML tags for easy formatting. Now with Word-like ribbon layout, advanced markup buttons, upgraded color picker with eyedropper and expanded palette, symbols palette, undo/redo, auto-resize, live preview, and character count.
// @author       FunkyJustin
// @license      MIT
// @match        https://danbooru.donmai.us/*
// @grant        none
// ==/UserScript==
/*
Update History:
- 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-04; 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.48.0',
            toolbarId: 'note-formatting-toolbar',
            buttons: [
                // Font Group
                {
                    id: 'font-family-dropdown',
                    type: 'dropdown',
                    title: 'Font Family (select and apply to text)',
                    options: [ // Generics first
                        {value: 'serif', label: 'Serif'},
                        {value: 'sans-serif', label: 'Sans Serif'},
                        {value: 'monospace', label: 'Monospace'},
                        {value: 'cursive', label: 'Cursive'},
                        {value: 'fantasy', label: 'Fantasy'},
                        // Danbooru-supplied (aliases as values, common labels)
                        {value: 'comic', label: 'Comic Sans MS'},
                        {value: 'narrow', label: 'Arial Narrow'},
                        {value: 'mono', label: 'Plex Mono'},
                        {value: 'slab sans', label: 'Impact'},
                        {value: 'slab serif', label: 'Rockwell'},
                        {value: 'formal serif', label: 'Formal Serif (Lora)'},
                        {value: 'formal cursive', label: 'Formal Cursive'},
                        {value: 'print', label: 'Print (Kalam)'},
                        {value: 'hand', label: 'Hand (Indie Flower)'},
                        {value: 'childlike', label: 'Childlike (Giselle)'},
                        {value: 'blackletter', label: 'Blackletter'},
                        {value: 'scary', label: 'Scary (Anarchy)'}
                    ],
                    action: function(ta, value) { applyFontFamily(ta, value); }
                },
                {
                    id: 'font-size-btn',
                    label: 'Size',
                    title: 'Font Size (prompt px/em)',
                    action: function(ta) { promptFontSize(ta); }
                },
                {
                    id: 'pink-outline-btn',
                    label: 'Pink Outline',
                    title: 'Pink Outline Template (white text with pink outline, customizable)',
                    action: function(ta) { openPinkOutlinePicker(ta); }
                },
                {
                    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); }
                },
                {
                    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>'); }
                },
                // 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
                {
                    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: '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 Min Width/Height for Auto-Resize',
                    action: function(ta) { openSettings(); }
                }
            ],
            icons: {
                'font-size-btn': '📏',
                'pink-outline-btn': '💖',
                'double-outline-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': '📋',
                'undo-btn': '↶',
                'redo-btn': '↷',
                'toggle-resize-btn': '📐',
                'toggle-center-btn': '🎯',
                'toggle-preview-btn': '👁',
                'settings-btn': '⚙️'
            },
            shortcuts: {
                bold: 'Ctrl+B',
                italic: 'Ctrl+I',
                underline: 'Ctrl+U',
                undo: 'Ctrl+Z',
                redo: 'Ctrl+Y'
            },
            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: 362
            },
            autoCenter: {
                enabled: true
            },
            previewEnabled: false,
            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 { " +
                    "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; " +
                    "} " +
                    "#color-picker h3, #shadow-picker h3, #symbol-picker h3, #pink-outline-picker h3, #double-outline-picker h3 { " +
                    "margin: 0 0 10px 0; " +
                    "text-align: center; " +
                    "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 { " +
                    "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
                    "} " +
                    "#nfh-preview.show { " +
                    "display: block; " +
                    "} " +
                    "#double-preview { " +
                    "background-color: #333; " +
                    "color: #D87C86; " +
                    "font-size: 20px; " +
                    "font-family: print; " +
                    "font-weight: bold; " +
                    "display: flex; " +
                    "align-items: center; " +
                    "justify-content: center; " +
                    "height: 50px; " +
                    "border: 1px solid #666; " +
                    "border-radius: 4px; " +
                    "margin-bottom: 10px; " +
                    "text-shadow: none; " + // JS sets shadows
                    "} " +
                    "#char-count { " +
                    "margin-left: auto; " +
                    "font-size: 10px; " +
                    "color: #666; " +
                    "padding: 2px 5px; " +
                    "background: #f9f9f9; " +
                    "border-radius: 3px; " +
                    "border: 1px solid #ddd; " +
                    "align-self: center; " +
                    "min-width: 60px; " +
                    "text-align: center; " +
                    "} " +
                    "@media (max-width: 600px) { " +
                    " #note-formatting-toolbar button, #note-formatting-toolbar select { " +
                    " font-size: 10px; " +
                    " padding: 3px 4px; " +
                    " } " +
                    " #note-formatting-toolbar .group { " +
                    " flex-wrap: wrap; " +
                    " gap: 1px; " +
                    " } " +
                    "}"
        };
        // Global variables for state management (per-tab isolation for multi-tab support)
        var observer = null;
        var activeDialogs = new Set(); // Track multiple dialogs if possible (rare, but robust)
        var toolbar = null;
        var textarea = null;
        var previewDiv = null;
        var charCountSpan = null;
        var isMinimized = false;
        var debugMode = true; // Enable for logging; set to false in prod if needed
        var manualTriggerHandler = null;
        var history = []; // Undo/redo history array of {value, start, end}
        var historyIndex = -1; // Current position in history
        var colorPicker = null; // Color picker dialog
        var shadowPicker = null; // Shadow picker dialog
        var symbolPicker = null; // Symbols picker dialog
        var pinkOutlinePicker = null; // Pink outline picker dialog
        var doubleOutlinePicker = null; // Double outline picker dialog
        var debounceTimer = null; // For input debouncing
        // 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>');
        }
        // Load configs from localStorage
        function loadConfigs() {
            try {
                loadAutoResizeConfig();
                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 - removed templates
                var groups = {
                    font: ['font-family-dropdown', 'font-size-btn', 'pink-outline-btn', 'double-outline-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'],
                    history: ['clear-btn', 'copy-btn', 'undo-btn', 'redo-btn', 'toggle-resize-btn', 'toggle-center-btn', 'toggle-preview-btn', 'settings-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;
                                // 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
                                        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;
                                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)
                                        if (!['undo-btn', 'redo-btn', 'toggle-resize-btn', 'toggle-center-btn', 'toggle-preview-btn', 'settings-btn', 'copy-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'].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 + ' - Upgraded Color Picker + Eyedropper (Firefox supported with ColorZilla link)';
                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 groups, double outline, and preview toggle.');
                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');
            }
        }
        // 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');
                if (!tb || !ta) {
                    log('Auto-resize skipped: Missing toolbar/textarea.');
                    return;
                }
                // Re-measure after height set (no inner timeout needed for immediacy)
                const tbRect = tb.getBoundingClientRect();
                const taRect = ta.getBoundingClientRect();
                const pvHeight = pv && CONFIG.previewEnabled ? pv.scrollHeight + 20 : 0;
                // Calculate content dimensions
                const contentWidth = Math.max(tbRect.width, taRect.width) + 30; // Padding for borders/scroll
                const contentHeight = tbRect.height + taRect.height + pvHeight + 50; // Buffer for padding/margins
                const newWidth = Math.max(CONFIG.autoResize.defaultWidth, Math.max(500, contentWidth));
                const newHeight = Math.max(CONFIG.autoResize.defaultHeight, contentHeight + 150); // Header/footer/dialog chrome buffer
                // Always use direct style resize (reliable, no jQuery dependency/error)
                dialog.style.width = newWidth + 'px';
                dialog.style.height = newHeight + 'px';
                log('Auto-resized dialog via direct style: ' + newWidth + 'x' + newHeight + '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);
            }
        }
        // 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 {
                CONFIG.previewEnabled = !CONFIG.previewEnabled;
                if (previewDiv) {
                    previewDiv.classList.toggle('show', CONFIG.previewEnabled);
                    if (CONFIG.previewEnabled) {
                        updatePreview();
                    }
                }
                const status = CONFIG.previewEnabled ? 'enabled' : 'disabled';
                log('Live preview ' + status + '.');
                showDebugIndicator('Live preview ' + status + '.', 'info');
                // Re-resize if enabled
                const dialog = document.querySelector('.note-edit-dialog');
                if (dialog) {
                    dialog.dataset.helperResized = 'false';
                    setTimeout(function() { autoResizeDialog(dialog); }, 100);
                }
                // Setup listener if enabling
                if (CONFIG.previewEnabled && textarea) {
                    textarea.addEventListener('input', debounceUpdatePreview);
                } else if (!CONFIG.previewEnabled && textarea) {
                    textarea.removeEventListener('input', debounceUpdatePreview);
                }
            } 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
        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, '');
                previewDiv.innerHTML = html;
                log('Preview updated.');
            } catch (err) {
                log('Preview update error', err);
                previewDiv.innerHTML = 'Preview error: Invalid HTML.';
            }
        }
        // Inject preview div below textarea
        function injectPreview(dialog) {
            try {
                if (previewDiv) return;
                previewDiv = document.createElement('div');
                previewDiv.id = 'nfh-preview';
                if (CONFIG.previewEnabled) previewDiv.classList.add('show');
                const content = dialog.querySelector('.ui-dialog-content');
                content.appendChild(previewDiv);
                if (CONFIG.previewEnabled && textarea) {
                    updatePreview();
                    textarea.addEventListener('input', debounceUpdatePreview);
                }
                log('Preview pane injected.');
            } catch (err) {
                log('Inject preview error', err);
            }
        }
        // Open settings for width/height mins
        function openSettings() {
            try {
                var newWidthStr = prompt('Enter default min width (px):', CONFIG.autoResize.defaultWidth.toString());
                var newWidth = parseInt(newWidthStr);
                if (isNaN(newWidth) || newWidth < 300) {
                    newWidth = CONFIG.autoResize.defaultWidth;
                    log('Invalid width; using default.');
                }
                CONFIG.autoResize.defaultWidth = newWidth;
                localStorage.setItem('nfh_defaultWidth', newWidth.toString());
                var newHeightStr = prompt('Enter default min height (px):', CONFIG.autoResize.defaultHeight.toString());
                var newHeight = parseInt(newHeightStr);
                if (isNaN(newHeight) || newHeight < 200) {
                    newHeight = CONFIG.autoResize.defaultHeight;
                    log('Invalid height; using default.');
                }
                CONFIG.autoResize.defaultHeight = newHeight;
                localStorage.setItem('nfh_defaultHeight', newHeight.toString());
                log('Settings updated.', CONFIG.autoResize);
                showDebugIndicator('Dimensions updated: ' + newWidth + 'x' + newHeight, 'info');
                // Re-apply to current dialog if enabled
                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('Settings error', err);
                alert('Settings update failed; check console.');
            }
        }
        // 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();
                // 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);
                const escHandler = function(e) {
                    if (e.key === 'Escape') closeColorPicker();
                };
                window.addEventListener('keydown', escHandler);
                // Eyedropper
                const eyedropperBtn = colorPicker.querySelector('#eyedropper');
                addColorZillaLink(eyedropperBtn);
                if (eyedropperBtn && 'eyedropper' in navigator) {
                    eyedropperBtn.addEventListener('click', async function() {
                        try {
                            const result = await navigator.eyedropper.pick({withAlpha: true});
                            if (result && result.sRGBHex) {
                                const hexFull = result.sRGBHex;
                                const alphaHex = hexFull.slice(-2);
                                const hex = '#' + hexFull.slice(1,7).toUpperCase();
                                const r = parseInt(hexFull.slice(1,3), 16);
                                const g = parseInt(hexFull.slice(3,5), 16);
                                const b = parseInt(hexFull.slice(5,7), 16);
                                const alpha = parseInt(alphaHex, 16) / 255;
                                colorPicker.querySelector('#r-slider').value = r;
                                colorPicker.querySelector('#g-slider').value = g;
                                colorPicker.querySelector('#b-slider').value = b;
                                colorPicker.querySelector('#r-input').value = r;
                                colorPicker.querySelector('#g-input').value = g;
                                colorPicker.querySelector('#b-input').value = b;
                                colorPicker.querySelector('#hex-input').value = hex;
                                colorPicker.querySelector('#alpha-slider').value = alpha;
                                colorPicker.querySelector('#alpha-value').textContent = alpha.toFixed(2);
                                updateColor(colorPicker);
                            }
                        } catch (err) {
                            log('Eyedropper error:', err);
                            showDebugIndicator('Eyedropper cancelled or failed.', 'info');
                        }
                    });
                } else if (eyedropperBtn) {
                    eyedropperBtn.disabled = true;
                    eyedropperBtn.title = 'EyeDropper API not supported in this browser';
                }
                function updateColor(picker) {
                    const r = picker.querySelector('#r-slider').value;
                    const g = picker.querySelector('#g-slider').value;
                    const b = picker.querySelector('#b-slider').value;
                    const alpha = picker.querySelector('#alpha-slider').value;
                    picker.querySelector('#alpha-value').textContent = alpha;
                    const color = 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')';
                    picker.querySelector('#color-preview').style.backgroundColor = color;
                    picker.querySelector('#r-input').value = r;
                    picker.querySelector('#g-input').value = g;
                    picker.querySelector('#b-input').value = b;
                    const hex = '#' + Math.round(r).toString(16).padStart(2, '0') + Math.round(g).toString(16).padStart(2, '0') + Math.round(b).toString(16).padStart(2, '0');
                    picker.querySelector('#hex-input').value = hex.toUpperCase();
                }
                function updateSlider(channel, picker) {
                    const input = picker.querySelector('#' + channel + '-input').value;
                    picker.querySelector('#' + channel + '-slider').value = input;
                    updateColor(picker);
                }
                colorPicker.addEventListener('input', function(e) {
                    if (e.target.matches('input[type="range"], input[type="number"]')) {
                        if (e.target.id.includes('input') && e.target.type === 'number') {
                            const channel = e.target.id.split('-')[0];
                            updateSlider(channel, colorPicker);
                        } else {
                            updateColor(colorPicker);
                        }
                    } else if (e.target.id === 'hex-input') {
                        const hexValue = e.target.value.replace('#', '').toUpperCase();
                        if (hexValue.match(/^([0-9A-F]{6})$/)) {
                            const rgb = hexToRgb(hexValue);
                            colorPicker.querySelector('#r-slider').value = rgb.r;
                            colorPicker.querySelector('#g-slider').value = rgb.g;
                            colorPicker.querySelector('#b-slider').value = rgb.b;
                            colorPicker.querySelector('#r-input').value = rgb.r;
                            colorPicker.querySelector('#g-input').value = rgb.g;
                            colorPicker.querySelector('#b-input').value = rgb.b;
                            updateColor(colorPicker);
                        }
                    }
                });
                updateColor(colorPicker);
                function closeColorPicker() {
                    if (colorPicker) colorPicker.remove();
                    if (overlay) overlay.remove();
                    colorPicker = null;
                    window.removeEventListener('keydown', escHandler);
                }
            } catch (err) {
                log('Color picker open error', err);
            }
        }
        // Close all open pickers
        function closeAllPickers() {
            if (colorPicker) closeColorPicker();
            if (shadowPicker) closeShadowPicker();
            if (symbolPicker) closeSymbolsPicker();
            if (pinkOutlinePicker) closePinkOutlinePicker();
            if (doubleOutlinePicker) closeDoubleOutlinePicker();
        }
        function closeColorPicker() {
            if (colorPicker) {
                const overlay = document.getElementById('color-picker-overlay');
                if (overlay) overlay.remove();
                colorPicker.remove();
                colorPicker = null;
            }
        }
        function closeShadowPicker() {
            if (shadowPicker) {
                const overlay = document.getElementById('shadow-picker-overlay');
                if (overlay) overlay.remove();
                shadowPicker.remove();
                shadowPicker = null;
            }
        }
        function closeSymbolsPicker() {
            if (symbolPicker) {
                const overlay = document.getElementById('symbol-picker-overlay');
                if (overlay) overlay.remove();
                symbolPicker.remove();
                symbolPicker = null;
            }
        }
        function closePinkOutlinePicker() {
            if (pinkOutlinePicker) {
                const overlay = document.getElementById('pink-outline-picker-overlay');
                if (overlay) overlay.remove();
                pinkOutlinePicker.remove();
                pinkOutlinePicker = null;
            }
        }
        function closeDoubleOutlinePicker() {
            if (doubleOutlinePicker) {
                const overlay = document.getElementById('double-outline-picker-overlay');
                if (overlay) overlay.remove();
                doubleOutlinePicker.remove();
                doubleOutlinePicker = null;
            }
        }
        // Open shadow picker for text shadow (outlines/drop shadows)
        function openShadowPicker(ta) {
            try {
                closeAllPickers();
                // 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);
                const escHandler = function(e) {
                    if (e.key === 'Escape') closeShadowPicker();
                };
                window.addEventListener('keydown', escHandler);
                // Eyedropper for shadow
                const eyedropperShadowBtn = shadowPicker.querySelector('#eyedropper-shadow');
                addColorZillaLink(eyedropperShadowBtn);
                if (eyedropperShadowBtn && 'eyedropper' in navigator) {
                    eyedropperShadowBtn.addEventListener('click', async function() {
                        try {
                            const result = await navigator.eyedropper.pick({withAlpha: true});
                            if (result && result.sRGBHex) {
                                const hexFull = result.sRGBHex;
                                const alphaHex = hexFull.slice(-2);
                                const hex = '#' + hexFull.slice(1,7).toUpperCase();
                                const r = parseInt(hexFull.slice(1,3), 16);
                                const g = parseInt(hexFull.slice(3,5), 16);
                                const b = parseInt(hexFull.slice(5,7), 16);
                                const alpha = parseInt(alphaHex, 16) / 255;
                                shadowPicker.querySelector('#r-slider').value = r;
                                shadowPicker.querySelector('#g-slider').value = g;
                                shadowPicker.querySelector('#b-slider').value = b;
                                shadowPicker.querySelector('#r-input').value = r;
                                shadowPicker.querySelector('#g-input').value = g;
                                shadowPicker.querySelector('#b-input').value = b;
                                shadowPicker.querySelector('#hex-input').value = hex;
                                shadowPicker.querySelector('#alpha-slider').value = alpha;
                                shadowPicker.querySelector('#alpha-value').textContent = alpha.toFixed(2);
                                updateShadowPreview(shadowPicker);
                            }
                        } catch (err) {
                            log('Eyedropper error:', err);
                            showDebugIndicator('Eyedropper cancelled or failed.', 'info');
                        }
                    });
                } else if (eyedropperShadowBtn) {
                    eyedropperShadowBtn.disabled = true;
                    eyedropperShadowBtn.title = 'EyeDropper API not supported in this browser';
                }
                function updateShadowPreview(picker) {
                    const x = parseFloat(picker.querySelector('#x-offset').value) || 1;
                    const y = parseFloat(picker.querySelector('#y-offset').value) || 1;
                    const blur = parseFloat(picker.querySelector('#blur').value) || 0;
                    const r = picker.querySelector('#r-slider').value;
                    const g = picker.querySelector('#g-slider').value;
                    const b = picker.querySelector('#b-slider').value;
                    const alpha = picker.querySelector('#alpha-slider').value;
                    const colorValue = (alpha < 1) ? 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')' : 'rgb(' + r + ', ' + g + ', ' + b + ')';
                    var shadowRule;
                    const absX = Math.abs(x);
                    const absY = Math.abs(y);
                    if (picker.querySelector('#outline-mode').checked) {
                        shadowRule = '-' + absX + 'px -' + absY + 'px ' + blur + 'px ' + colorValue + ', ' + absX + 'px -' + absY + 'px ' + blur + 'px ' + colorValue + ', -' + absX + 'px ' + absY + 'px ' + blur + 'px ' + colorValue + ', ' + absX + 'px ' + absY + 'px ' + blur + 'px ' + colorValue;
                    } else {
                        shadowRule = x + 'px ' + y + 'px ' + blur + 'px ' + colorValue;
                    }
                    picker.querySelector('#shadow-preview').style.textShadow = shadowRule;
                    picker.querySelector('#r-input').value = r;
                    picker.querySelector('#g-input').value = g;
                    picker.querySelector('#b-input').value = b;
                    const hex = '#' + Math.round(r).toString(16).padStart(2, '0') + Math.round(g).toString(16).padStart(2, '0') + Math.round(b).toString(16).padStart(2, '0');
                    picker.querySelector('#hex-input').value = hex.toUpperCase();
                    picker.querySelector('#alpha-value').textContent = alpha;
                }
                function updateSlider(channel, picker) {
                    const input = picker.querySelector('#' + channel + '-input').value;
                    picker.querySelector('#' + channel + '-slider').value = input;
                    updateShadowPreview(picker);
                }
                shadowPicker.addEventListener('input', function(e) {
                    if (e.target.matches('#x-offset, #y-offset, #blur')) {
                        updateShadowPreview(shadowPicker);
                    } else if (e.target.matches('input[type="range"], input[type="number"]')) {
                        if (e.target.id.includes('input') && e.target.type === 'number') {
                            const channel = e.target.id.split('-')[0];
                            updateSlider(channel, shadowPicker);
                        } else {
                            updateShadowPreview(shadowPicker);
                        }
                    } else if (e.target.id === 'hex-input') {
                        const hexValue = e.target.value.replace('#', '').toUpperCase();
                        if (hexValue.match(/^([0-9A-F]{6})$/)) {
                            const rgb = hexToRgb(hexValue);
                            shadowPicker.querySelector('#r-slider').value = rgb.r;
                            shadowPicker.querySelector('#g-slider').value = rgb.g;
                            shadowPicker.querySelector('#b-slider').value = rgb.b;
                            shadowPicker.querySelector('#r-input').value = rgb.r;
                            shadowPicker.querySelector('#g-input').value = rgb.g;
                            shadowPicker.querySelector('#b-input').value = rgb.b;
                            updateShadowPreview(shadowPicker);
                        }
                    }
                });
                updateShadowPreview(shadowPicker);
            } catch (err) {
                log('Shadow picker open error', err);
                showDebugIndicator('Shadow picker failed; check console.', 'error');
            }
        }
        // Open pink outline picker - added eyedropper for text and outline
        function openPinkOutlinePicker(ta) {
            try {
                closeAllPickers();
                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);
                const escHandler = function(e) {
                    if (e.key === 'Escape') closePinkOutlinePicker();
                };
                window.addEventListener('keydown', escHandler);
                // Eyedropper for text
                const eyedropperTextBtn = pinkOutlinePicker.querySelector('#eyedropper-text');
                addColorZillaLink(eyedropperTextBtn);
                if (eyedropperTextBtn && 'eyedropper' in navigator) {
                    eyedropperTextBtn.addEventListener('click', async function() {
                        try {
                            const result = await navigator.eyedropper.pick({withAlpha: true});
                            if (result && result.sRGBHex) {
                                const hexFull = result.sRGBHex;
                                const alphaHex = hexFull.slice(-2);
                                const hex = '#' + hexFull.slice(1,7).toUpperCase();
                                const r = parseInt(hexFull.slice(1,3), 16);
                                const g = parseInt(hexFull.slice(3,5), 16);
                                const b = parseInt(hexFull.slice(5,7), 16);
                                const alpha = parseInt(alphaHex, 16) / 255;
                                pinkOutlinePicker.querySelector('#text-r-slider').value = r;
                                pinkOutlinePicker.querySelector('#text-g-slider').value = g;
                                pinkOutlinePicker.querySelector('#text-b-slider').value = b;
                                pinkOutlinePicker.querySelector('#text-r-input').value = r;
                                pinkOutlinePicker.querySelector('#text-g-input').value = g;
                                pinkOutlinePicker.querySelector('#text-b-input').value = b;
                                pinkOutlinePicker.querySelector('#text-hex-input').value = hex;
                                pinkOutlinePicker.querySelector('#text-alpha-slider').value = alpha;
                                pinkOutlinePicker.querySelector('#text-alpha-value').textContent = alpha.toFixed(2);
                                updateOutlinePreview(pinkOutlinePicker);
                            }
                        } catch (err) {
                            log('Eyedropper error:', err);
                            showDebugIndicator('Eyedropper cancelled or failed.', 'info');
                        }
                    });
                } else if (eyedropperTextBtn) {
                    eyedropperTextBtn.disabled = true;
                    eyedropperTextBtn.title = 'EyeDropper API not supported in this browser';
                }
                // Eyedropper for outline
                const eyedropperOutlineBtn = pinkOutlinePicker.querySelector('#eyedropper-outline');
                addColorZillaLink(eyedropperOutlineBtn);
                if (eyedropperOutlineBtn && 'eyedropper' in navigator) {
                    eyedropperOutlineBtn.addEventListener('click', async function() {
                        try {
                            const result = await navigator.eyedropper.pick({withAlpha: true});
                            if (result && result.sRGBHex) {
                                const hexFull = result.sRGBHex;
                                const alphaHex = hexFull.slice(-2);
                                const hex = '#' + hexFull.slice(1,7).toUpperCase();
                                const r = parseInt(hexFull.slice(1,3), 16);
                                const g = parseInt(hexFull.slice(3,5), 16);
                                const b = parseInt(hexFull.slice(5,7), 16);
                                const alpha = parseInt(alphaHex, 16) / 255;
                                pinkOutlinePicker.querySelector('#outline-r-slider').value = r;
                                pinkOutlinePicker.querySelector('#outline-g-slider').value = g;
                                pinkOutlinePicker.querySelector('#outline-b-slider').value = b;
                                pinkOutlinePicker.querySelector('#outline-r-input').value = r;
                                pinkOutlinePicker.querySelector('#outline-g-input').value = g;
                                pinkOutlinePicker.querySelector('#outline-b-input').value = b;
                                pinkOutlinePicker.querySelector('#outline-hex-input').value = hex;
                                pinkOutlinePicker.querySelector('#outline-alpha-slider').value = alpha;
                                pinkOutlinePicker.querySelector('#outline-alpha-value').textContent = alpha.toFixed(2);
                                updateOutlinePreview(pinkOutlinePicker);
                            }
                        } catch (err) {
                            log('Eyedropper error:', err);
                            showDebugIndicator('Eyedropper cancelled or failed.', 'info');
                        }
                    });
                } else if (eyedropperOutlineBtn) {
                    eyedropperOutlineBtn.disabled = true;
                    eyedropperOutlineBtn.title = 'EyeDropper API not supported in this browser';
                }
                function updateOutlinePreview(picker) {
                    const fontFamily = picker.querySelector('#font-family-select').value;
                    const fontFamilyStr = fontFamily.includes(' ') ? fontFamily + ', serif' : fontFamily + ', sans-serif';
                    const fontSize = picker.querySelector('#font-size').value + 'px';
                    const bold = picker.querySelector('#font-bold').checked ? 'bold' : 'normal';
                    const offset = parseFloat(picker.querySelector('#offset').value) || 2;
                    const blur = parseFloat(picker.querySelector('#blur').value) || 5;
                    const textR = picker.querySelector('#text-r-slider').value;
                    const textG = picker.querySelector('#text-g-slider').value;
                    const textB = picker.querySelector('#text-b-slider').value;
                    const textAlpha = picker.querySelector('#text-alpha-slider').value;
                    const outlineR = picker.querySelector('#outline-r-slider').value;
                    const outlineG = picker.querySelector('#outline-g-slider').value;
                    const outlineB = picker.querySelector('#outline-b-slider').value;
                    const outlineAlpha = picker.querySelector('#outline-alpha-slider').value;
                    const textColor = (textAlpha < 1) ? 'rgba(' + textR + ', ' + textG + ', ' + textB + ', ' + textAlpha + ')' : 'rgb(' + textR + ', ' + textG + ', ' + textB + ')';
                    const outlineColor = (outlineAlpha < 1) ? 'rgba(' + outlineR + ', ' + outlineG + ', ' + outlineB + ', ' + outlineAlpha + ')' : 'rgb(' + outlineR + ', ' + outlineG + ', ' + outlineB + ')';
                    const absOffset = Math.abs(offset);
                    var shadowRule = '-' + absOffset + 'px -' + absOffset + 'px ' + blur + 'px ' + outlineColor + ', ' +
                                     '-' + absOffset + 'px 0px ' + blur + 'px ' + outlineColor + ', ' +
                                     '-' + absOffset + 'px ' + absOffset + 'px ' + blur + 'px ' + outlineColor + ', ' +
                                     '0px -' + absOffset + 'px ' + blur + 'px ' + outlineColor + ', ' +
                                     '0px ' + absOffset + 'px ' + blur + 'px ' + outlineColor + ', ' +
                                     absOffset + 'px -' + absOffset + 'px ' + blur + 'px ' + outlineColor + ', ' +
                                     absOffset + 'px 0px ' + blur + 'px ' + outlineColor + ', ' +
                                     absOffset + 'px ' + absOffset + 'px ' + blur + 'px ' + outlineColor;
                    const preview = picker.querySelector('#outline-preview');
                    preview.style.fontFamily = fontFamilyStr;
                    preview.style.fontSize = fontSize;
                    preview.style.fontWeight = bold;
                    preview.style.color = textColor;
                    preview.style.textShadow = shadowRule;
                    picker.querySelector('#text-alpha-value').textContent = textAlpha;
                    picker.querySelector('#outline-alpha-value').textContent = outlineAlpha;
                    const textHex = '#' + Math.round(textR).toString(16).padStart(2, '0') + Math.round(textG).toString(16).padStart(2, '0') + Math.round(textB).toString(16).padStart(2, '0');
                    picker.querySelector('#text-hex-input').value = textHex.toUpperCase();
                    const outlineHex = '#' + Math.round(outlineR).toString(16).padStart(2, '0') + Math.round(outlineG).toString(16).padStart(2, '0') + Math.round(outlineB).toString(16).padStart(2, '0');
                    picker.querySelector('#outline-hex-input').value = outlineHex.toUpperCase();
                }
                function updateTextSlider(channel, picker) {
                    const input = picker.querySelector('#text-' + channel + '-input').value;
                    picker.querySelector('#text-' + channel + '-slider').value = input;
                    updateOutlinePreview(picker);
                }
                function updateOutlineSlider(channel, picker) {
                    const input = picker.querySelector('#outline-' + channel + '-input').value;
                    picker.querySelector('#outline-' + channel + '-slider').value = input;
                    updateOutlinePreview(picker);
                }
                pinkOutlinePicker.addEventListener('input', function(e) {
                    if (e.target.id === 'font-family-select' || e.target.id === 'font-size' || e.target.id === 'font-bold' || e.target.id === 'offset' || e.target.id === 'blur' || e.target.id === 'text-alpha-slider' || e.target.id === 'outline-alpha-slider') {
                        updateOutlinePreview(pinkOutlinePicker);
                    } else if (e.target.matches('#text-r-slider, #text-g-slider, #text-b-slider')) {
                        updateOutlinePreview(pinkOutlinePicker);
                    } else if (e.target.matches('#outline-r-slider, #outline-g-slider, #outline-b-slider')) {
                        updateOutlinePreview(pinkOutlinePicker);
                    } else if (e.target.id.includes('text-') && e.target.id.includes('input') && e.target.type === 'number') {
                        const channel = e.target.id.replace('text-', '').replace('-input', '');
                        updateTextSlider(channel, pinkOutlinePicker);
                    } else if (e.target.id.includes('outline-') && e.target.id.includes('input') && e.target.type === 'number') {
                        const channel = e.target.id.replace('outline-', '').replace('-input', '');
                        updateOutlineSlider(channel, pinkOutlinePicker);
                    } else if (e.target.id === 'text-hex-input') {
                        const hexValue = e.target.value.replace('#', '').toUpperCase();
                        if (hexValue.match(/^([0-9A-F]{6})$/)) {
                            const rgb = hexToRgb(hexValue);
                            pinkOutlinePicker.querySelector('#text-r-slider').value = rgb.r;
                            pinkOutlinePicker.querySelector('#text-g-slider').value = rgb.g;
                            pinkOutlinePicker.querySelector('#text-b-slider').value = rgb.b;
                            pinkOutlinePicker.querySelector('#text-r-input').value = rgb.r;
                            pinkOutlinePicker.querySelector('#text-g-input').value = rgb.g;
                            pinkOutlinePicker.querySelector('#text-b-input').value = rgb.b;
                            updateOutlinePreview(pinkOutlinePicker);
                        }
                    } else if (e.target.id === 'outline-hex-input') {
                        const hexValue = e.target.value.replace('#', '').toUpperCase();
                        if (hexValue.match(/^([0-9A-F]{6})$/)) {
                            const rgb = hexToRgb(hexValue);
                            pinkOutlinePicker.querySelector('#outline-r-slider').value = rgb.r;
                            pinkOutlinePicker.querySelector('#outline-g-slider').value = rgb.g;
                            pinkOutlinePicker.querySelector('#outline-b-slider').value = rgb.b;
                            pinkOutlinePicker.querySelector('#outline-r-input').value = rgb.r;
                            pinkOutlinePicker.querySelector('#outline-g-input').value = rgb.g;
                            pinkOutlinePicker.querySelector('#outline-b-input').value = rgb.b;
                            updateOutlinePreview(pinkOutlinePicker);
                        }
                    }
                });
                updateOutlinePreview(pinkOutlinePicker);
            } catch (err) {
                log('Pink outline picker open error', err);
                showDebugIndicator('Pink outline picker failed; check console.', 'error');
            }
        }
        // Open double outline picker - added eyedropper for text, inner, outer
        function openDoubleOutlinePicker(ta) {
            try {
                closeAllPickers();
                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);
                const escHandler = function(e) {
                    if (e.key === 'Escape') closeDoubleOutlinePicker();
                };
                window.addEventListener('keydown', escHandler);
                // Eyedropper for text
                const eyedropperTextBtn = doubleOutlinePicker.querySelector('#eyedropper-text');
                addColorZillaLink(eyedropperTextBtn);
                if (eyedropperTextBtn && 'eyedropper' in navigator) {
                    eyedropperTextBtn.addEventListener('click', async function() {
                        try {
                            const result = await navigator.eyedropper.pick({withAlpha: true});
                            if (result && result.sRGBHex) {
                                const hexFull = result.sRGBHex;
                                const alphaHex = hexFull.slice(-2);
                                const hex = '#' + hexFull.slice(1,7).toUpperCase();
                                const r = parseInt(hexFull.slice(1,3), 16);
                                const g = parseInt(hexFull.slice(3,5), 16);
                                const b = parseInt(hexFull.slice(5,7), 16);
                                const alpha = parseInt(alphaHex, 16) / 255;
                                doubleOutlinePicker.querySelector('#text-r-slider').value = r;
                                doubleOutlinePicker.querySelector('#text-g-slider').value = g;
                                doubleOutlinePicker.querySelector('#text-b-slider').value = b;
                                doubleOutlinePicker.querySelector('#text-r-input').value = r;
                                doubleOutlinePicker.querySelector('#text-g-input').value = g;
                                doubleOutlinePicker.querySelector('#text-b-input').value = b;
                                doubleOutlinePicker.querySelector('#text-hex-input').value = hex;
                                doubleOutlinePicker.querySelector('#text-alpha-slider').value = alpha;
                                doubleOutlinePicker.querySelector('#text-alpha-value').textContent = alpha.toFixed(2);
                                updateDoublePreview(doubleOutlinePicker);
                            }
                        } catch (err) {
                            log('Eyedropper error:', err);
                            showDebugIndicator('Eyedropper cancelled or failed.', 'info');
                        }
                    });
                } else if (eyedropperTextBtn) {
                    eyedropperTextBtn.disabled = true;
                    eyedropperTextBtn.title = 'EyeDropper API not supported in this browser';
                }
                // Eyedropper for inner
                const eyedropperInnerBtn = doubleOutlinePicker.querySelector('#eyedropper-inner');
                addColorZillaLink(eyedropperInnerBtn);
                if (eyedropperInnerBtn && 'eyedropper' in navigator) {
                    eyedropperInnerBtn.addEventListener('click', async function() {
                        try {
                            const result = await navigator.eyedropper.pick({withAlpha: true});
                            if (result && result.sRGBHex) {
                                const hexFull = result.sRGBHex;
                                const alphaHex = hexFull.slice(-2);
                                const hex = '#' + hexFull.slice(1,7).toUpperCase();
                                const r = parseInt(hexFull.slice(1,3), 16);
                                const g = parseInt(hexFull.slice(3,5), 16);
                                const b = parseInt(hexFull.slice(5,7), 16);
                                const alpha = parseInt(alphaHex, 16) / 255;
                                doubleOutlinePicker.querySelector('#inner-r-slider').value = r;
                                doubleOutlinePicker.querySelector('#inner-g-slider').value = g;
                                doubleOutlinePicker.querySelector('#inner-b-slider').value = b;
                                doubleOutlinePicker.querySelector('#inner-r-input').value = r;
                                doubleOutlinePicker.querySelector('#inner-g-input').value = g;
                                doubleOutlinePicker.querySelector('#inner-b-input').value = b;
                                doubleOutlinePicker.querySelector('#inner-hex-input').value = hex;
                                doubleOutlinePicker.querySelector('#inner-alpha-slider').value = alpha;
                                doubleOutlinePicker.querySelector('#inner-alpha-value').textContent = alpha.toFixed(2);
                                updateDoublePreview(doubleOutlinePicker);
                            }
                        } catch (err) {
                            log('Eyedropper error:', err);
                            showDebugIndicator('Eyedropper cancelled or failed.', 'info');
                        }
                    });
                } else if (eyedropperInnerBtn) {
                    eyedropperInnerBtn.disabled = true;
                    eyedropperInnerBtn.title = 'EyeDropper API not supported in this browser';
                }
                // Eyedropper for outer
                const eyedropperOuterBtn = doubleOutlinePicker.querySelector('#eyedropper-outer');
                addColorZillaLink(eyedropperOuterBtn);
                if (eyedropperOuterBtn && 'eyedropper' in navigator) {
                    eyedropperOuterBtn.addEventListener('click', async function() {
                        try {
                            const result = await navigator.eyedropper.pick({withAlpha: true});
                            if (result && result.sRGBHex) {
                                const hexFull = result.sRGBHex;
                                const alphaHex = hexFull.slice(-2);
                                const hex = '#' + hexFull.slice(1,7).toUpperCase();
                                const r = parseInt(hexFull.slice(1,3), 16);
                                const g = parseInt(hexFull.slice(3,5), 16);
                                const b = parseInt(hexFull.slice(5,7), 16);
                                const alpha = parseInt(alphaHex, 16) / 255;
                                doubleOutlinePicker.querySelector('#outer-r-slider').value = r;
                                doubleOutlinePicker.querySelector('#outer-g-slider').value = g;
                                doubleOutlinePicker.querySelector('#outer-b-slider').value = b;
                                doubleOutlinePicker.querySelector('#outer-r-input').value = r;
                                doubleOutlinePicker.querySelector('#outer-g-input').value = g;
                                doubleOutlinePicker.querySelector('#outer-b-input').value = b;
                                doubleOutlinePicker.querySelector('#outer-hex-input').value = hex;
                                doubleOutlinePicker.querySelector('#outer-alpha-slider').value = alpha;
                                doubleOutlinePicker.querySelector('#outer-alpha-value').textContent = alpha.toFixed(2);
                                updateDoublePreview(doubleOutlinePicker);
                            }
                        } catch (err) {
                            log('Eyedropper error:', err);
                            showDebugIndicator('Eyedropper cancelled or failed.', 'info');
                        }
                    });
                } else if (eyedropperOuterBtn) {
                    eyedropperOuterBtn.disabled = true;
                    eyedropperOuterBtn.title = 'EyeDropper API not supported in this browser';
                }
                function updateDoublePreview(picker) {
                    const fontFamily = picker.querySelector('#font-family-select').value;
                    const fontFamilyStr = fontFamily.includes(' ') ? fontFamily + ', serif' : fontFamily + ', sans-serif';
                    const fontSize = picker.querySelector('#font-size').value + 'px';
                    const bold = picker.querySelector('#font-bold').checked ? 'bold' : 'normal';
                    const textR = picker.querySelector('#text-r-slider').value;
                    const textG = picker.querySelector('#text-g-slider').value;
                    const textB = picker.querySelector('#text-b-slider').value;
                    const textAlpha = picker.querySelector('#text-alpha-slider').value;
                    const textColor = (textAlpha < 1) ? 'rgba(' + textR + ', ' + textG + ', ' + textB + ', ' + textAlpha + ')' : 'rgb(' + textR + ', ' + textG + ', ' + textB + ')';
                    const innerOffset = parseFloat(picker.querySelector('#inner-offset').value) || 2;
                    const innerBlur = parseFloat(picker.querySelector('#inner-blur').value) || 0;
                    const innerR = picker.querySelector('#inner-r-slider').value;
                    const innerG = picker.querySelector('#inner-g-slider').value;
                    const innerB = picker.querySelector('#inner-b-slider').value;
                    const innerAlpha = picker.querySelector('#inner-alpha-slider').value;
                    const innerColor = (innerAlpha < 1) ? 'rgba(' + innerR + ', ' + innerG + ', ' + innerB + ', ' + innerAlpha + ')' : 'rgb(' + innerR + ', ' + innerG + ', ' + innerB + ')';
                    const outerOffset = parseFloat(picker.querySelector('#outer-offset').value) || 3;
                    const outerBlur = parseFloat(picker.querySelector('#outer-blur').value) || 0;
                    const outerR = picker.querySelector('#outer-r-slider').value;
                    const outerG = picker.querySelector('#outer-g-slider').value;
                    const outerB = picker.querySelector('#outer-b-slider').value;
                    const outerAlpha = picker.querySelector('#outer-alpha-slider').value;
                    const outerColor = (outerAlpha < 1) ? 'rgba(' + outerR + ', ' + outerG + ', ' + outerB + ', ' + outerAlpha + ')' : 'rgb(' + outerR + ', ' + outerG + ', ' + outerB + ')';
                    const absInner = Math.abs(innerOffset);
                    var innerRule = '-' + absInner + 'px -' + absInner + 'px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    '-' + absInner + 'px 0px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    '-' + absInner + 'px ' + absInner + 'px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    '0px -' + absInner + 'px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    '0px ' + absInner + 'px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    absInner + 'px -' + absInner + 'px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    absInner + 'px 0px ' + innerBlur + 'px ' + innerColor + ', ' +
                                    absInner + 'px ' + absInner + 'px ' + innerBlur + 'px ' + innerColor;
                    const absOuter = Math.abs(outerOffset);
                    var outerRule = '-' + absOuter + 'px -' + absOuter + 'px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    '-' + absOuter + 'px 0px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    '-' + absOuter + 'px ' + absOuter + 'px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    '0px -' + absOuter + 'px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    '0px ' + absOuter + 'px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    absOuter + 'px -' + absOuter + 'px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    absOuter + 'px 0px ' + outerBlur + 'px ' + outerColor + ', ' +
                                    absOuter + 'px ' + absOuter + 'px ' + outerBlur + 'px ' + outerColor;
                    const preview = picker.querySelector('#double-preview');
                    preview.style.fontFamily = fontFamilyStr;
                    preview.style.fontSize = fontSize;
                    preview.style.fontWeight = bold;
                    preview.style.color = textColor;
                    preview.style.textShadow = innerRule + ', ' + outerRule;
                    picker.querySelector('#text-alpha-value').textContent = textAlpha;
                    picker.querySelector('#inner-alpha-value').textContent = innerAlpha;
                    picker.querySelector('#outer-alpha-value').textContent = outerAlpha;
                    const textHex = '#' + Math.round(textR).toString(16).padStart(2, '0') + Math.round(textG).toString(16).padStart(2, '0') + Math.round(textB).toString(16).padStart(2, '0');
                    picker.querySelector('#text-hex-input').value = textHex.toUpperCase();
                    const innerHex = '#' + Math.round(innerR).toString(16).padStart(2, '0') + Math.round(innerG).toString(16).padStart(2, '0') + Math.round(innerB).toString(16).padStart(2, '0');
                    picker.querySelector('#inner-hex-input').value = innerHex.toUpperCase();
                    const outerHex = '#' + Math.round(outerR).toString(16).padStart(2, '0') + Math.round(outerG).toString(16).padStart(2, '0') + Math.round(outerB).toString(16).padStart(2, '0');
                    picker.querySelector('#outer-hex-input').value = outerHex.toUpperCase();
                }
                // Update sliders for text/inner/outer (similar to pink)
                function updateTextSlider(channel, picker) {
                    const input = picker.querySelector('#text-' + channel + '-input').value;
                    picker.querySelector('#text-' + channel + '-slider').value = input;
                    updateDoublePreview(picker);
                }
                function updateInnerSlider(channel, picker) {
                    const input = picker.querySelector('#inner-' + channel + '-input').value;
                    picker.querySelector('#inner-' + channel + '-slider').value = input;
                    updateDoublePreview(picker);
                }
                function updateOuterSlider(channel, picker) {
                    const input = picker.querySelector('#outer-' + channel + '-input').value;
                    picker.querySelector('#outer-' + channel + '-slider').value = input;
                    updateDoublePreview(picker);
                }
                doubleOutlinePicker.addEventListener('input', function(e) {
                    if (e.target.id === 'font-family-select' || e.target.id === 'font-size' || e.target.id === 'font-bold' || e.target.id === 'text-alpha-slider' || e.target.id === 'inner-offset' || e.target.id === 'inner-blur' || e.target.id === 'inner-alpha-slider' || e.target.id === 'outer-offset' || e.target.id === 'outer-blur' || e.target.id === 'outer-alpha-slider') {
                        updateDoublePreview(doubleOutlinePicker);
                    } else if (e.target.matches('#text-r-slider, #text-g-slider, #text-b-slider')) {
                        updateDoublePreview(doubleOutlinePicker);
                    } else if (e.target.matches('#inner-r-slider, #inner-g-slider, #inner-b-slider')) {
                        updateDoublePreview(doubleOutlinePicker);
                    } else if (e.target.matches('#outer-r-slider, #outer-g-slider, #outer-b-slider')) {
                        updateDoublePreview(doubleOutlinePicker);
                    } else if (e.target.id.includes('text-') && e.target.id.includes('input') && e.target.type === 'number') {
                        const channel = e.target.id.replace('text-', '').replace('-input', '');
                        updateTextSlider(channel, doubleOutlinePicker);
                    } else if (e.target.id.includes('inner-') && e.target.id.includes('input') && e.target.type === 'number') {
                        const channel = e.target.id.replace('inner-', '').replace('-input', '');
                        updateInnerSlider(channel, doubleOutlinePicker);
                    } else if (e.target.id.includes('outer-') && e.target.id.includes('input') && e.target.type === 'number') {
                        const channel = e.target.id.replace('outer-', '').replace('-input', '');
                        updateOuterSlider(channel, doubleOutlinePicker);
                    } else if (e.target.id === 'text-hex-input') {
                        const hexValue = e.target.value.replace('#', '').toUpperCase();
                        if (hexValue.match(/^([0-9A-F]{6})$/)) {
                            const rgb = hexToRgb(hexValue);
                            doubleOutlinePicker.querySelector('#text-r-slider').value = rgb.r;
                            doubleOutlinePicker.querySelector('#text-g-slider').value = rgb.g;
                            doubleOutlinePicker.querySelector('#text-b-slider').value = rgb.b;
                            doubleOutlinePicker.querySelector('#text-r-input').value = rgb.r;
                            doubleOutlinePicker.querySelector('#text-g-input').value = rgb.g;
                            doubleOutlinePicker.querySelector('#text-b-input').value = rgb.b;
                            updateDoublePreview(doubleOutlinePicker);
                        }
                    } else if (e.target.id === 'inner-hex-input') {
                        const hexValue = e.target.value.replace('#', '').toUpperCase();
                        if (hexValue.match(/^([0-9A-F]{6})$/)) {
                            const rgb = hexToRgb(hexValue);
                            doubleOutlinePicker.querySelector('#inner-r-slider').value = rgb.r;
                            doubleOutlinePicker.querySelector('#inner-g-slider').value = rgb.g;
                            doubleOutlinePicker.querySelector('#inner-b-slider').value = rgb.b;
                            doubleOutlinePicker.querySelector('#inner-r-input').value = rgb.r;
                            doubleOutlinePicker.querySelector('#inner-g-input').value = rgb.g;
                            doubleOutlinePicker.querySelector('#inner-b-input').value = rgb.b;
                            updateDoublePreview(doubleOutlinePicker);
                        }
                    } else if (e.target.id === 'outer-hex-input') {
                        const hexValue = e.target.value.replace('#', '').toUpperCase();
                        if (hexValue.match(/^([0-9A-F]{6})$/)) {
                            const rgb = hexToRgb(hexValue);
                            doubleOutlinePicker.querySelector('#outer-r-slider').value = rgb.r;
                            doubleOutlinePicker.querySelector('#outer-g-slider').value = rgb.g;
                            doubleOutlinePicker.querySelector('#outer-b-slider').value = rgb.b;
                            doubleOutlinePicker.querySelector('#outer-r-input').value = rgb.r;
                            doubleOutlinePicker.querySelector('#outer-g-input').value = rgb.g;
                            doubleOutlinePicker.querySelector('#outer-b-input').value = rgb.b;
                            updateDoublePreview(doubleOutlinePicker);
                        }
                    }
                });
                updateDoublePreview(doubleOutlinePicker);
            } catch (err) {
                log('Double outline picker open error', err);
                showDebugIndicator('Double outline picker failed; check console.', 'error');
            }
        }
        // Open symbols palette picker
        function openSymbolsPicker(ta) {
            try {
                closeAllPickers();
                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);
                const escHandler = function(e) {
                    if (e.key === 'Escape') closeSymbolsPicker();
                };
                window.addEventListener('keydown', escHandler);
            } catch (err) {
                log('Symbols picker open error', err);
                showDebugIndicator('Symbols picker failed; check console.', 'error');
            }
        }
        // Clear formatting: Strip HTML tags
        function clearFormatting(ta) {
            try {
                const text = ta.value.replace(/<[^>]*>/g, '');
                ta.value = text;
                ta.selectionStart = ta.selectionEnd = 0;
                ta.focus();
                ta.dispatchEvent(new Event('input', { bubbles: true }));
                // Post-snapshot after clear
                saveState(ta);
                // Update preview
                if (CONFIG.previewEnabled) debounceUpdatePreview();
                updateCharCount();
                log('Formatting cleared.');
            } catch (err) {
                log('Clear formatting error', err);
            }
        }
        // Insert link prompt with intelligent defaults
        function insertLink(ta) {
            try {
                const start = ta.selectionStart;
                const end = ta.selectionEnd;
                const selected = ta.value.substring(start, end);
                const url = prompt('Enter URL:');
                if (!url) return; // No post-save if cancelled
                var text;
                if (selected) {
                    text = prompt('Enter link text (optional):', selected) || selected;
                } else {
                    text = prompt('Enter link text (optional):', '') || url;
                }
                const linkHTML = '<a href="' + url + '">' + text + '</a>';
                insertAtCursor(ta, linkHTML); // Post-save inside insertAtCursor
            } catch (err) {
                log('Link insert error', err);
            }
        }
        // Insert text at cursor
        function insertAtCursor(ta, text) {
            try {
                const start = ta.selectionStart;
                const end = ta.selectionEnd;
                ta.value = ta.value.substring(0, start) + text + ta.value.substring(end);
                ta.selectionStart = ta.selectionEnd = start + text.length;
                ta.focus();
                ta.dispatchEvent(new Event('input', { bubbles: true }));
                // Post-snapshot after insert
                saveState(ta);
                // Update preview
                if (CONFIG.previewEnabled) debounceUpdatePreview();
                updateCharCount();
            } catch (err) {
                log('Insert at cursor error', err);
            }
        }
        // History management for undo/redo - now saves {value, start, end} - debounced save
        function saveState(ta) {
            try {
                if (debounceTimer) clearTimeout(debounceTimer);
                debounceTimer = setTimeout(function() {
                    const state = {
                        value: ta.value,
                        start: ta.selectionStart,
                        end: ta.selectionEnd
                    };
                    // Trim history to limit
                    if (history.length > CONFIG.historyLimit) {
                        history.shift();
                        if (historyIndex > 0) historyIndex--;
                    }
                    // Push current state after current index (linear, discards branches)
                    history = history.slice(0, historyIndex + 1);
                    history.push(state);
                    historyIndex = history.length - 1;
                    log('State saved. History length: ' + history.length);
                }, CONFIG.debounceDelay);
            } catch (err) {
                log('Save state error', err);
            }
        }
        function undo(ta) {
            try {
                if (historyIndex > 0) {
                    historyIndex--;
                    const state = history[historyIndex];
                    ta.value = state.value;
                    ta.selectionStart = state.start;
                    ta.selectionEnd = state.end;
                    ta.focus();
                    ta.dispatchEvent(new Event('input', { bubbles: true }));
                    if (CONFIG.previewEnabled) debounceUpdatePreview();
                    updateCharCount();
                    log('Undo to index ' + historyIndex);
                } else {
                    log('Undo: At start of history.');
                }
            } catch (err) {
                log('Undo error', err);
            }
        }
        function redo(ta) {
            try {
                if (historyIndex < history.length - 1) {
                    historyIndex++;
                    const state = history[historyIndex];
                    ta.value = state.value;
                    ta.selectionStart = state.start;
                    ta.selectionEnd = state.end;
                    ta.focus();
                    ta.dispatchEvent(new Event('input', { bubbles: true }));
                    if (CONFIG.previewEnabled) debounceUpdatePreview();
                    updateCharCount();
                    log('Redo to index ' + historyIndex);
                } else {
                    log('Redo: At end of history.');
                }
            } catch (err) {
                log('Redo error', err);
            }
        }
        // Setup initial history snapshot (no ongoing listener)
        function setupHistory(ta) {
            try {
                // Initial save (pre-any-changes backup)
                saveState(ta);
                log('Initial history snapshot set up.');
            } catch (err) {
                log('History setup error', err);
            }
        }
        // Keyboard shortcuts (per-tab, checks focus) - extended for undo/redo
        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);
                    }
                }
            } 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
                const observerCleanup = new MutationObserver(function(mutations) {
                    if (!document.body.contains(dialog)) {
                        activeDialogs.delete(dialog); // Track by reference now
                        if (tb && tb.parentNode) tb.remove();
                        if (previewDiv) previewDiv.remove();
                        delete dialog.dataset.helperInjected;
                        delete dialog.dataset.helperResized;
                        history = [];
                        historyIndex = -1;
                        closeAllPickers();
                        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! Upgraded Color Picker with Eyedropper (Firefox supported with ColorZilla link), Live Preview, Char Count 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' : '#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 + '! Upgraded Color Picker with Eyedropper (Firefox supported with ColorZilla link), Preview, Char Count 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);
    }
})();