Amazon Full View

Automatically expand all collapsed sections, truncated text, and FAQ accordions on Amazon product pages

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Amazon Full View
// @namespace    http://tampermonkey.net/
// @version      3.0
// @description  Automatically expand all collapsed sections, truncated text, and FAQ accordions on Amazon product pages
// @author       zhaozyu with kiro
// @homepageURL  https://greasyfork.org/en/scripts/580063-amazon-dp-auto-expand-collapsed-sections
// @include      https://www.amazon.*/dp/*
// @include      https://www.amazon.*/*/dp/*
// @include      https://www.amazon.*/gp/product/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function () {
  "use strict";

  /**
   * Global capture-phase click interceptor.
   * 1. Blocks Amazon's delegated popover handler from firing on truncated
   *    attribute "See more" links in the product overview area.
   * 2. Blocks programmatic clicks on "詳細はこちら" navigation links.
   */
  document.addEventListener(
    "click",
    (e) => {
      const target = e.target.closest ? e.target.closest("a") : null;
      if (!target) return;

      // Block any click on links containing skip text (e.g. "詳細はこちら")
      const targetText = target.textContent.trim();
      if (SKIP_TEXTS.some((t) => targetText.includes(t))) {
        // Only block programmatic clicks (isTrusted=false), allow real user clicks
        if (!e.isTrusted) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          return;
        }
      }

      // Only intercept links inside product overview truncate areas
      const overviewArea = target.closest(
        "#productOverview_feature_div, #poExpander, .po-expander, " +
        "#productFactsDesktop_Section, .product-facts"
      );
      if (!overviewArea) return;

      // Check if this link is a popover trigger or inside a truncate cell
      const isPopoverTrigger =
        target.hasAttribute("data-a-popover") ||
        target.getAttribute("data-action") === "a-popover";
      const cell = target.closest("td");
      const hasTruncate = cell && cell.querySelector(
        ".a-truncate, .a-truncate-full, .a-truncate-cut"
      );

      if (isPopoverTrigger || hasTruncate) {
        const text = target.textContent.trim();
        const expandTexts = ["表示を増やす", "See more", "Show more", "Read more"];
        if (expandTexts.some((t) => text.includes(t))) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          // Trigger inline expansion instead
          expandTruncatedCellsInline();
        }
      }
    },
    true // capture phase - fires before Amazon's handlers
  );

  /**
   * Expand truncated attribute cells inline instead of opening a popover/modal.
   * Targets the product overview table where fields like "特徴" are cut off
   * with a "表示を増やす" link that normally triggers a popup.
   */
  function expandTruncatedCellsInline() {
    // Target all a-truncate containers in the product overview area
    const truncateContainers = document.querySelectorAll(
      [
        "#productOverview_feature_div .a-truncate",
        "#poExpander .a-truncate",
        ".po-expander .a-truncate",
        "#productFactsDesktop_Section .a-truncate",
        ".product-facts .a-truncate",
        'table.a-normal .a-truncate',
        'table.prodDetTable .a-truncate',
      ].join(",")
    );

    truncateContainers.forEach((container) => {
      // Skip if already processed
      if (container.dataset.inlineExpanded === "true") return;

      const fullTextEl = container.querySelector(".a-truncate-full");
      const cutTextEl = container.querySelector(".a-truncate-cut");

      if (fullTextEl && cutTextEl) {
        // Show the full text element and hide the cut version
        fullTextEl.style.display = "inline";
        fullTextEl.classList.remove("a-offscreen");
        cutTextEl.style.display = "none";

        // Remove the max-height / overflow constraints on the container
        container.style.maxHeight = "none";
        container.style.overflow = "visible";
        container.style.webkitLineClamp = "unset";
        container.classList.remove("a-truncate");

        // Hide the "表示を増やす" / "See more" link
        const seeMoreLink = container.querySelector(
          ".a-truncate-see-more, .a-declarative"
        );
        if (seeMoreLink) {
          seeMoreLink.style.display = "none";
        }

        // Also remove any popover trigger attributes to prevent modal
        const popoverTriggers = container.querySelectorAll(
          '[data-action="a-popover"], [data-a-popover]'
        );
        popoverTriggers.forEach((trigger) => {
          trigger.remove();
        });

        // Remove the "表示を増やす" link entirely from the container
        const seeMoreLinks = container.querySelectorAll(
          'a[href="javascript:void(0)"], .a-truncate-see-more, .a-declarative'
        );
        seeMoreLinks.forEach((link) => {
          link.remove();
        });

        container.dataset.inlineExpanded = "true";
      }
    });

    // Also handle the case where the "See more" link is outside the truncate
    // container but within the same table cell (td)
    const overviewCells = document.querySelectorAll(
      [
        "#productOverview_feature_div td",
        "#poExpander td",
        ".po-expander td",
        "#productFactsDesktop_Section td",
        ".product-facts td",
      ].join(",")
    );

    overviewCells.forEach((cell) => {
      if (cell.dataset.inlineExpanded === "true") return;

      const truncateEl = cell.querySelector(".a-truncate");
      if (!truncateEl) return;

      const fullTextEl = truncateEl.querySelector(".a-truncate-full");
      const cutTextEl = truncateEl.querySelector(".a-truncate-cut");

      if (fullTextEl && cutTextEl) {
        fullTextEl.style.display = "inline";
        fullTextEl.classList.remove("a-offscreen");
        cutTextEl.style.display = "none";

        truncateEl.style.maxHeight = "none";
        truncateEl.style.overflow = "visible";
        truncateEl.classList.remove("a-truncate");

        // Remove any "表示を増やす" popover links from the cell entirely
        const expandLinks = cell.querySelectorAll(
          'a[data-action="a-popover"], a[data-a-popover], .a-truncate-see-more, a[href="javascript:void(0)"]'
        );
        expandLinks.forEach((link) => {
          link.remove();
        });

        cell.dataset.inlineExpanded = "true";
      }
    });
  }

  /**
   * Intercept and block popover triggers for truncated product overview cells.
   * This prevents the modal from appearing even if the link is clicked.
   */
  function blockTruncatePopovers() {
    const popoverLinks = document.querySelectorAll(
      [
        '#productOverview_feature_div a[data-action="a-popover"]',
        '#poExpander a[data-action="a-popover"]',
        '.po-expander a[data-action="a-popover"]',
        '#productFactsDesktop_Section a[data-action="a-popover"]',
        '.product-facts a[data-action="a-popover"]',
      ].join(",")
    );

    popoverLinks.forEach((link) => {
      if (link.dataset.popoverBlocked === "true") return;

      // Check if this link is inside or near a truncate container
      const cell = link.closest("td") || link.closest("tr");
      if (!cell) return;

      const hasTruncate = cell.querySelector(
        ".a-truncate, .a-truncate-full, .a-truncate-cut"
      );
      if (!hasTruncate) return;

      // Block the popover by capturing click and stopping propagation
      link.addEventListener(
        "click",
        (e) => {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();

          // Instead of popover, expand inline
          expandTruncatedCellsInline();
        },
        true
      );

      link.dataset.popoverBlocked = "true";
    });
  }

  /**
   * Text patterns that indicate a link/expander should NOT be auto-expanded.
   * These are navigation or info links, not content expanders.
   */
  const SKIP_TEXTS = ["詳細はこちら", "Details here", "Click here for details"];

  /**
   * Check if an element itself contains skip text.
   * Only checks the element's own direct text, not parent containers,
   * to avoid false positives on nearby expanders.
   */
  function shouldSkipElement(el) {
    // If already marked as skip, return immediately
    if (el.dataset.dpAutoSkip === "true") return true;

    // Check the element's own text content
    const text = el.textContent.trim();
    if (SKIP_TEXTS.some((t) => text.includes(t))) {
      el.dataset.dpAutoSkip = "true";
      return true;
    }

    // Check only the immediate expander content (sibling), not the whole page section
    const expanderContainer = el.closest(".a-expander-container");
    if (expanderContainer) {
      // Only check the expander-content div that is a direct child
      const contentDiv = expanderContainer.querySelector(
        ":scope > .a-expander-content"
      );
      if (contentDiv) {
        const contentText = contentDiv.textContent.trim();
        if (SKIP_TEXTS.some((t) => contentText.includes(t))) {
          el.dataset.dpAutoSkip = "true";
          return true;
        }
      }
    }

    return false;
  }

  /**
   * Track elements we've already clicked to prevent repeated expansion.
   */
  const clickedElements = new WeakSet();

  /**
   * Pre-scan and mark all elements that should never be auto-expanded.
   * This runs before expandAllSections to block "詳細はこちら" etc.
   */
  function markSkipElements() {
    // Find all expander headers/links that contain skip text
    const allExpanders = document.querySelectorAll(
      '.a-expander-header, .a-expander-prompt, .a-expander-partial-collapse-header'
    );
    allExpanders.forEach((el) => {
      const text = el.textContent.trim();
      if (SKIP_TEXTS.some((t) => text.includes(t))) {
        el.dataset.dpAutoSkip = "true";
        clickedElements.add(el);
      }
    });

    // Find ALL links that contain skip text (covers non-expander <a> tags)
    const allLinks = document.querySelectorAll("a");
    allLinks.forEach((link) => {
      const text = link.textContent.trim();
      if (SKIP_TEXTS.some((t) => text.includes(t))) {
        link.dataset.dpAutoSkip = "true";
        clickedElements.add(link);
      }
    });

    // Also find links whose expander-content contains skip text
    const allContainers = document.querySelectorAll(".a-expander-container");
    allContainers.forEach((container) => {
      const contentText = container.textContent;
      if (SKIP_TEXTS.some((t) => contentText.includes(t))) {
        const header = container.querySelector(
          ".a-expander-header, .a-expander-prompt"
        );
        if (header) {
          header.dataset.dpAutoSkip = "true";
          clickedElements.add(header);
        }
      }
    });
  }

  /**
   * Safe click that prevents clicking on skip-marked elements.
   * Allows retry if the element is still collapsed (click didn't take effect).
   * Only permanently blocks elements marked as dpAutoSkip.
   */
  function safeClick(el) {
    if (el.dataset.dpAutoSkip === "true") return false;
    if (clickedElements.has(el)) {
      // If previously clicked, only skip if it's now expanded
      const isExpanded =
        el.getAttribute("aria-expanded") === "true" ||
        el.closest(".a-expander-container")?.classList.contains("a-expander-expanded") ||
        el.closest(".a-expander-partial-collapse-container")?.classList.contains("a-expander-inline-expanded");
      if (isExpanded) return false;
      // Otherwise allow retry
    }
    clickedElements.add(el);
    el.click();
    return true;
  }

  /**
   * Force-expand all A+ FAQ module Q&A panels.
   * Amazon A+ Premium FAQ uses a custom structure:
   *   ul.faq-list > li.faq-block > span[data-faq-question][aria-expanded]
   *   Answer panels are controlled via aria-controls pointing to answer element IDs.
   * The accordion behavior only allows one open at a time.
   * We force all panels open and disable the exclusive behavior.
   */
  function forceExpandAplusAccordions() {
    // Target A+ FAQ question triggers
    const faqQuestions = document.querySelectorAll(
      'span[data-faq-question], [data-faq-question]'
    );

    faqQuestions.forEach((trigger) => {
      const isExpanded = trigger.getAttribute("aria-expanded") === "true";
      if (isExpanded) return;

      // Get the answer element via aria-controls
      const controlsId = trigger.getAttribute("aria-controls");
      const answerEl = controlsId ? document.getElementById(controlsId) : null;

      // Force expand
      trigger.setAttribute("aria-expanded", "true");

      if (answerEl) {
        answerEl.style.display = "block";
        answerEl.style.visibility = "visible";
        answerEl.style.height = "auto";
        answerEl.style.maxHeight = "none";
        answerEl.style.overflow = "visible";
        answerEl.style.opacity = "1";
        answerEl.removeAttribute("hidden");
        answerEl.classList.remove("a-hidden");
      }

      // Also try to find the answer as a sibling
      const parentLi = trigger.closest("li");
      if (parentLi) {
        const answer = parentLi.querySelector(
          ".aplus-answer, .faq-answer, [class*='answer']"
        );
        if (answer) {
          answer.style.display = "block";
          answer.style.visibility = "visible";
          answer.style.height = "auto";
          answer.style.maxHeight = "none";
          answer.style.overflow = "visible";
          answer.style.opacity = "1";
        }
      }
    });

    // Also handle via CSS class on the faq-block list items
    const faqBlocks = document.querySelectorAll(
      "li.faq-block, .faq-block"
    );
    faqBlocks.forEach((block) => {
      block.classList.add("faq-expanded");
      // Show all child elements that might be hidden answers
      const hiddenChildren = block.querySelectorAll(
        '[style*="display: none"], [style*="display:none"], [hidden], .a-hidden'
      );
      hiddenChildren.forEach((child) => {
        child.style.display = "block";
        child.removeAttribute("hidden");
        child.classList.remove("a-hidden");
      });
    });

    // Disable the exclusive accordion behavior by removing event bindings
    // Clone and replace the FAQ list to strip all event listeners
    const faqLists = document.querySelectorAll("ul.faq-list");
    faqLists.forEach((list) => {
      if (list.dataset.accordionDisabled === "true") return;

      // Remove click handlers by cloning
      const clone = list.cloneNode(true);
      // Re-add click handlers that just toggle individual items
      clone.querySelectorAll("span[data-faq-question]").forEach((trigger) => {
        trigger.addEventListener("click", (e) => {
          e.stopPropagation();
          const expanded = trigger.getAttribute("aria-expanded") === "true";
          const controlsId = trigger.getAttribute("aria-controls");
          const answerEl = controlsId
            ? document.getElementById(controlsId)
            : null;

          if (expanded) {
            trigger.setAttribute("aria-expanded", "false");
            if (answerEl) answerEl.style.display = "none";
          } else {
            trigger.setAttribute("aria-expanded", "true");
            if (answerEl) {
              answerEl.style.display = "block";
              answerEl.style.height = "auto";
            }
          }
        });
      });

      clone.dataset.accordionDisabled = "true";
      list.parentNode.replaceChild(clone, list);
    });
  }

  function expandAllSections() {
    let expanded = 0;

    // =========================================================
    // 0. Pre-scan: mark elements that should never be expanded
    // =========================================================
    markSkipElements();

    // =========================================================
    // 0b. Expand truncated cells inline (prevent popover/modal)
    // =========================================================
    expandTruncatedCellsInline();
    blockTruncatePopovers();

    // =========================================================
    // 1. Product overview attributes table "表示を増やす" / "See more"
    //    Located in the product overview/facts section (top area)
    // =========================================================
    const overviewExpanders = document.querySelectorAll(
      [
        // Product facts "See more" / "表示を増やす"
        '#productOverview_feature_div .a-expander-header[aria-expanded="false"]',
        '#productOverview_feature_div a-expander-header[aria-expanded="false"]',
        '#poExpander .a-expander-header[aria-expanded="false"]',
        '.po-expander .a-expander-header[aria-expanded="false"]',
        // Product facts desktop section
        '#productFactsDesktop_Section .a-expander-header[aria-expanded="false"]',
        '.product-facts .a-expander-header[aria-expanded="false"]',
      ].join(",")
    );
    overviewExpanders.forEach((el) => {
      if (shouldSkipElement(el)) return;
      if (safeClick(el)) expanded++;
    });

    // =========================================================
    // 2. "この商品について" / "About this item" - "もっと見る" / "See more"
    //    The bullet point list that gets truncated
    // =========================================================
    const featureBulletExpanders = document.querySelectorAll(
      [
        '#featurebullets_feature_div .a-expander-header[aria-expanded="false"]',
        '#feature-bullets .a-expander-header[aria-expanded="false"]',
        '#featurebullets_feature_div a[data-action="a-expander-toggle"]',
      ].join(",")
    );
    featureBulletExpanders.forEach((el) => {
      if (shouldSkipElement(el)) return;
      const container = el.closest(".a-expander-partial-collapse-container");
      if (
        !container ||
        container.classList.contains("a-expander-collapsed") ||
        container.style.maxHeight
      ) {
        if (safeClick(el)) expanded++;
      }
    });

    // Also handle the partial-collapse style "もっと見る" links
    const partialCollapseHeaders = document.querySelectorAll(
      ".a-expander-partial-collapse-header"
    );
    partialCollapseHeaders.forEach((header) => {
      if (shouldSkipElement(header)) return;
      const container = header.closest(
        ".a-expander-partial-collapse-container"
      );
      if (container && container.classList.contains("a-expander-collapsed")) {
        if (safeClick(header)) expanded++;
      }
    });

    // =========================================================
    // 3. Bottom "商品情報" section - 商品詳細, サイズ, 機能と仕様, etc.
    //    These are the tabbed/accordion panels in the lower part of the page
    // =========================================================
    const productInfoExpanders = document.querySelectorAll(
      [
        // Product information section (bottom of page)
        '#productDetails_expanderSectionTriggers .a-expander-header[aria-expanded="false"]',
        '#prodDetails .a-expander-header[aria-expanded="false"]',
        '#detailBullets_feature_div .a-expander-header[aria-expanded="false"]',
        // "商品情報" accordion panels
        '#productDetails_feature_div .a-expander-header[aria-expanded="false"]',
        '#productDetails_db_sections .a-expander-header[aria-expanded="false"]',
        '#productDetails_techSpec_sections .a-expander-header[aria-expanded="false"]',
        // Newer layout with card-style panels
        '.product-details-grid .a-expander-header[aria-expanded="false"]',
        '#productDetails_expanderTrigger_702[aria-expanded="false"]',
      ].join(",")
    );
    productInfoExpanders.forEach((el) => {
      if (shouldSkipElement(el)) return;
      if (safeClick(el)) expanded++;
    });

    // =========================================================
    // 4. Generic: any remaining collapsed expanders in product area
    // =========================================================
    const allCollapsedHeaders = document.querySelectorAll(
      '.a-expander-header[aria-expanded="false"]'
    );
    allCollapsedHeaders.forEach((header) => {
      if (!isInProductArea(header)) return;
      if (shouldSkipElement(header)) return;
      if (safeClick(header)) expanded++;
    });

    // =========================================================
    // 4b. A+ Content Q&A sections (商品の説明 area)
    //     These are collapsible Q&A panels in the brand story / A+ section.
    //     Amazon uses an exclusive accordion (only one open at a time).
    //     We bypass the accordion logic by directly manipulating DOM/styles
    //     to force all panels open simultaneously.
    // =========================================================
    const aplusExpanders = document.querySelectorAll(
      [
        '#aplus_feature_div .a-expander-header[aria-expanded="false"]',
        '#aplus .a-expander-header[aria-expanded="false"]',
        '#aplusProductDescription .a-expander-header[aria-expanded="false"]',
        '#productDescription_feature_div .a-expander-header[aria-expanded="false"]',
        '.aplus-module .a-expander-header[aria-expanded="false"]',
        '[class*="aplus"] .a-expander-header[aria-expanded="false"]',
      ].join(",")
    );
    aplusExpanders.forEach((el) => {
      if (shouldSkipElement(el)) return;
      if (safeClick(el)) expanded++;
    });

    // Force-expand A+ accordion panels by directly manipulating DOM
    forceExpandAplusAccordions();

    // =========================================================
    // 5. Handle "表示を増やす" / "See more" text links specifically
    //    These may be <a> or <span> elements with specific text
    // =========================================================
    const allLinks = document.querySelectorAll(
      "a.a-expander-header, span.a-expander-header, .a-expander-prompt"
    );
    allLinks.forEach((link) => {
      // Skip links inside cells already expanded inline
      const parentCell = link.closest("td");
      if (parentCell && parentCell.dataset.inlineExpanded === "true") return;
      // Skip links that are popover triggers (handled by inline expansion)
      if (
        link.hasAttribute("data-a-popover") ||
        link.getAttribute("data-action") === "a-popover" ||
        link.dataset.popoverBlocked === "true"
      ) return;

      const text = link.textContent.trim();
      const expandTexts = [
        "表示を増やす",
        "表示件数を増やす",
        "さらに表示する",
        "もっと見る",
        "続きを見る",
        "See more",
        "Read more",
        "Show more",
        "Mehr anzeigen",
        "Ver más",
        "Voir plus",
        "Mostra di più",
        "Meer weergeven",
      ];
      // Skip navigation links
      if (shouldSkipElement(link)) return;

      if (expandTexts.some((t) => text.includes(t))) {
        const isAlreadyExpanded =
          link.getAttribute("aria-expanded") === "true" ||
          link
            .closest(".a-expander-container")
            ?.classList.contains("a-expander-expanded");
        if (!isAlreadyExpanded) {
          if (safeClick(link)) expanded++;
        }
      }
    });

    // =========================================================
    // 6. Product attribute table "表示を増やす" (truncated specs)
    //    The tech-spec / product-info table that truncates rows
    //    and shows a "表示を増やす ▼" link at the bottom.
    //    Uses various structures: a-truncate, showMoreLink, etc.
    // =========================================================

    // 6a. Handle a-truncate based "See more" in product tables
    const truncateToggles = document.querySelectorAll(
      [
        ".a-truncate-cut + .a-truncate-see-more",
        ".a-truncate .a-link-normal",
        '#technicalSpecifications_feature_div a[href="javascript:void(0)"]',
        '#productDetails_techSpec_section_1 a[href="javascript:void(0)"]',
      ].join(",")
    );
    truncateToggles.forEach((toggle) => {
      // Skip if already handled by inline expansion
      const parentCell = toggle.closest("td");
      if (parentCell && parentCell.dataset.inlineExpanded === "true") return;
      if (toggle.dataset.popoverBlocked === "true") return;

      const container = toggle.closest(".a-truncate");
      if (container && container.dataset.inlineExpanded === "true") return;
      if (container && container.querySelector(".a-truncate-cut")) {
        if (safeClick(toggle)) expanded++;
      }
    });

    // 6b. Handle "See more" / "表示を増やす" links that expand hidden table rows
    const tableExpanders = document.querySelectorAll(
      [
        "#poShowMoreLink",
        "#showMoreLink",
        ".po-show-more a",
        'a[data-action="po-show-more"]',
        '#productOverview_feature_div a[href="javascript:void(0)"]',
        ".a-size-small.po-truncate-link a",
      ].join(",")
    );
    tableExpanders.forEach((link) => {
      // Skip if already handled by inline expansion
      const parentCell = link.closest("td");
      if (parentCell && parentCell.dataset.inlineExpanded === "true") return;
      if (link.dataset.popoverBlocked === "true") return;
      if (
        link.hasAttribute("data-a-popover") ||
        link.getAttribute("data-action") === "a-popover"
      ) return;

      const text = link.textContent.trim();
      // Skip navigation links
      if (shouldSkipElement(link)) return;

      if (
        text.includes("表示を増やす") ||
        text.includes("表示件数を増やす") ||
        text.includes("さらに表示する") ||
        text.includes("See more") ||
        text.includes("Show more")
      ) {
        if (safeClick(link)) expanded++;
      }
    });

    // 6c. Broad fallback: find any clickable element with expand text
    //     inside product detail/overview tables that hasn't been handled
    const allClickables = document.querySelectorAll(
      [
        '#productOverview_feature_div a',
        '#productOverview_feature_div span[role="button"]',
        '#poExpander a',
        '.po-expander a',
        '#technicalSpecifications_feature_div a',
        '#productDetails_techSpec_section_1 a',
        'table.a-normal a',
        'table.prodDetTable a',
      ].join(",")
    );
    allClickables.forEach((el) => {
      // Skip if already handled by inline expansion
      const parentCell = el.closest("td");
      if (parentCell && parentCell.dataset.inlineExpanded === "true") return;
      if (el.dataset.popoverBlocked === "true") return;
      if (
        el.hasAttribute("data-a-popover") ||
        el.getAttribute("data-action") === "a-popover"
      ) return;

      const text = el.textContent.trim();
      // Skip navigation links
      if (shouldSkipElement(el)) return;

      if (text.includes("表示を増やす") || text.includes("表示件数を増やす") || text.includes("さらに表示する") || text.includes("See more")) {
        if (safeClick(el)) expanded++;
      }
    });

    return expanded;
  }

  /**
   * Check if an element is within the product content area
   * (excludes reviews, Q&A, recommendations, etc.)
   */
  function isInProductArea(el) {
    // Exclude areas we don't want to auto-expand
    const excludeSelectors = [
      "#reviewsMedley",
      "#ask-btf_feature_div",
      "#similarities_feature_div",
      "#sp_detail",
      "#sponsoredProducts",
      "#rhf",
      "#navFooter",
      '[data-feature-name="reviewFeatureGroup"]',
      "#smbLogoImg_feature_div",
      "#smb_feature_div",
      ".smb-container",
      "#olp_feature_div",
      "#moreBuyingChoices_feature_div",
      "#extraProductInfoTips_feature_div",
      "#productAlert_feature_div",
      "#legal_feature_div",
      "#globalStoreBadgePopover_feature_div",
      '[id*="smbLogo"]',
      '[id*="smb_"]',
    ];
    if (excludeSelectors.some((sel) => el.closest(sel))) {
      return false;
    }

    // Include areas we want to expand
    const includeSelectors = [
      "#centerCol",
      "#dp-container",
      "#productDetails_feature_div",
      "#detailBullets_feature_div",
      "#productFactsDesktop_Section",
      "#featurebullets_feature_div",
      "#feature-bullets",
      "#productOverview_feature_div",
      "#productDescription_feature_div",
      "#aplus_feature_div",
      "#prodDetails",
      "#productDetails_db_sections",
      "#productDetails_techSpec_sections",
      ".product-details-grid",
      '[id*="productDetail"]',
    ];
    return includeSelectors.some((sel) => el.closest(sel));
  }

  /**
   * Run expansion with retries to handle lazy-loaded content.
   * Uses increasing delays to catch content that loads later.
   */
  function runWithRetries() {
    // Multiple attempts at increasing intervals to catch lazy-loaded content
    const delays = [500, 1000, 1500, 2000, 3000, 4000, 6000, 9000];
    delays.forEach((delay) => {
      setTimeout(expandAllSections, delay);
    });
  }

  // Inject CSS to ensure inline-expanded text displays properly
  const style = document.createElement("style");
  style.textContent = `
    /* Force full text visible in product overview truncated cells */
    [data-inline-expanded="true"] .a-truncate-full,
    .a-truncate-full.dp-auto-expanded {
      display: inline !important;
      position: static !important;
      clip: auto !important;
      width: auto !important;
      height: auto !important;
      overflow: visible !important;
    }
    [data-inline-expanded="true"] .a-truncate-cut {
      display: none !important;
    }
    [data-inline-expanded="true"] .a-truncate-see-more,
    [data-inline-expanded="true"] a[data-action="a-popover"] {
      display: none !important;
    }
    /* Force A+ FAQ accordion panels to stay open */
    span[data-faq-question][aria-expanded="true"] ~ .aplus-answer,
    span[data-faq-question][aria-expanded="true"] ~ p.aplus-answer,
    li.faq-block .aplus-answer,
    li.faq-block [id^="faq_a_"] {
      display: block !important;
      visibility: visible !important;
      height: auto !important;
      max-height: none !important;
      overflow: visible !important;
      opacity: 1 !important;
    }
  `;
  document.head.appendChild(style);

  // Initial run after page load
  runWithRetries();

  // Observe DOM changes for dynamically loaded content
  const observer = new MutationObserver((mutations) => {
    let shouldExpand = false;
    for (const mutation of mutations) {
      if (mutation.addedNodes.length > 0) {
        for (const node of mutation.addedNodes) {
          if (node.nodeType === 1 && node.querySelector) {
            if (
              node.querySelector('[aria-expanded="false"]') ||
              node.querySelector(".a-expander-collapsed") ||
              node.querySelector(".a-expander-header") ||
              node.matches?.('[aria-expanded="false"]')
            ) {
              shouldExpand = true;
              break;
            }
          }
        }
      }
      if (shouldExpand) break;
    }

    if (shouldExpand) {
      setTimeout(expandAllSections, 500);
    }
  });

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