Modern34 (Rule34 Modern Theme)

A modern (dark) theme for rule34.xxx.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Modern34 (Rule34 Modern Theme)
// @description  A modern (dark) theme for rule34.xxx.
// @namespace    Violentmonkey Scripts
// @match        *://rule34.xxx/*
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @connect      localhost
// @run-at       document-end
// @version      1.0
// @license      MIT
// ==/UserScript==

(function() {
    "use strict";

    //region Config
    const CFG = {
        defaultColumns: GM_getValue("defaultColumns", 0),
        imageReplacement: GM_getValue("imageReplacement", true)
    };

    function cfgSet(key, val) {
        CFG[key] = val;
        GM_setValue(key, val);
    }

    function cfgToggle(key) {
        cfgSet(key, !CFG[key]);
        location.reload();
    }

    const flag = v => v ? "✅" : "❌";
    GM_registerMenuCommand(`${flag(CFG.imageReplacement)} Image Replacement`, () => cfgToggle("imageReplacement"));
    GM_registerMenuCommand("🔄 Reset Image Server Cache", () => {
        const host = location.hostname.replace(/^www\./, "");
        GM_setValue(`imageServerBase_${host}`, "");
        GM_setValue(`imageServerExpiry_${host}`, 0);
        location.reload();
    });

    GM_registerMenuCommand("⚙️ Set Default Columns...", () => {
        const input = prompt(
            "Default column count (1-8).\nSet 0 for automatic responsive breakpoints.\nCurrent: " + (CFG.defaultColumns || "auto"),
            CFG.defaultColumns
        );
        if (input === null) return;
        const n = parseInt(input, 10);
        if (isNaN(n) || n < 0 || n > 8) { alert("Invalid value"); return; }
        cfgSet("defaultColumns", n);
        location.reload();
    });
    //endregion

    if (CFG.defaultColumns > 0) {
        document.documentElement.style.setProperty("--grid-columns", CFG.defaultColumns);
    }

    // Note: this css document was AI generated although meticulously edited to ensure it is valid + functional
    

    GM_addStyle("/* ==========================================================================\n   1. Theme Variables & Universal Overrides\n   ========================================================================== */\n:root,\n*,\n*::before,\n*::after {\n  /* Dark Mode Palette Tokens */\n  --bg-main: #121212;\n  --bg-surface: #1e1e1e;\n  --bg-surface-elevated: #282828;\n  --bg-accent: #2c2c2c;\n  --bg-hover: rgba(255, 255, 255, 0.06);\n  --bg-active: rgba(255, 255, 255, 0.1);\n  --bg-glass: rgba(20, 20, 20, 0.9);\n\n  --text-main: #e0e0e0;\n  --text-muted: #888888;\n  --text-dim: #666666;\n  --text-inverse: #121212;\n\n  --accent-color: #8ab4f8;\n  --accent-hover: #aecbfa;\n  --border-color: #333333;\n  --border-subtle: #242424;\n\n  /* Tag Colors */\n  --tag-general: #8ab4f8;\n  --tag-artist: #f28b82;\n  --tag-character: #81c995;\n  --tag-copyright: #d7aefb;\n  --tag-meta: #fdd663;\n\n  /* Fonts, Shadows & Radii */\n  --font-sans: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n  --font-mono: ui-monospace, SFMono-Regular, Consolas, \"Liberation Mono\", Menlo, monospace;\n  --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4);\n  --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.5);\n  --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.65);\n  --radius-xs: 2px;\n  --radius-sm: 4px;\n  --radius-md: 8px;\n  --radius-lg: 12px;\n  --radius-full: 9999px;\n\n  --grid-columns: 3;\n\n  /* Site Variable Overrides (replacing original green variables) */\n  --c-bg: #121212 !important;\n  --c-bg-alt: #1e1e1e !important;\n  --c-bg-deep: #282828 !important;\n  --c-bg-highlight: #2c2c2c !important;\n  --c-bg-overlay: rgba(20, 20, 20, 0.9) !important;\n  --c-text: #e0e0e0 !important;\n  --c-text-soft: #888888 !important;\n  --c-link: #8ab4f8 !important;\n  --c-link-soft: #888888 !important;\n  --c-link-artist: #f28b82 !important;\n  --c-link-character: #81c995 !important;\n  --c-link-copyright: #d7aefb !important;\n  --c-link-metadata: #fdd663 !important;\n  --ff-narrow: var(--font-sans) !important;\n}\n\n*,\n*::before,\n*::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\nhtml {\n  min-height: 100%;\n  background-color: var(--bg-main) !important;\n  background: var(--bg-main) !important;\n  color-scheme: dark;\n}\n\nbody,\ndiv,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\np,\nul,\nol,\nli,\ndd,\ndt {\n  font-family: var(--font-sans);\n  margin: 0;\n  padding: 0;\n}\n\nbody {\n  background-color: var(--bg-main) !important;\n  background: var(--bg-main) !important;\n  color: var(--text-main) !important;\n  font-family: var(--font-sans);\n  font-size: 14px;\n  line-height: 1.5;\n  min-height: 100vh;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n/* ==========================================================================\n   2. Base Typography, Links & Spans\n   ========================================================================== */\na,\na:link,\na:visited {\n  color: var(--accent-color);\n  text-decoration: none;\n  transition: color 0.15s ease;\n}\n\na:hover {\n  color: var(--accent-hover);\n  text-decoration: underline;\n}\n\na:active {\n  color: var(--accent-color);\n}\n\na.last-page {\n  color: var(--text-muted);\n}\n\na.original-file-changed#highres {\n  color: var(--accent-color);\n  font-weight: 600;\n  display: inline-flex;\n  align-items: center;\n  gap: 4px;\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n  color: #fff;\n  font-weight: 600;\n  margin-bottom: 0.5em;\n  line-height: 1.3;\n}\n\nh1 { font-size: 1.75rem; }\nh2 { font-size: 1.5rem; }\nh3 { font-size: 1.25rem; }\nh4 { font-size: 1.1rem; }\nh5 { font-size: 1rem; }\nh6 { font-size: 0.9rem; }\n\nh2.wiki-title {\n  color: var(--accent-color);\n  font-size: 1.5rem;\n  margin-bottom: 0.5rem;\n  border-bottom: 1px solid var(--border-color);\n  padding-bottom: 0.35rem;\n}\n\np,\nul,\nol {\n  margin-bottom: 1em;\n}\n\nli {\n  margin-right: 0.5em;\n  margin-bottom: 0.25em;\n}\n\ncode,\ntextarea {\n  font-family: var(--font-mono);\n}\n\ncode {\n  font-size: 0.9em;\n  background-color: var(--bg-surface-elevated);\n  color: var(--accent-color);\n  padding: 2px 6px;\n  border-radius: var(--radius-sm);\n  border: 1px solid var(--border-color);\n}\n\nimg {\n  border: none;\n  vertical-align: middle;\n  max-width: 100%;\n}\n\naddr {\n  display: block;\n  margin-left: 1.5em;\n  font-weight: 600;\n  color: var(--text-muted);\n}\n\ndd {\n  margin-bottom: 1em;\n  margin-left: 1.5em;\n}\n\ndt {\n  font-weight: 600;\n  color: var(--text-main);\n}\n\ndt.bad {\n  color: #ff4d4f;\n}\n\nlabel {\n  font-weight: 600;\n  white-space: nowrap;\n  color: var(--text-main);\n}\n\nhr {\n  border: 0;\n  height: 1px;\n  background: var(--border-color);\n  margin: 1.5em 0;\n}\n\nhr.light {\n  background: var(--border-subtle);\n}\n\n/* Spoilers */\n.spoiler,\na.spoiler,\nspan.spoiler {\n  background: #333;\n  border-radius: var(--radius-sm);\n  color: transparent;\n  cursor: pointer;\n  padding: 0 4px;\n  user-select: none;\n  transition: color 0.15s ease, background-color 0.15s ease;\n}\n\n.spoiler:hover,\na.spoiler:hover,\nspan.spoiler:hover {\n  color: #fff;\n  background-color: var(--bg-accent);\n  user-select: text;\n}\n\n/* Metadata Spans */\nspan.author {\n  color: var(--accent-color);\n  font-weight: 600;\n}\n\nspan.date {\n  color: var(--text-muted);\n  font-size: 0.85em;\n}\n\nspan.locked-topic {\n  color: #ff4d4f;\n  font-weight: 600;\n}\n\nspan.informational {\n  cursor: help;\n  border-bottom: 1px dotted var(--text-muted);\n}\n\nspan.post-count {\n  color: var(--text-muted);\n  font-size: 0.85em;\n}\n\nspan.plid {\n  display: none;\n}\n\nspan.unread-topic {\n  font-weight: bold;\n  color: var(--accent-color);\n}\n\nstrong.divider {\n  font-weight: 600;\n  color: var(--border-color);\n  margin: 0 6px;\n}\n\n/* Lists */\nul.bare-list {\n  padding-left: 0;\n  list-style: none;\n}\n\nul.bare-list li {\n  list-style-type: none;\n}\n\nul.flat-list {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  gap: 12px;\n  margin: 0;\n  padding: 0;\n  list-style: none;\n}\n\nul.flat-list li {\n  display: inline-flex;\n  align-items: center;\n  list-style-type: none;\n  font-weight: 500;\n  padding: 0;\n  margin: 0;\n}\n\nul.flat-list li a {\n  font-weight: normal;\n  color: var(--accent-color);\n}\n\nul.flat-list li a.forum-update {\n  font-style: italic;\n  color: var(--tag-meta);\n}\n\nul.link-page {\n  font-size: 1.1em;\n  font-weight: 600;\n  padding: 0;\n  list-style: none;\n}\n\nul.link-page > li {\n  margin: 6px 0;\n  padding: 0;\n  list-style-type: none;\n}\n\n/* ==========================================================================\n   3. Forms & Inputs\n   ========================================================================== */\ninput,\nselect,\ntextarea {\n  font-family: inherit;\n  font-size: 14px;\n  color: var(--text-main);\n  background-color: var(--bg-surface-elevated);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-sm);\n  padding: 6px 10px;\n  outline: none;\n  transition: border-color 0.2s ease, box-shadow 0.2s ease;\n}\n\ninput[type=\"text\"],\ninput[type=\"password\"],\ninput[type=\"search\"] {\n  background-color: var(--bg-surface-elevated);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-sm);\n  color: var(--text-main);\n  font-family: inherit;\n  font-size: 14px;\n  padding: 6px 10px;\n  outline: none;\n  transition: border-color 0.2s ease, box-shadow 0.2s ease;\n}\n\ninput[type=\"text\"]:focus,\ninput[type=\"password\"]:focus,\ninput[type=\"search\"]:focus,\ninput[type=password]:focus,\ninput[type=text]:focus,\nselect:focus,\ntextarea:focus {\n  border-color: var(--accent-color);\n  box-shadow: 0 0 0 2px rgba(138, 180, 248, 0.2);\n  background-color: var(--bg-surface-elevated);\n}\n\ntextarea {\n  display: block;\n  font-family: var(--font-mono);\n  font-size: 14px;\n  padding: 8px 10px;\n  line-height: 1.5;\n  width: 100%;\n  box-sizing: border-box;\n}\n\ntextarea.no-block {\n  display: inline;\n}\n\ninput[type=\"submit\"],\ninput[type=\"button\"],\nbutton,\n.button {\n  -webkit-appearance: none !important;\n  -moz-appearance: none !important;\n  appearance: none !important;\n  background-color: var(--bg-surface-elevated) !important;\n  background: var(--bg-surface-elevated) !important;\n  border: 1px solid var(--border-color) !important;\n  border-radius: var(--radius-sm);\n  color: var(--text-main) !important;\n  cursor: pointer;\n  font-family: var(--font-sans);\n  font-size: 14px;\n  font-weight: 500;\n  padding: 6px 14px;\n  transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.1s ease;\n}\n\ninput[type=\"submit\"]:hover,\ninput[type=\"button\"]:hover,\nbutton:hover,\n.button:hover {\n  background-color: #383838 !important;\n  background: #383838 !important;\n  border-color: #555555 !important;\n}\n\ninput[type=\"submit\"]:active,\ninput[type=\"button\"]:active,\nbutton:active,\n.button:active {\n  transform: translateY(1px);\n}\n\n.tradio {\n  position: absolute;\n  clip: rect(0, 0, 0, 0);\n}\n\ndiv#upload-form table,\ndiv#edit-form table {\n  width: 100%;\n  max-width: 850px;\n}\n\n/* ==========================================================================\n   4. Autocomplete (Awesomplete & Legacy)\n   ========================================================================== */\n.awesomplete {\n  position: relative;\n  width: 100%;\n}\n\n.awesomplete [hidden] {\n  display: none;\n}\n\n.awesomplete .visually-hidden {\n  position: absolute;\n  clip: rect(0, 0, 0, 0);\n}\n\n.awesomplete > input {\n  width: 100%;\n  display: block;\n}\n\n.awesomplete > ul {\n  background: var(--bg-surface-elevated);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-sm);\n  box-shadow: var(--shadow-md);\n  box-sizing: border-box;\n  left: 0;\n  list-style: none;\n  margin: 4px 0 0;\n  max-height: 300px;\n  min-width: 100%;\n  overflow-y: auto;\n  padding: 4px 0;\n  position: absolute;\n  z-index: 1000;\n}\n\n.awesomplete > ul:empty {\n  display: none;\n}\n\n@supports (transform: scale(0)) {\n  .awesomplete > ul {\n    transform-origin: top left;\n    transition: opacity 0.15s ease, transform 0.15s ease;\n  }\n\n  .awesomplete > ul[hidden],\n  .awesomplete > ul:empty {\n    opacity: 0;\n    transform: scale(0.95);\n    display: none;\n  }\n}\n\n.awesomplete > ul:before {\n  content: \"\";\n  position: absolute;\n  top: -5px;\n  left: 1.25rem;\n  width: 8px;\n  height: 8px;\n  background-color: var(--bg-surface-elevated);\n  border-left: 1px solid var(--border-color);\n  border-top: 1px solid var(--border-color);\n  transform: rotate(45deg);\n}\n\n.awesomplete > ul > li,\n.awesomeplete > ul > li {\n  color: var(--text-main);\n  cursor: pointer;\n  padding: 6px 12px;\n  position: relative;\n  font-size: 14px;\n  transition: background-color 0.15s ease;\n}\n\n.awesomplete > ul > li:hover,\n.awesomplete > ul > li[aria-selected=\"true\"] {\n  background-color: var(--accent-color);\n  color: var(--text-inverse);\n}\n\n.awesomplete mark {\n  background: transparent;\n  color: var(--tag-meta);\n  font-weight: bold;\n}\n\n.awesomplete > ul > li:hover mark,\n.awesomplete > ul > li[aria-selected=\"true\"] mark {\n  color: var(--text-inverse);\n}\n\n/* Legacy AutoComplete */\ndiv.auto_complete {\n  width: 350px;\n  background-color: var(--bg-surface-elevated);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-sm);\n  box-shadow: var(--shadow-md);\n  z-index: 1000;\n}\n\ndiv.auto_complete ul {\n  border: none;\n  margin: 0;\n  padding: 4px 0;\n  width: 100%;\n  list-style-type: none;\n}\n\ndiv.auto_complete ul li {\n  margin: 0;\n  padding: 6px 12px;\n  color: var(--text-main);\n  cursor: pointer;\n}\n\ndiv.auto_complete ul li.selected {\n  background-color: var(--accent-color);\n  color: var(--text-inverse);\n}\n\ndiv.auto_complete ul strong.highlight {\n  color: var(--tag-meta);\n  font-weight: bold;\n}\n\n/* ==========================================================================\n   5. Booru Layout Structure & Sidebars\n   ========================================================================== */\n#content,\ndiv#content {\n  display: flex;\n  gap: 20px;\n  padding: 20px;\n  max-width: 1600px;\n  margin: 0 auto;\n}\n\ndiv.content {\n  margin: 0 1rem;\n}\n\n.sidebar,\ndiv.sidebar {\n  background-color: var(--bg-surface);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-md);\n  box-shadow: var(--shadow-sm);\n  flex-shrink: 0;\n  padding: 16px;\n  width: 220px;\n  min-width: 200px;\n  max-width: 260px;\n  box-sizing: border-box;\n}\n\n.sidebar li,\ndiv.sidebar li {\n  list-style: none;\n  margin-bottom: 4px;\n}\n\ndiv#post-view,\ndiv#post-list,\n.flexi,\ndiv.flexi {\n  display: flex;\n  gap: 20px;\n  width: 100%;\n}\n\ndiv#right-col {\n  flex: 1 1 auto;\n  min-width: 0;\n}\n\ndiv.postListSidebarRight {\n  margin-left: 20px;\n  width: 200px;\n  min-height: 400px;\n  box-sizing: border-box;\n}\n\ndiv.postViewSidebarRight {\n  margin-left: 20px;\n  width: 320px;\n  min-height: 400px;\n  box-sizing: border-box;\n}\n\n/* Search Sidebar Form */\n.tag-search,\ndiv.tag-search {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n  margin-bottom: 1rem;\n  width: 100%;\n}\n\ndiv.tag-search small {\n  display: none;\n}\n\ndiv.tag-search form br {\n  display: none;\n}\n\ndiv.tag-search input[type=\"text\"] {\n  width: 100%;\n}\n\ndiv.tag-search input[type=\"submit\"] {\n  width: 100%;\n}\n\ndiv.page,\ndiv.help {\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 20px;\n  background-color: var(--bg-surface);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-md);\n  box-shadow: var(--shadow-sm);\n}\n\ndiv.verticalFlexWithMargins {\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n}\n\ndiv.verticalFlexWithMargins > * {\n  margin: 0;\n}\n\ndiv.horizontalFlexWithMargins {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  gap: 10px;\n}\n\ndiv.horizontalFlexWithMargins > * {\n  margin: 0;\n}\n\n/* Static Home Index */\ndiv#static-index {\n  text-align: center;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  padding: 3rem 1.5rem;\n  gap: 1.5rem;\n}\n\ndiv#static-index > div#links {\n  display: flex;\n  flex-wrap: wrap;\n  justify-content: center;\n  gap: 10px;\n}\n\ndiv#static-index > div#links > a {\n  display: inline-flex;\n  align-items: center;\n  padding: 6px 14px;\n  background-color: var(--bg-surface);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-full);\n  color: var(--text-main);\n  font-weight: 500;\n  transition: all 0.15s ease;\n}\n\ndiv#static-index > div#links > a:hover {\n  background-color: var(--bg-surface-elevated);\n  border-color: var(--accent-color);\n  color: var(--accent-color);\n  text-decoration: none;\n  transform: translateY(-1px);\n}\n\ndiv#static-more {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n  gap: 20px;\n  width: 100%;\n  max-width: 1000px;\n  margin: 2rem auto;\n}\n\ndiv#static-more > div {\n  float: none;\n  width: auto;\n  background-color: var(--bg-surface);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-md);\n  padding: 16px;\n}\n\ndiv#static-more > div > ul {\n  margin-top: 8px;\n  padding-left: 0;\n}\n\ndiv#static-more > div > ul > li {\n  list-style-type: none;\n  margin: 6px 0;\n}\n\n/* Related & Tag Columns */\ndiv#related {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 20px;\n  margin: 0 0 1.5rem 0;\n}\n\ndiv#related > div.tag-column {\n  float: none;\n  margin: 0;\n  min-width: 160px;\n  max-width: 220px;\n  background-color: var(--bg-surface);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-md);\n  padding: 16px;\n}\n\ndiv#related > div.tag-column > a {\n  display: block;\n  margin-bottom: 6px;\n}\n\ndiv#tag-list {\n  text-align: center;\n  margin-bottom: 2em;\n  display: flex;\n  flex-wrap: wrap;\n  justify-content: center;\n  gap: 8px;\n}\n\ndiv#tag-list > a {\n  padding: 4px 8px;\n  background-color: var(--bg-surface);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-sm);\n}\n\n.history-search-row {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  margin: 10px 0;\n  clear: both;\n}\n\n.history-search-label {\n  min-width: 100px;\n  float: none;\n  font-weight: 600;\n  color: var(--text-muted);\n}\n\n/* ==========================================================================\n   6. Post Grid & Thumbnails (JS Grid Masonry)\n   ========================================================================== */\ndiv.image-list {\n  display: grid !important;\n  grid-template-columns: repeat(var(--grid-columns, 3), 1fr) !important;\n  grid-auto-rows: 1px !important;\n  row-gap: 0 !important;\n  column-gap: 16px !important;\n  width: 100% !important;\n  padding: 8px 0 !important;\n}\n\n.thumb,\nspan.thumb {\n  position: relative !important;\n  align-self: start !important; /* Prevents grid item from stretching and creating empty gaps */\n  width: 100% !important;\n  height: max-content !important;\n  display: block !important;\n  margin: 0 !important;\n  padding: 0 !important;\n  overflow: hidden;\n  grid-row-end: span 250; /* Default span of 250px until JS takes over */\n  background-color: var(--bg-surface);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-md);\n  box-shadow: var(--shadow-sm);\n  box-sizing: border-box;\n  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;\n}\n\ndiv.image-list > span[style]:not(.thumb),\ndiv#content > div[style*=\"float: left\"] span[style*=\"display: grid\"]:not(.thumb) {\n  display: contents !important;\n}\n\n.thumb:hover,\nspan.thumb:hover {\n  box-shadow: var(--shadow-md);\n  border-color: var(--accent-color);\n  transform: translateY(-2px);\n}\n\n/* Primary Thumbnail Media Link */\n.thumb a,\nspan.thumb a,\n.thumb > a:has(img, video),\nspan.thumb > a:has(img, video),\n.thumb > a:first-of-type,\nspan.thumb > a:first-of-type {\n  display: block !important;\n  width: 100% !important;\n  height: auto !important;\n  min-height: 150px !important;\n  position: relative;\n  overflow: hidden;\n  border-radius: var(--radius-md);\n}\n\n/* Secondary Action Links & Remove Buttons inside .thumb or outer wrapper */\n.thumb > a[href*=\"remove\"]:not(:has(img, video)),\n.thumb > a[href*=\"delete\"]:not(:has(img, video)),\n.thumb > a[onclick*=\"remove\"]:not(:has(img, video)),\n.thumb > a[onclick*=\"delete\"]:not(:has(img, video)),\nspan.thumb > a[href*=\"remove\"]:not(:has(img, video)),\nspan.thumb > a[href*=\"delete\"]:not(:has(img, video)),\nspan.thumb > a[onclick*=\"remove\"]:not(:has(img, video)),\nspan.thumb > a[onclick*=\"delete\"]:not(:has(img, video)),\nspan[style] > a[href*=\"remove\"]:not(:has(img, video)),\nspan[style] > a[href*=\"delete\"]:not(:has(img, video)),\nspan[style] > a[onclick*=\"remove\"]:not(:has(img, video)),\nspan[style] > a[onclick*=\"delete\"]:not(:has(img, video)),\n.thumb > input[type=\"submit\"],\nspan.thumb > input[type=\"submit\"] {\n  position: absolute !important;\n  top: 8px !important;\n  right: 8px !important;\n  z-index: 10 !important;\n  display: inline-flex !important;\n  align-items: center !important;\n  justify-content: center !important;\n  width: auto !important;\n  height: auto !important;\n  min-height: 0 !important;\n  padding: 4px 10px !important;\n  background-color: rgba(239, 68, 68, 0.9) !important;\n  color: #ffffff !important;\n  font-size: 11px !important;\n  font-weight: 600 !important;\n  text-decoration: none !important;\n  border: none !important;\n  border-radius: var(--radius-sm) !important;\n  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4) !important;\n  backdrop-filter: blur(4px) !important;\n  transition: all 0.15s ease !important;\n  cursor: pointer !important;\n}\n\n.thumb > a[href*=\"remove\"]:not(:has(img, video)) b,\n.thumb > a[href*=\"delete\"]:not(:has(img, video)) b,\n.thumb > a[onclick*=\"remove\"]:not(:has(img, video)) b,\n.thumb > a[onclick*=\"delete\"]:not(:has(img, video)) b,\nspan.thumb > a[href*=\"remove\"]:not(:has(img, video)) b,\nspan.thumb > a[href*=\"delete\"]:not(:has(img, video)) b,\nspan.thumb > a[onclick*=\"remove\"]:not(:has(img, video)) b,\nspan.thumb > a[onclick*=\"delete\"]:not(:has(img, video)) b,\nspan[style] > a[href*=\"remove\"]:not(:has(img, video)) b,\nspan[style] > a[href*=\"delete\"]:not(:has(img, video)) b,\nspan[style] > a[onclick*=\"remove\"]:not(:has(img, video)) b,\nspan[style] > a[onclick*=\"delete\"]:not(:has(img, video)) b {\n  color: #ffffff !important;\n  font-size: 11px !important;\n  font-weight: 600 !important;\n}\n\n.thumb > a:not(:has(img, video)):not(:first-of-type):hover,\nspan.thumb > a:not(:has(img, video)):not(:first-of-type):hover,\n.thumb > a[href*=\"remove\"]:not(:has(img, video)):hover,\n.thumb > a[href*=\"delete\"]:not(:has(img, video)):hover,\n.thumb > a[onclick*=\"remove\"]:not(:has(img, video)):hover,\n.thumb > a[onclick*=\"delete\"]:not(:has(img, video)):hover,\nspan.thumb > a[href*=\"remove\"]:not(:has(img, video)):hover,\nspan.thumb > a[href*=\"delete\"]:not(:has(img, video)):hover,\nspan.thumb > a[onclick*=\"remove\"]:not(:has(img, video)):hover,\nspan.thumb > a[onclick*=\"delete\"]:not(:has(img, video)):hover,\nspan[style] > a[href*=\"remove\"]:not(:has(img, video)):hover,\nspan[style] > a[href*=\"delete\"]:not(:has(img, video)):hover,\nspan[style] > a[onclick*=\"remove\"]:not(:has(img, video)):hover,\nspan[style] > a[onclick*=\"delete\"]:not(:has(img, video)):hover,\n.thumb > input[type=\"submit\"]:hover,\nspan.thumb > input[type=\"submit\"]:hover {\n  background-color: rgba(220, 38, 38, 1) !important;\n  color: #ffffff !important;\n  transform: scale(1.05) !important;\n  box-shadow: 0 4px 10px rgba(220, 38, 38, 0.5) !important;\n}\n\n/* Hide line breaks inside .thumb */\n.thumb br,\nspan.thumb br {\n  display: none !important;\n}\n\n.thumb img,\n.thumb video,\nspan.thumb img,\nspan.thumb video {\n  width: 100% !important;\n  height: auto !important;\n  max-width: 100% !important;\n  max-height: unset !important;\n  object-fit: contain;\n  border-radius: var(--radius-md);\n  transition: transform 0.15s ease;\n}\n\n/* Ensure images without inline display:none are displayed as block */\n.thumb img:not([style*=\"display: none\"]),\n.thumb video:not([style*=\"display: none\"]),\nspan.thumb img:not([style*=\"display: none\"]),\nspan.thumb video:not([style*=\"display: none\"]) {\n  display: block !important;\n}\n\n.thumb:hover img,\n.thumb:hover video {\n  transform: scale(1.02);\n}\n\n/* Status Badges on Thumbs */\nimg.flagged      { outline: 3px solid #ff4d4f; border-color: #ff4d4f; }\nimg.has-children { outline: 3px solid #52c41a; border-color: #52c41a; }\nimg.has-parent   { outline: 3px solid #faad14; border-color: #faad14; }\nimg.pending      { outline: 3px solid #1890ff; border-color: #1890ff; }\nimg.video        { outline: 3px solid var(--accent-color); border-color: var(--accent-color); }\n.webm-thumb      { border: 2px solid var(--accent-color); }\n\nimg.preview {\n  margin: auto;\n  display: block;\n  max-width: 100%;\n  border-radius: var(--radius-md);\n  box-shadow: var(--shadow-md);\n}\n\nspan.blacklisted-image {\n  background: var(--bg-surface-elevated) url(/blacklisted.png) no-repeat center;\n  background-size: contain;\n  display: inline-block;\n  vertical-align: top;\n  border-radius: var(--radius-md);\n}\n\ndiv#post-popular > h3 {\n  text-align: center;\n  margin-bottom: 1.5rem;\n  font-size: 1.5rem;\n}\n\ndiv#post-add p#scale {\n  font-style: italic;\n  color: var(--text-muted);\n}\n\n/* ==========================================================================\n   7. Booru Tag Categories\n   ========================================================================== */\n.tag-type-artist > a, .tag-type-artist { color: var(--tag-artist) !important; }\n.tag-type-artist > a:hover, .tag-type-artist:hover { color: #ffb4ab !important; text-decoration: underline; }\n\n.tag-type-character > a, .tag-type-character { color: var(--tag-character) !important; }\n.tag-type-character > a:hover, .tag-type-character:hover { color: #a8f5bb !important; text-decoration: underline; }\n\n.tag-type-copyright > a, .tag-type-copyright { color: var(--tag-copyright) !important; }\n.tag-type-copyright > a:hover, .tag-type-copyright:hover { color: #eed5ff !important; text-decoration: underline; }\n\n.tag-type-metadata > a, .tag-type-metadata { color: var(--tag-meta) !important; }\n.tag-type-metadata > a:hover, .tag-type-metadata:hover { color: #ffe89e !important; text-decoration: underline; }\n\n.tag-count {\n  color: var(--text-muted);\n  font-size: 0.85em;\n  margin-left: 0.3em;\n}\n\n.blacklisted-tags-disabled {\n  color: var(--text-dim) !important;\n  text-decoration: line-through;\n  opacity: 0.6;\n}\n\n.obsolete-tag-change {\n  color: var(--text-dim);\n  text-decoration: line-through;\n}\n\n.added-tags   { color: #52c41a !important; font-weight: 600; }\n.removed-tags { color: #ff4d4f !important; font-weight: 600; text-decoration: line-through; }\n.unchanged-tags { font-size: 0.85em; color: var(--text-muted); }\n\n/* ==========================================================================\n   8. Navigation, Header & Pagination\n   ========================================================================== */\n#header,\ndiv#header {\n  background-color: var(--bg-surface);\n  border-bottom: 1px solid var(--border-color);\n  padding: 10px 20px;\n  margin-bottom: 1.5rem;\n}\n\n#site-title,\ndiv#header #site-title {\n  padding: 10px 0 6px 0;\n  background-image: none;\n}\n\n#site-title a {\n  font-size: 1.75rem;\n  font-weight: 800;\n  color: #fff;\n  text-decoration: none;\n}\n\n#site-title a:hover {\n  color: var(--accent-color);\n}\n\nlabel[for=\"nav-trigger\"],\n.nav-trigger {\n  display: none;\n}\n\n#header ul#navbar,\ndiv#header ul#navbar,\n#header ul#subnavbar,\ndiv#header ul#subnavbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  list-style: none;\n  margin: 0;\n  padding: 4px 0;\n}\n\n#header ul#navbar li a,\ndiv#header ul#navbar li a,\n#header ul#subnavbar li a,\ndiv#header ul#subnavbar li a {\n  border-radius: var(--radius-sm);\n  color: var(--text-muted);\n  font-weight: 500;\n  padding: 4px 10px;\n  display: inline-block;\n  transition: color 0.15s ease, background-color 0.15s ease;\n}\n\n#header ul#navbar li.current-page a,\n#navbar li.current-page a,\ndiv#header ul#navbar li.current-page a,\n#header ul#navbar li a:hover,\ndiv#header ul#navbar li a:hover {\n  background-color: var(--bg-surface-elevated);\n  color: #fff;\n  text-decoration: none;\n}\n\ndiv#header ul#navbar li.current-page,\n#navbar li.current-page {\n  background-image: none;\n}\n\n#header ul#subnavbar,\ndiv#header ul#subnavbar {\n  background-color: var(--bg-surface-elevated);\n  border-radius: var(--radius-sm);\n  padding: 4px 8px;\n  margin-top: 6px;\n}\n\n#header ul#subnavbar li a:hover,\ndiv#header ul#subnavbar li a:hover {\n  color: var(--accent-color);\n}\n\n/* Pagination */\n#paginator,\ndiv#paginator,\n.pagination,\ndiv#paginator .pagination {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  justify-content: center;\n  align-items: center;\n  margin: 2.5rem 0 2rem 0;\n  padding: 8px 0;\n  clear: both;\n}\n\n#paginator a,\n#paginator span,\n#paginator b,\ndiv#paginator a,\ndiv#paginator span,\ndiv#paginator b,\n.pagination a,\n.pagination span,\n.pagination b {\n  background-color: var(--bg-surface);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-sm);\n  color: var(--text-main);\n  padding: 6px 12px;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  min-width: 36px;\n  height: 36px;\n  font-size: 14px;\n  font-weight: 500;\n  line-height: 1;\n  text-align: center;\n  box-sizing: border-box;\n  margin: 0;\n  text-decoration: none;\n  transition: all 0.15s ease;\n  user-select: none;\n}\n\n#paginator a:hover,\ndiv#paginator a:hover,\n.pagination a:hover {\n  background-color: var(--accent-color);\n  border-color: var(--accent-color);\n  color: var(--text-inverse);\n  text-decoration: none;\n  transform: translateY(-1px);\n  box-shadow: var(--shadow-sm);\n}\n\n/* Current active page indicator */\n#paginator b,\ndiv#paginator b,\n.pagination b,\n#paginator span.current,\ndiv#paginator span.current,\ndiv#paginator a.current,\n.pagination span.current,\n.pagination a.current {\n  background-color: var(--accent-color) !important;\n  border: 1px solid var(--accent-color) !important;\n  color: var(--text-inverse) !important;\n  font-weight: 700 !important;\n  cursor: default;\n  box-shadow: 0 0 10px rgba(138, 180, 248, 0.3);\n  transform: none !important;\n}\n\n/* Manual Page Chooser Form */\n.manual-page-chooser,\nform#manualpage,\ndiv#paginator form.manual-page-chooser {\n  display: inline-flex !important;\n  align-items: center;\n  gap: 6px;\n  margin: 0 0 0 12px;\n}\n\n.manual-page-chooser > input[type=\"text\"],\n.manual-page-chooser input[type=\"text\"],\n#manualpage input[type=\"text\"],\n#pageid {\n  -webkit-appearance: none !important;\n  -moz-appearance: none !important;\n  appearance: none !important;\n  width: 4.5rem;\n  height: 36px;\n  text-align: center;\n  padding: 6px 8px;\n  background-color: var(--bg-surface-elevated) !important;\n  background: var(--bg-surface-elevated) !important;\n  border: 1px solid var(--border-color) !important;\n  border-radius: var(--radius-sm) !important;\n  color: var(--text-main) !important;\n  box-sizing: border-box;\n}\n\n.manual-page-chooser > input[type=\"submit\"],\n.manual-page-chooser input[type=\"submit\"],\n#manualpage input[type=\"submit\"],\nform#manualpage input[type=\"submit\"] {\n  -webkit-appearance: none !important;\n  -moz-appearance: none !important;\n  appearance: none !important;\n  height: 36px;\n  padding: 6px 14px;\n  background-color: var(--bg-surface-elevated) !important;\n  background: var(--bg-surface-elevated) !important;\n  border: 1px solid var(--border-color) !important;\n  border-radius: var(--radius-sm) !important;\n  color: var(--text-main) !important;\n  cursor: pointer !important;\n  font-weight: 600;\n  box-sizing: border-box;\n  transition: all 0.15s ease;\n}\n\n.manual-page-chooser > input[type=\"submit\"]:hover,\n.manual-page-chooser input[type=\"submit\"]:hover,\n#manualpage input[type=\"submit\"]:hover,\nform#manualpage input[type=\"submit\"]:hover {\n  background-color: var(--accent-color) !important;\n  background: var(--accent-color) !important;\n  border-color: var(--accent-color) !important;\n  color: var(--text-inverse) !important;\n}\n\n/* ==========================================================================\n   9. Tables & Data\n   ========================================================================== */\ntable {\n  border-collapse: collapse;\n  margin-bottom: 2rem;\n  width: 100%;\n  background-color: var(--bg-surface);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-md);\n  overflow: hidden;\n}\n\ntable th,\ntable td {\n  border-bottom: 1px solid var(--border-color);\n  padding: 10px 12px;\n  text-align: left;\n}\n\ntable th,\ntr.tableheader,\nthead tr {\n  background-color: var(--bg-surface) !important;\n  color: #fff;\n  font-weight: 600;\n  background-image: none !important;\n}\n\ntable tr:nth-child(even) {\n  background-color: rgba(255, 255, 255, 0.02);\n}\n\ntable tr:nth-child(odd) {\n  background-color: transparent;\n}\n\ntable.highlightable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\n\ntable.highlightable th {\n  color: #fff;\n}\n\ntable.highlightable > tbody > tr:hover,\ntable.highlightable tbody tr:hover {\n  background-color: var(--bg-surface-elevated);\n}\n\ntable.highlightable td {\n  border: none;\n  border-bottom: 1px solid var(--border-color);\n}\n\ntable tr.good {\n  background-color: rgba(82, 196, 26, 0.15) !important;\n}\n\ntable tr.selected {\n  background-color: rgba(138, 180, 248, 0.18) !important;\n}\n\ntable tr.highlight {\n  background-color: rgba(250, 173, 20, 0.15) !important;\n}\n\ntable tr.pending-tag {\n  background-color: rgba(24, 144, 255, 0.15) !important;\n}\n\ntable.form {\n  width: 100%;\n  max-width: 900px;\n}\n\ntable.form th {\n  color: #fff;\n  white-space: normal;\n  padding-right: 1.5rem;\n  text-align: right;\n  width: 25%;\n}\n\ntable.form p {\n  font-size: 0.85em;\n  font-weight: normal;\n  color: var(--text-muted);\n  margin-top: 4px;\n}\n\ndiv#artist-index > table td {\n  white-space: nowrap;\n}\n\ndiv#user-record > table > tbody > tr.positive-record {\n  background-color: rgba(82, 196, 26, 0.15) !important;\n}\n\ndiv#user-record > table > tbody > tr.negative-record {\n  background-color: rgba(255, 77, 79, 0.15) !important;\n}\n\ndiv#forum > table th:first-child,\ndiv#forum > table td:first-child {\n  text-align: left;\n}\n\n/* ==========================================================================\n   10. Notices, Blocked, Notes & Image Overlays\n   ========================================================================== */\ndiv.notice,\ndiv.status-notice {\n  background-color: var(--bg-surface);\n  border: 1px solid var(--border-color);\n  border-left: 4px solid var(--accent-color);\n  border-radius: var(--radius-sm);\n  margin: 1rem 0;\n  padding: 12px 16px;\n  color: var(--text-main);\n}\n\ndiv.status-notice p {\n  margin: 0;\n}\n\ndiv.blocked {\n  background-color: rgba(255, 77, 79, 0.1);\n  border: 1px solid #ff4d4f;\n  border-radius: var(--radius-sm);\n  color: #ff4d4f;\n  padding: 1rem;\n  font-weight: 600;\n  text-align: center;\n}\n\ndiv.quote {\n  margin: 1rem 0;\n  padding: 12px 16px;\n  font-style: italic;\n  font-size: 0.95rem;\n  background-color: var(--bg-surface-elevated);\n  border: 1px solid var(--border-color);\n  border-left: 4px solid var(--accent-color);\n  border-radius: var(--radius-sm);\n  color: var(--text-main);\n}\n\ndiv.tips {\n  background-color: var(--bg-surface);\n  border: 1px solid var(--border-color);\n  border-left: 4px solid var(--accent-color);\n  padding: 10px 16px;\n  font-size: 0.9rem;\n  color: var(--text-main);\n  border-radius: var(--radius-sm);\n  margin-bottom: 1rem;\n}\n\ndiv#info {\n  font-style: italic;\n  color: var(--text-muted);\n}\n\n/* Note Overlay on Image */\n#note-container,\ndiv#note-container {\n  position: absolute;\n}\n\n#note-container .note-box,\ndiv#note-container > div.note-box {\n  border: 1px dashed #fff;\n  background-color: rgba(255, 255, 255, 0.08);\n  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5);\n  cursor: move;\n  position: absolute;\n  width: 150px;\n  height: 150px;\n  border-radius: var(--radius-xs);\n  z-index: 90;\n}\n\ndiv#note-container > div.note-box > div.note-corner {\n  width: 8px;\n  height: 8px;\n  position: absolute;\n  bottom: -4px;\n  right: -4px;\n  background-color: var(--accent-color);\n  border: 1px solid var(--bg-main);\n  cursor: se-resize;\n}\n\n#note-container .note-body,\ndiv#note-container > div.note-body {\n  background: rgba(20, 20, 20, 0.9);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-sm);\n  box-shadow: var(--shadow-md);\n  color: #fff;\n  padding: 8px 12px;\n  position: absolute;\n  max-width: 320px;\n  min-width: 160px;\n  backdrop-filter: blur(8px);\n  cursor: pointer;\n  overflow: auto;\n  display: none;\n  z-index: 100;\n}\n\ndiv#note-container > div.note-body > p.tn,\ntn {\n  font-size: 0.8em;\n  color: var(--text-muted);\n}\n\ndiv#note-container > div.unsaved {\n  border: 1px dashed #ff4d4f;\n  background-color: rgba(255, 77, 79, 0.15);\n}\n\ndiv#note-container > div.unsaved > div.note-corner {\n  background-color: #ff4d4f;\n}\n\ndiv#edit-box {\n  background-color: var(--bg-surface-elevated);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-sm);\n  padding: 1rem;\n}\n\ndiv#post-view > div#right-col > div#edit {\n  margin-bottom: 2rem;\n  padding: 1rem;\n  background-color: var(--bg-surface);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-md);\n}\n\n/* ==========================================================================\n   11. Comments, Forums & Response Lists\n   ========================================================================== */\ndiv#post-comments {\n  max-width: 850px;\n  margin-top: 1.5rem;\n}\n\ndiv#post-comments div#comment-list > div,\ndiv#comments div#comment-list > div.post {\n  display: flex;\n  flex-direction: column;\n  background-color: var(--bg-surface);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-md);\n  padding: 16px;\n  margin-bottom: 1rem;\n  box-shadow: var(--shadow-sm);\n  float: none;\n  clear: both;\n}\n\ndiv#post-comments div#comment-list > div > div.col1,\ndiv#comments div#comment-list > div.post > div.col1 {\n  float: none;\n  clear: none;\n  margin-bottom: 8px;\n}\n\ndiv#post-comments div#comment-list > div > div.col2,\ndiv#comments div#comment-list > div.post > div.col2 {\n  float: none;\n  width: 100%;\n  margin-top: 0;\n}\n\ndiv#post-comments div#comment-list > div > div.col2 > div.header,\ndiv#comments div#comment-list > div.post > div.col2 > div.header {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  gap: 8px;\n  margin-bottom: 8px;\n  padding-bottom: 8px;\n  border-bottom: 1px solid var(--border-subtle);\n}\n\ndiv#post-comments div#comment-list > div > div.col2 > div.header > div > span.info,\ndiv#comments div#comment-list > div.post > div.col2 > div.header > div > span.info {\n  margin-right: 8px;\n  color: var(--text-muted);\n  font-size: 0.85rem;\n}\n\ndiv#post-comments div#comment-list > div > div.col2 > div.header > div.tags > span,\ndiv#comments div#comment-list > div.post > div.col2 > div.header > div.tags > span {\n  margin-right: 8px;\n}\n\ndiv.response-list {\n  display: flex;\n  flex-direction: column;\n  gap: 16px;\n}\n\ndiv.response-list > div.post {\n  display: flex;\n  flex-direction: row;\n  gap: 20px;\n  background-color: var(--bg-surface);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-md);\n  padding: 16px;\n  box-shadow: var(--shadow-sm);\n  margin-bottom: 0;\n}\n\ndiv.response-list > div.post > div.author {\n  float: none;\n  width: 180px;\n  flex: 0 0 180px;\n  padding-right: 0;\n  border-right: 1px solid var(--border-subtle);\n  overflow-x: hidden;\n}\n\ndiv.response-list > div.post > div.author > h6 {\n  font-size: 1.1rem;\n  color: var(--accent-color);\n  margin-bottom: 6px;\n}\n\ndiv.response-list > div.post > div.content {\n  float: none;\n  flex: 1 1 auto;\n  width: auto;\n  padding: 0;\n  margin: 0;\n}\n\ndiv.response-list > div.post > div.content > div.footer {\n  margin-top: 12px;\n  padding-top: 8px;\n  border-top: 1px solid var(--border-subtle);\n  color: var(--text-muted);\n  font-size: 0.85rem;\n  display: flex;\n  align-items: center;\n  gap: 12px;\n}\n\ndiv.response-list > div.post > div.content > div.footer a {\n  color: var(--text-muted);\n}\n\ndiv.response-list > div.post > div.content > div.footer a:hover {\n  color: var(--accent-color);\n}\n\n/* ==========================================================================\n   12. Wiki, Diffs, Help & Mail\n   ========================================================================== */\ndiv.wiki > h2.title {\n  color: var(--accent-color);\n  margin-bottom: 12px;\n}\n\ndiv#wiki-show > div#body {\n  line-height: 1.7;\n  max-width: 900px;\n  width: 100%;\n  text-align: left;\n  float: none;\n  background-color: var(--bg-surface);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-md);\n  padding: 20px;\n}\n\ndiv#wiki-show > div#body ul {\n  line-height: 1.6;\n  margin: 8px 0 8px 24px;\n}\n\ndiv#wiki-show > div#body > ul {\n  margin-bottom: 1em;\n}\n\ndiv#wiki-show > div#body > div#byline {\n  font-style: italic;\n  color: var(--text-muted);\n  margin-top: 20px;\n  padding-top: 8px;\n  border-top: 1px solid var(--border-subtle);\n  font-size: 0.9em;\n}\n\ndiv#wiki-diff > div#body {\n  font-family: var(--font-mono);\n  font-size: 14px;\n  margin-top: 1rem;\n  padding: 16px;\n  background-color: var(--bg-surface);\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-md);\n  line-height: 1.6;\n}\n\ndiv#wiki-diff del {\n  text-decoration: line-through;\n  background-color: rgba(255, 77, 79, 0.25);\n  color: #ff4d4f;\n  padding: 2px 6px;\n  border-radius: var(--radius-xs);\n}\n\ndiv#wiki-diff ins {\n  text-decoration: none;\n  background-color: rgba(82, 196, 26, 0.25);\n  color: #52c41a;\n  padding: 2px 6px;\n  border-radius: var(--radius-xs);\n}\n\ndiv.help {\n  margin-top: 1.5rem;\n}\n\ndiv.help a.code {\n  font-family: var(--font-mono);\n}\n\ndiv.help div.code {\n  border: 1px solid var(--border-color);\n  background-color: var(--bg-surface-elevated);\n  margin: 1rem 0;\n  padding: 14px 18px;\n  overflow-x: auto;\n  font-family: var(--font-mono);\n  border-radius: var(--radius-sm);\n  color: var(--accent-color);\n}\n\ndiv.help > div.section {\n  margin-bottom: 2rem;\n}\n\ndiv.help > div.section > div.section {\n  margin-bottom: 1.25rem;\n}\n\ndiv.help h1,\ndiv.help h2,\ndiv.help h3,\ndiv.help h4 {\n  margin: 0 0 0.5em 0;\n}\n\ndiv.help h4 {\n  color: var(--accent-color);\n}\n\n/* Mail */\ndiv.mail .received {\n  color: #52c41a;\n  font-weight: 600;\n}\n\ndiv.mail .sent {\n  color: var(--accent-color);\n  font-weight: 600;\n}\n\ndiv.has-mail {\n  border: 1px solid #52c41a;\n  background-color: rgba(82, 196, 26, 0.15);\n  color: #52c41a;\n  padding: 14px 20px;\n  margin: 1rem 0;\n  font-size: 1.1rem;\n  font-weight: 600;\n  text-align: center;\n  border-radius: var(--radius-sm);\n}\n\ndiv#mail-show div.message {\n  margin-bottom: 2rem;\n}\n\ndiv.mailmessage {\n  max-width: 800px;\n  width: 100%;\n}\n\ndiv.mailbuttons {\n  font-size: 14px;\n  color: var(--text-muted);\n  text-align: center;\n  margin-top: 1rem;\n  display: flex;\n  justify-content: center;\n  gap: 8px;\n}\n\ndiv.mailbuttons :not(:last-child):after {\n  content: \"|\";\n  margin-left: 8px;\n  color: var(--border-color);\n}\n\ndiv.mailhint {\n  padding: 14px 20px;\n  font-weight: 600;\n  background-color: var(--bg-surface-elevated);\n  border-radius: var(--radius-sm);\n  margin-bottom: 1rem;\n  border: 1px solid var(--border-color);\n}\n\ndiv.mailheader {\n  margin-bottom: 8px;\n}\n\ndiv.mailbody {\n  margin: 8px 0;\n  border: 1px solid var(--border-color);\n  background-color: var(--bg-surface);\n  border-radius: var(--radius-sm);\n  padding: 16px;\n  line-height: 1.6;\n}\n\ndiv.mailfooter {\n  margin-top: 8px;\n}\n\ndiv.mailtitle {\n  font-weight: 400;\n  font-size: 1.15rem;\n}\n\ndiv.mailtitle > div {\n  font-weight: 700;\n  padding-left: 14px;\n  color: var(--accent-color);\n}\n\n/* ==========================================================================\n   13. GDPR Consent Banner & Footer\n   ========================================================================== */\n.gdprcontainer {\n  position: fixed;\n  bottom: 24px;\n  left: 0;\n  right: 0;\n  width: 100%;\n  display: flex;\n  justify-content: center;\n  z-index: 10000;\n  pointer-events: none;\n}\n\n.gdprinner {\n  pointer-events: auto;\n  width: auto;\n  max-width: 600px;\n  min-width: 320px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  gap: 16px;\n  background-color: var(--bg-glass) !important;\n  background: var(--bg-glass) !important;\n  border: 1px solid var(--border-color);\n  border-radius: var(--radius-md);\n  padding: 12px 20px;\n  box-shadow: var(--shadow-lg);\n  backdrop-filter: blur(12px);\n}\n\n.gdprtext {\n  font-size: 14px;\n  color: var(--text-main);\n}\n\n.gdprtext a {\n  font-weight: 600;\n  color: var(--accent-color);\n}\n\n.gdprbutton {\n  text-align: center;\n  flex-shrink: 0;\n}\n\n.gdprbutton button {\n  font-size: 14px;\n  padding: 6px 16px;\n  border-radius: var(--radius-sm);\n}\n\n/* Footer */\ndiv#footer {\n  margin-top: 3rem;\n  padding: 2rem 1rem;\n  text-align: center;\n  font-size: 14px;\n  color: var(--text-muted);\n  border-top: 1px solid var(--border-subtle);\n  clear: both;\n}\n\ndiv#footer > .footer-disabled {\n  color: var(--text-dim);\n}\n\ndiv#footer > #label {\n  color: var(--text-dim);\n}\n\n\n/* ==========================================================================\n   Responsive Grid Columns\n   ========================================================================== */\n@media (min-width: 2560px) { :root { --grid-columns: 6; } }\n@media (max-width: 2559px) and (min-width: 1920px) { :root { --grid-columns: 5; } }\n@media (max-width: 1919px) and (min-width: 1600px) { :root { --grid-columns: 4; } }\n@media (max-width: 1599px) and (min-width: 1200px) { :root { --grid-columns: 4; } }\n@media (max-width: 1199px) and (min-width: 992px) { :root { --grid-columns: 3; } }\n@media (max-width: 991px) and (min-width: 768px) { :root { --grid-columns: 3; } }\n@media (max-width: 767px) and (min-width: 576px) { :root { --grid-columns: 2; } }\n@media (max-width: 575px) { :root { --grid-columns: 1; } }\n");
    setTimeout(() => {
        ImageReplacement.init();
        MasonryLayout.init();
    }, 50);

    //region Utilities
    const Utils = {
        debounce: (fn, ms) => { let t; return (...a) => { clearTimeout(t); t = setTimeout(() => fn(...a), ms); }; },
        log: (...args) => console.log("[Modern34]", ...args)
    };

    const currentHost = location.hostname.replace(/^www\./, "");
    const SiteConfig = {
        fallbackImageServer: `https://${currentHost}/images`,
        thumbsOnlyServers: []
    };

    const ImageServer = {
        CACHE_TTL: 7 * 24 * 60 * 60 * 1000,

        resolve() {
            const cacheKey = `imageServerBase_${currentHost}`;
            const expiryKey = `imageServerExpiry_${currentHost}`;
            const cached = GM_getValue(cacheKey, "");
            const expiry = GM_getValue(expiryKey, 0);

            if (cached && Date.now() < expiry) {
                Utils.log("Image server (cached):", cached);
                return cached;
            }

            const fromPage = this.detectFromPage();
            if (fromPage) { this.cache(fromPage); return fromPage; }

            const fallback = SiteConfig.fallbackImageServer;
            this.cache(fallback);
            return fallback;
        },

        detectFromPage() {
            const thumb = document.querySelector('img[src*="/thumbnails/"]');
            if (!thumb) return null;
            try {
                const url = new URL(thumb.src);
                const base = `${url.protocol}//${url.host}/images`;
                Utils.log("Image server (page-detected):", base);
                return base;
            } catch { return null; }
        },

        cache(base) {
            GM_setValue(`imageServerBase_${currentHost}`, base);
            GM_setValue(`imageServerExpiry_${currentHost}`, Date.now() + this.CACHE_TTL);
        }
    };

    const ImageReplacement = {
        processedImages: new Set(),
        extCache: new Map(),
        retryQueue: new Map(),
        serverBase: "",
        observer: null,
        MAX_RETRIES: 3,
        RETRY_DELAYS: [2000, 5000, 12000],

        init() {
            if (!CFG.imageReplacement) return;
            this.serverBase = ImageServer.resolve();
            Utils.log("Using image server:", this.serverBase);

            this.blockSampleLoads();

            this.observer = new IntersectionObserver(
                (entries) => {
                    entries.forEach(entry => {
                        if (entry.isIntersecting) {
                            const img = entry.target;
                            this.replaceThumbnail(img);
                        }
                    });
                },
                { rootMargin: "300px 0px" }
            );

            this.observeAll();

            new MutationObserver(() => this.observeAll())
                .observe(document.body, { childList: true, subtree: true });
        },

        blockSampleLoads() {
            document.querySelectorAll('img[src*="/thumbnails/"]').forEach(img => {
                if (img.style.display === "none") return;
                img.loading = "lazy";
                img.decoding = "async";
                if (!img.dataset.thumbSrc) {
                    img.dataset.thumbSrc = img.src;
                }
            });
        },

        observeAll() {
            document.querySelectorAll('img[src*="/thumbnails/"]').forEach(img => {
                if (img.style.display === "none") return;
                if (!img.dataset.observed) {
                    img.dataset.observed = "true";
                    img.loading = "lazy";
                    img.decoding = "async";
                    if (!img.dataset.thumbSrc) img.dataset.thumbSrc = img.src;
                    this.observer.observe(img);
                }
            });
        },

        extractHash(url) { const m = url.match(/thumbnail_([a-f0-9]+)/); return m ? m[1] : null; },
        extractDirectory(url) { const m = url.match(/thumbnails\/+(\d+)\//); return m ? m[1] : null; },
        extractID(url) { const m = url.match(/thumbnail_[a-f0-9]+\.\w+\?(\d+)/); return m ? m[1] : null; },

        createBaseUrl(thumbUrl) {
            const hash = this.extractHash(thumbUrl);
            const dir = this.extractDirectory(thumbUrl);
            if (!hash || !dir) return null;
            return { base: `${this.serverBase}/${dir}/${hash}`, hash };
        },

        replaceThumbnail(img) {
            const src = img.dataset.thumbSrc || img.src;
            if (this.processedImages.has(src) || img.dataset.processing === "true" || img.dataset.replaced) return;

            const data = this.createBaseUrl(src);
            if (!data) return;

            const { base, hash } = data;
            this.processedImages.add(src);
            img.dataset.processing = "true";
            const id = this.extractID(src);

            if (this.extCache.has(hash)) {
                const ext = this.extCache.get(hash);
                this.loadImage(img, `${base}.${ext}`, hash, ext, src, id);
                return;
            }

            this.tryExtensions(img, base, hash, src, id, 0);
        },

        tryExtensions(img, base, hash, thumbSrc, id, extIdx) {
            const exts = ["jpg", "png", "jpeg", "gif"];
            if (extIdx >= exts.length) {
                Utils.log("All extensions failed for", hash, "– scheduling retry");
                img.dataset.processing = "false";
                this.processedImages.delete(thumbSrc);
                this.scheduleRetry(img, thumbSrc);
                return;
            }

            const ext = exts[extIdx];
            const url = id ? `${base}.${ext}?${id}` : `${base}.${ext}`;

            const probe = new Image();
            probe.onload = () => {
                this.extCache.set(hash, ext);
                img.src = url;
                img.dataset.replaced = "loaded";
                img.dataset.processing = "false";
                this.cancelRetry(thumbSrc);
                img.onload = () => {
                    const thumb = img.closest(".thumb, span.thumb");
                    if (thumb) MasonryLayout.measureThumb(thumb);
                };
            };
            probe.onerror = () => {
                this.tryExtensions(img, base, hash, thumbSrc, id, extIdx + 1);
            };
            probe.src = url;
        },

        loadImage(img, url, hash, ext, thumbSrc, id) {
            const fullUrl = id ? `${url}?${id}` : url;
            const probe = new Image();
            probe.onload = () => {
                img.src = fullUrl;
                img.dataset.replaced = "cached";
                img.dataset.processing = "false";
                this.cancelRetry(thumbSrc);
                img.onload = () => {
                    const thumb = img.closest(".thumb, span.thumb");
                    if (thumb) MasonryLayout.measureThumb(thumb);
                };
            };
            probe.onerror = () => {
                Utils.log("Cached extension failed for", hash, "– retrying");
                this.extCache.delete(hash);
                img.dataset.processing = "false";
                this.processedImages.delete(thumbSrc);
                this.scheduleRetry(img, thumbSrc);
            };
            probe.src = fullUrl;
        },

        scheduleRetry(img, thumbSrc) {
            const existing = this.retryQueue.get(thumbSrc);
            const retries = existing ? existing.retries : 0;

            if (retries >= this.MAX_RETRIES) return;

            const delay = this.RETRY_DELAYS[retries] || this.RETRY_DELAYS[this.RETRY_DELAYS.length - 1];
            const timer = setTimeout(() => {
                this.retryQueue.delete(thumbSrc);
                const r = img.getBoundingClientRect();
                if (r.top < window.innerHeight + 500 && r.bottom > -500) {
                    this.replaceThumbnail(img);
                }
            }, delay);

            this.retryQueue.set(thumbSrc, { img, retries: retries + 1, timer });
        },

        cancelRetry(thumbSrc) {
            const entry = this.retryQueue.get(thumbSrc);
            if (entry) {
                clearTimeout(entry.timer);
                this.retryQueue.delete(thumbSrc);
            }
        }
    };
    //endregion

    //region Masonry Layout
    const MasonryLayout = {
        gridGap: 16, // vertical gap
        gridRowHeight: 1, // 1px precision rows

        init() {
            const imageList = document.querySelector("div.image-list");
            if (!imageList) return;

            // inital layout
            setTimeout(() => this.layoutAll(imageList), 250);

            imageList.addEventListener("load", (e) => { // reload when necessary
                if (e.target.tagName === "IMG") {
                    this.measureThumb(e.target.closest(".thumb, span.thumb"));
                }
            }, true);

            window.addEventListener("resize", Utils.debounce(() => { // on resize
                this.layoutAll(imageList);
            }, 200));

            // watcher
            new MutationObserver(() => {
                this.layoutAll(imageList);
            }).observe(imageList, { childList: true, subtree: true });
        },

        layoutAll(container) {
            if (!container) container = document.querySelector("div.image-list");
            if (!container) return;

            this.fixRemoveButtons(container);
            const thumbs = container.querySelectorAll(".thumb, span.thumb");
            thumbs.forEach(thumb => this.measureThumb(thumb));
        },

        fixRemoveButtons(container) {
            const target = container || document;
            target.querySelectorAll('span[style] > a[onclick*="delete"], span[style] > a[onclick*="remove"], span[style] > a[href*="delete"], span[style] > a[href*="remove"]').forEach(btn => {
                const thumb = btn.parentElement.querySelector('.thumb, span.thumb');
                if (thumb && btn.parentElement !== thumb) {
                    thumb.appendChild(btn);
                }
            });
        },

        measureThumb(thumb) {
            if (!thumb) return;

            requestAnimationFrame(() => {
                requestAnimationFrame(() => {
                    const content = thumb.querySelector("a") || thumb;
                    const img = thumb.querySelector("img, video");

                    if (img && img.tagName === "IMG" && !img.complete) {
                        if (!img.dataset.masonryBound) {
                            img.dataset.masonryBound = "true";
                            img.addEventListener("load", () => this.measureThumb(thumb), { once: true });
                        }
                        return;
                    }

                    const h = thumb.scrollHeight; 
                    if (h <= 0) return;

                    const span = Math.ceil((h + this.gridGap) / this.gridRowHeight);
                    thumb.style.gridRowEnd = `span ${span}`;
                });
            });
        }
    };
    //endregion
})();