(() => {
  // --- Font Size State & Observer ---
  let activeFontSize = parseFloat(window.getComputedStyle(document.body).fontSize) || 16;
  let maskActive = false;
  let magnifierActive = false;
  let magnificationLevel = 1.5;
  let savedRange = null;
  let savedSelectedText = "";
  let selectionSaveTimer = null;
  let activeHighlightColor = "#FFFF00";
  let activeTtsSpeed = 1;
  let activeTtsVoiceName = "";
  const MIN_TTS_SPEED = 0.5;
  const MAX_CLEAR_TTS_SPEED = 1.5;
  const ESTIMATED_TTS_CHARS_PER_SECOND = 13;
  const ttsSession = {
    text: "",
    utterance: null,
    charIndex: 0,
    progressStartedAt: 0,
    progressStartedIndex: 0,
    progressRate: 1,
    isSpeaking: false,
    isRestarting: false,
    restartId: 0
  };

  const COMPLEX_WORD_CLASS = "dyslexia-complex-word-highlight";
  const MAGNIFIER_SOURCE_SELECTOR = [
    "p",
    "li",
    "blockquote",
    "td",
    "th",
    "label",
    "h1",
    "h2",
    "h3",
    "h4",
    "h5",
    "h6",
    "figcaption",
    "dd",
    "dt"
  ].join(", ");
  const MAGNIFIER_MEDIA_SELECTOR = [
    "img",
    "picture",
    "svg",
    "canvas",
    "video"
  ].join(", ");
  const MAGNIFIER_FALLBACK_SELECTOR = [
    "article",
    "section",
    "main",
    "div"
  ].join(", ");
  const MAGNIFIER_EXCLUDED_SELECTOR = [
    "header",
    "nav",
    "footer",
    "aside",
    "menu",
    "[role='navigation']",
    "[role='banner']",
    "[role='contentinfo']",
    "[role='menu']",
    "[role='menubar']",
    ".navbar",
    ".nav",
    ".navigation",
    ".menu",
    ".header",
    ".site-header",
    ".page-header",
    ".footer",
    ".site-footer",
    ".toolbar",
    ".topbar",
    ".sidebar",
    ".breadcrumb",
    ".pagination",
    ".tabs",
    ".search",
    ".search-bar",
    ".searchbox",
    "form",
    "button",
    "select",
    "option",
    "iframe"
  ].join(", ");

  function warnContentScript(context, error) {
    console.warn(`[Dyslexia Helper] ${context}`, error);
  }

  function getErrorMessage(error) {
    return error instanceof Error ? error.message : String(error);
  }

  function normalizeHighlightColor(color) {
    return typeof color === "string" && color.trim() ? color.trim() : "#FFFF00";
  }

  function setActiveHighlightColor(color) {
    activeHighlightColor = normalizeHighlightColor(color);
  }

  function normalizeTtsSpeed(value) {
    const parsed = Number(value);
    if (!Number.isFinite(parsed)) return 1;
    return Math.min(MAX_CLEAR_TTS_SPEED, Math.max(MIN_TTS_SPEED, parsed));
  }

  function getTtsResumeIndex(text, index) {
    const clampedIndex = Math.min(Math.max(0, index), text.length);
    const previousText = text.slice(0, clampedIndex);
    const previousWordBoundary = Math.max(
      previousText.lastIndexOf(" "),
      previousText.lastIndexOf("\n"),
      previousText.lastIndexOf("\t")
    );
    const candidateIndex = previousWordBoundary >= 0 ? previousWordBoundary + 1 : 0;
    const nextReadableCharacter = text.slice(candidateIndex).search(/\S/);
    return nextReadableCharacter === -1 ? text.length : candidateIndex + nextReadableCharacter;
  }

  function estimateTtsProgressIndex(text, startIndex, startedAt, rate) {
    if (!startedAt) return startIndex;
    const elapsedSeconds = Math.max(0, (Date.now() - startedAt) / 1000);
    const estimatedProgress = Math.floor(elapsedSeconds * ESTIMATED_TTS_CHARS_PER_SECOND * normalizeTtsSpeed(rate));
    return Math.min(text.length, Math.max(startIndex, startIndex + estimatedProgress));
  }

  function normalizeTtsVoiceName(value) {
    return typeof value === "string" ? value.trim() : "";
  }

  function getActiveSpeechVoice() {
    const voices = window.speechSynthesis.getVoices();
    return (
      voices.find((voice) => voice.name === activeTtsVoiceName) ||
      voices.find((voice) => voice.lang === "en-US") ||
      voices[0]
    );
  }

  function applyActiveVoiceToUtterance(utterance) {
    const voice = getActiveSpeechVoice();
    if (voice) {
      utterance.voice = voice;
      utterance.lang = voice.lang || "en-US";
      return;
    }

    utterance.lang = "en-US";
  }

  function startContentSpeech(text, startIndex) {
    const resumeIndex = getTtsResumeIndex(text, startIndex);
    const remainingText = text.slice(resumeIndex);

    if (!remainingText.trim()) {
      ttsSession.text = "";
      ttsSession.utterance = null;
      ttsSession.charIndex = 0;
      ttsSession.progressStartedAt = 0;
      ttsSession.progressStartedIndex = 0;
      ttsSession.progressRate = 1;
      ttsSession.isSpeaking = false;
      ttsSession.isRestarting = false;
      return;
    }

    const utterance = new SpeechSynthesisUtterance(remainingText);
    const normalizedRate = normalizeTtsSpeed(activeTtsSpeed);
    applyActiveVoiceToUtterance(utterance);
    utterance.rate = normalizedRate;
    utterance.pitch = 1;
    utterance.volume = 1;

    ttsSession.text = text;
    ttsSession.utterance = utterance;
    ttsSession.charIndex = resumeIndex;
    ttsSession.progressStartedAt = Date.now();
    ttsSession.progressStartedIndex = resumeIndex;
    ttsSession.progressRate = normalizedRate;
    ttsSession.isSpeaking = true;
    ttsSession.isRestarting = false;

    utterance.onboundary = (event) => {
      if (ttsSession.utterance !== utterance) return;
      if (typeof event.charIndex !== "number") return;
      ttsSession.charIndex = Math.min(text.length, resumeIndex + event.charIndex);
      ttsSession.progressStartedAt = Date.now();
      ttsSession.progressStartedIndex = ttsSession.charIndex;
    };

    utterance.onend = () => {
      if (ttsSession.utterance !== utterance) return;
      ttsSession.text = "";
      ttsSession.utterance = null;
      ttsSession.charIndex = 0;
      ttsSession.progressStartedAt = 0;
      ttsSession.progressStartedIndex = 0;
      ttsSession.progressRate = 1;
      ttsSession.isSpeaking = false;
      ttsSession.isRestarting = false;
    };

    utterance.onerror = () => {
      if (ttsSession.utterance !== utterance) return;
      ttsSession.utterance = null;
      ttsSession.isSpeaking = false;
      ttsSession.progressStartedAt = 0;
      ttsSession.progressStartedIndex = 0;
      ttsSession.progressRate = 1;
      ttsSession.isRestarting = false;
    };

    window.speechSynthesis.speak(utterance);
  }

  function restartActiveContentSpeech() {
    if (!ttsSession.isSpeaking || !ttsSession.text) return;

    const text = ttsSession.text;
    const estimatedIndex = estimateTtsProgressIndex(
      text,
      ttsSession.progressStartedIndex || ttsSession.charIndex,
      ttsSession.progressStartedAt,
      ttsSession.progressRate
    );
    const resumeIndex = getTtsResumeIndex(text, Math.max(ttsSession.charIndex, estimatedIndex));
    const restartId = ttsSession.restartId + 1;
    ttsSession.restartId = restartId;
    ttsSession.isRestarting = true;
    ttsSession.utterance = null;

    window.speechSynthesis.cancel();
    window.setTimeout(() => {
      if (!ttsSession.isRestarting || ttsSession.restartId !== restartId) return;
      startContentSpeech(text, resumeIndex);
    }, 80);
  }

  function setActiveTtsSpeed(value) {
    const nextSpeed = normalizeTtsSpeed(value);
    if (activeTtsSpeed === nextSpeed) return;

    activeTtsSpeed = nextSpeed;
    restartActiveContentSpeech();
  }

  function setActiveTtsVoice(value) {
    const nextVoiceName = normalizeTtsVoiceName(value);
    if (activeTtsVoiceName === nextVoiceName) return;

    activeTtsVoiceName = nextVoiceName;
    restartActiveContentSpeech();
  }

  function speakContentText(text) {
    const trimmed = typeof text === "string" ? text.trim() : "";
    if (!trimmed) return;

    const speechId = ttsSession.restartId + 1;
    ttsSession.restartId = speechId;
    ttsSession.text = trimmed;
    ttsSession.utterance = null;
    ttsSession.charIndex = 0;
    ttsSession.progressStartedAt = 0;
    ttsSession.progressStartedIndex = 0;
    ttsSession.progressRate = 1;
    ttsSession.isSpeaking = false;
    ttsSession.isRestarting = false;

    window.speechSynthesis.cancel();
    window.setTimeout(() => {
      if (ttsSession.restartId !== speechId || ttsSession.text !== trimmed) return;
      startContentSpeech(trimmed, 0);
    }, 80);
  }

  function stopContentTts() {
    ttsSession.restartId += 1;
    ttsSession.text = "";
    ttsSession.utterance = null;
    ttsSession.charIndex = 0;
    ttsSession.progressStartedAt = 0;
    ttsSession.progressStartedIndex = 0;
    ttsSession.progressRate = 1;
    ttsSession.isSpeaking = false;
    ttsSession.isRestarting = false;
    window.speechSynthesis.cancel();
  }

  window.speechSynthesis.addEventListener("voiceschanged", () => {
    if (activeTtsVoiceName && ttsSession.isSpeaking) {
      restartActiveContentSpeech();
    }
  });

  function isTransparentColor(value) {
    return (
      !value ||
      value === "transparent" ||
      value === "rgba(0, 0, 0, 0)" ||
      value === "rgba(0,0,0,0)"
    );
  }

  function getResolvedBackgroundColor(element) {
    let currentElement = element;

    while (currentElement && currentElement !== document.documentElement) {
      const backgroundColor = window.getComputedStyle(currentElement).backgroundColor;
      if (!isTransparentColor(backgroundColor)) {
        return backgroundColor;
      }
      currentElement = currentElement.parentElement;
    }

    const bodyBackground = window.getComputedStyle(document.body).backgroundColor;
    if (!isTransparentColor(bodyBackground)) return bodyBackground;

    const htmlBackground = window.getComputedStyle(document.documentElement).backgroundColor;
    return isTransparentColor(htmlBackground) ? "#ffffff" : htmlBackground;
  }

  function getResolvedPageBackgroundColor() {
    const bodyBackground = getResolvedBackgroundColor(document.body);
    if (!isTransparentColor(bodyBackground)) return bodyBackground;

    const htmlBackground = window.getComputedStyle(document.documentElement).backgroundColor;
    return isTransparentColor(htmlBackground) ? "#ffffff" : htmlBackground;
  }

  function applyMagnifierPresentationStyles(sourceElement, targetElement, sourceRect) {
    if (!sourceElement || !targetElement) return;

    const computedStyle = window.getComputedStyle(sourceElement);
    const backgroundColor = getResolvedBackgroundColor(sourceElement);
    const displayValue = computedStyle.display === "inline" ? "inline-block" : computedStyle.display;

    targetElement.style.setProperty("background-color", backgroundColor, "important");
    targetElement.style.setProperty("color", computedStyle.color, "important");
    targetElement.style.setProperty("font-family", computedStyle.fontFamily, "important");
    targetElement.style.setProperty("font-size", computedStyle.fontSize, "important");
    targetElement.style.setProperty("font-weight", computedStyle.fontWeight, "important");
    targetElement.style.setProperty("font-style", computedStyle.fontStyle, "important");
    targetElement.style.setProperty("line-height", computedStyle.lineHeight, "important");
    targetElement.style.setProperty("letter-spacing", computedStyle.letterSpacing, "important");
    targetElement.style.setProperty("word-spacing", computedStyle.wordSpacing, "important");
    targetElement.style.setProperty("text-transform", computedStyle.textTransform, "important");
    targetElement.style.setProperty("text-decoration", computedStyle.textDecoration, "important");
    targetElement.style.setProperty("display", displayValue, "important");
    targetElement.style.setProperty("box-sizing", "border-box", "important");
    targetElement.style.setProperty("margin", "0", "important");
    targetElement.style.setProperty("position", "static", "important");
    targetElement.style.setProperty("left", "0", "important");
    targetElement.style.setProperty("top", "0", "important");
    targetElement.style.setProperty("transform", "none", "important");
    targetElement.style.setProperty("max-width", "none", "important");
    targetElement.style.setProperty("max-height", "none", "important");
    targetElement.style.setProperty("pointer-events", "none", "important");

    if (sourceRect) {
      targetElement.style.setProperty("width", `${sourceRect.width}px`, "important");
      targetElement.style.setProperty("height", `${sourceRect.height}px`, "important");
    }

    targetElement.querySelectorAll("*").forEach((element) => {
      element.style.setProperty("animation", "none", "important");
      element.style.setProperty("transition", "none", "important");
    });
  }

  // Save selection when user selects text with mouse or keyboard
  function saveCurrentSelection() {
    const selection = window.getSelection();
    const text = selection ? selection.toString().trim() : "";

    if (selection && selection.rangeCount > 0 && text !== "") {
      // Clear any pending save and schedule a new one
      if (selectionSaveTimer) clearTimeout(selectionSaveTimer);
      selectionSaveTimer = setTimeout(() => {
        try {
          savedRange = selection.getRangeAt(0).cloneRange();
        } catch (e) {
          // keep last valid range if cloning fails
        }
        savedSelectedText = text;
      }, 50);
    }
  }

  document.addEventListener("mouseup", saveCurrentSelection);
  document.addEventListener("keyup", saveCurrentSelection);

  function getCurrentSelectionRange() {
    const selection = window.getSelection();
    if (!selection || selection.rangeCount === 0 || selection.toString().trim() === "") {
      return null;
    }

    try {
      return selection.getRangeAt(0).cloneRange();
    } catch (error) {
      warnContentScript("Could not read the current selection range.", error);
      return null;
    }
  }

  function getHighlightRange() {
    return getCurrentSelectionRange() || savedRange;
  }

  function clearSelectionCache() {
    const selection = window.getSelection();
    selection?.removeAllRanges();
    savedRange = null;
    savedSelectedText = "";
  }

  function applyHighlightToRange(range, color) {
    if (!range || range.toString().trim() === "") return false;

    const highlightColor = normalizeHighlightColor(color);
    const ancestor = range.commonAncestorContainer;

    if (ancestor.nodeType === Node.TEXT_NODE) {
      const mark = document.createElement("mark");
      mark.className = "dyslexia-highlight";
      mark.style.backgroundColor = highlightColor;
      mark.style.color = "inherit";
      range.surroundContents(mark);
      clearSelectionCache();
      return true;
    }

    const treeWalker = document.createTreeWalker(
      ancestor,
      NodeFilter.SHOW_TEXT,
      {
        acceptNode: (node) => {
          return range.intersectsNode(node)
            ? NodeFilter.FILTER_ACCEPT
            : NodeFilter.FILTER_REJECT;
        },
      }
    );

    const textNodes = [];
    let currentNode = treeWalker.nextNode();
    while (currentNode) {
      textNodes.push(currentNode);
      currentNode = treeWalker.nextNode();
    }

    textNodes.forEach((textNode) => {
      const nodeRange = document.createRange();
      nodeRange.selectNodeContents(textNode);

      if (textNode === range.startContainer) {
        nodeRange.setStart(textNode, range.startOffset);
      }
      if (textNode === range.endContainer) {
        nodeRange.setEnd(textNode, range.endOffset);
      }

      if (nodeRange.toString().trim() === "") return;

      const mark = document.createElement("mark");
      mark.className = "dyslexia-highlight";
      mark.style.backgroundColor = highlightColor;
      mark.style.color = "inherit";
      nodeRange.surroundContents(mark);
    });

    clearSelectionCache();
    return true;
  }

  // --- Global Keybindings ---
  const DEFAULT_KEYBINDINGS = {
    toggleMask:      { key: "m", alt: true, ctrl: false, shift: false },
    toggleMagnifier: { key: "g", alt: true, ctrl: false, shift: false },
    highlight:       { key: "h", alt: true, ctrl: false, shift: false },
    readText:        { key: "r", alt: true, ctrl: false, shift: false },
    stopTTS:         { key: "s", alt: true, ctrl: false, shift: false },
    increaseFontSize:{ key: "=", alt: true, ctrl: false, shift: false },
    decreaseFontSize:{ key: "-", alt: true, ctrl: false, shift: false },
  };

  let activeKeybindings = { ...DEFAULT_KEYBINDINGS };

  function getReadableTextElements() {
  const readableSelector = [
    "p",
    "span",
    "a",
    "button",
    "[role='button']",
    "li",
    "blockquote",
    "td",
    "th",
    "label",
    "summary",
    "article",
    "section",
    "main",
    "h1",
    "h2",
    "h3",
    "h4",
    "h5",
    "h6",
    "figcaption",
    "dd",
    "dt"
  ].join(", ");

  const excludedContainerSelector = [
    "form",
    "input",
    "textarea",
    "select",
    "option",
    "progress",
    "meter",
    "[contenteditable='true']",
    "script",
    "style",
    "noscript",
    "svg",
    "canvas",
    "picture",
    "img",
    "video",
    "iframe"
  ].join(", ");

  return Array.from(document.querySelectorAll(readableSelector)).filter((el) => {
    if (!el || !el.textContent || !el.textContent.trim()) return false;
    if (el.closest(excludedContainerSelector)) return false;
    return true;
  });
}

function clearAppliedFontStyles() {
  const existing = document.getElementById("dyslexia-font-style");
  if (existing) existing.remove();

  document.querySelectorAll("[data-dyslexia-font-applied='true']").forEach((el) => {
    el.style.removeProperty("font-family");
    el.removeAttribute("data-dyslexia-font-applied");
  });
}

// Selectors for controls/media that should not receive reading-style layout changes.
const EXCLUDED_REGION_SELECTORS = [
  "form",
  "input", "textarea", "select", "option", "progress", "meter",
  "[contenteditable='true']",
  "script", "style", "noscript",
  "svg", "canvas", "picture", "img", "video", "iframe", "object", "embed",
  "audio", "source", "track",
  "#dyslexia-reading-mask", "#dyslexia-reading-mask *",
  "#dyslexia-magnifier-container", "#dyslexia-magnifier-container *",
  "#dyslexia-style-indicator-host", "#dyslexia-style-indicator-host *",
  "[data-dyslexia-magnifier-clone='true']", "[data-dyslexia-magnifier-clone='true'] *"
].join(", ");

const CONTENT_STYLE_TARGET_SELECTORS = [
  "p", "span", "a", "button", "[role='button']", "li", "blockquote", "td", "th", "label", "summary",
  "article", "section", "main",
  "h1", "h2", "h3", "h4", "h5", "h6",
  "figcaption", "dd", "dt"
].join(", ");

const CONTENT_STYLE_CSS_SELECTOR = [
  ":where(p, span, a, button, [role='button'], li, blockquote, td, th, label, summary, article, section, main, h1, h2, h3, h4, h5, h6, figcaption, dd, dt)",
  ":not(:where(form, input, textarea, select, option, progress, meter, [contenteditable='true'], script, style, noscript, svg, canvas, picture, img, video, iframe, object, embed, audio, source, track, #dyslexia-reading-mask, #dyslexia-magnifier-container, #dyslexia-style-indicator-host, [data-dyslexia-magnifier-clone='true']))",
  ":not(:where(form, input, textarea, select, option, progress, meter, [contenteditable='true'], script, style, noscript, svg, canvas, picture, img, video, iframe, object, embed, audio, source, track, #dyslexia-reading-mask, #dyslexia-magnifier-container, #dyslexia-style-indicator-host, [data-dyslexia-magnifier-clone='true']) *)"
].join("");

function getSafeContentStyleTargets() {
  return Array.from(document.querySelectorAll(CONTENT_STYLE_TARGET_SELECTORS)).filter((el) => {
    if (!el || !el.textContent || !el.textContent.trim()) return false;
    if (el.closest(EXCLUDED_REGION_SELECTORS)) return false;
    return true;
  });
}

function applySafeFont(fontId, fontOptions) {
  const fontConfig = fontOptions.find((f) => f.id === fontId);
  if (!fontConfig) return;

  // Always fully clear previous font styles first
  clearAppliedFontStyles();

  // "system" means reset to default — clearing is all we need
  if (fontId === "system") return;

  let fontFaceBlock = "";
  if (fontId === "open-dyslexic") {
    const regWoff2 = chrome.runtime.getURL("fonts/OpenDyslexic-Regular.woff2");
    const boldWoff2 = chrome.runtime.getURL("fonts/OpenDyslexic-Bold.woff2");
    const italicWoff2 = chrome.runtime.getURL("fonts/OpenDyslexic-Italic.woff2");
    fontFaceBlock = `
      @font-face {
        font-family: "OpenDyslexic";
        src: url(${regWoff2}) format("woff2");
        font-weight: 400;
        font-style: normal;
      }
      @font-face {
        font-family: "OpenDyslexic";
        src: url(${boldWoff2}) format("woff2");
        font-weight: 700;
        font-style: normal;
      }
      @font-face {
        font-family: "OpenDyslexic";
        src: url(${italicWoff2}) format("woff2");
        font-weight: 400;
        font-style: italic;
      }
    `;
  }

  // Apply only to content elements not inside excluded regions.
  getSafeContentStyleTargets().forEach((el) => {
    el.style.setProperty("font-family", fontConfig.cssFont, "important");
    el.setAttribute("data-dyslexia-font-applied", "true");
  });

  // Inject @font-face if needed (for OpenDyslexic)
  if (fontFaceBlock) {
    const style = document.createElement("style");
    style.id = "dyslexia-font-style";
    style.textContent = fontFaceBlock;
    document.head.appendChild(style);
  }
}

  const PREFS_STORAGE_KEY = "dyslexiaUserPreferences";
  const VOICE_STORAGE_KEY = "selectedVoiceName";
const FONT_OPTIONS = [
  { id: "system", cssFont: "system-ui, sans-serif" },
  { id: "open-dyslexic", cssFont: "OpenDyslexic, sans-serif" },
  { id: "arial", cssFont: "Arial, sans-serif" },
  { id: "verdana", cssFont: "Verdana, sans-serif" },
  { id: "georgia", cssFont: "Georgia, serif" },
  { id: "courier", cssFont: "'Courier New', monospace" },
  { id: "trebuchet", cssFont: "'Trebuchet MS', sans-serif" },
];

const STYLE_INDICATOR_HOST_ID = "dyslexia-style-indicator-host";
const DYSLEXIA_BACKGROUND_ATTR = "data-dyslexia-background-applied";
const DYSLEXIA_BACKGROUND_STYLE_ID = "dyslexia-background-style";
const DYSLEXIA_BACKGROUND_ROOT_ATTR = "data-dyslexia-background-active";
const DYSLEXIA_BACKGROUND_COLOR_VAR = "--dyslexia-background-color";
const DYSLEXIA_BACKGROUND_PREVIOUS_STYLE_ATTR = "data-dyslexia-background-previous-style";
const DYSLEXIA_TEXT_COLOR_ATTR = "data-dyslexia-text-color-applied";
const DYSLEXIA_TEXT_COLOR_PREVIOUS_STYLE_ATTR = "data-dyslexia-text-color-previous-style";
const DEFAULT_PAGE_FONT_SIZE = activeFontSize;
const DEFAULT_PAGE_LINE_SPACING = getCurrentBodyLineSpacing();
const activeStyleState = {
  fontSize: false,
  font: false,
  letterSpacing: false,
  wordSpacing: false,
  lineSpacing: false,
  backgroundColor: false,
  textColor: false,
};
let activeBackgroundColor = "";
let backgroundApplyTimer = null;
let lastObservedPageKey = "";
let pageNavigationTimer = null;

function cancelBackgroundApplyTimer() {
  if (!backgroundApplyTimer) return;
  clearTimeout(backgroundApplyTimer);
  backgroundApplyTimer = null;
}

function saveInlineStyleSnapshot(el, property, snapshotAttr) {
  if (!el?.style || el.hasAttribute(snapshotAttr)) return;

  const value = el.style.getPropertyValue(property);
  const priority = el.style.getPropertyPriority(property);
  el.setAttribute(snapshotAttr, JSON.stringify({ value, priority }));
}

function restoreInlineStyleSnapshot(el, property, snapshotAttr) {
  if (!el?.style) return;

  const rawSnapshot = el.getAttribute(snapshotAttr);
  if (!rawSnapshot) {
    el.style.removeProperty(property);
    return;
  }

  try {
    const snapshot = JSON.parse(rawSnapshot);
    if (snapshot?.value) {
      el.style.setProperty(property, snapshot.value, snapshot.priority || "");
    } else {
      el.style.removeProperty(property);
    }
  } catch (error) {
    warnContentScript(`Unable to restore ${property}`, error);
    el.style.removeProperty(property);
  } finally {
    el.removeAttribute(snapshotAttr);
  }
}

function setTrackedInlineStyle(el, property, value, priority, appliedAttr, snapshotAttr) {
  saveInlineStyleSnapshot(el, property, snapshotAttr);
  el.style.setProperty(property, value, priority);
  el.setAttribute(appliedAttr, "true");
}

function clearTrackedInlineStyle(el, property, appliedAttr, snapshotAttr) {
  restoreInlineStyleSnapshot(el, property, snapshotAttr);
  el.removeAttribute(appliedAttr);
}

function getCurrentBodyLineSpacing() {
  const computedStyle = window.getComputedStyle(document.body);
  const lineHeight = computedStyle.lineHeight;

  if (!lineHeight || lineHeight === "normal") return 1.0;
  if (lineHeight.includes("px")) {
    const pxValue = parseFloat(lineHeight);
    const fontSizePx = parseFloat(computedStyle.fontSize) || 16;
    return fontSizePx > 0 ? pxValue / fontSizePx : 1.0;
  }

  const parsed = parseFloat(lineHeight);
  return Number.isFinite(parsed) && parsed > 0 ? parsed : 1.0;
}

function hasActiveReadingStyles() {
  return Object.values(activeStyleState).some(Boolean);
}

function ensureStyleIndicator() {
  let host = document.getElementById(STYLE_INDICATOR_HOST_ID);
  if (host) return host;

  host = document.createElement("div");
  host.id = STYLE_INDICATOR_HOST_ID;
  host.setAttribute("aria-hidden", "true");
  host.style.setProperty("all", "initial", "important");
  host.style.setProperty("position", "fixed", "important");
  host.style.setProperty("right", "18px", "important");
  host.style.setProperty("bottom", "18px", "important");
  host.style.setProperty("z-index", "2147483646", "important");
  host.style.setProperty("pointer-events", "none", "important");
  host.style.setProperty("display", "none", "important");

  const shadowRoot = host.attachShadow({ mode: "open" });
  shadowRoot.innerHTML = `
    <style>
      :host {
        all: initial;
      }
      .pill {
        display: inline-flex;
        align-items: center;
        gap: 8px;
        padding: 8px 12px;
        border-radius: 999px;
        background: rgba(37, 37, 71, 0.9);
        border: 1px solid rgba(127, 119, 221, 0.35);
        color: #ffffff;
        font: 500 12px/1.2 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
        letter-spacing: 0.02em;
        box-shadow: 0 10px 30px rgba(16, 18, 38, 0.22);
        backdrop-filter: blur(10px);
        opacity: 0;
        transform: translateY(6px);
        transition: opacity 160ms ease, transform 160ms ease;
      }
      .pill.is-visible {
        opacity: 0.94;
        transform: translateY(0);
      }
      .mark {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 22px;
        height: 22px;
        border-radius: 999px;
        background: linear-gradient(135deg, #7f77dd 0%, #9b95ef 100%);
        color: #ffffff;
        font: 700 11px/1 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
      }
      .label {
        white-space: nowrap;
      }
    </style>
    <div class="pill" role="status" aria-live="polite">
      <span class="mark">Aa</span>
      <span class="label">Reading style on</span>
    </div>
  `;

  document.documentElement.appendChild(host);
  return host;
}

function updateStyleIndicator() {
  const host = ensureStyleIndicator();
  const pill = host.shadowRoot?.querySelector(".pill");
  if (!pill) return;

  if (!hasActiveReadingStyles()) {
    pill.classList.remove("is-visible");
    host.style.setProperty("display", "none", "important");
    return;
  }

  host.style.setProperty("display", "block", "important");
  pill.classList.add("is-visible");
}

function applyWordSpacing(value) {
  document.querySelectorAll("[data-dyslexia-word-spacing-applied='true']").forEach((el) => {
    el.style.removeProperty("word-spacing");
    el.removeAttribute("data-dyslexia-word-spacing-applied");
  });
  getSafeContentStyleTargets().forEach((el) => {
    el.style.setProperty("word-spacing", value + "em", "important");
    el.setAttribute("data-dyslexia-word-spacing-applied", "true");
  });
  activeStyleState.wordSpacing = Math.abs(Number(value)) > 0.001;
  updateStyleIndicator();
}

function applyLetterSpacing(value) {
  document.querySelectorAll("[data-dyslexia-letter-spacing-applied='true']").forEach((el) => {
    el.style.removeProperty("letter-spacing");
    el.removeAttribute("data-dyslexia-letter-spacing-applied");
  });
  getSafeContentStyleTargets().forEach((el) => {
    el.style.setProperty("letter-spacing", value + "em", "important");
    el.setAttribute("data-dyslexia-letter-spacing-applied", "true");
  });
  activeStyleState.letterSpacing = Math.abs(Number(value)) > 0.001;
  updateStyleIndicator();
}

function applyLineSpacing(value) {
  document.querySelectorAll("[data-dyslexia-line-spacing-applied='true']").forEach((el) => {
    el.style.removeProperty("line-height");
    el.removeAttribute("data-dyslexia-line-spacing-applied");
  });
  getSafeContentStyleTargets().forEach((el) => {
    el.style.setProperty("line-height", String(value), "important");
    el.setAttribute("data-dyslexia-line-spacing-applied", "true");
  });
  activeStyleState.lineSpacing = Math.abs(Number(value) - DEFAULT_PAGE_LINE_SPACING) > 0.001;
  updateStyleIndicator();
}

const BACKGROUND_STYLE_TARGET_SELECTORS = [
  "html", "body",
  "main", "article", "section", "div",
  "header", "footer", "nav", "aside",
  "form", "fieldset", "legend", "label",
  "details", "summary", "dl", "dt", "dd",
  "ul", "ol", "li",
  "table", "thead", "tbody", "tfoot", "tr", "td", "th",
  "blockquote", "pre", "p", "span", "a",
  "h1", "h2", "h3", "h4", "h5", "h6",
  "[role='main']", "[role='article']", "[role='region']", "[role='complementary']"
].join(", ");
const BACKGROUND_MEDIA_SELECTORS = [
  "img", "picture", "video", "canvas", "svg", "iframe", "object", "embed",
  "audio", "source", "track"
];
const BACKGROUND_CONTROL_SELECTORS = [
  "button", "[role='button']",
  "input", "textarea", "select", "option", "progress", "meter"
];
const BACKGROUND_EXTENSION_SELECTORS = [
  `[id='${STYLE_INDICATOR_HOST_ID}']`, `[id='${STYLE_INDICATOR_HOST_ID}'] *`,
  `[id='${DYSLEXIA_BACKGROUND_STYLE_ID}']`,
  "#dyslexia-magnifier-container", "#dyslexia-magnifier-container *",
  "[data-dyslexia-magnifier-clone='true']", "[data-dyslexia-magnifier-clone='true'] *"
];
const BACKGROUND_HIGHLIGHT_SELECTORS = [
  "mark",
  "mark.dyslexia-highlight",
  `mark.${COMPLEX_WORD_CLASS}`
];
const BACKGROUND_EXCLUDED_SELECTORS = [
  ...BACKGROUND_MEDIA_SELECTORS,
  ...BACKGROUND_CONTROL_SELECTORS,
  ...BACKGROUND_EXTENSION_SELECTORS,
  ...BACKGROUND_HIGHLIGHT_SELECTORS
].join(", ");
const BACKGROUND_EXCLUDED_ANCESTOR_SELECTORS = [
  ...BACKGROUND_MEDIA_SELECTORS,
  ...BACKGROUND_CONTROL_SELECTORS,
  ...BACKGROUND_EXTENSION_SELECTORS
].join(", ");
const BACKGROUND_VISIBLE_CHILD_SELECTOR = [
  "p", "span", "a", "li", "td", "th",
  "blockquote", "pre", "label",
  "h1", "h2", "h3", "h4", "h5", "h6",
  "img", "picture", "video", "canvas", "svg"
].join(", ");

function clearAppliedBackgroundColor() {
  cancelBackgroundApplyTimer();
  const existingStyle = document.getElementById(DYSLEXIA_BACKGROUND_STYLE_ID);
  if (existingStyle) existingStyle.remove();
  document.documentElement.removeAttribute(DYSLEXIA_BACKGROUND_ROOT_ATTR);
  document.documentElement.style.removeProperty(DYSLEXIA_BACKGROUND_COLOR_VAR);

  document.querySelectorAll(`[${DYSLEXIA_BACKGROUND_ATTR}='true']`).forEach((el) => {
    clearTrackedInlineStyle(
      el,
      "background-color",
      DYSLEXIA_BACKGROUND_ATTR,
      DYSLEXIA_BACKGROUND_PREVIOUS_STYLE_ATTR
    );
  });
}

function getBackgroundStyleTargets() {
  return Array.from(document.querySelectorAll(BACKGROUND_STYLE_TARGET_SELECTORS)).filter((el) => {
    if (!el || el.matches(BACKGROUND_EXCLUDED_SELECTORS)) return false;
    const excludedAncestor = el.closest(BACKGROUND_EXCLUDED_ANCESTOR_SELECTORS);
    if (excludedAncestor && excludedAncestor !== document.body && excludedAncestor !== document.documentElement) {
      return false;
    }
    return shouldApplyBackgroundToElement(el);
  });
}

function shouldApplyBackgroundToElement(el) {
  if (el === document.documentElement || el === document.body) return true;
  if (!el?.style) return false;

  const computedStyle = window.getComputedStyle(el);
  if (
    computedStyle.display === "none" ||
    computedStyle.visibility === "hidden" ||
    Number(computedStyle.opacity || "1") <= 0.01
  ) {
    return false;
  }

  const rect = el.getBoundingClientRect();
  const hasBox = rect.width > 0 && rect.height > 0;
  const text = (el.textContent || "").replace(/\s+/g, " ").trim();
  const hasReadableText = text.length > 0;
  const hasVisibleContentChild = Boolean(el.querySelector(BACKGROUND_VISIBLE_CHILD_SELECTOR));

  // Wikipedia and other large sites use empty header/spacer layers near the top of
  // the viewport. Painting those turns them into a solid colored cover over content.
  if (!hasReadableText && !hasVisibleContentChild) return false;
  if (hasBox && !hasReadableText && rect.height > 24) return false;

  const position = computedStyle.position;
  if (
    hasBox &&
    (position === "fixed" || position === "sticky") &&
    rect.height > Math.max(72, window.innerHeight * 0.12)
  ) {
    return false;
  }

  if (
    hasBox &&
    position === "absolute" &&
    !hasReadableText &&
    rect.width > window.innerWidth * 0.5 &&
    rect.height > window.innerHeight * 0.2
  ) {
    return false;
  }

  return true;
}

function setBackgroundOverrideState(value) {
  ensureBackgroundOverrideStyle();
  document.documentElement.setAttribute(DYSLEXIA_BACKGROUND_ROOT_ATTR, "true");
  document.documentElement.style.setProperty(DYSLEXIA_BACKGROUND_COLOR_VAR, value);
  setTrackedInlineStyle(
    document.documentElement,
    "background-color",
    `var(${DYSLEXIA_BACKGROUND_COLOR_VAR})`,
    "important",
    DYSLEXIA_BACKGROUND_ATTR,
    DYSLEXIA_BACKGROUND_PREVIOUS_STYLE_ATTR
  );
  setTrackedInlineStyle(
    document.body,
    "background-color",
    `var(${DYSLEXIA_BACKGROUND_COLOR_VAR})`,
    "important",
    DYSLEXIA_BACKGROUND_ATTR,
    DYSLEXIA_BACKGROUND_PREVIOUS_STYLE_ATTR
  );
}

function applyBackgroundInlineOverrides() {
  getBackgroundStyleTargets().forEach((el) => {
    setTrackedInlineStyle(
      el,
      "background-color",
      `var(${DYSLEXIA_BACKGROUND_COLOR_VAR})`,
      "important",
      DYSLEXIA_BACKGROUND_ATTR,
      DYSLEXIA_BACKGROUND_PREVIOUS_STYLE_ATTR
    );
  });
}

function ensureBackgroundOverrideStyle() {
  let style = document.getElementById(DYSLEXIA_BACKGROUND_STYLE_ID);
  if (!style) {
    style = document.createElement("style");
    style.id = DYSLEXIA_BACKGROUND_STYLE_ID;
    (document.head || document.documentElement).appendChild(style);
  }

  style.textContent = `
    html[${DYSLEXIA_BACKGROUND_ROOT_ATTR}="true"],
    html[${DYSLEXIA_BACKGROUND_ROOT_ATTR}="true"] body,
    html[${DYSLEXIA_BACKGROUND_ROOT_ATTR}="true"] [${DYSLEXIA_BACKGROUND_ATTR}="true"] {
      background-color: var(--dyslexia-background-color) !important;
    }
  `;
}

function applyBackgroundColor(value) {
  if (!value) {
    clearAppliedBackgroundColor();
    activeBackgroundColor = "";
    activeStyleState.backgroundColor = false;
    updateStyleIndicator();
    return;
  }

  activeBackgroundColor = value;
  const wasActive = activeStyleState.backgroundColor || document.documentElement.hasAttribute(DYSLEXIA_BACKGROUND_ROOT_ATTR);
  setBackgroundOverrideState(value);
  if (!wasActive) applyBackgroundInlineOverrides();
  activeStyleState.backgroundColor = true;
  updateStyleIndicator();
}

function refreshActiveBackgroundColor() {
  if (!activeBackgroundColor) return;
  if (backgroundApplyTimer) clearTimeout(backgroundApplyTimer);
  backgroundApplyTimer = setTimeout(() => {
    backgroundApplyTimer = null;
    setBackgroundOverrideState(activeBackgroundColor);
    applyBackgroundInlineOverrides();
  }, 100);
}

function clearAppliedTextColor() {
  document.querySelectorAll(`[${DYSLEXIA_TEXT_COLOR_ATTR}='true']`).forEach((el) => {
    clearTrackedInlineStyle(
      el,
      "color",
      DYSLEXIA_TEXT_COLOR_ATTR,
      DYSLEXIA_TEXT_COLOR_PREVIOUS_STYLE_ATTR
    );
  });
}

const TEXT_COLOR_NAVIGATION_LINK_SCOPE_SELECTOR = [
  "header", "nav", "aside", "menu",
  "[role='navigation']", "[role='menu']", "[role='menubar']",
  ".navbar", ".nav", ".navigation", ".menu",
  ".header", ".site-header", ".page-header",
  ".toolbar", ".topbar", ".sidebar",
  ".breadcrumb", ".pagination", ".tabs"
].join(", ");

const TEXT_COLOR_EXCLUDED_SELECTORS = [
  "form", "form *",
  "input", "textarea", "select", "option", "progress", "meter",
  "[contenteditable='true']",
  "script", "style", "noscript",
  "svg", "svg *", "canvas", "img", "picture", "video", "iframe", "object", "embed",
  "audio", "source", "track",
  "#dyslexia-reading-mask", "#dyslexia-reading-mask *",
  "#dyslexia-magnifier-container", "#dyslexia-magnifier-container *",
  "#dyslexia-style-indicator-host", "#dyslexia-style-indicator-host *",
  "[data-dyslexia-magnifier-clone='true']", "[data-dyslexia-magnifier-clone='true'] *"
].join(", ");

const TEXT_COLOR_TARGET_SELECTORS = [
  "body", "p", "span", "a", "div", "li", "main", "article", "section",
  "header", "footer", "nav", "aside", "menu", "summary",
  "button", "[role='button']", "[role='menuitem']", "[role='tab']",
  "h1", "h2", "h3", "h4", "h5", "h6",
  "label", "strong", "em", "small", "blockquote", "td", "th", "figcaption",
  "#content",
  "#mw-content-text", ".mw-body", ".vector-body", ".vector-page",
  ".page-content", ".content", ".main-content", ".mw-parser-output"
].join(", ");

function isContentHyperlinkText(el) {
  const link = el?.closest?.("a");
  if (!link) return false;
  return !link.closest(TEXT_COLOR_NAVIGATION_LINK_SCOPE_SELECTOR);
}

function shouldSkipTextColorElement(el) {
  if (!el || !el.textContent || !el.textContent.trim()) return true;
  if (el.matches(TEXT_COLOR_EXCLUDED_SELECTORS) || el.closest(TEXT_COLOR_EXCLUDED_SELECTORS)) return true;
  const button = el.closest("button, [role='button']");
  if (button && !button.closest(TEXT_COLOR_NAVIGATION_LINK_SCOPE_SELECTOR)) return true;
  return isContentHyperlinkText(el);
}

function preserveContentHyperlinkColors() {
  document.querySelectorAll("a").forEach((link) => {
    if (link.closest(TEXT_COLOR_NAVIGATION_LINK_SCOPE_SELECTOR)) return;
    if (link.matches(TEXT_COLOR_EXCLUDED_SELECTORS) || link.closest(TEXT_COLOR_EXCLUDED_SELECTORS)) return;

    const computedColor = window.getComputedStyle(link).color;
    if (!computedColor) return;

    setTrackedInlineStyle(
      link,
      "color",
      computedColor,
      "important",
      DYSLEXIA_TEXT_COLOR_ATTR,
      DYSLEXIA_TEXT_COLOR_PREVIOUS_STYLE_ATTR
    );
  });
}

function applyTextColor(value) {
  clearAppliedTextColor();
  if (!value) {
    activeStyleState.textColor = false;
    updateStyleIndicator();
    return;
  }

  preserveContentHyperlinkColors();

  setTrackedInlineStyle(
    document.documentElement,
    "color",
    value,
    "important",
    DYSLEXIA_TEXT_COLOR_ATTR,
    DYSLEXIA_TEXT_COLOR_PREVIOUS_STYLE_ATTR
  );
  setTrackedInlineStyle(
    document.body,
    "color",
    value,
    "important",
    DYSLEXIA_TEXT_COLOR_ATTR,
    DYSLEXIA_TEXT_COLOR_PREVIOUS_STYLE_ATTR
  );

  document.querySelectorAll(TEXT_COLOR_TARGET_SELECTORS).forEach((el) => {
    if (shouldSkipTextColorElement(el)) return;
    setTrackedInlineStyle(
      el,
      "color",
      value,
      "important",
      DYSLEXIA_TEXT_COLOR_ATTR,
      DYSLEXIA_TEXT_COLOR_PREVIOUS_STYLE_ATTR
    );
  });
  activeStyleState.textColor = true;
  updateStyleIndicator();
}

function applyFontById(fontId) {
  applySafeFont(fontId, FONT_OPTIONS);
  activeStyleState.font = fontId !== "system";
  updateStyleIndicator();
}

function getCurrentSiteKey() {
  const hostname = window.location.hostname.toLowerCase();
  return hostname.startsWith("www.") ? hostname.slice(4) : hostname;
}

function getCurrentPageKey() {
  return `${window.location.origin}${window.location.pathname}${window.location.search}`;
}

function getSavedPageFont(saved) {
  const pageKey = getCurrentPageKey();
  if (!pageKey || !saved || typeof saved.pageFontPreferences !== "object" || saved.pageFontPreferences === null) {
    return undefined;
  }

  return typeof saved.pageFontPreferences[pageKey] === "string" ? saved.pageFontPreferences[pageKey] : undefined;
}

function getSavedSiteFont(saved) {
  const siteKey = getCurrentSiteKey();
  if (!siteKey || !saved || typeof saved.siteFontPreferences !== "object" || saved.siteFontPreferences === null) {
    return undefined;
  }

  return typeof saved.siteFontPreferences[siteKey] === "string" ? saved.siteFontPreferences[siteKey] : undefined;
}

function getSavedPageBackgroundColor(saved) {
  const pageKey = getCurrentPageKey();
  if (!pageKey || !saved || typeof saved.pageBackgroundPreferences !== "object" || saved.pageBackgroundPreferences === null) {
    return undefined;
  }

  return typeof saved.pageBackgroundPreferences[pageKey] === "string" ? saved.pageBackgroundPreferences[pageKey] : undefined;
}

function getThemeTextColorForBackground(background) {
  if (background === "#000000") return "#ffffff";
  if (background === "#ffffff") return "#000000";
  if (background === "#fdf6e3") return "#333333";
  return undefined;
}

function getSavedPageTextColor(saved) {
  const pageKey = getCurrentPageKey();
  if (!pageKey || !saved) return undefined;

  if (typeof saved.pageTextColorPreferences === "object" && saved.pageTextColorPreferences !== null) {
    const pageTextColor = saved.pageTextColorPreferences[pageKey];
    if (typeof pageTextColor === "string") return pageTextColor;
  }

  const pageBackgroundColor = getSavedPageBackgroundColor(saved);
  return typeof pageBackgroundColor === "string" ? getThemeTextColorForBackground(pageBackgroundColor) : undefined;
}

function getSavedGlobalBackgroundColor(saved) {
  if (!saved) return undefined;
  if (typeof saved.globalBackgroundColor === "string" && saved.globalBackgroundColor !== "#ffffff") {
    return saved.globalBackgroundColor;
  }
  if (typeof saved.globalBackgroundColor !== "string" && typeof saved.backgroundColor === "string") {
    if (saved.backgroundColorSet === true || saved.backgroundColor !== "#ffffff") return saved.backgroundColor;
  }
  return undefined;
}

function getSavedGlobalTextColor(saved) {
  if (!saved) return undefined;

  if (typeof saved.globalTextColor === "string" && saved.globalTextColor !== "#000000") {
    return saved.globalTextColor;
  }

  const hasPageBackgrounds =
    typeof saved.pageBackgroundPreferences === "object" &&
    saved.pageBackgroundPreferences !== null &&
    Object.keys(saved.pageBackgroundPreferences).length > 0;
  const hasGlobalBackground = getSavedGlobalBackgroundColor(saved) !== undefined;

  if (typeof saved.textColor === "string" && saved.textColor !== "#000000") {
    if (hasGlobalBackground || !hasPageBackgrounds) return saved.textColor;
  }

  return undefined;
}

function getEffectiveSavedBackgroundColor(saved) {
  const pageBackground = getSavedPageBackgroundColor(saved);
  if (pageBackground !== undefined) return pageBackground;
  return getSavedGlobalBackgroundColor(saved);
}

function getEffectiveSavedTextColor(saved) {
  const pageTextColor = getSavedPageTextColor(saved);
  if (pageTextColor !== undefined) return pageTextColor;
  return getSavedGlobalTextColor(saved);
}

function applySavedAppearanceForCurrentPage(saved) {
  const savedBackgroundColor = getEffectiveSavedBackgroundColor(saved);
  if (savedBackgroundColor !== undefined) {
    applyBackgroundColor(savedBackgroundColor);
  } else {
    clearAppliedBackgroundColor();
    activeBackgroundColor = "";
    activeStyleState.backgroundColor = false;
  }

  const savedTextColor = getEffectiveSavedTextColor(saved);
  if (savedTextColor !== undefined) {
    applyTextColor(savedTextColor);
  } else {
    clearAppliedTextColor();
    activeStyleState.textColor = false;
  }

  updateStyleIndicator();
}

function refreshAppearanceForCurrentPage() {
  chrome.storage.sync.get(PREFS_STORAGE_KEY, (result) => {
    applySavedAppearanceForCurrentPage(result?.[PREFS_STORAGE_KEY]);
  });
}

function handlePossiblePageNavigation() {
  const currentPageKey = getCurrentPageKey();
  if (currentPageKey === lastObservedPageKey) return;

  lastObservedPageKey = currentPageKey;
  if (pageNavigationTimer) clearTimeout(pageNavigationTimer);
  pageNavigationTimer = setTimeout(() => {
    pageNavigationTimer = null;
    refreshAppearanceForCurrentPage();
  }, 50);
}

function installPageNavigationWatcher() {
  lastObservedPageKey = getCurrentPageKey();
  const notifyNavigation = () => {
    window.setTimeout(handlePossiblePageNavigation, 0);
  };

  ["pushState", "replaceState"].forEach((methodName) => {
    const originalMethod = history[methodName];
    if (typeof originalMethod !== "function" || originalMethod._dyslexiaNavigationWrapped) return;

    const wrappedMethod = function(...args) {
      const result = originalMethod.apply(this, args);
      notifyNavigation();
      return result;
    };
    wrappedMethod._dyslexiaNavigationWrapped = true;
    history[methodName] = wrappedMethod;
  });

  window.addEventListener("popstate", notifyNavigation);
  window.addEventListener("hashchange", notifyNavigation);
  window.addEventListener("pageshow", notifyNavigation);
}

function getEffectiveSavedFont(saved) {
  const pageFont = getSavedPageFont(saved);
  if (pageFont !== undefined) return pageFont;
  const siteFont = getSavedSiteFont(saved);
  if (siteFont !== undefined) return siteFont;
  if (saved?.fontSet === true && typeof saved.selectedFont === "string") return saved.selectedFont;
  return "system";
}

function hasRestorableCustomization(saved) {
  if (!saved) return false;

  return Boolean(
    saved.userHasCustomized ||
    saved.fontSizeSet === true ||
    (typeof saved.fontSize === "number" && saved.fontSize !== 18) ||
    saved.fontSet === true ||
    (typeof saved.selectedFont === "string" && saved.selectedFont !== "system") ||
    getSavedPageFont(saved) !== undefined ||
    getSavedSiteFont(saved) !== undefined ||
    getEffectiveSavedBackgroundColor(saved) !== undefined ||
    getEffectiveSavedTextColor(saved) !== undefined ||
    saved.textColorSet === true ||
    (typeof saved.textColor === "string" && saved.textColor !== "#000000") ||
    saved.lineSpacingSet === true ||
    saved.letterSpacing !== 0 ||
    saved.wordSpacing !== 0 ||
    saved.maskActive === true
  );
}

function restoreSavedPreferences() {
  chrome.storage.sync.get([PREFS_STORAGE_KEY, VOICE_STORAGE_KEY], (result) => {
    const saved = result?.[PREFS_STORAGE_KEY];
    const savedVoice = result?.[VOICE_STORAGE_KEY];
    if (saved && typeof saved.highlightColor === "string") {
      setActiveHighlightColor(saved.highlightColor);
    }
    if (saved && typeof saved.ttsSpeed === "number") {
      setActiveTtsSpeed(saved.ttsSpeed);
    }
    if (typeof savedVoice === "string") {
      setActiveTtsVoice(savedVoice);
    } else if (saved && typeof saved.selectedVoice === "string") {
      setActiveTtsVoice(saved.selectedVoice);
    }
    if (!hasRestorableCustomization(saved)) return;

    if (
      typeof saved.fontSize === "number" &&
      (saved.fontSizeSet === true || saved.fontSize !== 18)
    ) {
      applyFontSize(saved.fontSize);
    }
    if (typeof saved.letterSpacing === "number" && saved.letterSpacing !== 0) {
      applyLetterSpacing(saved.letterSpacing);
    }
    if (typeof saved.wordSpacing === "number" && saved.wordSpacing !== 0) {
      applyWordSpacing(saved.wordSpacing);
    }
    if (saved.lineSpacingSet === true && typeof saved.lineSpacing === "number") {
      applyLineSpacing(saved.lineSpacing);
    }
    applySavedAppearanceForCurrentPage(saved);
    applyFontById(getEffectiveSavedFont(saved));
    if (saved.maskActive === true) createReadingMask();
  });
}

chrome.storage.onChanged.addListener((changes, areaName) => {
  if (areaName !== "sync") return;

  const preferencesChange = changes[PREFS_STORAGE_KEY];
  const changedPreferences = preferencesChange?.newValue;
  if (preferencesChange && !changedPreferences) {
    setActiveTtsSpeed(1);
  }
  if (changedPreferences && typeof changedPreferences.highlightColor === "string") {
    setActiveHighlightColor(changedPreferences.highlightColor);
  }
  if (changedPreferences && typeof changedPreferences.ttsSpeed === "number") {
    setActiveTtsSpeed(changedPreferences.ttsSpeed);
  }
  if (changedPreferences && typeof changedPreferences.selectedVoice === "string") {
    setActiveTtsVoice(changedPreferences.selectedVoice);
  }

  const voiceChange = changes[VOICE_STORAGE_KEY];
  if (voiceChange) {
    setActiveTtsVoice(voiceChange.newValue);
  }
});

  // Load saved keybindings from storage
  chrome.storage.sync.get("dyslexiaKeybindings", (result) => {
    if (result?.dyslexiaKeybindings) {
      activeKeybindings = { ...DEFAULT_KEYBINDINGS, ...result.dyslexiaKeybindings };
    }
  });

  function matchesBinding(e, binding) {
    return (
      e.key.toLowerCase() === binding.key.toLowerCase() &&
      e.altKey === binding.alt &&
      e.ctrlKey === binding.ctrl &&
      e.shiftKey === binding.shift
    );
  }

  document.addEventListener("keydown", (e) => {
    // Ignore if user is typing in an input/textarea
    const tag = document.activeElement?.tagName;
    if (tag === "INPUT" || tag === "TEXTAREA" || document.activeElement?.isContentEditable) return;

    if (matchesBinding(e, activeKeybindings.toggleMask)) {
      e.preventDefault();
      if (maskActive) removeReadingMask();
      else createReadingMask();
      return;
    }

    if (matchesBinding(e, activeKeybindings.toggleMagnifier)) {
      e.preventDefault();
      if (magnifierActive) removeMagnifier();
      else createMagnifier();
      return;
    }

    if (matchesBinding(e, activeKeybindings.highlight)) {
      e.preventDefault();
      const range = getHighlightRange();
      if (!range || range.toString().trim() === "") return;
      try {
        applyHighlightToRange(range, activeHighlightColor);
      } catch (error) {
        warnContentScript("Could not highlight the saved selection from a keybinding.", error);
      }
      return;
    }

    if (matchesBinding(e, activeKeybindings.readText)) {
      e.preventDefault();
      const text = savedSelectedText || savedRange?.toString()?.trim() || window.getSelection()?.toString()?.trim() || "";
      if (!text) return;
      speakContentText(text);
      return;
    }

    if (matchesBinding(e, activeKeybindings.stopTTS)) {
      e.preventDefault();
      stopContentTts();
      return;
    }

    if (matchesBinding(e, activeKeybindings.increaseFontSize)) {
      e.preventDefault();
      if (activeFontSize !== null) applyFontSize(Math.min(activeFontSize + 2, 36));
      return;
    }

    if (matchesBinding(e, activeKeybindings.decreaseFontSize)) {
      e.preventDefault();
      if (activeFontSize !== null) applyFontSize(Math.max(activeFontSize - 2, 12));
      return;
    }
  });

  // Listen for keybinding updates from popup
  chrome.runtime.onMessage.addListener((msg) => {
    if (msg?.type === "UPDATE_KEYBINDINGS") {
      activeKeybindings = { ...DEFAULT_KEYBINDINGS, ...msg.keybindings };
    }

    if (msg?.type === "UPDATE_HIGHLIGHT_COLOR") {
      setActiveHighlightColor(msg.color);
    }

    if (msg?.type === "UPDATE_TTS_SPEED") {
      setActiveTtsSpeed(msg.ttsSpeed);
    }

    if (msg?.type === "UPDATE_TTS_VOICE") {
      setActiveTtsVoice(msg.voiceName);
    }

    if (msg?.type === "STOP_TTS") {
      stopContentTts();
    }
  });

  const fontSizeObserver = new MutationObserver(() => {
    if (activeFontSize !== null) {
      overrideInlineFontSizes(activeFontSize);
    }
  });
  const backgroundObserver = new MutationObserver(() => {
    refreshActiveBackgroundColor();
  });

  if (document.body) {
    backgroundObserver.observe(document.body, {
      childList: true,
      subtree: true,
    });
  }

  function overrideInlineFontSizes(size) {
    const walker = document.createTreeWalker(
      document.body,
      NodeFilter.SHOW_ELEMENT
    );
    let node = walker.nextNode();
    while (node) {
      if (node.closest && node.closest(EXCLUDED_REGION_SELECTORS)) {
        node = walker.nextNode();
        continue;
      }
      if (node.matches && !node.matches(CONTENT_STYLE_TARGET_SELECTORS)) {
        node = walker.nextNode();
        continue;
      }
      if (node.style && node.style.fontSize) {
        node.style.fontSize = "";
      }
      node = walker.nextNode();
    }
  }

  function applyFontSize(size) {
    activeFontSize = size;
    activeStyleState.fontSize = Math.abs(Number(size) - DEFAULT_PAGE_FONT_SIZE) > 0.01;

    let styleEl = document.getElementById("dyslexia-font-size-style");
    if (!styleEl) {
      styleEl = document.createElement("style");
      styleEl.id = "dyslexia-font-size-style";
      document.head.appendChild(styleEl);
    }

    styleEl.textContent = `
      ${CONTENT_STYLE_CSS_SELECTOR} {
        font-size: ${size}px !important;
      }
    `;

    overrideInlineFontSizes(size);

    fontSizeObserver.disconnect();
    fontSizeObserver.observe(document.body, {
      childList: true,
      subtree: true,
    });

    updateStyleIndicator();
  }

  function createReadingMask() {
    if (document.getElementById("dyslexia-reading-mask")) return;

    maskActive = true;

    const style = document.createElement("style");
    style.id = "dyslexia-mask-style";
    style.textContent = `
      #dyslexia-reading-mask {
        position: fixed;
        top: 0;
        left: 0;
        width: 100vw;
        height: 100vh;
        pointer-events: none;
        z-index: 999999;
      }
    `;
    document.head.appendChild(style);

    const canvas = document.createElement("canvas");
    canvas.id = "dyslexia-reading-mask";
    document.body.appendChild(canvas);

    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    let mouseY = window.innerHeight / 2;
    const maskHeight = 80;

    function drawMask(y) {
      const ctx = canvas.getContext("2d");
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.fillStyle = "rgba(0, 0, 0, 0.6)";
      ctx.fillRect(0, 0, canvas.width, y - maskHeight / 2);
      ctx.fillRect(0, y + maskHeight / 2, canvas.width, canvas.height);
    }

    drawMask(mouseY);

    function onMouseMove(e) {
      mouseY = e.clientY;
      drawMask(mouseY);
    }

    function onResize() {
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
      drawMask(mouseY);
    }

    canvas._onMouseMove = onMouseMove;
    canvas._onResize = onResize;

    window.addEventListener("mousemove", onMouseMove);
    window.addEventListener("resize", onResize);
  }

  function removeReadingMask() {
    maskActive = false;

    const canvas = document.getElementById("dyslexia-reading-mask");
    if (canvas) {
      window.removeEventListener("mousemove", canvas._onMouseMove);
      window.removeEventListener("resize", canvas._onResize);
      canvas.remove();
    }

    const style = document.getElementById("dyslexia-mask-style");
    if (style) style.remove();
  }

  function unwrapMarksByClass(className) {
    document.querySelectorAll(`mark.${className}`).forEach((el) => {
      const parent = el.parentNode;
      if (!parent) return;
      while (el.firstChild) {
        parent.insertBefore(el.firstChild, el);
      }
      parent.removeChild(el);
      parent.normalize();
    });
  }

  function unwrapMarkElement(markElement) {
    const parent = markElement.parentNode;
    if (!parent) return;
    while (markElement.firstChild) {
      parent.insertBefore(markElement.firstChild, markElement);
    }
    parent.removeChild(markElement);
    parent.normalize();
  }

  function clearSelectionHighlights() {
    const highlightCount = document.querySelectorAll("mark.dyslexia-highlight").length;
    unwrapMarksByClass("dyslexia-highlight");
    return highlightCount;
  }

  function getSavedOrCurrentSelectionRange() {
    const selection = window.getSelection();
    if (selection && selection.rangeCount > 0 && selection.toString().trim() !== "") {
      return selection.getRangeAt(0);
    }
    return savedRange;
  }

  function clearSelectedHighlights() {
    const range = getSavedOrCurrentSelectionRange();
    if (!range || range.toString().trim() === "") {
      return { success: false, error: "Select highlighted text on the page first." };
    }

    const matchingMarks = Array.from(document.querySelectorAll("mark.dyslexia-highlight")).filter((mark) => {
      try {
        return range.intersectsNode(mark);
      } catch {
        return false;
      }
    });

    if (matchingMarks.length === 0) {
      return { success: true, count: 0 };
    }

    matchingMarks.forEach((mark) => unwrapMarkElement(mark));

    const selection = window.getSelection();
    selection?.removeAllRanges();
    savedRange = null;

    return { success: true, count: matchingMarks.length };
  }

  function normalizeWord(word) {
    return word.toLowerCase().replace(/^[^a-z]+|[^a-z]+$/gi, "");
  }

  function isComplexWord(word, minLength) {
    const clean = normalizeWord(word);
    if (!clean) return false;
    if (clean.length >= minLength) return true;
    return (
      clean.endsWith("tion") ||
      clean.endsWith("sion") ||
      clean.endsWith("ment") ||
      clean.endsWith("ality") ||
      clean.endsWith("ically")
    );
  }

  function getEligibleTextNodes() {
    const textNodes = [];
    const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, {
      acceptNode(node) {
        if (!node.nodeValue || !node.nodeValue.trim()) return NodeFilter.FILTER_REJECT;
        const parent = node.parentElement;
        if (!parent) return NodeFilter.FILTER_REJECT;
        const tag = parent.tagName;
        if (tag === "SCRIPT" || tag === "STYLE" || tag === "NOSCRIPT" ||
            tag === "TEXTAREA" || tag === "INPUT" || tag === "OPTION") {
          return NodeFilter.FILTER_REJECT;
        }
        if (
          parent.isContentEditable ||
          parent.closest("script, style, noscript, textarea, input, option") ||
          parent.closest("mark.dyslexia-highlight") ||
          parent.closest(`mark.${COMPLEX_WORD_CLASS}`)
        ) {
          return NodeFilter.FILTER_REJECT;
        }
        return NodeFilter.FILTER_ACCEPT;
      },
    });

    let current = walker.nextNode();
    while (current) {
      textNodes.push(current);
      current = walker.nextNode();
    }
    return textNodes;
  }

  function highlightComplexWords(color, minLength) {
    unwrapMarksByClass(COMPLEX_WORD_CLASS);

    const textNodes = getEligibleTextNodes();
    let highlightCount = 0;

    textNodes.forEach((textNode) => {
      const text = textNode.nodeValue;
      if (!text || !/[a-z]/i.test(text)) return;

      const regex = /\b([A-Za-z][A-Za-z'-]*)\b/g;
      let match;
      let lastIndex = 0;
      let changed = false;
      const fragment = document.createDocumentFragment();

      while ((match = regex.exec(text)) !== null) {
        const [fullMatch, word] = match;
        const start = match.index;
        const end = start + fullMatch.length;

        if (start > lastIndex) {
          fragment.appendChild(document.createTextNode(text.slice(lastIndex, start)));
        }

        if (isComplexWord(word, minLength)) {
          const mark = document.createElement("mark");
          mark.className = COMPLEX_WORD_CLASS;
          mark.style.backgroundColor = color;
          mark.style.color = "inherit";
          mark.textContent = fullMatch;
          fragment.appendChild(mark);
          highlightCount += 1;
          changed = true;
        } else {
          fragment.appendChild(document.createTextNode(fullMatch));
        }

        lastIndex = end;
      }

      if (!changed) return;
      if (lastIndex < text.length) {
        fragment.appendChild(document.createTextNode(text.slice(lastIndex)));
      }
      textNode.parentNode.replaceChild(fragment, textNode);
    });

    return highlightCount;
  }

  function createMagnifier() {
    if (document.getElementById("dyslexia-magnifier-container")) return;

    magnifierActive = true;

    const SIZE = 200;
    const HALF = SIZE / 2;
    let mouseX = window.innerWidth / 2;
    let mouseY = window.innerHeight / 2;
    let rafId = null;

    // ── Styles ─────────────────────────────────────────────────────────────
    const style = document.createElement("style");
    style.id = "dyslexia-magnifier-style";
    style.textContent = `
      #dyslexia-magnifier-container {
        position: fixed;
        pointer-events: none;
        z-index: 2147483647;
        width: ${SIZE}px;
        height: ${SIZE}px;
        border: 3px solid #7F77DD;
        border-radius: 50%;
        box-shadow: 0 4px 24px rgba(0,0,0,0.4);
        overflow: hidden;
        background: white;
        display: none;
      }
      #dyslexia-magnifier-lens {
        position: absolute;
        top: 0;
        left: 0;
        transform-origin: 0 0;
        pointer-events: none;
        overflow: hidden;
      }
      #dyslexia-magnifier-lens #dyslexia-magnifier-container,
      #dyslexia-magnifier-lens #dyslexia-mask-style,
      #dyslexia-magnifier-lens #dyslexia-reading-mask {
        display: none !important;
      }
    `;
    document.head.appendChild(style);

    const container = document.createElement("div");
    container.id = "dyslexia-magnifier-container";
    container.style.setProperty("background-color", getResolvedPageBackgroundColor(), "important");
    document.body.appendChild(container);

    const lens = document.createElement("div");
    lens.id = "dyslexia-magnifier-lens";
    container.appendChild(lens);

    // ── Full-body clone ─────────────────────────────────────────────────────
    // Clone the entire body so every element retains its true document position.
    // This is the only reliable way to show the correct content at the cursor—
    // cloning a single element loses parent layout context (padding, flex/grid,
    // containing-block offsets) causing the vertical-offset bug.
    function buildBodyClone() {
      // Snapshot scroll positions of every scrollable element BEFORE cloning,
      // so we can replay them on the clone. This handles sites (like sc.edu)
      // that scroll an inner <div> or <main> rather than the window.
      const scrollSnapshots = new Map();
      document.body.querySelectorAll("*").forEach(el => {
        if (el.scrollTop !== 0 || el.scrollLeft !== 0) {
          scrollSnapshots.set(el, { top: el.scrollTop, left: el.scrollLeft });
        }
      });

      const bodyClone = document.body.cloneNode(true);

      // Remove extension UI from the clone
      bodyClone.querySelectorAll(
        "#dyslexia-magnifier-container, #dyslexia-magnifier-style, " +
        "#dyslexia-mask-style, #dyslexia-reading-mask, script"
      ).forEach(n => n.remove());

      // Stop animations so the clone stays static
      bodyClone.querySelectorAll("*").forEach(el => {
        if (!el.style || typeof el.style.setProperty !== "function") return;
        el.style.setProperty("animation", "none", "important");
        el.style.setProperty("transition", "none", "important");
      });

      // Replay scroll offsets onto cloned elements so their content position
      // matches the real page. We match by position in the DOM tree.
      const realEls = Array.from(document.body.querySelectorAll("*"));
      const cloneEls = Array.from(bodyClone.querySelectorAll("*"));
      realEls.forEach((realEl, i) => {
        const snap = scrollSnapshots.get(realEl);
        if (snap && cloneEls[i]) {
          cloneEls[i].scrollTop  = snap.top;
          cloneEls[i].scrollLeft = snap.left;
        }
      });

      // Position the body clone to match body's actual rendered offset.
      // body.getBoundingClientRect().top is negative when scrolled (e.g. -800).
      // We set top/left to those values so the clone aligns with the real page.
      const bodyRect = document.body.getBoundingClientRect();
      const cs = window.getComputedStyle(document.body);
      const pageBackgroundColor = getResolvedPageBackgroundColor();
      bodyClone.style.cssText = "";
      bodyClone.style.setProperty("margin",          "0",                "important");
      bodyClone.style.setProperty("padding",         cs.padding,         "important");
      bodyClone.style.setProperty("background-color",pageBackgroundColor, "important");
      bodyClone.style.setProperty("color",           cs.color,           "important");
      bodyClone.style.setProperty("font-family",     cs.fontFamily,      "important");
      bodyClone.style.setProperty("font-size",       cs.fontSize,        "important");
      bodyClone.style.setProperty("line-height",     cs.lineHeight,      "important");
      bodyClone.style.setProperty("overflow",        "hidden",           "important");
      bodyClone.style.setProperty("pointer-events",  "none",             "important");
      bodyClone.style.setProperty("position",        "absolute",         "important");
      // Anchor the clone at (0,0) in the lens — pan math handles the offset
      bodyClone.style.setProperty("top",  "0", "important");
      bodyClone.style.setProperty("left", "0", "important");
      bodyClone.style.setProperty("width",  document.documentElement.scrollWidth  + "px", "important");
      bodyClone.style.setProperty("height", document.documentElement.scrollHeight + "px", "important");

      return bodyClone;
    }

    let bodyClone = buildBodyClone();
    lens.style.width  = document.documentElement.scrollWidth  + "px";
    lens.style.height = document.documentElement.scrollHeight + "px";
    lens.appendChild(bodyClone);

    // ── Pan math ────────────────────────────────────────────────────────────
    // The lens div is a scaled copy of the full document.
    // We want the point under the real cursor — at absolute document position
    // (pageX, pageY) = (mouseX + scrollX, mouseY + scrollY) — to appear
    // at the centre of the bubble (HALF, HALF).
    //
    // After transform: scale(level) from origin (0,0), the document point
    // (pageX, pageY) maps to (pageX*level, pageY*level) in lens space.
    // To place that at (HALF, HALF) we translate by:
    //   tx = HALF - pageX * level
    //   ty = HALF - pageY * level
    function updateTransform() {
      const level = magnificationLevel;

      container.style.left = (mouseX - HALF) + "px";
      container.style.top  = (mouseY - HALF) + "px";
      container.style.display = "block";

      // Use getBoundingClientRect on the body to find how far the document
      // has scrolled in *any* scroll container — window OR inner divs.
      // bodyRect.top is negative when scrolled down (e.g. -800 means 800px scrolled).
      // The cursor's absolute position in the document is therefore:
      //   pageX = mouseX - bodyRect.left
      //   pageY = mouseY - bodyRect.top
      // This works even on sites like sc.edu that scroll an inner container
      // rather than the window, where window.scrollY would return 0.
      const bodyRect = document.body.getBoundingClientRect();
      const pageX = mouseX - bodyRect.left;
      const pageY = mouseY - bodyRect.top;

      const tx = HALF - pageX * level;
      const ty = HALF - pageY * level;

      lens.style.transform = "translate(" + tx + "px, " + ty + "px) scale(" + level + ")";
    }

    // ── Event handlers ──────────────────────────────────────────────────────
    function onMouseMove(e) {
      mouseX = e.clientX;
      mouseY = e.clientY;
      if (rafId) cancelAnimationFrame(rafId);
      rafId = requestAnimationFrame(updateTransform);
    }

    function onViewportChange() {
      if (rafId) cancelAnimationFrame(rafId);
      rafId = requestAnimationFrame(updateTransform);
    }

    // Rebuild clone after scroll stops so freshly-visible content appears
    let rebuildTimer = null;
    function onScroll() {
      updateTransform();
      clearTimeout(rebuildTimer);
      rebuildTimer = setTimeout(function() {
        const fresh = buildBodyClone();
        const sw = document.documentElement.scrollWidth  + "px";
        const sh = document.documentElement.scrollHeight + "px";
        lens.style.width  = sw;
        lens.style.height = sh;
        lens.replaceChildren(fresh);
        bodyClone = fresh;
        updateTransform();
      }, 200);
    }

    container._onMouseMove      = onMouseMove;
    container._onViewportChange = onViewportChange;
    container._refreshMagnifier = updateTransform;

    window.addEventListener("mousemove", onMouseMove, { passive: true });
    window.addEventListener("scroll",    onScroll,    { passive: true, capture: true });
    window.addEventListener("resize",    onViewportChange);

    updateTransform();
  }

  function removeMagnifier() {
    magnifierActive = false;

    const container = document.getElementById("dyslexia-magnifier-container");
    if (container) {
      window.removeEventListener("mousemove", container._onMouseMove);
      window.removeEventListener("scroll", container._onViewportChange, true);
      window.removeEventListener("resize", container._onViewportChange);
      container.remove();
    }

    const style = document.getElementById("dyslexia-magnifier-style");
    if (style) style.remove();
  }

  // --- Message Listener ---
  chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {

    if (msg?.type === "SET_STYLE") {
      if (typeof msg.fontSize === "number") {
        applyFontSize(msg.fontSize);
      }
    }

    if (msg?.type === "SET_WORD_SPACING") {
     const value = msg.wordSpacing || 0;
     applyWordSpacing(value);
    }

    if (msg?.type === "SET_BACKGROUND_COLOR") {
      const value = msg.backgroundColor || "";
      applyBackgroundColor(value);
    }

    if (msg?.type === "SET_TEXT_COLOR") {
      const value = msg.textColor || "";
      applyTextColor(value);
    }

    if (msg?.type === "SET_HIGH_CONTRAST_THEME") {
      const bg = msg.background;
      const text = msg.text;
      applyBackgroundColor(bg);
      applyTextColor(text);
    }

    // --- Highlight Selected Text ---
    if (msg?.type === "HIGHLIGHT_SELECTION") {
      const color = normalizeHighlightColor(msg.color);
      setActiveHighlightColor(color);
      const range = getHighlightRange();

      if (!range || range.toString().trim() === "") {
        sendResponse({ success: false, error: "No text selected" });
        return true;
      }

      try {
        applyHighlightToRange(range, color);
        sendResponse({ success: true });
      } catch (error) {
        sendResponse({ success: false, error: getErrorMessage(error) });
      }
      return true;
    }

    if (msg?.type === "TOGGLE_READING_MASK") {
      if (maskActive) {
        removeReadingMask();
        sendResponse({ active: false });
      } else {
        createReadingMask();
        sendResponse({ active: true });
      }
      return true;
    }

    if (msg?.type === "CLEAR_HIGHLIGHTS") {
      try {
        const count = clearSelectionHighlights();
        sendResponse({ success: true, count });
      } catch (error) {
        sendResponse({ success: false, error: getErrorMessage(error) });
      }
      return true;
    }

    if (msg?.type === "CLEAR_SELECTED_HIGHLIGHT") {
      try {
        sendResponse(clearSelectedHighlights());
      } catch (error) {
        sendResponse({ success: false, error: getErrorMessage(error) });
      }
      return true;
    }

    if (msg?.type === "HIGHLIGHT_COMPLEX_WORDS") {
      try {
        const color = msg.color || "#ffd54f";
        const minLength = Number(msg.minLength) || 10;
        const count = highlightComplexWords(color, minLength);
        sendResponse({ success: true, count });
      } catch (error) {
        sendResponse({ success: false, error: getErrorMessage(error) });
      }
      return true;
    }

    if (msg?.type === "CLEAR_COMPLEX_WORDS") {
      try {
        unwrapMarksByClass(COMPLEX_WORD_CLASS);
        sendResponse({ success: true });
      } catch (error) {
        sendResponse({ success: false, error: getErrorMessage(error) });
      }
      return true;
    }

    if (msg?.type === "TOGGLE_MAGNIFIER") {
      if (magnifierActive) {
        removeMagnifier();
        sendResponse({ active: false });
      } else {
        magnificationLevel = msg.magnificationLevel || 1.5;
        createMagnifier();
        sendResponse({ active: true });
      }
      return true;
    }

    if (msg?.type === "UPDATE_MAGNIFICATION_LEVEL") {
      magnificationLevel = msg.magnificationLevel || 1.5;
      if (magnifierActive) {
        const container = document.getElementById("dyslexia-magnifier-container");
        if (container && container._refreshMagnifier) {
          container._refreshMagnifier();
        }
      }
      return true;
    }

    if (msg?.type === "RESET_PAGE") {
      stopContentTts();
      setActiveTtsSpeed(1);
      activeFontSize = null;
      savedRange = null;
      savedSelectedText = "";
      Object.keys(activeStyleState).forEach((key) => {
        activeStyleState[key] = false;
      });
      fontSizeObserver.disconnect();
      removeReadingMask();
      removeMagnifier();

      const fontStyle = document.getElementById("dyslexia-font-style");
      if (fontStyle) fontStyle.remove();
      const fontSizeStyle = document.getElementById("dyslexia-font-size-style");
      if (fontSizeStyle) fontSizeStyle.remove();

      document.querySelectorAll("mark.dyslexia-highlight").forEach((el) => {
        const parent = el.parentNode;
        while (el.firstChild) parent.insertBefore(el.firstChild, el);
        parent.removeChild(el);
      });

      unwrapMarksByClass(COMPLEX_WORD_CLASS);

      document.body.style.fontSize = "";
      document.body.style.lineHeight = "";
      document.body.style.letterSpacing = "";
      document.body.style.wordSpacing = "";
      document.body.style.fontFamily = "";
      document.body.style.color = "";
      document.documentElement.style.color = "";
      clearAppliedTextColor();
      clearAppliedBackgroundColor();
      activeBackgroundColor = "";

      document.querySelectorAll("p, span, div, li, a").forEach((el) => {
        el.style.letterSpacing = "";
        el.style.wordSpacing = "";
        el.style.lineHeight = "";
        el.style.fontFamily = "";
      });

      document
        .querySelectorAll(
          "[data-dyslexia-font-applied='true'], " +
          "[data-dyslexia-letter-spacing-applied='true'], " +
          "[data-dyslexia-word-spacing-applied='true'], " +
          "[data-dyslexia-line-spacing-applied='true']"
        )
        .forEach((el) => {
          el.style.removeProperty("font-family");
          el.style.removeProperty("letter-spacing");
          el.style.removeProperty("word-spacing");
          el.style.removeProperty("line-height");
          el.removeAttribute("data-dyslexia-font-applied");
          el.removeAttribute("data-dyslexia-letter-spacing-applied");
          el.removeAttribute("data-dyslexia-word-spacing-applied");
          el.removeAttribute("data-dyslexia-line-spacing-applied");
        });

      updateStyleIndicator();
    }

    if (msg?.type === "APPLY_DYSLEXIA_FONT") {
      applySafeFont("open-dyslexic", FONT_OPTIONS);
      activeStyleState.font = true;
      updateStyleIndicator();
    }

    if (msg?.type === "REMOVE_DYSLEXIA_FONT") {
      clearAppliedFontStyles();
      activeStyleState.font = false;
      updateStyleIndicator();
    }

    if (msg?.type === "SET_FONT") {
      const fontId = msg.fontId;
      const fontOptions = msg.fontOptions || [];
      applySafeFont(fontId, fontOptions);
      activeStyleState.font = fontId !== "system";
      updateStyleIndicator();
    }

    if (msg?.type === "SET_LETTER_SPACING") {
      const value = msg.letterSpacing || 0;
      applyLetterSpacing(value);
    }

    if (msg?.type === "GET_SELECTION_TEXT") {
      let selectedText = "";

      try {
        selectedText = window.getSelection()?.toString()?.trim() || "";
      } catch (e) {
        selectedText = "";
      }

      if (!selectedText) {
        try {
          selectedText = savedRange ? savedRange.toString().trim() : "";
        } catch (e) {
          selectedText = "";
        }
      }

      if (!selectedText && savedSelectedText) {
        selectedText = savedSelectedText;
      }

      sendResponse({ selectedText });
      return true;
    }

    if (msg?.type === "GET_LINE_SPACING") {
      const computedStyle = window.getComputedStyle(document.body);
      const lineHeight = computedStyle.lineHeight;
      let lineHeightValue = 1.0;
      if (lineHeight && lineHeight !== "normal") {
        if (lineHeight.includes("px")) {
          const pxValue = parseFloat(lineHeight);
          const fontSizePx = parseFloat(computedStyle.fontSize) || 16;
          lineHeightValue = fontSizePx > 0 ? pxValue / fontSizePx : 1.0;
        } else {
          lineHeightValue = parseFloat(lineHeight);
        }
      }
      sendResponse({ lineSpacing: lineHeightValue });
      return;
    }

  if (msg?.type === "SET_LINE_SPACING") {
    const value = msg.lineSpacing || 1.0;
    applyLineSpacing(value);
    }
  });

  installPageNavigationWatcher();

  if (document.readyState === "loading") {
  document.addEventListener("DOMContentLoaded", restoreSavedPreferences, { once: true });
} else {
  restoreSavedPreferences();
}
})();
