Rule34 - Reskin

Rule34 - Dark Masonry Reskin

As of 2025-05-24. See the latest version.

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

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Rule34 - Reskin
// @namespace    ko-fi.com/awesome97076
// @version      1
// @description  Rule34 - Dark Masonry Reskin
// @author       Awesome
// @match        https://rule34.xxx/*
// @license      MIT
// @icon         https://www.google.com/s2/favicons?sz=64&domain=rule34.xxx
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function () {
  "use strict";

  const style = document.createElement("style");
  style.innerHTML = `
:root {
  /* Dark theme color palette */
  --bg-color: #121212;
  --bg-secondary: #1e1e1e;
  --bg-tertiary: #2d2d2d;
  --accent-color: #9c64a6;
  --accent-secondary: #ae81ff;
  --text-primary: #e0e0e0;
  --text-secondary: #b0b0b0;
  --text-muted: #707070;
  --border-color: rgba(255, 255, 255, 0.1);
  --success-color: #50fa7b;
  --warning-color: #ffb86c;
  --error-color: #ff5555;

  /* Tag type colors - vibrant but fitting dark theme */
  --tag-artist: #ff79c6;
  --tag-character: #50fa7b;
  --tag-copyright: #bd93f9;
  --tag-metadata: #f1fa8c;

  /* Layout - Responsive defaults */
  --container-width: 100%;
  --container-max-width: 100%;
  --column-count: 3; /* Start with max columns for large screens */
  --column-gap: 3px;
  --content-padding: 8px;
  --border-radius: 6px;
  --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);

  /* Thumbnails */
  --thumbnail-padding: 0;
  --thumbnail-border-width: 2px;
  --thumbnail-border-radius: 8px;
  --thumbnail-hover-scale: 1.08;
  --thumbnail-transition: all 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94);
  --video-border-color: #8e44ad;

  /* Typography - Responsive base */
  --font-primary: 'Inter', 'Segoe UI', Roboto, -apple-system, BlinkMacSystemFont, sans-serif;
  --font-secondary: 'Poppins', 'Segoe UI', Roboto, sans-serif;
  --font-code: 'Fira Code', 'Cascadia Code', Consolas, monospace;
  --font-size-base: clamp(14px, 2.5vw, 16px); /* Responsive font size */
  --line-height: 1.6;

  /* UI Elements */
  --button-padding: 8px 16px;
  --input-padding: 8px 12px;
  --nav-height: 60px;
}

/* Basic resets and body styling */
html, body {
  margin: 0;
  padding: 0;
  font-family: var(--font-primary);
  font-size: var(--font-size-base);
  line-height: var(--line-height);
  background-color: var(--bg-color);
  color: var(--text-primary);
  min-width: 320px; /* Minimum supported width */
  overflow-x: hidden; /* Prevent horizontal scroll */
}

body, div, h1, h2, h3, h4, h5, h6, p, ul, li, dd, dt {
  font-family: var(--font-primary);
}

/* Apply border-box sizing to all elements for consistent sizing */
*, *::before, *::after {
  box-sizing: border-box;
}

/* Content container - Fully responsive */
div#content {
  width: 100%;
  max-width: 100%;
  margin: 0 auto;
  padding: var(--content-padding);
  box-sizing: border-box;
}

/* Header area modernization - Mobile first */
div#header {
  background: var(--bg-secondary);
  padding: 0;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
  position: sticky;
  top: 0;
  z-index: 1000;
  margin-bottom: clamp(16px, 3vw, 24px);
}

div#header #site-title {
  background-image: none;
  padding: clamp(10px, 2vw, 15px) clamp(15px, 3vw, 20px);
}

div#header #site-title a {
  color: var(--accent-color);
  font-size: clamp(20px, 4vw, 24px);
  font-weight: bold;
  font-family: var(--font-secondary);
  word-wrap: break-word;
}

.tag-count {
  color: var(--text-secondary);
  font-size: clamp(12px, 2vw, 14px);
}

/* Navigation styling - Responsive */
div#header ul#navbar,
div#header ul#subnavbar {
  background: var(--bg-secondary);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  list-style: none;
  padding: 0 clamp(10px, 2vw, 20px);
  margin: 0;
  border-bottom: 1px solid var(--border-color);
  gap: 0;
  overflow-x: auto; /* Allow horizontal scroll on very small screens */
  -webkit-overflow-scrolling: touch; /* Smooth scrolling on iOS */
}

div#header ul#navbar li,
div#header ul#subnavbar li {
  margin: 0;
  padding: 0;
  flex-shrink: 0;
}

div#header ul#navbar li a,
div#header ul#subnavbar li a {
  display: block;
  padding: clamp(10px, 2vw, 15px);
  color: var(--text-secondary);
  transition: color 0.2s ease;
  text-decoration: none;
  white-space: nowrap;
  font-size: clamp(13px, 2.5vw, 15px);
}

div#header ul#navbar li a:hover,
div#header ul#subnavbar li a:hover {
  color: var(--accent-color);
}

div#header ul#navbar li.current-page,
div#header ul#subnavbar li.current-page {
  background: transparent;
}

div#header ul#navbar li.current-page a,
div#header ul#subnavbar li.current-page a {
  color: var(--accent-color);
  font-weight: bold;
}

/* Links styling */
a:link, a:visited {
  color: var(--accent-color);
  text-decoration: none;
  transition: color 0.2s, text-decoration 0.2s;
}

a:hover, a:active {
  color: var(--accent-secondary);
  text-decoration: underline;
}

/* Search form styling - Responsive */
div.tag-search {
  background: var(--bg-tertiary);
  padding: clamp(10px, 2vw, 15px);
  border-radius: var(--border-radius);
  margin-bottom: clamp(15px, 3vw, 20px);
  width: 100%;
  max-width: 100%;
}

div.tag-search input[type="text"] {
  width: 100%;
  padding: var(--input-padding);
  background: var(--bg-color);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius);
  color: var(--text-primary);
  font-family: var(--font-primary);
  font-size: clamp(14px, 2.5vw, 16px);
  margin-bottom: 10px;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
}

div.tag-search input[type="submit"] {
  padding: var(--button-padding);
  background: var(--accent-color);
  color: white;
  border: none;
  border-radius: var(--border-radius);
  cursor: pointer;
  font-weight: bold;
  font-size: clamp(14px, 2.5vw, 16px);
  transition: background 0.2s;
  width: 100%;
  max-width: 200px;
}

div.tag-search input[type="submit"]:hover {
  background: var(--accent-secondary);
}

/* Tag styling */
.tag-type-artist a, .tag-type-artist {
  color: var(--tag-artist) !important;
}

.tag-type-character a, .tag-type-character {
  color: var(--tag-character) !important;
}

.tag-type-copyright a, .tag-type-copyright {
  color: var(--tag-copyright) !important;
}

.tag-type-metadata a, .tag-type-metadata {
  color: var(--tag-metadata) !important;
}

/* Masonry layout for the image grid - Fully responsive */
div.image-list {
  display: block !important;
  column-count: var(--column-count);
  column-gap: var(--column-gap);
  width: 100%;
  max-width: 100%;
  margin: 0 auto;
  padding: 0;
}

/* Thumbnail container */
.thumb {
  width: 100% !important;
  height: auto !important;
  display: inline-block !important;
  break-inside: avoid;
  margin-bottom: var(--column-gap);
  padding: var(--thumbnail-padding);
  box-sizing: border-box;
  page-break-inside: avoid;
}

/* Thumbnail links */
.thumb a {
  display: block !important;
  position: relative;
  overflow: hidden;
  border-radius: var(--thumbnail-border-radius);
  background: var(--bg-tertiary);
  box-shadow: var(--box-shadow);
  transition: var(--thumbnail-transition);
  max-height: 100%;
  height: 100%;
  width: auto;
}

.thumb a:hover {
  transform: scale(var(--thumbnail-hover-scale));
  transition: var(--thumbnail-transition);
  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.4);
  z-index: 9999;
}

/* Images within thumbnails */
.thumb img, .thumb video {
  width: 100% !important;
  height: auto !important;
  max-height: 100% !important;
  max-width: 100% !important;
  display: block !important;
  transition: var(--thumbnail-transition);
  object-fit: contain;
}

/* Special styling for video thumbnails */
.webm-thumb {
  border: var(--thumbnail-border-width) solid var(--video-border-color) !important;
  border-radius: var(--thumbnail-border-radius) !important;
  box-sizing: border-box !important;
}

/* Hover effect for video thumbnails */
.thumb a:hover .webm-thumb {
  opacity: 0.9;
}

/* Enhanced responsive breakpoints for any screen size */
/* Ultra-wide screens (4K+) */
@media (min-width: 2560px) {
  :root {
    --column-count: 6;
    --column-gap: 20px;
    --content-padding: 30px;
    --font-size-base: 17px;
  }
}

/* Very large screens */
@media (max-width: 2559px) and (min-width: 1920px) {
  :root {
    --column-count: 4;
    --column-gap: 18px;
  }
}

/* Large screens */
@media (max-width: 1919px) and (min-width: 1600px) {
  :root {
    --column-count: 4;
    --column-gap: 16px;
  }
}

/* Medium-large screens */
@media (max-width: 1599px) and (min-width: 1200px) {
  :root {
    --column-count: 3;
    --column-gap: 14px;
  }
}

/* Medium screens */
@media (max-width: 1199px) and (min-width: 992px) {
  :root {
    --column-count: 3;
    --column-gap: 12px;
  }
}

/* Small tablets */
@media (max-width: 991px) and (min-width: 768px) {
  :root {
    --column-count: 3;
    --column-gap: 10px;
    --content-padding: 15px;
  }
}

/* Large phones / small tablets */
@media (max-width: 767px) and (min-width: 576px) {
  :root {
    --column-count: 2;
    --column-gap: 8px;
    --content-padding: 12px;
  }
}

/* Mobile phones */
@media (max-width: 575px) {
  :root {
    --column-count: 1;
    --column-gap: 10px;
    --content-padding: 10px;
    --nav-height: 50px;
  }

  /* Stack navigation vertically on very small screens */
  div#header ul#navbar,
  div#header ul#subnavbar {
    justify-content: center;
  }

  /* Make buttons full width on mobile */
  button, input[type="submit"] {
    width: 100%;
  }
}

/* Ultra-small screens (older phones) */
@media (max-width: 359px) {
  :root {
    --content-padding: 8px;
    --column-gap: 8px;
    --thumbnail-padding: 4px;
  }

  div#header ul#navbar li a,
  div#header ul#subnavbar li a {
    padding: 8px;
  }
}

/* Paginator styling - Responsive */
div#paginator {
  display: flex;
  justify-content: center;
  align-items: center;
  margin: clamp(20px, 4vw, 30px) 0;
  flex-wrap: wrap;
  gap: 4px;
}

div#paginator a,
div#paginator b {
  display: inline-block;
  padding: clamp(6px, 1.5vw, 8px) clamp(10px, 2vw, 14px);
  margin: 2px;
  border-radius: var(--border-radius);
  background: var(--bg-tertiary);
  color: var(--text-secondary);
  text-decoration: none;
  transition: all 0.2s;
  font-size: clamp(13px, 2.5vw, 15px);
}

div#paginator a:hover {
  background: var(--accent-color);
  color: white;
  text-decoration: none;
}

div#paginator b {
  background: var(--accent-color);
  color: white;
  font-weight: bold;
}

/* Manual page chooser styling - Responsive */
.manual-page-chooser {
  display: inline-flex;
  align-items: center;
  margin: 10px 0;
  width: 100%;
  max-width: 200px;
}

.manual-page-chooser input[type="text"] {
  width: 100%;
  padding: clamp(6px, 1.5vw, 8px);
  background: var(--bg-tertiary);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius) 0 0 var(--border-radius);
  color: var(--text-primary);
  font-size: clamp(13px, 2.5vw, 15px);
}

.manual-page-chooser input[type="submit"] {
  padding: clamp(6px, 1.5vw, 8px) clamp(10px, 2vw, 12px);
  background: var(--accent-color);
  color: white;
  border: none;
  border-radius: 0 var(--border-radius) var(--border-radius) 0;
  cursor: pointer;
  font-size: clamp(13px, 2.5vw, 15px);
  white-space: nowrap;
}

/* Tables styling - Responsive */
table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: clamp(15px, 3vw, 20px);
  background: var(--bg-secondary);
  border-radius: var(--border-radius);
  overflow: hidden;
  display: block;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

table th {
  background: var(--bg-tertiary);
  color: var(--text-primary);
  padding: clamp(8px, 1.5vw, 12px);
  text-align: left;
  font-weight: bold;
  font-size: clamp(13px, 2.5vw, 15px);
  white-space: nowrap;
}

table td {
  padding: clamp(8px, 1.5vw, 12px);
  border-top: 1px solid var(--border-color);
  color: var(--text-secondary);
  font-size: clamp(13px, 2.5vw, 15px);
}

table tr:hover td {
  background: rgba(255, 255, 255, 0.03);
}

/* Form controls - Responsive */
input, textarea, select {
  background: var(--bg-tertiary);
  color: var(--text-primary);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius);
  padding: var(--input-padding);
  font-family: var(--font-primary);
  font-size: clamp(14px, 2.5vw, 16px);
  max-width: 100%;
}

/* Auto-expanding text input fields */
textarea, input[type="text"] {
  word-wrap: break-word;
  word-break: normal;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  min-height: 2.5em;
  line-height: 1.5;
  vertical-align: top;
  resize: vertical;
  width: 100%;
}

/* Convert single-line text inputs to auto-expanding */
input[type="text"] {
  height: auto !important;
  min-height: calc(1.5em + var(--input-padding) * 2);
}

/* Ensure inputs can expand on user input with JavaScript support */
textarea {
  overflow: hidden;
  transition: height 0.2s ease;
}

input:focus, textarea:focus, select:focus {
  outline: none;
  border-color: var(--accent-color);
  box-shadow: 0 0 0 2px rgba(156, 100, 166, 0.2);
}

button, input[type="submit"] {
  background: var(--accent-color);
  color: white;
  border: none;
  border-radius: var(--border-radius);
  padding: var(--button-padding);
  cursor: pointer;
  font-weight: bold;
  transition: background 0.2s;
  font-size: clamp(14px, 2.5vw, 16px);
  touch-action: manipulation; /* Improve touch responsiveness */
}

button:hover, input[type="submit"]:hover {
  background: var(--accent-secondary);
}

/* Additional modernizations */

/* Notice styling - Responsive */
div.notice, div.status-notice {
  background: var(--bg-secondary);
  border-left: 4px solid var(--accent-color);
  color: var(--text-primary);
  padding: clamp(10px, 2vw, 15px);
  margin: clamp(10px, 2vw, 15px) 0;
  border-radius: 0 var(--border-radius) var(--border-radius) 0;
  font-size: clamp(13px, 2.5vw, 15px);
}

/* Quote blocks - Responsive */
div.quote {
  background: var(--bg-tertiary);
  border-left: 4px solid var(--accent-secondary);
  padding: clamp(10px, 2vw, 15px);
  margin: clamp(10px, 2vw, 15px) 0;
  font-style: italic;
  font-size: clamp(13px, 2.5vw, 15px);
  border-radius: 0 var(--border-radius) var(--border-radius) 0;
}

/* Footer styling - Responsive */
div#footer {
  margin-top: clamp(30px, 5vw, 40px);
  padding: clamp(15px, 3vw, 20px) 0;
  text-align: center;
  color: var(--text-muted);
  border-top: 1px solid var(--border-color);
  font-size: clamp(12px, 2vw, 14px);
}

/* Hide or modernize ads */
[data-nosnippet], .gdprcontainer {
  display: none !important;
}

/* Special effects for links in masonry grid */
.thumb a::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: linear-gradient(to bottom, rgba(0,0,0,0) 70%, rgba(0,0,0,0.8) 100%);
  opacity: 0;
  transition: opacity 0.3s ease;
  z-index: 1;
  border-radius: var(--thumbnail-border-radius);
}

/* Disable hover effects on touch devices */
@media (hover: hover) {
  .thumb a:hover::before {
    opacity: 1;
  }
}

img {
  border: none;
  vertical-align: middle;
  max-height: 90vh;
  width: auto;
}

/* ===== SIDEBAR STYLES ===== */
div.sidebar {
  background: var(--bg-secondary);
  border-radius: 12px;
  box-shadow: var(--box-shadow);
  padding: 0;
  margin-right: clamp(10px, 2vw, 20px);
  margin-bottom: clamp(15px, 3vw, 25px);
  max-width: 280px;
  min-width: 260px;
  border: 1px solid var(--border-color);
  overflow: hidden;
  transition: all 0.3s ease;
}

/* Hover effect for entire sidebar */
div.sidebar:hover {
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.4);
  border-color: rgba(156, 100, 166, 0.3);
}

/* Search section - Enhanced modern design */
div.tag-search {
  background: linear-gradient(135deg, var(--bg-tertiary) 0%, rgba(45, 45, 45, 0.8) 100%);
  padding: clamp(15px, 3vw, 20px);
  margin-bottom: 0;
  border-radius: 0;
  border-bottom: 1px solid var(--border-color);
  position: relative;
}

div.tag-search::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: linear-gradient(90deg, var(--accent-color), var(--accent-secondary));
  opacity: 0.8;
}

div.tag-search h5 {
  color: var(--text-primary);
  font-family: var(--font-secondary);
  font-weight: 600;
  font-size: clamp(16px, 3vw, 18px);
  margin: 0 0 12px 0;
  display: flex;
  align-items: center;
  gap: 8px;
}

div.tag-search h5::before {
  content: "🔍";
  font-size: 16px;
  opacity: 0.8;
}

/* Enhanced search input */
div.tag-search input[type="text"] {
  width: 100%;
  padding: 12px 16px;
  background: var(--bg-color);
  border: 2px solid rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  color: var(--text-primary);
  font-family: var(--font-primary);
  font-size: 14px;
  margin-bottom: 12px;
  transition: all 0.3s ease;
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
}

div.tag-search input[type="text"]:focus {
  border-color: var(--accent-color);
  box-shadow:
    inset 0 2px 4px rgba(0, 0, 0, 0.1),
    0 0 0 3px rgba(156, 100, 166, 0.15);
  transform: translateY(-1px);
}

div.tag-search input[type="text"]::placeholder {
  color: var(--text-muted);
}

/* Modern search button */
div.tag-search input[type="submit"] {
  width: 100%;
  padding: 12px 20px;
  background: linear-gradient(135deg, var(--accent-color) 0%, var(--accent-secondary) 100%);
  color: white;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font-weight: 600;
  font-size: 14px;
  transition: all 0.3s ease;
  position: relative;
  overflow: hidden;
}

div.tag-search input[type="submit"]:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(156, 100, 166, 0.4);
}

div.tag-search input[type="submit"]:active {
  transform: translateY(0);
}

/* Ripple effect for search button */
div.tag-search input[type="submit"]::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.3);
  transition: width 0.3s, height 0.3s, top 0.3s, left 0.3s;
  transform: translate(-50%, -50%);
}

/* Search hint styling */
div.tag-search small {
  color: var(--text-muted);
  font-size: 12px;
  display: block;
  margin-top: 8px;
  font-style: italic;
}

/* Display options section */
ul#displayOptions {
  background: var(--bg-tertiary);
  margin: 0;
  padding: 15px 20px;
  list-style: none;
  border-bottom: 1px solid var(--border-color);
}

ul#displayOptions li {
  margin: 0;
  padding: 8px 0;
}

ul#displayOptions label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: var(--text-secondary);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: color 0.2s ease;
}

ul#displayOptions label:hover {
  color: var(--text-primary);
}

/* Modern toggle switch styling */
.switch {
  position: relative;
  display: inline-block;
  width: 48px;
  height: 24px;
  margin-left: 10px;
}

.switch input {
  opacity: 0;
  width: 0;
  height: 0;
}

.slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(255, 255, 255, 0.1);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  border-radius: 24px;
  border: 1px solid rgba(255, 255, 255, 0.1);
}

.slider:before {
  position: absolute;
  content: "";
  height: 18px;
  width: 18px;
  left: 3px;
  bottom: 2px;
  background: var(--text-muted);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  border-radius: 50%;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}

input:checked + .slider {
  background: linear-gradient(135deg, var(--accent-color), var(--accent-secondary));
  border-color: var(--accent-color);
}

input:checked + .slider:before {
  transform: translateX(24px);
  background: white;
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.3);
}

.slider:hover {
  box-shadow: 0 0 0 8px rgba(156, 100, 166, 0.1);
}

/* Tags section - Modern card design */
#tag-sidebar {
  margin: 0;
  padding: 0;
  list-style: none;
  max-height: 60vh;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--accent-color) var(--bg-tertiary);
}

/* Custom scrollbar for tag section */
#tag-sidebar::-webkit-scrollbar {
  width: 6px;
}

#tag-sidebar::-webkit-scrollbar-track {
  background: var(--bg-tertiary);
}

#tag-sidebar::-webkit-scrollbar-thumb {
  background: var(--accent-color);
  border-radius: 3px;
}

#tag-sidebar::-webkit-scrollbar-thumb:hover {
  background: var(--accent-secondary);
}

/* Tag category headers with collapsible functionality */
#tag-sidebar h6 {
  background: linear-gradient(135deg, var(--bg-tertiary) 0%, rgba(45, 45, 45, 0.8) 100%);
  color: var(--text-primary);
  margin: 0;
  padding: 12px 20px;
  font-family: var(--font-secondary);
  font-weight: 600;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 1px;
  border-bottom: 1px solid var(--border-color);
  position: sticky;
  top: 0;
  z-index: 10;
  cursor: pointer;
  user-select: none;
  transition: all 0.3s ease;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

#tag-sidebar h6:hover {
  background: linear-gradient(135deg, rgba(156, 100, 166, 0.2) 0%, rgba(174, 129, 255, 0.15) 100%);
  color: var(--accent-color);
}

/* Equilateral arrowhead indicator */
#tag-sidebar h6::after {
  content: '';
  width: 0;
  height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-top: 8px solid currentColor;
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  opacity: 0.7;
  margin-left: 8px;
}

/* Collapsed state arrow */
#tag-sidebar h6.collapsed::after {
  transform: rotate(-90deg);
}

/* Tag section container for collapsing */
.tag-section {
  overflow: hidden;
  transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease;
  max-height: 1000px; /* Large enough for any tag list */
  opacity: 1;
}

.tag-section.collapsed {
  max-height: 0;
  opacity: 0;
}

/* Smooth animation for tag items when collapsing */
.tag-section.collapsing {
  transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease;
}

/* Tag list items - Updated selector to work with all tag items */
#tag-sidebar li {
  padding: 0;
  margin: 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  transition: background 0.2s ease;
}

/* Only apply hover to non-header items */
#tag-sidebar li:not(:has(h6)):hover,
#tag-sidebar li.tag-type-general:hover,
#tag-sidebar li.tag-type-artist:hover,
#tag-sidebar li.tag-type-character:hover,
#tag-sidebar li.tag-type-copyright:hover,
#tag-sidebar li.tag-type-metadata:hover {
  background: rgba(255, 255, 255, 0.03);
}

#tag-sidebar li:last-child {
  border-bottom: none;
}

/* Ensure header items don't get tag item styling */
#tag-sidebar li:has(h6) {
  padding: 0;
  border-bottom: none;
  background: none;
}

/* Tag links and controls */
#tag-sidebar li a {
  display: inline-block;
  padding: 8px 12px;
  color: var(--text-secondary);
  text-decoration: none;
  font-size: 13px;
  transition: all 0.2s ease;
  border-radius: 4px;
}

#tag-sidebar li a:hover {
  color: var(--text-primary);
  background: rgba(255, 255, 255, 0.05);
}

/* Tag type specific colors with modern styling - Updated for all tag types */
.tag-type-general a, .tag-type-general {
  color: var(--text-secondary) !important;
}

.tag-type-artist a, .tag-type-artist {
  color: var(--tag-artist) !important;
}

.tag-type-character a, .tag-type-character {
  color: var(--tag-character) !important;
}

.tag-type-copyright a, .tag-type-copyright {
  color: var(--tag-copyright) !important;
}

.tag-type-metadata a, .tag-type-metadata {
  color: var(--tag-metadata) !important;
}

/* Tag control buttons (?, +, -) */
#tag-sidebar li a[href*="wiki"],
#tag-sidebar li a[onclick*="+"]:not([href*="tags="]),
#tag-sidebar li a[onclick*="-"]:not([href*="tags="]) {
  width: 26px;
  height: 26px;
  padding: 0;
  margin: 0 2px;
  text-align: center;
  line-height: 26px;
  font-size: 13px;
  font-weight: bold;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.1);
  color: var(--text-muted);
  transition: all 0.2s ease;
}

#tag-sidebar li a[href*="wiki"]:hover {
  background: var(--accent-color);
  color: white;
}

#tag-sidebar li a[onclick*="+"]:not([href*="tags="]):hover {
  background: var(--success-color);
  color: white;
}

#tag-sidebar li a[onclick*="-"]:not([href*="tags="]):hover {
  background: var(--error-color);
  color: white;
}

/* Tag count styling */
.tag-count {
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 500;
  background: rgba(255, 255, 255, 0.05);
  padding: 2px 6px;
  border-radius: 10px;
  margin-left: auto;
  display: inline-block;
  min-width: 20px;
  text-align: center;
}

/* Tag item layout - flexbox for better control */
#tag-sidebar li.tag-type-general,
#tag-sidebar li.tag-type-artist,
#tag-sidebar li.tag-type-character,
#tag-sidebar li.tag-type-copyright,
#tag-sidebar li.tag-type-metadata {
  display: flex;
  align-items: center;
  padding: 6px 12px;
  gap: 4px;
  flex-wrap: wrap;
}

/* Ensure header items display normally */
#tag-sidebar li:has(h6) {
  display: block;
}

/* Main tag link takes remaining space */
#tag-sidebar li a[href*="tags="]:not([onclick]) {
  flex: 1;
  padding: 4px 8px;
  margin: 0;
  word-break: break-word;
}

/* Hidden blacklist section styling */
#blacklisted-sidebar {
  background: var(--bg-tertiary);
  padding: 15px 20px;
  border-bottom: 1px solid var(--border-color);
}

#blacklisted-sidebar h5 {
  margin: 0;
  color: var(--text-secondary);
  font-size: 14px;
  font-weight: 600;
}

#blacklisted-sidebar a {
  color: var(--warning-color);
  text-decoration: none;
}

#blacklisted-sidebar a:hover {
  color: var(--error-color);
}

/* Sidebar footer - cute mascot area */
div.sidebar img[src*="r34chibi"] {
  display: block;
  margin: 15px auto;
  opacity: 0.6;
  transition: opacity 0.3s ease;
  filter: brightness(1.2) contrast(1.1);
}

div.sidebar img[src*="r34chibi"]:hover {
  opacity: 1;
}

/* Responsive sidebar adjustments */
@media (max-width: 1199px) {
  div.sidebar {
    max-width: 240px;
    min-width: 220px;
  }
}

@media (max-width: 575px) {
  div.sidebar {
    border-radius: 8px;
    margin-bottom: 15px;
  }

  div.tag-search {
    padding: 12px 15px;
  }

  #tag-sidebar {
    max-height: 35vh;
  }

  #tag-sidebar li:not(:has(h6)) {
    padding: 8px 15px;
  }
}

/* Loading animation for tag section */
@keyframes tagLoad {
  0% { opacity: 0; transform: translateY(10px); }
  100% { opacity: 1; transform: translateY(0); }
}

#tag-sidebar li {
  animation: tagLoad 0.3s ease forwards;
}

/* Add stagger effect for tag items */
#tag-sidebar li:nth-child(even) {
  animation-delay: 0.05s;
}

#tag-sidebar li:nth-child(3n) {
  animation-delay: 0.1s;
}

/* Dark mode specific enhancements */
@media (prefers-color-scheme: dark) {
  div.sidebar {
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.6);
  }
}

/* High contrast mode support */
@media (prefers-contrast: high) {
  div.sidebar {
    border: 2px solid var(--accent-color);
  }

  .tag-count {
    background: var(--accent-color);
    color: var(--bg-color);
  }
}
  `;
  document.head.appendChild(style);

function setColumnCount(count) {
  document.documentElement.style.setProperty('--column-count', count);
}

function addColumnControlPanel() {
  // Avoid duplicate panel
  if (document.getElementById('columnSlider')) return;

  // Create the panel
  const controlPanel = document.createElement('div');
  controlPanel.innerHTML = `
    <div style="margin-bottom: 18px; background: #1a1a1a; padding: 10px; border-radius: 8px; border: 1px solid #2a2a2a; display: flex; align-items: center; gap: 10px;">
      <label style="color: #e0e0e0;">Columns:</label>
      <input type="range" id="columnSlider" min="1" max="8" value="4" style="width: 100px;">
      <span id="columnCount" style="color: #e0e0e0;">4</span>
    </div>
  `;

  // Insert above .image-list
  const imageList = document.querySelector('.image-list');
  if (imageList && imageList.parentNode) {
    imageList.parentNode.insertBefore(controlPanel, imageList);
  } else {
    // fallback: append to body if .image-list not found
    document.body.appendChild(controlPanel);
  }

  const slider = document.getElementById('columnSlider');
  const countDisplay = document.getElementById('columnCount');

  slider.addEventListener('input', function() {
    const count = this.value;
    countDisplay.textContent = count;
    setColumnCount(count);
    localStorage.setItem('galleryColumns', count);
  });

  // Load saved preference
  const savedColumns = localStorage.getItem('galleryColumns');
  if (savedColumns) {
    slider.value = savedColumns;
    countDisplay.textContent = savedColumns;
    setColumnCount(savedColumns);
  }
}

  const processedImages = new Set();
  const apiCache = new Map();

  function extractHashFromThumbnail(thumbnailUrl) {
    // Extract hash from: https://wimg.rule34.xxx/thumbnails/5046/thumbnail_fffef5df32fbff452a0be69302ca8e8b.jpg?5747620
    const match = thumbnailUrl.match(/thumbnail_([a-f0-9]+)/);
    return match ? match[1] : null;
  }

  function extractDirectoryFromThumbnail(thumbnailUrl) {
    // Extract directory from: https://wimg.rule34.xxx/thumbnails/5046/thumbnail_...
    const match = thumbnailUrl.match(/thumbnails\/(\d+)\//);
    return match ? match[1] : null;
  }

  function createJPGUrl(thumbnailUrl) {
    const hash = extractHashFromThumbnail(thumbnailUrl);
    const directory = extractDirectoryFromThumbnail(thumbnailUrl);

    if (!hash || !directory) return null;

    // Convert: thumbnails/5046/thumbnail_hash.jpg → samples/5046/sample_hash.jpg
    return `https://rule34.xxx/images/${directory}/${hash}.jpg`;
  }
  function createJPEGUrl(thumbnailUrl) {
    const hash = extractHashFromThumbnail(thumbnailUrl);
    const directory = extractDirectoryFromThumbnail(thumbnailUrl);

    if (!hash || !directory) return null;

    // Convert: thumbnails/5046/thumbnail_hash.jpg → samples/5046/sample_hash.jpg
    return `https://rule34.xxx/images/${directory}/${hash}.jpeg`;
  }
  function createPNGUrl(thumbnailUrl) {
    const hash = extractHashFromThumbnail(thumbnailUrl);
    const directory = extractDirectoryFromThumbnail(thumbnailUrl);

    if (!hash || !directory) return null;

    // Convert: thumbnails/5046/thumbnail_hash.jpg → samples/5046/sample_hash.jpg
    return `https://rule34.xxx/images/${directory}/${hash}.gif`;
  }

  function getCurrentPageTags() {
    // Extract tags from current URL: tags=rating%3Asafe+sort%3Ascore%3Adesc&pid=126
    const urlParams = new URLSearchParams(window.location.search);
    const tags = urlParams.get("tags") || "";

    // Clean up the tags - decode and keep only the filter tags, remove sort
    let cleanTags = decodeURIComponent(tags);
    // Remove sorting parameters but keep filter tags
    cleanTags = cleanTags.replace(/\s*sort:[^\s]+/g, "").trim();

    console.log(`Current page tags: "${cleanTags}"`);
    return cleanTags;
  }

  function extractIdFromThumbnail(thumbnailUrl) {
    // Extract ID from: https://wimg.rule34.xxx/thumbnails/5046/thumbnail_fffef5df32fbff452a0be69302ca8e8b.jpg?5747620
    const match = thumbnailUrl.match(/\?(\d+)$/);
    return match ? match[1] : null;
  }

  async function fallbackToAPI(thumbnailUrl, img) {
    const postId = extractIdFromThumbnail(thumbnailUrl);
    if (!postId) {
      console.log(`Could not extract post ID from ${thumbnailUrl}`);
      return;
    }

    // Check if we already have this specific post cached
    if (apiCache.has(postId)) {
      const cachedPost = apiCache.get(postId);
      replaceWithApiData(cachedPost, img);
      return;
    }

    // Make API call for this specific post ID
    const apiUrl = `https://api.rule34.xxx/index.php?page=dapi&s=post&q=index&json=1&limit=1000&id=${postId}`;

    console.log(`Making API call for post ${postId}: ${apiUrl}`);

    try {
      const response = await fetch(apiUrl);
      if (!response.ok) {
        console.log(`API request failed: ${response.status}`);
        return;
      }

      const data = await response.json();
      console.log(`API returned data for post ${postId}:`, data);

      // Handle response format - could be array or single object
      let post = null;
      if (Array.isArray(data) && data.length > 0) {
        post = data[0];
      } else if (data.id) {
        post = data;
      }

      if (post) {
        // Cache the post data
        apiCache.set(postId, post);

        // Replace the image
        replaceWithApiData(post, img);
      } else {
        console.log(`No post data found for ID ${postId}`);
      }
    } catch (error) {
      console.error(`API call failed for post ${postId}:`, error);
    }
  }

  function replaceWithApiData(post, img) {
    // Use sample_url if available, otherwise file_url
    let betterUrl;
    if (post.file_url.endsWith(".mp4") || post.file_url.endsWith(".webm")) {
      betterUrl = post.sample_url;
    } else {
      betterUrl = post.file_url;
    }

    if (betterUrl) {
      console.log(`Found API replacement for post ${post.id}: ${betterUrl}`);

      // Test the image before replacing
      const testImg = new Image();
      testImg.onload = function () {
        img.src = betterUrl;
        img.dataset.replacedViaApi = "true";
        img.dataset.replaced = "api";
        console.log(`✅ Successfully replaced post ${post.id} via API`);

        // Visual feedback - different color for API replacements
        img.style.transition = "box-shadow 0.3s ease";
        img.style.boxShadow = "0 0 8px #0099ff";
        setTimeout(() => {
          img.style.boxShadow = "";
        }, 800);
      };
      testImg.onerror = function () {
        console.log(
          `❌ API replacement failed to load for post ${post.id}: ${betterUrl}`
        );
      };
      testImg.src = betterUrl;
    } else {
      console.log(`No suitable URL found for post ${post.id} in API response`);
    }
  }

  async function replaceThumbnailWithSample(img) {
    const originalSrc = img.src;

    // Skip if already processed
    if (processedImages.has(originalSrc) || img.dataset.replaced) {
      return;
    }

    // Only process thumbnail images
    if (!originalSrc.includes("/thumbnails/")) {
      return;
    }

    processedImages.add(originalSrc);
    img.dataset.processing = "true";

    console.log(`Processing thumbnail: ${originalSrc}`);

    // Try all sample URL creators in order
    const urlCreators = [createJPGUrl, createJPEGUrl, createPNGUrl];

    let replaced = false;
    for (const createUrl of urlCreators) {
      const sampleUrl = createUrl(originalSrc);
      if (!sampleUrl) continue;

      console.log(`Trying sample URL: ${sampleUrl}`);

      // eslint-disable-next-line no-await-in-loop
      const success = await testImageUrl(sampleUrl);
      if (success) {
        img.src = sampleUrl;
        img.dataset.replaced = "sample";
        img.dataset.processing = "false";
        console.log(`✅ Successfully replaced with sample: ${sampleUrl}`);

        // Visual feedback
        img.style.transition = "box-shadow 0.3s ease";
        img.style.boxShadow = "0 0 8px #00ff00";
        setTimeout(() => {
          img.style.boxShadow = "";
        }, 800);

        replaced = true;
        break;
      }
    }

    if (!replaced) {
      fallbackToAPI(originalSrc, img);
    }
  }

  // Helper to test if an image URL loads
  function testImageUrl(url) {
    return new Promise((resolve) => {
      const testImg = new Image();
      testImg.onload = () => resolve(true);
      testImg.onerror = () => resolve(false);
      testImg.src = url;
    });
  }

  function processAllThumbnails() {
    const thumbnails = Array.from(
      document.querySelectorAll('img[src*="/thumbnails/"]')
    );
    console.log(`Found ${thumbnails.length} thumbnail images to process`);

    thumbnails.forEach((img, index) => {
      // Add small delay to avoid overwhelming the browser
      setTimeout(() => {
        replaceThumbnailWithSample(img);
      }, index * 50);
    });
  }

  function processVisibleThumbnails() {
    const thumbnails = Array.from(
      document.querySelectorAll('img[src*="/thumbnails/"]')
    ).filter((img) => {
      if (img.dataset.replaced || img.dataset.processing) return false;

      const rect = img.getBoundingClientRect();
      const windowHeight = window.innerHeight;
      return rect.top < windowHeight + 300 && rect.bottom > -300;
    });

    if (thumbnails.length > 0) {
      console.log(`Processing ${thumbnails.length} visible thumbnails`);
      thumbnails.forEach(replaceThumbnailWithSample);
    }
  }

  // Debug functions
  function showStats() {
    const total = document.querySelectorAll('img[src*="/thumbnails/"]').length;
    const sampleReplaced = document.querySelectorAll(
      'img[data-replaced="sample"]'
    ).length;
    const apiReplaced = document.querySelectorAll(
      'img[data-replaced="api"]'
    ).length;
    const processing = document.querySelectorAll(
      'img[data-processing="true"]'
    ).length;

    console.log(
      `Stats: ${sampleReplaced} sample, ${apiReplaced} API, ${processing} processing, ${total} total`
    );
    console.log(`API cache: ${apiCache.size} entries`);

    return {
      total,
      sampleReplaced,
      apiReplaced,
      processing,
      cached: apiCache.size,
    };
  }

  function clearCacheAndReprocess() {
    processedImages.clear();
    apiCache.clear();

    // Reset all images
    document
      .querySelectorAll("img[data-replaced], img[data-processing]")
      .forEach((img) => {
        delete img.dataset.replaced;
        delete img.dataset.processing;
        delete img.dataset.replacedViaApi;
      });

    console.log("Cache cleared, reprocessing...");
    setTimeout(processAllThumbnails, 100);
  }

  // Make functions globally available
  window.processAllThumbnails = processAllThumbnails;
  window.showStats = showStats;
  window.clearCacheAndReprocess = clearCacheAndReprocess;

  // Initialize
  function initialize() {
    console.log("Rule34 Simple Reskin initialized");

    // Process images after page loads
    setTimeout(processAllThumbnails, 500);
	document.head.appendChild(style);
	addColumnControlPanel();
  }

  // Start when ready
  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", initialize);
  } else {
    initialize();
  }

  // Process visible images on scroll
  let scrollTimeout;
  window.addEventListener("scroll", () => {
    clearTimeout(scrollTimeout);
    scrollTimeout = setTimeout(processVisibleThumbnails, 200);
  });

  // Process new images when they're dynamically added
  const observer = new MutationObserver((mutations) => {
    let hasNewThumbnails = false;
    mutations.forEach((mutation) => {
      mutation.addedNodes.forEach((node) => {
        if (node.nodeType === Node.ELEMENT_NODE) {
          if (node.tagName === "IMG" && node.src.includes("/thumbnails/")) {
            hasNewThumbnails = true;
          } else if (
            node.querySelector &&
            node.querySelector('img[src*="/thumbnails/"]')
          ) {
            hasNewThumbnails = true;
          }
        }
      });
    });

    if (hasNewThumbnails) {
      setTimeout(processVisibleThumbnails, 200);
    }
  });

// ===== COLLAPSIBLE SIDEBAR FUNCTIONALITY =====

function initializeCollapsibleSidebar() {
  const tagSidebar = document.getElementById('tag-sidebar');
  if (!tagSidebar) return;

  // Get saved collapsed states from localStorage
  const savedStates = JSON.parse(localStorage.getItem('sidebarCollapsedStates') || '{}');

  // Find all h6 headers and wrap subsequent tag items
  const headers = tagSidebar.querySelectorAll('h6');

  headers.forEach((header, index) => {
    const categoryName = header.textContent.toLowerCase().trim();

    // Create wrapper for tag items in this category
    const tagSection = document.createElement('div');
    tagSection.className = 'tag-section';
    tagSection.dataset.category = categoryName;

    // Find all tag items until the next h6 or end of list
    let currentElement = header.parentElement.nextElementSibling;
    const tagItems = [];

    while (currentElement && !currentElement.querySelector('h6')) {
      tagItems.push(currentElement);
      currentElement = currentElement.nextElementSibling;
    }

    // Move tag items into the wrapper
    tagItems.forEach(item => {
      tagSection.appendChild(item);
    });

    // Insert wrapper after header
    header.parentElement.parentNode.insertBefore(tagSection, header.parentElement.nextElementSibling);

    // Apply saved collapsed state
    if (savedStates[categoryName]) {
      header.classList.add('collapsed');
      tagSection.classList.add('collapsed');
    }

    // Add click handler
    header.addEventListener('click', function(e) {
      e.preventDefault();
      e.stopPropagation();
      toggleSection(header, tagSection, categoryName);
    });

    // Add keyboard support
    header.addEventListener('keydown', function(e) {
      if (e.key === 'Enter' || e.key === ' ') {
        e.preventDefault();
        toggleSection(header, tagSection, categoryName);
      }
    });

    // Make headers focusable
    header.setAttribute('tabindex', '0');
    header.setAttribute('role', 'button');
    header.setAttribute('aria-expanded', !savedStates[categoryName]);
    header.setAttribute('aria-controls', `tag-section-${categoryName}`);

    tagSection.setAttribute('id', `tag-section-${categoryName}`);
  });
}

function toggleSection(header, section, categoryName) {
  const isCollapsed = header.classList.contains('collapsed');

  // Update visual state
  header.classList.toggle('collapsed');
  header.setAttribute('aria-expanded', isCollapsed);

  if (isCollapsed) {
    // Expanding
    section.classList.remove('collapsed');
    section.style.maxHeight = section.scrollHeight + 'px';

    // Reset max-height after animation
    setTimeout(() => {
      if (!section.classList.contains('collapsed')) {
        section.style.maxHeight = '1000px';
      }
    }, 400);
  } else {
    // Collapsing
    section.style.maxHeight = section.scrollHeight + 'px';
    section.classList.add('collapsing');

    // Force reflow
    section.offsetHeight;

    section.style.maxHeight = '0px';

    setTimeout(() => {
      section.classList.add('collapsed');
      section.classList.remove('collapsing');
    }, 400);
  }

  // Save state to localStorage
  saveCollapsedStates();
}

function saveCollapsedStates() {
  const states = {};
  const headers = document.querySelectorAll('#tag-sidebar h6');

  headers.forEach(header => {
    const categoryName = header.textContent.toLowerCase().trim();
    states[categoryName] = header.classList.contains('collapsed');
  });

  localStorage.setItem('sidebarCollapsedStates', JSON.stringify(states));
}

// Utility functions for manual control
function expandAllSections() {
  const headers = document.querySelectorAll('#tag-sidebar h6.collapsed');
  headers.forEach(header => {
    const categoryName = header.textContent.toLowerCase().trim();
    const section = document.getElementById(`tag-section-${categoryName}`);
    if (section) {
      toggleSection(header, section, categoryName);
    }
  });
}

function collapseAllSections() {
  const headers = document.querySelectorAll('#tag-sidebar h6:not(.collapsed)');
  headers.forEach(header => {
    const categoryName = header.textContent.toLowerCase().trim();
    const section = document.getElementById(`tag-section-${categoryName}`);
    if (section) {
      toggleSection(header, section, categoryName);
    }
  });
}

// Reset all collapsed states
function resetSidebarStates() {
  localStorage.removeItem('sidebarCollapsedStates');
  location.reload();
}

// Make functions globally available for debugging
window.expandAllSections = expandAllSections;
window.collapseAllSections = collapseAllSections;
window.resetSidebarStates = resetSidebarStates;

// Initialize when DOM is ready
function initSidebarWhenReady() {
  if (document.getElementById('tag-sidebar')) {
    setTimeout(initializeCollapsibleSidebar, 100);
  } else {
    // Retry after a short delay if sidebar isn't ready yet
    setTimeout(initSidebarWhenReady, 500);
  }
}

// Call initialization
initSidebarWhenReady();

// Re-initialize if new content is loaded dynamically
const sidebarObserver = new MutationObserver((mutations) => {
  mutations.forEach((mutation) => {
    if (mutation.addedNodes.length > 0) {
      const addedSidebar = Array.from(mutation.addedNodes).find(node =>
        node.id === 'tag-sidebar' || (node.querySelector && node.querySelector('#tag-sidebar'))
      );
      if (addedSidebar) {
        setTimeout(initializeCollapsibleSidebar, 100);
      }
    }
  });
});

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