Bitcointalk Modern Theme

Modern light/dark/system theme for bitcointalk.org with separate customisable Main and Accent presets, plus an OFF mode.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Advertisement:

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

Advertisement:

// ==UserScript==
// @name         Bitcointalk Modern Theme
// @namespace    bitcointalk-modern-theme
// @version      3.2.2
// @description  Modern light/dark/system theme for bitcointalk.org with separate customisable Main and Accent presets, plus an OFF mode.
// @license      MIT
// @match        *://*.bitcointalk.org/*
// @match        *://bitcointalk.org/*
// @run-at       document-start
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function () {
  'use strict';

  // ===========================================================================
  // Configuration
  // ===========================================================================

  const MAIN_PRESETS_DARK = {
    slate: { name: 'Slate (default)', base: '#15171D' },
    midnight: { name: 'Midnight', base: '#0F1424' },
    forest: { name: 'Forest', base: '#0E1A18' },
    plum: { name: 'Plum', base: '#1A0F1F' },
    charcoal: { name: 'Charcoal', base: '#141414' },
  };
  const MAIN_PRESETS_LIGHT = {
    // v2.10.30: "Classic" preset rebuilt against the actual classic SMF
    // Bitcointalk theme. Body wash is now a near-neutral cool grey
    // (#EEF0F3) so the page reads as a desaturated grey-white rather than
    // an azure tint. The visible blue in the original lives only in two
    // places: the catbg/header band (#5B7FA0 navy) and the link colour
    // (#225588). Both are kept in their own rules below.
    sky: { name: 'Classic (default)', base: '#EEF0F3' },
    // v2.10.32: "Pearl" given a clear sky-blue identity (the visible azure
    // tint that the old "Classic" preset used to have, before v2.10.30
    // moved Classic to a neutral grey). New base #E5ECF3 + a clean blue
    // accent so the preset reads as "blue & white" rather than the
    // previous neutral-with-indigo-tint that came across as violet.
    pearl: { name: 'Sky Blue', base: '#E5ECF3' },
    cream: { name: 'Soft Cream', base: '#F4EFE6' },
    mint: { name: 'Soft Mint', base: '#EEF3EF' },
    rose: { name: 'Soft Rose', base: '#F5EEEE' },
  };

  const AUTO_ACCENT_MAP = {
    light: {
      // v2.10.28: classic SMF Bitcointalk uses the legendary #225588 blue
      // for links throughout. Switching the auto-accent for the Classic
      // preset to that exact hex pulls links, hover states and the active
      // nav tab much closer to the original look.
      sky:   '#225588',  // classic SMF Bitcointalk link blue
      // v2.10.32: pearl auto-accent moved from indigo (#3A3DAA, which read
       // as violet on the cool body wash) to a clean medium blue that pairs
       // naturally with the pearl base #E5ECF3.
      pearl: '#1E5FA8',  // clean medium blue
      cream: '#9A6842',  // muted rust
      mint:  '#4C735D',  // muted pine
      rose:  '#7B5662',  // muted plum
    },
    dark: {
      slate:    '#7F84D8', // muted periwinkle
      midnight: '#8E84CF', // muted lavender
      forest:   '#55B7A9', // muted aqua
      plum:     '#B779C2', // muted orchid
      charcoal: '#CC8E45', // muted amber
    },
  };

  const ACCENTS_DARK = {
    orange: { name: 'Bitcoin Orange', base: '#F7931A' },
  };
  const ACCENTS_LIGHT = {
    orange: { name: 'Bitcoin Orange', base: '#D97706' },
  };

  const DEFAULT_THEME = 'dark';
  const SCRIPT_VERSION = '3.2.2'; // build-injected from package.json (see build.mjs)
  const DEFAULT_ACCENT_DARK_KEY = 'orange';
  const DEFAULT_ACCENT_LIGHT_KEY = 'auto';
  const DEFAULT_MAIN_DARK_KEY = 'slate';
  const DEFAULT_MAIN_LIGHT_KEY = 'sky';
  const DEFAULT_CUSTOM_MAIN_DARK = '#15171D';
  const DEFAULT_CUSTOM_MAIN_LIGHT = '#F4F5F8';
  const DEFAULT_CUSTOM_ACCENT_DARK = '#F7931A';
  const DEFAULT_CUSTOM_ACCENT_LIGHT = '#5B7FA0';
  const DEFAULT_MOBILE_VERSION = 'off';

  const KEY_THEME = 'bt-modern-theme';
  const KEY_THEME_PRIOR = 'bt-modern-theme-prior';
  const KEY_MAIN_DARK = 'bt-modern-main-dark';
  const KEY_MAIN_LIGHT = 'bt-modern-main-light';
  const KEY_ACCENT_DARK = 'bt-modern-accent-dark';
  const KEY_ACCENT_LIGHT = 'bt-modern-accent-light';
  const KEY_MAIN_DARK_CUSTOM = 'bt-modern-main-dark-custom';
  const KEY_MAIN_LIGHT_CUSTOM = 'bt-modern-main-light-custom';
  const KEY_ACCENT_DARK_CUSTOM = 'bt-modern-accent-dark-custom';
  const KEY_ACCENT_LIGHT_CUSTOM = 'bt-modern-accent-light-custom';
  const KEY_MOBILE_VERSION = 'bt-modern-mobile-version';

  const ALL_KEYS = [
    KEY_THEME,
    KEY_THEME_PRIOR,
    KEY_MAIN_DARK, KEY_MAIN_LIGHT,
    KEY_ACCENT_DARK, KEY_ACCENT_LIGHT,
    KEY_MAIN_DARK_CUSTOM, KEY_MAIN_LIGHT_CUSTOM,
    KEY_ACCENT_DARK_CUSTOM, KEY_ACCENT_LIGHT_CUSTOM,
    KEY_MOBILE_VERSION,
  ];

  // Safari flash guard constants (integrated extreme-fast variant)
  const NAV_SELECTOR = '[class*="maintab_"] a, #main_menu a, .dropmenu a';
  const INCOMING_HOLD_STYLE_ID = 'bt-paint-hold';
  const OUTGOING_HOLD_STYLE_ID = 'bt-pre-nav-hold';
  const HOLD_REVEALED_ATTR = 'data-bt-revealed';
  const NAV_UNTIL_SESSION_KEY = 'bt-modern-nav-hold-until';
  const NAV_BG_SESSION_KEY = 'bt-modern-nav-hold-bg';
  const OUTGOING_CANCEL_RESTORE_MS = 120;
  const NAV_HOLD_SESSION_MS = 800;

  // ===========================================================================
  // Storage layer
  // ===========================================================================

  const hasGM = typeof GM_getValue === 'function' && typeof GM_setValue === 'function';

  function lsGet(key) {
    try { return localStorage.getItem(key); } catch (e) { return null; }
  }
  function lsSet(key, value) {
    try { localStorage.setItem(key, value); return true; } catch (e) { return false; }
  }
  function gmGet(key) {
    if (!hasGM) return null;
    try {
      const v = GM_getValue(key, null);
      return v == null ? null : String(v);
    } catch (e) { return null; }
  }
  function gmSet(key, value) {
    if (!hasGM) return false;
    try { GM_setValue(key, String(value)); return true; } catch (e) { return false; }
  }
  function ssGet(key) {
    try {
      const v = sessionStorage.getItem(key);
      return v == null || v === '' ? null : v;
    } catch (e) { return null; }
  }
  function ssSet(key, value) {
    try { sessionStorage.setItem(key, String(value)); } catch (e) {}
  }
  function ssRemove(key) {
    try { sessionStorage.removeItem(key); } catch (e) {}
  }

  function getPref(key, fallback) {
    let v = gmGet(key);
    if (v == null || v === '') v = lsGet(key);
    if (v == null || v === '') return fallback;
    return v;
  }

  function setPref(key, value) {
    const lsOk = lsSet(key, value);
    const gmOk = gmSet(key, value);
    return lsOk || gmOk;
  }

  function syncStores() {
    ALL_KEYS.forEach(k => {
      const ls = lsGet(k);
      const gm = gmGet(k);
      if (gm && (!ls || ls === '')) lsSet(k, gm);
      else if (ls && (!gm || gm === '') && hasGM) gmSet(k, ls);
    });
  }

  function isMobileLite() {
    return getPref(KEY_MOBILE_VERSION, DEFAULT_MOBILE_VERSION) === 'on';
  }

  function updateMobileLiteAttribute() {
    document.documentElement.setAttribute('data-bt-mobile-lite', isMobileLite() ? 'true' : 'false');
  }

  function updatePageAttribute() {
    let page = '';
    try {
      if (isMeritPage()) page = 'merit';
    } catch (e) {}
    if (page) document.documentElement.setAttribute('data-bt-page', page);
    else document.documentElement.removeAttribute('data-bt-page');
  }

  function isMeritPage() {
    try {
      const search = window.location.search || '';
      const params = new URLSearchParams(search);
      const action = (params.get('action') || '').toLowerCase();
      return action === 'merit' || action.indexOf('merit;') === 0 || /[?;&]action=merit(?:[;&]|$)/i.test(search);
    } catch (e) {
      return /[?;&]action=merit(?:[;&]|$)/i.test(window.location.search || '');
    }
  }

  // ===========================================================================
  // Color helpers
  // ===========================================================================

  function sanitizeHex(hex, fallback = '#000000') {
    const v = String(hex || '').trim();
    const fallbackValue = String(fallback || '').trim();
    const fallbackMatch = fallbackValue.match(/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/);
    const fb = fallbackMatch
      ? '#' + (
          fallbackMatch[1].length === 3
            ? fallbackMatch[1].split('').map(ch => ch + ch).join('')
            : fallbackMatch[1]
        ).toUpperCase()
      : '#000000';

    const match = v.match(/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/);
    if (!match) return fb;

    const body = match[1];
    return '#' + (
      body.length === 3
        ? body.split('').map(ch => ch + ch).join('')
        : body
    ).toUpperCase();
  }

  function hexToRgb(hex) {
    hex = sanitizeHex(hex, '#000000');
    const c = String(hex || '').replace('#', '');
    const full = c.length === 3 ? c.split('').map(x => x + x).join('') : c;
    const n = parseInt(full, 16);
    return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 };
  }
  function clamp(v, min, max) { return Math.max(min, Math.min(max, v)); }
  function rgbToHex(r, g, b) {
    return '#' + [r, g, b]
      .map(x => clamp(Math.round(x), 0, 255).toString(16).padStart(2, '0'))
      .join('');
  }
  function lighten(hex, amt) {
    const { r, g, b } = hexToRgb(hex);
    return rgbToHex(r + (255 - r) * amt, g + (255 - g) * amt, b + (255 - b) * amt);
  }
  function darken(hex, amt) {
    const { r, g, b } = hexToRgb(hex);
    return rgbToHex(r * (1 - amt), g * (1 - amt), b * (1 - amt));
  }
  function rgbToHsl(r, g, b) {
    r /= 255; g /= 255; b /= 255;
    const max = Math.max(r, g, b), min = Math.min(r, g, b);
    let h, s; const l = (max + min) / 2;
    if (max === min) {
      h = 0; s = 0;
    } else {
      const d = max - min;
      s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
      if (max === r) h = ((g - b) / d + (g < b ? 6 : 0));
      else if (max === g) h = (b - r) / d + 2;
      else h = (r - g) / d + 4;
      h /= 6;
    }
    return { h, s, l };
  }
  function hslToRgb(h, s, l) {
    let r, g, b;
    if (s === 0) {
      r = g = b = l;
    } else {
      const hue2rgb = (p, q, t) => {
        if (t < 0) t += 1;
        if (t > 1) t -= 1;
        if (t < 1 / 6) return p + (q - p) * 6 * t;
        if (t < 1 / 2) return q;
        if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
        return p;
      };
      const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
      const p = 2 * l - q;
      r = hue2rgb(p, q, h + 1 / 3);
      g = hue2rgb(p, q, h);
      b = hue2rgb(p, q, h - 1 / 3);
    }
    return { r: r * 255, g: g * 255, b: b * 255 };
  }
  function adjustHsl(hex, deltaL, deltaS) {
    const { r, g, b } = hexToRgb(hex);
    const hsl = rgbToHsl(r, g, b);
    const newL = clamp(hsl.l + deltaL, 0, 1);
    const newS = clamp(hsl.s + deltaS, 0, 1);
    const rgb = hslToRgb(hsl.h, newS, newL);
    return sanitizeHex(rgbToHex(rgb.r, rgb.g, rgb.b), sanitizeHex(hex, '#000000'));
  }
  function relativeLuminance(hex) {
    const { r, g, b } = hexToRgb(hex);
    const [R, G, B] = [r, g, b].map(v => {
      v /= 255;
      return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
    });
    return 0.2126 * R + 0.7152 * G + 0.0722 * B;
  }
  function deriveAccent(hex) {
    return {
      base: hex,
      hover: lighten(hex, 0.15),
      active: darken(hex, 0.10),
      onText: relativeLuminance(hex) > 0.45 ? '#1A1D21' : '#FFFFFF',
    };
  }

  function deriveMainPalette(hex, theme, mainKey) {
    if (theme === 'dark') {
      return {
        bg: hex,
        bgEnd: darken(hex, 0.06),
        surface1: lighten(hex, 0.06),
        surface2: lighten(hex, 0.12),
        surface3: lighten(hex, 0.20),
        postBg: lighten(hex, 0.055),
        postAltBg: lighten(hex, 0.085),
        posterBg: lighten(hex, 0.050),
        posterAltBg: lighten(hex, 0.075),
        inputBg: darken(hex, 0.06),
        quoteBg: lighten(hex, 0.04),
        codeBg: darken(hex, 0.06),
        border: 'rgba(255,255,255,0.07)',
        border2: 'rgba(255,255,255,0.14)',
        text: '#E3E3E8',
        text2: '#A8B0BA',
        text3: '#6B7480',
        iconFilter: 'invert(1) opacity(0.75)',
        shadow: '0 1px 0 rgba(0,0,0,0.2)',
        shadowLg: '0 12px 32px rgba(0,0,0,0.35)',
      };
    }

    // Light theme: all surfaces derive from the chosen main hex with hue
    // preserved (HSL adjust). v2.8.3 also derives post alternation colours
    // from the selected light theme, rather than hardcoding Classic/Sky.
    //
    // v2.10.28: when the Classic (sky) preset is active, we override the
    // surface tones with hand-picked SMF Bitcointalk values instead of the
    // generic adjustHsl derivation. This gives us:
    //   surface1  → windowbg-equivalent (the post background)
    //   surface2  → titlebg-equivalent (sub-category headers)
    //   surface3  → catbg-equivalent (top category headers, deeper blue)
    // Other presets continue to use the algorithmic derivation so their
    // chromatic identity is preserved.
    //
    // v2.10.30: Classic surface/post/border/quote/code values further
    // desaturated toward neutral cool grey to match the original SMF look,
    // which reads much more grey than blue against a white browser canvas.
    // The catbg-equivalent (surface3) is the only place that retains a
    // visible blue tint, since classic SMF Bitcointalk uses a steel-blue
    // for category headers.
    const isClassic = mainKey === 'sky';

    const navActiveBg = adjustHsl(hex, -0.38, 0.36);
    return {
      bg: hex,
      bgEnd: adjustHsl(hex, -0.025, 0.04),
      // v2.10.30: classic SMF surfaces. Posts on near-white (the original's
      // windowbg is essentially #F8F9FA), titlebg as a soft grey-blue band,
      // catbg as a saturated navy (the iconic SMF header colour).
      surface1: isClassic ? '#FAFBFC' : adjustHsl(hex, -0.055, 0.12),
      surface2: isClassic ? '#D8DDE5' : adjustHsl(hex, -0.12, 0.20),
      surface3: isClassic ? '#5B7FA0' : adjustHsl(hex, -0.20, 0.28),

      // v2.10.30: classic alternation in cool neutral greys. The originals
      // were #F0F4F7 / #F8F9FA — both very light, very desaturated. We use
      // pure white for the primary windowbg and a faintest grey for the
      // alternate row so the stripe is visible without becoming blue.
      postBg: isClassic ? '#FFFFFF' : adjustHsl(hex, -0.010, 0.015),
      postAltBg: isClassic ? '#F2F4F7' : adjustHsl(hex, -0.045, 0.060),
      posterBg: isClassic ? '#F0F2F5' : adjustHsl(hex, -0.035, 0.045),
      posterAltBg: isClassic ? '#E6E9EE' : adjustHsl(hex, -0.070, 0.090),

      navActiveBg: isClassic ? '#5B7FA0' : navActiveBg,
      navActiveText: isClassic
        ? '#FFFFFF'
        : (relativeLuminance(navActiveBg) > 0.45 ? '#1A1D21' : '#FFFFFF'),
      inputBg: '#FFFFFF',
      quoteBg: isClassic ? '#EFF2F6' : adjustHsl(hex, -0.02, 0.04),
      codeBg: isClassic ? '#E0E4EB' : adjustHsl(hex, -0.09, 0.18),
      // v2.10.30: classic borders as solid neutral greys, not rgba —
       // semi-transparent borders pick up the underlying tint and read as
       // bluish on a cool-grey body. Solid #C0C5CC / #99A0AB give the
       // crisp grey separators of the classic SMF theme.
      border: isClassic ? '#C0C5CC' : 'rgba(0,0,0,0.08)',
      border2: isClassic ? '#99A0AB' : 'rgba(0,0,0,0.16)',
      text: isClassic ? '#000000' : '#1A1D21',
      text2: isClassic ? '#3F4754' : '#4A5058',
      text3: isClassic ? '#6E7785' : '#7A8088',
      iconFilter: 'opacity(0.7)',
      shadow: '0 1px 0 rgba(0,0,0,0.04)',
      shadowLg: '0 12px 32px rgba(0,0,0,0.12)',
    };
  }

  function deriveAutoAccent(mainHex, theme, mainKey) {
    mainHex = sanitizeHex(mainHex, theme === 'dark' ? DEFAULT_CUSTOM_MAIN_DARK : DEFAULT_CUSTOM_MAIN_LIGHT);
    if (mainKey && AUTO_ACCENT_MAP[theme] && AUTO_ACCENT_MAP[theme][mainKey]) {
      return sanitizeHex(AUTO_ACCENT_MAP[theme][mainKey], theme === 'dark' ? DEFAULT_CUSTOM_ACCENT_DARK : DEFAULT_CUSTOM_ACCENT_LIGHT);
    }
    const { r, g, b } = hexToRgb(mainHex);
    const hsl = rgbToHsl(r, g, b);
    if (hsl.s < 0.05) {
      return sanitizeHex(theme === 'dark' ? '#E8A04A' : '#B5552B', theme === 'dark' ? DEFAULT_CUSTOM_ACCENT_DARK : DEFAULT_CUSTOM_ACCENT_LIGHT);
    }
    const newH = hsl.h;
    const newS = Math.min(0.65, Math.max(0.50, hsl.s + 0.25));
    const newL = theme === 'dark' ? 0.62 : 0.38;
    const rgb = hslToRgb(newH, newS, newL);
    return sanitizeHex(rgbToHex(rgb.r, rgb.g, rgb.b), theme === 'dark' ? DEFAULT_CUSTOM_ACCENT_DARK : DEFAULT_CUSTOM_ACCENT_LIGHT);
  }

  function toCssVar(name) {
    return '--bt-' + name
      .replace(/([A-Z]|\d+)/g, '-$1')
      .toLowerCase()
      .replace(/^-/, '');
  }

  // ===========================================================================
  // Theme application
  // ===========================================================================

  function getEffectiveTheme(pref) {
    if (pref === 'off') return 'off';
    if (pref === 'system') {
      return window.matchMedia &&
        window.matchMedia('(prefers-color-scheme: dark)').matches
        ? 'dark' : 'light';
    }
    return pref === 'light' ? 'light' : 'dark';
  }

  function getMainKey(theme) {
    const def = theme === 'dark' ? DEFAULT_MAIN_DARK_KEY : DEFAULT_MAIN_LIGHT_KEY;
    return getPref(theme === 'dark' ? KEY_MAIN_DARK : KEY_MAIN_LIGHT, def);
  }
  function getAccentKey(theme) {
    const def = theme === 'dark' ? DEFAULT_ACCENT_DARK_KEY : DEFAULT_ACCENT_LIGHT_KEY;
    return getPref(theme === 'dark' ? KEY_ACCENT_DARK : KEY_ACCENT_LIGHT, def);
  }
  function getActiveMainHex(theme) {
    const presets = theme === 'dark' ? MAIN_PRESETS_DARK : MAIN_PRESETS_LIGHT;
    const key = getMainKey(theme);
    if (key === 'custom') {
      const def = theme === 'dark' ? DEFAULT_CUSTOM_MAIN_DARK : DEFAULT_CUSTOM_MAIN_LIGHT;
      return sanitizeHex(getPref(theme === 'dark' ? KEY_MAIN_DARK_CUSTOM : KEY_MAIN_LIGHT_CUSTOM, def), def);
    }
    const fallbackKey = theme === 'dark' ? DEFAULT_MAIN_DARK_KEY : DEFAULT_MAIN_LIGHT_KEY;
    return sanitizeHex((presets[key] || presets[fallbackKey]).base, presets[fallbackKey].base);
  }
  function getActiveAccentHex(theme) {
    const presets = theme === 'dark' ? ACCENTS_DARK : ACCENTS_LIGHT;
    const key = getAccentKey(theme);
    if (key === 'auto') {
      return deriveAutoAccent(getActiveMainHex(theme), theme, getMainKey(theme));
    }
    if (key === 'custom') {
      const def = theme === 'dark' ? DEFAULT_CUSTOM_ACCENT_DARK : DEFAULT_CUSTOM_ACCENT_LIGHT;
      return sanitizeHex(getPref(theme === 'dark' ? KEY_ACCENT_DARK_CUSTOM : KEY_ACCENT_LIGHT_CUSTOM, def), def);
    }
    if (presets[key]) {
      return sanitizeHex(presets[key].base, presets.orange ? presets.orange.base : '#F7931A');
    }
    return deriveAutoAccent(getActiveMainHex(theme), theme, getMainKey(theme));
  }

  // ===========================================================================
  // Replacement visibility / OFF-mode restoration
  // ===========================================================================
  //
  // We replace classic SMF image buttons with modern spans/SVGs. Earlier builds
  // hid the original <img> nodes with inline `display:none !important`.
  // That is stronger than stylesheet rules, so when OFF mode disabled the theme
  // stylesheet the original BBCode/Quote buttons stayed hidden. This helper is
  // the OFF-mode fix: it explicitly restores original images and hides only our
  // replacement nodes.
  function setModernizedControlsActive(active) {
    const showOriginals = !active;

    document.querySelectorAll('img[data-bt-replaced="1"], img[data-bt-smiley="1"]').forEach(img => {
      // v2.10.15: inline-icon IMGs (lock + new) are handled via src swap.
      // Skip the display:none codepath here and route to src restore/apply.
      if (img.dataset.btInlineIconActive === '1' || Object.prototype.hasOwnProperty.call(img.dataset, 'btOriginalSrc')) {
        if (active) {
          // Re-apply the modern src in case OFF mode reverted it. Pick the
          // right data-URL based on the recorded type.
          if (img.dataset.btTopicType === 'locked') {
            applyInlineIconSrc(img, INLINE_LOCK_DATAURL);
          } else if (img.dataset.btTopicType === 'newPost') {
            applyInlineIconSrc(img, INLINE_NEW_DATAURL);
          } else if (img.dataset.btTopicType === 'sticky') {
            applyInlineIconSrc(img, INLINE_STICKY_DATAURL);
          }
        } else {
          restoreInlineIconSrc(img);
        }
        return;
      }

      if (active) {
        if (!Object.prototype.hasOwnProperty.call(img.dataset, 'btOriginalStyle')) {
          img.dataset.btOriginalStyle = img.getAttribute('style') || '';
        }
        img.style.setProperty('display', 'none', 'important');
      } else {
        if (Object.prototype.hasOwnProperty.call(img.dataset, 'btOriginalStyle')) {
          const originalStyle = img.dataset.btOriginalStyle || '';
          if (originalStyle) img.setAttribute('style', originalStyle);
          else img.removeAttribute('style');
        } else {
          img.style.removeProperty('display');
          if (!img.getAttribute('style')) img.removeAttribute('style');
        }
      }
    });

    // Hide/show our modern replacement UI. Important: do NOT hide every
    // .bt-post-action in OFF mode, because +Merit and #post anchors are real
    // site links that merely receive this class for styling. Only replacement
    // spans created from hidden image buttons carry data-bt-replacement="1".
    document.querySelectorAll('.bt-bbc-icon, .bt-smiley, .bt-topic-icon[data-bt-replacement="1"], .bt-post-action[data-bt-replacement="1"]').forEach(el => {
      if (showOriginals) {
        el.style.setProperty('display', 'none', 'important');
      } else {
        el.style.removeProperty('display');
        if (!el.getAttribute('style')) el.removeAttribute('style');
      }
    });

    // v2.10.20: re-apply / clear inline sticky-row tint when toggling between
    // modern and OFF mode. The CSS variable lookup happens fresh each time so
    // a theme switch (light↔dark) gets the right shade without any extra
    // wiring — applyTheme() flips data-bt-theme on :root, then this loop
    // re-reads the variable and pushes the new color inline.
    document.querySelectorAll('tr.bt-row-sticky').forEach(tr => {
      if (active) {
        // Clear the stale inline tint from the previous theme so the new one
        // takes effect, then re-apply.
        const subjectCell = tr.querySelector(':scope > td.bt-cell-sticky-subject');
        if (subjectCell) delete subjectCell.dataset.btOriginalBg;
        tr.dataset.btStickyTintApplied = '0';
        const stickyImg = tr.querySelector('img.bt-inline-status-img--sticky');
        applyStickyRowTintInline(tr, stickyImg);
      } else {
        clearStickyRowTintInline(tr);
      }
    });

    // v2.10.34: brute-force breadcrumb sizing was needed when the CSS rule
    // kept losing source-order battles. v2.10.35 found the actual root cause
    // (SMF inline style="font-size: smaller" on <div class="nav">) and beats
    // it with a properly specified !important rule, so the brute-force JS is
    // kept as a safety net only — it does no harm and covers any future SMF
    // markup variations that the CSS rule might miss.
    if (active) {
      applyBreadcrumbSizeInline();
      applyHeaderSizesInline();
      applyMeritPageSizingInline();
      applyComposeSizingInline();
    } else {
      clearBreadcrumbSizeInline();
      clearHeaderSizesInline();
      clearMeritPageSizingInline();
      clearComposeSizingInline();
    }
  }

  function isThemeActiveNow() {
    return getEffectiveTheme(getPref(KEY_THEME, DEFAULT_THEME)) !== 'off';
  }

  function applyTheme() {
    const themePref = getPref(KEY_THEME, DEFAULT_THEME);
    const effective = getEffectiveTheme(themePref);
    const root = document.documentElement;
    updateMobileLiteAttribute();
    updatePageAttribute();

    if (effective === 'off') {
      root.setAttribute('data-bt-active', 'false');
      root.setAttribute('data-bt-theme-pref', themePref);
      if (themeStyleEl) themeStyleEl.disabled = true;
      root.style.colorScheme = '';
      clearMeritPageSizingInline();
      clearComposeSizingInline();
      setModernizedControlsActive(false);
      updateSwitcherUI(themePref, effective, null, null, null, null);
      return;
    }

    const mainKey = getMainKey(effective);
    const accentKey = getAccentKey(effective);
    const mainHex = getActiveMainHex(effective);
    const accentHex = getActiveAccentHex(effective);
    const palette = deriveMainPalette(mainHex, effective, mainKey);
    const accent = deriveAccent(accentHex);

    root.setAttribute('data-bt-active', 'true');
    root.setAttribute('data-bt-theme', effective);
    root.setAttribute('data-bt-theme-pref', themePref);
    root.setAttribute('data-bt-main', mainKey);
    root.setAttribute('data-bt-accent', accentKey);
    root.style.colorScheme = effective;

    Object.entries(palette).forEach(([k, v]) => root.style.setProperty(toCssVar(k), v));
    root.style.setProperty('--bt-accent', accent.base);
    root.style.setProperty('--bt-accent-h', accent.hover);
    root.style.setProperty('--bt-accent-a', accent.active);
    root.style.setProperty('--bt-on-accent', accent.onText);

    if (themeStyleEl) themeStyleEl.disabled = false;
    setModernizedControlsActive(!isMobileLite());
    applyMeritPageSizingInline();
    applyComposeSizingInline();
    ensureFonts();

    updateSwitcherUI(themePref, effective, mainKey, accentKey, mainHex, accentHex);
  }

  function updateSwitcherUI(themePref, effective, mainKey, accentKey, mainHex, accentHex) {
    const sw = document.getElementById('bt-switcher');
    if (!sw) return;
    ensureMobileToggleControl(sw);

    const mainBtn = sw.querySelector('#bt-switcher-button');
    if (mainBtn) {
      if (themePref === 'off') {
        mainBtn.innerHTML = '<span class="bt-sw-on-text">ON</span>';
        mainBtn.title = 'Turn the theme back on';
        mainBtn.setAttribute('aria-label', 'Turn the theme back on');
      } else {
        mainBtn.innerHTML = ICONS.cog;
        mainBtn.title = 'Theme settings';
        mainBtn.setAttribute('aria-label', 'Theme settings');
      }
    }

    const customizeEl = sw.querySelector('.bt-sw-customize');
    if (customizeEl) {
      const hide = themePref === 'system' || themePref === 'off';
      customizeEl.style.display = hide ? 'none' : '';
    }
    const titleEl = sw.querySelector('#bt-sw-customize-title');
    if (titleEl && effective !== 'off') {
      titleEl.textContent = `Customise ${effective === 'dark' ? 'Dark' : 'Light'} Colours`;
    }

    sw.querySelectorAll('[data-theme]').forEach(b => {
      b.classList.toggle('selected', b.dataset.theme === themePref);
    });
    const offBtn = sw.querySelector('[data-theme="off"]');
    if (offBtn) {
      const lbl = offBtn.querySelector('.bt-sw-theme-label');
      if (lbl) lbl.textContent = themePref === 'off' ? 'ON' : 'OFF';
      offBtn.title = themePref === 'off' ? 'Turn the theme back on' : 'Turn the theme off';
    }

    if (effective !== 'off') {
      rebuildSwatches(sw, 'main', effective, mainKey, mainHex);
      rebuildSwatches(sw, 'accent', effective, accentKey, accentHex);
    }

    const mobileBtn = sw.querySelector('#bt-sw-mobile-version');
    if (mobileBtn) {
      const mobileOn = isMobileLite();
      mobileBtn.classList.toggle('selected', mobileOn);
      mobileBtn.classList.toggle('bt-sw-mobile-on', mobileOn);
      mobileBtn.title = mobileOn ? 'Use the full theme' : 'Use the lightweight mobile version';
      mobileBtn.setAttribute('aria-pressed', mobileOn ? 'true' : 'false');
      const state = mobileBtn.querySelector('.bt-sw-toggle-state');
      if (state) state.textContent = mobileOn ? 'ON' : 'OFF';
    }
  }

  function ensureMobileToggleControl(sw) {
    if (!sw) return null;
    const panel = sw.querySelector('#bt-switcher-panel');
    if (!panel) return null;

    let mobileBtn = sw.querySelector('#bt-sw-mobile-version');
    if (!mobileBtn) {
      const section = document.createElement('div');
      section.className = 'bt-sw-section bt-sw-performance-section';
      section.innerHTML = `
        <button type="button" class="bt-sw-toggle bt-sw-mobile-toggle" id="bt-sw-mobile-version" title="Use the lightweight mobile version" aria-pressed="false">
          <span class="bt-sw-theme-label">Mobile version</span>
          <span class="bt-sw-toggle-spacer"></span>
          <span class="bt-sw-toggle-track" aria-hidden="true"><span class="bt-sw-toggle-knob"></span></span>
          <span class="bt-sw-toggle-state">OFF</span>
        </button>
      `;
      const customize = panel.querySelector('.bt-sw-customize');
      if (customize) panel.insertBefore(section, customize);
      else panel.appendChild(section);
      mobileBtn = section.querySelector('#bt-sw-mobile-version');
    }

    if (mobileBtn && mobileBtn.dataset.btMobileBound !== '1') {
      mobileBtn.dataset.btMobileBound = '1';
      mobileBtn.addEventListener('click', () => {
        const next = isMobileLite() ? 'off' : 'on';
        setPref(KEY_MOBILE_VERSION, next);
        applyTheme();
        if (next === 'on') {
          stopModernizeObserver();
          applyBreadcrumbSizeInline();
          applyHeaderSizesInline();
        } else {
          ensureFonts();
          runInitialPasses();
          installModernizeObserver();
        }
      });
    }
    return mobileBtn;
  }

  function rebuildSwatches(sw, kind, effective, selectedKey, currentHex) {
    const row = sw.querySelector(`#bt-sw-${kind}-row`);
    if (!row) return;
    const presets = kind === 'main'
      ? (effective === 'dark' ? MAIN_PRESETS_DARK : MAIN_PRESETS_LIGHT)
      : (effective === 'dark' ? ACCENTS_DARK : ACCENTS_LIGHT);
    const customDef = kind === 'main'
      ? (effective === 'dark' ? DEFAULT_CUSTOM_MAIN_DARK : DEFAULT_CUSTOM_MAIN_LIGHT)
      : (effective === 'dark' ? DEFAULT_CUSTOM_ACCENT_DARK : DEFAULT_CUSTOM_ACCENT_LIGHT);
    const customStorageKey = kind === 'main'
      ? (effective === 'dark' ? KEY_MAIN_DARK_CUSTOM : KEY_MAIN_LIGHT_CUSTOM)
      : (effective === 'dark' ? KEY_ACCENT_DARK_CUSTOM : KEY_ACCENT_LIGHT_CUSTOM);
    const customHex = sanitizeHex(getPref(customStorageKey, customDef), customDef);
    const safeCurrentHex = sanitizeHex(currentHex, customHex);
    const isCustomSelected = selectedKey === 'custom';
    const isAutoSelected = selectedKey === 'auto';

    const presetSwatches = Object.entries(presets).map(([key, p]) => {
      const safePresetHex = sanitizeHex(p.base, customDef);
      return `<button type="button" class="bt-sw-swatch${key === selectedKey ? ' selected' : ''}" data-${kind}="${key}" title="${p.name}" style="background:${safePresetHex}"></button>`;
    }).join('');

    const autoSwatch = kind === 'accent'
      ? `<button type="button" class="bt-sw-swatch bt-sw-auto${isAutoSelected ? ' selected' : ''}" data-${kind}="auto" title="Auto (matches your Main)"></button>`
      : '';

    row.innerHTML = autoSwatch + presetSwatches + `
      <span class="bt-sw-custom-wrap${isCustomSelected ? ' selected' : ''}" data-target="${kind}" title="Custom ${kind} colour">
        <span class="bt-sw-custom-display"${isCustomSelected ? ` style="background:${safeCurrentHex}"` : ''}></span>
        <input type="color" data-target="${kind}" value="${customHex}" />
      </span>
    `;
  }

  // ===========================================================================
  // Maintab fix
  // ===========================================================================

  function fixMaintabCaps() {
    document.querySelectorAll('[class*="maintab"], [class*="mirrortab"]').forEach(el => {
      const text = (el.textContent || '').replace(/\s+/g, '');
      if (!text) el.style.cssText = 'display: none !important;';
    });
  }

  // ===========================================================================
  // BBCode icons
  // ===========================================================================

  const BBCODE_SVGS = {
    bold: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M6 4h7a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/><path d="M6 12h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/></svg>',
    italic: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="4" x2="10" y2="4"/><line x1="14" y1="20" x2="5" y2="20"/><line x1="15" y1="4" x2="9" y2="20"/></svg>',
    underline: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3v8a6 6 0 0 0 12 0V3"/><line x1="4" y1="21" x2="20" y2="21"/></svg>',
    strike: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 4H9a3 3 0 0 0-2.83 4M14 12a4 4 0 0 1 0 8H6"/><line x1="4" y1="12" x2="20" y2="12"/></svg>',
    pre: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>',
    code: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>',
    quote: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21c3 0 7-1 7-8V5c0-1.25-.75-2-2-2H4c-1.25 0-2 .75-2 2v6c0 1.25.75 2 2 2h1v1c0 1-1 2-2 2v3c0 1 0 1 1 1z"/><path d="M15 21c3 0 7-1 7-8V5c0-1.25-.75-2-2-2h-4c-1.25 0-2 .75-2 2v6c0 1.25.75 2 2 2h1v1c0 1-1 2-2 2v3c0 1 0 1 1 1z"/></svg>',
    img: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>',
    url: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>',
    email: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/><polyline points="22,6 12,13 2,6"/></svg>',
    ftp: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg>',
    flash: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>',
    youtube: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22.54 6.42a2.78 2.78 0 0 0-1.94-2C18.88 4 12 4 12 4s-6.88 0-8.6.46a2.78 2.78 0 0 0-1.94 2A29 29 0 0 0 1 11.75a29 29 0 0 0 .46 5.33A2.78 2.78 0 0 0 3.4 19c1.72.46 8.6.46 8.6.46s6.88 0 8.6-.46a2.78 2.78 0 0 0 1.94-2 29 29 0 0 0 .46-5.25 29 29 0 0 0-.46-5.33z"/><polygon points="9.75 15.02 15.5 11.75 9.75 8.48 9.75 15.02"/></svg>',
    list: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><circle cx="3.5" cy="6" r="1"/><circle cx="3.5" cy="12" r="1"/><circle cx="3.5" cy="18" r="1"/></svg>',
    orderlist: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/></svg>',
    left: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="17" y1="10" x2="3" y2="10"/><line x1="21" y1="6" x2="3" y2="6"/><line x1="21" y1="14" x2="3" y2="14"/><line x1="17" y1="18" x2="3" y2="18"/></svg>',
    center: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="10" x2="6" y2="10"/><line x1="21" y1="6" x2="3" y2="6"/><line x1="21" y1="14" x2="3" y2="14"/><line x1="18" y1="18" x2="6" y2="18"/></svg>',
    right: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="21" y1="10" x2="7" y2="10"/><line x1="21" y1="6" x2="3" y2="6"/><line x1="21" y1="14" x2="3" y2="14"/><line x1="21" y1="18" x2="7" y2="18"/></svg>',
    hr: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="12" x2="21" y2="12"/></svg>',
    sub: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 5l8 11"/><path d="M12 5L4 16"/><text x="14" y="22" font-size="9" fill="currentColor" stroke="none">2</text></svg>',
    sup: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 8l8 11"/><path d="M12 8L4 19"/><text x="14" y="9" font-size="9" fill="currentColor" stroke="none">2</text></svg>',
    tt: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 7 4 4 20 4 20 7"/><line x1="9" y1="20" x2="15" y2="20"/><line x1="12" y1="4" x2="12" y2="20"/></svg>',
    font: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 7 4 4 20 4 20 7"/><line x1="9" y1="20" x2="15" y2="20"/><line x1="12" y1="4" x2="12" y2="20"/></svg>',
    size: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7V4h18v3"/><path d="M8 20h8"/><path d="M12 4v16"/><path d="M18 14v6"/><path d="M15 14h6"/></svg>',
    color: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10c.93 0 1.69-.76 1.69-1.69 0-.43-.16-.83-.43-1.13-.27-.31-.43-.71-.43-1.13a1.69 1.69 0 0 1 1.69-1.69h1.99c3.04 0 5.49-2.45 5.49-5.49C22 6.04 17.52 2 12 2z"/><circle cx="6.5" cy="11.5" r="1.5"/><circle cx="9.5" cy="7.5" r="1.5"/><circle cx="14.5" cy="7.5" r="1.5"/><circle cx="17.5" cy="11.5" r="1.5"/></svg>',
    table: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="15" y1="3" x2="15" y2="21"/></svg>',
    glow: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>',
    shadow: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 3a9 9 0 0 1 0 18z" fill="currentColor"/></svg>',
    spoiler: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>',
    abbr: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>',
    bitcoin: '<svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M16.43 11.36c.5-.4.85-1 .85-1.74 0-1.6-1.27-2.66-3.06-2.84V5h-1.5v1.7H11V5H9.5v1.7H7v1.5h1.2V16H7v1.5h2.5V19H11v-1.5h1.72V19h1.5v-1.5c2.05 0 3.34-1.18 3.34-3 0-.93-.42-1.74-1.13-2.14zm-5.93-3.16h2.74c.74 0 1.3.42 1.3 1.2 0 .76-.56 1.2-1.3 1.2H10.5V8.2zm3.04 7.6H10.5v-2.6h3.04c.83 0 1.46.5 1.46 1.3 0 .8-.63 1.3-1.46 1.3z"/></svg>',
    trow: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><rect x="3" y="9" width="18" height="6" fill="currentColor" stroke="none" opacity="0.35"/></svg>',
    tcol: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="15" y1="3" x2="15" y2="21"/><rect x="9" y="3" width="6" height="18" fill="currentColor" stroke="none" opacity="0.35"/></svg>',
    trash: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2"/></svg>',
    pencil: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z"/></svg>',
  };

  const BBCODE_ALIASES = {
    italic: ['italicize'],
    bold: ['bolden'],
    strike: ['strikethrough', 'linethrough'],
    tt: ['teletype', 'monospace', 'tele'],
    font: ['fontface', 'fontname', 'face'],
    color: ['colour', 'fontcolour', 'fontcolor'],
    size: ['fontsize'],
    left: ['alignleft'],
    center: ['aligncenter'],
    right: ['alignright'],
    list: ['bullist'],
    orderlist: ['numlist', 'numberedlist'],
    img: ['image', 'picture'],
    url: ['link', 'hyperlink'],
    email: ['mail'],
    youtube: ['video', 'youtu'],
    hr: ['horizontalrule'],
    sub: ['subscript'],
    sup: ['superscript'],
    bitcoin: ['btc'],
    trow: ['tr'],
    tcol: ['td'],
    trash: ['delete', 'remove', 'del'],
    pencil: ['modify', 'edit', 'pen'],
  };
  Object.entries(BBCODE_ALIASES).forEach(([canon, aliases]) => {
    aliases.forEach(a => { BBCODE_SVGS[a] = BBCODE_SVGS[canon]; });
  });

  // ===========================================================================
  // Topic-list / board-list / message-status icon modernisation
  // ===========================================================================
  //
  // Bitcointalk thread-list and thread-header pages still use classic SMF GIFs:
  // topic state icons, locked/sticky/poll icons, and message icons such as
  // exclamation/question/lamp/thumbs. These are not BBCode compose toolbar
  // icons, so they need a separate pass. The originals stay in the DOM and are
  // restored in OFF mode via setModernizedControlsActive().
  const TOPIC_ICON_SVGS = {
    topic: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 6.5A2.5 2.5 0 0 1 6.5 4h11A2.5 2.5 0 0 1 20 6.5v8A2.5 2.5 0 0 1 17.5 17H9l-5 3v-13.5z"/><line x1="8" y1="9" x2="16" y2="9"/><line x1="8" y1="12" x2="14" y2="12"/></svg>',
    hot: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8.5 14.5A3.5 3.5 0 0 0 12 21a6 6 0 0 0 6-6c0-4-3-6-4.5-9.5-.4 2.4-1.6 3.9-3.5 5.2C8.5 11.7 8 12.8 8.5 14.5z"/><path d="M12 21c-1.2-1-1.6-2.4-.9-4 .5-1.1 1.4-1.8 2-3 .7 1.2 1.4 2.2 1.4 3.6A3.2 3.2 0 0 1 12 21z"/></svg>',
    veryhot: '<svg viewBox="0 0 24 24" fill="rgba(217,48,37,0.18)" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7.5 14.5A4.5 4.5 0 0 0 12 21a7 7 0 0 0 7-7c0-4.4-3.2-6.7-4.8-10.5-.5 2.7-1.9 4.4-4 5.9-1.8 1.3-2.9 2.8-2.7 5.1z"/><path d="M12 21c-1.5-1.2-2-2.8-1.1-4.7.6-1.3 1.7-2.1 2.4-3.5.9 1.4 1.7 2.6 1.7 4.2A3.8 3.8 0 0 1 12 21z" fill="rgba(217,48,37,0.35)"/><path d="M5 9c-1 .9-1.5 2-1.3 3.3" fill="none"/></svg>',
    locked: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="10" width="14" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg>',
    sticky: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 4l5 5-4 1-4.5 4.5.5 4.5-2 2-3.5-6.5L1 11l2-2 4.5.5L12 5z"/><line x1="14" y1="6" x2="18" y2="10"/></svg>',
    poll: '<svg viewBox="0 0 24 24" fill="rgba(139,92,246,0.25)" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="20" x2="20" y2="20" fill="none"/><rect x="6" y="10" width="3" height="7" rx="1"/><rect x="11" y="6" width="3" height="11" rx="1"/><rect x="16" y="13" width="3" height="4" rx="1"/></svg>',
    moved: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="M13 6l6 6-6 6"/><path d="M5 5v14"/></svg>',
    newPost: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="6" width="18" height="12" rx="4"/><path d="M7 15V9l4 6V9"/><path d="M13 9h4"/><path d="M13 12h3"/><path d="M13 15h4"/></svg>',
    important: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><line x1="12" y1="7" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
    question: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M9.4 9a3 3 0 1 1 4.9 2.3c-1.3.9-2.3 1.5-2.3 3.2"/><line x1="12" y1="18" x2="12.01" y2="18"/></svg>',
    idea: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18h6"/><path d="M10 22h4"/><path d="M8 14a6 6 0 1 1 8 0c-1.2.9-1.5 1.8-1.5 3h-5c0-1.2-.3-2.1-1.5-3z"/></svg>',
    thumbsUp: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"/><path d="M7 11l4-8a3 3 0 0 1 3 3v4h5a2 2 0 0 1 2 2l-1 7a3 3 0 0 1-3 3H7z"/></svg>',
    thumbsDown: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 2h3a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2h-3"/><path d="M17 13l-4 8a3 3 0 0 1-3-3v-4H5a2 2 0 0 1-2-2l1-7a3 3 0 0 1 3-3h10z"/></svg>',
    smile: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M8 14s1.5 2 4 2 4-2 4-2"/><line x1="9" y1="9" x2="9.01" y2="9"/><line x1="15" y1="9" x2="15.01" y2="9"/></svg>',
    angry: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M8 8l3 2"/><path d="M16 8l-3 2"/><path d="M8 16s1.5-1.5 4-1.5S16 16 16 16"/></svg>',
    standard: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 4h7l5 5v11a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z"/><path d="M14 4v5h5"/></svg>',
  };

  // ===========================================================================
  // v2.10.15: Inline status icons via src replacement
  // ===========================================================================
  //
  // Classic SMF places certain status icons as <img> elements that the browser
  // already positions correctly:
  //   • quick_lock.gif lives on the lock'd thread row with align="right" — the
  //     browser floats it to the right edge of the subject cell automatically.
  //   • the "new" indicator is an <img> inside <a href="...new">, which sits
  //     naturally after the topic title.
  //
  // Earlier versions tried to hide these <img>s and inject synthetic spans in
  // their place. That fights the SMF layout and led to the wrapper-position
  // and visibility bugs in 2.10.13/2.10.14. The cleaner approach is to keep
  // every <img> exactly where SMF rendered it and only swap its `src` to a
  // modern SVG data-URL. OFF mode restoration becomes trivial: put the
  // original src back.
  //
  // Colors are baked into the SVGs because data-URL SVGs run in their own
  // document context and cannot read the host page's CSS variables. Gold for
  // the lock matches the classic Bitcointalk yellow padlock; the dark-blue
  // pill for "new" matches the original SMF "new" badge tint.

  const INLINE_LOCK_SVG = (
    // v2.10.17: bumped from 14×14 to 17×17 to match the upsized sticky pin.
    '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14" width="17" height="17">' +
      '<path d="M4.2 6V4.2C4.2 2.65 5.45 1.4 7 1.4C8.55 1.4 9.8 2.65 9.8 4.2V6" ' +
        'fill="none" stroke="#a17710" stroke-width="1.3" stroke-linecap="round"/>' +
      '<rect x="2.6" y="6" width="8.8" height="6.6" rx="1.1" fill="#e8b923" stroke="#a17710" stroke-width="0.6"/>' +
      '<circle cx="7" cy="9" r="0.85" fill="#7a5808"/>' +
      '<rect x="6.55" y="9" width="0.9" height="2.2" rx="0.3" fill="#7a5808"/>' +
    '</svg>'
  );

  const INLINE_NEW_SVG = (
    '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 13" width="26" height="13">' +
      '<rect x="0" y="0" width="26" height="13" rx="6.5" fill="#4d6c8c"/>' +
      '<text x="13" y="9.6" text-anchor="middle" ' +
        'font-family="-apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif" ' +
        'font-size="8" font-weight="600" fill="#ffffff" letter-spacing="0.2">new</text>' +
    '</svg>'
  );

  // v2.10.17: bumped from 14×14 to 17×17 for better visual presence next to
  // the topic title — the previous size felt cramped especially compared to
  // the title's 13px font.
  const INLINE_STICKY_SVG = (
    '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14" width="17" height="17">' +
      '<line x1="8.5" y1="8.5" x2="12.5" y2="12.5" stroke="#4a4a4a" stroke-width="1" stroke-linecap="round"/>' +
      '<circle cx="6" cy="6" r="4" fill="#cc3838" stroke="#7a2020" stroke-width="0.6"/>' +
      '<ellipse cx="4.6" cy="4.8" rx="1.1" ry="0.8" fill="#ee9595" opacity="0.75"/>' +
    '</svg>'
  );

  function svgToDataUrl(svg) {
    return 'data:image/svg+xml;utf8,' + encodeURIComponent(svg);
  }

  const INLINE_LOCK_DATAURL = svgToDataUrl(INLINE_LOCK_SVG);
  const INLINE_NEW_DATAURL = svgToDataUrl(INLINE_NEW_SVG);
  const INLINE_STICKY_DATAURL = svgToDataUrl(INLINE_STICKY_SVG);

  // True when the IMG is the standalone lock indicator that SMF places to the
  // right of a topic title. Compound state icons like sticky_locked.gif live
  // in the left state column and are handled separately.
  function isInlineLockImage(img) {
    const src = (img.getAttribute('src') || '').toLowerCase();
    if (!/lock/.test(src)) return false;
    if (/sticky[_-]?locked|locked[_-]?(post|sticky|topic|poll)/.test(src)) return false;
    // align="right" is the SMF tell — only inline lock indicators use it.
    if ((img.getAttribute('align') || '').toLowerCase() === 'right') return true;
    // Fallback: filenames like quick_lock.gif / lock.gif that aren't compound.
    return /(^|[/_-])(quick[_-]?lock|lock)\.(gif|png)/.test(src);
  }

  // v2.10.16: standalone sticky-pin indicator that SMF places to the right of
  // a sticky topic title. Same parsing rules as the lock detector — exclude
  // compound left-column icons (sticky_post / sticky_locked / sticky_poll),
  // trigger on align="right", fall back to filename pattern.
  function isInlineStickyImage(img) {
    const src = (img.getAttribute('src') || '').toLowerCase();
    if (!/sticky|pin/.test(src)) return false;
    if (/sticky[_-]?(locked|post|poll|topic)|locked[_-]?sticky/.test(src)) return false;
    if ((img.getAttribute('align') || '').toLowerCase() === 'right') return true;
    return /(^|[/_-])(quick[_-]?sticky|sticky|pin|pinned)\.(gif|png)/.test(src);
  }

  // True when the IMG is the "new" indicator wrapped in <a href="...new">.
  function isInlineNewImage(img) {
    const src = (img.getAttribute('src') || '').toLowerCase();
    if (!/(^|[/_-])(new|unread)/.test(src)) return false;
    const link = img.closest('a[href*="topic="]');
    if (!link) return false;
    const href = (link.getAttribute('href') || '').toLowerCase();
    return /[#;]new\b|;topicseen|msg\d+/.test(href);
  }

  // Swap the IMG's src to a modern SVG data-URL while preserving the original
  // for OFF-mode restoration. Returns true if the swap was applied (or already
  // in place), false otherwise.
  function applyInlineIconSrc(img, dataUrl) {
    if (!img || !dataUrl) return false;
    if (img.dataset.btInlineIconActive === '1' && img.getAttribute('src') === dataUrl) return true;
    if (!Object.prototype.hasOwnProperty.call(img.dataset, 'btOriginalSrc')) {
      img.dataset.btOriginalSrc = img.getAttribute('src') || '';
    }
    img.setAttribute('src', dataUrl);
    img.dataset.btInlineIconActive = '1';
    img.dataset.btReplaced = '1';
    return true;
  }

  function restoreInlineIconSrc(img) {
    if (!img) return;
    if (img.dataset.btInlineIconActive !== '1') return;
    const originalSrc = img.dataset.btOriginalSrc;
    if (typeof originalSrc === 'string') img.setAttribute('src', originalSrc);
    img.dataset.btInlineIconActive = '0';
  }

  // v2.10.20: read the active sticky-row tint color from the cascade and
  // push it as an inline style on the subject <td> of the row. This is a
  // fallback that bypasses any specificity battle with other theme rules —
  // inline styles always beat external CSS unless the latter uses !important
  // AND higher specificity, which the theme's row rules don't.
  // v2.10.21: tint only the subject cell (the one holding the pin), not the
  // whole row — this matches the classic Bitcointalk treatment.
  function applyStickyRowTintInline(tr, stickyImg) {
    if (!tr || tr.dataset.btStickyTintApplied === '1') return;
    const root = document.documentElement;
    const themeActive = root.getAttribute('data-bt-active') === 'true';
    if (!themeActive) return;
    const styles = getComputedStyle(root);
    const tint = (styles.getPropertyValue('--bt-sticky-row-bg') || '').trim();
    if (!tint) return;
    // The subject cell is the <td> that directly contains the sticky IMG.
    const subjectCell = stickyImg && stickyImg.closest('td');
    if (!subjectCell) return;
    if (!Object.prototype.hasOwnProperty.call(subjectCell.dataset, 'btOriginalBg')) {
      subjectCell.dataset.btOriginalBg = subjectCell.style.backgroundColor || '';
    }
    subjectCell.classList.add('bt-cell-sticky-subject');
    subjectCell.style.setProperty('background-color', tint, 'important');
    subjectCell.style.setProperty('background-image', 'none', 'important');
    tr.dataset.btStickyTintApplied = '1';
  }

  function clearStickyRowTintInline(tr) {
    if (!tr) return;
    const subjectCell = tr.querySelector(':scope > td.bt-cell-sticky-subject');
    if (!subjectCell) return;
    if (Object.prototype.hasOwnProperty.call(subjectCell.dataset, 'btOriginalBg')) {
      const original = subjectCell.dataset.btOriginalBg;
      subjectCell.style.removeProperty('background-color');
      subjectCell.style.removeProperty('background-image');
      if (original) subjectCell.style.backgroundColor = original;
    }
    tr.dataset.btStickyTintApplied = '0';
  }

  // v2.10.36: definitive breadcrumb fix. SMF Bitcointalk hardcodes
  // style="font-size: smaller" on <div class="nav"> in the page HTML it
  // serves. Inline styles cascade to every descendant via inheritance, and
  // any external CSS rule needs !important on every descendant selector to
  // overrule the inheritance — easy to get wrong as we just experienced.
  // The clean fix is to STRIP the inline font-size from the parent <div>:
  // once it's gone, the normal cascade resolves the size correctly from
  // the body's --bt-fs-base.
  // v2.10.38: definitive breadcrumb fix. SMF Bitcointalk hardcodes
  // style="font-size: smaller" on <div class="nav"> in the page HTML it
  // serves. Inline styles cascade to every descendant via inheritance, and
  // any external CSS rule needs !important on every descendant selector to
  // overrule the inheritance. The clean fix is to STRIP the inline font-size
  // from the parent <div> and stamp our own size on it + every descendant.
  // Sized at 14px (slightly above body's 13px) so the breadcrumb reads as
  // page-level navigation rather than tiny meta text — confirmed via
  // computed-style inspection that 13px alone reads visually too small
  // against the larger thread title shown below it on every page.
  function applyBreadcrumbSizeInline() {
    const root = document.documentElement;
    if (root.getAttribute('data-bt-active') !== 'true') return;

    const containers = document.querySelectorAll(
      'div.nav, .navigate_section, td.middletext .nav'
    );

    containers.forEach(container => {
      // Skip empty/decorative .nav divs that don't actually hold a breadcrumb.
      if (!container.querySelector('a[href*="index.php"]')) return;

      // Save original inline font-size on first apply so we can restore on OFF.
      const inlineFs = container.style.fontSize;
      if (inlineFs && !Object.prototype.hasOwnProperty.call(container.dataset, 'btOriginalFs')) {
        container.dataset.btOriginalFs = inlineFs;
      }
      // Strip the SMF "font-size: smaller" inline style. Then stamp our own.
      container.style.removeProperty('font-size');
      container.style.setProperty('font-size', '14px', 'important');
      container.style.setProperty('line-height', '1.4', 'important');
      container.classList.add('bt-breadcrumb-fixed');

      // Force-size every descendant to defeat any rule that targets
      // <a>, <b>, etc inside the breadcrumb (the visible structure is
      // <b><a>...</a></b> with bare " > " text nodes between).
      container.querySelectorAll('*').forEach(child => {
        child.style.setProperty('font-size', '14px', 'important');
        child.style.setProperty('line-height', '1.4', 'important');
      });
    });
  }

  function clearBreadcrumbSizeInline() {
    const containers = document.querySelectorAll(
      'div.nav, .navigate_section, td.middletext .nav'
    );
    containers.forEach(container => {
      container.style.removeProperty('font-size');
      container.style.removeProperty('line-height');
      // Restore the original SMF inline font-size if we had one saved.
      if (Object.prototype.hasOwnProperty.call(container.dataset, 'btOriginalFs')) {
        const original = container.dataset.btOriginalFs;
        if (original) container.style.fontSize = original;
        delete container.dataset.btOriginalFs;
      }
    });
  }

  // v3.0.1: brute-force size override for SMF headers that the legacy
  // theme stylesheet renders inconsistently in the modern layer:
  //   • the news ticker ("News: Latest Bitcoin Core release: ...")
  //   • the topic header ("Topic: ... (Read N times)") above thread posts
  // CSS-only attempts kept losing source-order battles, so use the same
  // strip-and-stamp approach proven on the breadcrumb. The news strip is
  // classic smalltext-sized; the topic header is classic body-sized.
  function applyHeaderSizesInline() {
    const root = document.documentElement;
    if (root.getAttribute('data-bt-active') !== 'true') return;

    // News ticker: usually a <td class="windowbg"> with a <b>News:</b>,
    // or sometimes a <div class="news"> in newer SMF layouts.
    const newsBlocks = document.querySelectorAll(
      '.news, .smalltext, .middletext, [class*="windowbg"], [class*="windowbg"] .news, [class*="windowbg"] .newstitle, #upshrink_news, #news'
    );
    newsBlocks.forEach(el => {
      const text = el.textContent || '';
      if (/News:|Latest Bitcoin Core/i.test(text)) stampSize(el, '11px');
    });

    // v3.0.32: the timestamp in the upper-right header should be quiet
    // utility text, clearly smaller than the "Hello username" strip.
    document.querySelectorAll(
      '#smfheader td.titlebg2[align="right"] span.smalltext, ' +
      '#smfheader td.titlebg2[align="right"] span.smalltext *, ' +
      '#upshrink_header td, #upshrink_header span, #top_section td, #top_section span, #upper_section td, #upper_section span'
    ).forEach(el => {
      const text = (el.textContent || '').replace(/\s+/g, ' ').trim();
      if (
        text.length <= 90 &&
        /\b(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\.?\s+\d{1,2},\s+\d{4},/i.test(text) &&
        !/News:|Latest Bitcoin Core|Hello/i.test(text)
      ) {
        stampSelfSize(el, '13px', 'normal');
      }
    });

    // SMF renders the self-moderated warning as classic small text even when
    // the surrounding post/table area is body-sized.
    document.querySelectorAll('td[class*="windowbg"], div[class*="windowbg"], .smalltext, .middletext').forEach(el => {
      const text = el.textContent || '';
      if (/self-moderated topic|posts by \d+\+? user deleted/i.test(text)) {
        stampSize(el, '11px');
      }
    });

    // Topic header: <td class="catbg"> containing "Topic: ... (Read N times)".
    // Only the catbg cells that actually contain "Topic:" or "(Read" text —
    // not every catbg on the page (board indexes, forum-stats banners, etc).
    const catbgCells = document.querySelectorAll('td[class*="catbg"], th[class*="catbg"]');
    catbgCells.forEach(cell => {
      const txt = cell.textContent || '';
      if (/Topic:|\(Read \d+ times\)/.test(txt)) {
        stampSize(cell, '13px');
      }
    });
  }

  function stampSize(el, size) {
    if (!el) return;
    if (!Object.prototype.hasOwnProperty.call(el.dataset, 'btOriginalHdrFs')) {
      el.dataset.btOriginalHdrFs = el.style.fontSize || '';
    }
    el.style.setProperty('font-size', size, 'important');
    el.style.setProperty('line-height', 'normal', 'important');
    el.querySelectorAll('*').forEach(child => {
      child.style.setProperty('font-size', size, 'important');
      child.style.setProperty('line-height', 'normal', 'important');
    });
    el.classList.add('bt-header-sized');
  }

  function stampSelfSize(el, size, weight) {
    if (!el) return;
    if (!Object.prototype.hasOwnProperty.call(el.dataset, 'btOriginalHdrSelfFs')) {
      el.dataset.btOriginalHdrSelfFs = el.style.fontSize || '';
    }
    if (!Object.prototype.hasOwnProperty.call(el.dataset, 'btOriginalHdrSelfLh')) {
      el.dataset.btOriginalHdrSelfLh = el.style.lineHeight || '';
    }
    if (!Object.prototype.hasOwnProperty.call(el.dataset, 'btOriginalHdrSelfFw')) {
      el.dataset.btOriginalHdrSelfFw = el.style.fontWeight || '';
    }
    el.style.setProperty('font-size', size, 'important');
    el.style.setProperty('line-height', 'normal', 'important');
    if (weight) el.style.setProperty('font-weight', weight, 'important');
    el.classList.add('bt-header-self-sized');
  }

  function clearHeaderSizesInline() {
    document.querySelectorAll('.bt-header-sized').forEach(el => {
      el.style.removeProperty('font-size');
      el.style.removeProperty('line-height');
      if (Object.prototype.hasOwnProperty.call(el.dataset, 'btOriginalHdrFs')) {
        const original = el.dataset.btOriginalHdrFs;
        if (original) el.style.fontSize = original;
        delete el.dataset.btOriginalHdrFs;
      }
      el.querySelectorAll('*').forEach(child => {
        child.style.removeProperty('font-size');
        child.style.removeProperty('line-height');
      });
      el.classList.remove('bt-header-sized');
    });
    document.querySelectorAll('.bt-header-self-sized').forEach(el => {
      el.style.removeProperty('font-size');
      el.style.removeProperty('line-height');
      if (Object.prototype.hasOwnProperty.call(el.dataset, 'btOriginalHdrSelfFs')) {
        const original = el.dataset.btOriginalHdrSelfFs;
        if (original) el.style.fontSize = original;
        delete el.dataset.btOriginalHdrSelfFs;
      }
      if (Object.prototype.hasOwnProperty.call(el.dataset, 'btOriginalHdrSelfLh')) {
        const original = el.dataset.btOriginalHdrSelfLh;
        if (original) el.style.lineHeight = original;
        delete el.dataset.btOriginalHdrSelfLh;
      }
      el.style.removeProperty('font-weight');
      if (Object.prototype.hasOwnProperty.call(el.dataset, 'btOriginalHdrSelfFw')) {
        const original = el.dataset.btOriginalHdrSelfFw;
        if (original) el.style.fontWeight = original;
        delete el.dataset.btOriginalHdrSelfFw;
      }
      el.classList.remove('bt-header-self-sized');
    });
  }

  function stampMeritText(el, size, lineHeight) {
    if (!el) return;
    if (!Object.prototype.hasOwnProperty.call(el.dataset, 'btOriginalMeritFs')) {
      el.dataset.btOriginalMeritFs = el.style.fontSize || '';
      el.dataset.btOriginalMeritLh = el.style.lineHeight || '';
      el.dataset.btOriginalMeritFamily = el.style.fontFamily || '';
    }
    el.style.setProperty('font-family', 'Verdana, Arial, Helvetica, sans-serif', 'important');
    el.style.setProperty('font-size', size, 'important');
    el.style.setProperty('line-height', lineHeight, 'important');
    el.classList.add('bt-merit-sized');
  }

  function applyMeritPageSizingInline() {
    const root = document.documentElement;
    if (root.getAttribute('data-bt-active') !== 'true' || !isMeritPage()) {
      stopMeritSizingObserver();
      removeMeritLockStyle();
      return;
    }
    const bodyarea = document.getElementById('bodyarea') || document.getElementById('content_section') || document.body;
    if (!bodyarea) return;

    ensureMeritLockStyle();
    meritSizingApplying = true;
    try {
      stampMeritText(bodyarea, '13px', '1.32');
      bodyarea.querySelectorAll('p, div, span, table, tbody, tr, td, th, ul, ol, li, b, strong, a').forEach(el => {
        stampMeritText(el, '13px', '1.32');
      });
      bodyarea.querySelectorAll('h1, h1 *').forEach(el => {
        stampMeritText(el, '23px', '1.24');
        el.style.setProperty('letter-spacing', '0', 'important');
      });
      bodyarea.querySelectorAll('h2, h2 *, h3, h3 *').forEach(el => {
        stampMeritText(el, '17px', '1.28');
        el.style.setProperty('letter-spacing', '0', 'important');
      });
    } finally {
      setTimeout(() => { meritSizingApplying = false; }, 0);
    }
    installMeritSizingObserver();
  }

  function clearMeritPageSizingInline() {
    stopMeritSizingObserver();
    removeMeritLockStyle();
    document.querySelectorAll('.bt-merit-sized').forEach(el => {
      el.style.removeProperty('font-size');
      el.style.removeProperty('line-height');
      el.style.removeProperty('font-family');
      el.style.removeProperty('letter-spacing');
      if (Object.prototype.hasOwnProperty.call(el.dataset, 'btOriginalMeritFs')) {
        if (el.dataset.btOriginalMeritFs) el.style.fontSize = el.dataset.btOriginalMeritFs;
        if (el.dataset.btOriginalMeritLh) el.style.lineHeight = el.dataset.btOriginalMeritLh;
        if (el.dataset.btOriginalMeritFamily) el.style.fontFamily = el.dataset.btOriginalMeritFamily;
        delete el.dataset.btOriginalMeritFs;
        delete el.dataset.btOriginalMeritLh;
        delete el.dataset.btOriginalMeritFamily;
      }
      el.classList.remove('bt-merit-sized');
    });
  }

  let meritSizingObserver = null;
  let meritSizingTimer = null;
  let meritSizingApplying = false;

  function ensureMeritLockStyle() {
    if (document.getElementById('bt-merit-lock-css')) return;
    const style = document.createElement('style');
    style.id = 'bt-merit-lock-css';
    style.textContent = `
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea,
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea p,
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea div,
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea span,
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea table,
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea tbody,
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea tr,
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea td,
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea th,
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea ul,
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea ol,
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea li,
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea b,
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea strong,
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea a {
        font-family: Verdana, Arial, Helvetica, sans-serif !important;
        font-size: 13px !important;
        line-height: 1.32 !important;
      }
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea h1,
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea h1 * {
        font-family: Verdana, Arial, Helvetica, sans-serif !important;
        font-size: 23px !important;
        line-height: 1.24 !important;
        letter-spacing: 0 !important;
      }
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea h2,
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea h2 *,
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea h3,
      html[data-bt-active="true"][data-bt-page="merit"] #bodyarea h3 * {
        font-family: Verdana, Arial, Helvetica, sans-serif !important;
        font-size: 17px !important;
        line-height: 1.28 !important;
        letter-spacing: 0 !important;
      }
    `;
    (document.head || document.documentElement).appendChild(style);
  }

  function removeMeritLockStyle() {
    const style = document.getElementById('bt-merit-lock-css');
    if (style) style.remove();
  }

  function scheduleMeritPageSizingInline() {
    if (meritSizingApplying || meritSizingTimer) return;
    meritSizingTimer = setTimeout(() => {
      meritSizingTimer = null;
      applyMeritPageSizingInline();
    }, 30);
  }

  function installMeritSizingObserver() {
    if (meritSizingObserver || !isMeritPage() || typeof MutationObserver === 'undefined') return;
    const target = document.getElementById('bodyarea') || document.getElementById('content_section') || document.body;
    if (!target) return;
    meritSizingObserver = new MutationObserver(() => {
      if (!meritSizingApplying) scheduleMeritPageSizingInline();
    });
    meritSizingObserver.observe(target, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: ['style', 'class'],
    });
  }

  function stopMeritSizingObserver() {
    if (meritSizingTimer) {
      clearTimeout(meritSizingTimer);
      meritSizingTimer = null;
    }
    if (!meritSizingObserver) return;
    try { meritSizingObserver.disconnect(); } catch (e) {}
    meritSizingObserver = null;
  }

  function composeEditors() {
    return document.querySelectorAll(
      'textarea.editor[name="message"], textarea[name="message"], #message, #quickReply textarea, ' +
      '#postmodify textarea.editor, #post_modify textarea.editor, form[name="postmodify"] textarea.editor, ' +
      'form[action*="action=post"] textarea.editor'
    );
  }

  function applyComposeSizingInline() {
    const root = document.documentElement;
    if (root.getAttribute('data-bt-active') !== 'true') return;
    composeEditors().forEach(el => {
      if (!Object.prototype.hasOwnProperty.call(el.dataset, 'btOriginalComposeFs')) {
        el.dataset.btOriginalComposeFs = el.style.fontSize || '';
        el.dataset.btOriginalComposeLh = el.style.lineHeight || '';
      }
      el.style.setProperty('font-size', '14px', 'important');
      el.style.setProperty('line-height', '1.45', 'important');
      el.classList.add('bt-compose-sized');
    });
  }

  function clearComposeSizingInline() {
    document.querySelectorAll('.bt-compose-sized').forEach(el => {
      el.style.removeProperty('font-size');
      el.style.removeProperty('line-height');
      if (Object.prototype.hasOwnProperty.call(el.dataset, 'btOriginalComposeFs')) {
        if (el.dataset.btOriginalComposeFs) el.style.fontSize = el.dataset.btOriginalComposeFs;
        if (el.dataset.btOriginalComposeLh) el.style.lineHeight = el.dataset.btOriginalComposeLh;
        delete el.dataset.btOriginalComposeFs;
        delete el.dataset.btOriginalComposeLh;
      }
      el.classList.remove('bt-compose-sized');
    });
  }


  function getImageBaseName(img) {
    const src = img.getAttribute('src') || '';
    const clean = src.split(/[?#]/)[0];
    const file = clean.slice(clean.lastIndexOf('/') + 1);
    return file
      .replace(/\.(?:gif|png|jpe?g|webp)$/i, '')
      .toLowerCase()
      .replace(/^(?:topic|theme|smf|icon)_/, '');
  }

  function topicIconHaystack(img) {
    const src = (img.getAttribute('src') || '').toLowerCase();
    const name = getImageBaseName(img);
    const label = ((img.getAttribute('alt') || '') + ' ' + (img.getAttribute('title') || '')).toLowerCase();
    return { src, name, label, hay: `${src} ${name} ${label}` };
  }

  function isCompoundStickyTopicIconName(name, hay) {
    // sticky_post / sticky_poll / sticky_locked are the LEFT topic-state icon.
    // They are not the inline PIN marker. The inline pin is usually a small
    // standalone sticky/pin/pinned icon near the title.
    return /(?:^|[_-])sticky(?:[_-](?:post|poll|locked)|locked)(?:[_-]|$)/.test(name) ||
      /sticky_(?:post|poll|locked)/.test(name) ||
      /stickylocked|stickied_post|stickied_poll/.test(name) ||
      /sticky[_-]?(?:topic|thread|post|poll|locked)|(?:^|[_-])sticky(?:[_-]|$).*(?:post|poll|locked)/.test(hay);
  }

  function isCompoundLockedTopicIconName(name, hay) {
    return /locked?[_-](?:post|poll|topic|thread)/.test(name) ||
      /(?:^|[_-])locked(?:[_-](?:post|poll|topic|thread)|post|poll)(?:[_-]|$)/.test(name) ||
      /sticky[_-]?locked/.test(name) ||
      /lock(?:ed)?[_-]?(?:topic|thread|post|poll)|(?:^|[_-])lock(?:ed)?(?:[_-]|$).*(?:post|poll|topic|thread)/.test(hay);
  }

  function topicIconTypeForImage(img) {
    const { src, name, label, hay } = topicIconHaystack(img);

    // Avoid touching avatars, rank images, logos, the compose toolbar, and
    // actual inline post images. Thread/message icons can live in wrappers
    // such as .td_headerandpost, but should not live inside the .post body.
    if (
      img.closest('#bbcBox_message, .bbc_buttons, [id^="bbc_"], td.post, .post, .personalmessage, .signature, .poster_info') ||
      /avatar|rank|star|user|flag|logo/i.test(src)
    ) {
      return null;
    }

    const looksLikeTopicIcon =
      /\/topic\//i.test(src) ||
      /(?:^|[_-])(normal|hot|veryhot|very_hot|locked|lock|sticky|pin|pinned|sticky_locked|poll|moved|redirect|new|unread)(?:[_-]|$)/i.test(name) &&
      /(post|topic|poll|locked|lock|sticky|pin|pinned|hot|veryhot|normal|moved|redirect|new|unread)/i.test(name + ' ' + label);

    const looksLikeMessageIcon =
      /\/post\//i.test(src) ||
      /\/message/i.test(src) ||
      /(?:^|[_-])(standard|xx|exclam|exclamation|question|lamp|idea|thumbup|thumbdown|thumb_up|thumb_down|smiley|happy|angry|sad|cheesy|grin|wink|new|unread)(?:[_-]|$)/i.test(name);

    if (!looksLikeTopicIcon && !looksLikeMessageIcon) return null;

    if (/(^|[_\s./-])(new|unread)([_\s./-]|$)|\bnew\b|\bunread\b/.test(hay)) return 'newPost';
    if (/moved|redirect/.test(hay)) return 'moved';

    const compoundSticky = isCompoundStickyTopicIconName(name, hay);
    const compoundLocked = isCompoundLockedTopicIconName(name, hay);

    // Compound SMF files such as sticky_locked.gif, locked_post.gif and
    // sticky_post.gif are LEFT-column folder/topic-state images. In the
    // classic visual layout, the PIN/LOCK markers belong beside the title.
    // Therefore this function returns only the neutral left-state icon; the
    // actual status marker is added by maybeAddInlineStatusIcon().
    if (compoundSticky || compoundLocked) {
      if (/poll/.test(hay)) return 'poll';
      if (/veryhot|very_hot|very hot/.test(hay)) return 'veryhot';
      if (/(^|[_\s/-])hot([_\s./-]|$)/.test(hay)) return 'hot';
      return 'topic';
    }

    if (/sticky|pinned|\bpin\b/.test(hay)) return 'sticky';
    if (/locked|\block\b/.test(hay)) return 'locked';
    if (/poll/.test(hay)) return 'poll';
    if (/veryhot|very_hot|very hot/.test(hay)) return 'veryhot';
    if (/(^|[_\s/-])hot([_\s./-]|$)/.test(hay)) return 'hot';

    if (/exclam|exclamation|important|warning|alert|\!/.test(hay)) return 'important';
    if (/question|\?/.test(hay)) return 'question';
    if (/lamp|idea|lightbulb|bulb/.test(hay)) return 'idea';
    if (/thumbup|thumb_up|thumbs? up|\+1/.test(hay)) return 'thumbsUp';
    if (/thumbdown|thumb_down|thumbs? down|-1/.test(hay)) return 'thumbsDown';
    if (/angry|mad/.test(hay)) return 'angry';
    if (/smiley|happy|cheesy|grin|wink|sad/.test(hay)) return 'smile';
    if (/standard|xx/.test(hay)) return 'standard';

    return 'topic';
  }

  function isInlineTopicStatusIcon(img, type) {
    if (type !== 'sticky' && type !== 'locked' && type !== 'newPost') return false;

    const { name, hay } = topicIconHaystack(img);
    if (type === 'newPost') return true;

    if (type === 'sticky') {
      return /(?:^|[_\s./-])(sticky|pin|pinned)([_\s./-]|$)|\bsticky\b|\bpinned\b|\bpin\b/.test(hay) &&
        !isCompoundStickyTopicIconName(name, hay);
    }

    if (type === 'locked') {
      return /(?:^|[_\s./-])(lock|locked)([_\s./-]|$)|\blocked\b/.test(hay) &&
        !isCompoundLockedTopicIconName(name, hay);
    }

    return false;
  }

  function findTopicTitleLinkNear(img) {
    const row = img.closest('tr');
    const scope = row || img.closest('td') || document;
    const links = Array.from(scope.querySelectorAll('a[href*="topic="]'));
    return links.find(a => {
      const text = (a.textContent || '').trim();
      const href = a.getAttribute('href') || '';
      if (!text) return false;
      if (/#[a-z0-9_-]+$/i.test(href) && !/topic=/i.test(href)) return false;
      if (/new|last post|last edit|notify/i.test(text) && text.length < 12) return false;
      return true;
    }) || null;
  }

  function insertStatusIconAfterTitle(titleLink, span) {
    const parent = titleLink && titleLink.parentNode;
    if (!parent) return false;

    // Keep status markers in the title area, but never add raw text nodes.
    // OFF mode can then hide/remove modern elements without leaving visible
    // whitespace artefacts in the classic Bitcointalk layout.
    const children = Array.from(parent.childNodes);
    const titleIndex = children.indexOf(titleLink);
    let anchor = titleLink;
    for (let i = titleIndex + 1; i < children.length; i += 1) {
      const node = children[i];
      if (node.nodeType === Node.ELEMENT_NODE && node.matches && node.matches('.bt-topic-icon--inline-status[data-bt-replacement="1"]')) {
        anchor = node;
        continue;
      }
      if (node.nodeType === Node.TEXT_NODE && !String(node.nodeValue || '').trim()) continue;
      break;
    }
    anchor.insertAdjacentElement('afterend', span);
    return true;
  }

  function makeTopicStatusSpan(type, sourceImg) {
    if (!TOPIC_ICON_SVGS[type]) return null;
    const span = document.createElement('span');
    span.className = `bt-topic-icon bt-topic-icon--${type} bt-topic-icon--inline-status`;
    span.dataset.btReplacement = '1';
    span.dataset.btSynthetic = '1';
    span.dataset.btTopicType = type;
    span.title = (sourceImg && (sourceImg.alt || sourceImg.title)) ||
      (type === 'sticky' ? 'Pinned topic' : type === 'locked' ? 'Locked topic' : type);
    span.setAttribute('aria-label', span.title);
    span.innerHTML = TOPIC_ICON_SVGS[type];
    return span;
  }

  function maybeAddInlineStatusIcon(img, type) {
    const titleLink = findTopicTitleLinkNear(img);
    if (!titleLink || !titleLink.parentNode) return false;

    const parent = titleLink.parentNode;
    const existing = parent.querySelector(`.bt-topic-icon--inline-status[data-bt-topic-type="${type}"]`);
    if (existing) return true;

    const span = makeTopicStatusSpan(type, img);
    if (!span) return false;
    return insertStatusIconAfterTitle(titleLink, span);
  }

  function modernizeTopicListIcons() {
    // v2.10.17: clean up any leftover bt-topic-icon spans that previous
    // versions of this script accidentally created inside compose-panel
    // smiley links. Without this, users upgrading from 2.10.15/16 keep
    // seeing the duplicate outline glyphs until they hard-reload.
    document.querySelectorAll(
      'a[onclick*="replaceText"] > span.bt-topic-icon[data-bt-replacement="1"]'
    ).forEach(span => span.remove());

    const imgs = document.querySelectorAll(
      'img[src*="/topic/"], img[src*="/post/"], img[src*="/Post/"], ' +
      'img[src*="normal_post"], img[src*="hot_post"], img[src*="veryhot_post"], ' +
      'img[src*="locked_post"], img[src*="lock_post"], img[src*="sticky_post"], img[src*="sticky_locked"], ' +
      'img[src*="lock"], img[src*="locked"], img[src*="sticky"], img[src*="pin"], img[src*="pinned"], ' +
      'img[src*="new"], img[src*="unread"], img[alt*="New"], img[title*="New"], img[alt*="new"], img[title*="new"], ' +
      'img[alt*="Sticky"], img[title*="Sticky"], img[alt*="Pinned"], img[title*="Pinned"], ' +
      'img[alt*="Locked"], img[title*="Locked"], img[alt*="Lock"], img[title*="Lock"], ' +
      'img[src*="normal_poll"], img[src*="hot_poll"], img[src*="veryhot_poll"], ' +
      'img[src*="locked_poll"], img[src*="sticky_poll"], ' +
      'img[src*="moved"], img[src*="redirect"], ' +
      'img[src*="exclam"], img[src*="exclamation"], img[src*="question"], ' +
      'img[src*="lamp"], img[src*="idea"], img[src*="thumbup"], img[src*="thumbdown"], ' +
      'img[src*="thumb_up"], img[src*="thumb_down"], img[src*="standard"], img[src*="xx.gif"], ' +
      'img[src*="smiley"], img[src*="happy"], img[src*="angry"]'
    );

    imgs.forEach(img => {
      if (img.dataset.btTopicIcon === '1' || img.dataset.btReplaced === '1') return;

      // v2.10.17: critical exclusion. modernizeTopicListIcons must never touch
      // smiley images that live inside the compose smiley panel — those are
      // owned by modernizeSmileys, which renders them as native emoji. The
      // old selector matched smiley.gif/happy.gif/angry.gif anywhere on the
      // page and inserted outline SVG spans on top of the emoji spans,
      // producing two visually inconsistent smiley layers in the same <a>.
      // Detection: the smiley panel uses <a onclick="replaceText(...)"> and
      // sits inside the post form (#postmodify / form[name=postmodify]).
      const inComposeSmileyPanel =
        img.closest('a[onclick*="replaceText"]') ||
        img.dataset.btSmiley === '1' ||
        (img.closest('form[name="postmodify"], #postmodify, form[action*="action=post"]') &&
         /\/Smileys\//i.test(img.getAttribute('src') || ''));
      if (inComposeSmileyPanel) return;

      // v2.10.15: Inline LOCK and NEW are handled by replacing the IMG src
      // with a modern SVG data-URL. The IMG keeps its native SMF position
      // (align="right" for lock, inside <a href="...new"> for new) so the
      // browser does the layout for us. No synthetic span, no display:none.
      // v2.10.16: STICKY pushpin added with the same approach.
      if (isInlineStickyImage(img)) {
        applyInlineIconSrc(img, INLINE_STICKY_DATAURL);
        img.dataset.btTopicIcon = '1';
        img.dataset.btTopicType = 'sticky';
        img.classList.add('bt-inline-status-img', 'bt-inline-status-img--sticky');
        // v2.10.18: tag the parent <tr> so the sticky-row tint CSS can match
        // it reliably without relying on :has() resolving in time.
        // v2.10.20: belt-and-suspenders — also push the tint as an inline
        // style on every <td> of the row, which beats any external CSS rule
        // regardless of specificity. The CSS variable lookup happens at
        // application time so the right theme color is used; if the theme
        // toggles later, applyStickyRowTintInline() can be re-invoked.
        const tr = img.closest('tr');
        if (tr) {
          tr.classList.add('bt-row-sticky');
          applyStickyRowTintInline(tr, img);
        }
        return;
      }
      if (isInlineLockImage(img)) {
        applyInlineIconSrc(img, INLINE_LOCK_DATAURL);
        img.dataset.btTopicIcon = '1';
        img.dataset.btTopicType = 'locked';
        img.classList.add('bt-inline-status-img', 'bt-inline-status-img--locked');
        return;
      }
      if (isInlineNewImage(img)) {
        applyInlineIconSrc(img, INLINE_NEW_DATAURL);
        img.dataset.btTopicIcon = '1';
        img.dataset.btTopicType = 'newPost';
        img.classList.add('bt-inline-status-img', 'bt-inline-status-img--newPost');
        return;
      }

      const { name, hay } = topicIconHaystack(img);
      const compoundSticky = isCompoundStickyTopicIconName(name, hay);
      const compoundLocked = isCompoundLockedTopicIconName(name, hay) || /sticky[_-]?locked/.test(name + ' ' + hay);

      // Compound SMF icons such as sticky_locked.gif are left-column state
      // icons (folder + pin overlay). v2.10.15 follows the classic layout:
      // sticky/locked compounds render only as the modernised left-column
      // folder icon, with no extra inline marker beside the title. The lone
      // quick_lock.gif is the only inline status indicator (handled above).

      const type = topicIconTypeForImage(img);
      if (!type || !TOPIC_ICON_SVGS[type]) return;

      img.dataset.btTopicIcon = '1';
      img.dataset.btReplaced = '1';
      if (!Object.prototype.hasOwnProperty.call(img.dataset, 'btOriginalStyle')) {
        img.dataset.btOriginalStyle = img.getAttribute('style') || '';
      }
      img.style.setProperty('display', 'none', 'important');

      // Skip lone sticky/locked/newPost that fell through the inline detectors
      // — let them render via their natural SMF image rather than producing a
      // misplaced span in the left column.
      if (type === 'sticky' || type === 'locked' || type === 'newPost') {
        return;
      }

      const span = document.createElement('span');
      span.className = `bt-topic-icon bt-topic-icon--${type}`;
      span.dataset.btReplacement = '1';
      span.dataset.btTopicType = type;
      span.title = img.alt || img.title || type;
      span.setAttribute('aria-label', span.title);
      span.innerHTML = TOPIC_ICON_SVGS[type];

      if (img.parentNode) {
        img.parentNode.insertBefore(span, img);
      }
    });

    setModernizedControlsActive(isThemeActiveNow());
  }

  function modernizeBBCodeIcons() {
    const imgs = document.querySelectorAll(
      '#bbcBox_message img, .bbc_buttons img, [id^="bbc_"] img, ' +
      'img[src*="/bbc/"], img[src*="bbc_"], ' +
      'img[src*="/frostee/"], img[src*="frostee_"], ' +
      'img.reply_button, img.remove_button, img.modify_button, ' +
      'img[class$="_button"]'
    );
    imgs.forEach(img => {
      if (img.dataset.btReplaced === '1') return;
      const src = img.getAttribute('src') || '';
      const m = src.match(/\/(\w+)\.(?:gif|png)/i);
      const name = m ? m[1].toLowerCase().replace(/^(bbc|frostee|theme)_/, '') : '';

      const isPostAction = /\/frostee\//i.test(src) ||
                           img.classList.contains('reply_button') ||
                           img.classList.contains('modify_button') ||
                           img.classList.contains('remove_button');
      if (isPostAction) {
        const POST_ACTION_LABELS = {
          quote: 'Quote',
          edit: 'Edit',
          modify: 'Edit',
          delete: 'Delete',
          remove: 'Delete',
        };
        const label = POST_ACTION_LABELS[name];
        if (label) {
          const span = document.createElement('span');
          span.className = 'bt-post-action';
          span.dataset.btReplacement = '1';
          span.title = img.alt || img.title || label;
          span.textContent = label;
          span.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();
            img.click();
          });
          img.dataset.btReplaced = '1';
          if (!Object.prototype.hasOwnProperty.call(img.dataset, 'btOriginalStyle')) {
            img.dataset.btOriginalStyle = img.getAttribute('style') || '';
          }
          img.style.setProperty('display', 'none', 'important');
          img.parentNode.insertBefore(span, img);
          return;
        }
      }

      const svg = BBCODE_SVGS[name];
      if (!svg) return;

      const span = document.createElement('span');
      span.className = 'bt-bbc-icon';
      span.title = img.alt || img.title || name;
      span.setAttribute('aria-label', span.title);
      span.innerHTML = svg;
      span.addEventListener('click', (e) => {
        e.preventDefault();
        e.stopPropagation();
        img.click();
      });

      img.dataset.btReplaced = '1';
      if (!Object.prototype.hasOwnProperty.call(img.dataset, 'btOriginalStyle')) {
        img.dataset.btOriginalStyle = img.getAttribute('style') || '';
      }
      img.style.setProperty('display', 'none', 'important');
      img.parentNode.insertBefore(span, img);
    });
    setModernizedControlsActive(isThemeActiveNow());
  }

  // ===========================================================================
  // New-PM highlight (client-side port of PowerGlove's SMF patch)
  // ===========================================================================
  //
  // PowerGlove's original patch is server-side (SMF Themes/default/index.template.php):
  // when $context['user']['unread_messages'] > 0 it renders a small indicator
  // next to the "Messages" maintab. We cannot touch bitcointalk's PHP, so we
  // reproduce it in the DOM. SMF already prints the unread count inside the PM
  // link as "[<strong>N</strong>]", so we read it and flag the tab; the
  // stylesheet then lights it up with a pulsing accent dot.

  const PM_LINK_SELECTOR =
    '[class*="maintab_"] a[href*="action=pm"], ' +
    '#main_menu a[href*="action=pm"], ' +
    '.dropmenu a[href*="action=pm"]';

  function pmUnreadCount(link) {
    // Preferred signal: the bold count SMF renders for unread PMs.
    const strong = link.querySelector('strong');
    if (strong) {
      const n = parseInt((strong.textContent || '').replace(/\D+/g, ''), 10);
      if (n > 0) return n;
    }
    // Fallback: a bracketed number anywhere in the link text, e.g. "[3]".
    const m = (link.textContent || '').match(/\[\s*(\d+)\s*\]/);
    if (m) {
      const n = parseInt(m[1], 10);
      if (n > 0) return n;
    }
    return 0;
  }

  function highlightUnreadPm() {
    let links;
    try {
      links = document.querySelectorAll(PM_LINK_SELECTOR);
    } catch (e) {
      return;
    }
    links.forEach((link) => {
      const count = pmUnreadCount(link);
      const tab = link.closest('td[class*="maintab_"], li') || link;
      if (count > 0) {
        link.classList.add('bt-pm-unread');
        tab.classList.add('bt-pm-unread-tab');
        link.setAttribute('data-bt-pm-count', String(count));
      } else {
        link.classList.remove('bt-pm-unread');
        tab.classList.remove('bt-pm-unread-tab');
        link.removeAttribute('data-bt-pm-count');
      }
    });
  }

  // ===========================================================================
  // Smiley modernisation
  // ===========================================================================

  const SMILEY_SVGS = {
    smiley: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle class="bt-smiley-face" cx="12" cy="12" r="10"/><circle class="bt-smiley-eye" cx="8.7" cy="9.6" r="1.25"/><circle class="bt-smiley-eye" cx="15.3" cy="9.6" r="1.25"/><path class="bt-smiley-line" d="M7.8 14.1c1 1.7 2.5 2.6 4.2 2.6s3.2-.9 4.2-2.6"/></svg>',
    wink: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle class="bt-smiley-face" cx="12" cy="12" r="10"/><circle class="bt-smiley-eye" cx="15.3" cy="9.6" r="1.25"/><path class="bt-smiley-line" d="M7.5 9.6h2.6"/><path class="bt-smiley-line" d="M7.8 14.2c1 1.6 2.5 2.5 4.2 2.5s3.2-.9 4.2-2.5"/></svg>',
    grin: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle class="bt-smiley-face" cx="12" cy="12" r="10"/><circle class="bt-smiley-eye" cx="8.7" cy="9.3" r="1.2"/><circle class="bt-smiley-eye" cx="15.3" cy="9.3" r="1.2"/><path class="bt-smiley-fill" d="M7.4 13.2h9.2c-.35 2.4-2.1 3.9-4.6 3.9s-4.25-1.5-4.6-3.9z"/><path class="bt-smiley-line" d="M7.4 13.2h9.2"/></svg>',
    laugh: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle class="bt-smiley-face" cx="12" cy="12" r="10"/><path class="bt-smiley-line" d="M7.5 9.2l2.3 1.5M16.5 9.2l-2.3 1.5"/><path class="bt-smiley-fill" d="M7.3 13.4h9.4c-.45 2.7-2.2 4.1-4.7 4.1s-4.25-1.4-4.7-4.1z"/></svg>',
    sad: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle class="bt-smiley-face" cx="12" cy="12" r="10"/><circle class="bt-smiley-eye" cx="8.7" cy="9.6" r="1.25"/><circle class="bt-smiley-eye" cx="15.3" cy="9.6" r="1.25"/><path class="bt-smiley-line" d="M8 16.2c1-1.4 2.3-2.1 4-2.1s3 .7 4 2.1"/></svg>',
    angry: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle class="bt-smiley-face" cx="12" cy="12" r="10"/><path class="bt-smiley-line" d="M7.4 8.2l3 1.5M16.6 8.2l-3 1.5"/><circle class="bt-smiley-eye" cx="9" cy="10.8" r="1.05"/><circle class="bt-smiley-eye" cx="15" cy="10.8" r="1.05"/><path class="bt-smiley-line" d="M8.2 16.3c1-.9 2.3-1.4 3.8-1.4s2.8.5 3.8 1.4"/></svg>',
    shocked: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle class="bt-smiley-face" cx="12" cy="12" r="10"/><circle class="bt-smiley-eye" cx="8.7" cy="9.3" r="1.2"/><circle class="bt-smiley-eye" cx="15.3" cy="9.3" r="1.2"/><ellipse class="bt-smiley-line" cx="12" cy="15" rx="2.1" ry="2.6"/></svg>',
    cool: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle class="bt-smiley-face" cx="12" cy="12" r="10"/><path class="bt-smiley-fill" d="M5.9 9h5.1v2.1c0 1.25-.95 2.2-2.25 2.2H8.1c-1.3 0-2.2-.95-2.2-2.2V9zm7.1 0h5.1v2.1c0 1.25-.95 2.2-2.25 2.2h-.65c-1.3 0-2.2-.95-2.2-2.2V9z"/><path class="bt-smiley-line" d="M11 10h2M8.2 16c1 .7 2.3 1.1 3.8 1.1s2.8-.4 3.8-1.1"/></svg>',
    confused: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle class="bt-smiley-face" cx="12" cy="12" r="10"/><circle class="bt-smiley-eye" cx="8.7" cy="9.6" r="1.2"/><circle class="bt-smiley-eye" cx="15.3" cy="9.6" r="1.2"/><path class="bt-smiley-line" d="M8.5 15.6c2.1-.8 4.4-.8 7 0"/></svg>',
    tongue: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle class="bt-smiley-face" cx="12" cy="12" r="10"/><circle class="bt-smiley-eye" cx="8.7" cy="9.4" r="1.2"/><circle class="bt-smiley-eye" cx="15.3" cy="9.4" r="1.2"/><path class="bt-smiley-line" d="M8 13.7h8"/><path class="bt-smiley-tongue" d="M10 13.7v2.2a2 2 0 0 0 4 0v-2.2"/></svg>',
    heart: '<svg viewBox="0 0 24 24" aria-hidden="true"><path class="bt-smiley-heart" d="M12 20s-7-4.4-9.1-8.3C1.3 8.8 3 5.7 6.1 5.3c1.8-.2 3.3.6 4.1 2 1-1.6 2.5-2.4 4.4-2 3.1.6 4.6 3.7 3 6.4C15.6 15.6 12 20 12 20z"/></svg>',
    thumbsup: '<svg viewBox="0 0 24 24" aria-hidden="true"><path class="bt-smiley-line" d="M7 21H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3v11zM7 10l4-7a2.5 2.5 0 0 1 2.5 2.5V9H19a2 2 0 0 1 2 2l-1 6.5A3 3 0 0 1 17 20H7"/></svg>',
    thumbsdown: '<svg viewBox="0 0 24 24" aria-hidden="true"><path class="bt-smiley-line" d="M17 3h3a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2h-3V3zM17 14l-4 7a2.5 2.5 0 0 1-2.5-2.5V15H5a2 2 0 0 1-2-2l1-6.5A3 3 0 0 1 7 4h10"/></svg>',
    star: '<svg viewBox="0 0 24 24" aria-hidden="true"><path class="bt-smiley-star" d="M12 2.8l2.8 5.7 6.2.9-4.5 4.4 1.1 6.2L12 17.1 6.4 20l1.1-6.2L3 9.4l6.2-.9L12 2.8z"/></svg>',
    fire: '<svg viewBox="0 0 24 24" aria-hidden="true"><path class="bt-smiley-fire" d="M12 22a7 7 0 0 1-7-7c0-3.8 2.5-5.9 5.1-8.7.1 2.1.9 3.6 2.4 4.6 1.5-2.2 1.7-4.7.6-8 3.8 2.8 5.9 6.7 5.9 10.8A7 7 0 0 1 12 22z"/></svg>',
    check: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle class="bt-smiley-face" cx="12" cy="12" r="10"/><path class="bt-smiley-line" d="M7 12.3l3.1 3.1L17.5 8"/></svg>',
    cross: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle class="bt-smiley-face" cx="12" cy="12" r="10"/><path class="bt-smiley-line" d="M8.5 8.5l7 7M15.5 8.5l-7 7"/></svg>'
  };

  const SMILEY_ALIASES = {
    happy: 'smiley', cheesy: 'grin', grin: 'grin', lol: 'laugh', laugh: 'laugh',
    cry: 'sad', mad: 'angry', surprised: 'shocked', huh: 'confused', undecided: 'confused',
    sunglasses: 'cool', embarrassed: 'confused', lipsrsealed: 'confused', kiss: 'smiley',
    rolleyes: 'confused', evil: 'angry', azn: 'cool', afro: 'cool', police: 'cool',
    wassat: 'confused', chocked: 'shocked', skeptical: 'confused', facepalm: 'sad',
    thumbup: 'thumbsup', thumbs_up: 'thumbsup', thumbdown: 'thumbsdown', thumbs_down: 'thumbsdown',
    tick: 'check',
    // v2.10.16: SMF Bitcointalk serves the default-smile button as smile.gif
    // (not smiley.gif), so without this alias it slipped through and stayed
    // as the original GIF while every other glyph rendered as an emoji.
    smile: 'smiley'
  };

  // v2.10.16: yellow-face emoji set in the compose smiley panel. 🙂 and 🙁
  // render as outline glyphs on Apple Color Emoji and 😠/😡 render red, all
  // of which break visual coherence with the rest of the (filled-yellow) panel.
  // 😤 was tried for "angry" in v2.10.15 but the orange steam plumes still
  // read as visually different from a pure yellow face — switched to 😬
  // (Grimacing Face), which is plain yellow with bared teeth and reads as
  // "annoyed" without breaking the chromatic uniformity of the panel.
  const SMILEY_EMOJI = {
    smiley: '😊', wink: '😉', grin: '😀', laugh: '😄', sad: '😞', angry: '😬',
    shocked: '😮', cool: '😎', confused: '😕', tongue: '😛', heart: '❤️',
    thumbsup: '👍', thumbsdown: '👎', star: '⭐', fire: '🔥', check: '✅', cross: '❌'
  };

  function smileyGlyphForName(name) {
    const key = SMILEY_EMOJI[name] ? name : SMILEY_ALIASES[name];
    return key ? SMILEY_EMOJI[key] : null;
  }

  function smileySvgForName(name) {
    const key = SMILEY_SVGS[name] ? name : SMILEY_ALIASES[name];
    return key ? SMILEY_SVGS[key] : null;
  }

  function stylePostMetaButtons() {
    document.querySelectorAll('a').forEach(a => {
      if (a.dataset.btPostMeta === '1') return;
      const text = (a.textContent || '').trim();
      const href = a.getAttribute('href') || '';
      const isMerit = /^\+\s*merit\b/i.test(text);
      const isMsgNum = /^#\d+$/.test(text) &&
        /(topic=|#msg|action=display|action=profile)/i.test(href);
      if (isMerit || isMsgNum) {
        a.classList.add('bt-post-action');
        a.dataset.btPostMeta = '1';
      }
    });
  }

  function modernizeSmileys() {
    const imgs = document.querySelectorAll(
      'img[src*="Smileys/"], img[src*="/smileys/"], img.smiley'
    );
    imgs.forEach(img => {
      if (img.dataset.btSmiley === '1') return;
      const src = img.getAttribute('src') || '';
      if (!/(Smileys|smileys|smiley)/i.test(src) && !img.classList.contains('smiley')) return;
      const m = src.match(/\/(\w+)\.(?:gif|png|jpg)/i);
      const name = m ? m[1].toLowerCase() : '';
      const glyph = smileyGlyphForName(name);
      if (!glyph) return;

      const span = document.createElement('span');
      span.className = 'bt-smiley bt-smiley-modern bt-smiley-native';
      span.textContent = glyph;
      span.title = img.alt || img.title || name;

      img.dataset.btSmiley = '1';
      if (!Object.prototype.hasOwnProperty.call(img.dataset, 'btOriginalStyle')) {
        img.dataset.btOriginalStyle = img.getAttribute('style') || '';
      }
      img.style.setProperty('display', 'none', 'important');
      img.parentNode.insertBefore(span, img);
    });
    setModernizedControlsActive(isThemeActiveNow());
  }

  // ===========================================================================
  // CSS
  // ===========================================================================

  const css = `
    :root[data-bt-theme="dark"] {
      --bt-bg: #1A1D21;
      --bt-surface-1: #22262B;
      --bt-surface-2: #2A2F35;
      --bt-surface-3: #343A41;
      --bt-border: rgba(255,255,255,0.07);
      --bt-border-2: rgba(255,255,255,0.14);
      --bt-text: #E3E3E8;
      --bt-text-2: #A8B0BA;
      --bt-text-3: #6B7480;
      --bt-input-bg: #14171B;
      --bt-quote-bg: #1F2328;
      --bt-code-bg: #14171B;
      --bt-bg-end: #17191D;
      --bt-icon-filter: invert(1) opacity(0.75);
      --bt-shadow: 0 1px 0 rgba(0,0,0,0.2);
      --bt-shadow-lg: 0 12px 32px rgba(0,0,0,0.35);
    }
    :root[data-bt-theme="light"] {
      --bt-bg: #F2F4F7;
      --bt-surface-1: #E3E6EA;
      --bt-surface-2: #D7DAE0;
      --bt-surface-3: #C5C8CD;
      --bt-post-bg: #EEF3F7;
      --bt-post-alt-bg: #E3EBF1;
      --bt-poster-bg: #E8EEF3;
      --bt-poster-alt-bg: #DCE7EE;
      --bt-border: rgba(0,0,0,0.08);
      --bt-border-2: rgba(0,0,0,0.16);
      --bt-text: #1A1D21;
      --bt-text-2: #4A5058;
      --bt-text-3: #7A8088;
      --bt-input-bg: #FFFFFF;
      --bt-quote-bg: #E1E4E8;
      --bt-code-bg: #DBDEE3;
      --bt-bg-end: #E9ECEF;
      --bt-icon-filter: opacity(0.7);
      --bt-shadow: 0 1px 0 rgba(0,0,0,0.04);
      --bt-shadow-lg: 0 12px 32px rgba(0,0,0,0.12);
    }
    :root {
      --bt-radius: 10px;
      --bt-radius-sm: 6px;
      --bt-radius-lg: 14px;
      --bt-font: 'Source Sans 3', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      --bt-mono: 'Cascadia Code', 'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace;
      --bt-trans: 150ms ease;
      --bt-danger: #EF4444;
      --bt-accent: #F7931A;
      --bt-accent-h: #FFA733;
      --bt-accent-a: #E08410;
      --bt-on-accent: #1A1D21;
    }

    /*-- BT_ALWAYS_ON_START --*/
    :root[data-bt-active="false"] .bt-bbc-icon,
    :root[data-bt-active="false"] .bt-smiley,
    :root[data-bt-active="false"] .bt-topic-icon[data-bt-replacement="1"],
    :root[data-bt-active="false"] .bt-post-action[data-bt-replacement="1"] {
      display: none !important;
    }
    [data-bt-no-transitions] *,
    [data-bt-no-transitions] *::before,
    [data-bt-no-transitions] *::after {
      transition: none !important;
      animation-duration: 0s !important;
    }
    /*-- BT_ALWAYS_ON_END --*/

    html,
    body {
      background: var(--bt-bg) !important;
      color: var(--bt-text) !important;
      font-family: var(--bt-font) !important;
      font-size: 12px !important;
      -webkit-font-smoothing: antialiased;
      text-rendering: optimizeLegibility;
    }

    body {
      background:
        radial-gradient(140% 100% at 15% -10%, color-mix(in srgb, var(--bt-accent) 6%, transparent) 0%, transparent 55%),
        radial-gradient(120% 100% at 90% -5%,  color-mix(in srgb, var(--bt-accent) 3%, transparent) 0%, transparent 60%),
        linear-gradient(180deg, var(--bt-bg) 0%, var(--bt-bg-end) 100%) !important;
      background-attachment: fixed !important;
    }

    body, td, th, tr { color: var(--bt-text) !important; }
    h1, h2, h3, h4, h5, h6 {
      color: var(--bt-text) !important;
      font-weight: 600 !important;
      letter-spacing: -0.005em;
    }

    a, a:link, a:visited {
      color: var(--bt-accent) !important;
      text-decoration: none !important;
      transition: color var(--bt-trans) !important;
    }
    a:hover, a:active, a:focus {
      color: var(--bt-accent-h) !important;
      text-decoration: none !important;
    }

    #upper_section, #header, #wrapper, #content_section, #main_content_section,
    #footer_section, .frame, #bodyarea, .navigate_section, .roundframe,
    #content {
      background: transparent !important;
      color: var(--bt-text) !important;
    }

    table.bordercolor,
    table.table_list,
    table.table_grid,
    .tborder {
      background: var(--bt-surface-1) !important;
      border: 1px solid var(--bt-border-2) !important;
      border-radius: var(--bt-radius) !important;
      overflow: hidden !important;
      border-spacing: 0 !important;
      box-shadow: var(--bt-shadow);
    }

    [class*="windowbg"],
    td[class*="windowbg"],
    tr[class*="windowbg"] > td {
      background-color: var(--bt-surface-1) !important;
      background-image: none !important;
      color: var(--bt-text) !important;
      border-color: var(--bt-border) !important;
      border-bottom: 1px solid var(--bt-border) !important;
    }
    tr.windowbg:hover > td,
    tr.windowbg2:hover > td,
    tr.windowbg3:hover > td {
      background-color: var(--bt-surface-2) !important;
    }

    /* v2.8.1: restore the classic-looking post alternation on light-theme
       thread pages. The previous attempt only caught a narrow header row on
       some thread layouts (the "Report to moderator" strip), because the
       actual post body/sidebar wrappers are often .td_headerandpost, .post,
       .poster and .poster_info rather than the row cell that carries
       windowbg/windowbg2 itself.

       We therefore paint BOTH the row cells and the common wrappers inside
       them, so the entire post block alternates again like classic
       bitcointalk/SMF. */
    :root[data-bt-theme="light"] tr.windowbg > td,
    :root[data-bt-theme="light"] td.windowbg,
    :root[data-bt-theme="light"] tr.windowbg td.td_headerandpost,
    :root[data-bt-theme="light"] td.windowbg.td_headerandpost,
    :root[data-bt-theme="light"] tr.windowbg td.post,
    :root[data-bt-theme="light"] td.windowbg.post,
    :root[data-bt-theme="light"] tr.windowbg td.poster,
    :root[data-bt-theme="light"] tr.windowbg td.poster_info,
    :root[data-bt-theme="light"] td.windowbg.poster,
    :root[data-bt-theme="light"] td.windowbg.poster_info {
      background-color: #e9f0f7 !important;
      background-image: none !important;
    }

    :root[data-bt-theme="light"] tr.windowbg2 > td,
    :root[data-bt-theme="light"] td.windowbg2,
    :root[data-bt-theme="light"] tr.windowbg2 td.td_headerandpost,
    :root[data-bt-theme="light"] td.windowbg2.td_headerandpost,
    :root[data-bt-theme="light"] tr.windowbg2 td.post,
    :root[data-bt-theme="light"] td.windowbg2.post,
    :root[data-bt-theme="light"] tr.windowbg2 td.poster,
    :root[data-bt-theme="light"] tr.windowbg2 td.poster_info,
    :root[data-bt-theme="light"] td.windowbg2.poster,
    :root[data-bt-theme="light"] td.windowbg2.poster_info {
      background-color: #dfe9f3 !important;
      background-image: none !important;
    }

    :root[data-bt-theme="light"] tr.windowbg3 > td,
    :root[data-bt-theme="light"] td.windowbg3,
    :root[data-bt-theme="light"] tr.windowbg3 td.td_headerandpost,
    :root[data-bt-theme="light"] td.windowbg3.td_headerandpost,
    :root[data-bt-theme="light"] tr.windowbg3 td.post,
    :root[data-bt-theme="light"] td.windowbg3.post,
    :root[data-bt-theme="light"] tr.windowbg3 td.poster,
    :root[data-bt-theme="light"] tr.windowbg3 td.poster_info,
    :root[data-bt-theme="light"] td.windowbg3.poster,
    :root[data-bt-theme="light"] td.windowbg3.poster_info {
      background-color: #e9f0f7 !important;
      background-image: none !important;
    }

    :root[data-bt-theme="light"] tr.windowbg:hover > td,
    :root[data-bt-theme="light"] tr.windowbg:hover td.td_headerandpost,
    :root[data-bt-theme="light"] tr.windowbg:hover td.post,
    :root[data-bt-theme="light"] tr.windowbg:hover td.poster,
    :root[data-bt-theme="light"] tr.windowbg:hover td.poster_info,
    :root[data-bt-theme="light"] tr.windowbg2:hover > td,
    :root[data-bt-theme="light"] tr.windowbg2:hover td.td_headerandpost,
    :root[data-bt-theme="light"] tr.windowbg2:hover td.post,
    :root[data-bt-theme="light"] tr.windowbg2:hover td.poster,
    :root[data-bt-theme="light"] tr.windowbg2:hover td.poster_info,
    :root[data-bt-theme="light"] tr.windowbg3:hover > td,
    :root[data-bt-theme="light"] tr.windowbg3:hover td.td_headerandpost,
    :root[data-bt-theme="light"] tr.windowbg3:hover td.post,
    :root[data-bt-theme="light"] tr.windowbg3:hover td.poster,
    :root[data-bt-theme="light"] tr.windowbg3:hover td.poster_info {
      background-color: #d7e3ef !important;
      background-image: none !important;
    }

    .poster, td.poster, .poster_info, td.poster_info, [id*="poster"] {
      background-color: var(--bt-surface-1) !important;
      background-image: none !important;
      color: var(--bt-text) !important;
    }
    :root[data-bt-theme="dark"] td.poster_info *,
    :root[data-bt-theme="dark"] td.poster *,
    :root[data-bt-theme="dark"] .poster_info * {
      color: var(--bt-text-2) !important;
    }
    :root[data-bt-theme="dark"] td.poster_info b a,
    :root[data-bt-theme="dark"] td.poster_info h4 a,
    :root[data-bt-theme="dark"] .poster_info b a,
    :root[data-bt-theme="dark"] .poster_info h4 a {
      color: var(--bt-text) !important;
      font-weight: 600 !important;
    }
    :root[data-bt-theme="dark"] td.poster_info b a:hover,
    :root[data-bt-theme="dark"] td.poster_info h4 a:hover,
    :root[data-bt-theme="dark"] .poster_info b a:hover,
    :root[data-bt-theme="dark"] .poster_info h4 a:hover {
      color: var(--bt-accent) !important;
    }
    :root[data-bt-theme="dark"] td.poster_info a,
    :root[data-bt-theme="dark"] .poster_info a {
      color: var(--bt-accent) !important;
    }
    :root[data-bt-theme="dark"] td.poster_info a:hover,
    :root[data-bt-theme="dark"] .poster_info a:hover {
      color: var(--bt-accent-h) !important;
    }
    :root[data-bt-theme="dark"] td.poster_info img,
    :root[data-bt-theme="dark"] .poster_info img { color: initial !important; }

    table.bordercolor tr[style] > td,
    table.bordercolor td[style],
    .tborder tr[style] > td,
    .tborder td[style] {
      background-color: var(--bt-surface-1) !important;
      background-image: none !important;
    }

    [class*="catbg"], [class*="titlebg"], td[class*="catbg"], td[class*="titlebg"],
    th[class*="catbg"], th[class*="titlebg"], tr[class*="catbg"] > td,
    tr[class*="catbg"] > th, tr[class*="catbg"] td, tr[class*="catbg"] th,
    tr[class*="titlebg"] > td, tr[class*="titlebg"] > th, tr[class*="titlebg"] td,
    tr[class*="titlebg"] th, h3[class*="catbg"], h4[class*="catbg"],
    h3[class*="titlebg"], h4[class*="titlebg"] {
      background-color: var(--bt-surface-2) !important;
      background-image: none !important;
      color: var(--bt-text) !important;
      border-bottom: 1px solid var(--bt-border-2) !important;
      font-weight: 600 !important;
      letter-spacing: 0.01em;
      text-shadow: none !important;
    }
    [class*="catbg"] a, [class*="titlebg"] a, tr[class*="catbg"] a, tr[class*="titlebg"] a {
      color: var(--bt-text) !important;
    }
    [class*="catbg"] a:hover, [class*="titlebg"] a:hover, tr[class*="catbg"] a:hover, tr[class*="titlebg"] a:hover {
      color: var(--bt-accent) !important;
    }

    /* v2.10.39: catbg/titlebg headers (the navy/grey banner above content
       sections) include the topic header row "Topic: ... (Read N times)"
       above each thread's posts. SMF inherits a large font-size for these
       cells from the legacy theme stylesheet, which makes the topic title
       look oversized vs the body text and the breadcrumb. Bring it back
       down to match the classic SMF Bitcointalk subject size: 13-14px,
       semibold (the bold weight is already set above). */
    body [class*="catbg"],
    body [class*="titlebg"],
    body td[class*="catbg"],
    body td[class*="titlebg"],
    body tr[class*="catbg"] > td,
    body tr[class*="titlebg"] > td,
    body [class*="catbg"] b,
    body [class*="titlebg"] b,
    body [class*="catbg"] span:not(.smalltext),
    body [class*="titlebg"] span:not(.smalltext),
    body [class*="catbg"] a,
    body [class*="titlebg"] a {
      font-size: 14px !important;
      line-height: 1.4 !important;
    }

    /* v3.0.35: data in alto a destra (span.smalltext dentro #smfheader td.titlebg2)
       deve rimanere a 13px — escluso dalla regola sopra tramite :not(.smalltext)
       e ribadito qui per battere qualsiasi .smalltext baseline */
    body [class*="catbg"] span.smalltext,
    body [class*="titlebg"] span.smalltext,
    #smfheader td.titlebg2 span.smalltext,
    #smfheader td.titlebg2[align="right"] span.smalltext,
    #smfheader td.titlebg2 span.smalltext * {
      font-size: 13px !important;
      line-height: normal !important;
    }

    /* v2.10.28: when the Classic (sky) preset is active in the light theme,
       reproduce the SMF Bitcointalk gradient look more faithfully:
        - catbg = top-level category banners → deep blue gradient with white text
        - titlebg = sub-section headers → light blue gradient with dark text
       This matches the original cascade where each header layer has its own
       distinct identity instead of both falling back to the same surface tone. */
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body [class*="catbg"],
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body td[class*="catbg"],
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body th[class*="catbg"],
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body tr[class*="catbg"] > td,
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body tr[class*="catbg"] > th {
      background: linear-gradient(180deg, #8FA8CE 0%, #6E8EBE 100%) !important;
      color: #FFFFFF !important;
      border-bottom: 1px solid #5C7AAB !important;
    }
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body [class*="catbg"] a,
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body tr[class*="catbg"] a {
      color: #FFFFFF !important;
    }
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body [class*="catbg"] a:hover,
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body tr[class*="catbg"] a:hover {
      color: #FFE08A !important;
      text-decoration: underline !important;
    }

    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body [class*="titlebg"],
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body td[class*="titlebg"],
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body th[class*="titlebg"],
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body tr[class*="titlebg"] > td,
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body tr[class*="titlebg"] > th {
      background: linear-gradient(180deg, #DDE5F0 0%, #C2D2E5 100%) !important;
      color: #1F2A3A !important;
      border-bottom: 1px solid #A6B7CD !important;
    }

    /* Classic Bitcointalk uses the legendary #225588 link blue everywhere
       except inside the dark catbg banners. Reinforce that for normal body
       links so the page reads as authentically classic. */
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body td[class*="windowbg"] a:link,
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body td[class*="windowbg"] a:visited,
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body .post a:link,
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body .post a:visited {
      color: #225588 !important;
    }
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body td[class*="windowbg"] a:hover,
    :root[data-bt-theme="light"][data-bt-main="sky"][data-bt-active="true"] body .post a:hover {
      color: #1A4570 !important;
      text-decoration: underline !important;
    }

    th {
      background-color: var(--bt-surface-2) !important;
      background-image: none !important;
      color: var(--bt-text) !important;
    }

    .post, td.post, #preview_body, #preview_section, #preview_section td,
    #preview_section table, .personalmessage, td.personalmessage {
      background-color: var(--bt-surface-1) !important;
      background-image: none !important;
      color: var(--bt-text) !important;
    }
    .post { line-height: 1.6 !important; font-size: 14px !important; }
    .signature {
      color: var(--bt-text-3) !important;
      border-top: 1px solid var(--bt-border) !important;
      padding-top: 8px !important;
      margin-top: 12px !important;
      font-size: 12px !important;
    }
    .smalltext, .middletext, .small_header, .nav, a.nav, .navPages, .prevnext,
    .lastpostcol, .ignored_topic, .utusers, .utusers a {
      color: var(--bt-text-2) !important;
    }
    .smalltext a, .middletext a, .small_header a, .lastpostcol a {
      color: var(--bt-accent) !important;
    }

    .quoteheader, .codeheader {
      background: var(--bt-surface-3) !important;
      color: var(--bt-text-2) !important;
      border: 1px solid var(--bt-border) !important;
      border-bottom: none !important;
      border-radius: var(--bt-radius-sm) var(--bt-radius-sm) 0 0 !important;
      padding: 6px 10px !important;
      font-size: 12px !important;
      font-weight: 500 !important;
    }
    .codeheader {
      box-sizing: border-box !important;
      width: 96% !important;
      max-width: 100% !important;
    }
    .quote {
      background: var(--bt-quote-bg) !important;
      color: var(--bt-text-2) !important;
      border: 1px solid var(--bt-border) !important;
      border-left: 3px solid var(--bt-accent) !important;
      border-radius: 0 var(--bt-radius-sm) var(--bt-radius-sm) var(--bt-radius-sm) !important;
      padding: 10px 14px !important;
      margin: 6px 0 12px !important;
    }
    .code {
      background: var(--bt-code-bg) !important;
      color: var(--bt-text) !important;
      font-family: var(--bt-mono) !important;
      border: 1px solid var(--bt-border) !important;
      border-radius: 0 0 var(--bt-radius-sm) var(--bt-radius-sm) !important;
      padding: 10px 14px !important;
      box-sizing: border-box !important;
      width: 96% !important;
      max-width: 100% !important;
      overflow: auto !important;
    }

    .button_submit, .button_reset, input[type="submit"], input[type="button"],
    input[type="reset"], button:not([id^="bt-"]):not([class^="bt-sw-"]),
    .button:not([id^="bt-"]) {
      background: var(--bt-accent) !important;
      color: var(--bt-on-accent) !important;
      border: 1px solid var(--bt-accent) !important;
      border-radius: var(--bt-radius-sm) !important;
      padding: 5px 10px !important;
      font-family: var(--bt-font) !important;
      font-weight: 600 !important;
      font-size: 12px !important;
      cursor: pointer !important;
      transition: background var(--bt-trans), transform var(--bt-trans), box-shadow var(--bt-trans) !important;
      box-shadow: none !important;
      text-shadow: none !important;
    }
    .button_submit:hover, .button_reset:hover, input[type="submit"]:hover,
    input[type="button"]:hover, input[type="reset"]:hover,
    button:not([id^="bt-"]):not([class^="bt-sw-"]):hover,
    .button:not([id^="bt-"]):hover {
      background: var(--bt-accent-h) !important;
      border-color: var(--bt-accent-h) !important;
      box-shadow: 0 2px 12px color-mix(in srgb, var(--bt-accent) 30%, transparent) !important;
      transform: translateY(-1px);
      color: var(--bt-on-accent) !important;
    }
    .button_submit:active, input[type="submit"]:active {
      background: var(--bt-accent-a) !important;
      transform: translateY(0) !important;
    }

    a.button {
      background: transparent !important;
      color: var(--bt-text) !important;
      border: 1px solid var(--bt-border-2) !important;
    }
    a.button:hover {
      border-color: var(--bt-accent) !important;
      color: var(--bt-accent) !important;
      background: color-mix(in srgb, var(--bt-accent) 10%, transparent) !important;
      box-shadow: none !important;
      transform: none !important;
    }

    /* v2.10.27: topic action bar (Reply | Unwatch | Notify | Mark unread |
       Print | View topic archive) compacted vertically and horizontally so
       it occupies less of the precious top/bottom-of-thread real estate.
       Reductions: padding 3×7 → 2×6, font-size 12→11px, letter-spacing
       0.04em→0.02em, margin-right 3→2px. Results in a strip roughly 25%
       shorter without losing readability. */
    a.quotebutton, a.replybutton, a.modifybutton, a.removebutton, a.reportbutton,
    a.splitbutton, a.notifybutton, a.markreadbutton, a.unwatchbutton,
    a.printbutton, a.archivebutton {
      display: inline-block !important;
      background: transparent !important;
      color: var(--bt-text-2) !important;
      border: 1px solid var(--bt-border-2) !important;
      border-radius: var(--bt-radius-sm) !important;
      padding: 2px 6px !important;
      margin: 0 2px 0 0 !important;
      font-family: var(--bt-font) !important;
      font-weight: 500 !important;
      font-size: 11px !important;
      text-transform: uppercase !important;
      letter-spacing: 0.02em !important;
      line-height: 1.3 !important;
      text-decoration: none !important;
      box-shadow: none !important;
      text-shadow: none !important;
      transition: background var(--bt-trans), color var(--bt-trans), border-color var(--bt-trans) !important;
      cursor: pointer !important;
    }
    a.quotebutton:hover, a.replybutton:hover, a.modifybutton:hover,
    a.removebutton:hover, a.reportbutton:hover, a.splitbutton:hover,
    a.notifybutton:hover, a.markreadbutton:hover, a.unwatchbutton:hover,
    a.printbutton:hover, a.archivebutton:hover {
      background: var(--bt-accent) !important;
      color: var(--bt-on-accent) !important;
      border-color: var(--bt-accent) !important;
      transform: none !important;
      box-shadow: none !important;
    }

    .bt-post-action {
      display: inline-block !important;
      padding: 3px 7px !important;
      margin: 0 3px 0 0 !important;
      background: transparent !important;
      color: var(--bt-text-2) !important;
      border: 1px solid var(--bt-border-2) !important;
      border-radius: var(--bt-radius-sm) !important;
      font-family: var(--bt-font) !important;
      font-weight: 500 !important;
      font-size: 12px !important;
      text-transform: uppercase !important;
      letter-spacing: 0.04em !important;
      cursor: pointer !important;
      vertical-align: middle !important;
      transition: background var(--bt-trans), color var(--bt-trans), border-color var(--bt-trans) !important;
    }
    .bt-post-action:hover {
      background: var(--bt-accent) !important;
      color: var(--bt-on-accent) !important;
      border-color: var(--bt-accent) !important;
      text-decoration: none !important;
    }
    a.bt-post-action, a.bt-post-action:link, a.bt-post-action:visited {
      color: var(--bt-text-2) !important;
    }
    a.bt-post-action:hover { color: var(--bt-on-accent) !important; }
    span[title="Quote to clipboard"] { display: none !important; }

    input[type="text"], input[type="password"], input[type="email"],
    input[type="search"], input[type="url"], input[type="number"],
    textarea, select {
      background: var(--bt-input-bg) !important;
      color: var(--bt-text) !important;
      border: 1px solid var(--bt-border-2) !important;
      border-radius: var(--bt-radius-sm) !important;
      padding: 8px 10px !important;
      font-family: var(--bt-font) !important;
      font-size: 13px !important;
      transition: border-color var(--bt-trans), box-shadow var(--bt-trans) !important;
      outline: none !important;
    }
    input[type="text"]:focus, input[type="password"]:focus,
    input[type="email"]:focus, input[type="search"]:focus,
    textarea:focus, select:focus {
      border-color: var(--bt-accent) !important;
      box-shadow: 0 0 0 3px color-mix(in srgb, var(--bt-accent) 20%, transparent) !important;
    }
    textarea {
      min-height: 120px !important;
      line-height: 1.5 !important;
      font-family: var(--bt-font) !important;
      font-size: 13px !important;
    }

    #bbcBox_message, .bbc_buttons, #post_modify, .post_buttons {
      background: var(--bt-surface-2) !important;
      border: 1px solid var(--bt-border) !important;
      border-radius: var(--bt-radius-sm) !important;
      padding: 6px !important;
    }
    #bbcBox_message img, .bbc_buttons img {
      filter: var(--bt-icon-filter) !important;
      transition: filter var(--bt-trans) !important;
    }
    #bbcBox_message img:hover, .bbc_buttons img:hover { filter: none !important; }

    .bt-bbc-icon {
      display: inline-flex !important;
      align-items: center !important;
      justify-content: center !important;
      width: 26px !important;
      height: 26px !important;
      margin: 1px !important;
      padding: 4px !important;
      border-radius: var(--bt-radius-sm) !important;
      color: var(--bt-text-2) !important;
      background: transparent !important;
      cursor: pointer !important;
      vertical-align: middle !important;
      transition: background var(--bt-trans), color var(--bt-trans) !important;
      box-sizing: border-box !important;
    }
    .bt-bbc-icon svg {
      width: 100% !important;
      height: 100% !important;
      display: block !important;
    }
    .bt-bbc-icon:hover {
      background: color-mix(in srgb, var(--bt-accent) 18%, transparent) !important;
      color: var(--bt-accent) !important;
    }

    .bt-topic-icon {
      display: inline-flex !important;
      align-items: center !important;
      justify-content: center !important;
      width: 17px !important;
      height: 17px !important;
      margin: 0 4px 0 0 !important;
      padding: 1px !important;
      border-radius: 4px !important;
      color: var(--bt-text-2) !important;
      background: transparent !important;
      border: none !important;
      vertical-align: -3px !important;
      box-sizing: border-box !important;
      transition: color var(--bt-trans), background var(--bt-trans), opacity var(--bt-trans) !important;
      opacity: 1 !important;
    }
    .bt-topic-icon svg {
      width: 100% !important;
      height: 100% !important;
      display: block !important;
      stroke-width: 2.25 !important;
    }
    .bt-topic-icon:hover {
      color: var(--bt-accent) !important;
      background: color-mix(in srgb, var(--bt-accent) 10%, transparent) !important;
    }

    /* v2.10.25: distinct hue for very-hot topics so they pop visually beyond
       the default accent. Hot stays on the accent (warm orange) for continuity,
       very-hot bumps to a saturated red — a visual "+1 alarm" over hot. Poll
       gets its own colour (purple/indigo) so it's identifiable as a poll at a
       glance without reading the icon shape. */
    .bt-topic-icon--hot,
    .bt-topic-icon--question,
    .bt-topic-icon--idea,
    .bt-topic-icon--thumbsUp {
      color: var(--bt-accent) !important;
    }
    .bt-topic-icon--veryhot {
      color: #d93025 !important; /* fire-red, theme-agnostic for instant recognition */
    }
    .bt-topic-icon--poll {
      color: #8b5cf6 !important; /* indigo/violet — consistent across light & dark themes */
    }
    .bt-topic-icon--important,
    .bt-topic-icon--thumbsDown,
    .bt-topic-icon--angry {
      color: var(--bt-danger) !important;
    }
    .bt-topic-icon--standard,
    .bt-topic-icon--smile {
      color: var(--bt-text-2) !important;
    }

    /* Locked must be clearly visible; 2.10.4 made some locks too faint or only
       treated them as inline status. */
    .bt-topic-icon--locked {
      color: var(--bt-text) !important;
      width: 18px !important;
      height: 18px !important;
      opacity: 1 !important;
    }

    /* Inline status icons inserted beside the actual topic title. This is used
       for the PIN/sticky marker so it appears on the title side, not in the
       left topic-state icon column. */
    .bt-topic-icon--inline-status {
      width: 15px !important;
      height: 15px !important;
      margin: 0 2px !important;
      padding: 0 !important;
      border-radius: 0 !important;
      background: transparent !important;
      border: none !important;
      vertical-align: -2px !important;
      opacity: 1 !important;
    }
    .bt-topic-icon--inline-status:hover {
      background: transparent !important;
      color: var(--bt-accent) !important;
    }
    .bt-topic-icon--sticky {
      color: var(--bt-accent) !important;
    }
    .bt-topic-icon--inline-status.bt-topic-icon--locked {
      color: var(--bt-text) !important;
      width: 15px !important;
      height: 15px !important;
    }

    /* v2.10.15: inline status IMGs whose src has been swapped to the modern
       SVG data-URL (lock + new). The browser sizes them from the SVG's
       width/height attributes, so we just need to harmonise vertical
       alignment with the topic title baseline and add a small horizontal
       margin so they don't kiss the surrounding text. */
    img.bt-inline-status-img {
      vertical-align: -2px !important;
      margin: 0 4px !important;
      border: 0 !important;
      background: transparent !important;
      box-shadow: none !important;
      filter: none !important;
      opacity: 1 !important;
    }
    img.bt-inline-status-img--locked {
      /* SMF places this with align="right". Keep the baseline cleaner than
         the SVG's natural drop. */
      vertical-align: -3px !important;
    }
    img.bt-inline-status-img--sticky {
      /* v2.10.16: pushpin sits on the right edge alongside the lock. Tilted
         needle reads better with a slight baseline drop. */
      vertical-align: -3px !important;
    }
    img.bt-inline-status-img--newPost {
      vertical-align: -2px !important;
    }

    /* v2.10.21: sticky tint applies ONLY to the subject cell (the <td> that
       holds the pin and the topic title), not the entire row. Classic
       Bitcointalk highlights only the title column. We detect the subject
       cell as the one whose direct child is the inline sticky IMG. */
    :root[data-bt-theme="light"] {
      /* v2.10.22: tint lightened from #d4dde7 to #dce5ef — closer to the
         normal windowbg (#e9f0f7) so the highlight reads as a subtle
         distinction rather than a strong band. */
      --bt-sticky-row-bg: #dce5ef;
      --bt-sticky-row-bg-hover: #d4dde7;
    }
    :root[data-bt-theme="dark"] {
      --bt-sticky-row-bg: #2a2d33;
      --bt-sticky-row-bg-hover: #32363d;
    }
    :root[data-bt-active="true"][data-bt-theme] body tr.bt-row-sticky > td.bt-cell-sticky-subject {
      background-color: var(--bt-sticky-row-bg) !important;
      background-image: none !important;
    }
    :root[data-bt-active="true"][data-bt-theme] body tr.bt-row-sticky:hover > td.bt-cell-sticky-subject {
      background-color: var(--bt-sticky-row-bg-hover) !important;
    }

    .bt-topic-icon--newPost {
      width: auto !important;
      min-width: 28px !important;
      height: 14px !important;
      margin: 0 4px 0 2px !important;
      padding: 1px 4px !important;
      border-radius: 4px !important;
      background: color-mix(in srgb, var(--bt-accent) 14%, transparent) !important;
      border: 1px solid color-mix(in srgb, var(--bt-accent) 34%, transparent) !important;
      color: var(--bt-accent) !important;
      font-family: var(--bt-font) !important;
      font-size: 9px !important;
      font-weight: 700 !important;
      letter-spacing: 0.05em !important;
      line-height: 1 !important;
      text-transform: uppercase !important;
      vertical-align: 1px !important;
      opacity: 1 !important;
    }
    .bt-topic-icon--newPost:hover {
      background: color-mix(in srgb, var(--bt-accent) 20%, transparent) !important;
      color: var(--bt-accent-h) !important;
    }

    .bt-smiley {
      display: inline-block !important;
      font-size: 1.15em !important;
      line-height: 1 !important;
      vertical-align: -0.12em !important;
      font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji",
                   "Twemoji Mozilla", sans-serif !important;
    }

    [class*="maintab_"],
    [class*="mirrortab_"] {
      display: inline-block !important;
      margin: 0 !important;
      padding: 0 !important;
      border-radius: 0 !important;
      background-color: var(--bt-surface-2) !important;
      background-image: none !important;
      border: 1px solid var(--bt-border) !important;
      border-left: none !important;
      box-shadow: none !important;
      text-shadow: none !important;
      transition: background-color var(--bt-trans), border-color var(--bt-trans) !important;
      vertical-align: middle !important;
      line-height: 1 !important;
      width: auto !important;
      min-width: 0 !important;
      height: auto !important;
    }
    .maintab_first,
    .mirrortab_first,
    .maintab_active_first + .maintab_active_back,
    .mirrortab_active_first + .mirrortab_active_back {
      border-left: 1px solid var(--bt-border) !important;
      border-top-left-radius: var(--bt-radius-sm) !important;
      border-bottom-left-radius: var(--bt-radius-sm) !important;
    }
    .maintab_last,
    .mirrortab_last,
    .maintab_active_back + .maintab_active_last,
    .mirrortab_active_back + .mirrortab_active_last {
      border-top-right-radius: var(--bt-radius-sm) !important;
      border-bottom-right-radius: var(--bt-radius-sm) !important;
    }
    [class*="maintab_"] a,
    [class*="maintab_"] a:link,
    [class*="maintab_"] a:visited,
    [class*="mirrortab_"] a,
    [class*="mirrortab_"] a:link,
    [class*="mirrortab_"] a:visited {
      display: inline-block !important;
      padding: 6px 14px !important;
      margin: 0 !important;
      color: var(--bt-text-2) !important;
      font-family: var(--bt-font) !important;
      font-weight: 500 !important;
      font-size: 13px !important; /* v3.0.35: was 12px */
      text-transform: uppercase !important;
      letter-spacing: 0.05em !important;
      transition: color var(--bt-trans) !important;
      background: transparent !important;
      background-image: none !important;
      border: none !important;
      box-shadow: none !important;
      text-shadow: none !important;
      line-height: 1 !important;
    }

    /* ----------------------------------------------------------------------
       New-PM highlight — client-side port of PowerGlove's SMF patch. When the
       PM maintab has unread messages (flagged by highlightUnreadPm), show a
       pulsing accent dot before the link, in place of the original icon.
       ---------------------------------------------------------------------- */
    [class*="maintab_"] a.bt-pm-unread::before,
    #main_menu a.bt-pm-unread::before,
    .dropmenu a.bt-pm-unread::before {
      content: "" !important;
      display: inline-block !important;
      width: 7px !important;
      height: 7px !important;
      margin-right: 7px !important;
      border-radius: 50% !important;
      background: var(--bt-accent) !important;
      vertical-align: 1px !important;
      animation: bt-pm-pulse 2s ease-out infinite !important;
    }
    .maintab_active_back a.bt-pm-unread::before,
    .mirrortab_active_back a.bt-pm-unread::before {
      background: var(--bt-on-accent) !important;
    }
    @keyframes bt-pm-pulse {
      0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--bt-accent) 55%, transparent); }
      70%  { box-shadow: 0 0 0 6px color-mix(in srgb, var(--bt-accent) 0%, transparent); }
      100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--bt-accent) 0%, transparent); }
    }

    /* v2.10.29: compact override ONLY for mirrortab_ (topic action bar:
       Reply | Unwatch | Notify | Mark unread | Print | view topic archive).
       Earlier attempts failed because the markup uses raw <a> elements
       inside <td class="mirrortab_back">, NOT a.replybutton/a.notifybutton —
       those classes don't exist in this SMF theme. The general maintab/
       mirrortab rule above (padding 6×14, font-size 12px, letter-spacing
       0.05em) is shared with the top navigation menu, so we override only
       mirrortab here to avoid affecting the main nav. */
    [class*="mirrortab_"] a,
    [class*="mirrortab_"] a:link,
    [class*="mirrortab_"] a:visited {
      padding: 2px 4px !important; /* v3.2.1: tighter so the topic action bar (Reply|Unwatch|…|Print) fits on one line */
      font-size: 11px !important;
      letter-spacing: 0.02em !important;
      line-height: 1.3 !important;
    }
    /* Tighten the cell itself so vertical padding from the <td> doesn't
       reintroduce the height we just removed from the links. */
    td[class*="mirrortab_"] {
      padding-top: 2px !important;
      padding-bottom: 2px !important;
    }
    .maintab_first:hover, .maintab_back:hover, .maintab_last:hover {
      background-color: var(--bt-surface-3) !important;
      border-color: var(--bt-border-2) !important;
    }
    .maintab_first:hover a, .maintab_back:hover a, .maintab_last:hover a {
      color: var(--bt-text) !important;
    }
    .maintab_active_back,
    .mirrortab_active_back {
      background-color: var(--bt-accent) !important;
      border-color: var(--bt-accent) !important;
    }
    .maintab_active_back a,
    .maintab_active_back a:hover,
    .mirrortab_active_back a,
    .mirrortab_active_back a:hover {
      color: var(--bt-on-accent) !important;
      font-weight: 600 !important;
    }

    /* v2.7.15: In LIGHT themes, selected main/mirror tabs use a
       theme-derived extrapolated colour: it continues from Main -> surface3
       by the same interval again. In other words, if surface3 is the
       "intermediate/illuminated" tone, the active tab doubles that HSL move. */
    :root[data-bt-theme="light"] .maintab_active_back,
    :root[data-bt-theme="light"] .mirrortab_active_back {
      background-color: var(--bt-nav-active-bg, var(--bt-surface-3)) !important;
      border-color: var(--bt-nav-active-bg, var(--bt-border-2)) !important;
    }
    :root[data-bt-theme="light"] .maintab_active_back a,
    :root[data-bt-theme="light"] .maintab_active_back a:hover,
    :root[data-bt-theme="light"] .mirrortab_active_back a,
    :root[data-bt-theme="light"] .mirrortab_active_back a:hover {
      color: var(--bt-nav-active-text, var(--bt-text)) !important;
      font-weight: 600 !important;
    }
    .maintab_active_first.maintab_active_first,
    .maintab_active_last.maintab_active_last,
    .mirrortab_active_first.mirrortab_active_first,
    .mirrortab_active_last.mirrortab_active_last {
      display: none !important;
    }

    #main_menu, .dropmenu {
      background: transparent !important;
      border: none !important;
    }
    #main_menu li, .dropmenu li {
      background: var(--bt-surface-2) !important;
      border: 1px solid var(--bt-border) !important;
      border-radius: var(--bt-radius-sm) !important;
      margin-right: 4px !important;
    }
    #main_menu li.chosen, .dropmenu li.chosen,
    #main_menu li.active, .dropmenu li.active {
      background: var(--bt-accent) !important;
      border-color: var(--bt-accent) !important;
    }
    #main_menu li a, .dropmenu li a {
      color: var(--bt-text-2) !important;
      padding: 6px 14px !important;
      font-size: 13px !important; /* v3.0.35: was 12px */
      text-transform: uppercase !important;
      letter-spacing: 0.05em !important;
    }
    #main_menu li.chosen a, .dropmenu li.chosen a,
    #main_menu li.active a, .dropmenu li.active a {
      color: var(--bt-on-accent) !important;
      font-weight: 600 !important;
    }

    #upper_section {
      border-bottom: 1px solid var(--bt-border) !important;
      position: relative !important;
    }
    #top_section, #upshrink_header, .navigate_section ul, .navigate_section {
      background: transparent !important;
      color: var(--bt-text-2) !important;
    }
    .navigate_section a, .navigate_section a:link, .navigate_section a:visited {
      color: var(--bt-text-2) !important;
    }
    .navigate_section a:hover { color: var(--bt-accent) !important; }
    .navigate_section .last a, .navigate_section li:last-child a {
      color: var(--bt-text) !important;
      font-weight: 600 !important;
    }
    /* v2.10.33: breadcrumb sizing rule moved below the smalltext/middletext
       block — see the override declared after that block. The classic
       breadcrumb sits inside <td class="middletext">, which the smalltext
       block was sizing down to --bt-fs-small. Since CSS at equal specificity
       is decided by source order, the override has to come after. */
    #header img, #logo { filter: brightness(1.05) !important; }

    hr {
      border: none !important;
      border-top: 1px solid var(--bt-border) !important;
      margin: 16px 0 !important;
    }

    #footer_section, .copywrite {
      background: transparent !important;
      color: var(--bt-text-3) !important;
      border-top: 1px solid var(--bt-border) !important;
    }
    #footer_section a, .copywrite a { color: var(--bt-text-2) !important; }
    #footer_section a:hover, .copywrite a:hover { color: var(--bt-accent) !important; }

    .information, .descbox, .infobox {
      background: var(--bt-surface-2) !important;
      color: var(--bt-text-2) !important;
      border: 1px solid var(--bt-border) !important;
      border-radius: var(--bt-radius-sm) !important;
      padding: 10px 12px !important;
    }
    .errorbox, .noticebox {
      background: color-mix(in srgb, var(--bt-danger) 12%, transparent) !important;
      border: 1px solid color-mix(in srgb, var(--bt-danger) 40%, transparent) !important;
      color: var(--bt-danger) !important;
      border-radius: var(--bt-radius-sm) !important;
      padding: 10px 12px !important;
    }

    .pagelinks { color: var(--bt-text-2) !important; }
    .pagelinks a, .pagelinks a:link, .pagelinks a:visited {
      color: var(--bt-text-2) !important;
      padding: 4px 8px !important;
      border-radius: var(--bt-radius-sm) !important;
      transition: background var(--bt-trans), color var(--bt-trans) !important;
    }
    .pagelinks a:hover {
      background: color-mix(in srgb, var(--bt-accent) 12%, transparent) !important;
      color: var(--bt-accent) !important;
    }
    .pagelinks strong {
      background: var(--bt-accent) !important;
      color: var(--bt-on-accent) !important;
      padding: 4px 8px !important;
      border-radius: var(--bt-radius-sm) !important;
    }

    /* v2.10.26: top-of-page pagelinks (« 1 2 3 ... 1234 » above topic tables
       or post lists) are page-level navigation and must be readable — same
       size as body text in classic Bitcointalk. Earlier versions collapsed
       them all to --bt-fs-pagelinks (10px), which is appropriate only for
       the per-topic mini paginators inside subject cells. The dedicated
       per-topic rule (further down, scoped to td[class*="windowbg"] with
       :has(> a[href*="topic="])) keeps those at 10px without affecting the
       page-level paginator. */
    .pagelinks,
    .pagelinks a,
    .pagelinks a:link,
    .pagelinks a:visited,
    .pagelinks strong,
    .pagelinks b {
      font-size: var(--bt-fs-base) !important;
      line-height: 1.35 !important;
      font-weight: 400 !important;
      vertical-align: baseline !important;
    }
    .pagelinks a,
    .pagelinks a:link,
    .pagelinks a:visited,
    .pagelinks strong,
    .pagelinks b {
      display: inline-block !important;
      min-width: 0 !important;
      padding: 1px 4px !important;
      margin: 0 1px !important;
      border-radius: 4px !important;
    }
    .pagelinks strong,
    .pagelinks b {
      font-weight: 600 !important;
    }

    .poster h4 a, .poster h4 a:link, .poster h4 a:visited {
      color: var(--bt-text) !important;
      font-weight: 600 !important;
    }
    .poster h4 a:hover { color: var(--bt-accent) !important; }
    .poster ul, .poster li {
      color: var(--bt-text-3) !important;
      font-size: 12px !important;
    }
    .avatar, img.avatar {
      border-radius: var(--bt-radius-sm) !important;
      border: 1px solid var(--bt-border) !important;
    }

    * { scrollbar-width: thin; scrollbar-color: var(--bt-surface-3) var(--bt-bg); }
    *::-webkit-scrollbar { width: 10px; height: 10px; }
    *::-webkit-scrollbar-track { background: var(--bt-bg); }
    *::-webkit-scrollbar-thumb {
      background: var(--bt-surface-3);
      border-radius: 5px;
      border: 2px solid var(--bt-bg);
    }
    *::-webkit-scrollbar-thumb:hover { background: var(--bt-accent); }

    ::selection {
      background: color-mix(in srgb, var(--bt-accent) 35%, transparent);
      color: var(--bt-text);
    }

    img[src*="bullet"], img[src*="off"], img[src*="on"] { opacity: 0.85; }

    select {
      appearance: none !important;
      -webkit-appearance: none !important;
      background-image:
        linear-gradient(45deg, transparent 50%, var(--bt-text-2) 50%),
        linear-gradient(135deg, var(--bt-text-2) 50%, transparent 50%) !important;
      background-position: calc(100% - 14px) 50%, calc(100% - 9px) 50% !important;
      background-size: 5px 5px, 5px 5px !important;
      background-repeat: no-repeat !important;
      padding-right: 28px !important;
    }

    /* v3.0 Mobile version: optional performance mode enabled from settings.
       It keeps the same layout and palette, but removes costly effects and DOM
       modernisation work for slower devices. */
    :root[data-bt-mobile-lite="true"] {
      --bt-radius: 6px;
      --bt-radius-sm: 4px;
      --bt-radius-lg: 8px;
      --bt-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      --bt-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
      --bt-trans: 0ms linear;
      --bt-shadow: none;
      --bt-shadow-lg: none;
    }

    :root[data-bt-mobile-lite="true"],
    :root[data-bt-mobile-lite="true"] body {
      -webkit-font-smoothing: auto;
      text-rendering: auto;
    }

    :root[data-bt-mobile-lite="true"] body {
      background: var(--bt-bg) !important;
      background-attachment: scroll !important;
    }

    :root[data-bt-mobile-lite="true"] *,
    :root[data-bt-mobile-lite="true"] *::before,
    :root[data-bt-mobile-lite="true"] *::after {
      transition: none !important;
      animation: none !important;
    }

    :root[data-bt-mobile-lite="true"] table.bordercolor,
    :root[data-bt-mobile-lite="true"] table.table_list,
    :root[data-bt-mobile-lite="true"] table.table_grid,
    :root[data-bt-mobile-lite="true"] .tborder,
    :root[data-bt-mobile-lite="true"] #bt-switcher-button,
    :root[data-bt-mobile-lite="true"] #bt-switcher-panel {
      box-shadow: none !important;
    }

    :root[data-bt-mobile-lite="true"] table.bordercolor,
    :root[data-bt-mobile-lite="true"] table.table_list,
    :root[data-bt-mobile-lite="true"] table.table_grid,
    :root[data-bt-mobile-lite="true"] .tborder {
      border-radius: var(--bt-radius-sm) !important;
    }

    :root[data-bt-mobile-lite="true"] #header img,
    :root[data-bt-mobile-lite="true"] #logo,
    :root[data-bt-mobile-lite="true"] #bbcBox_message img,
    :root[data-bt-mobile-lite="true"] .bbc_buttons img {
      filter: none !important;
    }

    /*-- BT_ALWAYS_ON_START --*/
    #bt-switcher {
      position: absolute !important;
      top: 8px !important;
      right: 8px !important;
      z-index: 10000 !important;
      font-family: var(--bt-font) !important;
      font-size: 13px !important;
      color: var(--bt-text, #1A1D21) !important;
      line-height: 1 !important;
    }
    #bt-switcher-button {
      display: flex !important;
      align-items: center !important;
      justify-content: center !important;
      width: 32px !important;
      height: 32px !important;
      padding: 0 !important;
      margin: 0 !important;
      border-radius: 50% !important;
      background: var(--bt-surface-2, #2A2F35) !important;
      color: var(--bt-text-2, #A8B0BA) !important;
      border: 1px solid var(--bt-border-2, rgba(255,255,255,0.14)) !important;
      cursor: pointer !important;
      transition: background var(--bt-trans), color var(--bt-trans), transform 200ms ease !important;
      box-shadow: var(--bt-shadow, 0 1px 0 rgba(0,0,0,0.2)) !important;
      text-shadow: none !important;
      font: inherit !important;
      text-transform: none !important;
      letter-spacing: 0 !important;
    }
    #bt-switcher-button:hover {
      background: var(--bt-surface-3, #343A41) !important;
      color: var(--bt-text, #E3E3E8) !important;
      transform: rotate(20deg);
    }
    #bt-switcher-button svg { width: 18px; height: 18px; }

    :root[data-bt-active="false"] #bt-switcher-button {
      width: auto !important;
      min-width: 44px !important;
      padding: 0 12px !important;
      border-radius: 14px !important;
      background: #2A2F35 !important;
      color: #FFFFFF !important;
      border: 1px solid #4ADE80 !important;
      font-weight: 700 !important;
      font-size: 11px !important;
      text-transform: uppercase !important;
      letter-spacing: 0.08em !important;
      box-shadow: 0 0 0 0 rgba(74, 222, 128, 0.4) !important;
      animation: bt-pulse-on 2.4s ease-in-out infinite;
    }
    :root[data-bt-active="false"] #bt-switcher-button:hover {
      background: #4ADE80 !important;
      color: #1A1D21 !important;
      transform: none !important;
      animation: none;
    }
    .bt-sw-on-text { display: inline-block; line-height: 1; }
    @keyframes bt-pulse-on {
      0%, 100% { box-shadow: 0 0 0 0 rgba(74, 222, 128, 0.0); }
      50%      { box-shadow: 0 0 0 4px rgba(74, 222, 128, 0.18); }
    }

    #bt-switcher-panel {
      position: absolute !important;
      top: 40px !important;
      right: 0 !important;
      min-width: 300px !important;
      background: var(--bt-surface-1, #22262B) !important;
      border: 1px solid var(--bt-border-2, rgba(255,255,255,0.14)) !important;
      border-radius: var(--bt-radius, 10px) !important;
      padding: 14px !important;
      box-shadow: var(--bt-shadow-lg, 0 12px 32px rgba(0,0,0,0.35)) !important;
      display: none !important;
      color: var(--bt-text, #E3E3E8) !important;
    }
    #bt-switcher.open #bt-switcher-panel { display: block !important; }
    .bt-sw-section { margin-bottom: 14px; }
    .bt-sw-section:last-child { margin-bottom: 0; }
    .bt-sw-performance-section {
      padding: 0 !important;
      border: 1px solid var(--bt-border, rgba(255,255,255,0.07)) !important;
      border-radius: var(--bt-radius-sm, 6px) !important;
      background: var(--bt-surface-2, #2A2F35) !important;
      min-height: 32px !important;
    }
    .bt-sw-label {
      display: block !important;
      font-size: 10px !important;
      font-weight: 600 !important;
      text-transform: uppercase !important;
      letter-spacing: 0.08em !important;
      color: var(--bt-text-3, #6B7480) !important;
      margin-bottom: 8px !important;
    }
    .bt-sw-version-badge {
      display: inline-block !important;
      margin-left: 6px !important;
      padding: 2px 5px !important;
      border-radius: 999px !important;
      background: #EF4444 !important;
      color: #FFFFFF !important;
      font-size: 10px !important;
      font-weight: 800 !important;
      letter-spacing: 0 !important;
      vertical-align: 1px !important;
    }
    .bt-sw-local-badge {
      display: inline-block !important;
      margin-left: 6px !important;
      padding: 2px 6px !important;
      border-radius: 999px !important;
      background: #DC2626 !important;
      color: #FFFFFF !important;
      font-size: 10px !important;
      font-weight: 800 !important;
      letter-spacing: 0.06em !important;
      vertical-align: 1px !important;
      text-transform: uppercase !important;
      box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.3) inset !important;
    }
    .bt-sw-row {
      display: flex !important;
      gap: 6px !important;
      align-items: center !important;
      flex-wrap: wrap !important;
    }
    .bt-sw-theme-btn {
      flex: 1 !important;
      min-width: 0 !important;
      display: inline-flex !important;
      align-items: center !important;
      justify-content: center !important;
      gap: 5px !important;
      padding: 8px 6px !important;
      margin: 0 !important;
      background: var(--bt-surface-2, #2A2F35) !important;
      color: var(--bt-text-2, #A8B0BA) !important;
      border: 1px solid var(--bt-border, rgba(255,255,255,0.07)) !important;
      border-radius: var(--bt-radius-sm, 6px) !important;
      cursor: pointer !important;
      font-family: var(--bt-font) !important;
      font-size: 11px !important;
      font-weight: 500 !important;
      text-transform: none !important;
      letter-spacing: 0 !important;
      text-shadow: none !important;
      box-shadow: none !important;
      transition: background var(--bt-trans), color var(--bt-trans), border-color var(--bt-trans) !important;
      line-height: 1 !important;
    }
    .bt-sw-theme-btn:hover {
      background: var(--bt-surface-3, #343A41) !important;
      color: var(--bt-text, #E3E3E8) !important;
      transform: none !important;
    }
    .bt-sw-theme-btn.selected {
      background: var(--bt-accent, #F7931A) !important;
      color: var(--bt-on-accent, #1A1D21) !important;
      border-color: var(--bt-accent, #F7931A) !important;
    }
    .bt-sw-theme-btn svg { width: 14px; height: 14px; }
    .bt-sw-theme-btn[data-theme="off"].selected {
      background: var(--bt-danger, #EF4444) !important;
      color: #FFFFFF !important;
      border-color: var(--bt-danger, #EF4444) !important;
    }

    .bt-sw-toggle {
      width: 100% !important;
      display: flex !important;
      align-items: center !important;
      justify-content: space-between !important;
      gap: 10px !important;
      padding: 3px 8px !important;
      margin: 0 !important;
      background: transparent !important;
      color: var(--bt-text-2, #A8B0BA) !important;
      border: none !important;
      border-radius: var(--bt-radius-sm, 6px) !important;
      cursor: pointer !important;
      font-family: var(--bt-font) !important;
      font-size: 11px !important;
      font-weight: 500 !important;
      line-height: 1 !important;
      text-shadow: none !important;
      box-shadow: none !important;
      transition: background var(--bt-trans), color var(--bt-trans), border-color var(--bt-trans) !important;
    }
    .bt-sw-toggle-spacer {
      flex: 1 1 auto !important;
    }
    .bt-sw-mobile-toggle .bt-sw-theme-label {
      flex: 1 1 auto !important;
      text-align: left !important;
      font-size: 11px !important;
      font-weight: 600 !important;
      color: inherit !important;
      white-space: nowrap !important;
    }
    .bt-sw-toggle:hover {
      background: transparent !important;
      color: var(--bt-text, #E3E3E8) !important;
      transform: none !important;
    }
    /* v3.0.37: toggle ridisegnato — knob perfettamente centrato
       Math (track 44×24, border-box):
         track interno = 44×24 (con 1px border incluso)
         knob 20×20 (border-box, niente bordo) posizionato a top:2 left:2
         → gap verticale: 2px sopra, 24-2-20 = 2px sotto  ✓ centrato
         → gap orizzontale OFF: 2px sx, 22px dx
         → gap orizzontale ON:  translateX(20px) → 22px sx, 2px dx  */
    .bt-sw-toggle-track {
      width: 44px !important;
      height: 24px !important;
      flex: 0 0 44px !important;
      position: relative !important;
      box-sizing: border-box !important;
      padding: 0 !important;
      border-radius: 999px !important;
      background: var(--bt-surface-2, #D8DDE5) !important;
      border: 1px solid var(--bt-border-2, #99A0AB) !important;
      transition: background 200ms cubic-bezier(0.4, 0, 0.2, 1),
                  border-color 200ms cubic-bezier(0.4, 0, 0.2, 1) !important;
    }
    .bt-sw-toggle-knob {
      position: absolute !important;
      top: 2px !important;
      left: 2px !important;
      width: 20px !important;
      height: 20px !important;
      box-sizing: border-box !important;
      border-radius: 50% !important;
      background: #FFFFFF !important;
      border: 0 !important;
      box-shadow: 0 2px 4px rgba(0,0,0,0.18),
                  0 1px 2px rgba(0,0,0,0.10) !important;
      transition: transform 220ms cubic-bezier(0.4, 0, 0.2, 1),
                  background 200ms cubic-bezier(0.4, 0, 0.2, 1),
                  box-shadow 200ms !important;
    }
    .bt-sw-toggle:hover .bt-sw-toggle-knob {
      box-shadow: 0 3px 6px rgba(0,0,0,0.22),
                  0 1px 2px rgba(0,0,0,0.10) !important;
    }
    .bt-sw-toggle.selected {
      color: var(--bt-text, #E3E3E8) !important;
    }
    .bt-sw-toggle.selected .bt-sw-toggle-track {
      background: var(--bt-accent, #F7931A) !important;
      border-color: var(--bt-accent, #F7931A) !important;
    }
    .bt-sw-toggle.selected .bt-sw-toggle-knob {
      transform: translateX(20px) !important;
      background: #FFFFFF !important;
    }
    .bt-sw-toggle:active .bt-sw-toggle-knob {
      width: 22px !important; /* leggero squash al click per feel tattile */
    }
    .bt-sw-toggle.selected:active .bt-sw-toggle-knob {
      transform: translateX(18px) !important;
    }
    .bt-sw-toggle-state {
      display: none !important;
    }
    .bt-sw-toggle.selected .bt-sw-toggle-state {
      color: var(--bt-accent, #F7931A) !important;
    }

    .bt-sw-customize {
      margin-top: 14px;
      padding-top: 14px;
      border-top: 1px solid var(--bt-border, rgba(255,255,255,0.07));
    }
    .bt-sw-customize-title {
      display: block;
      text-align: center;
      font-size: 11px;
      font-weight: 600;
      letter-spacing: 0.05em;
      color: var(--bt-text, #E3E3E8);
      margin-bottom: 12px;
    }

    .bt-sw-swatch {
      width: 26px !important;
      height: 26px !important;
      padding: 0 !important;
      margin: 0 !important;
      border-radius: 50% !important;
      border: 2px solid transparent !important;
      cursor: pointer !important;
      transition: transform var(--bt-trans), border-color var(--bt-trans) !important;
      box-shadow: inset 0 0 0 1px rgba(0,0,0,0.15) !important;
      font-size: 0 !important;
      text-shadow: none !important;
    }
    .bt-sw-swatch:hover { transform: scale(1.12) !important; }
    .bt-sw-swatch.selected { border-color: var(--bt-text, #E3E3E8) !important; }

    .bt-sw-auto {
      width: auto !important;
      min-width: 44px !important;
      padding: 0 8px !important;
      height: 26px !important;
      border-radius: 13px !important;
      background: conic-gradient(#ef4444, #f59e0b, #10b981, #3b82f6, #8b5cf6, #ef4444) !important;
      display: inline-flex !important;
      align-items: center !important;
      justify-content: center !important;
      color: #FFFFFF !important;
      font-weight: 700 !important;
      font-size: 9px !important;
      letter-spacing: 0.06em !important;
      line-height: 1 !important;
      text-shadow: 0 1px 2px rgba(0,0,0,0.6) !important;
      font-family: var(--bt-font) !important;
    }
    .bt-sw-auto::before { content: 'AUTO'; }

    .bt-sw-custom-wrap {
      position: relative;
      display: inline-block;
      width: 26px;
      height: 26px;
    }
    .bt-sw-custom-wrap input[type="color"] {
      position: absolute !important;
      inset: 0 !important;
      width: 100% !important;
      height: 100% !important;
      padding: 0 !important;
      margin: 0 !important;
      border: 0 !important;
      border-radius: 50% !important;
      background: transparent !important;
      cursor: pointer !important;
      opacity: 0 !important;
      box-shadow: none !important;
    }
    .bt-sw-custom-display {
      width: 26px;
      height: 26px;
      border-radius: 50%;
      border: 2px solid transparent;
      pointer-events: none;
      box-shadow: inset 0 0 0 1px rgba(0,0,0,0.15);
      transition: border-color var(--bt-trans);
      background: conic-gradient(#ef4444, #f59e0b, #10b981, #3b82f6, #8b5cf6, #ef4444);
      display: flex;
      align-items: center;
      justify-content: center;
      color: #FFFFFF;
      font-weight: 700;
      font-size: 14px;
      line-height: 1;
      text-shadow: 0 1px 2px rgba(0,0,0,0.5);
    }
    .bt-sw-custom-display::before { content: '+'; }
    .bt-sw-custom-wrap.selected .bt-sw-custom-display::before { content: ''; }
    .bt-sw-custom-wrap.selected .bt-sw-custom-display {
      border-color: var(--bt-text, #E3E3E8);
    }
    :root[data-bt-mobile-lite="true"] #bt-switcher-button,
    :root[data-bt-mobile-lite="true"] #bt-switcher-panel,
    :root[data-bt-mobile-lite="true"] .bt-sw-theme-btn,
    :root[data-bt-mobile-lite="true"] .bt-sw-toggle,
    :root[data-bt-mobile-lite="true"] .bt-sw-toggle-track,
    :root[data-bt-mobile-lite="true"] .bt-sw-toggle-knob,
    :root[data-bt-mobile-lite="true"] .bt-sw-swatch,
    :root[data-bt-mobile-lite="true"] .bt-sw-custom-display {
      transition: none !important;
      animation: none !important;
      box-shadow: none !important;
    }
    :root[data-bt-mobile-lite="true"] #bt-switcher-button:hover,
    :root[data-bt-mobile-lite="true"] .bt-sw-swatch:hover {
      transform: none !important;
    }
    :root[data-bt-mobile-lite="true"] #bt-switcher-panel {
      min-width: min(300px, calc(100vw - 24px)) !important;
      max-width: calc(100vw - 24px) !important;
    }
    /*-- BT_ALWAYS_ON_END --*/


    /* ============================================================
       v2.8.3 — LIGHT THREAD POST ALTERNATION, FINAL OVERRIDE

       Theme-aware version. The post colours come from the active light
       palette, not from Classic hardcoded blue:
         --bt-post-bg
         --bt-post-alt-bg
         --bt-poster-bg
         --bt-poster-alt-bg

       So Classic, Pearl, Cream, Mint and Rose each get their own matching
       alternation. This block deliberately sits at the END of the theme CSS
       so it wins over broad rules like .post, .poster_info and td[style].
       ============================================================ */

    :root[data-bt-theme="light"] tr.windowbg,
    :root[data-bt-theme="light"] td.windowbg,
    :root[data-bt-theme="light"] tr.windowbg > td,
    :root[data-bt-theme="light"] table.bordercolor tr.windowbg > td,
    :root[data-bt-theme="light"] table.bordercolor td.windowbg,
    :root[data-bt-theme="light"] .tborder tr.windowbg > td,
    :root[data-bt-theme="light"] .tborder td.windowbg {
      background-color: var(--bt-post-bg, var(--bt-surface-1)) !important;
      background-image: none !important;
    }

    :root[data-bt-theme="light"] tr.windowbg2,
    :root[data-bt-theme="light"] td.windowbg2,
    :root[data-bt-theme="light"] tr.windowbg2 > td,
    :root[data-bt-theme="light"] table.bordercolor tr.windowbg2 > td,
    :root[data-bt-theme="light"] table.bordercolor td.windowbg2,
    :root[data-bt-theme="light"] .tborder tr.windowbg2 > td,
    :root[data-bt-theme="light"] .tborder td.windowbg2 {
      background-color: var(--bt-post-alt-bg, var(--bt-surface-2)) !important;
      background-image: none !important;
    }

    :root[data-bt-theme="light"] tr.windowbg3,
    :root[data-bt-theme="light"] td.windowbg3,
    :root[data-bt-theme="light"] tr.windowbg3 > td,
    :root[data-bt-theme="light"] table.bordercolor tr.windowbg3 > td,
    :root[data-bt-theme="light"] table.bordercolor td.windowbg3,
    :root[data-bt-theme="light"] .tborder tr.windowbg3 > td,
    :root[data-bt-theme="light"] .tborder td.windowbg3 {
      background-color: var(--bt-post-bg, var(--bt-surface-1)) !important;
      background-image: none !important;
    }

    /* Visible post body/wrapper. */
    :root[data-bt-theme="light"] tr.windowbg .td_headerandpost,
    :root[data-bt-theme="light"] td.windowbg.td_headerandpost,
    :root[data-bt-theme="light"] td.windowbg .td_headerandpost,
    :root[data-bt-theme="light"] tr.windowbg .post,
    :root[data-bt-theme="light"] td.windowbg .post,
    :root[data-bt-theme="light"] tr.windowbg td.post,
    :root[data-bt-theme="light"] td.windowbg.post,
    :root[data-bt-theme="light"] tr.windowbg td[style],
    :root[data-bt-theme="light"] td.windowbg td[style] {
      background-color: var(--bt-post-bg, var(--bt-surface-1)) !important;
      background-image: none !important;
    }

    :root[data-bt-theme="light"] tr.windowbg2 .td_headerandpost,
    :root[data-bt-theme="light"] td.windowbg2.td_headerandpost,
    :root[data-bt-theme="light"] td.windowbg2 .td_headerandpost,
    :root[data-bt-theme="light"] tr.windowbg2 .post,
    :root[data-bt-theme="light"] td.windowbg2 .post,
    :root[data-bt-theme="light"] tr.windowbg2 td.post,
    :root[data-bt-theme="light"] td.windowbg2.post,
    :root[data-bt-theme="light"] tr.windowbg2 td[style],
    :root[data-bt-theme="light"] td.windowbg2 td[style] {
      background-color: var(--bt-post-alt-bg, var(--bt-surface-2)) !important;
      background-image: none !important;
    }

    :root[data-bt-theme="light"] tr.windowbg3 .td_headerandpost,
    :root[data-bt-theme="light"] td.windowbg3.td_headerandpost,
    :root[data-bt-theme="light"] td.windowbg3 .td_headerandpost,
    :root[data-bt-theme="light"] tr.windowbg3 .post,
    :root[data-bt-theme="light"] td.windowbg3 .post,
    :root[data-bt-theme="light"] tr.windowbg3 td.post,
    :root[data-bt-theme="light"] td.windowbg3.post,
    :root[data-bt-theme="light"] tr.windowbg3 td[style],
    :root[data-bt-theme="light"] td.windowbg3 td[style] {
      background-color: var(--bt-post-bg, var(--bt-surface-1)) !important;
      background-image: none !important;
    }

    /* Poster/sidebar column: same theme family, slightly deeper for separation. */
    :root[data-bt-theme="light"] tr.windowbg .poster,
    :root[data-bt-theme="light"] tr.windowbg .poster_info,
    :root[data-bt-theme="light"] td.windowbg.poster,
    :root[data-bt-theme="light"] td.windowbg.poster_info {
      background-color: var(--bt-poster-bg, var(--bt-post-bg)) !important;
      background-image: none !important;
    }

    :root[data-bt-theme="light"] tr.windowbg2 .poster,
    :root[data-bt-theme="light"] tr.windowbg2 .poster_info,
    :root[data-bt-theme="light"] td.windowbg2.poster,
    :root[data-bt-theme="light"] td.windowbg2.poster_info {
      background-color: var(--bt-poster-alt-bg, var(--bt-post-alt-bg)) !important;
      background-image: none !important;
    }

    :root[data-bt-theme="light"] tr.windowbg3 .poster,
    :root[data-bt-theme="light"] tr.windowbg3 .poster_info,
    :root[data-bt-theme="light"] td.windowbg3.poster,
    :root[data-bt-theme="light"] td.windowbg3.poster_info {
      background-color: var(--bt-poster-bg, var(--bt-post-bg)) !important;
      background-image: none !important;
    }

    /* Keep quote/code/input blocks distinct inside alternating posts. */
    :root[data-bt-theme="light"] tr.windowbg .quote,
    :root[data-bt-theme="light"] tr.windowbg2 .quote,
    :root[data-bt-theme="light"] tr.windowbg3 .quote,
    :root[data-bt-theme="light"] td.windowbg .quote,
    :root[data-bt-theme="light"] td.windowbg2 .quote,
    :root[data-bt-theme="light"] td.windowbg3 .quote {
      background-color: var(--bt-quote-bg) !important;
      border-left-color: var(--bt-accent) !important;
    }

    :root[data-bt-theme="light"] tr.windowbg .code,
    :root[data-bt-theme="light"] tr.windowbg2 .code,
    :root[data-bt-theme="light"] tr.windowbg3 .code,
    :root[data-bt-theme="light"] td.windowbg .code,
    :root[data-bt-theme="light"] td.windowbg2 .code,
    :root[data-bt-theme="light"] td.windowbg3 .code {
      background-color: var(--bt-code-bg) !important;
    }

    :root[data-bt-theme="light"] tr.windowbg input,
    :root[data-bt-theme="light"] tr.windowbg2 input,
    :root[data-bt-theme="light"] tr.windowbg3 input,
    :root[data-bt-theme="light"] tr.windowbg textarea,
    :root[data-bt-theme="light"] tr.windowbg2 textarea,
    :root[data-bt-theme="light"] tr.windowbg3 textarea,
    :root[data-bt-theme="light"] tr.windowbg select,
    :root[data-bt-theme="light"] tr.windowbg2 select,
    :root[data-bt-theme="light"] tr.windowbg3 select {
      background-color: var(--bt-input-bg) !important;
    }



    /* ============================================================
       v2.10.7 — TYPOGRAPHY NORMALISATION

       Goal: keep the modern theme visually close to classic Bitcointalk text
       sizing, while removing the internal inconsistency introduced by many
       scattered 10/11/12px overrides. OFF mode remains clean because this CSS
       block is part of the disabled theme stylesheet, not an always-on rule.
       ============================================================ */

    :root {
      /* v3.0.3 typography pass against measured classic/OFF Bitcointalk:
         - base/post 13px match classic table and body text
         - small 11px matches classic smalltext/meta blocks
         - micro 10px preserves compact auxiliary text
         - compose 14px keeps the message editor aligned with forum body text
         - pagelinks stay compact as per-topic micro-navigation */
      --bt-fs-base: 13px;
      --bt-fs-small: 11px;
      --bt-fs-micro: 10px;
      --bt-fs-post: 13px;
      --bt-fs-pagelinks: 10px;
      --bt-fs-compose: 14px;
      --bt-lh-base: normal;
      --bt-lh-post: 1.55;
    }

    html,
    body {
      font-size: var(--bt-fs-base) !important;
      line-height: var(--bt-lh-base) !important;
    }

    body,
    table,
    td,
    th,
    tr,
    dd,
    dt,
    input,
    select,
    textarea,
    button:not([id^="bt-"]):not([class^="bt-sw-"]) {
      font-size: var(--bt-fs-base) !important;
    }

    table.table_list td,
    table.table_grid td,
    table.bordercolor td,
    .tborder td,
    [class*="windowbg"],
    [class*="windowbg"] td,
    .windowbg,
    .windowbg2,
    .windowbg3,
    .catbg,
    .catbg2,
    .titlebg,
    .titlebg2,
    [class*="catbg"],
    [class*="titlebg"] {
      font-size: var(--bt-fs-base) !important;
      line-height: var(--bt-lh-base) !important;
    }

    .post,
    td.post,
    .personalmessage,
    td.personalmessage,
    #preview_body,
    #preview_section,
    #preview_section td {
      font-size: var(--bt-fs-post) !important;
      line-height: var(--bt-lh-post) !important;
    }

    .post font[size="1"],
    td.post font[size="1"],
    .personalmessage font[size="1"],
    td.personalmessage font[size="1"],
    #preview_body font[size="1"],
    #preview_section font[size="1"],
    .post font[size="1"] *,
    td.post font[size="1"] *,
    .personalmessage font[size="1"] *,
    td.personalmessage font[size="1"] *,
    #preview_body font[size="1"] *,
    #preview_section font[size="1"] * {
      font-size: var(--bt-fs-small) !important;
      line-height: normal !important;
    }

    /* Classic Bitcointalk uses many small meta labels, but they should not
       collapse to tiny 10/11px text in the modern layer.
       v2.10.26: removed .nav / a.nav from this list — those are breadcrumb
       links (Bitcoin Forum > Economy > Economics > ...) and should render at
       body size, NOT at small-meta size. Same for .navPages. */
    .smalltext,
    .middletext,
    .small_header,
    .prevnext,
    .lastpostcol,
    .ignored_topic,
    .utusers,
    .utusers a,
    .poster ul,
    .poster li,
    .poster_info,
    td.poster_info,
    .poster,
    td.poster,
    .signature,
    .quoteheader,
    .codeheader {
      font-size: var(--bt-fs-small) !important;
      line-height: normal !important;
    }
    .codeheader {
      font-size: 10px !important;
      line-height: 12px !important;
    }

    table td.smalltext,
    table td.middletext,
    table.bordercolor td.smalltext,
    table.bordercolor td.middletext,
    table.tborder td.smalltext,
    table.tborder td.middletext,
    .tborder td.smalltext,
    .tborder td.middletext,
    [class*="windowbg"] td.smalltext,
    [class*="windowbg"] td.middletext,
    td[class*="windowbg"] td.smalltext,
    td[class*="windowbg"] td.middletext {
      font-size: var(--bt-fs-small) !important;
      line-height: normal !important;
    }

    /* v2.10.33: breadcrumb (Bitcoin Forum > Economy > Economics > ...) at
       body size to match the classic theme. The earlier rule was placed
       BEFORE the smalltext/middletext block above and lost the source-order
       battle since SMF puts the breadcrumb inside <td class="middletext">.
       Now declared after, with higher specificity (.navigate_section.navigate_section
       and the body-prefixed form for direct .nav containers) so it wins
       both by specificity AND by source order. */
    /* v2.10.35: SMF Bitcointalk injects an inline style="font-size: smaller"
       directly on the breadcrumb container <div class="nav">. That inline
       style cascades to every descendant via inheritance. CSS rules without
       !important can never beat an inline style by specificity, but a CSS
       rule WITH !important does win. The fix: !important on the .nav
       container itself plus a universal descendant selector so every <a>,
       <b>, text-wrapping element ends up at body size, including the
       <b><a>...</a></b> structure SMF actually uses for breadcrumb links
       and the bare " > " text nodes that float as direct children. */
    body div.nav.nav,
    body div.nav.nav *,
    body .navigate_section.navigate_section,
    body .navigate_section.navigate_section *,
    body td.middletext .nav,
    body td.middletext .nav * {
      font-size: var(--bt-fs-base) !important;
      line-height: 1.4 !important;
    }

    /* Topic rows and board indexes: make titles and row text match the classic
       density instead of looking smaller than the surrounding chrome. */
    table.table_list a[href*="topic="],
    table.bordercolor a[href*="topic="],
    .tborder a[href*="topic="],
    [class*="windowbg"] a[href*="topic="],
    span[id^="msg_"] a,
    .subject,
    .topic_subject {
      font-size: var(--bt-fs-base) !important;
      line-height: var(--bt-lh-base) !important;
    }

    table.table_list .pagelinks a[href*="topic="],
    table.bordercolor .pagelinks a[href*="topic="],
    .tborder .pagelinks a[href*="topic="],
    [class*="windowbg"] .pagelinks a[href*="topic="] {
      font-size: var(--bt-fs-pagelinks) !important;
      line-height: 1.15 !important;
    }

    /* v2.10.25: per-topic mini pagelinks sit inside <span class="smalltext">
       (or "middletext"), NOT inside .pagelinks — SMF renders them as raw
       <a> elements. The earlier rule [class*="windowbg"] a[href*="topic="]
       was hitting them with --bt-fs-base (13px) before our pagelink rule
       had a chance to match.
       v2.10.25 narrows the match to ONLY the smalltext blocks that actually
       contain topic/board page-jump links, using :has(). The previous v2.10.23
       form was too broad — it caught every smalltext inside any windowbg cell,
       including "Show unread posts since last visit", "Watchlist", "Total time
       logged in:", etc. which live in user-info windowbg cells. */
    :root[data-bt-active="true"][data-bt-theme] body td[class*="windowbg"] span.smalltext:has(> a[href*="topic="]),
    :root[data-bt-active="true"][data-bt-theme] body td[class*="windowbg"] span.middletext:has(> a[href*="topic="]),
    :root[data-bt-active="true"][data-bt-theme] body td[class*="windowbg"] span.smalltext:has(> a[href*="topic="]) > a,
    :root[data-bt-active="true"][data-bt-theme] body td[class*="windowbg"] span.middletext:has(> a[href*="topic="]) > a {
      font-size: var(--bt-fs-pagelinks) !important;
      line-height: 1.2 !important;
    }

    :root[data-bt-active="true"] #smfheader td.titlebg2[align="right"],
    :root[data-bt-active="true"] #smfheader td.titlebg2[align="right"] span.smalltext,
    :root[data-bt-active="true"] #smfheader td.titlebg2[align="right"] span.smalltext * {
      font-family: Verdana, Arial, Helvetica, sans-serif !important;
      font-size: 13px !important;
      font-weight: 400 !important;
      line-height: normal !important;
      letter-spacing: 0 !important;
    }

    [class*="catbg"] a,
    [class*="titlebg"] a,
    th a,
    td[class*="catbg"] a,
    td[class*="titlebg"] a {
      font-size: var(--bt-fs-base) !important;
      line-height: var(--bt-lh-base) !important;
    }

    /* v3.0.19: Merit summary needs to follow the classic skin, where the
       document-like content is visibly larger than the modern table density.
       This is deliberately late in the typography block so it wins over the
       global 13px table/body normalisation above. */
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea {
      font-family: Verdana, Arial, Helvetica, sans-serif !important;
      font-size: 13px !important;
      line-height: 1.32 !important;
    }
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea p,
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea div,
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea span,
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea table,
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea tbody,
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea tr,
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea td,
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea th,
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea ul,
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea ol,
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea li,
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea b,
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea strong,
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea a {
      font-family: Verdana, Arial, Helvetica, sans-serif !important;
      font-size: 13px !important;
      line-height: 1.32 !important;
    }
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea h1,
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea h1 * {
      font-family: Verdana, Arial, Helvetica, sans-serif !important;
      font-size: 23px !important;
      line-height: 1.24 !important;
      letter-spacing: 0 !important;
    }
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea h2,
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea h2 *,
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea h3,
    :root[data-bt-active="true"][data-bt-page="merit"] #bodyarea h3 * {
      font-family: Verdana, Arial, Helvetica, sans-serif !important;
      font-size: 17px !important;
      line-height: 1.28 !important;
      letter-spacing: 0 !important;
    }

    /* v3.0: normal form controls use body-sized text; the post-composition
       message area gets its own larger writing size below. */
    textarea,
    textarea[name="message"],
    #message,
    #quickReply textarea,
    #postmodify textarea,
    #post_modify textarea,
    input[type="text"],
    input[type="password"],
    input[type="email"],
    input[type="search"],
    input[type="url"],
    input[type="number"],
    select {
      font-family: var(--bt-font) !important;
      font-size: 14px !important;
      line-height: 1.4 !important;
    }

    textarea[name="message"],
    #message,
    #quickReply textarea,
    #postmodify textarea[name="message"],
    #post_modify textarea[name="message"] {
      font-size: var(--bt-fs-compose) !important;
      line-height: 1.45 !important;
    }

    textarea.editor[name="message"],
    #postmodify textarea.editor[name="message"],
    #post_modify textarea.editor[name="message"],
    form[name="postmodify"] textarea.editor[name="message"],
    form[action*="action=post"] textarea.editor[name="message"] {
      font-size: var(--bt-fs-compose) !important;
      line-height: 1.45 !important;
    }

    input[type="submit"],
    /* v2.10.28: removed topic-action buttons (replybutton/notifybutton/etc.)
       and .bt-post-action from this universal button rule. They get a more
       compact treatment in their own rule below — this rule kept overriding
       the compact sizing because it appears later in the cascade and
       therefore won at equal specificity. */
    input[type="button"],
    input[type="reset"],
    button:not([id^="bt-"]):not([class^="bt-sw-"]),
    a.button,
    .button_submit,
    .button_reset,
    .button,
    a[role="button"],
    span[role="button"],
    .poster_info button,
    .poster_info input[type="button"],
    .poster_info input[type="submit"],
    .poster_info a[class*="button"],
    .poster_info span[class*="button"],
    .poster_info a[class*="Button"],
    .poster_info span[class*="Button"] {
      font-size: var(--bt-fs-small) !important;
      line-height: 1.2 !important;
      padding-top: 4px !important;
      padding-bottom: 4px !important;
      padding-left: 8px !important;
      padding-right: 8px !important;
      min-height: 0 !important;
    }

    /* v2.10.28: compact topic action bar (Reply | Unwatch | Notify | ...).
       Declared after the universal button rule above so it wins by source
       order at equal specificity, and uses high-specificity selectors anyway
       (a.replybutton.replybutton — class repetition trick — to outweigh any
       theme rule that targets just the single class). */
    a.quotebutton.quotebutton,
    a.replybutton.replybutton,
    a.modifybutton.modifybutton,
    a.removebutton.removebutton,
    a.reportbutton.reportbutton,
    a.splitbutton.splitbutton,
    a.notifybutton.notifybutton,
    a.markreadbutton.markreadbutton,
    a.unwatchbutton.unwatchbutton,
    a.printbutton.printbutton,
    a.archivebutton.archivebutton,
    .bt-post-action.bt-post-action {
      font-size: 11px !important;
      line-height: 1.3 !important;
      padding-top: 2px !important;
      padding-bottom: 2px !important;
      padding-left: 6px !important;
      padding-right: 6px !important;
      margin: 0 2px 0 0 !important;
      letter-spacing: 0.02em !important;
      min-height: 0 !important;
      height: auto !important;
    }

    input[type="submit"],
    .button_submit {
      padding-top: 5px !important;
      padding-bottom: 5px !important;
      padding-left: 10px !important;
      padding-right: 10px !important;
      font-size: var(--bt-fs-small) !important;
      line-height: 1.2 !important;
    }

    [class*="maintab_"] a,
    [class*="mirrortab_"] a,
    .dropmenu a,
    #main_menu a {
      font-size: 13px !important;
      line-height: 1.15 !important;
    }

    .quote,
    .code {
      font-size: var(--bt-fs-small) !important;
      line-height: 1.3 !important;
    }
    .code {
      font-size: 12px !important;
      line-height: 1.3 !important;
    }

    .bt-topic-icon--newPost {
      font-size: 10px !important;
      line-height: 1 !important;
      min-width: 30px !important;
      height: 15px !important;
    }

    /* Compose toolbar icon buttons remain compact, but do not shrink text in
       the editor or in the rest of the compose page. */
    #bbcBox_message .bt-bbc-icon,
    .bbc_buttons .bt-bbc-icon,
    [id^="bbc_"] .bt-bbc-icon {
      width: 22px !important;
      height: 22px !important;
      padding: 3px !important;
    }

    #bbcBox_message img,
    .bbc_buttons img,
    [id^="bbc_"] img {
      max-width: 22px !important;
      max-height: 22px !important;
    }

    #smileyBox_message .bt-smiley,
    #smileyBox_message img,
    [id^="smileyBox"] .bt-smiley,
    [id^="smileyBox"] img,
    #postmodify .bt-smiley,
    #postmodify img.smiley,
    #post_modify .bt-smiley,
    #post_modify img.smiley,
    form[action*="post"] .bt-smiley,
    form[action*="post"] img.smiley {
      font-size: var(--bt-fs-base) !important;
      width: 14px !important;
      height: 14px !important;
      max-width: 14px !important;
      max-height: 14px !important;
      line-height: 1 !important;
      vertical-align: -0.12em !important;
    }

    #smileyBox_message .bt-smiley,
    [id^="smileyBox"] .bt-smiley,
    #postmodify .bt-smiley,
    #post_modify .bt-smiley,
    form[action*="post"] .bt-smiley {
      display: inline-flex !important;
      align-items: center !important;
      justify-content: center !important;
      width: 18px !important;
      height: 18px !important;
      color: var(--bt-text2) !important;
    }

    #smileyBox_message .bt-smiley-modern svg,
    [id^="smileyBox"] .bt-smiley-modern svg,
    #postmodify .bt-smiley-modern svg,
    #post_modify .bt-smiley-modern svg,
    form[action*="post"] .bt-smiley-modern svg {
      width: 18px !important;
      height: 18px !important;
      display: block !important;
    }

    #smileyBox_message .bt-smiley-native,
    [id^="smileyBox"] .bt-smiley-native,
    #postmodify .bt-smiley-native,
    #post_modify .bt-smiley-native,
    form[action*="post"] .bt-smiley-native {
      font-family: Apple Color Emoji, Segoe UI Emoji, Noto Color Emoji, sans-serif !important;
      font-size: 16px !important;
      line-height: 18px !important;
      color: initial !important;
      text-rendering: optimizeLegibility !important;
    }

    .bt-smiley-face {
      fill: rgba(247, 147, 26, 0.16);
      stroke: currentColor;
      stroke-width: 1.7;
    }
    .bt-smiley-eye,
    .bt-smiley-fill {
      fill: currentColor;
    }
    .bt-smiley-line {
      fill: none;
      stroke: currentColor;
      stroke-width: 1.8;
      stroke-linecap: round;
      stroke-linejoin: round;
    }
    .bt-smiley-tongue {
      fill: rgba(247, 147, 26, 0.22);
      stroke: currentColor;
      stroke-width: 1.5;
      stroke-linecap: round;
      stroke-linejoin: round;
    }
    .bt-smiley-heart,
    .bt-smiley-star,
    .bt-smiley-fire {
      fill: currentColor;
    }

    /* The theme switcher is intentionally separate UI: keep its compact scale
       and avoid forum-wide typography overrides leaking into it. */
    #bt-switcher,
    #bt-switcher * {
      line-height: 1 !important;
    }

    /* =========================================================================
       v3.0.34 — DEFENSIVE FINAL OVERRIDES (must stay at end of stylesheet)
       Force 13px on the SMF header date (top-right) and the main navigation
       menu links. Specificity is bumped with html body and the [data-bt-active]
       root attribute so these rules beat any earlier --bt-fs-small reset.
       Source-order placement at the end of the CSS string guarantees they win
       over rules with equal specificity. */
    html body #smfheader td.titlebg2[align="right"],
    html body #smfheader td.titlebg2[align="right"] *,
    html body #smfheader td.titlebg2[align="right"] span.smalltext,
    html body #smfheader td.titlebg2[align="right"] span.smalltext * {
      font-size: 13px !important;
      line-height: normal !important;
    }

    html body [class*="maintab_"],
    html body [class*="maintab_"] a,
    html body [class*="maintab_"] a:link,
    html body [class*="maintab_"] a:visited,
    html body [class*="maintab_"] a:hover,
    html body [class*="maintab_"] a:active,
    html body [class*="mirrortab_"],
    html body [class*="mirrortab_"] a,
    html body [class*="mirrortab_"] a:link,
    html body [class*="mirrortab_"] a:visited,
    html body .dropmenu,
    html body .dropmenu a,
    html body #main_menu,
    html body #main_menu a {
      font-size: 13px !important;
    }

    /* =========================================================================
       v3.0.38 — TYPOGRAPHIC HARMONY FINAL PASS
       Several SMF pages inject large inherited sizes into headings, title bars
       and post wrappers. Keep the modern theme close to the classic scale:
       normal content/post text at 13px, compact meta at 11px, compose at 14px.
       Merit has its own page-specific block above, so it is excluded here. */
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body #bodyarea,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body #content_section,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body #main_content_section,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body table.bordercolor td,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .tborder td,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body td.windowbg,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body td.windowbg2,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body td.windowbg3,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .post,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body td.post,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .personalmessage,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body td.personalmessage,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body #preview_body,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body #preview_section,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body #preview_section td {
      font-size: 13px !important;
    }

    html[data-bt-active="true"]:not([data-bt-page="merit"]) body [class*="catbg"],
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body [class*="titlebg"],
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body td[class*="catbg"],
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body td[class*="titlebg"],
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body th[class*="catbg"],
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body th[class*="titlebg"],
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body [class*="catbg"] b,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body [class*="titlebg"] b,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body [class*="catbg"] strong,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body [class*="titlebg"] strong,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body [class*="catbg"] span:not(.smalltext),
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body [class*="titlebg"] span:not(.smalltext),
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body [class*="catbg"] a,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body [class*="titlebg"] a {
      font-size: 13px !important;
      line-height: 1.35 !important;
      letter-spacing: 0 !important;
    }

    html[data-bt-active="true"]:not([data-bt-page="merit"]) body #bodyarea h1,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body #content_section h1,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body #main_content_section h1 {
      font-size: 18px !important;
      line-height: 1.25 !important;
      font-weight: 600 !important;
      letter-spacing: 0 !important;
    }

    html[data-bt-active="true"]:not([data-bt-page="merit"]) body #bodyarea h2,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body #bodyarea h3,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body #content_section h2,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body #content_section h3,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body #main_content_section h2,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body #main_content_section h3,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .poster h4,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .poster h4 a,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .poster_info h4,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .poster_info h4 a {
      font-size: 15px !important;
      line-height: 1.3 !important;
      font-weight: 600 !important;
      letter-spacing: 0 !important;
    }

    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .smalltext,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .middletext,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .small_header,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .prevnext,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .lastpostcol,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .ignored_topic,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .utusers,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .poster ul,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .poster li,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .poster_info,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body td.poster_info,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .signature,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .quoteheader,
    html[data-bt-active="true"]:not([data-bt-page="merit"]) body .codeheader {
      font-size: 11px !important;
      line-height: normal !important;
    }

  `;

  // ===========================================================================
  // CSS injection
  // ===========================================================================

  function splitCss(src) {
    const startMarker = '/*-- BT_ALWAYS_ON_START --*/';
    const endMarker = '/*-- BT_ALWAYS_ON_END --*/';
    let theme = '';
    let always = '';
    let i = 0;
    while (i < src.length) {
      const startIdx = src.indexOf(startMarker, i);
      if (startIdx === -1) {
        theme += src.slice(i);
        break;
      }
      theme += src.slice(i, startIdx);
      const after = startIdx + startMarker.length;
      const endIdx = src.indexOf(endMarker, after);
      if (endIdx === -1) {
        always += src.slice(after);
        break;
      }
      always += src.slice(after, endIdx);
      i = endIdx + endMarker.length;
    }
    return { theme, always };
  }

  let themeStyleEl = null;
  let alwaysStyleEl = null;

  function ensureFonts() {
    if (isMobileLite() || document.getElementById('bt-fonts')) return;
    const link = document.createElement('link');
    link.id = 'bt-fonts';
    link.rel = 'stylesheet';
    link.href =
      'https://fonts.googleapis.com/css2?family=Source+Sans+3:wght@400;500;600;700' +
      '&family=JetBrains+Mono:wght@400;500&display=swap';
    (document.head || document.documentElement).appendChild(link);
  }

  function injectCSS() {
    const { theme, always } = splitCss(css);
    themeStyleEl = document.createElement('style');
    themeStyleEl.id = 'bt-theme-css';
    themeStyleEl.textContent = theme;
    (document.head || document.documentElement).appendChild(themeStyleEl);

    alwaysStyleEl = document.createElement('style');
    alwaysStyleEl.id = 'bt-always-css';
    alwaysStyleEl.textContent = always;
    (document.head || document.documentElement).appendChild(alwaysStyleEl);

    ensureFonts();
  }

  // ===========================================================================
  // Switcher UI
  // ===========================================================================

  const ICONS = {
    cog: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>',
    sun: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg>',
    moon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>',
    monitor: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>',
    power: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18.36 6.64a9 9 0 1 1-12.73 0"/><line x1="12" y1="2" x2="12" y2="12"/></svg>',
  };

  // True for the "Locale" dev build (its @name ends with " Locale"), so the
  // settings panel can show red version/LOCAL badges to tell it apart from the
  // published build at a glance.
  const BT_IS_LOCAL = !!(
    typeof GM_info !== 'undefined' &&
    GM_info.script &&
    / Locale\b/i.test(GM_info.script.name || '')
  );

  function buildSwitcher(host) {
    if (document.getElementById('bt-switcher')) return;

    const container = document.createElement('div');
    container.id = 'bt-switcher';
    container.innerHTML = `
      <button id="bt-switcher-button" type="button" title="Theme settings" aria-label="Theme settings">
        ${ICONS.cog}
      </button>
      <div id="bt-switcher-panel" role="dialog" aria-label="Theme settings">
        <div class="bt-sw-section">
          <span class="bt-sw-label">Mode${BT_IS_LOCAL ? ` <span class="bt-sw-version-badge">v${SCRIPT_VERSION}</span> <span class="bt-sw-local-badge">LOCAL</span>` : ''}</span>
          <div class="bt-sw-row">
            <button type="button" class="bt-sw-theme-btn" data-theme="light" title="Light">
              ${ICONS.sun}<span class="bt-sw-theme-label">Light</span>
            </button>
            <button type="button" class="bt-sw-theme-btn" data-theme="dark" title="Dark">
              ${ICONS.moon}<span class="bt-sw-theme-label">Dark</span>
            </button>
            <button type="button" class="bt-sw-theme-btn" data-theme="system" title="Follow system">
              ${ICONS.monitor}<span class="bt-sw-theme-label">Auto</span>
            </button>
            <button type="button" class="bt-sw-theme-btn" data-theme="off" title="Turn the theme off">
              ${ICONS.power}<span class="bt-sw-theme-label">OFF</span>
            </button>
          </div>
        </div>
        <div class="bt-sw-section bt-sw-performance-section">
          <button type="button" class="bt-sw-toggle bt-sw-mobile-toggle" id="bt-sw-mobile-version" title="Use the lightweight mobile version" aria-pressed="false">
            <span class="bt-sw-theme-label">Mobile version</span>
            <span class="bt-sw-toggle-spacer"></span>
            <span class="bt-sw-toggle-track" aria-hidden="true"><span class="bt-sw-toggle-knob"></span></span>
            <span class="bt-sw-toggle-state">OFF</span>
          </button>
        </div>
        <div class="bt-sw-customize">
          <span class="bt-sw-customize-title" id="bt-sw-customize-title">Customise Colours</span>
          <div class="bt-sw-section">
            <span class="bt-sw-label">Main</span>
            <div class="bt-sw-row" id="bt-sw-main-row"></div>
          </div>
          <div class="bt-sw-section">
            <span class="bt-sw-label">Accent</span>
            <div class="bt-sw-row" id="bt-sw-accent-row"></div>
          </div>
        </div>
      </div>
    `;
    host.appendChild(container);

    const btn = container.querySelector('#bt-switcher-button');
    btn.addEventListener('click', (e) => {
      e.stopPropagation();
      const currentPref = getPref(KEY_THEME, DEFAULT_THEME);
      if (currentPref === 'off') {
        const prior = getPref(KEY_THEME_PRIOR, 'dark');
        setPref(KEY_THEME, prior);
        applyTheme();
        return;
      }
      container.classList.toggle('open');
    });
    document.addEventListener('click', (e) => {
      if (!container.contains(e.target)) container.classList.remove('open');
    });
    document.addEventListener('keydown', (e) => {
      if (e.key === 'Escape') container.classList.remove('open');
    });

    container.querySelectorAll('.bt-sw-theme-btn').forEach(b => {
      b.addEventListener('click', () => {
        if (!b.dataset.theme) return;
        const choice = b.dataset.theme;
        if (choice === 'off') {
          const current = getPref(KEY_THEME, DEFAULT_THEME);
          if (current === 'off') {
            const prior = getPref(KEY_THEME_PRIOR, 'dark');
            setPref(KEY_THEME, prior);
          } else {
            setPref(KEY_THEME_PRIOR, current);
            setPref(KEY_THEME, 'off');
          }
          container.classList.remove('open');
        } else {
          setPref(KEY_THEME, choice);
        }
        applyTheme();
      });
    });

    ensureMobileToggleControl(container);

    function handleSwatchClick(kind) {
      return (e) => {
        const swatch = e.target.closest(`[data-${kind}]`);
        if (!swatch || swatch.tagName === 'INPUT') return;
        const themeNow = getEffectiveTheme(getPref(KEY_THEME, DEFAULT_THEME));
        if (themeNow === 'off') return;
        const storageKey = kind === 'main'
          ? (themeNow === 'dark' ? KEY_MAIN_DARK : KEY_MAIN_LIGHT)
          : (themeNow === 'dark' ? KEY_ACCENT_DARK : KEY_ACCENT_LIGHT);
        setPref(storageKey, swatch.dataset[kind]);
        applyTheme();
      };
    }
    container.querySelector('#bt-sw-main-row').addEventListener('click', handleSwatchClick('main'));
    container.querySelector('#bt-sw-accent-row').addEventListener('click', handleSwatchClick('accent'));

    function handleColorInput(kind) {
      return (e) => {
        if (!e.target.matches(`input[type="color"][data-target="${kind}"]`)) return;
        const themeNow = getEffectiveTheme(getPref(KEY_THEME, DEFAULT_THEME));
        if (themeNow === 'off') return;
        const storageKey = kind === 'main'
          ? (themeNow === 'dark' ? KEY_MAIN_DARK : KEY_MAIN_LIGHT)
          : (themeNow === 'dark' ? KEY_ACCENT_DARK : KEY_ACCENT_LIGHT);
        const customStorageKey = kind === 'main'
          ? (themeNow === 'dark' ? KEY_MAIN_DARK_CUSTOM : KEY_MAIN_LIGHT_CUSTOM)
          : (themeNow === 'dark' ? KEY_ACCENT_DARK_CUSTOM : KEY_ACCENT_LIGHT_CUSTOM);
        const safeValue = sanitizeHex(
          e.target.value,
          kind === 'main'
            ? (themeNow === 'dark' ? DEFAULT_CUSTOM_MAIN_DARK : DEFAULT_CUSTOM_MAIN_LIGHT)
            : (themeNow === 'dark' ? DEFAULT_CUSTOM_ACCENT_DARK : DEFAULT_CUSTOM_ACCENT_LIGHT)
        );
        setPref(customStorageKey, safeValue);
        setPref(storageKey, 'custom');
        applyTheme();
      };
    }
    container.querySelector('#bt-sw-main-row').addEventListener('input', handleColorInput('main'));
    container.querySelector('#bt-sw-accent-row').addEventListener('input', handleColorInput('accent'));

    applyTheme();
  }

  function injectSwitcher() {
    const host =
      document.getElementById('upper_section') ||
      document.getElementById('header') ||
      document.body;
    if (host) buildSwitcher(host);
  }
  // ===========================================================================
  // Integrated Safari flash guard
  // ===========================================================================

  function getHoldBackground() {
    const pref = getPref(KEY_THEME, DEFAULT_THEME);
    const effective = getEffectiveTheme(pref);
    if (effective === 'off') return null;
    const fallback = effective === 'dark' ? DEFAULT_CUSTOM_MAIN_DARK : DEFAULT_CUSTOM_MAIN_LIGHT;
    const fromSession = ssGet(NAV_BG_SESSION_KEY);
    if (fromSession) return sanitizeHex(fromSession, fallback);
    return sanitizeHex(getActiveMainHex(effective) || fallback, fallback);
  }

  function getCurrentThemeBackground() {
    const root = document.documentElement;
    const fallback = getHoldBackground() || DEFAULT_CUSTOM_MAIN_DARK;
    try {
      const inlineVar = root && root.style ? root.style.getPropertyValue('--bt-bg').trim() : '';
      if (inlineVar) return sanitizeHex(inlineVar, fallback);
    } catch (e) {}
    try {
      const computedVar = getComputedStyle(root).getPropertyValue('--bt-bg').trim();
      if (computedVar) return sanitizeHex(computedVar, fallback);
    } catch (e) {}
    return sanitizeHex(fallback, DEFAULT_CUSTOM_MAIN_DARK);
  }

  function holdCss(bg, revealedSelector) {
    bg = sanitizeHex(bg, getEffectiveTheme(getPref(KEY_THEME, DEFAULT_THEME)) === 'light' ? DEFAULT_CUSTOM_MAIN_LIGHT : DEFAULT_CUSTOM_MAIN_DARK);
    const scheme = getEffectiveTheme(getPref(KEY_THEME, DEFAULT_THEME)) === 'light' ? 'light' : 'dark';
    return `
      html${revealedSelector} {
        background: ${bg} !important;
        color-scheme: ${scheme} !important;
      }
      html${revealedSelector}::before {
        content: '' !important;
        position: fixed !important;
        inset: 0 !important;
        width: 100vw !important;
        height: 100vh !important;
        background: ${bg} !important;
        z-index: 2147483647 !important;
        pointer-events: none !important;
      }
      html${revealedSelector} body {
        visibility: hidden !important;
        opacity: 0 !important;
        background: ${bg} !important;
      }
    `;
  }

  function injectTransientStyle(id, cssText) {
    let style = document.getElementById(id);
    if (!style) {
      style = document.createElement('style');
      style.id = id;
      (document.documentElement || document.head || document.body).appendChild(style);
    }
    style.textContent = cssText;
    return style;
  }

  function removeTransientStyle(id) {
    const style = document.getElementById(id);
    if (style) style.remove();
  }

  function holdPaint() {
    if (document.readyState === 'complete') return false;
    const bg = getHoldBackground();
    if (!bg) return false;

    const root = document.documentElement;
    const holdStyle = injectTransientStyle(
      INCOMING_HOLD_STYLE_ID,
      holdCss(bg, `:not([${HOLD_REVEALED_ATTR}])`)
    );

    if (typeof MutationObserver !== 'undefined') {
      const observer = new MutationObserver(() => {
        if (!document.getElementById(INCOMING_HOLD_STYLE_ID)) {
          try { document.documentElement.appendChild(holdStyle); } catch (e) {}
        }
      });
      try {
        observer.observe(root, { childList: true });
        holdStyle._btObserver = observer;
      } catch (e) {}
    }

    setTimeout(() => revealNow(), 1500);
    return true;
  }

  function revealNow() {
    document.documentElement.setAttribute(HOLD_REVEALED_ATTR, '');
    const holdStyle = document.getElementById(INCOMING_HOLD_STYLE_ID);
    if (holdStyle && holdStyle._btObserver) {
      try { holdStyle._btObserver.disconnect(); } catch (e) {}
    }
    ssRemove(NAV_UNTIL_SESSION_KEY);
  }

  function releasePaint() {
    revealNow();
  }

  function markNavigationHold(bg) {
    const effective = getEffectiveTheme(getPref(KEY_THEME, DEFAULT_THEME));
    const fallback = effective === 'light' ? DEFAULT_CUSTOM_MAIN_LIGHT : DEFAULT_CUSTOM_MAIN_DARK;
    ssSet(NAV_UNTIL_SESSION_KEY, String(Date.now() + NAV_HOLD_SESSION_MS));
    ssSet(NAV_BG_SESSION_KEY, sanitizeHex(bg, fallback));
  }

  function applyOutgoingHold() {
    if (getPref(KEY_THEME, DEFAULT_THEME) === 'off') return;
    const bg = getCurrentThemeBackground();
    markNavigationHold(bg);
    injectTransientStyle(OUTGOING_HOLD_STYLE_ID, holdCss(bg, ''));
    setTimeout(() => removeTransientStyle(OUTGOING_HOLD_STYLE_ID), OUTGOING_CANCEL_RESTORE_MS);
  }

  function isPlainPrimaryActivation(e) {
    if (e.defaultPrevented) return false;
    if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return false;
    if (typeof e.button === 'number' && e.button !== 0) return false;
    return true;
  }

  function shouldHoldForLink(a, e) {
    if (!a || !isPlainPrimaryActivation(e)) return false;
    if (a.target && a.target !== '_self') return false;
    if (a.hasAttribute('download')) return false;

    const href = a.getAttribute('href') || '';
    if (!href) return false;
    if (href.startsWith('#')) return false;
    if (/^\s*javascript:/i.test(href)) return false;

    let url;
    try {
      url = new URL(href, window.location.href);
    } catch (e2) {
      return false;
    }

    if (
      url.origin === window.location.origin &&
      url.pathname === window.location.pathname &&
      url.search === window.location.search &&
      url.hash
    ) {
      return false;
    }
    return true;
  }

  function targetLinkFromEvent(e) {
    const target = e.target;
    return target && target.closest ? target.closest(NAV_SELECTOR) : null;
  }

  function installPreNavigationHold() {
    ['pointerdown', 'mousedown', 'touchstart', 'click'].forEach((type) => {
      document.addEventListener(type, (e) => {
        const a = targetLinkFromEvent(e);
        if (!shouldHoldForLink(a, e)) return;
        applyOutgoingHold();
      }, true);
    });

    document.addEventListener('keydown', (e) => {
      if (e.key !== 'Enter' && e.key !== ' ') return;
      const active = document.activeElement;
      const a = active && active.closest ? active.closest(NAV_SELECTOR) : null;
      if (!shouldHoldForLink(a, e)) return;
      applyOutgoingHold();
    }, true);

    window.addEventListener('beforeunload', () => {
      if (getPref(KEY_THEME, DEFAULT_THEME) !== 'off') applyOutgoingHold();
    });
    window.addEventListener('pagehide', () => {
      if (getPref(KEY_THEME, DEFAULT_THEME) !== 'off') applyOutgoingHold();
    });
    window.addEventListener('pageshow', () => {
      removeTransientStyle(OUTGOING_HOLD_STYLE_ID);
      document.documentElement.setAttribute(HOLD_REVEALED_ATTR, '');
    });
  }

  // ===========================================================================
  // Init
  // ===========================================================================

  function setupRootAttributes() {
    let pref = null;
    if (hasGM) {
      try {
        const v = GM_getValue(KEY_THEME, null);
        if (v != null && v !== '') pref = String(v);
      } catch (e) {}
    }
    if (!pref) {
      try {
        const v = localStorage.getItem(KEY_THEME);
        if (v != null && v !== '') pref = v;
      } catch (e) {}
    }
    if (!pref) pref = DEFAULT_THEME;

    const effective = getEffectiveTheme(pref);
    const root = document.documentElement;
    root.setAttribute('data-bt-no-transitions', '');
    updateMobileLiteAttribute();
    updatePageAttribute();

    if (effective === 'off') {
      root.setAttribute('data-bt-active', 'false');
      return;
    }

    root.setAttribute('data-bt-active', 'true');
    root.setAttribute('data-bt-theme', effective);
    root.style.colorScheme = effective;
  }

  const paintHeld = false; // disabled: separate early guard handles paint
  setupRootAttributes();
  syncStores();
  injectCSS();
  applyTheme();
  if (paintHeld) releasePaint();
  // // // // installPreNavigationHold(); // disabled: separate 000 early guard handles Safari navigation flash // disabled: separate early guard handles Safari navigation flash // disabled: readable-bootstrap guard handles Safari navigation flash // disabled: bootstrap guard handles Safari navigation flash

  function enableTransitions() {
    requestAnimationFrame(() => {
      requestAnimationFrame(() => {
        document.documentElement.removeAttribute('data-bt-no-transitions');
      });
    });
  }
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', enableTransitions, { once: true });
  } else {
    enableTransitions();
  }

  window.addEventListener('storage', (e) => {
    if (e && e.key && ALL_KEYS.indexOf(e.key) !== -1) {
      applyTheme();
    }
  });

  let modernizeTimer = null;
  let modernizeObserver = null;

  function scheduleModernize() {
    if (isMobileLite()) return;
    if (modernizeTimer) return;
    modernizeTimer = setTimeout(() => {
      modernizeTimer = null;
      modernizeBBCodeIcons();
      modernizeTopicListIcons();
      modernizeSmileys();
      stylePostMetaButtons();
      fixMaintabCaps();
      highlightUnreadPm();
      // v2.10.36: re-strip the SMF inline "font-size: smaller" on the
      // breadcrumb every time the DOM mutates, in case the breadcrumb is
      // inserted late (some pages load it after initial render).
      applyBreadcrumbSizeInline();
      // v2.10.40: same brute-force approach for news ticker and topic header.
      applyHeaderSizesInline();
      applyMeritPageSizingInline();
      applyComposeSizingInline();
    }, 50);
  }

  function runInitialPasses() {
    fixMaintabCaps();
    highlightUnreadPm();
    if (isMobileLite()) {
      applyBreadcrumbSizeInline();
      applyHeaderSizesInline();
      applyMeritPageSizingInline();
      applyComposeSizingInline();
      return;
    }
    modernizeBBCodeIcons();
    modernizeTopicListIcons();
    modernizeSmileys();
    stylePostMetaButtons();
    applyBreadcrumbSizeInline();
    applyHeaderSizesInline();
    applyMeritPageSizingInline();
    applyComposeSizingInline();
  }

  function stopModernizeObserver() {
    if (!modernizeObserver) return;
    try { modernizeObserver.disconnect(); } catch (e) {}
    modernizeObserver = null;
  }

  function installModernizeObserver() {
    if (modernizeObserver || isMobileLite() || typeof MutationObserver === 'undefined') return;
    modernizeObserver = new MutationObserver((mutations) => {
      for (const m of mutations) {
        if (m.addedNodes && m.addedNodes.length > 0) {
          scheduleModernize();
          return;
        }
      }
    });
    if (document.body) {
      modernizeObserver.observe(document.body, { childList: true, subtree: true });
    } else {
      document.addEventListener('DOMContentLoaded', () => {
        if (!isMobileLite() && document.body) {
          modernizeObserver.observe(document.body, { childList: true, subtree: true });
        }
      }, { once: true });
    }
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => {
      applyTheme();
      injectSwitcher();
      runInitialPasses();
    }, { once: true });
  } else {
    injectSwitcher();
    runInitialPasses();
  }

  // v2.10.38: belt-and-suspenders breadcrumb fix retries. Some pages render
  // the breadcrumb late, after MutationObserver has already settled. Schedule
  // multiple delayed attempts so we eventually catch every breadcrumb.
  // v2.10.40: same retries for news ticker / topic header sizing.
  setTimeout(() => { applyBreadcrumbSizeInline(); applyHeaderSizesInline(); applyMeritPageSizingInline(); applyComposeSizingInline(); }, 100);
  setTimeout(() => { applyBreadcrumbSizeInline(); applyHeaderSizesInline(); applyMeritPageSizingInline(); applyComposeSizingInline(); }, 500);
  setTimeout(() => { applyBreadcrumbSizeInline(); applyHeaderSizesInline(); applyMeritPageSizingInline(); applyComposeSizingInline(); }, 1500);
  setTimeout(() => { applyBreadcrumbSizeInline(); applyHeaderSizesInline(); applyMeritPageSizingInline(); applyComposeSizingInline(); }, 3000);

  installModernizeObserver();

  if (window.matchMedia) {
    const mq = window.matchMedia('(prefers-color-scheme: dark)');
    const reapply = () => {
      if (getPref(KEY_THEME, DEFAULT_THEME) === 'system') applyTheme();
    };
    if (mq.addEventListener) mq.addEventListener('change', reapply);
    else if (mq.addListener) mq.addListener(reapply);
  }
})();