diff --git a/docs/superpowers/plans/2026-08-26-reddit-badge-tweaks.md b/docs/superpowers/plans/2026-08-26-reddit-badge-tweaks.md
new file mode 100644
index 0000000..806e048
--- /dev/null
+++ b/docs/superpowers/plans/2026-08-26-reddit-badge-tweaks.md
@@ -0,0 +1,987 @@
+# Firefox Reddit Tweaks Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Build a Firefox extension that relocates subreddit name and comment count on old Reddit into styled badge elements above each post title.
+
+**Architecture:** Pure CSS + vanilla JS content script (Manifest V2). No frameworks, no build step. Content scripts share global scope via the manifest's `content_scripts.js` array. CSS is injected at parse time to prevent FOUC. Settings stored in `browser.storage.local`.
+
+**Tech Stack:** Vanilla JavaScript, CSS, Firefox WebExtension API (Manifest V2)
+
+## Global Constraints
+
+- Target: old Reddit only (`*://old.reddit.com/*` and `*://www.reddit.com/*`)
+- Manifest V2 (not V3)
+- No ES module imports in content scripts — all JS files listed in manifest `content_scripts.js`, shared global scope
+- All injected DOM elements use the `rt-` class prefix
+- All CSS custom properties use the `--rt-` prefix
+- Storage: `browser.storage.local` (not sync)
+- Permissions: `storage`, `activeTab`
+- Post selector: `div.thing.link` (never bare `div.thing`)
+- Zero-comment edge case: when original text is `"comment"`, badge displays `"0 comments"`
+- Master toggle change triggers `location.reload()`, not teardown
+- Color settings changes apply live via `storage.onChanged` without reload
+- Dark mode detection: `.res-nightmode` on `
`, observed reactively via MutationObserver
+
+---
+
+## File Structure
+
+| File | Responsibility |
+|---|---|
+| `manifest.json` | Extension manifest — permissions, content scripts (CSS + JS), browser action |
+| `lib/settings.js` | Defaults, load/save helpers for `browser.storage.local`. Shared by content scripts and popup |
+| `content/styles/badges.css` | Badge appearance (light + dark), CSS custom properties with fallbacks, hiding rules for original elements (FOUC prevention) |
+| `content/tweaks/badges.js` | Badge tweak module — DOM manipulation, MutationObservers (content + dark mode), storage change listener |
+| `content/main.js` | Entry point — loads settings, checks `enabled`, calls badge tweak |
+| `popup/popup.html` | Settings popup HTML structure |
+| `popup/popup.css` | Settings popup styling |
+| `popup/popup.js` | Settings popup logic — binds controls to storage, handles per-subreddit overrides |
+| `icons/icon-48.png` | Toolbar icon (48×48) |
+| `icons/icon-96.png` | Toolbar icon (96×96) |
+
+---
+
+### Task 1: Extension Skeleton (Manifest + Settings + Icons)
+
+**Files:**
+- Create: `manifest.json`
+- Create: `lib/settings.js`
+- Create: `icons/icon-48.png`
+- Create: `icons/icon-96.png`
+
+**Interfaces:**
+- Consumes: nothing (first task)
+- Produces:
+ - `window.RedditTweaks.DEFAULTS` — object with all default setting values (see settings table in spec)
+ - `window.RedditTweaks.loadSettings()` — returns `Promise` with merged defaults + stored values
+ - `window.RedditTweaks.saveSettings(settings)` — returns `Promise`, writes to `browser.storage.local`
+ - `window.RedditTweaks.resetSettings()` — returns `Promise`, clears storage
+
+- [ ] **Step 1: Create `manifest.json`**
+
+```json
+{
+ "manifest_version": 2,
+ "name": "Reddit Tweaks",
+ "version": "1.0.0",
+ "description": "Reskins old Reddit post listings with badge-style subreddit and comment indicators.",
+ "permissions": ["storage", "activeTab"],
+ "icons": {
+ "48": "icons/icon-48.png",
+ "96": "icons/icon-96.png"
+ },
+ "browser_action": {
+ "default_icon": "icons/icon-48.png",
+ "default_popup": "popup/popup.html",
+ "default_title": "Reddit Tweaks Settings"
+ },
+ "content_scripts": [
+ {
+ "matches": ["*://old.reddit.com/*", "*://www.reddit.com/*"],
+ "css": ["content/styles/badges.css"],
+ "js": [
+ "lib/settings.js",
+ "content/tweaks/badges.js",
+ "content/main.js"
+ ],
+ "run_at": "document_idle"
+ }
+ ]
+}
+```
+
+- [ ] **Step 2: Create `lib/settings.js`**
+
+```js
+(function () {
+ "use strict";
+
+ const DEFAULTS = {
+ enabled: true,
+ darkMode: "auto",
+ "light.subredditBgColor": "transparent",
+ "light.subredditBorderColor": "#c84a20",
+ "light.subredditTextColor": "#c84a20",
+ "light.commentsBgColor": "#e0e0e0",
+ "light.commentsBorderColor": "#4a4a4a",
+ "light.commentsTextColor": "#4a4a4a",
+ "dark.subredditBgColor": "transparent",
+ "dark.subredditBorderColor": "#ff6b3d",
+ "dark.subredditTextColor": "#ff6b3d",
+ "dark.commentsBgColor": "#3a3a3a",
+ "dark.commentsBorderColor": "#888888",
+ "dark.commentsTextColor": "#cccccc",
+ subredditColors: {},
+ };
+
+ async function loadSettings() {
+ const stored = await browser.storage.local.get(null);
+ return Object.assign({}, DEFAULTS, stored);
+ }
+
+ async function saveSettings(settings) {
+ const toStore = {};
+ for (const key of Object.keys(settings)) {
+ if (key in DEFAULTS) {
+ toStore[key] = settings[key];
+ }
+ }
+ await browser.storage.local.set(toStore);
+ }
+
+ async function resetSettings() {
+ await browser.storage.local.clear();
+ }
+
+ window.RedditTweaks = window.RedditTweaks || {};
+ window.RedditTweaks.DEFAULTS = DEFAULTS;
+ window.RedditTweaks.loadSettings = loadSettings;
+ window.RedditTweaks.saveSettings = saveSettings;
+ window.RedditTweaks.resetSettings = resetSettings;
+})();
+```
+
+- [ ] **Step 3: Create placeholder icons**
+
+Generate simple 48×48 and 96×96 PNG icons. These can be single-color placeholder squares (e.g., orange `#c84a20` background with a white "RT" text or just a solid color). Use any available method — canvas script, ImageMagick, or a minimal valid PNG.
+
+Place at `icons/icon-48.png` and `icons/icon-96.png`.
+
+- [ ] **Step 4: Verify extension loads in Firefox**
+
+The extension needs the content script files to exist (even if empty) or Firefox will error. Create empty placeholder files:
+
+```bash
+mkdir -p content/styles content/tweaks
+touch content/styles/badges.css content/tweaks/badges.js content/main.js
+```
+
+Then verify the manifest is valid JSON:
+
+```bash
+python3 -c "import json; json.load(open('manifest.json')); print('valid')"
+```
+
+Expected: `valid`
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add manifest.json lib/settings.js icons/ content/styles/badges.css content/tweaks/badges.js content/main.js
+git commit -m "feat: extension skeleton with manifest, settings module, and placeholder icons"
+```
+
+---
+
+### Task 2: Badge Styling (CSS)
+
+**Files:**
+- Create: `content/styles/badges.css` (replace the empty placeholder)
+
+**Interfaces:**
+- Consumes: nothing (pure CSS, no JS dependencies)
+- Produces:
+ - CSS rules for `.reddit-tweaks-badges`, `.rt-badge`, `.rt-badge--subreddit`, `.rt-badge--comments`
+ - CSS rules hiding original elements: `.thing.link .tagline .subreddit` and `.thing.link .flat-list .comments` set to `display: none`
+ - Dark mode variant via `.rt-dark` class on `.reddit-tweaks-badges`
+ - CSS custom properties: `--rt-sub-bg`, `--rt-sub-border`, `--rt-sub-text`, `--rt-com-bg`, `--rt-com-border`, `--rt-com-text`
+
+- [ ] **Step 1: Write `content/styles/badges.css`**
+
+```css
+/* --- FOUC prevention: hide original elements immediately --- */
+.thing.link .tagline .subreddit,
+.thing.link .flat-list .comments {
+ display: none !important;
+}
+
+/* --- Badge container --- */
+.reddit-tweaks-badges {
+ margin-bottom: 4px;
+ line-height: 1.6;
+}
+
+/* --- Shared badge styles --- */
+.rt-badge {
+ display: inline-block;
+ border: 1px solid;
+ border-radius: 3px;
+ padding: 2px 6px;
+ font-size: 11px;
+ font-weight: bold;
+ text-decoration: none;
+ vertical-align: middle;
+ margin-right: 6px;
+ cursor: pointer;
+}
+
+.rt-badge:hover {
+ opacity: 0.8;
+}
+
+/* --- Light mode (default) --- */
+.rt-badge--subreddit {
+ background: var(--rt-sub-bg, transparent);
+ border-color: var(--rt-sub-border, #c84a20);
+ color: var(--rt-sub-text, #c84a20);
+}
+
+.rt-badge--comments {
+ background: var(--rt-com-bg, #e0e0e0);
+ border-color: var(--rt-com-border, #4a4a4a);
+ color: var(--rt-com-text, #4a4a4a);
+}
+
+/* --- Dark mode --- */
+.rt-dark .rt-badge--subreddit {
+ background: var(--rt-sub-bg, transparent);
+ border-color: var(--rt-sub-border, #ff6b3d);
+ color: var(--rt-sub-text, #ff6b3d);
+}
+
+.rt-dark .rt-badge--comments {
+ background: var(--rt-com-bg, #3a3a3a);
+ border-color: var(--rt-com-border, #888888);
+ color: var(--rt-com-text, #cccccc);
+}
+```
+
+- [ ] **Step 2: Verify CSS is valid**
+
+```bash
+python3 -c "
+css = open('content/styles/badges.css').read()
+print(f'CSS file: {len(css)} chars')
+assert '.thing.link' in css, 'missing post selector'
+assert '.rt-badge' in css, 'missing badge class'
+assert '.rt-dark' in css, 'missing dark mode'
+assert '--rt-sub-bg' in css, 'missing CSS custom properties'
+assert 'display: none' in css, 'missing FOUC hide rules'
+print('all checks passed')
+"
+```
+
+Expected: `all checks passed`
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add content/styles/badges.css
+git commit -m "feat: badge CSS with light/dark mode, custom properties, and FOUC prevention"
+```
+
+---
+
+### Task 3: Badge DOM Manipulation
+
+**Files:**
+- Create: `content/tweaks/badges.js` (replace the empty placeholder)
+- Create: `content/main.js` (replace the empty placeholder)
+
+**Interfaces:**
+- Consumes:
+ - `window.RedditTweaks.loadSettings()` → `Promise` (from `lib/settings.js`, Task 1)
+ - `window.RedditTweaks.DEFAULTS` → object (from `lib/settings.js`, Task 1)
+ - CSS classes from Task 2: `.reddit-tweaks-badges`, `.rt-badge`, `.rt-badge--subreddit`, `.rt-badge--comments`, `.rt-dark`
+ - CSS custom properties from Task 2: `--rt-sub-bg`, `--rt-sub-border`, `--rt-sub-text`, `--rt-com-bg`, `--rt-com-border`, `--rt-com-text`
+- Produces:
+ - `window.RedditTweaks.badgeTweak.apply(settings)` — processes all unprocessed `div.thing.link` posts, starts MutationObservers
+ - `window.RedditTweaks.badgeTweak.updateColors(settings)` — re-applies CSS custom properties to all badge containers
+
+- [ ] **Step 1: Write `content/tweaks/badges.js`**
+
+```js
+(function () {
+ "use strict";
+
+ let contentObserver = null;
+ let darkModeObserver = null;
+ let currentSettings = null;
+
+ function isDarkMode(settings) {
+ if (settings.darkMode === "light") return false;
+ if (settings.darkMode === "dark") return true;
+ return document.body.classList.contains("res-nightmode");
+ }
+
+ function getColorPrefix(settings) {
+ return isDarkMode(settings) ? "dark" : "light";
+ }
+
+ function applyColorsToContainer(container, settings, subredditName) {
+ const prefix = getColorPrefix(settings);
+
+ if (isDarkMode(settings)) {
+ container.classList.add("rt-dark");
+ } else {
+ container.classList.remove("rt-dark");
+ }
+
+ const overrides = subredditName && settings.subredditColors[subredditName];
+
+ container.style.setProperty("--rt-sub-bg", overrides?.bg || settings[prefix + ".subredditBgColor"]);
+ container.style.setProperty("--rt-sub-border", overrides?.border || settings[prefix + ".subredditBorderColor"]);
+ container.style.setProperty("--rt-sub-text", overrides?.text || settings[prefix + ".subredditTextColor"]);
+ container.style.setProperty("--rt-com-bg", settings[prefix + ".commentsBgColor"]);
+ container.style.setProperty("--rt-com-border", settings[prefix + ".commentsBorderColor"]);
+ container.style.setProperty("--rt-com-text", settings[prefix + ".commentsTextColor"]);
+ }
+
+ function normalizeCommentText(text) {
+ const trimmed = text.trim();
+ if (trimmed === "comment") return "0 comments";
+ return trimmed;
+ }
+
+ function processPost(post, settings) {
+ if (post.hasAttribute("data-rt-processed")) return;
+
+ const entry = post.querySelector(".entry");
+ if (!entry) return;
+
+ const container = document.createElement("div");
+ container.className = "reddit-tweaks-badges";
+
+ let subredditName = null;
+
+ const subLink = post.querySelector(".tagline .subreddit");
+ if (subLink) {
+ const badge = document.createElement("a");
+ badge.className = "rt-badge rt-badge--subreddit";
+ badge.href = subLink.href;
+ badge.textContent = subLink.textContent.trim();
+ subredditName = subLink.textContent.trim().replace(/^r\//, "");
+ container.appendChild(badge);
+ }
+
+ const commentsLink = post.querySelector(".flat-list .comments");
+ if (commentsLink) {
+ const badge = document.createElement("a");
+ badge.className = "rt-badge rt-badge--comments";
+ badge.href = commentsLink.href;
+ badge.textContent = normalizeCommentText(commentsLink.textContent);
+ container.appendChild(badge);
+ }
+
+ if (container.children.length === 0) return;
+
+ applyColorsToContainer(container, settings, subredditName);
+ entry.insertBefore(container, entry.firstChild);
+ post.setAttribute("data-rt-processed", "true");
+ }
+
+ function processPosts(settings) {
+ const posts = document.querySelectorAll("div.thing.link:not([data-rt-processed])");
+ posts.forEach(function (post) {
+ processPost(post, settings);
+ });
+ }
+
+ function getObserverTarget() {
+ return (
+ document.getElementById("siteTable") ||
+ document.querySelector('.content[role="main"]') ||
+ document.body
+ );
+ }
+
+ function startContentObserver(settings) {
+ if (contentObserver) contentObserver.disconnect();
+
+ const target = getObserverTarget();
+ contentObserver = new MutationObserver(function (mutations) {
+ let hasNewPosts = false;
+ for (const mutation of mutations) {
+ for (const node of mutation.addedNodes) {
+ if (node.nodeType === Node.ELEMENT_NODE) {
+ if (node.matches && node.matches("div.thing.link")) {
+ hasNewPosts = true;
+ } else if (node.querySelector && node.querySelector("div.thing.link")) {
+ hasNewPosts = true;
+ }
+ }
+ }
+ }
+ if (hasNewPosts) processPosts(currentSettings);
+ });
+
+ contentObserver.observe(target, { childList: true, subtree: true });
+ }
+
+ function startDarkModeObserver() {
+ if (darkModeObserver) darkModeObserver.disconnect();
+
+ darkModeObserver = new MutationObserver(function () {
+ if (currentSettings && currentSettings.darkMode === "auto") {
+ updateColors(currentSettings);
+ }
+ });
+
+ darkModeObserver.observe(document.body, {
+ attributes: true,
+ attributeFilter: ["class"],
+ });
+ }
+
+ function updateColors(settings) {
+ currentSettings = settings;
+ const containers = document.querySelectorAll(".reddit-tweaks-badges");
+ containers.forEach(function (container) {
+ const subBadge = container.querySelector(".rt-badge--subreddit");
+ const subredditName = subBadge
+ ? subBadge.textContent.trim().replace(/^r\//, "")
+ : null;
+ applyColorsToContainer(container, settings, subredditName);
+ });
+ }
+
+ function apply(settings) {
+ currentSettings = settings;
+ processPosts(settings);
+ startContentObserver(settings);
+ startDarkModeObserver();
+ }
+
+ window.RedditTweaks = window.RedditTweaks || {};
+ window.RedditTweaks.badgeTweak = {
+ apply: apply,
+ updateColors: updateColors,
+ };
+})();
+```
+
+- [ ] **Step 2: Write `content/main.js`**
+
+```js
+(function () {
+ "use strict";
+
+ async function init() {
+ const settings = await window.RedditTweaks.loadSettings();
+ if (!settings.enabled) return;
+
+ window.RedditTweaks.badgeTweak.apply(settings);
+
+ browser.storage.onChanged.addListener(function (changes) {
+ if (changes.enabled) {
+ location.reload();
+ return;
+ }
+
+ window.RedditTweaks.loadSettings().then(function (updated) {
+ window.RedditTweaks.badgeTweak.updateColors(updated);
+ });
+ });
+ }
+
+ init();
+})();
+```
+
+- [ ] **Step 3: Verify JS is valid**
+
+```bash
+node --check lib/settings.js && echo "settings.js: valid"
+node --check content/tweaks/badges.js && echo "badges.js: valid"
+node --check content/main.js && echo "main.js: valid"
+```
+
+Expected: all three report `valid`
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add content/tweaks/badges.js content/main.js
+git commit -m "feat: badge DOM manipulation with MutationObservers and dark mode detection"
+```
+
+---
+
+### Task 4: Settings Popup UI
+
+**Files:**
+- Create: `popup/popup.html`
+- Create: `popup/popup.css`
+- Create: `popup/popup.js`
+
+**Interfaces:**
+- Consumes:
+ - `window.RedditTweaks.DEFAULTS` → object (from `lib/settings.js`, Task 1)
+ - `window.RedditTweaks.loadSettings()` → `Promise` (from `lib/settings.js`, Task 1)
+ - `window.RedditTweaks.saveSettings(settings)` → `Promise` (from `lib/settings.js`, Task 1)
+ - `window.RedditTweaks.resetSettings()` → `Promise` (from `lib/settings.js`, Task 1)
+- Produces: complete settings popup (no API consumed by other tasks)
+
+- [ ] **Step 1: Create `popup/popup.html`**
+
+```html
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+Note: `transparent` is not a valid ` ` value. The popup.js code must handle this: when loading settings, map `"transparent"` to `#ffffff` (or a sensible stand-in) for the color picker; when saving, if the default for that field is `"transparent"`, provide a "Use transparent" checkbox next to the background color pickers. Alternatively, use a text input instead of a color picker for background fields. Choose the approach that keeps the UI simplest — a text input with a color preview swatch is recommended for fields that may be `"transparent"`.
+
+- [ ] **Step 2: Create `popup/popup.css`**
+
+```css
+* {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ width: 320px;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
+ font-size: 13px;
+ color: #1a1a1a;
+ background: #ffffff;
+}
+
+.popup-container {
+ padding: 16px;
+}
+
+h1 {
+ font-size: 16px;
+ margin-bottom: 12px;
+ padding-bottom: 8px;
+ border-bottom: 1px solid #e0e0e0;
+}
+
+h2 {
+ font-size: 13px;
+ font-weight: 600;
+ margin-bottom: 8px;
+}
+
+.section {
+ margin-bottom: 16px;
+ padding-bottom: 12px;
+ border-bottom: 1px solid #f0f0f0;
+}
+
+.section:last-child {
+ border-bottom: none;
+ margin-bottom: 0;
+}
+
+.toggle-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ cursor: pointer;
+}
+
+label {
+ display: block;
+ margin-bottom: 4px;
+}
+
+select {
+ width: 100%;
+ padding: 4px 8px;
+ border: 1px solid #ccc;
+ border-radius: 3px;
+ font-size: 13px;
+ margin-top: 4px;
+}
+
+fieldset {
+ border: 1px solid #e0e0e0;
+ border-radius: 4px;
+ padding: 8px 12px;
+ margin-bottom: 8px;
+}
+
+legend {
+ font-weight: 600;
+ font-size: 12px;
+ padding: 0 4px;
+}
+
+fieldset label {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 4px;
+ font-size: 12px;
+}
+
+fieldset input[type="color"] {
+ width: 40px;
+ height: 24px;
+ border: 1px solid #ccc;
+ border-radius: 3px;
+ padding: 1px;
+ cursor: pointer;
+}
+
+fieldset input[type="text"] {
+ width: 80px;
+ padding: 2px 6px;
+ border: 1px solid #ccc;
+ border-radius: 3px;
+ font-size: 12px;
+ font-family: monospace;
+}
+
+.btn-secondary {
+ background: #f0f0f0;
+ border: 1px solid #ccc;
+ border-radius: 3px;
+ padding: 4px 10px;
+ font-size: 12px;
+ cursor: pointer;
+ margin-top: 4px;
+}
+
+.btn-secondary:hover {
+ background: #e0e0e0;
+}
+
+.btn-danger {
+ background: #d9534f;
+ color: white;
+ border: 1px solid #c9302c;
+ border-radius: 3px;
+ padding: 6px 12px;
+ font-size: 12px;
+ cursor: pointer;
+ width: 100%;
+}
+
+.btn-danger:hover {
+ background: #c9302c;
+}
+
+.subreddit-entry {
+ display: flex;
+ gap: 4px;
+ align-items: center;
+ margin-bottom: 6px;
+ padding: 6px;
+ background: #fafafa;
+ border: 1px solid #e0e0e0;
+ border-radius: 4px;
+}
+
+.subreddit-entry input[type="text"] {
+ flex: 1;
+ min-width: 0;
+}
+
+.subreddit-entry .btn-remove {
+ background: none;
+ border: none;
+ color: #999;
+ cursor: pointer;
+ font-size: 16px;
+ padding: 0 4px;
+}
+
+.subreddit-entry .btn-remove:hover {
+ color: #d9534f;
+}
+```
+
+- [ ] **Step 3: Create `popup/popup.js`**
+
+```js
+(function () {
+ "use strict";
+
+ const COLOR_KEYS = [
+ "light.subredditBorderColor",
+ "light.subredditTextColor",
+ "light.commentsBgColor",
+ "light.commentsBorderColor",
+ "light.commentsTextColor",
+ "dark.subredditBorderColor",
+ "dark.subredditTextColor",
+ "dark.commentsBgColor",
+ "dark.commentsBorderColor",
+ "dark.commentsTextColor",
+ ];
+
+ const BG_KEYS = [
+ "light.subredditBgColor",
+ "dark.subredditBgColor",
+ ];
+
+ function colorToHex(val) {
+ if (!val || val === "transparent") return "#ffffff";
+ return val;
+ }
+
+ function populateControls(settings) {
+ document.getElementById("enabled").checked = settings.enabled;
+ document.getElementById("darkMode").value = settings.darkMode;
+
+ COLOR_KEYS.forEach(function (key) {
+ const el = document.getElementById(key);
+ if (el) el.value = colorToHex(settings[key]);
+ });
+
+ BG_KEYS.forEach(function (key) {
+ const el = document.getElementById(key);
+ if (el) {
+ el.value = settings[key] === "transparent" ? "" : settings[key];
+ el.placeholder = "transparent";
+ }
+ });
+
+ renderSubredditOverrides(settings.subredditColors || {});
+ }
+
+ function renderSubredditOverrides(overrides) {
+ const list = document.getElementById("subredditList");
+ list.innerHTML = "";
+
+ Object.entries(overrides).forEach(function ([name, colors]) {
+ addSubredditEntry(name, colors);
+ });
+ }
+
+ function addSubredditEntry(name, colors) {
+ const list = document.getElementById("subredditList");
+ const entry = document.createElement("div");
+ entry.className = "subreddit-entry";
+
+ const nameInput = document.createElement("input");
+ nameInput.type = "text";
+ nameInput.placeholder = "subreddit";
+ nameInput.value = name || "";
+
+ const bgInput = document.createElement("input");
+ bgInput.type = "color";
+ bgInput.value = colorToHex(colors?.bg);
+ bgInput.title = "Background";
+
+ const borderInput = document.createElement("input");
+ borderInput.type = "color";
+ borderInput.value = colorToHex(colors?.border);
+ borderInput.title = "Border";
+
+ const textInput = document.createElement("input");
+ textInput.type = "color";
+ textInput.value = colorToHex(colors?.text);
+ textInput.title = "Text";
+
+ const removeBtn = document.createElement("button");
+ removeBtn.className = "btn-remove";
+ removeBtn.textContent = "×";
+ removeBtn.addEventListener("click", function () {
+ entry.remove();
+ saveFromControls();
+ });
+
+ [nameInput, bgInput, borderInput, textInput].forEach(function (input) {
+ input.addEventListener("change", saveFromControls);
+ });
+
+ entry.appendChild(nameInput);
+ entry.appendChild(bgInput);
+ entry.appendChild(borderInput);
+ entry.appendChild(textInput);
+ entry.appendChild(removeBtn);
+ list.appendChild(entry);
+ }
+
+ function collectSubredditOverrides() {
+ const overrides = {};
+ const entries = document.querySelectorAll(".subreddit-entry");
+ entries.forEach(function (entry) {
+ const inputs = entry.querySelectorAll("input");
+ const name = inputs[0].value.trim().replace(/^r\//, "");
+ if (name) {
+ overrides[name] = {
+ bg: inputs[1].value,
+ border: inputs[2].value,
+ text: inputs[3].value,
+ };
+ }
+ });
+ return overrides;
+ }
+
+ function saveFromControls() {
+ const settings = {
+ enabled: document.getElementById("enabled").checked,
+ darkMode: document.getElementById("darkMode").value,
+ subredditColors: collectSubredditOverrides(),
+ };
+
+ COLOR_KEYS.forEach(function (key) {
+ const el = document.getElementById(key);
+ if (el) settings[key] = el.value;
+ });
+
+ BG_KEYS.forEach(function (key) {
+ const el = document.getElementById(key);
+ if (el) {
+ settings[key] = el.value.trim() || "transparent";
+ }
+ });
+
+ window.RedditTweaks.saveSettings(settings);
+ }
+
+ async function init() {
+ const settings = await window.RedditTweaks.loadSettings();
+ populateControls(settings);
+
+ document.getElementById("enabled").addEventListener("change", saveFromControls);
+ document.getElementById("darkMode").addEventListener("change", saveFromControls);
+
+ COLOR_KEYS.forEach(function (key) {
+ const el = document.getElementById(key);
+ if (el) el.addEventListener("input", saveFromControls);
+ });
+
+ BG_KEYS.forEach(function (key) {
+ const el = document.getElementById(key);
+ if (el) el.addEventListener("input", saveFromControls);
+ });
+
+ document.getElementById("addSubreddit").addEventListener("click", function () {
+ addSubredditEntry("", {});
+ });
+
+ document.getElementById("resetLight").addEventListener("click", async function () {
+ const settings = await window.RedditTweaks.loadSettings();
+ const DEFAULTS = window.RedditTweaks.DEFAULTS;
+ Object.keys(DEFAULTS).forEach(function (key) {
+ if (key.startsWith("light.")) settings[key] = DEFAULTS[key];
+ });
+ await window.RedditTweaks.saveSettings(settings);
+ populateControls(settings);
+ });
+
+ document.getElementById("resetDark").addEventListener("click", async function () {
+ const settings = await window.RedditTweaks.loadSettings();
+ const DEFAULTS = window.RedditTweaks.DEFAULTS;
+ Object.keys(DEFAULTS).forEach(function (key) {
+ if (key.startsWith("dark.")) settings[key] = DEFAULTS[key];
+ });
+ await window.RedditTweaks.saveSettings(settings);
+ populateControls(settings);
+ });
+
+ document.getElementById("resetAll").addEventListener("click", async function () {
+ if (confirm("Reset all settings to defaults?")) {
+ await window.RedditTweaks.resetSettings();
+ const settings = await window.RedditTweaks.loadSettings();
+ populateControls(settings);
+ }
+ });
+ }
+
+ init();
+})();
+```
+
+- [ ] **Step 4: Verify HTML references are correct**
+
+```bash
+python3 -c "
+import os
+html = open('popup/popup.html').read()
+assert 'popup.css' in html, 'missing CSS reference'
+assert 'popup.js' in html, 'missing JS reference'
+assert '../lib/settings.js' in html, 'missing settings.js reference'
+print('all references present')
+
+for f in ['popup/popup.html', 'popup/popup.css', 'popup/popup.js']:
+ assert os.path.exists(f), f'missing {f}'
+print('all popup files exist')
+"
+```
+
+Expected: both checks pass
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add popup/
+git commit -m "feat: settings popup with color pickers, dark mode, and subreddit overrides"
+```
+
+---
+
+## Post-Implementation
+
+After all tasks are complete, verify the full extension by loading it as a temporary add-on in Firefox via `about:debugging` → "This Firefox" → "Load Temporary Add-on" → select `manifest.json`. Then run through the test checklist from the spec (items 1–15).