Vyneer.me VODs Chat Enhancement

Complete chat enhancement: emote replacer, keyword highlighter, and custom flairs for vyneer.me VODs

// ==UserScript==
// @name         Vyneer.me VODs Chat Enhancement
// @namespace    FishVernanda
// @version      2025-10-06
// @description  Complete chat enhancement: emote replacer, keyword highlighter, and custom flairs for vyneer.me VODs
// @author       FishVernanda
// @match        https://vyneer.me/vods/*
// @match        http://localhost:1234/*
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // ========================================
    // EMOTE REPLACER
    // ========================================

    GM_addStyle(`
      /* Flair Icons */
      .flair.flair30 {
        background-image: url("https://wikicdn.destiny.gg/c/ca/Flair_league_master.png");
        height: 16px;
        width: 16px;
      }
      .flair.flair4 {
        background-image: url("https://wikicdn.destiny.gg/8/87/Flair_4.png");
        height: 16px;
        width: 16px;
      }
      .flair.flair5 {
        background-image: url("https://wikicdn.destiny.gg/5/5e/Flair_5.png");
        height: 16px;
        width: 16px;
      }
      .flair.flair16 {
        background-image: url("https://wikicdn.destiny.gg/d/df/Flair_emote_contributor.png");
        height: 16px;
        width: 16px;
      }
      .flair.flair10 {
        background-image: url("https://wikicdn.destiny.gg/6/61/Flair_starcraft_2.png");
        height: 16px;
        width: 16px;
      }
      .flair.flair25 {
        background-image: url("https://cdn.destiny.gg/flairs/5f28cdec6ed24.png");
        height: 16px;
        width: 16px;
      }
      .flair.flair27 {
        background-image: url("https://cdn.destiny.gg/flairs/60b130f07c0c4.png");
        height: 18px;
        width: 18px;
      }

      /* Emotes */
      .emote.RainbowPls {
        width: 32px; height: 32px;
        background-image: url('https://wikicdn.destiny.gg/f/f4/GRainbowPls.png');
      }
      .msg-chat .emote.RainbowPls { margin-top: -30px; top: 7.5px; }

      .emote.NathanD {
        width: 28px; height: 28px;
        background-image: url('https://wikicdn.destiny.gg/4/41/NathanD.png');
      }
      .msg-chat .emote.NathanD { margin-top: -28px; top: 7px; }

      .emote.pokiKick {
        width: 61px; height: 32px;
        background-image: url('https://wikicdn.destiny.gg/b/b1/Pokikick.gif');
      }
      .msg-chat .emote.pokiKick { margin-top: -32px; top: 8px; }

      .emote.nathanW {
        width: 28px; height: 28px;
        background-image: url('https://wikicdn.destiny.gg/e/ef/NathanW.png');
      }
      .msg-chat .emote.nathanW { margin-top: -28px; top: 7px; }

      .emote.melW {
        width: 28px; height: 28px;
        background-image: url('https://wikicdn.destiny.gg/2/2e/MelWemote.png');
      }
      .msg-chat .emote.melW { margin-top: -28px; top: 7px; }

      .emote.DestiSenpaii {
        width: 32px; height: 30px;
        background-image: url('https://wikicdn.destiny.gg/1/18/DestiSenpaii.png');
      }
      .msg-chat .emote.DestiSenpaii { margin-top: -30px; top: 7px; }

      .emote.NathanSenpai {
        width: 28px; height: 28px;
        background-image: url('https://wikicdn.destiny.gg/a/a4/NathanSenpai.png');
      }
      .msg-chat .emote.NathanSenpai { margin-top: -28px; top: 7px; }

      .emote.PICNIC {
        width: 50px; height: 20px;
        background-image: url('https://wikicdn.destiny.gg/f/f8/PICNIC.png');
      }
      .msg-chat .emote.PICNIC { margin-top: -20px; top: 6px; }

      .emote.Yoda1 {
        width: 28px; height: 28px;
        background-image: url('https://wikicdn.destiny.gg/1/18/Yoda1.png');
      }
      .msg-chat .emote.Yoda1 { margin-top: -28px; top: 7px; }

      .emote.DatGeoff {
        width: 21px; height: 30px;
        background-image: url('https://wikicdn.destiny.gg/3/3c/DatGeoff.png');
      }
      .msg-chat .emote.DatGeoff { margin-top: -30px; top: 7px; }

      .emote.ComfyMel {
        width: 32px; height: 32px;
        background-image: url('https://wikicdn.destiny.gg/5/56/ComfyMel.png');
      }
      .msg-chat .emote.ComfyMel { margin-top: -32px; top: 7px; }

      .emote.ATAB {
        width: 40px; height: 30px;
        background-image: url('https://wikicdn.destiny.gg/c/c2/ATAB.png');
      }
      .msg-chat .emote.ATAB { margin-top: -30px; top: 7px; }

      .emote.nathanShroom {
        width: 28px; height: 28px;
        background-image: url('https://wikicdn.destiny.gg/3/32/NathanShroom.png');
      }
      .msg-chat .emote.nathanShroom { margin-top: -28px; top: 7px; }

      .emote.ComfyAYA {
        width: 32px; height: 32px;
        background-image: url('https://wikicdn.destiny.gg/9/95/ComfyAYA.png');
      }
      .msg-chat .emote.ComfyAYA { margin-top: -32px; top: 7px; }

      .emote.AUTISTINY {
        width: 28px; height: 28px;
        background-image: url('https://wikicdn.destiny.gg/8/83/NathanDerp.png');
      }
      .msg-chat .emote.AUTISTINY { margin-top: -28px; top: 7px; }

      .emote.SoDoge {
        width: 52px; height: 30px;
        background-image: url('https://wikicdn.destiny.gg/7/73/SoDoge.png');
      }
      .msg-chat .emote.SoDoge { margin-top: -30px; top: 7px; }

      .emote.nathanYikes {
        width: 28px; height: 28px;
        background-image: url('https://wikicdn.destiny.gg/1/1a/NathanYikes.png');
      }
      .msg-chat .emote.nathanYikes { margin-top: -28px; top: 7px; }

      .emote.nathanEZ {
        width: 28px; height: 28px;
        background-image: url('https://wikicdn.destiny.gg/0/0e/NathanEZ.png');
      }
      .msg-chat .emote.nathanEZ { margin-top: -28px; top: 7px; }

      .emote.TF {
        height: 30px; width: 37px;
        background-image: url("https://wikicdn.destiny.gg/c/c8/TrollFace.png");
      }
      .msg-chat .emote.TF { margin-top: -30px; top: 7.5px; }
      .emote.tf {
        height: 30px; width: 37px;
        background-image: url("https://wikicdn.destiny.gg/c/c8/TrollFace.png");
      }
      .msg-chat .emote.tf { margin-top: -30px; top: 7.5px; }
    `);

    const emoteMap = {
        rainbowpls: "RainbowPls",
        pokikick: "pokiKick",
        nathanw: "nathanW",
        melw: "melW",
        destisenpaii: "DestiSenpaii",
        nathansenpai: "NathanSenpai",
        picnic: "PICNIC",
        yoda1: "Yoda1",
        datgeoff: "DatGeoff",
        comfymel: "ComfyMel",
        atab: "ATAB",
        nathand: "NathanD",
        nathanshroom: "nathanShroom",
        comfyaya: "ComfyAYA",
        autistiny: "AUTISTINY",
        sodoge: "SoDoge",
        nathanyikes: "nathanYikes",
        nathanez: "nathanEZ",
        tf: "TF",
    };

    function getFullEmoteMap() {
        const fullMap = {...emoteMap};
        for (const [searchName, emoteData] of Object.entries(customEmotes)) {
            fullMap[searchName.toLowerCase()] = emoteData.displayName || searchName;
        }
        return fullMap;
    }

    function replaceTextWithEmotesInNode(textNode) {
        const parent = textNode.parentNode;
        const text = textNode.nodeValue;
        const fullEmoteMap = getFullEmoteMap();
        const pattern = new RegExp(`\\b(${Object.keys(fullEmoteMap).join('|')})\\b`, 'gi');
        if (!pattern.test(text)) return;

        const parts = text.split(pattern);
        parent.removeChild(textNode);

        for (const part of parts) {
            if (!part) continue;
            const key = part.toLowerCase();
            if (fullEmoteMap[key]) {
                const emoteDiv = document.createElement('div');
                emoteDiv.className = 'emote ' + fullEmoteMap[key];
                emoteDiv.title = fullEmoteMap[key];
                parent.appendChild(emoteDiv);
            } else {
                parent.appendChild(document.createTextNode(part));
            }
        }
    }

    function processMessageSpanForEmotes(span) {
        const childNodes = Array.from(span.childNodes);
        for (const node of childNodes) {
            if (node.nodeType === Node.TEXT_NODE) {
                replaceTextWithEmotesInNode(node);
            }
        }
    }

    function processExistingMessagesForEmotes() {
        const chatMessages = document.querySelectorAll('#chat-stream .msg-chat .message');
        chatMessages.forEach(processMessageSpanForEmotes);
    }

    // ========================================
    // KEYWORD HIGHLIGHTER & SETTINGS UI
    // ========================================

    GM_addStyle(`
      .msg-highlight {
        color: #dedede !important;
        background-color: #06263e !important;
      }

      #highlight-settings-container {
        position: relative;
        display: inline-block;
      }
      #highlight-settings-content {
        display: none;
        position: absolute;
        right: 0;
        top: 32px;
        background-color: #1c1c1c;
        border: 1px solid #444;
        padding: 12px;
        z-index: 1001;
        width: 300px;
        box-shadow: 0 2px 8px rgba(0,0,0,.4);
        border-radius: 4px;
        max-height: 70vh;
        overflow: auto;
      }

      .label-text {
        color: #ccc;
        font-size: 12px;
        margin-bottom: 4px;
        display: block;
      }

      textarea {
        width: 90% !important;
        background: #2a2a2a;
        border: 1px solid #444;
        color: #fff;
        border-radius: 2px;
        font-size: 12px;
        padding: 6px;
      }

      select {
        background: #2a2a2a;
        border: 1px solid #444;
        color: #fff;
        border-radius: 2px;
        font-size: 12px;
        padding: 4px;
      }

      .emote-section, .flair-row, .blacklist-row {
        background: #1a1a1a;
        border: 1px solid #333;
        border-radius: 4px;
        padding: 8px;
        margin-bottom: 8px;
      }

      .settings-export-import {
        display: flex;
        gap: 4px;
        margin-bottom: 12px;
        padding-bottom: 12px;
        border-bottom: 1px solid #666;
        justify-content: flex-end;
        align-items: center;
      }

      .settings-export-import .focus-checkbox-wrapper {
        margin-right: auto;
        display: flex;
        align-items: center;
        gap: 6px;
      }

      .settings-export-import .focus-checkbox-wrapper input[type="checkbox"] {
        cursor: pointer;
      }

      .settings-export-import .focus-checkbox-wrapper label {
        cursor: pointer;
        font-size: 12px;
        color: #ccc;
      }

      .settings-export-import button {
        padding: 4px 8px;
        background: #444;
        border: 1px solid #666;
        color: #fff;
        cursor: pointer;
        border-radius: 3px;
        font-size: 11px;
      }

      .settings-export-import button:hover {
        background: #555;
      }

      .settings-export-import button.success {
        background: #2d5016;
        border-color: #4a8028;
      }

      .settings-export-import button.error {
        background: #661111;
        border-color: #881111;
      }

      #highlightKeywords, #flair34Users, #vipUsers {
        width: 95%;
        margin-top: 5px;
        margin-bottom: 10px;
        resize: vertical;
        min-height: 80px;
      }

      .flair-row {
        display: block;
        margin-bottom: 12px;
        border-top: 1px solid rgba(255,255,255,0.04);
        padding-top: 8px;
      }

      .flair-row .flair-top {
        display: flex;
        gap: 8px;
        align-items: center;
      }

      .flair-row .flair-top select {
        flex: 1 1 auto;
        padding: 6px;
        font-size: 13px;
      }

      .remove-flair, .remove-emote, .remove-blacklist {
        flex: 0 0 30px;
        height: 30px;
        background: #661111;
        border: 1px solid #881111;
        color: #fff;
        cursor: pointer;
        border-radius: 3px;
        font-weight: bold;
      }

      .remove-flair:hover, .remove-emote:hover, .remove-blacklist:hover {
        background: #881111;
      }

      .flair-row textarea {
        width: 95%;
        margin-top: 6px;
        min-height: 70px;
        resize: vertical;
        padding: 6px;
      }

      #addFlairButton {
        padding: 6px 12px;
        background: #444;
        border: 1px solid #666;
        color: #fff;
        cursor: pointer;
        border-radius: 3px;
        margin-top: 5px;
      }

      #addFlairButton:hover {
        background: #666;
      }

      .emote-section,
      .flair-removal-section,
      .custom-flairs-section {
        margin-top: 15px;
        padding: 12px;
        background: #1a1a1a;
        border: 1px solid #333;
        border-radius: 4px;
      }

      .settings-section-header {
        font-size: 14px;
        color: #ccc;
        margin-bottom: 12px;
        padding-bottom: 8px;
        border-bottom: 1px solid #333;
      }

      .emote-row {
        display: block;
        margin-bottom: 12px;
        border-top: 1px solid rgba(255,255,255,0.04);
        padding-top: 8px;
        max-width: 340px;
      }

      .toggle-advanced {
        background: none !important;
        border: none !important;
        color: #aaa !important;
        padding: 4px 0 !important;
        cursor: pointer !important;
        width: 100% !important;
        text-align: left !important;
        margin: 8px 0 !important;
        font-size: 12px !important;
      }

      .toggle-advanced:hover {
        color: #fff !important;
      }

      .emote-advanced-settings {
        display: none;
        margin-top: 8px;
        padding: 8px;
        background: #1a1a1a;
        border-radius: 4px;
      }

      .emote-row-header {
        display: flex;
        gap: 8px;
        align-items: center;
        margin-bottom: 6px;
      }

      .toggle-advanced {
        background: none !important;
        border: none !important;
        color: #aaa !important;
        padding: 4px 0 !important;
        cursor: pointer !important;
        width: 100% !important;
        text-align: left !important;
        margin: 8px 0 !important;
        font-size: 12px !important;
      }

      .toggle-advanced:hover {
        color: #fff !important;
      }

      .emote-advanced-settings {
        display: none;
        margin-top: 8px;
        padding: 8px;
        background: #1a1a1a;
        border-radius: 4px;
      }

      .emote-row-header button.remove-emote {
        flex: 0 0 30px;
        height: 30px;
        background: #661111;
        border: 1px solid #881111;
        color: #fff;
        cursor: pointer;
        border-radius: 3px;
        font-weight: bold;
      }

      .emote-row-header button.remove-emote:hover {
        background: #881111;
      }

      .emote-row input[type="text"] {
        width: 180px;
        box-sizing: border-box;
        padding: 4px 6px;
        margin-bottom: 4px;
        font-size: 12px;
        background: #2a2a2a;
        border: 1px solid #444;
        color: #fff;
        border-radius: 2px;
      }

      .emote-row input[type="number"] {
        width: 70px;
        box-sizing: border-box;
        padding: 4px 6px;
        margin-bottom: 4px;
        font-size: 12px;
        background: #2a2a2a;
        border: 1px solid #444;
        color: #fff;
        border-radius: 2px;
      }

      .emote-row .emote-dimensions {
        margin-bottom: 8px;
      }

      .emote-row .emote-dimensions .dimension-group {
        display: flex;
        gap: 8px;
        margin-bottom: 4px;
      }

      .emote-row .emote-dimensions .dimension-group > div {
        flex: 1;
      }

      .emote-row textarea {
        width: 100%;
        box-sizing: border-box;
        margin-top: 4px;
        min-height: 60px;
        resize: vertical;
        padding: 6px;
        font-family: monospace;
        font-size: 11px;
        background: #2a2a2a;
        border: 1px solid #444;
        color: #fff;
        border-radius: 2px;
      }

      .emote-row label {
        display: block;
        font-size: 11px;
        color: #aaa;
        margin-bottom: 2px;
      }

      .emote-section {
        margin-top: 15px;
        padding: 8px;
        background: #1a1a1a;
        border-radius: 4px;
      }

      .emote-advanced-settings {
        display: none;
        margin-top: 8px;
        padding: 8px;
        background: #1a1a1a;
        border-radius: 4px;
      }

      .toggle-advanced {
        background: none !important;
        border: none !important;
        color: #aaa !important;
        padding: 4px 0 !important;
        cursor: pointer !important;
        width: 100% !important;
        text-align: left !important;
        margin: 8px 0 !important;
        font-size: 12px !important;
      }

      .toggle-advanced:hover {
        color: #fff !important;
      }

      .emote-advanced-settings {
        background: #1a1a1a;
        border-radius: 4px;
        padding: 8px;
        margin-top: 8px;
      }

      .advanced-toggle {
        background: none;
        border: none;
        color: #aaa;
        padding: 4px 0;
        cursor: pointer;
        width: 100%;
        text-align: left;
        margin-top: 8px;
        font-size: 12px;
      }

      .advanced-toggle:hover {
        color: #fff;
      }

      .emote-property {
        margin-bottom: 8px;
      }

      .emote-presets {
        margin-bottom: 12px;
        padding: 8px;
        background: #2a2a2a;
        border-radius: 4px;
      }

      .emote-presets select {
        width: 100%;
        padding: 6px;
        background: #333;
        border: 1px solid #444;
        color: #fff;
        border-radius: 2px;
      }

      #addEmoteButton {
        padding: 6px 12px;
        background: #444;
        border: 1px solid #666;
        color: #fff;
        cursor: pointer;
        border-radius: 3px;
        margin-top: 5px;
      }

      #addEmoteButton:hover {
        background: #666;
      }

      .flair-removal-section {
        margin-top: 15px;
        padding-top: 15px;
        border-top: 1px solid #666;
      }

      .blacklist-row {
        display: block;
        margin-bottom: 12px;
        border-top: 1px solid rgba(255,255,255,0.04);
        padding-top: 8px;
      }

      .blacklist-row .blacklist-top {
        display: flex;
        gap: 8px;
        align-items: center;
      }

      .blacklist-row .blacklist-top select {
        flex: 1 1 auto;
        padding: 6px;
        font-size: 13px;
      }

      .blacklist-row .blacklist-top button.remove-blacklist {
        flex: 0 0 30px;
        height: 30px;
        background: #661111;
        border: 1px solid #881111;
        color: #fff;
        cursor: pointer;
        border-radius: 3px;
        font-weight: bold;
      }

      .blacklist-row .blacklist-top button.remove-blacklist:hover {
        background: #881111;
      }

      .blacklist-row textarea {
        width: 95%;
        margin-top: 6px;
        min-height: 70px;
        resize: vertical;
        padding: 6px;
      }

      #addBlacklistButton {
        padding: 6px 12px;
        background: #883333;
        border: 1px solid #aa4444;
        color: #fff;
        cursor: pointer;
        border-radius: 3px;
        margin-top: 5px;
      }

      #addBlacklistButton:hover {
        background: #aa4444;
      }

      #highlight-settings-button {
        cursor: pointer;
        margin-left: 4px;
      }

      #highlight-settings-button .octicon {
        width: 16px;
        height: 16px;
        display: inline-block;
        vertical-align: middle;
      }
    `);

    let keywordsToHighlight = [];
    let flair34Users = [];
    let vipUsers = [];
    let selectedFlairUsers = {};
    let flairBlacklist = {};
    let customEmotes = {};
    let includeMentionsInFocus = false;

    const flairDefinitions = {
        'flair13': 'Subscriber Tier 1',
        'flair1': 'Subscriber Tier 2',
        'flair3': 'Subscriber Tier 3',
        'flair8': 'Subscriber Tier 4',
        'flair42': 'Subscriber Tier 5',
        'subscriber': 'Subscriber',
        'flair9': 'Twitch Subscriber',
        'flair32': 'Tier 1 AE Sub',
        'flair22': 'Tier 2 AE Sub',
        'flair24': 'Tier 3 AE Sub',
        'flair26': 'Tier 4 AE Sub',
        'flair33': 'Tier 5 AE Sub',
        'bot': 'Bot',
        'flair11': 'Old Bot',
        'admin': 'Admin',
        'moderator': 'Moderator',
        'vip': 'VIP',
        'flair12': 'Broadcaster',
        'flair20': 'Verified',
        'flair2': 'Notable Chatter',
        'flair17': 'Extra Notable Chatter',
        'flair4': 'Trusted',
        'flair5': 'Contributor',
        'flair16': 'Emote Contributor',
        'flair25': 'Youtube Contributor',
        'flair18': 'Emote Manager',
        'flair19': 'DGG Shirt Designer',
        'flair21': 'YouTube Editor',
        'flair27': 'TikTok Editor',
        'flair31': 'Developer',
        'flair7': 'NFL Chatter',
        'flair10': 'StarCraft 2',
        'flair30': 'League Master',
        'flair29': 'Weight Lifting',
        'flair23': 'Doctor',
        'flair28': 'Lawyer',
        'flair34': 'Conductor',
        'flair15': 'Birthday',
        'flair58': 'New User'
    };

    function loadKeywords() {
        const savedKeywords = GM_getValue('highlightKeywords', '');
        keywordsToHighlight = savedKeywords.split(',').map(k => k.trim().toLowerCase()).filter(Boolean);
    }

    function loadFlair34Users() {
        const saved = GM_getValue('flair34Users', '');
        flair34Users = saved.split(',').map(k => k.trim().toLowerCase()).filter(Boolean);
    }

    function loadVipUsers() {
        const saved = GM_getValue('vipUsers', '');
        vipUsers = saved.split(',').map(k => k.trim().toLowerCase()).filter(Boolean);
    }

    function loadSelectedFlairUsers() {
        const saved = GM_getValue('selectedFlairUsers', '{}');
        try {
            selectedFlairUsers = JSON.parse(saved);
        } catch (e) {
            selectedFlairUsers = {};
        }
    }

    function loadFlairBlacklist() {
        const saved = GM_getValue('flairBlacklist', '{}');
        try {
            flairBlacklist = JSON.parse(saved);
        } catch (e) {
            flairBlacklist = {};
        }
    }

    function loadCustomEmotes() {
        const saved = GM_getValue('customEmotes', '{}');
        try {
            customEmotes = JSON.parse(saved);
        } catch (e) {
            customEmotes = {};
        }
    }

    function loadIncludeMentionsInFocus() {
        includeMentionsInFocus = GM_getValue('includeMentionsInFocus', false);
    }

    function applyCustomEmoteStyles() {
        let emoteStyles = '';
        for (const [emoteName, emoteData] of Object.entries(customEmotes)) {
            const className = emoteData.displayName || emoteName;
            emoteStyles += `
                .emote.${className} {
                    width: ${emoteData.width}px;
                    height: ${emoteData.height}px;
                    background-image: url('${emoteData.url}');
                }
                .msg-chat .emote.${className} {
                    margin-top: ${emoteData.marginTop || -emoteData.height}px;
                    top: ${emoteData.top || Math.floor(emoteData.height / 4)}px;
                    ${emoteData.marginLeft ? `margin-left: ${emoteData.marginLeft};` : ''}
                    ${emoteData.marginRight ? `margin-right: ${emoteData.marginRight};` : ''}
                    ${emoteData.transform ? `transform: ${emoteData.transform};` : ''}
                    ${emoteData.filter ? `filter: ${emoteData.filter};` : ''}
                    ${emoteData.backgroundPosition ? `background-position: ${emoteData.backgroundPosition};` : ''}
                    ${emoteData.backgroundSize ? `background-size: ${emoteData.backgroundSize};` : ''}
                    ${emoteData.backgroundRepeat ? `background-repeat: ${emoteData.backgroundRepeat};` : ''}
                    ${emoteData.transition ? `transition: ${emoteData.transition};` : ''}
                }
            `;

            if (emoteData.animation) {
                emoteStyles += emoteData.animation + '\n';
            }
        }
        if (emoteStyles) {
            GM_addStyle(emoteStyles);
        }
    }

    function saveKeywords(keywordsStr) {
        const keywords = keywordsStr.split(',').map(k => k.trim().toLowerCase()).filter(Boolean);
        keywordsToHighlight = keywords;
        GM_setValue('highlightKeywords', keywords.join(','));
        clearHighlights();
        processExistingMessagesForHighlight();
    }

    function saveFlair34Users(usersStr) {
        const users = usersStr.split(',').map(k => k.trim().toLowerCase()).filter(Boolean);
        flair34Users = users;
        GM_setValue('flair34Users', users.join(','));
        processExistingMessagesForFlairs();
    }

    function saveVipUsers(usersStr) {
        const users = usersStr.split(',').map(k => k.trim().toLowerCase()).filter(Boolean);
        vipUsers = users;
        GM_setValue('vipUsers', users.join(','));
        processExistingMessagesForFlairs();
    }

    function clearHighlights() {
        const highlighted = document.querySelectorAll('#chat-stream .msg-highlight');
        highlighted.forEach(el => el.classList.remove('msg-highlight'));
    }

    function shouldHighlight(text) {
        const lower = text.toLowerCase();
        return keywordsToHighlight.some(name => lower.includes(name));
    }

    function processMessageForHighlight(div) {
        const rawText = div.textContent || '';
        if (shouldHighlight(rawText)) {
            div.classList.add('msg-highlight');
        }
    }

    function processExistingMessagesForHighlight() {
        const messages = document.querySelectorAll('#chat-stream .msg-chat');
        messages.forEach(processMessageForHighlight);
    }

    function setupSettingsUI() {
        const settingsContainer = document.getElementById('settings');
        if (!settingsContainer) {
            setTimeout(setupSettingsUI, 500);
            return;
        }

        const dropdownContainer = document.createElement('div');
        dropdownContainer.id = 'highlight-settings-container';
        dropdownContainer.className = 'settings-dropdown';

        const button = document.createElement('a');
        button.id = 'highlight-settings-button';
        button.setAttribute('aria-label', 'Highlight Settings');
        button.setAttribute('data-tippy-content', 'Highlight Settings');
        button.innerHTML = '<span class="octicon octicon-gear"></span>';

        const content = document.createElement('div');
        content.id = 'highlight-settings-content';

        const exportImportDiv = document.createElement('div');
        exportImportDiv.className = 'settings-export-import';

        const focusCheckboxWrapper = document.createElement('div');
        focusCheckboxWrapper.className = 'focus-checkbox-wrapper';

        const focusCheckbox = document.createElement('input');
        focusCheckbox.type = 'checkbox';
        focusCheckbox.id = 'includeMentionsCheckbox';
        focusCheckbox.checked = includeMentionsInFocus;
        focusCheckbox.addEventListener('change', () => {
            includeMentionsInFocus = focusCheckbox.checked;
            GM_setValue('includeMentionsInFocus', includeMentionsInFocus);
            // No need to call updateFocusRules - it checks the checkbox dynamically
        });

        const focusLabel = document.createElement('label');
        focusLabel.htmlFor = 'includeMentionsCheckbox';
        focusLabel.textContent = 'Include mentions when focused';

        focusCheckboxWrapper.appendChild(focusCheckbox);
        focusCheckboxWrapper.appendChild(focusLabel);

        const exportButton = document.createElement('button');
        exportButton.textContent = 'Copy';
        exportButton.addEventListener('click', () => {
            const settings = {
                highlightKeywords: keywordsToHighlight,
                flair34Users: flair34Users,
                vipUsers: vipUsers,
                selectedFlairUsers: selectedFlairUsers,
                flairBlacklist: flairBlacklist,
                customEmotes: customEmotes,
                includeMentionsInFocus: includeMentionsInFocus
            };

            navigator.clipboard.writeText(JSON.stringify(settings, null, 2)).then(() => {
                const originalText = exportButton.textContent;
                exportButton.textContent = '✓';
                exportButton.classList.add('success');
                setTimeout(() => {
                    exportButton.textContent = originalText;
                    exportButton.classList.remove('success');
                }, 2000);
            }).catch(err => {
                console.error('Failed to copy:', err);
                exportButton.textContent = '✗';
                exportButton.classList.add('error');
                setTimeout(() => {
                    exportButton.textContent = 'Copy';
                    exportButton.classList.remove('error');
                }, 2000);
            });
        });

        const importButton = document.createElement('button');
        importButton.textContent = 'Paste';

        const label1 = document.createElement('label');
        label1.className = 'label-text';
        label1.textContent = 'Highlight Keywords:';

        const textarea1 = document.createElement('textarea');
        textarea1.id = 'highlightKeywords';
        textarea1.className = 'settings-options';
        textarea1.placeholder = 'Comma separated keywords...';
        textarea1.value = keywordsToHighlight.join(',');
        textarea1.addEventListener('change', () => saveKeywords(textarea1.value));

        const label2 = document.createElement('label');
        label2.className = 'label-text';
        label2.textContent = 'Conductors (Flair34):';

        const textarea2 = document.createElement('textarea');
        textarea2.id = 'flair34Users';
        textarea2.className = 'settings-options';
        textarea2.placeholder = 'Comma separated usernames...';
        textarea2.value = flair34Users.join(',');
        textarea2.addEventListener('change', () => saveFlair34Users(textarea2.value));

        const label3 = document.createElement('label');
        label3.className = 'label-text';
        label3.textContent = 'VIP Users:';

        const textarea3 = document.createElement('textarea');
        textarea3.id = 'vipUsers';
        textarea3.className = 'settings-options';
        textarea3.placeholder = 'Comma separated usernames...';
        textarea3.value = vipUsers.join(',');
        textarea3.addEventListener('change', () => saveVipUsers(textarea3.value));

        const label4 = document.createElement('label');
        label4.className = 'label-text';
        label4.textContent = 'Custom Flairs:';
        label4.style.marginTop = '10px';

        const flairContainer = document.createElement('div');
        flairContainer.id = 'flairContainer';

        const addButton = document.createElement('button');
        addButton.id = 'addFlairButton';
        addButton.textContent = '+ Add Flair';
        addButton.addEventListener('click', () => addFlairRow());

        function createFlairRow(flairKey = '', users = '') {
            const row = document.createElement('div');
            row.className = 'flair-row';

            const top = document.createElement('div');
            top.className = 'flair-top';

            const select = document.createElement('select');
            const defaultOpt = document.createElement('option');
            defaultOpt.value = '';
            defaultOpt.textContent = 'Select flair...';
            select.appendChild(defaultOpt);

            for (const [key, name] of Object.entries(flairDefinitions)) {
                const opt = document.createElement('option');
                opt.value = key;
                opt.textContent = `${name}`;
                if (key === flairKey) opt.selected = true;
                select.appendChild(opt);
            }

            const removeBtn = document.createElement('button');
            removeBtn.className = 'remove-flair';
            removeBtn.textContent = '×';
            removeBtn.title = 'Remove this flair mapping';
            removeBtn.addEventListener('click', (e) => {
                e.stopPropagation();
                row.remove();
                saveAllFlairRows();
            });

            top.appendChild(select);
            top.appendChild(removeBtn);

            const textarea = document.createElement('textarea');
            textarea.placeholder = 'Comma separated usernames...';
            textarea.value = users;

            select.addEventListener('change', () => saveAllFlairRows());
            textarea.addEventListener('input', () => saveAllFlairRows());

            row.appendChild(top);
            row.appendChild(textarea);

            return row;
        }

        function addFlairRow(flairKey = '', users = '') {
            const row = createFlairRow(flairKey, users);
            flairContainer.appendChild(row);
        }

        function saveAllFlairRows() {
            const rows = flairContainer.querySelectorAll('.flair-row');
            const newFlairUsers = {};

            rows.forEach(row => {
                const select = row.querySelector('select');
                const textarea = row.querySelector('textarea');
                const flair = select.value;
                const users = textarea.value.split(',').map(u => u.trim().toLowerCase()).filter(Boolean);

                if (flair && users.length > 0) {
                    if (!newFlairUsers[flair]) {
                        newFlairUsers[flair] = [];
                    }
                    newFlairUsers[flair].push(...users);
                }
            });

            selectedFlairUsers = newFlairUsers;
            GM_setValue('selectedFlairUsers', JSON.stringify(selectedFlairUsers));
            processExistingMessagesForFlairs();
        }

        for (const [flairKey, usersList] of Object.entries(selectedFlairUsers)) {
            addFlairRow(flairKey, Array.isArray(usersList) ? usersList.join(',') : String(usersList));
        }

        const removalSection = document.createElement('div');
        removalSection.className = 'flair-removal-section';

        const removalLabel = document.createElement('label');
        removalLabel.className = 'label-text';
        removalLabel.textContent = 'Remove Flairs from Users:';

        const blacklistContainer = document.createElement('div');
        blacklistContainer.id = 'blacklistContainer';

        const addBlacklistButton = document.createElement('button');
        addBlacklistButton.id = 'addBlacklistButton';
        addBlacklistButton.textContent = '+ Add Flair Removal';
        addBlacklistButton.addEventListener('click', () => addBlacklistRow());

        function createBlacklistRow(flairKey = '', users = '') {
            const row = document.createElement('div');
            row.className = 'blacklist-row';

            const top = document.createElement('div');
            top.className = 'blacklist-top';

            const select = document.createElement('select');
            const defaultOpt = document.createElement('option');
            defaultOpt.value = '';
            defaultOpt.textContent = 'Select flair to remove...';
            select.appendChild(defaultOpt);

            for (const [key, name] of Object.entries(flairDefinitions)) {
                const opt = document.createElement('option');
                opt.value = key;
                opt.textContent = `${name}`;
                if (key === flairKey) opt.selected = true;
                select.appendChild(opt);
            }

            const removeBtn = document.createElement('button');
            removeBtn.className = 'remove-blacklist';
            removeBtn.textContent = '×';
            removeBtn.title = 'Remove this flair removal mapping';
            removeBtn.addEventListener('click', (e) => {
                e.stopPropagation();
                row.remove();
                saveAllBlacklistRows();
            });

            top.appendChild(select);
            top.appendChild(removeBtn);

            const textarea = document.createElement('textarea');
            textarea.placeholder = 'Comma separated usernames to remove this flair from...';
            textarea.value = users;

            select.addEventListener('change', () => saveAllBlacklistRows());
            textarea.addEventListener('input', () => saveAllBlacklistRows());

            row.appendChild(top);
            row.appendChild(textarea);

            return row;
        }

        function addBlacklistRow(flairKey = '', users = '') {
            const row = createBlacklistRow(flairKey, users);
            blacklistContainer.appendChild(row);
        }

        function saveAllBlacklistRows() {
            const rows = blacklistContainer.querySelectorAll('.blacklist-row');
            const newBlacklist = {};

            rows.forEach(row => {
                const select = row.querySelector('select');
                const textarea = row.querySelector('textarea');
                const flair = select.value;
                const users = textarea.value.split(',').map(u => u.trim().toLowerCase()).filter(Boolean);

                if (flair && users.length > 0) {
                    users.forEach(username => {
                        if (!newBlacklist[username]) {
                            newBlacklist[username] = [];
                        }
                        if (!newBlacklist[username].includes(flair)) {
                            newBlacklist[username].push(flair);
                        }
                    });
                }
            });

            flairBlacklist = newBlacklist;
            GM_setValue('flairBlacklist', JSON.stringify(flairBlacklist));
            processExistingMessagesForFlairRemoval();
        }

        const rebuildBlacklistRows = () => {
            blacklistContainer.innerHTML = '';
            const blacklistByFlair = {};
            for (const [username, flairs] of Object.entries(flairBlacklist)) {
                flairs.forEach(flair => {
                    if (!blacklistByFlair[flair]) {
                        blacklistByFlair[flair] = [];
                    }
                    blacklistByFlair[flair].push(username);
                });
            }
            for (const [flair, users] of Object.entries(blacklistByFlair)) {
                addBlacklistRow(flair, users.join(','));
            }
        };

        rebuildBlacklistRows();

        importButton.addEventListener('click', () => {
            navigator.clipboard.readText().then(text => {
                try {
                    const settings = JSON.parse(text);

                    if (!settings || typeof settings !== 'object') {
                        throw new Error('Invalid settings format');
                    }

                    if (Array.isArray(settings.highlightKeywords)) {
                        keywordsToHighlight = settings.highlightKeywords;
                        GM_setValue('highlightKeywords', keywordsToHighlight.join(','));
                        textarea1.value = keywordsToHighlight.join(',');
                    }

                    if (Array.isArray(settings.flair34Users)) {
                        flair34Users = settings.flair34Users;
                        GM_setValue('flair34Users', flair34Users.join(','));
                        textarea2.value = flair34Users.join(',');
                    }

                    if (Array.isArray(settings.vipUsers)) {
                        vipUsers = settings.vipUsers;
                        GM_setValue('vipUsers', vipUsers.join(','));
                        textarea3.value = vipUsers.join(',');
                    }

                    if (settings.selectedFlairUsers && typeof settings.selectedFlairUsers === 'object') {
                        selectedFlairUsers = settings.selectedFlairUsers;
                        GM_setValue('selectedFlairUsers', JSON.stringify(selectedFlairUsers));

                        flairContainer.innerHTML = '';
                        for (const [flairKey, usersList] of Object.entries(selectedFlairUsers)) {
                            addFlairRow(flairKey, Array.isArray(usersList) ? usersList.join(',') : String(usersList));
                        }
                    }

                    if (settings.flairBlacklist && typeof settings.flairBlacklist === 'object') {
                        flairBlacklist = settings.flairBlacklist;
                        GM_setValue('flairBlacklist', JSON.stringify(flairBlacklist));
                        rebuildBlacklistRows();
                    }

                    if (settings.customEmotes && typeof settings.customEmotes === 'object') {
                        customEmotes = settings.customEmotes;
                        GM_setValue('customEmotes', JSON.stringify(customEmotes));
                        rebuildEmoteRows();
                        applyCustomEmoteStyles();
                    }

                    if (typeof settings.includeMentionsInFocus === 'boolean') {
                        includeMentionsInFocus = settings.includeMentionsInFocus;
                        GM_setValue('includeMentionsInFocus', includeMentionsInFocus);
                        focusCheckbox.checked = includeMentionsInFocus;
                        updateFocusRules();
                    }

                    clearHighlights();
                    processExistingMessagesForHighlight();
                    processExistingMessagesForFlairRemoval();
                    processExistingMessagesForFlairs();
                    processExistingMessagesForEmotes();

                    const originalText = importButton.textContent;
                    importButton.textContent = '✓';
                    importButton.classList.add('success');
                    setTimeout(() => {
                        importButton.textContent = originalText;
                        importButton.classList.remove('success');
                    }, 2000);

                } catch (err) {
                    console.error('Failed to parse settings:', err);
                    importButton.textContent = '✗';
                    importButton.classList.add('error');
                    setTimeout(() => {
                        importButton.textContent = 'Paste';
                        importButton.classList.remove('error');
                    }, 2000);
                }
            }).catch(err => {
                console.error('Failed to read clipboard:', err);
                importButton.textContent = '✗';
                importButton.classList.add('error');
                setTimeout(() => {
                    importButton.textContent = 'Paste';
                    importButton.classList.remove('error');
                }, 2000);
            });
        });

        removalSection.appendChild(removalLabel);
        removalSection.appendChild(blacklistContainer);
        removalSection.appendChild(addBlacklistButton);

        // Custom Emote Section
        const emoteSection = document.createElement('div');
        emoteSection.className = 'emote-section';

        const emoteLabel = document.createElement('label');
        emoteLabel.className = 'label-text';
        emoteLabel.textContent = 'Custom Emotes:';

        const emoteContainer = document.createElement('div');
        emoteContainer.id = 'emoteContainer';

        const addEmoteButton = document.createElement('button');
        addEmoteButton.id = 'addEmoteButton';
        addEmoteButton.textContent = '+ Add Emote';
        addEmoteButton.addEventListener('click', () => addEmoteRow());

        // Keep track of advanced toggle state for each row
        const advancedStates = new WeakMap();

        function createAdvancedToggle(section) {
                const toggle = document.createElement('button');
                toggle.className = 'advanced-toggle';
                toggle.innerHTML = '&#9654; Advanced Settings';
                toggle.style.background = 'none';
                toggle.style.border = 'none';
                toggle.style.color = '#aaa';
                toggle.style.padding = '4px 0';
                toggle.style.cursor = 'pointer';
                toggle.style.width = '100%';
                toggle.style.textAlign = 'left';
                toggle.style.marginTop = '8px';

                toggle.addEventListener('click', () => {
                    const isCollapsed = section.style.display === 'none';
                    section.style.display = isCollapsed ? 'block' : 'none';
                    toggle.innerHTML = `${isCollapsed ? '&#9660;' : '&#9654;'} Advanced Settings`;
                });

                return toggle;
            }

            function createEmoteRow(searchName = '', data = {}) {
                const row = document.createElement('div');
                row.className = 'emote-row';

            // Header with remove button
            const rowHeader = document.createElement('div');
            rowHeader.className = 'emote-row-header';

            const removeButton = document.createElement('button');
            removeButton.className = 'remove-emote';
            removeButton.textContent = '×';
            removeButton.title = 'Remove this emote';
            
            removeButton.addEventListener('click', (e) => {
                e.stopPropagation();
                row.remove();
                saveAllEmoteRows();
            });

            rowHeader.appendChild(removeButton);
            row.appendChild(rowHeader);

            // Basic fields
            const createField = (labelText, type, placeholder, value) => {
                const container = document.createElement('div');
                const label = document.createElement('label');
                label.textContent = labelText;
                const input = document.createElement('input');
                input.type = type;
                input.placeholder = placeholder;
                input.value = value;
                input.addEventListener('input', () => saveAllEmoteRows());
                container.appendChild(label);
                container.appendChild(input);
                return { container, input };
            };

            const searchField = createField('Search Name (case-insensitive):', 'text', 'e.g., itsrawww', searchName);
            const displayField = createField('Display Name (case-sensitive):', 'text', 'e.g., ITSRAWWW', data.displayName || '');
            const urlField = createField('Emote URL:', 'text', 'https://wikicdn.destiny.gg/...', data.url || '');

            row.appendChild(searchField.container);
            row.appendChild(displayField.container);
            row.appendChild(urlField.container);

            // Dimensions section
            const dimDiv = document.createElement('div');
            dimDiv.className = 'emote-dimensions';

            // Add preset selector
            const presetDiv = document.createElement('div');
            presetDiv.className = 'emote-presets';
            
            const presetLabel = document.createElement('label');
            presetLabel.textContent = 'Preset Size:';
            presetLabel.style.fontSize = '12px';
            presetLabel.style.color = '#ccc';
            presetLabel.style.display = 'block';
            presetLabel.style.marginBottom = '4px';
            
            const presetSelect = document.createElement('select');
            presetSelect.style.marginBottom = '8px';
            
            const presets = [
                { label: 'Custom Size', w: '', h: '', mt: '', t: '' },
                { label: 'Standard Emote (28x28)', w: 28, h: 28, mt: -28, t: 7 },
                { label: 'Large Emote (32x32)', w: 32, h: 32, mt: -32, t: 7 },
                { label: 'Wide Emote (52x30)', w: 52, h: 30, mt: -30, t: 7 },
                { label: 'Extra Wide (61x32)', w: 61, h: 32, mt: -32, t: 8 },
                { label: 'Small Wide (50x20)', w: 50, h: 20, mt: -20, t: 6 },
                { label: 'Medium Wide (40x30)', w: 40, h: 30, mt: -30, t: 7 },
                { label: 'Portrait (21x30)', w: 21, h: 30, mt: -30, t: 7 },
                { label: 'Medium Portrait (37x30)', w: 37, h: 30, mt: -30, t: 7.5 }
            ];

            presets.forEach(preset => {
                const option = document.createElement('option');
                option.textContent = preset.label;
                option.value = JSON.stringify(preset);
                presetSelect.appendChild(option);
            });

            presetSelect.addEventListener('change', () => {
                const preset = JSON.parse(presetSelect.value);
                widthInput.value = preset.w;
                heightInput.value = preset.h;
                marginTopInput.value = preset.mt;
                topInput.value = preset.t;
                saveAllEmoteRows();
            });

            presetDiv.appendChild(presetLabel);
            presetDiv.appendChild(presetSelect);

            // Size inputs
            const dimensionsLabel = document.createElement('label');
            dimensionsLabel.textContent = 'Custom Size:';
            dimensionsLabel.style.display = 'block';
            dimensionsLabel.style.marginBottom = '4px';

            const widthInput = document.createElement('input');
            widthInput.type = 'number';
            widthInput.placeholder = 'Width';
            widthInput.value = data.width || '';
            widthInput.addEventListener('input', () => saveAllEmoteRows());

            const heightInput = document.createElement('input');
            heightInput.type = 'number';
            heightInput.placeholder = 'Height';
            heightInput.value = data.height || '';
            heightInput.addEventListener('input', () => saveAllEmoteRows());

            // Position adjustments
            const positionLabel = document.createElement('label');
            positionLabel.textContent = 'Position Adjustment:';
            positionLabel.style.display = 'block';
            positionLabel.style.marginTop = '8px';
            positionLabel.style.marginBottom = '4px';

            const posDiv = document.createElement('div');
            posDiv.className = 'emote-position';

            const marginTopInput = document.createElement('input');
            marginTopInput.type = 'number';
            marginTopInput.placeholder = 'e.g., -28';
            marginTopInput.value = data.marginTop || '';
            marginTopInput.title = 'Usually negative height (e.g., -28 for 28px height)';
            marginTopInput.addEventListener('input', () => saveAllEmoteRows());

            const topInput = document.createElement('input');
            topInput.type = 'number';
            topInput.placeholder = 'e.g., 7';
            topInput.value = data.top || '';
            topInput.title = 'Usually height/4 (e.g., 7 for 28px height)';
            topInput.addEventListener('input', () => saveAllEmoteRows());

            const createInputGroup = (labelText, input) => {
                const group = document.createElement('div');
                group.style.marginBottom = '8px';

                const label = document.createElement('label');
                label.textContent = labelText;
                label.style.display = 'block';
                label.style.fontSize = '11px';
                label.style.color = '#aaa';
                label.style.marginBottom = '2px';

                group.appendChild(label);
                group.appendChild(input);
                return group;
            };

            const marginTopGroup = createInputGroup('Margin Top:', marginTopInput);
            const topGroup = createInputGroup('Top Offset:', topInput);

            posDiv.appendChild(marginTopGroup);
            posDiv.appendChild(topGroup);

            // Create Advanced Settings section
            const advancedSection = document.createElement('div');
            advancedSection.className = 'emote-advanced-settings';

            const toggleBtn = createAdvancedToggle(advancedSection);

            const advancedLabel = document.createElement('label');
            advancedLabel.textContent = 'Advanced Properties:';
            advancedLabel.style.display = 'block';
            advancedLabel.style.marginBottom = '8px';
            advancedLabel.style.fontWeight = 'bold';

            const createInput = (label, placeholder, tooltip = '') => {
                const container = document.createElement('div');
                container.className = 'emote-property';
                container.style.marginBottom = '8px';

                const inputLabel = document.createElement('label');
                inputLabel.textContent = label;
                inputLabel.style.display = 'block';
                inputLabel.style.fontSize = '11px';
                inputLabel.style.marginBottom = '2px';
                inputLabel.style.color = '#aaa';

                const input = document.createElement('input');
                input.type = 'text';
                input.placeholder = placeholder;
                input.style.width = '100%';
                input.title = tooltip;
                input.addEventListener('input', () => saveAllEmoteRows());

                container.appendChild(inputLabel);
                container.appendChild(input);
                return container;
            };

            // Basic dimensions and position
            dimDiv.appendChild(presetDiv);
            dimDiv.appendChild(dimensionsLabel);
            dimDiv.appendChild(widthInput);
            dimDiv.appendChild(heightInput);
            dimDiv.appendChild(positionLabel);
            dimDiv.appendChild(posDiv);
            posDiv.appendChild(marginTopInput);
            posDiv.appendChild(topInput);

            // Advanced properties
            const advancedProps = [
                {
                    label: 'Margin Left',
                    placeholder: 'e.g., -2px',
                    tooltip: 'Left margin (can be negative)'
                },
                {
                    label: 'Margin Right',
                    placeholder: 'e.g., -4px',
                    tooltip: 'Right margin (can be negative)'
                },
                {
                    label: 'Transform',
                    placeholder: 'e.g., scaleX(-1)',
                    tooltip: 'CSS transform property (scale, rotate, etc)'
                },
                {
                    label: 'Filter',
                    placeholder: 'e.g., contrast(1) brightness(1.25)',
                    tooltip: 'CSS filters like contrast, brightness, etc'
                },
                {
                    label: 'Background Position',
                    placeholder: 'e.g., 0px or -32px',
                    tooltip: 'For sprite sheets, position of the image'
                },
                {
                    label: 'Background Size',
                    placeholder: 'e.g., 5544px 34px',
                    tooltip: 'Size of background image (for sprite sheets)'
                },
                {
                    label: 'Background Repeat',
                    placeholder: 'e.g., no-repeat',
                    tooltip: 'How the background image repeats'
                },
                {
                    label: 'Transition',
                    placeholder: 'e.g., all 0.2s',
                    tooltip: 'Transition effects for hover states'
                }
            ];


            
            advancedSection.appendChild(advancedLabel);
            advancedProps.forEach(prop => {
                advancedSection.appendChild(createInput(prop.label, prop.placeholder, prop.tooltip));
            });

            // Animation Keyframes section
            const keyframesLabel = document.createElement('label');
            keyframesLabel.textContent = 'Animation Keyframes:';
            keyframesLabel.style.display = 'block';
            keyframesLabel.style.marginBottom = '4px';
            keyframesLabel.style.marginTop = '12px';

            const keyframesTip = document.createElement('div');
            keyframesTip.textContent = 'Define @keyframes animation. For hover effects, create two animations (e.g., myEmote-anim and myEmote-hover)';
            keyframesTip.style.fontSize = '11px';
            keyframesTip.style.color = '#888';
            keyframesTip.style.marginBottom = '8px';

            const animationInput = document.createElement('textarea');
            animationInput.placeholder = `@keyframes myEmote-anim {
  0% { transform: scale(1); }
  50% { transform: scale(1.2); }
  100% { transform: scale(1); }
}

@keyframes myEmote-hover {
  0% { filter: brightness(1); }
  100% { filter: brightness(1.5); }
}`;
            animationInput.style.height = '150px';
            animationInput.style.fontFamily = 'monospace';
            animationInput.value = data.animation || '';
            animationInput.addEventListener('input', () => saveAllEmoteRows());

            // Create single advanced toggle button
            const advSettingsToggle = document.createElement('button');
            const advSettingsBtn = createAdvancedToggle(advancedSection);

            dimDiv.appendChild(advSettingsBtn);
            dimDiv.appendChild(advancedSection);
            advancedSection.appendChild(keyframesLabel);
            advancedSection.appendChild(keyframesTip);
            advancedSection.appendChild(animationInput);

            const animLabel = document.createElement('label');
            animLabel.textContent = 'CSS Animation Keyframes (optional):';
            const animTextarea = document.createElement('textarea');
            animTextarea.placeholder = '@keyframes example-anim { ... }';
            animTextarea.value = data.animation || '';
            animTextarea.addEventListener('input', () => saveAllEmoteRows());

            // Create basic field labels
            const searchLabel = document.createElement('label');
            searchLabel.textContent = 'Search Name (case-insensitive):';
            searchLabel.className = 'label-text';

            const displayLabel = document.createElement('label');
            displayLabel.textContent = 'Display Name (case-sensitive):';
            displayLabel.className = 'label-text';

            const urlLabel = document.createElement('label');
            urlLabel.textContent = 'Emote URL:';
            urlLabel.className = 'label-text';

            const dimLabel = document.createElement('label');
            dimLabel.textContent = 'Dimensions:';
            dimLabel.className = 'label-text';

            // Create input fields
            const searchInput = document.createElement('input');
            searchInput.type = 'text';
            searchInput.placeholder = 'e.g., itsrawww';
            searchInput.value = searchName;

            const displayInput = document.createElement('input');
            displayInput.type = 'text';
            displayInput.placeholder = 'e.g., ITSRAWWW';
            displayInput.value = data.displayName || '';

            const urlInput = document.createElement('input');
            urlInput.type = 'text';
            urlInput.placeholder = 'https://wikicdn.destiny.gg/...';
            urlInput.value = data.url || '';

            // Add event listeners
            searchInput.addEventListener('input', () => saveAllEmoteRows());
            displayInput.addEventListener('input', () => saveAllEmoteRows());
            urlInput.addEventListener('input', () => saveAllEmoteRows());

            // Append elements in order
            row.appendChild(searchLabel);
            row.appendChild(searchInput);
            row.appendChild(displayLabel);
            row.appendChild(displayInput);
            row.appendChild(urlLabel);
            row.appendChild(urlInput);
            row.appendChild(dimLabel);
            row.appendChild(dimDiv);
            row.appendChild(animLabel);
            row.appendChild(animTextarea);

            return row;
        }

        function addEmoteRow(searchName = '', data = {}) {
            const row = createEmoteRow(searchName, data);
            emoteContainer.appendChild(row);
        }

        function saveAllEmoteRows() {
            const rows = emoteContainer.querySelectorAll('.emote-row');
            const newEmotes = {};

            rows.forEach(row => {
                const inputs = row.querySelectorAll('input');
                const searchName = inputs[0].value.trim().toLowerCase();
                const displayName = inputs[1].value.trim();
                const url = inputs[2].value.trim();
                const width = parseInt(inputs[3].value) || 0;
                const height = parseInt(inputs[4].value) || 0;
                const marginTop = parseInt(inputs[5].value) || -height; // Default to -height if not set
                const top = parseInt(inputs[6].value) || Math.floor(height/4); // Default to height/4 if not set
                const animation = row.querySelector('textarea').value.trim();

                if (searchName && displayName && url && width && height) {
                    const emoteData = {
                        displayName,
                        url,
                        width,
                        height,
                        marginTop,
                        top
                    };

                    // Add optional properties if they have values
                    const optionalProps = {
                        marginLeft: inputs[7]?.value || null,
                        marginRight: inputs[8]?.value || null,
                        transform: inputs[9]?.value || null,
                        filter: inputs[10]?.value || null,
                        backgroundPosition: inputs[11]?.value || null,
                        backgroundSize: inputs[12]?.value || null,
                        backgroundRepeat: inputs[13]?.value || null,
                        transition: inputs[14]?.value || null,
                        animation: animation || null
                    };

                    // Only include properties that have values
                    Object.entries(optionalProps).forEach(([key, value]) => {
                        if (value) emoteData[key] = value;
                    });

                    newEmotes[searchName] = emoteData;
                }
            });

            customEmotes = newEmotes;
            GM_setValue('customEmotes', JSON.stringify(customEmotes));
            applyCustomEmoteStyles();
            processExistingMessagesForEmotes();
        }

        const rebuildEmoteRows = () => {
            emoteContainer.innerHTML = '';
            for (const [searchName, data] of Object.entries(customEmotes)) {
                addEmoteRow(searchName, data);
            }
        };

        rebuildEmoteRows();

        emoteSection.appendChild(emoteLabel);
        emoteSection.appendChild(emoteContainer);
        emoteSection.appendChild(addEmoteButton);

        exportImportDiv.appendChild(focusCheckboxWrapper);
        exportImportDiv.appendChild(exportButton);
        exportImportDiv.appendChild(importButton);

        content.appendChild(exportImportDiv);
        content.appendChild(label1);
        content.appendChild(textarea1);
        content.appendChild(label2);
        content.appendChild(textarea2);
        content.appendChild(label3);
        content.appendChild(textarea3);
        content.appendChild(label4);
        content.appendChild(flairContainer);
        content.appendChild(addButton);
        content.appendChild(emoteSection);
        content.appendChild(removalSection);

        dropdownContainer.appendChild(button);
        dropdownContainer.appendChild(content);

        button.addEventListener('click', (e) => {
            e.stopPropagation();
            const isDisplayed = content.style.display === 'block';
            document.querySelectorAll('.class-settings-content, .class-skip-content').forEach(p => {
                if (p.style.display === 'block') {
                    p.style.display = 'none';
                }
            });
            content.style.display = isDisplayed ? 'none' : 'block';
        });

        document.addEventListener('click', (e) => {
            if (!dropdownContainer.contains(e.target)) {
                content.style.display = 'none';
            }
        });

        const existingSettings = settingsContainer.querySelector('.settings-dropdown');
        if (existingSettings) {
            existingSettings.parentElement.insertBefore(dropdownContainer, existingSettings);
        } else {
            settingsContainer.appendChild(dropdownContainer);
        }
    }

    // ========================================
    // FLAIR BLACKLIST (REMOVE FLAIRS FROM CHAT)
    // ========================================

    function stripFlairsFromMessage(msg) {
        const username = msg.getAttribute('data-username');
        if (!username || username === 'null') return;

        const lowerUsername = username.toLowerCase();
        if (!flairBlacklist[lowerUsername]) return;

        const flairsToRemove = flairBlacklist[lowerUsername];
        const featuresSpan = msg.querySelector('.features');
        const userSpan = msg.querySelector('span.user');

        if (featuresSpan) {
            flairsToRemove.forEach(flairClass => {
                const flairIcon = featuresSpan.querySelector(`i.flair.${flairClass}`);
                if (flairIcon) {
                    flairIcon.remove();
                }
            });
        }

        if (userSpan) {
            flairsToRemove.forEach(flairClass => {
                if (userSpan.classList.contains(flairClass)) {
                    userSpan.classList.remove(flairClass);
                }
            });
        }
    }

    function processExistingMessagesForFlairRemoval() {
        const messages = document.querySelectorAll('#chat-stream .msg-chat');
        messages.forEach(stripFlairsFromMessage);
    }

    // ========================================
    // CUSTOM FLAIRS
    // ========================================

    const customFlairs = {
        flair4:  "https://wikicdn.destiny.gg/8/87/Flair_4.png",
        flair5:  "https://wikicdn.destiny.gg/5/5e/Flair_5.png",
        flair10: "https://wikicdn.destiny.gg/6/61/Flair_starcraft_2.png",
        flair16: "https://wikicdn.destiny.gg/d/df/Flair_emote_contributor.png",
        flair25: "https://cdn.destiny.gg/flairs/5f28cdec6ed24.png",
        flair27: "https://cdn.destiny.gg/flairs/60b130f07c0c4.png",
        flair30: "https://wikicdn.destiny.gg/c/ca/Flair_league_master.png"
    };

    const defaultFlairDefinitions = {
        'flair4': 'Trusted',
        'flair5': 'Contributor',
        'flair10': 'Starcraft Player',
        'flair16': 'Emote Contributor',
        'flair25': 'League Champion',
        'flair27': 'Premium Subscriber',
        'flair30': 'League Master'
    };

    // UPDATED: Flair color mappings from flairs.css
    // This ensures username colors are applied correctly
    const flairColors = {
        'flair1': '#2ADDC8',      // T2 Sub
        'flair3': '#4DB524',      // T3 Sub
        'flair7': '#FC4C02',
        'flair8': '#DD29D2',      // T4 Sub
        'flair9': '#5900ff',      // Twitch Sub
        'flair11': '#929292',
        'flair12': '#E79015',
        'flair13': '#59AEEA',     // T1 Sub
        'flair17': '#FCE205',
        'flair18': '#f082bb',
        'flair22': '#2ADDC8',     // T2 AE Sub
        'flair24': '#4DB524',     // T3 AE Sub
        'flair26': '#DD29D2',     // T4 AE Sub
        'flair32': '#59AEEA',     // T1 AE Sub
        'bot': '#0088CC',
        'admin': '#EE1F1F',
        'moderator': '#DB4C1C',
        'subscriber': '#59AEEA',
        'vip': '#ffbb00'
    };

        function applyCustomFlairs() {
        let styleOverrides = '';

        // Base rainbow gradient definition
        styleOverrides += `
            :root {
                --rainbow-saturation: 100%;
                --rainbow-lightness: 65%;
                --rainbow-gradient: repeating-linear-gradient(
                    90deg,
                    hsl(0, var(--rainbow-saturation), var(--rainbow-lightness)),
                    hsl(45, var(--rainbow-saturation), var(--rainbow-lightness)),
                    hsl(90, var(--rainbow-saturation), var(--rainbow-lightness)),
                    hsl(135, var(--rainbow-saturation), var(--rainbow-lightness)),
                    hsl(180, var(--rainbow-saturation), var(--rainbow-lightness)),
                    hsl(225, var(--rainbow-saturation), var(--rainbow-lightness)),
                    hsl(270, var(--rainbow-saturation), var(--rainbow-lightness)),
                    hsl(315, var(--rainbow-saturation), var(--rainbow-lightness)),
                    hsl(360, var(--rainbow-saturation), var(--rainbow-lightness))
                    50%
                );
            }

            @keyframes move {
                to {
                    background-position-x: -100%;
                }
            }
        `;        // Apply custom flair styles first
        for (const [flair, url] of Object.entries(customFlairs)) {
            styleOverrides += `
                i.flair.${flair},
                i.flair.${flair}.${flair} {
                    display: inline-block !important;
                    background-image: url("${url}") !important;
                    background-size: 16px 16px !important;
                    background-repeat: no-repeat !important;
                    background-position: center !important;
                    width: 16px !important;
                    height: 16px !important;
                    margin: 0 1px !important;
                    vertical-align: middle !important;
                }
            `;
        }

        // Order flairs by destiny.gg's ordering (lower numbers appear first)
        const flairPriority = {
            'admin': 1,          // Admin (highest priority)
            'moderator': 1,      // Moderator
            'flair18': 2,        // Emote Manager
            'flair12': 3,        // Broadcaster
            'flair20': 3,        // Verified
            'flair7': 3,         // NFL Chatter
            'flair33': 2,        // T5 AE Sub (higher priority)
            'flair42': 2,        // T5 Sub (higher priority)
            'flair26': 4,        // T4 AE Sub
            'flair8': 4,         // T4 Sub
            'flair24': 5,        // T3 AE Sub
            'flair3': 5,         // T3 Sub
            'flair22': 6,        // T2 AE Sub
            'flair1': 6,         // T2 Sub
            'flair32': 7,        // T1 AE Sub
            'flair13': 7,        // T1 Sub
            'subscriber': 8,      // Regular subscriber
            'flair9': 8,         // Twitch subscriber
            'bot': 10,           // Bot
            'flair11': 11,       // Old Bot
            'flair31': 50,       // Developer
            'flair23': 50,       // Doctor
            'flair28': 50,       // Lawyer
            'flair34': 50,       // Conductor
            'flair29': 50,       // Weight Lifting
            'flair58': 50,       // New User
            'flair2': 127,       // Notable Chatter
            'flair15': 127,      // Birthday
            'flair17': 127,      // Extra Notable Chatter
            'protected': 127     // Protected
        };

        // Sort flairs by priority before creating styles
        const sortedFlairs = Object.entries(flairColors)
            .sort(([a], [b]) => (flairPriority[b] || 0) - (flairPriority[a] || 0));

        // Create style rules in priority order
        for (const [flair, color] of sortedFlairs) {
            const priority = flairPriority[flair] || 0;
            styleOverrides += `
                .user.${flair} {
                    color: ${color} !important;
                    --flair-priority: ${priority};
                }
                i.flair.${flair} {
                    z-index: ${100 - priority} !important;
                    position: relative !important;
                }
            `;
        }

        // Special handling for rainbow flairs
        styleOverrides += `
            .flair.flair33,
            .flair.flair42 {
                background-image: url("https://cdn.destiny.gg/flairs/66465c372e9c9.png");
                height: 19px;
                width: 18px;
                order: 3;
            }
            .user.flair33,
            .user.flair42 {
                color: transparent;
                background: var(--rainbow-gradient);
                background-clip: text;
                background-size: 200% 100%;
                -webkit-background-clip: text;
                animation: move 3s linear infinite;
                position: relative;
                order: 3;
            }
        `;

        GM_addStyle(styleOverrides);
    }

    function injectFlair34(msg) {
        const username = msg.getAttribute('data-username');
        if (!username || username === 'null') return;

        if (!flair34Users.includes(username.toLowerCase())) return;

        const featuresSpan = msg.querySelector('.features');
        if (!featuresSpan) return;

        if (featuresSpan.querySelector('i.flair.flair34')) return;
        const flair34 = document.createElement('i');
        flair34.setAttribute('data-flair', 'flair34');
        flair34.setAttribute('data-injected', 'true');
        flair34.className = 'flair flair34';
        flair34.title = 'Conductor';
        featuresSpan.appendChild(flair34);
    }

    function injectVIP(msg) {
        const username = msg.getAttribute('data-username');
        if (!username || username === 'null' || !vipUsers.includes(username.toLowerCase())) return;

        const userSpan = msg.querySelector('span.user');
        if (userSpan && !userSpan.classList.contains('vip')) {
            userSpan.classList.add('vip');
        }
    }

    function injectCustomFlair(msg) {
        const username = msg.getAttribute('data-username');
        // Skip processing for combo messages or invalid usernames
        if (!username || username === 'null') return;

        const lowerUsername = username.toLowerCase();
        
        // Find or create features span
        let featuresSpan = msg.querySelector('.features');
        if (!featuresSpan) {
            featuresSpan = document.createElement('span');
            featuresSpan.className = 'features';
            // Insert after time-seconds
            const timeSeconds = msg.querySelector('.time-seconds');
            if (timeSeconds && timeSeconds.nextSibling) {
                msg.insertBefore(featuresSpan, timeSeconds.nextSibling);
            }
        }

        // Find or create user span
        let userSpan = msg.querySelector('.user');
        if (!userSpan) {
            userSpan = document.createElement('span');
            userSpan.className = 'user';
            userSpan.setAttribute('onclick', 'document._addFocusRule("' + username + '")');
            // Add after features span
            if (featuresSpan.nextSibling) {
                msg.insertBefore(userSpan, featuresSpan.nextSibling);
            } else {
                msg.appendChild(userSpan);
            }
        }

        // Store existing flairs before clearing
        const existingFlairs = [];
        featuresSpan.querySelectorAll('i.flair').forEach(flair => {
            const classes = Array.from(flair.classList);
            const flairClass = classes.find(cls => cls !== 'flair');
            if (flairClass) existingFlairs.push(flairClass);
        });

        // Store existing user classes
        const existingUserClasses = Array.from(userSpan.classList)
            .filter(cls => cls.startsWith('flair') || ['admin', 'moderator', 'subscriber', 'protected', 'bot', 'vip'].includes(cls));

        // Clear existing flairs
        featuresSpan.innerHTML = '';

        // Always add user-[username] class and preserve username
        const currentText = userSpan.textContent;
        userSpan.className = `user-${username} user`;
        userSpan.textContent = currentText || username;

        // Function to add a flair
        const addFlair = (flairClass) => {
            // Add flair icon if it doesn't exist
            if (!featuresSpan.querySelector(`i.flair.${flairClass}`)) {
                const flairElement = document.createElement('i');
                flairElement.className = `flair ${flairClass}`;
                flairElement.title = flairDefinitions[flairClass] || flairClass;
                featuresSpan.appendChild(flairElement);
            }

            // Add class to username if not present
            if (!userSpan.classList.contains(flairClass)) {
                userSpan.classList.add(flairClass);
            }
        };

        // First, restore existing flairs
        existingFlairs.forEach(flairClass => {
            addFlair(flairClass);
        });

        // Restore existing user classes
        existingUserClasses.forEach(className => {
            if (!userSpan.classList.contains(className)) {
                userSpan.classList.add(className);
            }
        });

        // Then handle custom flairs
        for (const [flairKey, usersList] of Object.entries(selectedFlairUsers)) {
            if (Array.isArray(usersList) && usersList.includes(lowerUsername)) {
                addFlair(flairKey);
            }
        }
    }

    function processExistingMessagesForFlairs() {
        const messages = document.querySelectorAll('#chat-stream .msg-chat');
        messages.forEach(msg => {
            // Don't clear existing flairs, just apply custom ones
            injectCustomFlair(msg);
        });
    }

    // ========================================
    // FOCUS ENHANCEMENT - INCLUDE MENTIONS
    // ========================================

    function updateFocusRules() {
        // Remove any existing scripts first
        const existingScript = document.getElementById('focus-override-script');
        if (existingScript) {
            existingScript.remove();
        }
        
        // Add the base styles once
        GM_addStyle(`
            #chat-stream.hide-on-focus .msg-chat {
                opacity: 0.3 !important;
            }
            #chat-stream.hide-on-focus .msg-chat[data-focused="true"],
            #chat-stream.hide-on-focus .msg-chat[data-mentioned="true"] {
                opacity: 1 !important;
                position: relative !important;
                z-index: 1 !important;
            }
        `);

        // Create and add our focus handling script
        const focusScript = document.createElement('script');
        focusScript.id = 'focus-override-script';
        focusScript.textContent = `
            document.addEventListener('click', function(event) {
                const target = event.target;
                
                // Handle user click
                if (target.classList.contains('user')) {
                    const username = target.textContent;
                    console.log('User clicked:', username);
                    
                    // Clear previous focus
                    document.querySelectorAll('#chat-stream .msg-chat[data-focused="true"], #chat-stream .msg-chat[data-mentioned="true"]')
                        .forEach(msg => {
                            msg.removeAttribute('data-focused');
                            msg.removeAttribute('data-mentioned');
                        });

                    // Focus user's messages
                    document.querySelectorAll(\`#chat-stream .msg-chat[data-username="\${username}"]\`)
                        .forEach(msg => {
                            msg.setAttribute('data-focused', 'true');
                            console.log('Focused message:', msg.textContent);
                        });

                    // Check for mentions if enabled
                    const includeMentions = document.getElementById('includeMentionsCheckbox')?.checked;
                    console.log('Include mentions:', includeMentions);
                    
                    if (includeMentions) {
                        document.querySelectorAll('#chat-stream .msg-chat')
                            .forEach(msg => {
                                if (msg.getAttribute('data-username') !== username) {
                                    // Get all the text content including emote titles
                                    const messageSpan = msg.querySelector('.message');
                                    if (messageSpan) {
                                        let textContent = '';
                                        messageSpan.childNodes.forEach(node => {
                                            if (node.nodeType === 3) { // Text node
                                                textContent += node.textContent + ' ';
                                            } else if (node.nodeType === 1 && node.classList.contains('emote')) {
                                                // Add emote title/alt text if available
                                                textContent += (node.title || node.alt || '') + ' ';
                                            }
                                        });
                                        textContent = textContent.trim();
                                        console.log('Checking message text:', textContent);
                                        
                                        if (textContent.toLowerCase().includes(username.toLowerCase())) {
                                            msg.setAttribute('data-mentioned', 'true');
                                            console.log('Found mention in:', textContent);
                                        }
                                    }
                                }
                            });
                    }

                    // Add hide-on-focus class to chat stream
                    document.getElementById('chat-stream').classList.add('hide-on-focus');
                    event.stopPropagation();
                } 
                // Handle message click (remove focus)
                else if (target.classList.contains('message')) {
                    document.querySelectorAll('#chat-stream .msg-chat[data-focused="true"], #chat-stream .msg-chat[data-mentioned="true"]')
                        .forEach(msg => {
                            msg.removeAttribute('data-focused');
                            msg.removeAttribute('data-mentioned');
                        });
                    document.getElementById('chat-stream').classList.remove('hide-on-focus');
                }
            }, true);
        `;
        document.head.appendChild(focusScript);
    }

    function setupMentionObserver() {
        const chatContainer = document.querySelector('#chat-stream');
        if (!chatContainer) return;

        const mentionObserver = new MutationObserver((mutations) => {
            const script = document.createElement('script');
            script.textContent = `
                (function() {
                    if (window.focused) {
                        // Check the checkbox state dynamically
                        const checkbox = document.getElementById('includeMentionsCheckbox');
                        if (!checkbox || !checkbox.checked) return;
                        
                        const username = window.focused;
                        const messages = document.querySelectorAll('#chat-stream .msg-chat:not(.mention-focused):not([data-username="' + username + '"])');
                        messages.forEach(msg => {
                            const messageSpan = msg.querySelector('.message');
                            if (messageSpan) {
                                const text = messageSpan.innerText || messageSpan.textContent || '';
                                const regex = new RegExp('\\\\b' + username + '\\\\b', 'i');
                                if (regex.test(text)) {
                                    msg.classList.add('mention-focused');
                                    console.log('Added mention-focused to new message from', msg.getAttribute('data-username'), ':', text.substring(0, 50));
                                }
                            }
                        });
                    }
                })();
            `;
            document.head.appendChild(script);
            script.remove();
        });

        mentionObserver.observe(chatContainer, { childList: true });
    }

    // ========================================
    // UNIFIED OBSERVER
    // ========================================

    function setupUnifiedObserver() {
        const chatContainer = document.querySelector('#chat-stream');
        if (!chatContainer) {
            console.warn('Vyneer.me chat container (#chat-stream) not found, retrying...');
            setTimeout(setupUnifiedObserver, 1000);
            return;
        }

        const observer = new MutationObserver((mutations) => {
            for (const mutation of mutations) {
                if (mutation.type === 'childList') {
                    for (const addedNode of mutation.addedNodes) {
                        if (addedNode.nodeType === 1 && addedNode.classList?.contains('msg-chat')) {
                            // Process flairs first
                            injectCustomFlair(addedNode);

                            // Then process emotes
                            const messageSpan = addedNode.querySelector('.message');
                            if (messageSpan) {
                                processMessageSpanForEmotes(messageSpan);

                                // Check for mentions if a user is focused
                                if (chatContainer.classList.contains('hide-on-focus')) {
                                    const focusedMsg = document.querySelector('#chat-stream .msg-chat[data-focused="true"]');
                                    const focusedUsername = focusedMsg?.getAttribute('data-username');

                                    if (focusedUsername && document.getElementById('includeMentionsCheckbox')?.checked) {
                                        if (addedNode.getAttribute('data-username') !== focusedUsername) {
                                            let textContent = '';
                                            messageSpan.childNodes.forEach(node => {
                                                if (node.nodeType === 3) { // Text node
                                                    textContent += node.textContent + ' ';
                                                } else if (node.nodeType === 1 && node.classList.contains('emote')) {
                                                    textContent += (node.title || node.alt || '') + ' ';
                                                }
                                            });
                                            textContent = textContent.trim();
                                            
                                            if (textContent.toLowerCase().includes(focusedUsername.toLowerCase())) {
                                                addedNode.setAttribute('data-mentioned', 'true');
                                            }
                                        }
                                    }
                                }
                            }

                            // Process other features
                            processMessageForHighlight(addedNode);
                            stripFlairsFromMessage(addedNode);
                            injectFlair34(addedNode);
                            injectVIP(addedNode);
                        }
                    }
                }
            }
        });

        observer.observe(chatContainer, { childList: true });
    }

    // ========================================
    // INITIALIZATION
    // ========================================

    function init() {
        console.log('=== Vyneer.me Enhancement Suite Initializing ===');

        applyCustomFlairs();

        loadKeywords();
        loadFlair34Users();
        loadVipUsers();
        loadSelectedFlairUsers();
        loadFlairBlacklist();
        loadCustomEmotes();
        loadIncludeMentionsInFocus();

        setTimeout(() => {
            console.log('Processing existing messages...');
            applyCustomEmoteStyles();
            processExistingMessagesForEmotes();
            processExistingMessagesForHighlight();
            processExistingMessagesForFlairRemoval();
            processExistingMessagesForFlairs();
            setupUnifiedObserver();
            setupMentionObserver();
            updateFocusRules();
            setupSettingsUI();
        }, 1000);
    }

    if (document.readyState === 'complete') {
        setTimeout(init, 500);
    } else {
        window.addEventListener('load', function() {
            setTimeout(init, 500);
        });
    }

})();