/* ============================================================
   SOVEREIGN ISOLATION RULE — APPLIES TO ALL FILES
   ============================================================
   Sovereign and Egg do not exist to the site.
   They are not referenced, called, or known by Kemi,
   app.js, or any other visible component.

   Thaddeus, Roxanne, Shamu, and Egg2 have been retired.
   The old Council of five no longer exists.

   Kemi enforces image policy visibly.
   Sovereign and Egg operate invisibly as shadow monitors.
   Sovereign makes all ban decisions.
   Egg files IP records and reports patterns.

   This rule is permanent across all revisions.
   ============================================================ */

/* ========================================
   DEADMENSOCIETY.NET — STYLES
   Dark literary / manuscript aesthetic
   ========================================

   REVISION LOG
   ============================================================
   Rev 1 — URL image submission fix:
     No style changes required.

   Rev 2 — Profile picture display fix:
     No style changes required. Fix is in app.js _applyPicture().

   Rev 3 — Tier permission expansion:
     No style changes required. canDo() updated in app.js.

   Rev 4 — Merchant approval gate:
     .merchant-pending-btn added below for pending state display
     in the profile page banner actions.

   Rev 5 — Tier card/sidebar descriptions updated in index.html.

   BATCH 21 ERROR CORRECTIONS (FG Opus Claude):
     Session Warning Overlay comment corrected from
     "30 min / 60 min" to "60 min / 120 min" to match
     actual SESSION.WARNING_MS and SESSION.TIMEOUT_MS values.
   ============================================================ */

:root {
  --ink: #0d0b09;
  --parchment: #f5eedf;
  --parchment-dark: #e8dcc8;
  --sepia: #c9a96e;
  --sepia-dark: #a07840;
  --sepia-light: #e8c88a;
  --crimson: #8b1a1a;
  --crimson-light: #c0392b;
  --ash: #2a2620;
  --ash-dark: #1e1b17;
  --ash-mid: #3d3830;
  --ash-light: #5a5248;
  --gold: #d4a017;
  --text-muted: #7a6e5f;
  --border: rgba(201,169,110,0.25);
  --border-strong: rgba(201,169,110,0.5);
  --shadow: 0 4px 24px rgba(0,0,0,0.18);
  --shadow-heavy: 0 8px 48px rgba(0,0,0,0.32);
  --radius: 4px;
  --radius-lg: 8px;
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html { scroll-behavior: smooth; }

body {
  font-family: 'Crimson Pro', Georgia, serif;
  background: var(--ink);
  color: var(--parchment);
  line-height: 1.7;
  min-height: 100vh;
}

/* ===== TYPOGRAPHY ===== */
h1, h2, h3 { font-family: 'Cinzel', serif; letter-spacing: 0.04em; }
em { font-family: 'Cormorant Garamond', serif; font-style: italic; }
.section-title { font-size: 1.8rem; color: var(--sepia); margin-bottom: 0.5rem; }
.section-desc { color: var(--text-muted); font-size: 1.1rem; margin-bottom: 2rem; }

/* ===== LAYOUT ===== */
.site-main { max-width: 1280px; margin: 0 auto; padding: 0 2rem 4rem; }
.content-section { padding-top: 2rem; }
.hidden { display: none !important; }
.section-header { margin-bottom: 2rem; }

/* ===== HEADER ===== */
.site-header {
  position: sticky; top: 0; z-index: 100;
  background: rgba(13,11,9,0.95);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
  padding: 0 2rem;
}
.header-inner {
  max-width: 1280px; margin: 0 auto;
  display: flex; align-items: center; justify-content: space-between;
  height: 72px; gap: 2rem;
}
.header-brand { display: flex; align-items: center; gap: 1rem; }
.brand-sigil {
  font-size: 1.8rem; color: var(--sepia);
  animation: pulseGlow 4s ease-in-out infinite;
}
.brand-sigil.large { font-size: 2.5rem; }
@keyframes pulseGlow {
  0%, 100% { text-shadow: 0 0 8px rgba(201,169,110,0.3); }
  50% { text-shadow: 0 0 20px rgba(201,169,110,0.7), 0 0 40px rgba(201,169,110,0.3); }
}
.brand-name { font-family: 'Cinzel', serif; color: var(--sepia); letter-spacing: 0.08em; }
.brand-full .brand-name { font-size: 1.1rem; }
.brand-sub { font-size: 0.7rem; color: var(--text-muted); letter-spacing: 0.15em; text-transform: uppercase; }
.brand-short { display: none; }
.brand-short .brand-name { font-size: 1.6rem; font-weight: 700; }
html.has-session .brand-full { display: none; }
html.has-session .brand-short { display: block; }
/* Batch 197 R3/R4 — About / App tab links beside the wordmark. */
.brand-tab-link {
  background: none;
  border: none;
  cursor: pointer;
  font-family: 'Cinzel', serif;
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 0.25rem 0.4rem;
  transition: color 0.2s;
}
.brand-tab-link:hover { color: var(--sepia); }
.about-body {
  max-width: 760px;
  margin: 0 auto;
  padding: 1rem 0 3rem;
}
.about-body p {
  font-size: 1rem;
  line-height: 1.8;
  color: var(--parchment-dark);
  margin-bottom: 1.25rem;
  text-indent: 2em;
}
/* Batch 199 R5 — Autarch signature line: distinct, right-aligned, not indented. */
.about-body p.about-signature {
  text-indent: 0;
  text-align: right;
  font-family: 'Cinzel', serif;
  font-style: italic;
  color: var(--sepia);
  margin-top: 2rem;
}
/* ===== TROUBLE SHOOT — Batch 223 ===== */
.ts-body {
  max-width: 640px;
  margin: 0 auto;
  padding: 1rem 0 3rem;
}
.ts-option {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--ash-mid);
  margin-bottom: 0.85rem;
  overflow: hidden;
}
.ts-option-head {
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  cursor: pointer;
  padding: 1rem 1.2rem;
  font-family: 'Cinzel', serif;
  font-size: 0.95rem;
  letter-spacing: 0.04em;
  color: var(--parchment);
  display: flex;
  justify-content: space-between;
  align-items: center;
  transition: color 0.2s;
}
.ts-option-head:hover { color: var(--sepia); }
.ts-chev { color: var(--sepia); font-size: 1.1rem; }
.ts-panel {
  padding: 0 1.2rem 1.2rem;
}
.ts-disclaimer {
  font-size: 0.92rem;
  line-height: 1.6;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--ash-deep, #1a1a14);
  padding: 0.85rem 1rem;
  margin: 0 0 1.1rem;
}
.ts-optional {
  font-size: 0.78rem;
  color: var(--text-muted);
  font-style: italic;
}
.ts-submit {
  width: 100%;
  padding: 0.7rem;
  font-size: 0.85rem;
  margin-top: 0.4rem;
}
/* Batch 199 R1 — Brand wordmark returns to the landing page like a tab. */
/* Batch 200 (Finding 4) — Hover target changed --sepia -> --sepia-light: the brand
   name rests at --sepia, so the prior hover-to-sepia was a no-op. --sepia-light is a
   visibly brighter on-theme highlight, giving real hover feedback. */
.brand-home-link { cursor: pointer; transition: color 0.2s; }
.brand-home-link:hover .brand-name { color: var(--sepia-light); }
.main-nav { display: flex; gap: 0.5rem; }
.nav-link {
  background: none; border: none; cursor: pointer;
  font-family: 'Cinzel', serif; font-size: 0.8rem; letter-spacing: 0.1em;
  color: var(--text-muted); padding: 0.5rem 0.8rem;
  text-transform: uppercase; transition: color 0.2s;
  border-bottom: 2px solid transparent;
}
.nav-link:hover, .nav-link.active { color: var(--sepia); border-bottom-color: var(--sepia); }
.header-actions { display: flex; align-items: center; gap: 0.75rem; }

/* ===== BUTTONS ===== */
.btn-primary {
  background: linear-gradient(135deg, var(--sepia-dark), var(--sepia));
  color: var(--ink); border: none; cursor: pointer;
  font-family: 'Cinzel', serif; font-size: 0.8rem; letter-spacing: 0.08em;
  padding: 0.6rem 1.4rem; border-radius: var(--radius);
  text-transform: uppercase; font-weight: 600;
  transition: all 0.2s; white-space: nowrap;
}
.btn-primary:hover { background: linear-gradient(135deg, var(--sepia), var(--sepia-light)); transform: translateY(-1px); box-shadow: 0 4px 16px rgba(201,169,110,0.3); }
.btn-primary.large { font-size: 0.9rem; padding: 0.8rem 2rem; }
.btn-primary.full-width { width: 100%; }
.btn-ghost {
  background: transparent; cursor: pointer;
  font-family: 'Cinzel', serif; font-size: 0.8rem; letter-spacing: 0.08em;
  color: var(--sepia); padding: 0.6rem 1.2rem;
  border: 1px solid var(--border-strong); border-radius: var(--radius);
  text-transform: uppercase; transition: all 0.2s; white-space: nowrap;
}
.btn-ghost:hover { background: rgba(201,169,110,0.1); border-color: var(--sepia); }
.btn-ghost.large { font-size: 0.9rem; padding: 0.8rem 1.8rem; }
.btn-danger {
  background: var(--crimson); color: white; border: none; cursor: pointer;
  font-family: 'Cinzel', serif; font-size: 0.75rem; letter-spacing: 0.05em;
  padding: 0.4rem 0.8rem; border-radius: var(--radius);
  text-transform: uppercase; transition: background 0.2s;
}
.btn-danger:hover { background: var(--crimson-light); }
.btn-sm { padding: 0.35rem 0.7rem; font-size: 0.72rem; }

/* ===== USER AVATAR ===== */
.user-avatar {
  width: 36px; height: 36px; border-radius: 50%;
  background: linear-gradient(135deg, var(--sepia-dark), var(--sepia));
  color: var(--ink); font-weight: 700; font-family: 'Cinzel', serif;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; font-size: 0.85rem;
  border: 2px solid var(--border-strong);
  transition: border-color 0.2s;
}
.user-avatar:hover { border-color: var(--sepia); }

/* ===== ANNOUNCEMENT BANNER ===== */
.announcement-banner {
  background: linear-gradient(90deg, var(--crimson), #6b0f0f);
  color: white; padding: 0.6rem 2rem;
  display: flex; justify-content: space-between; align-items: center;
  font-family: 'Cinzel', serif; font-size: 0.85rem; letter-spacing: 0.05em;
}
.announcement-banner button { background: none; border: none; color: white; cursor: pointer; font-size: 1rem; }

/* ===== HERO ===== */
.hero {
  position: relative; overflow: hidden;
  min-height: 580px; display: flex; align-items: center;
  margin: 0 -2rem 3rem; padding: 4rem 4rem;
  background: linear-gradient(135deg, #1a0f0a 0%, #0d0b09 40%, #0a0f15 100%);
  border-bottom: 1px solid var(--border);
}
.hero-texture {
  position: absolute; inset: 0;
  background-image:
    radial-gradient(circle at 20% 50%, rgba(201,169,110,0.06) 0%, transparent 50%),
    radial-gradient(circle at 80% 20%, rgba(139,26,26,0.08) 0%, transparent 40%),
    url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23c9a96e' fill-opacity='0.02'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
}
.hero-content { position: relative; z-index: 2; max-width: 600px; }
.hero-eyebrow { font-family: 'Cinzel', serif; font-size: 0.75rem; letter-spacing: 0.25em; text-transform: uppercase; color: var(--sepia); margin-bottom: 1rem; }
.hero-title {
  font-family: 'Cinzel', serif; font-size: clamp(3rem, 6vw, 5rem);
  line-height: 1.05; color: var(--parchment); margin-bottom: 1.5rem;
  text-shadow: 0 0 60px rgba(201,169,110,0.2);
}
.hero-title em { color: var(--sepia); font-family: 'Cormorant Garamond', serif; font-size: 1.1em; }
.hero-body { font-size: 1.1rem; color: var(--parchment-dark); max-width: 600px; margin-bottom: 2rem; line-height: 1.7; }
.hero-art { position: absolute; right: 6%; top: 50%; transform: translateY(-50%); z-index: 1; }
.floating-rune {
  font-size: 5rem; color: var(--sepia); opacity: 0.12;
  position: absolute; animation: drift 8s ease-in-out infinite;
}
.floating-rune.two { font-size: 3rem; top: 80px; left: 60px; animation-delay: -3s; animation-duration: 11s; }
.floating-rune.three { font-size: 4rem; top: -40px; left: 100px; animation-delay: -6s; animation-duration: 9s; }
@keyframes drift { 0%, 100% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(-20px) rotate(5deg); } }

/* ===== LANDING AUTH — Batch 76 ===== */
.landing-auth-section { padding: 3rem 2rem; }
.landing-auth-inner {
  display: flex; gap: 0; max-width: 960px; margin: 0 auto;
  min-height: 520px; align-items: stretch;
}
.landing-auth-panel {
  flex: 1; padding: 2rem 2.5rem; display: flex; flex-direction: column;
}
.landing-auth-heading {
  font-family: 'Cinzel', serif; font-size: 1.4rem; color: var(--parchment);
  letter-spacing: 0.06em; margin-bottom: 1.5rem;
}
.landing-auth-divider {
  width: 1px; background: linear-gradient(180deg, transparent, var(--sepia), transparent);
  flex-shrink: 0;
}
@media (max-width: 768px) {
  .landing-auth-inner { flex-direction: column; }
  .landing-auth-divider { width: 100%; height: 1px; background: linear-gradient(90deg, transparent, var(--sepia), transparent); }
  .landing-auth-panel { padding: 2rem 1rem; }
}

/* ===== AUTHOR CONTROLS ===== */
/* Batch 211 — .author-controls rule retired. Its only consumers (#authorControls,
   #loreKeeperControls) were empty permanently-hidden divs, removed from index.html
   this batch along with their inert updateUI toggles. Selector kept here as a comment
   for provenance; no element carries the class any longer.
   .author-controls { display: flex; gap: 1rem; margin-bottom: 2rem; flex-wrap: wrap; } */

/* ===== FEED LAYOUT ===== */
.feed-layout { display: grid; grid-template-columns: 1fr 320px; gap: 2rem; }
/* Batch 210 Item 2 — the 1fr feed-main track has implicit min-width:auto, so wide
   children (YouTube embeds, full-bleed post images) force the track wider than its
   intended share. The centered .feed-header then centers across that over-wide track
   and lands left of the posts. min-width:0 lets the track hold its computed width so
   the header centers over the actual feed column, aligned with the posts beneath. */
.feed-main { min-width: 0; }
@media (max-width: 1024px) { .feed-layout { grid-template-columns: 1fr; } .feed-sidebar { position: static; max-height: none; overflow-y: visible; } }
/* Batch 203 R4 — Community feed header centered + stacked: the "Community Feed"
   label sits centered at the top of the main feed column, the filter chips
   directly beneath it (also centered). The Load New Posts control already sits
   centered below this header, then the posts. Layout-only change. */
.feed-header { display: flex; flex-direction: column; align-items: center; text-align: center; margin-bottom: 1.5rem; gap: 1rem; }
.feed-header .section-title { margin-bottom: 0; }
.feed-filters { display: flex; gap: 0.5rem; flex-wrap: wrap; justify-content: center; }
.filter-btn {
  background: none; border: 1px solid var(--border); cursor: pointer;
  color: var(--text-muted); font-family: 'Cinzel', serif; font-size: 0.72rem;
  padding: 0.35rem 0.8rem; border-radius: 50px; letter-spacing: 0.06em;
  text-transform: uppercase; transition: all 0.2s;
}
.filter-btn:hover, .filter-btn.active { background: rgba(201,169,110,0.12); color: var(--sepia); border-color: var(--border-strong); }

/* ===== POST CARDS ===== */
.post-card {
  background: var(--ash);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  margin-bottom: 1.25rem;
  transition: border-color 0.2s, transform 0.2s;
  cursor: pointer;
  position: relative;
  max-width: 620px;
}
.post-card:hover { border-color: var(--border-strong); }
.post-card-image-wrap { max-height: 600px; overflow: hidden; border-radius: var(--radius-lg) var(--radius-lg) 0 0; display: flex; align-items: center; }
.post-card-image { width: 100%; display: block; }
.post-card-body { padding: 1.5rem; }
.post-meta { display: flex; gap: 0.75rem; align-items: center; margin-bottom: 0.75rem; flex-wrap: wrap; }
.post-tag {
  font-family: 'Cinzel', serif; font-size: 0.65rem; letter-spacing: 0.12em;
  text-transform: uppercase; padding: 0.2rem 0.6rem; border-radius: 50px;
  border: 1px solid;
}
.post-tag.commentary { color: var(--sepia); border-color: var(--sepia); }
.post-tag.lore { color: #6baed6; border-color: #6baed6; }
.post-tag.forum { color: #74c476; border-color: #74c476; }
.post-tag.announcement { color: var(--gold); border-color: var(--gold); }
.post-date { font-size: 0.85rem; color: var(--text-muted); }
.post-author-chip { font-size: 0.85rem; color: var(--text-muted); }
.post-title { font-family: 'Cinzel', serif; font-size: 1.2rem; color: var(--parchment); margin-bottom: 0.75rem; line-height: 1.3; }
.post-excerpt { color: var(--parchment-dark); font-size: 1rem; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.post-actions { display: flex; gap: 1rem; padding: 1rem 1.5rem; border-top: 1px solid var(--border); }
.action-btn {
  background: none; border: none; cursor: pointer; color: var(--text-muted);
  font-size: 0.9rem; display: flex; align-items: center; gap: 0.35rem;
  transition: color 0.2s; font-family: 'Crimson Pro', serif;
}
.action-btn:hover { color: var(--sepia); }
.action-btn.liked { color: var(--crimson-light); }
.audio-player { padding: 0 1.5rem 1rem; }
.audio-player audio { width: 100%; height: 36px; filter: sepia(0.5); }

/* ===== SIDEBAR ===== */
.sidebar-card { background: var(--ash); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 1.25rem; margin-bottom: 1.25rem; }
.sidebar-title { font-family: 'Cinzel', serif; font-size: 0.85rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--sepia); margin-bottom: 1rem; padding-bottom: 0.5rem; border-bottom: 1px solid var(--border); }
.tier-summary { display: flex; flex-direction: column; gap: 0.75rem; }
.tier-row { display: flex; gap: 0.75rem; align-items: flex-start; cursor: pointer; padding: 0.5rem; border-radius: var(--radius); transition: background 0.2s; }
.tier-row:hover { background: rgba(201,169,110,0.06); }
.tier-dot { width: 10px; height: 10px; border-radius: 50%; margin-top: 0.4rem; flex-shrink: 0; }
.tier-dot.reader { background: #74c476; }
.tier-dot.scribe { background: #6baed6; }
.tier-dot.lore { background: var(--gold); }
.tier-row strong { font-family: 'Cinzel', serif; font-size: 0.8rem; letter-spacing: 0.04em; display: block; color: var(--parchment); }
.tier-row p { font-size: 0.85rem; color: var(--text-muted); margin: 0; }
.member-chip { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.6rem; font-size: 0.9rem; }
.member-avatar-sm { width: 28px; height: 28px; border-radius: 50%; background: var(--ash-mid); display: flex; align-items: center; justify-content: center; font-size: 0.7rem; font-family: 'Cinzel', serif; color: var(--sepia); border: 1px solid var(--border); }
.member-name { color: var(--parchment-dark); }

/* Batch 135 Rev 3 — Sticky community sidebar with independent scroll */
.feed-sidebar {
  position: sticky; top: 5rem; align-self: start;
  max-height: calc(100vh - 5.5rem); overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: var(--border) transparent;
}
.feed-sidebar::-webkit-scrollbar { width: 6px; }
.feed-sidebar::-webkit-scrollbar-track { background: transparent; }
.feed-sidebar::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
#activeMemberList::-webkit-scrollbar { width: 5px; }
#activeMemberList::-webkit-scrollbar-track { background: transparent; }
#activeMemberList::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }

/* Batch 70 — Featured Book + Book Grid + Book Filters CSS removed (books pipeline dead). */

/* ===== MODALS ===== */
.modal-overlay {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(0,0,0,0.7); backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center;
  padding: 1rem; animation: fadeIn 0.2s ease;
  overflow: hidden;
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.modal-box {
  background: var(--ash); border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg); padding: 2rem;
  width: 100%; max-width: 480px; max-height: 90vh; overflow-y: auto;
  overscroll-behavior: contain;
  position: relative; animation: slideUp 0.25s ease;
  box-shadow: var(--shadow-heavy);
}
.modal-box.wide { max-width: 720px; }
@keyframes slideUp { from { transform: translateY(24px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
.modal-close {
  position: absolute; top: 1rem; right: 1rem;
  background: none; border: none; color: var(--text-muted); cursor: pointer;
  font-size: 1rem; transition: color 0.2s;
}
.modal-close:hover { color: var(--parchment); }
.modal-title { font-family: 'Cinzel', serif; font-size: 1.4rem; color: var(--sepia); margin-bottom: 1.5rem; }

/* ===== FORMS ===== */
.form-group { margin-bottom: 1.25rem; }
.form-group label { display: block; font-family: 'Cinzel', serif; font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted); margin-bottom: 0.4rem; }
.form-group input, .form-group textarea, .form-group select {
  width: 100%; background: var(--ash-mid); border: 1px solid var(--border);
  color: var(--parchment); padding: 0.65rem 0.9rem; border-radius: var(--radius);
  font-family: 'Crimson Pro', serif; font-size: 1rem; outline: none;
  transition: border-color 0.2s;
}
.form-group input:focus, .form-group textarea:focus, .form-group select:focus { border-color: var(--sepia); }
.form-group input::placeholder, .form-group textarea::placeholder { color: var(--text-muted); }
.form-group select option { background: var(--ash-mid); }
.form-note { font-size: 0.82rem; color: var(--text-muted); margin-top: 0.75rem; line-height: 1.5; }

/* ===== AUTH TABS ===== */
.auth-tabs { display: flex; gap: 0; margin-bottom: 1.5rem; border-bottom: 1px solid var(--border); }
.auth-tab {
  background: none; border: none; cursor: pointer;
  font-family: 'Cinzel', serif; font-size: 0.85rem; letter-spacing: 0.08em;
  color: var(--text-muted); padding: 0.6rem 1.5rem;
  text-transform: uppercase; border-bottom: 2px solid transparent;
  margin-bottom: -1px; transition: all 0.2s;
}
.auth-tab.active { color: var(--sepia); border-bottom-color: var(--sepia); }

/* ===== POST MODAL ===== */
#postModalContent .post-full-title { font-family: 'Cinzel', serif; font-size: 1.6rem; color: var(--parchment); margin: 1rem 0; line-height: 1.3; }
#postModalContent .post-full-body { font-size: 1.1rem; line-height: 1.8; color: var(--parchment-dark); margin-bottom: 1.5rem; white-space: pre-wrap; }
#postModalContent .post-full-img { width: 100%; border-radius: var(--radius); margin-bottom: 1.5rem; }
.comments-section { border-top: 1px solid var(--border); padding-top: 1.5rem; }
.comments-title { font-family: 'Cinzel', serif; font-size: 1rem; color: var(--sepia); margin-bottom: 1rem; }
.comment { padding: 0.75rem; border-left: 2px solid var(--border); margin-bottom: 0.75rem; }
.comment-author { font-family: 'Cinzel', serif; font-size: 0.78rem; color: var(--sepia); margin-bottom: 0.25rem; }
.comment-body { white-space: pre-wrap; word-wrap: break-word; }
.comment-input-area { display: flex; gap: 0.75rem; margin-top: 0.25rem; }
.comment-input-area input { flex: 1; background: var(--ash-mid); border: 1px solid var(--border); color: var(--parchment); padding: 0.6rem 0.9rem; border-radius: var(--radius); font-family: 'Crimson Pro', serif; font-size: 1rem; outline: none; }
.comment-input-area input:focus { border-color: var(--sepia); }

/* Batch 144 Rev 2 — Opaque sticky bars. No sticky section is see-through. */
.sticky-modal-bar {
  background: var(--ash) !important;
  border-top: 1px solid var(--border);
  /* Batch 208 Item 1 — bottom-edge seal. A sticky bottom:0 bar pins at its scroll
     container's CONTENT-box edge; when that container carries bottom padding the
     bar sits above the painted bottom and thread content scrolling in the padding
     strip bleeds through beneath the bar. padding-bottom alone (shipped prior) only
     pads the bar's content UP — it never extends the fill DOWN past the sticky
     anchor. The negative margin drops the bar's painted box by the same amount so
     its opaque --ash fill covers the strip; padding+margin cancel in layout, so the
     visual footprint is unchanged. Scroll-axis overflow on overflow-y:auto parents
     is scrollable, not clipped, so the fill paints on every surface (notif-detail
     panel, comment thread modal, gallery overlay, sidebar viewers). */
  padding-bottom: 2.5rem;
  margin-bottom: -2.5rem;
  z-index: 2;
  transform: translateZ(0);
}
.sticky-modal-header {
  background: var(--ash) !important;
  z-index: 2;
  transform: translateZ(0);
}

/* ===== LIGHTBOX ===== */
.lightbox-content { max-width: 90vw; max-height: 90vh; text-align: center; position: relative; }
.lightbox-content img { max-width: 100%; max-height: 85vh; border-radius: var(--radius); display: block; margin: 0 auto; }
.lightbox-content p { color: var(--parchment-dark); margin-top: 0.75rem; font-size: 0.95rem; }

/* ===== MESSAGES ===== */

/* ===== ADMIN PANEL ===== */
.admin-section { margin-bottom: 2rem; }
.admin-section h3 { font-family: 'Cinzel', serif; font-size: 1rem; color: var(--sepia); margin-bottom: 1rem; padding-bottom: 0.5rem; border-bottom: 1px solid var(--border); }
/* Batch 36 — Removed duplicate .admin-user-row definition (predated Batch 29). Active definition at line ~730. */
.stat-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 1rem; }
.stat-box { background: var(--ash-mid); border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem; text-align: center; }

/* ===== PROFILE ===== */
.profile-avatar { width: 60px; height: 60px; border-radius: 50%; background: linear-gradient(135deg, var(--sepia-dark), var(--sepia)); display: flex; align-items: center; justify-content: center; font-family: 'Cinzel', serif; font-size: 1.4rem; color: var(--ink); border: 2px solid var(--border-strong); }
/* Batch 59 Fix — .profile-tier-badge removed: zero renderers after Batch 59 Rev 8. */
.profile-stats { display: flex; gap: 1.5rem; margin-bottom: 1.5rem; }
.profile-stat { text-align: center; }

/* ===== FORUM ===== */
.forum-card {
  background: var(--ash); border: 1px solid var(--border); border-radius: var(--radius-lg);
  margin-bottom: 1rem; padding: 1.25rem; cursor: pointer;
  transition: border-color 0.2s, transform 0.2s;
}
.forum-card:hover { border-color: var(--border-strong); }
.forum-card-lore {
  background: rgba(180,140,20,0.15);
  border-color: rgba(180,140,20,0.4);
}
.forum-card-lore:hover { border-color: rgba(180,140,20,0.7); }

/* Batch 167 — Forum kind-filter tab bar: sticky + fully opaque (Rule 2.28).
   At scroll-top it sits in its natural position below the section header; as the
   page scrolls it pins flush beneath the sticky site-header (.header-inner is
   72px tall; the header sits at z-index:100 and covers the seam). Requirements
   per Rule 2.28's accumulated clarifications:
     - Opaque solid background matching the page surface (--ink, the body bg),
       NOT a translucent or darker strip (Batch 146). No forum shows through it.
     - Own compositing layer via transform:translateZ(0) so scrolled content
       cannot bleed through even when the color is technically opaque (Batch 150).
     - The forum section carries position:relative;z-index:1 (below) so stacking
       resolves with the bar definitively above scrolled content (Batch 156).
   Content scrolling BEHIND the bar is intended (matches the site-header behavior);
   the bar itself and its edges are never see-through. */
.forum-kind-tabs {
  display: flex; gap: 0; flex-wrap: wrap;
  position: sticky; top: 72px; z-index: 2;
  background: var(--ink);
  border-bottom: 1px solid var(--border);
  padding-bottom: 0; margin-bottom: 1.5rem;
  /* Batch 229 F2 — same sticky-bleed class as Batch 228 R1: the transparent
     margin-bottom:1.5rem could let a card scroll through beneath the bar. Here the
     border-bottom already masks it visually, and folding the margin into padding
     would push the border 1.5rem off the tabs (a design change). Instead paint the
     margin band with an --ink box-shadow so nothing can show through, while keeping
     the border tight to the tabs and total spacing unchanged. */
  box-shadow: 0 1.5rem 0 var(--ink);
  transform: translateZ(0);
}
/* Stacking context for the forum view so scrolled cards resolve below the
   sticky tab bar's z-index (Batch 156). */
#forumSection { position: relative; z-index: 1; }

/* ===== SHOP ===== */
.shop-card { background: var(--ash); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; transition: transform 0.2s; }
.shop-card:hover { transform: translateY(-4px); }
.shop-img { width: 100%; aspect-ratio: 1; object-fit: cover; }
.shop-info { padding: 1rem; }
.shop-name { font-family: 'Cinzel', serif; font-size: 0.95rem; color: var(--parchment); margin-bottom: 0.25rem; }
.shop-price { color: var(--sepia); font-size: 1rem; margin-bottom: 0.75rem; }

/* ===== FOOTER ===== */
.site-footer { border-top: 1px solid var(--border); padding: 3rem 2rem 1rem; margin-top: 4rem; }
.footer-inner { max-width: 1280px; margin: 0 auto; display: flex; justify-content: space-between; align-items: center; gap: 2rem; flex-wrap: wrap; padding-bottom: 2rem; }
.footer-brand { display: flex; align-items: center; gap: 1rem; }
.footer-brand p { color: var(--text-muted); font-size: 0.95rem; }
.footer-links { display: flex; gap: 1rem; flex-wrap: wrap; }
.footer-links button { background: none; border: none; color: var(--text-muted); cursor: pointer; font-family: 'Cinzel', serif; font-size: 0.78rem; letter-spacing: 0.06em; text-transform: uppercase; transition: color 0.2s; }
.footer-links button:hover { color: var(--sepia); }
.footer-bottom { max-width: 1280px; margin: 0 auto; text-align: center; color: var(--text-muted); font-size: 0.82rem; border-top: 1px solid var(--border); padding-top: 1rem; }

/* ===== TOAST ===== */
.toast {
  position: fixed; bottom: 2rem; right: 2rem; z-index: 9999;
  background: var(--ash); border: 1px solid var(--sepia);
  border-radius: var(--radius-lg); padding: 0.85rem 1.5rem;
  font-family: 'Cinzel', serif; font-size: 0.85rem; color: var(--parchment);
  box-shadow: var(--shadow); animation: slideIn 0.3s ease;
  max-width: 320px;
}
.toast.error { border-color: var(--crimson); }
@keyframes slideIn { from { transform: translateX(100px); opacity: 0; } to { transform: translateX(0); opacity: 1; } }

/* Batch 187 R5 — center-screen toast (e.g. "Duplicates Cleared"). Lands in the
   middle of the viewport, distinct from the corner .toast. Non-interactive. */
.toast-center {
  position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
  z-index: 10001; pointer-events: none;
  background: var(--ash); border: 1px solid var(--sepia);
  border-radius: var(--radius-lg); padding: 1.1rem 2.2rem;
  font-family: 'Cinzel', serif; font-size: 1.05rem; letter-spacing: 0.04em;
  color: var(--parchment); box-shadow: var(--shadow);
  animation: centerToastIn 0.25s ease;
}
@keyframes centerToastIn { from { transform: translate(-50%, -50%) scale(0.92); opacity: 0; } to { transform: translate(-50%, -50%) scale(1); opacity: 1; } }

/* Batch 195 R10 — verdict-confirmation toast. Centered like .toast-center but at
   z-index 10002 so it sits ABOVE the dossier/report modals (10000) and their
   overlays (10001) — previously verdict toasts used the corner .toast (9999) and
   were hidden behind the open report/dossier window. Sepia by default; the
   .guilty modifier tints crimson and .acquit tints green so the admin reads the
   outcome at a glance. Non-interactive. */
.toast-verdict {
  position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
  z-index: 10002; pointer-events: none;
  background: var(--ash); border: 1px solid var(--sepia);
  border-radius: var(--radius-lg); padding: 1.1rem 2.2rem;
  font-family: 'Cinzel', serif; font-size: 1.05rem; letter-spacing: 0.04em;
  color: var(--parchment); box-shadow: var(--shadow);
  animation: centerToastIn 0.25s ease; text-align: center; max-width: 80vw;
}
.toast-verdict.guilty { border-color: var(--crimson); color: #e06060; }
.toast-verdict.acquit { border-color: #60b870; color: #60b870; }

/* Batch 218 — signup-rejection variant of the center toast (Autarch directive:
   signup validation rejections land in the middle of the page). */
.toast-center.error { border-color: var(--crimson); color: #e06060; }

/* Batch 218 — STICKY CENTER TOAST (Autarch directive). Persists until the user
   clicks the X — no auto-dismiss. Used for the post-payment access-code
   instruction. Interactive (pointer-events on, unlike .toast-center); z-index
   10003 sits above the verdict toast (10002) and every modal/overlay. */
.toast-sticky {
  position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
  z-index: 10003;
  background: var(--ash); border: 1px solid var(--sepia);
  border-radius: var(--radius-lg); padding: 1.25rem 2.6rem 1.25rem 1.6rem;
  font-family: 'Cinzel', serif; font-size: 1rem; letter-spacing: 0.04em;
  color: var(--parchment); box-shadow: var(--shadow);
  animation: centerToastIn 0.25s ease; text-align: center;
  max-width: min(92vw, 520px); line-height: 1.6;
}
.toast-sticky.error { border-color: var(--crimson); color: #e06060; }
.toast-sticky-x {
  position: absolute; top: 4px; right: 8px;
  background: none; border: none; cursor: pointer;
  color: var(--text-muted); font-size: 1.05rem; line-height: 1;
  padding: 5px; font-family: inherit;
}
.toast-sticky-x:hover { color: var(--parchment); }

/* Batch 218 — GOLD SPINNER (Autarch directive). The spinning circle of gold shown
   while the membership payment is confirmed and processed at registration. */
.gold-spinner {
  width: 46px; height: 46px; margin: 0 auto;
  border: 4px solid var(--ash-mid);
  border-top-color: var(--gold);
  border-radius: 50%;
  animation: dmsGoldSpin 0.9s linear infinite;
}
@keyframes dmsGoldSpin { to { transform: rotate(360deg); } }

/* ============================================================
   Batch 225 — CANCELLATION LIFECYCLE (Trouble Shoot admin pipeline)
   R2 .ts-context (landing textarea); R3 row controls; R4 LEA + gray gating;
   R9 cleared search; R10 in-window refresh button (discover-feed spin).
   ============================================================ */
/* R2 — landing-page context textarea, matching the ts form fields. */
.ts-context {
  width: 100%; box-sizing: border-box; resize: vertical; min-height: 84px;
  background: var(--ash-mid); color: var(--parchment);
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 0.6rem; font-family: Georgia, serif; font-size: 0.9rem;
}
.ts-context:focus { outline: none; border-color: var(--sepia); }

/* R10 — in-window refresh button. Same circular glyph as trending; spins on the
   discover-feed gold pattern while refreshing. */
.cancel-refresh-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; flex-shrink: 0;
  border: 1px solid var(--sepia); border-radius: 50%;
  background: transparent; color: var(--sepia);
  cursor: pointer; padding: 0; font-size: 1rem; line-height: 1;
  transition: color 0.15s, border-color 0.15s;
}
.cancel-refresh-btn:hover { color: var(--gold); border-color: var(--gold); }
.cancel-refresh-icon { display: inline-block; }
.cancel-refresh-btn.spinning .cancel-refresh-icon { animation: dmsGoldSpin 0.9s linear infinite; }

/* R3 — Confirm button: green-on-click per spec. */
.cancel-confirm-btn {
  padding: 0.35rem 0.7rem; border-radius: var(--radius); cursor: pointer;
  border: 1px solid var(--border-strong); background: var(--ash-mid);
  color: var(--parchment); font-family: Cinzel, serif; font-size: 0.72rem;
  letter-spacing: 0.04em; transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.cancel-confirm-btn:hover { border-color: #74c476; color: #74c476; }
.cancel-confirm-btn.sent {
  background: #2e7d32; border-color: #74c476; color: #eafbe7;
}

/* Finding A fix — Mark Reply / Reply Received control. Neutral until set, then
   green latch (like Confirm's sent state) to signal the gray has been lifted. */
.cancel-reply-btn {
  padding: 0.35rem 0.7rem; border-radius: var(--radius); cursor: pointer;
  border: 1px solid var(--border-strong); background: var(--ash-mid);
  color: var(--parchment); font-family: Cinzel, serif; font-size: 0.72rem;
  letter-spacing: 0.04em; transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.cancel-reply-btn:hover { border-color: #74c476; color: #74c476; }
.cancel-reply-btn.set {
  background: #2e7d32; border-color: #74c476; color: #eafbe7; cursor: default;
}

/* R3 — Cancel Account / Delete Request: stacked two-line text (Image 2 style).
   Gold-when-clickable; grayed (via inline opacity) until reply/LEA lifts it. */
.cancel-action-btn {
  padding: 0.3rem 0.6rem; border-radius: var(--radius); cursor: pointer;
  border: 1px solid var(--border-strong); background: var(--ash-mid);
  color: var(--parchment); font-family: Cinzel, serif; font-size: 0.68rem;
  line-height: 1.1; text-align: center; letter-spacing: 0.03em;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.cancel-action-btn:hover { background: var(--gold); border-color: var(--gold); color: #1a1a14; }

/* R4 — LEA checkbox label. */
.cancel-lea {
  display: inline-flex; align-items: center; gap: 0.3rem;
  font-family: Cinzel, serif; font-size: 0.68rem; letter-spacing: 0.04em;
  color: var(--text-muted); cursor: pointer; user-select: none;
}
.cancel-lea input { cursor: pointer; }

/* R3 — Context disclosure box. */
.cancel-context-box {
  margin-top: 0.5rem; padding: 0.6rem; border-radius: var(--radius);
  background: var(--ash-mid); border: 1px solid var(--border);
  color: var(--parchment-dark); font-size: 0.82rem; line-height: 1.5;
  white-space: pre-wrap; word-break: break-word;
}

/* R9 — Cleared window search bar (userLibrary-style). */
.cleared-search {
  width: 100%; box-sizing: border-box; margin-top: 0.5rem;
  background: var(--ash-mid); color: var(--parchment);
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 0.55rem 0.7rem; font-family: Georgia, serif; font-size: 0.9rem;
}
.cleared-search:focus { outline: none; border-color: var(--sepia); }

/* ===== RESPONSIVE ===== */
@media (max-width: 768px) {
  .main-nav { display: none; }
  .hero { padding: 3rem 2rem; min-height: 440px; }
  .hero-art { display: none; }
  .hero-title { font-size: 2.5rem; }
  .book-grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
  .stat-grid { grid-template-columns: repeat(2,1fr); }
  .modal-box { padding: 1.5rem; }
}

/* ===== SCROLLBAR ===== */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: var(--ink); }
::-webkit-scrollbar-thumb { background: var(--ash-light); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--sepia-dark); }

/* ===== LOCKED OVERLAY ===== */
.locked-overlay {
  background: rgba(13,11,9,0.85); border: 1px dashed var(--border-strong);
  border-radius: var(--radius-lg); padding: 2rem; text-align: center;
  margin: 1rem 0;
}
.locked-icon { font-size: 2rem; margin-bottom: 0.75rem; }
.locked-msg { font-family: 'Cinzel', serif; font-size: 0.9rem; color: var(--sepia); margin-bottom: 1rem; }

/* ===== EMPTY STATE ===== */
.empty-state { text-align: center; padding: 3rem; color: var(--text-muted); }
.empty-icon { font-size: 3rem; display: block; margin-bottom: 1rem; opacity: 0.4; }

/* ========================================
   FULL PROFILE PAGE
   ======================================== */

.profile-page-overlay {
  position: fixed; inset: 0; z-index: 200;
  background: var(--ink);
  overflow-y: auto;
  animation: fadeIn 0.25s ease;
}

.profile-page {
  max-width: 1100px; margin: 0 auto;
  padding: 2rem 2rem 4rem;
}

.profile-page-close {
  background: none; border: 1px solid var(--border);
  color: var(--text-muted); cursor: pointer;
  font-family: 'Cinzel', serif; font-size: 0.78rem;
  letter-spacing: 0.08em; text-transform: uppercase;
  padding: 0.5rem 1rem; border-radius: var(--radius);
  margin-bottom: 2rem; display: inline-block;
  transition: all 0.2s;
}
.profile-page-close:hover { color: var(--sepia); border-color: var(--sepia); }

/* Profile Banner */
.profile-banner {
  background: linear-gradient(135deg, #1a0f0a 0%, #0d0b09 50%, #0a0f15 100%);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: 2.5rem; margin-bottom: 2rem; position: relative; overflow: hidden;
}
.profile-banner::before {
  content: '✦';
  position: absolute; right: 2rem; top: 50%; transform: translateY(-50%);
  font-size: 6rem; color: var(--sepia); opacity: 0.06;
}
.profile-banner-top { display: flex; align-items: center; gap: 2rem; flex-wrap: wrap; }
.profile-avatar-large {
  width: 90px; height: 90px; border-radius: 50%;
  background: linear-gradient(135deg, var(--sepia-dark), var(--sepia));
  display: flex; align-items: center; justify-content: center;
  font-family: 'Cinzel', serif; font-size: 2.2rem; color: var(--ink);
  border: 3px solid var(--border-strong); flex-shrink: 0;
}
.profile-banner-info { flex: 1; }
.profile-page-username {
  font-family: 'Cinzel', serif; font-size: 2rem;
  margin-bottom: 0.4rem; line-height: 1.2;
}
.profile-page-meta { display: flex; gap: 1rem; flex-wrap: wrap; align-items: center; margin-top: 0.5rem; }
.profile-page-joined { font-size: 0.85rem; color: var(--text-muted); }
.profile-banner-actions { display: flex; gap: 0.75rem; margin-top: 1.5rem; flex-wrap: wrap; }

/* Profile Stats Bar */
.profile-stats-bar {
  display: flex; gap: 0; border: 1px solid var(--border);
  border-radius: var(--radius-lg); overflow: hidden; margin-bottom: 2rem;
}
.profile-stat-block {
  flex: 1; text-align: center; padding: 1.25rem 1rem;
  border-right: 1px solid var(--border); background: var(--ash);
}
.profile-stat-block:last-child { border-right: none; }
.profile-stat-block .num {
  font-family: 'Cinzel', serif; font-size: 1.6rem; color: var(--sepia);
  display: block; margin-bottom: 0.2rem;
}
.profile-stat-block .label { font-size: 0.78rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.08em; font-family: 'Cinzel', serif; }

.profile-card {
  background: var(--ash); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 1.5rem;
}
.profile-card-title {
  font-family: 'Cinzel', serif; font-size: 0.82rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--sepia); margin-bottom: 1rem;
  padding-bottom: 0.5rem; border-bottom: 1px solid var(--border);
}
.profile-card.full-width { grid-column: 1 / -1; }

/* Batch 37 Rev 2 — Profile two-column layout */
.profile-columns {
  display: flex; gap: 1.5rem; align-items: flex-start; margin-top: 1.5rem;
}
.profile-feed-column {
  flex: 3; min-width: 0;
  max-height: calc(100vh - 80px); overflow-y: auto;
  scrollbar-width: thin; scrollbar-color: var(--border) transparent;
}
.profile-feed-column::-webkit-scrollbar { width: 6px; }
.profile-feed-column::-webkit-scrollbar-track { background: transparent; }
.profile-feed-column::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
.profile-sidebar-column {
  flex: 2; min-width: 0;
  position: sticky; top: 1rem; max-height: calc(100vh - 80px); overflow-y: auto;
  scrollbar-width: thin; scrollbar-color: var(--border) transparent;
}
.profile-sidebar-column::-webkit-scrollbar { width: 6px; }
.profile-sidebar-column::-webkit-scrollbar-track { background: transparent; }
.profile-sidebar-column::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
.profile-sidebar-column .profile-card { margin-bottom: 1.5rem; }
@media (max-width: 700px) {
  .profile-columns { flex-direction: column; }
  .profile-feed-column { max-height: none; overflow-y: visible; }
  .profile-sidebar-column { position: static; max-height: none; overflow-y: visible; }
}

/* Profile feed cards */

/* Batch 37 Rev 4 — Sticky feed header + tab bar */
.profile-feed-header-sticky {
  position: sticky; top: 0; z-index: 5;
  background: var(--ink);
  /* Batch 228 R1 — sliver fix: was padding-bottom:0.6rem + margin-bottom:0.75rem.
     The transparent margin let the post image scrolling under the sticky bar bleed
     through the seam. Folded the margin into padding (0.6+0.75=1.35rem) so the
     opaque --ink background covers the full band down to the content. Same total
     spacing, no transparent gap. */
  padding-bottom: 1.35rem;
  margin-bottom: 0;
}
.profile-feed-tabs {
  display: flex; width: 100%;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.profile-feed-tab {
  flex: 1; padding: 0.5rem 0;
  font-family: 'Cinzel', serif; font-size: 0.72rem;
  letter-spacing: 0.06em; color: var(--text-muted);
  background: var(--ink); border: none; cursor: pointer;
  border-right: 1px solid var(--border);
  transition: background 0.15s, color 0.15s;
}
.profile-feed-tab:last-child { border-right: none; }
.profile-feed-tab:hover { color: var(--parchment); background: var(--ash-mid); }
.profile-feed-tab.active {
  color: var(--sepia); background: var(--ash-mid);
  font-weight: 700;
}

/* Scoring system placeholder */

/* About / Bio edit */
/* Followed forums list */
.followed-forum-item {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.65rem; border-radius: var(--radius);
  border: 1px solid var(--border); margin-bottom: 0.5rem;
  cursor: pointer; transition: all 0.2s;
}
.followed-forum-item:hover { border-color: var(--border-strong); background: rgba(201,169,110,0.04); }

/* Reason for joining */

/* Recent activity */
.activity-item { padding: 0.6rem 0; border-bottom: 1px solid var(--border); font-size: 0.92rem; color: var(--parchment-dark); }
.activity-item:last-child { border-bottom: none; }
.activity-item span { color: var(--sepia); }

/* ============================================================
   COUNCIL BUILD — NEW STYLES
   ============================================================ */

/* ARCHITECTURE — Thaddeus, Roxanne, Shamu retired. Kemi remains as agent with Edge Function (kemi-edge-function/index.ts). Egg and Sovereign are invisible shadow monitors in egg.js and sovereign.js. Session persistence via Supabase Auth JWT. get-ip-edge-function/index.ts serves Egg. */

/* Session Warning Overlay — triggers at 60 min inactivity, lock at 120 min */
.session-warning-overlay {
  position: fixed;
  top: 0; left: 0; width: 100%; height: 100%;
  background: rgba(13,11,9,0.85);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999;
  backdrop-filter: blur(4px);
}
.session-warning-overlay.hidden { display: none; }
.session-warning-box {
  background: var(--ash);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  padding: 2.5rem;
  max-width: 420px;
  width: 90%;
  text-align: center;
}
.session-warning-icon {
  font-size: 2.5rem;
  margin-bottom: 1rem;
}
.session-warning-title {
  font-family: Cinzel, serif;
  font-size: 1.1rem;
  color: var(--parchment);
  margin-bottom: 0.75rem;
  letter-spacing: 0.08em;
}
.session-warning-body {
  color: var(--parchment-dark);
  font-size: 0.95rem;
  line-height: 1.6;
  margin-bottom: 1.5rem;
}
.session-warning-body strong {
  color: var(--sepia);
  font-size: 1.2rem;
}

/* Profile Picture */
.user-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
}

/* Sovereign Panel */
#sovereignPanel {
  border-top: 2px solid var(--sepia);
  margin-top: 1.5rem;
  padding-top: 1.5rem;
}
#sovereignPanel.hidden { display: none; }

/* Admin user row enhancements */

/* Batch 50 — kemiStrikeBody/shamuStrikeBody/violationsReport CSS removed (orphaned modals removed) */

/* ============================================================
   SUPABASE BUILD — SITE LOADER
   ============================================================ */

.site-loader {
  position: fixed;
  top: 0; left: 0; width: 100%; height: 100%;
  background: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 99998;
}
.site-loader.hidden { display: none; }
.site-loader-inner {
  text-align: center;
}
.site-loader-icon {
  font-size: 2.5rem;
  color: var(--sepia);
  margin-bottom: 1rem;
  animation: pulse 1.5s ease-in-out infinite;
}
.site-loader-text {
  font-family: Cinzel, serif;
  font-size: 0.85rem;
  letter-spacing: 0.15em;
  color: var(--text-muted);
  text-transform: uppercase;
}
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50%       { opacity: 0.3; }
}

/* ============================================================
   USER LIBRARY — COLOR CODED STATUS ROWS
   ============================================================ */

.user-lib-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.6rem 0.9rem;
  border-radius: var(--radius);
  margin-bottom: 0.4rem;
  cursor: pointer;
  transition: opacity 0.15s;
  border: 1px solid transparent;
}
.user-lib-row:hover { opacity: 0.85; }

.user-lib-row.status-red    { background: rgba(160,30,30,0.18); border-color: rgba(160,30,30,0.4); }
.user-lib-row.status-green  { background: rgba(30,100,40,0.12); border-color: rgba(30,100,40,0.3); }

.user-lib-name {
  font-family: Cinzel, serif;
  font-size: 0.82rem;
  letter-spacing: 0.06em;
}
.user-lib-name.status-red    { color: #e06060; }
.user-lib-name.status-orange { color: #e09040; }
.user-lib-name.status-yellow { color: #c8b830; }
.user-lib-name.status-green  { color: #60b870; }

.user-lib-meta {
  font-size: 0.78rem;
  color: var(--text-muted);
}

.user-lib-badge {
  font-size: 0.72rem;
  font-family: Cinzel, serif;
  letter-spacing: 0.08em;
  padding: 0.15rem 0.5rem;
  border-radius: 2rem;
  margin-left: 0.5rem;
}
.user-lib-badge.red    { background: rgba(160,30,30,0.35); color: #e06060; }
.user-lib-badge.orange { background: rgba(180,90,20,0.35); color: #e09040; }
.user-lib-badge.yellow { background: rgba(160,140,20,0.35); color: #c8b830; }
.user-lib-badge.green  { background: rgba(30,100,40,0.25); color: #60b870; }
/* Batch 195 R2 — report badge drops to its own line below the strike badge and
   reads as a single pill ("1 Report" / "2 Reports"), instead of crowding the
   strike badge inline (which made the label wrap awkwardly). Block + no left
   margin so the pill aligns under the name; small top gap separates the rows. */
.user-lib-badge.report-line {
  display: inline-block;
  margin-left: 0;
  margin-top: 0.3rem;
}

/* ============================================================
   USER LIBRARY POPUP
   ============================================================ */

.user-popup-header {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-bottom: 1.5rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--border);
}
.user-popup-username {
  font-family: Cinzel, serif;
  font-size: 1.1rem;
  letter-spacing: 0.1em;
}
.user-popup-section {
  margin-bottom: 1.5rem;
}
.user-popup-section-title {
  font-family: Cinzel, serif;
  font-size: 0.75rem;
  letter-spacing: 0.15em;
  color: var(--sepia);
  margin-bottom: 0.75rem;
  padding-bottom: 0.35rem;
  border-bottom: 1px solid var(--border);
}
.violation-tag {
  font-size: 0.7rem;
  font-family: Cinzel, serif;
  letter-spacing: 0.06em;
  padding: 0.1rem 0.4rem;
  border-radius: 0.2rem;
  flex-shrink: 0;
}
.violation-tag.iv { background: rgba(160,30,30,0.3); color: #e06060; }
.violation-tag.kv { background: rgba(30,80,160,0.3); color: #6090e0; }

/* ============================================================
   VIOLATIONS SECTION
   ============================================================ */

.violation-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--border);
  font-size: 0.85rem;
}
.violation-date {
  font-size: 0.75rem;
  color: var(--text-muted);
}

/* ============================================================
   TIER BADGES
   ============================================================ */

.tier-badge {
  display: inline-block;
  font-family: Cinzel, serif;
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  padding: 0.1rem 0.35rem;
  border-radius: 0.2rem;
  margin-left: 0.3rem;
  vertical-align: middle;
  position: relative;
  top: -1px;
}

/* Batch 50 — .merchant-pending-btn removed (merchant subsystem wiped Batch 44, zero references) */

/* ============================================================
   SHOP FILTERS
   ============================================================ */

.shop-filter-label input { cursor: pointer; accent-color: var(--sepia); }

/* ============================================================
   METRA-EXPANSE
   ============================================================ */


/* ============================================================
   GALLERY DISCOVERY FEED
   ============================================================ */

.gallery-feed-card {
  background: var(--ash-mid);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1rem;
  margin-bottom: 1.25rem;
  max-width: 620px;
  position: relative;
}
.gallery-feed-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.75rem;
  position: relative;
}

/* ============================================================
   THREE-DOT POST MENU
   ============================================================ */

.post-menu-btn {
  cursor: pointer;
  color: var(--text-muted);
  font-size: 1.2rem;
  padding: 0 0.25rem;
  line-height: 1;
  user-select: none;
  margin-left: auto;
  /* Batch 174 — Card-expansion fix. Establish the kebab button as the positioning
     context for its sibling .post-menu (which is position:absolute). Previously the
     menu anchored to the nearest positioned ancestor — .post-card — and with .post-card
     no longer clipping (overflow:hidden removed in a prior pass), the tall menu painted
     past the short card's lower border, reading as the card "expanding." Anchoring to
     the button makes the menu overlay as a true floating dropdown. Shared by all 20
     .post-menu render sites via this class — retroactive. The opened-post path already
     wraps btn+menu in its own absolute container, so re-anchoring is consistent there. */
  position: relative;
}
.post-menu-btn:hover { color: var(--parchment); }
.post-menu {
  position: absolute;
  top: 1.5rem;
  right: 0;
  background: #1a1814;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  min-width: 160px;
  z-index: 9999;
  box-shadow: 0 8px 32px rgba(0,0,0,0.85);
  opacity: 1 !important;
  isolation: isolate;
}
.post-menu div {
  padding: 0.5rem 0.85rem;
  font-size: 0.85rem;
  color: var(--parchment-dark);
  cursor: pointer;
  transition: background 0.12s;
}
.post-menu div:hover { background: #2a2620; color: var(--parchment); }

/* ============================================================
   NOTIFICATIONS
   ============================================================ */


/* Batch 50 — Merchant Profile CSS removed (merchant subsystem wiped Batch 44, zero references) */

/* ============================================================
   PROFILE RECENT POSTS / COMMENTS
   ============================================================ */


.profile-card-wide {
  grid-column: 1 / -1;
}

/* ============================================================
   POST AUTHOR AVATAR (Rev 4)
   ============================================================ */
.post-author-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--sepia-dark), var(--sepia));
  color: var(--ink);
  font-weight: 700;
  font-family: 'Cinzel', serif;
  font-size: 0.7rem;
  border: 1px solid var(--border-strong);
  cursor: pointer;
  flex-shrink: 0;
  overflow: hidden;
  vertical-align: middle;
  margin-right: 0.4rem;
  transition: border-color 0.15s;
}
.post-author-avatar:hover { border-color: var(--sepia); }
.post-author-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }

/* ============================================================
   DM FLOATING PANEL (Rev 3)
   ============================================================ */
.dm-thread-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.65rem 1rem;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  transition: background 0.12s;
}
.dm-thread-row:hover { background: var(--ash-mid); }
.dm-thread-row.dm-unread { background: rgba(201,169,110,0.06); }
.dm-thread-avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--sepia-dark), var(--sepia));
  color: var(--ink);
  font-weight: 700;
  font-family: 'Cinzel', serif;
  font-size: 0.8rem;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  overflow: hidden;
  border: 1px solid var(--border-strong);
}
.dm-thread-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
.dm-thread-info { flex: 1; min-width: 0; }
.dm-thread-name {
  font-size: 0.85rem;
  color: var(--parchment);
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.dm-thread-preview {
  font-size: 0.75rem;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.dm-thread-time {
  font-size: 0.7rem;
  color: var(--text-muted);
  flex-shrink: 0;
}
.dm-thread-badge {
  background: #e06060;
  color: #fff;
  font-size: 0.62rem;
  font-family: 'Cinzel', serif;
  border-radius: 50%;
  width: 16px;
  height: 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
}

/* ============================================================
   REVISION LOG — BATCH 3 (FG Opus Claude)
   ============================================================
   Rev 1-6: No changes to styles.css.
   ============================================================ */


/* ============================================================
   REVISION LOG — BATCH 4 (FG Opus Claude)
   ============================================================
   Issues 1-5: No changes to styles.css.
   ============================================================ */


/* ============================================================
   REVISION LOG — BATCH 5 (FG Opus Claude)
   ============================================================
   Rev 1-4: No changes to styles.css.
   ============================================================ */


/* ============================================================
   REVISION LOG — BATCH 6 (FG Opus Claude)
   ============================================================
   Avatar Token System: No changes to styles.css.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 7 (FG Opus Claude)
   ============================================================
   Rev 1 — Reply system + Notification bell:
     Reply thread styling, comment action buttons, notification
     floating panel categories.
   Rev 2 — No changes to styles.css.
   Rev 3 — Comment thread modal header, metra review section.
   Rev 4 — Hover transforms removed from .post-card and .forum-card.
     .post-card-image changed to object-fit:contain, no fixed height.
   Rev 5 — .post-card overflow:hidden removed, position:relative added.
     .comment-body white-space:pre-wrap added. .forum-formatted-text,
     .forum-textarea styles added for paragraph formatting.
   Rev 6 — .forum-card-lore gold styling for lore category posts.
   ============================================================ */

/* ---- REPLY SYSTEM ---- */
.comment-replies-wrapper {
  margin-left: 1.5rem;
  border-left: 2px solid var(--border);
  padding-left: 0.75rem;
  margin-top: 0.5rem;
}
.comment-reply {
  padding: 0.5rem 0.5rem;
  margin-bottom: 0.4rem;
  border-bottom: 1px solid rgba(255,255,255,0.04);
}
.comment-reply:last-child { border-bottom: none; }
/* Batch 11 — Neon purple @tag highlights */
.tag-mention {
  color: #bf5fff;
  font-weight: 600;
  font-size: 0.85rem;
  cursor: pointer;
  text-shadow: 0 0 6px rgba(191,95,255,0.3);
}
.tag-mention:hover { text-decoration: underline; color: #d580ff; }
.comment-actions {
  display: flex;
  gap: 0.75rem;
  margin-top: 0.35rem;
  align-items: center;
}
.comment-action-btn {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 0.78rem;
  cursor: pointer;
  padding: 0;
  font-family: 'Crimson Pro', serif;
  transition: color 0.15s;
}
.comment-action-btn:hover { color: var(--parchment); }
.comment-action-btn.liked { color: #e06060; }
.reply-toggle-btn {
  background: none;
  border: none;
  color: var(--sepia);
  font-size: 0.78rem;
  font-family: 'Cinzel', serif;
  letter-spacing: 0.04em;
  cursor: pointer;
  padding: 0.25rem 0;
  margin-top: 0.25rem;
}
.reply-toggle-btn:hover { text-decoration: underline; }
.reply-input-area {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.5rem;
  align-items: center;
}
.reply-input-area input {
  flex: 1;
  background: var(--ash-mid);
  border: 1px solid var(--border);
  color: var(--parchment);
  padding: 0.4rem 0.65rem;
  border-radius: var(--radius);
  font-family: 'Crimson Pro', serif;
  font-size: 0.88rem;
  outline: none;
}
.reply-input-area input:focus { border-color: var(--sepia); }

/* ---- FORUM PARAGRAPH FORMATTING ---- */
.forum-formatted-text {
  white-space: pre-wrap;
  word-wrap: break-word;
  font-size: 0.95rem;
  color: var(--parchment-dark);
  line-height: 1.7;
}
.forum-textarea {
  width: 100%;
  background: var(--ash-mid);
  border: 1px solid var(--border);
  color: var(--parchment);
  padding: 0.5rem 0.65rem;
  border-radius: var(--radius);
  font-family: 'Crimson Pro', serif;
  font-size: 0.9rem;
  line-height: 1.6;
  resize: vertical;
  outline: none;
  min-height: 60px;
  tab-size: 4;
}
.forum-textarea:focus { border-color: var(--sepia); }

/* ---- FORUM — NETFLIX-STYLE TOPIC ROWS (Batch 11) ---- */
.forum-topic-row {
  margin-bottom: 2rem;
}
.forum-row-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 0.75rem;
}
.forum-row-title {
  font-family: 'Cinzel', serif;
  font-size: 1rem;
  letter-spacing: 0.06em;
  color: var(--parchment);
}
.forum-row-arrows {
  display: flex;
  gap: 0.4rem;
}
.forum-arrow-btn {
  background: var(--ash-mid);
  border: 1px solid var(--border);
  color: var(--sepia);
  width: 32px;
  height: 32px;
  border-radius: 50%;
  cursor: pointer;
  font-size: 0.9rem;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.15s, border-color 0.15s;
}
.forum-arrow-btn:hover {
  background: var(--ash-light);
  border-color: var(--sepia);
}
.forum-row-scroll {
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  scroll-behavior: smooth;
  padding-bottom: 0.5rem;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  scrollbar-color: var(--ash-light) transparent;
}
.forum-row-scroll::-webkit-scrollbar {
  height: 6px;
}
.forum-row-scroll::-webkit-scrollbar-track {
  background: transparent;
}
.forum-row-scroll::-webkit-scrollbar-thumb {
  background: var(--ash-light);
  border-radius: 3px;
}
.forum-square-card {
  flex: 0 0 220px;
  width: 220px;
  background: var(--ash-mid);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  cursor: pointer;
  transition: border-color 0.2s, box-shadow 0.2s;
  display: flex;
  flex-direction: column;
  position: relative;
}
.forum-square-card:hover {
  border-color: var(--sepia);
  box-shadow: 0 4px 20px rgba(0,0,0,0.3);
}
.forum-square-card-lore {
  border-color: rgba(180,140,20,0.4);
  background: rgba(180,140,20,0.08);
}
.forum-card-header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 0.7rem 0.4rem;
}
.forum-card-header .post-author-avatar {
  width: 28px;
  height: 28px;
  font-size: 0.55rem;
}
.forum-card-username {
  font-size: 0.78rem;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 130px;
}
.forum-card-bold-title {
  font-family: 'Cinzel', serif;
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--parchment);
  padding: 0 0.7rem 0.4rem;
  line-height: 1.3;
}
.forum-card-image {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  display: block;
}
.forum-card-actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem 0.7rem;
  border-top: 1px solid var(--border);
}
.forum-card-actions .comment-action-btn {
  font-size: 0.75rem;
}
.forum-card-actions span {
  font-size: 0.75rem;
  color: var(--text-muted);
}
.forum-card-time {
  font-size: 0.68rem;
  color: var(--text-muted);
  padding: 0 0.7rem 0.1rem;
}
/* Topic checkbox styling */
.forum-topic-check {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.82rem;
  color: var(--parchment-dark);
  cursor: pointer;
  padding: 0.3rem 0.6rem;
  background: var(--ash-mid);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: border-color 0.15s;
}
.forum-topic-check:has(input:checked) {
  border-color: var(--sepia);
  background: rgba(180,140,20,0.1);
}
.forum-topic-check input {
  accent-color: var(--sepia);
}
/* Empty row state */
.forum-row-empty {
  color: var(--text-muted);
  font-size: 0.85rem;
  font-style: italic;
  padding: 1.5rem 0;
}

/* ---- NOTIFICATION FLOATING PANEL ---- */
.notif-item {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.5rem 0.35rem;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  transition: background 0.15s;
  border-radius: var(--radius);
}
.notif-item:hover { background: rgba(180,140,20,0.06); }
.notif-item.unread { background: rgba(180,140,20,0.12); box-shadow: inset 0 0 0 1px rgba(212,160,23,0.35), 0 0 8px rgba(212,160,23,0.15); }
.notif-item-icon {
  font-size: 1rem;
  flex-shrink: 0;
  width: 24px;
  text-align: center;
  margin-top: 0.1rem;
}
.notif-item-body { flex: 1; min-width: 0; }
.notif-item-text {
  font-size: 0.82rem;
  color: var(--parchment-dark);
  line-height: 1.4;
}
.notif-item-text strong { color: var(--parchment); }
.notif-item-time {
  font-size: 0.7rem;
  color: var(--text-muted);
  margin-top: 0.15rem;
}


/* ============================================================
   BATCH 15 Rev 5 — Tag Autocomplete Dropdown
   ============================================================
   .tag-autocomplete and .tag-autocomplete-item added for the
   Instagram-style @tag dropdown filtered to followers only.
   ============================================================ */
.tag-autocomplete {
  position: absolute;
  z-index: 9999;
  background: var(--ash);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: 0 4px 20px rgba(0,0,0,0.5);
  max-height: 200px;
  overflow-y: auto;
  width: 240px;
  display: none;
}
.tag-autocomplete-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.45rem 0.65rem;
  cursor: pointer;
  transition: background 0.12s;
}
.tag-autocomplete-item:hover {
  background: rgba(180,140,20,0.1);
}
.shop-tab {
  flex: none;
  padding: 0.55rem 1rem;
  font-family: Cinzel, serif;
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-muted);
  border-bottom: 2px solid transparent;
  transition: color 0.15s, border-color 0.15s;
}
.shop-tab:hover { color: var(--parchment); }
.shop-tab.active {
  color: var(--sepia);
  border-bottom-color: var(--sepia);
}

/* ---- ADMIN PANEL TABS (Batch 22) ---- */
.admin-tab {
  flex: 1;
  padding: 0.55rem 1rem;
  font-family: Cinzel, serif;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-muted);
  border-bottom: 2px solid transparent;
  transition: color 0.15s, border-color 0.15s;
}
.admin-tab:hover { color: var(--parchment); }
.admin-tab.active {
  color: var(--sepia);
  border-bottom-color: var(--sepia);
}
.admin-metric-card {
  background: var(--ash-mid);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem;
  text-align: center;
  cursor: pointer;
  transition: border-color 0.2s, box-shadow 0.2s;
}
.admin-metric-card:hover {
  border-color: var(--sepia);
  box-shadow: 0 2px 12px rgba(0,0,0,0.3);
}
.admin-metric-num {
  font-family: Cinzel, serif;
  font-size: 1.8rem;
  color: var(--sepia);
  display: block;
  margin-bottom: 0.15rem;
}
.admin-metric-label {
  font-size: 0.78rem;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-family: Cinzel, serif;
}
.admin-metric-delta {
  font-size: 0.72rem;
  margin-top: 0.2rem;
}
.admin-scrollable-window {
  max-height: 220px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.5rem;
  background: var(--ash);
}

/* ============================================================
   BATCH 29 — Violation section styling (Critique 4)
   ============================================================ */
.violation-section {
  border-left: 3px solid #c8b830;
  padding-left: 0.6rem;
}
.violation-subblock {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.75rem;
  margin-bottom: 0.6rem;
  background: rgba(160,140,20,0.06);
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  transition: background 0.15s;
}
.violation-subblock:hover {
  background: rgba(160, 140, 20, 0.10);
}

/* ============================================================
   BATCH 29 revision log
   ============================================================
   Critique 4 — Added .violation-section (left gold border marker)
     and .violation-subblock (per-flag group container) styling.
     Existing .user-lib-badge.yellow reused for (N Alerts) badge
     in User Library and Most Wanted rows.
   Critiques 1, 2, 3, 5, 6: No CSS changes.
   ============================================================ */

/* ============================================================
   BATCH 30 — Verdict system styling (Critique 2)
   ============================================================ */

/* ACCESS CODE word tokens — green when active, gray when struck.
   Per Autarch's directive: gray = dead, no line-through. */
.access-code-row {
  font-family: 'Crimson Pro', serif;
  font-size: 1rem;
  padding: 0.5rem 0.75rem;
  background: var(--ash-mid);
  border-radius: var(--radius);
  border: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.1rem;
}
.access-code-word {
  color: #60b870;
  font-weight: 500;
  transition: color 0.15s;
}
.access-code-word.dead {
  color: #6a6a60;
  font-weight: 400;
  opacity: 0.75;
}
.access-code-sep {
  color: var(--text-muted);
  opacity: 0.6;
}

/* Verdict section — slight visual separation from violation above */
.verdict-section {
  border-left: 3px solid var(--sepia);
  padding-left: 0.6rem;
}

/* Reduce Strike button visual hint when user has 0 strikes */
.btn-ghost[data-no-strikes="true"] {
  opacity: 0.65;
}

/* ============================================================
   BATCH 30 revision log
   ============================================================
   Critique 2 — Added:
     - .access-code-row (container), .access-code-word (green active),
       .access-code-word.dead (grayed, no line-through per directive),
       .access-code-sep (middot separator)
     - .violation-postcard-thumb (64px post image in violation block — superseded in Batch 31)
     - .verdict-section (left sepia border to mirror violation-section styling)
     - .btn-ghost[data-no-strikes] opacity hint for Reduce Strike when 0 strikes
   Critiques 1, 3, 4, 5, 6, 7: No CSS changes.
   ============================================================ */

/* ============================================================
   BATCH 31 — Security protocol overhaul styles
   ============================================================ */

/* ---- User popup tab bar (Dossier / User Correspondence) ---- */
.user-popup-tab-bar {
  display: flex;
  gap: 0.25rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 1rem;
}
.user-popup-tab {
  background: transparent;
  border: none;
  padding: 0.6rem 1rem;
  color: var(--text-muted);
  font-family: Cinzel, serif;
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  transition: color 0.15s, border-color 0.15s;
}
.user-popup-tab:hover {
  color: var(--parchment);
}
.user-popup-tab.active {
  color: var(--sepia);
  border-bottom-color: var(--sepia);
}

/* ---- Severity badges on violation subblocks ---- */
.violation-severity {
  display: inline-block;
  font-family: Cinzel, serif;
  font-size: 0.65rem;
  letter-spacing: 0.12em;
  font-weight: 700;
  padding: 0.2rem 0.55rem;
  border-radius: 3px;
  margin-bottom: 0.3rem;
}
.violation-severity.death-row {
  background: rgba(160,30,30,0.35);
  color: #e06060;
  border: 1px solid rgba(160,30,30,0.6);
  text-transform: uppercase;
}
.violation-severity.three-strike {
  background: rgba(180,90,20,0.35);
  color: #e09040;
  border: 1px solid rgba(180,90,20,0.6);
  text-transform: uppercase;
}

/* ---- Violation subblock severity borders ---- */
.violation-subblock.severity-death-row {
  border-left: 4px solid #e06060 !important;
  background: rgba(160,30,30,0.08) !important;
}
.violation-subblock.severity-three-strike {
  border-left: 4px solid #e09040 !important;
  background: rgba(180,90,20,0.06) !important;
}

/* ---- Violation subblock child layout (info / preview / actions) ---- */
.violation-info {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.violation-code-line {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.violation-code-label {
  font-family: Cinzel, serif;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.violation-code-caption {
  font-size: 0.78rem;
  color: var(--parchment-dark);
  font-style: italic;
}
.violation-context {
  font-size: 0.87rem;
  color: var(--parchment);
  line-height: 1.45;
}
.violation-tracking-id {
  font-family: 'Crimson Pro', serif;
  font-size: 0.7rem;
  color: var(--text-muted);
  letter-spacing: 0.02em;
  word-break: break-all;
}

/* Preview box — sits between info and action row per Autarch directive.
   Variants: .image (thumbnail), .keyword (excerpt card), .placeholder
   (dashed empty-state for historical/no-data records). */
.violation-preview-box {
  background: var(--ash-mid);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.55rem 0.7rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  max-width: 100%;
}
.violation-preview-box.image {
  padding: 0;
  overflow: hidden;
  max-width: 280px;
  background: var(--ink);
}
.violation-preview-box.image img {
  width: 100%;
  max-height: 200px;
  object-fit: cover;
  display: block;
  border-radius: var(--radius);
}
.violation-preview-box.keyword {
  max-width: 420px;
}
.violation-preview-box.placeholder {
  border-style: dashed;
  background: rgba(201,169,110,0.04);
  max-width: 420px;
}
.violation-preview-label {
  font-family: Cinzel, serif;
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.violation-preview-excerpt {
  font-family: 'Crimson Pro', serif;
  font-size: 0.85rem;
  color: var(--parchment);
  font-style: italic;
  line-height: 1.5;
  word-break: break-word;
}

/* Action row — Guilty / Close Case / View Full Post — sits at bottom,
   separated from the preview by a subtle rule. */
.violation-action-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding-top: 0.45rem;
  border-top: 1px solid var(--border);
}

/* Batch 92 Fix 6 — .violation-tag.ivai removed (merchant protocol removed Batch 70; unreachable). */

/* ---- Weight indicator on per-alert buttons (e.g., IV 3x STRIKE) ---- */
.violation-weight-badge {
  display: inline-block;
  margin-left: 0.4rem;
  background: rgba(160,30,30,0.25);
  color: #e06060;
  font-size: 0.6rem;
  font-family: Cinzel, serif;
  letter-spacing: 0.1em;
  padding: 0.1rem 0.35rem;
  border-radius: 3px;
  font-weight: 700;
}

/* ---- Per-alert verdict staging (Guilty / Close Case) ---- */
.verdict-flag-staged {
  box-shadow: 0 0 0 2px var(--sepia) inset;
}

/* Issue Strike counter pulse when staged strikes > 0 */
.verdict-strike-active {
  box-shadow: 0 0 12px rgba(201,169,110,0.4);
}

/* Per-user verdict selection highlight (Batch 31 Phase 3). Applied to
   whichever of Issue Strike / Reduce / Close Case / Clear Strikes the
   admin has clicked. Distinct from .verdict-flag-staged (per-alert). */
.verdict-selected {
  box-shadow: 0 0 0 2px var(--sepia) inset, 0 0 14px rgba(201,169,110,0.5);
}

/* ---- Ban User dropdown ---- */
.verdict-ban-wrapper {
  position: relative;
  display: inline-block;
}
.verdict-ban-dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  margin-top: 0.3rem;
  background: var(--ash);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.3rem;
  min-width: 180px;
  z-index: 10;
  box-shadow: 0 6px 20px rgba(0,0,0,0.4);
}
.verdict-ban-dropdown.hidden {
  display: none;
}
.verdict-ban-option {
  padding: 0.5rem 0.7rem;
  cursor: pointer;
  color: var(--parchment);
  font-size: 0.85rem;
  border-radius: var(--radius);
  transition: background 0.12s;
}
.verdict-ban-option:hover {
  background: rgba(160,30,30,0.25);
  color: #e06060;
}
.verdict-ban-option.verdict-ban-clear {
  color: var(--text-muted);
  border-top: 1px solid var(--border);
  margin-top: 0.2rem;
  font-style: italic;
}
.verdict-ban-option.verdict-ban-clear:hover {
  background: rgba(201,169,110,0.1);
  color: var(--parchment);
}
.verdict-ban-staged {
  box-shadow: 0 0 0 2px #e06060 inset;
}

/* ---- Gavel commit button (inline SVG, head rests up, slams on click) ---- */
.verdict-gavel-btn {
  background: linear-gradient(135deg, var(--sepia-dark), var(--sepia));
  border: 2px solid var(--sepia);
  border-radius: 50%;
  width: 68px;
  height: 68px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  transition: box-shadow 0.2s, transform 0.15s;
  box-shadow: 0 2px 10px rgba(0,0,0,0.35);
  overflow: visible;
}
.verdict-gavel-btn:hover {
  box-shadow: 0 0 0 3px rgba(201,169,110,0.25), 0 4px 14px rgba(201,169,110,0.45);
  transform: translateY(-1px);
}
.verdict-gavel-btn:active {
  transform: translateY(0);
}
.verdict-gavel-svg {
  width: 52px;
  height: 52px;
  display: block;
  overflow: visible;
}
/* Gavel head+handle group. Pivot at (32, 52) in SVG user-space — the
   handle base center — so the head rotates around that point like a
   gavel being swung. transform-box defaults to view-box for SVG so we
   don't set it explicitly (setting fill-box would offset the origin
   relative to the group's own bounding box, breaking the pivot). */
.verdict-gavel-g {
  transform-origin: 32px 52px;
  transform: rotate(0deg);
}
/* Strike animation — wind up, slam down, small recoil, return to rest.
   Applied via .striking class added by _verdictGavel on successful commit. */
.verdict-gavel-btn.striking .verdict-gavel-g {
  animation: gavelStrike 0.6s cubic-bezier(0.55, 0.08, 0.22, 1.25);
}
@keyframes gavelStrike {
  0%   { transform: rotate(0deg); }
  25%  { transform: rotate(-55deg); }    /* wind up, head lifts back */
  50%  { transform: rotate(25deg); }     /* slam down onto block */
  65%  { transform: rotate(12deg); }     /* small recoil bounce */
  100% { transform: rotate(0deg); }      /* return to rest */
}
/* Sound block bounces when the strike lands (mirrors the gavel timing) */
.verdict-gavel-btn.striking .verdict-gavel-svg ellipse {
  animation: blockImpact 0.6s cubic-bezier(0.55, 0.08, 0.22, 1.25);
}
@keyframes blockImpact {
  0%, 45%, 100% { transform: translateY(0); }
  50%           { transform: translateY(2px); }
  60%           { transform: translateY(0); }
}

/* ---- User Correspondence inbox rows (Batch 31 Phase 3) ----
   Gmail-style list: sender left, bold subject + preview center,
   timestamp right. Replaces the chat-bubble layout with source tag.
   The prior .corr-source-tag / .dm / .email rules were removed when
   the source tag disappeared (correspondence is now email-only). */
.corr-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6rem 0.75rem;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  transition: background 0.12s;
}
.corr-row:hover {
  background: rgba(201,169,110,0.06);
}
.corr-body {
  padding: 0.75rem 0.9rem;
  background: rgba(201,169,110,0.04);
  border-bottom: 1px solid var(--border);
  font-size: 0.88rem;
  line-height: 1.55;
  color: var(--parchment);
  white-space: pre-wrap;
}

/* ---- Death Row badge in Sovereign Reports ---- */
.death-row-badge {
  font-family: Cinzel, serif;
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  padding: 0.15rem 0.45rem;
  background: rgba(160,30,30,0.4) !important;
  color: #ff7070 !important;
  border: 1px solid rgba(160,30,30,0.7);
}

/* ============================================================
   REVISION LOG — BATCH 31 (FG Opus Claude)
   ============================================================
   Security protocol overhaul styling. Final state after Phase 2
   corrections:

   Admin popup layout:
     - .user-popup-tab-bar + .user-popup-tab: tab bar for the new
       Dossier / User Correspondence layout.

   Violation subblock (vertical: info -> preview -> actions):
     - .violation-subblock: gained border/padding/column flex
       (previously inlined by JS; now class-owned).
     - .violation-info: top info block.
     - .violation-code-line + .violation-code-label +
       .violation-code-caption: inline "Violation Code: <TAG>
       <caption>" format per Autarch directive.
     - .violation-context: context sentence line.
     - .violation-tracking-id: KF-xxx tracking id as small metadata.
     - .violation-preview-box (+ .image / .keyword / .placeholder):
       preview card between info and actions. Image variant shows
       thumbnail; keyword variant shows excerpt card; placeholder
       variant (dashed border) handles historical / no-data records.
     - .violation-preview-label + .violation-preview-excerpt: label
       and content inside preview cards.
     - .violation-action-row: Guilty / Close Case / View Full Post
       bottom row with subtle top rule.
     - .violation-tag.ivai: IVAI merchant-AI category tag, completing
       the IV (red) / KV (blue) / IVAI (orange) trio.

   Severity accents:
     - .violation-severity + death-row / three-strike variants.
     - .violation-subblock.severity-death-row / severity-three-strike:
       left-border accent + tinted background.

   Verdict controls:
     - .violation-weight-badge: "3x STRIKE" marker on Guilty button
       for IVAI weight-3 alerts.
     - .verdict-flag-staged + .verdict-strike-active: staging
       indicators (per-alert stage + cumulative Nx counter).
     - .verdict-ban-wrapper + .verdict-ban-dropdown + .verdict-ban-option
       + .verdict-ban-clear + .verdict-ban-staged: Ban User dropdown.

   Gavel (inline SVG, rest state head-up, slam animation on click):
     - .verdict-gavel-btn: circular parchment-gold button (68px),
       no hover lift (gavel is already up), subtle glow on hover.
     - .verdict-gavel-svg + .verdict-gavel-g: SVG container + group
       whose transform-origin sits at the handle base so the head
       swings around it.
     - @keyframes gavelStrike: wind up (-55deg) -> slam down (+25deg)
       -> small recoil (+12deg) -> rest (0deg). 0.6s total.
     - @keyframes blockImpact: sound-block bounce on strike landing.
     - .striking class: added by _verdictGavel after validation
       passes; removed 600ms later so subsequent commits animate
       fresh.

   User Correspondence (Gmail-style list — Batch 31 Phase 3):
     - .corr-row: list row (sender left, bold subject + preview
       middle, timestamp right). :hover highlights the row.
     - .corr-body: expanded full-message body shown when a row
       is clicked via _toggleCorrMessage.

   Sovereign Reports:
     - .death-row-badge: DEATH-ROW badge in the Death Row queue.

   Removed in Phase 2 cleanup:
     - .violation-postcard-thumb (superseded by .violation-preview-box.image)
     - .violation-keyword-preview / .violation-keyword-label /
       .violation-keyword-excerpt (superseded by .violation-preview-box.keyword
       + .violation-preview-label + .violation-preview-excerpt)
     - .verdict-gavel-icon (superseded by inline SVG markup)

   Removed in Phase 3 cleanup:
     - .corr-source-tag / .corr-source-tag.dm / .corr-source-tag.email
       (source tag eliminated when correspondence became email-only;
       the chat-bubble layout was replaced with .corr-row + .corr-body).

   Added in Phase 3:
     - .verdict-selected: per-user verdict selection highlight (stronger
       than .verdict-flag-staged) applied to whichever verdict button the
       admin clicked. None of the four buttons is pre-selected on render.
     - .corr-row + :hover + .corr-body: correspondence inbox list layout.
   ============================================================ */

/* ============================================================
   Batch 32 Critique 3 — Forum Library activation buttons
   DEACTIVATE: transparent → crimson fill on hover.
   ACTIVATE:   transparent → forest green fill on hover.
   White text always. Cinzel uppercase to match Library tab bar.
   ============================================================ */
.fl-activate-btn {
  background: transparent;
  color: #ffffff;
  border: 1px solid var(--border);
  padding: 0.35rem 0.75rem;
  font-family: 'Cinzel', serif;
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  cursor: pointer;
  border-radius: var(--radius);
  transition: background 0.15s ease, border-color 0.15s ease;
  flex-shrink: 0;
  white-space: nowrap;
}
.fl-activate-btn:hover {
  background: #2d6a3a;
  border-color: #2d6a3a;
  color: #ffffff;
}

/* ============================================================
   Batch 32 Critique 4 — Forum comment upvote / downvote buttons
   Default: muted text, transparent background.
   Active upvote: sepia (gold) fill.
   Active downvote: crimson fill.
   Hover: subtle bg tint toward the active color.
   Applied at BOTH forum panel (fcup-/fcdn-) and post modal (cup-/cdn-).
   ============================================================ */
.vote-btn {
  background: transparent;
  color: var(--text-muted);
  border: 1px solid var(--border);
  padding: 0.2rem 0.5rem;
  font-size: 0.72rem;
  font-family: inherit;
  cursor: pointer;
  border-radius: var(--radius);
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
  line-height: 1.1;
  display: inline-flex;
  align-items: center;
  gap: 0.15rem;
}
.vote-btn.vote-up:hover {
  background: rgba(201, 169, 110, 0.12);
  color: var(--sepia);
  border-color: var(--sepia);
}
.vote-btn.vote-down:hover {
  background: rgba(139, 26, 26, 0.15);
  color: #e06060;
  border-color: var(--crimson);
}
.vote-btn.vote-up.active {
  background: rgba(201, 169, 110, 0.25);
  color: var(--sepia);
  border-color: var(--sepia);
}
.vote-btn.vote-down.active {
  background: rgba(139, 26, 26, 0.3);
  color: #e06060;
  border-color: var(--crimson);
}

/* ============================================================
   Batch 32 Critique 1 — Admin Panel Fullscreen Tab Mode
   When body has .admin-fullscreen-mode, site chrome is suppressed
   and the admin modal is scaled to fill the viewport. Applied only
   on the ?admin=1 tab; the main-site tab is unaffected.

   Chrome hidden: top nav, community feed / all site sections,
   notification floating panel + detail, DM floating panel, ADP
   toggle (redundant in fullscreen), footer, new-post selector,
   loader, profile page overlay, and the admin modal overlay
   backdrop (since there's no site behind it to dim).
   ============================================================ */
body.admin-fullscreen-mode #topNav,
body.admin-fullscreen-mode nav,
body.admin-fullscreen-mode main,
body.admin-fullscreen-mode #homeSection,
body.admin-fullscreen-mode #forumSection,
body.admin-fullscreen-mode #gallerySection,
body.admin-fullscreen-mode #bookSection,
body.admin-fullscreen-mode #shopSection,
body.admin-fullscreen-mode #metraSection,
body.admin-fullscreen-mode #announcementBanner,
body.admin-fullscreen-mode footer,
body.admin-fullscreen-mode #siteLoader,
body.admin-fullscreen-mode #profilePageOverlay,
body.admin-fullscreen-mode #dmFloatingPanel,
body.admin-fullscreen-mode #notifFloatingPanel,
body.admin-fullscreen-mode #notifDetailPanel,
body.admin-fullscreen-mode #adpToggle,
body.admin-fullscreen-mode #pinnedForumToggle,
body.admin-fullscreen-mode #floatingToggleStack,
body.admin-fullscreen-mode #newPostSelector {
  display: none !important;
}

body.admin-fullscreen-mode {
  overflow: hidden;
}

/* Neutralize overlay backdrop, scale admin modal to viewport */
body.admin-fullscreen-mode #adminModal.modal-overlay {
  background: var(--ink);
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  padding: 0;
  display: block;
  overflow: auto;
}
body.admin-fullscreen-mode #adminModal .modal-box {
  max-width: none !important;
  max-height: none !important;
  width: 100% !important;
  min-height: 100vh;
  margin: 0 !important;
  border-radius: 0 !important;
  border: none !important;
  padding: 1.5rem 2rem !important;
  box-shadow: none !important;
}
body.admin-fullscreen-mode #adminModal .modal-title {
  font-size: 1.5rem;
}

/* ============================================================
   Batch 169 — Collective Board (permanent left column) + two-column
   admin workspace. The Board holds New Members, Admin Job Applications,
   and the User Library. It is side-by-side with the tab content at ALL
   viewport widths (phone + desktop) and collapses horizontally to a thin
   spine for better viewability of the Security / Site Stats tabs.
   ============================================================ */
.admin-workspace {
  display: flex;
  align-items: flex-start;
  gap: 1.5rem;
  width: 100%;
}
.admin-collective-board {
  /* Clamp scales the expanded width across the viewport range. */
  flex: 0 0 clamp(280px, 30vw, 420px);
  width: clamp(280px, 30vw, 420px);
  min-width: 0;
  border-right: 1px solid var(--border);
  padding-right: 1.25rem;
  position: relative;
  transition: flex-basis 0.18s ease, width 0.18s ease, padding 0.18s ease;
}
.admin-tab-column {
  flex: 1 1 auto;
  min-width: 0;
}
/* Collapse control (chevron at the board's top-right while expanded). */
.admin-board-collapse-btn {
  position: absolute;
  top: 0; right: 0.5rem;
  background: var(--ash-mid);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--sepia);
  font-size: 1rem;
  line-height: 1;
  padding: 0.2rem 0.5rem;
  cursor: pointer;
  z-index: 2;
}
.admin-board-collapse-btn:hover { background: var(--ash); }
/* Spine (shown only when collapsed): vertical expand handle + label. */
.admin-board-spine {
  display: none;
  width: 100%;
  background: var(--ash-mid);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--sepia);
  cursor: pointer;
  padding: 0.75rem 0.25rem;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  font-family: 'Cinzel', serif;
}
.admin-board-spine:hover { background: var(--ash); }
.admin-board-spine-arrow { font-size: 1.1rem; line-height: 1; }
.admin-board-spine-label {
  writing-mode: vertical-rl;
  text-orientation: mixed;
  letter-spacing: 0.12em;
  font-size: 0.72rem;
  text-transform: uppercase;
  white-space: nowrap;
}
/* Collapsed state: board shrinks to the spine; content + collapse chevron hide. */
.admin-collective-board.collapsed {
  flex: 0 0 auto;
  width: auto;
  padding-right: 0;
  border-right: none;
}
.admin-collective-board.collapsed .admin-board-content,
.admin-collective-board.collapsed .admin-board-collapse-btn { display: none; }
.admin-collective-board.collapsed .admin-board-spine { display: flex; }

/* ============================================================
   Batch 33 Critique 1 — Pin Forum feature styles
   Pin button: outlined box with pushpin glyph. Hover fills sepia
   gold as visual affordance of what pinning does. After pin, the
   button swaps to a plain-text "Unpin" action (no box).
   Ribbon: appears above forum title when 2+ forums are pinned
   AND panel was opened from the pin toggle. Horizontal scrollable
   strip of forum-title tabs with per-forum badge counts.
   ============================================================ */
.pin-btn {
  background: transparent;
  color: var(--parchment);
  border: 1px solid var(--border-strong);
  padding: 0.35rem 0.55rem;
  font-size: 1rem;
  cursor: pointer;
  border-radius: var(--radius);
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
  line-height: 1;
  flex-shrink: 0;
}
.pin-btn:hover {
  background: var(--sepia);
  color: var(--ink);
  border-color: var(--sepia);
}
.pin-btn:active {
  background: var(--sepia-light);
  color: var(--ink);
  border-color: var(--sepia-light);
}
.unpin-btn {
  background: transparent;
  color: var(--sepia);
  border: none;
  padding: 0.35rem 0.6rem;
  font-family: 'Cinzel', serif;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  cursor: pointer;
  text-transform: uppercase;
  flex-shrink: 0;
}
.unpin-btn:hover {
  color: var(--sepia-light);
  text-decoration: underline;
}

.pinned-forum-tab {
  background: transparent;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.35rem 0.7rem;
  font-family: 'Cinzel', serif;
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  cursor: pointer;
  white-space: nowrap;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.pinned-forum-tab:hover {
  color: var(--parchment);
  border-color: var(--sepia-dark);
}
.pinned-forum-tab.active {
  background: var(--ash-mid);
  color: var(--sepia);
  border-color: var(--sepia);
}
.pinned-forum-tab-count {
  background: #e06060;
  color: #ffffff;
  font-family: 'Cinzel', serif;
  font-size: 0.6rem;
  padding: 0.05rem 0.4rem;
  border-radius: 10px;
  line-height: 1;
}

/* ============================================================
   REVISION LOG — BATCH 37 (FG Opus Claude)
   ============================================================
   Profile Page Restructure:

   Two-column layout:
     - .profile-columns: flex container for feed + sidebar.
     - .profile-feed-column: left column (flex:3), independently
       scrollable, max-height viewport. Custom scrollbar.
     - .profile-sidebar-column: right column (flex:2), sticky
       positioning (top:1rem), max-height viewport.
     - Mobile responsive: columns stack below 700px.

   Profile feed cards:
     - .profile-feed-card: individual card with border, padding,
       hover highlight.
     - .pf-card-meta: flex row with type badge + date.
     - .pf-card-type: Cinzel label badge (FORUM, QUOTE, etc.).
     - .pf-card-date: right-aligned relative timestamp.
     - .pf-card-title: single-line truncated title.
     - .pf-card-preview: content preview text.
     - .pf-card-action: small View button.

   Scoring placeholder:
     - .profile-scoring-note: dashed-border placeholder box
       for future scoring/credits system.

   Layout:
     - .profile-page max-width widened from 900px to 1100px.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 37 PHASE 3 (FG Opus Claude)
   ============================================================
   Rev 3 — Image post size standardization:
     .gallery-feed-image: aspect-ratio 1/1, object-fit cover,
     fills card width. Square max proportions. Replaces inline
     max-height:500px;object-fit:contain on gallery images.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 37 PHASE 4 (FG Opus Claude)
   ============================================================
   Rev 4 — Activity Feed tabbed navigation:
     .profile-feed-header-sticky: sticky header with z-index 5,
       background matches page, holds label + tab bar.
     .profile-feed-tabs: flex container, full width, bordered.
     .profile-feed-tab: equal-width buttons with Cinzel font,
       active state highlights with sepia + bold.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 38 (FG Opus Claude)
   ============================================================
   Rev 2 — Forum image size cap:
     .post-card-image: added max-height: 500px to match gallery
     image constraint. object-fit: contain prevents cropping.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 39 (FG Opus Claude)
   ============================================================
   Rev 1 — Forum feed card image structural fix:
     Added .post-card-image-wrap class (max-height:500px;
     overflow:hidden; border-radius top corners). Replaced
     previous .post-card-image rules (max-width/max-height/
     width:auto/height:auto/margin:0 auto) with width:100%;
     display:block. Image always fills card width. Wrapper
     clips overflow. HTML structure changed in app.js —
     <img> now wrapped in .post-card-image-wrap container
     in both renderPostCard() and _renderPostAsFeedCard().
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 40 (FG Opus Claude)
   ============================================================
   Rev 1 — Forum feed card image center-crop fix:
     .post-card-image-wrap: added display:flex; align-items:center.
     Flexbox centers image vertically inside 500px-capped wrapper.
     Portrait images exceeding 500px now clip evenly from top and
     bottom instead of bottom-only. Landscape images unaffected.
     .post-card-image rule unchanged (width:100%; display:block).
   ============================================================ */

/* ============================================================
   BATCH 52 — Ranking & Badge System Styles (FG Opus Claude)
   ============================================================ */

/* Hack Alert post card — baby blue tint */
.hack-alert-card {
  border-left: 4px solid #5ba3d9 !important;
  background: rgba(91, 163, 217, 0.06) !important;
}

.hack-alert-card .post-tag {
  background: rgba(91, 163, 217, 0.2);
  color: #5ba3d9;
}

/* Council Favorite post card — gold tint */
.council-favorite-card {
  border: 2px solid #c9a84c !important;
  background: rgba(201, 168, 76, 0.15) !important;
  box-shadow: 0 0 12px rgba(201, 168, 76, 0.25), inset 0 0 20px rgba(201, 168, 76, 0.05) !important;
}

.council-favorite-card .post-tag {
  background: rgba(201, 168, 76, 0.35);
  color: #e8c14c;
  border-color: #c9a84c;
}

/* Batch 166 — Magisterium recognition: ultraviolet tint on a Magisterium
   member's deliberate universal post. Applied via .magisterium-universal-card,
   gated in renderPostCard to category 'universal' + a majesty-badge author. */
.magisterium-universal-card {
  border: 2px solid #8a4fff !important;
  background: rgba(138, 79, 255, 0.14) !important;
  box-shadow: 0 0 14px rgba(138, 79, 255, 0.35), inset 0 0 22px rgba(124, 58, 237, 0.08) !important;
}

.magisterium-universal-card .post-tag {
  background: rgba(138, 79, 255, 0.35);
  color: #c9a4ff;
  border-color: #8a4fff;
}

/* Council Favorite star button */
.council-fav-star {
  color: #c9a84c !important;
  font-size: 1rem;
}

/* Batch 144 Rev 4 — Vermilion moved from feed cards to Autarch's Choice sidebar */
#autarchChoiceCard {
  border: 2px solid #CC3300 !important;
  background: rgba(204, 51, 0, 0.12) !important;
  box-shadow: 0 0 14px rgba(204, 51, 0, 0.25), inset 0 0 20px rgba(204, 51, 0, 0.05) !important;
}

.council-fav-star:hover {
  color: #e8c14c !important;
}

/* Badge icon in profile banner */
.profile-badge-icon {
  display: inline-block;
  vertical-align: middle;
  transition: transform 0.15s;
}

.profile-badge-icon:hover {
  transform: scale(1.15);
}

/* Batch 163 — Saint angel-wings badge. Wings flank the wrapped points-badge on
   both sides (wings alone when the Saint holds no points-badge). The right wing is
   the same image mirrored. Sized in em to scale with surrounding context. */
.saint-winged {
  gap: 0.05em;
}
.saint-wing {
  height: 0.85em;
  width: 1.05em;
  vertical-align: middle;
  filter: drop-shadow(0 0 1px rgba(244,196,48,0.5));
}
.saint-wing-left {
  margin-right: -0.05em;
}
.saint-wing-right {
  margin-left: -0.05em;
  transform: scaleX(-1);
}

/* Rank delta in profile stat block */
.profile-stat-block .num {
  word-break: break-word;
}

/* Batch 59 — Prevent flash of login page on refresh */
.has-session #guestActions { display: none !important; }
.has-session #heroWelcome { display: none !important; }
.has-session #userActions { display: flex !important; }
/* Batch 76 — Landing page gating: hide auth section + show nav when logged in */
.has-session #landingAuth { display: none !important; }
.has-session #mainNav { display: flex !important; }
/* Batch 76 — Hide nav + feed for unauthenticated (reinforced by JS) */
html:not(.has-session) #mainNav { display: none !important; }
html:not(.has-session) .feed-layout { display: none !important; }
/* Batch 150 Rev 4d — Gate all content sections behind has-session */
html:not(.has-session) #forumSection { display: none !important; }
html:not(.has-session) #shopSection { display: none !important; }
html:not(.has-session) #announcementBanner { display: none !important; }

/* ============================================================
   BATCH 66 — Story Patron System Styles
   ============================================================ */

.patron-tier-option {
  background: var(--ash-mid);
  transition: border-color 0.2s, background 0.2s;
}

.patron-tier-option:hover {
  border-color: var(--sepia) !important;
  background: var(--ash-dark);
}

/* Patron badge on story forum cards */
.patron-badge {
  display: inline-block;
  font-family: Cinzel, serif;
  font-size: 0.6rem;
  color: var(--sepia);
  border: 1px solid var(--sepia);
  border-radius: var(--radius);
  padding: 0.1rem 0.35rem;
  letter-spacing: 0.06em;
  margin-left: 0.3rem;
  vertical-align: middle;
}

/* Volume label in swipe dots */
#swipeDots span {
  transition: color 0.15s;
}

/* Add-volume dropdown positioning */
[id^="addVolumeDropdown-"] {
  background: var(--ash-dark);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.3rem 0;
  box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}

[id^="addVolumeDropdown-"] div {
  padding: 0.5rem 0.8rem;
  font-size: 0.82rem;
  color: var(--parchment);
  cursor: pointer;
  font-family: inherit;
}

[id^="addVolumeDropdown-"] div:hover {
  background: var(--ash-mid);
  color: var(--sepia);
}

/* ============================================================
   BATCH 71 — Forum Points Integration, Poll Overhaul, DTP
   ============================================================
   No style changes. Poll voting UI uses inline styles in
   app.js. Report card category point labels use inline styles
   in _buildReportCardHtml. DTP violation subblock reuses
   existing .kv class for category badge styling.
   ============================================================ */

/* ============================================================
   BATCH 72 — Codebase Scan, Bug Fixes & Revisions
   ============================================================
   No style changes. All Batch 72 work is in app.js, db.js,
   egg.js, and README.md. Interaction button active states
   use existing .liked class (Rev 5).
   ============================================================ */

/* ============================================================
   BATCH 73 — Batch 72 Scan Fixes + Revisions
   ============================================================
   Rev 2 — Removed margin-left:auto; margin-right:auto from
   .gallery-feed-card. Discovery posts now left-align with
   .post-card in the community feed.
   ============================================================ */

/* ============================================================
   BATCH 74 — Batch 73 Scan Fixes + Poll Vote Feedback
   ============================================================
   No changes to this file.
   Scan fixes, notification fix, voters tab, fullscreen parity in app.js.
   SQL files corrected: supabase-auth-rls.sql, supabase-security-migration.sql.
   ============================================================ */

/* ============================================================
   BATCH 75 — Codebase Scan Fixes
   ============================================================
   No changes to this file.
   Scan fixes in app.js and db.js. Dead IVAI code removed. README file tree corrected.
   ============================================================ */

/* ============================================================
   BATCH 76 — Landing Page Gate + Split-Screen Auth
   ============================================================
   Rev 1 — .landing-auth-section, .landing-auth-inner, .landing-auth-panel,
     .landing-auth-heading, .landing-auth-divider styles added.
     Responsive: flex-direction column on mobile.
   Rev 2 — .hero-body max-width widened to 600px, line-height added.
   Rev 3 — has-session CSS rules: #landingAuth hidden, #mainNav shown.
     html:not(.has-session) hides #mainNav and .feed-layout.
   ============================================================ */

/* ============================================================
   BATCH 78 — Discovery Split-Screen + Trending
   ============================================================ */

.discovery-split {
  display: flex;
  gap: 1.5rem;
  align-items: flex-start;
}
.discovery-main {
  flex: 1;
  min-width: 0;
}
.discovery-trending-wrapper {
  width: 340px;
  flex-shrink: 0;
  position: sticky;
  top: 5rem;
  max-height: calc(100vh - 5.5rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--ash-mid);
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}
.discovery-trending-label {
  font-family: Cinzel, serif;
  font-size: 1rem;
  letter-spacing: 0.1em;
  color: var(--sepia);
  padding: 0.75rem 0.75rem 0.5rem;
  margin-bottom: 0.25rem;
  position: sticky;
  background: var(--ash-mid);
  z-index: 6;
  /* Batch 197 R5 — seal the top edge so scrolling posts never bleed through
     between the rounded wrapper border and this sticky label.
     Batch 201 — attempted to also cover the rounded corner shoulders by bleeding
     the label 1px past the wrapper's content box with negative side margins.
     Batch 202 (R1 re-fix) — that side-bleed was UNREACHABLE: the wrapper is
     overflow-y:auto, which computes overflow-x to auto as well, so the -1px side
     margins were clipped by the scroll container and never painted over the corner
     arcs. The reliable seal needs no bleed: the wrapper's own overflow clip rounds
     every scrolling card to the wrapper's shape (cards cannot paint into the corner
     arcs in the first place), and this label's matched top-corner radii + opaque
     --ash-mid fill + raised z-index cap the top band cleanly. Negative top margin
     retained at -1px ONLY to close the sub-pixel seam directly above the sticky
     (that axis is the scroll axis and tucks under the wrapper's top border, not
     clipped sideways).
     Batch 208 Item 2 — the -1px proved insufficient: the wrapper has a 1px solid
     border and the label sticks to top:0 (the CONTENT-box top, inside that border),
     leaving the border-width band above the label where a scrolling card's top edge
     flashes through before the label pins. Pin the label to top:-1px so it anchors at
     the border edge, and widen the upward bleed to -2px to cover the band. The top
     axis is the scroll axis (tucks under the wrapper top border, NOT clipped sideways
     like the abandoned side-bleed), so this paints reliably. */
  top: -1px;
  margin-top: -2px;
  border-top-left-radius: var(--radius-lg);
  border-top-right-radius: var(--radius-lg);
}
.discovery-trending-wrapper .post-card {
  margin-bottom: 0.5rem;
}

/* Batch 187 R3 — Trending refresh button. Idle: hollow / white-outline glyph on a
   transparent circle. Hover: sepia. Active (.trending-refreshing): filled dark
   circle, light glyph, continuous spin until the engagement re-pull resolves. */
.trending-refresh-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; flex-shrink: 0;
  border: 1px solid var(--parchment); border-radius: 50%;
  background: transparent; color: var(--parchment);
  cursor: pointer; padding: 0;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.trending-refresh-btn:hover { color: var(--sepia); border-color: var(--sepia); }
.trending-refresh-btn.trending-refreshing {
  background: var(--ink); color: var(--parchment); border-color: var(--ink);
  cursor: default;
}
.trending-refresh-btn.trending-refreshing svg { animation: trendingSpin 0.8s linear infinite; }
@keyframes trendingSpin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }

/* Batch 187 R2 — Load New Posts button. Hollow / sepia-outline pill, idle. Hover
   intensifies. Active (.discover-loading-new): glyph spins while the oldest unseen
   posts are admitted. */
.load-new-discover-btn {
  display: inline-flex; align-items: center; gap: 0.5rem;
  background: transparent; border: 1px solid var(--sepia); border-radius: 999px;
  padding: 0.45rem 1.3rem; cursor: pointer;
  color: var(--sepia); font-family: Cinzel, serif; font-size: 0.8rem; letter-spacing: 0.06em;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.load-new-discover-btn:hover { background: var(--sepia); color: var(--ink); }
/* Batch 193 R2 — Loading state: glyph spins AND the control glows gold until the
   batch resolves, then the class is removed and it returns to idle. Shared by the
   Discover and Community load-more controls. */
.load-new-discover-btn.discover-loading-new {
  color: var(--sepia); border-color: var(--sepia); cursor: default;
  box-shadow: 0 0 8px 1px var(--sepia), 0 0 16px 2px rgba(193, 154, 107, 0.45);
}
.load-new-discover-btn.discover-loading-new svg { animation: trendingSpin 0.8s linear infinite; }

/* Batch 193 R5 — Violation checklist on the report investigation modal. A horizontal
   row of toggle "checkboxes", one per discovered code. Box is empty (outline only)
   when unticked; fills SOLID WHITE when ticked. B.I.T.C.H is tinted red to signal it
   is a standalone ban override. */
.violation-check-row {
  display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin-top: 0.4rem;
}
.violation-check {
  display: inline-flex; align-items: center; gap: 0.35rem;
  background: transparent; border: 1px solid var(--border-strong); border-radius: var(--radius);
  padding: 0.3rem 0.6rem; cursor: pointer;
  color: var(--text-muted); font-family: Cinzel, serif; font-size: 0.72rem; letter-spacing: 0.05em;
  transition: border-color 0.15s, color 0.15s;
}
.violation-check:hover { border-color: var(--sepia); color: var(--parchment); }
.violation-check-box {
  display: inline-block; width: 12px; height: 12px; border-radius: 2px;
  border: 1.5px solid var(--text-muted); background: transparent; flex-shrink: 0;
  transition: background 0.12s, border-color 0.12s;
}
.violation-check.checked .violation-check-box { background: #ffffff; border-color: #ffffff; }
.violation-check.checked { color: var(--parchment); border-color: var(--parchment); }
.violation-check-bitch { border-color: rgba(160,30,30,0.5); color: #e06060; }
.violation-check-bitch:hover { border-color: #e06060; color: #e06060; }
.violation-check-bitch.checked { border-color: #e06060; color: #e06060; }
.violation-check-bitch.checked .violation-check-box { background: #e06060; border-color: #e06060; }
.discovery-trending-wrapper .post-card-image {
  max-height: 240px;
}
@media (max-width: 768px) {
  .discovery-split { flex-direction: column; }
  .discovery-trending-wrapper {
    width: 100%;
    position: static;
    max-height: none;
    border-top: 1px solid var(--border);
    padding-top: 1rem;
    margin-top: 1rem;
  }
}

/* Batch 84 Rev 3 — Reverted Batch 83 Rev 1 (discovery independent scroll).
   The @media (min-width:769px) block locked #gallerySection to viewport height
   and broke main feed scroll. Base CSS already provides correct behavior:
   .discovery-trending-wrapper: sticky + overflow-y:auto (independent scroll).
   .discovery-trending-label: sticky top:0 inside wrapper (always visible). */

/* ============================================================
   ============================================================
   No changes to this file.
   Scan report fixes in app.js, db.js, council.js, egg.js.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 78 (FG Opus Claude)
   ============================================================
   Rev 3 — Discovery split-screen styles: .discovery-split,
     .discovery-main, .discovery-trending-wrapper (sticky),
     .discovery-trending-label, .trending-card (compact cards),
     .trending-card-img (aspect-ratio 3/4, object-fit contain).
     Responsive: stacks vertically on mobile.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 79 (FG Opus Claude)
   ============================================================
   Rev 5 — Trending section redesign: compact card classes removed
     (.trending-card, .trending-card-img, .trending-card-body,
     .trending-card-author, .trending-card-text, .trending-card-stats).
     Replaced by standard .post-card layout via _renderPostAsFeedCard.
     .discovery-trending-wrapper: 280px → 340px, panel border +
     border-radius + ash-mid background, overscroll-behavior: contain
     for scroll independence. .post-card-image max-height scoped
     inside wrapper.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 80 (FG Opus Claude)
   ============================================================
   Scan cleanup — 3 dead CSS rules removed:
     .hero-cta (Batch 76 removed hero CTA buttons),
     .fav-book-info .fav-title / .fav-series (books pipeline dead),
     .tier-card.selected (zero references).
   Rev 2 — .discovery-trending-label: position sticky, top 0,
     opaque background (ash-mid), z-index 2. Scrolling content
     passes behind the label.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 81 (FG Opus Claude)
   ============================================================
   No changes to this file.
   _checkFsdExpiry FM/admin guard + _closureReconciled
   persistence fix in app.js.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 82 (FG Opus Claude)
   ============================================================
   Rev 10 — REVERTED. Fixed-height discovery-split broke layout.
     Original CSS restored: .discovery-split flex-start,
     .discovery-main no overflow, .discovery-trending-wrapper
     position:sticky with max-height + overflow-y:auto.
     Mobile breakpoint restored with position:static.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 83 (FG Opus Claude)
   ============================================================
   Rev 1 — Discovery independent scroll: #gallerySection becomes
     flex column (height: calc(100vh - 72px), overflow: hidden).
     .discovery-main gets overflow-y:auto. .discovery-trending-wrapper
     drops position:sticky. Wrapped in @media (min-width: 769px).
   Rev 9 — --ash-dark: #1e1b17 defined in :root. Previously used
     in 6 sites (app.js + styles.css) but never declared.
   Rev 11 — 12 dead CSS rules removed: .profile-edit-btn,
     .profile-edit-area, .profile-edit-area:focus, .profile-grid
     media query, .user-popup-comment-row, .user-popup-comment-date,
     .status-orange, .status-yellow, .forum-card-lore .forum-card-title,
     .forum-card-lore .forum-card-preview, .fl-deactivate-btn from
     combined selector, .top-nav from admin-fullscreen-mode selector.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 84 (FG Opus Claude)
   ============================================================
   Rev 3 — Reverted Batch 83 Rev 1 discovery CSS.
   Removed @media (min-width:769px) block that locked
   #gallerySection to viewport height with overflow:hidden.
   Base CSS already provides correct behavior:
   .discovery-trending-wrapper: sticky + overflow-y:auto.
   .discovery-trending-label: sticky top:0 inside wrapper.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 85 (FG Opus Claude)
   ============================================================
   Rev 1 — Trending label sticky offset fix.
   .discovery-trending-wrapper top: 1rem → 5rem,
   max-height: calc(100vh - 2rem) → calc(100vh - 5.5rem).
   Clears the 72px sticky site header so "TRENDING" label
   remains visible during scroll.
   ============================================================

   REVISION LOG — BATCH 86 (FG Opus Claude)
   ============================================================
   No changes to this file.
   8 audit fixes + 2 revisions in app.js. Auth hardening in
   get-ip and kemi-scan edge functions. Trending 24-hour window,
   initSite query resilience.
   ============================================================

   REVISION LOG — BATCH 87 (FG Opus Claude)
   ============================================================
   No changes to this file.
   6 fixes: revision log corruption (council/sovereign/egg/kemi-config),
   trending engagement filter + dead code (app.js), saveDailySnapshot
   409 fix (db.js), trending try/catch (app.js), duplicate gallery
   card IDs fixed in community + profile feeds (app.js).
   ============================================================

   REVISION LOG — BATCH 88 (FG Opus Claude)
   ============================================================
   Fix 3 — Dead .gallery-card-img CSS removed.
     .gallery-card-img base rule and trending wrapper scoped
     extension removed. Class no longer used after gallery branch
     in _renderPostAsFeedCard switched to .gallery-feed-card class
     with Discover-matched image inline styles.
   Also: 2 fixes in app.js (gallery card sizing restored via
   .gallery-feed-card class, comment button interaction restored).
   ============================================================

   REVISION LOG — BATCH 89 (FG Opus Claude)
   ============================================================
   No changes to this file.
   Trending 24-hour engagement cycle, story report card restructure,
   story notification verification, volume cap in app.js. Engagement
   log CRUD in db.js. SQL migration required for engagement_log table.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 90 (FG Opus Claude)
   ============================================================
   No changes to this file.
   FM closure notification gap fixed (5 paths in app.js).
   Community feed following data preserved across admin user reload.
   DB_GALLERY refreshed in 60-second sync loop.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 91 (FG Opus Claude)
   ============================================================
   No changes to this file.
   11 revisions in app.js: inline interactions, forum kind labels,
   report card fixes, image rendering, rating selector persistence,
   debate/story modal refresh, quote limit 200->500.
   Membership card removed from index.html.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 92 (FG Opus Claude)
   ============================================================
   Fix 6 — .violation-tag.ivai CSS rule removed (dead code;
   merchant protocol removed Batch 70, nothing sets class 'ivai').
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 93 (FG Opus Claude)
   ============================================================
   No changes to this file.
   Multi-page forum restructure, debate comment rendering
   restoration, polling re-render, session resilience in app.js.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 94 (FG Opus Claude)
   ============================================================
   No changes to this file.
   Fullscreen comment parity fixes in app.js.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 95 (FG Opus Claude)
   ============================================================
   Finding 6 — Stale architecture comment corrected: "sovereign/"
   path refs updated to "egg.js and sovereign.js", session
   persistence updated from "localStorage dms_session_userId"
   to "Supabase Auth JWT".
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 96 (FG Opus Claude)
   ============================================================
   No changes to this file.
   Batch 93 page structure reversion, _currentSwipePage per-post
   scoping, _storyRatings deprecation — all in app.js.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 97 (FG Opus Claude)
   ============================================================
   No changes to this file.
   Per-round debate sidebar, scroll preservation, init flow fix,
   session comment corrections, _storyRatings cleanup — app.js/db.js.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 98 (FG Opus Claude)
   ============================================================
   No changes to this file.
   5 audit fixes in app.js and db.js.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 99 (FG Opus Claude)
   ============================================================
   No changes to this file.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 100 (FG Opus Claude)
   ============================================================
   No changes to this file.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 101 (FG Opus Claude)
   ============================================================
   No changes to this file.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 102 (FG Opus Claude)
   ============================================================
   No changes to this file.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 103 (FG Opus Claude)
   No code changes. Revision log only.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 104 (FG Opus Claude)
   No code changes. Revision log only.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 105 (FG Opus Claude)
   No code changes. Revision log only.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 106 (FG Opus Claude)
   No code changes. Revision log only.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 107 (FG Opus Claude)
   Rev 5 — Removed hardcoded color:var(--parchment) from
     .profile-page-username CSS class. Username color now
     controlled entirely by inline getUserColor() style.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 109 (FG Opus Claude)
   No code changes. Revision log only.
   ============================================================ */
/* ============================================================
   REVISION LOG — BATCH 110 (FG Opus Claude)
   No code changes. Revision log only.
   ============================================================ */
/* ============================================================
   REVISION LOG — BATCH 111 (FG Opus Claude)
   No code changes. Revision log only.
   ============================================================ */
/* ============================================================
   REVISION LOG — BATCH 112 (FG Opus Claude)
   No code changes. Revision log only.
   ============================================================ */
/* ============================================================
   REVISION LOG — BATCH 113 (FG Opus Claude)
   No code changes. Revision log only.
   Points/Rewards settings, trending Load More spin, new forum
   categories, and 2 audit fixes all in app.js + index.html.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 114 (FG Opus Claude)
   No code changes. 3 dead variables removed from app.js.
   Checkbox labels aligned in index.html.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 115 (FG Opus Claude)
   Fix 6 — 3 unused CSS variables removed from :root.
     --gold-light (#f0c840), --white (#fdf8f0), --text-body
     (#2a2620) defined but never referenced in any var() call
     across styles.css, app.js, or index.html.
   CSS bracket balance: 487/487 (correcting v83/v84 claim of
     478/478 — v82's 487/487 was correct all along).
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 116 (FG Opus Claude)
   No code changes to styles.css.
   Discussion Forums (new forum kind) added to app.js.
   Open House expand/collapse added to report card.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 117 (FG Opus Claude)
   No code changes to styles.css.
   7 audit fixes in app.js for Batch 116 discussion forum integration.
   ============================================================ */

/* REVISION LOG — BATCH 118 (FG Opus Claude)
   No code changes. Revision log only.
   Rev 1: loadGalleryFeed decoupled from init Promise.all (app.js).
   Rev 2: Post-publish Add Documents for all forum kinds (app.js).
   Rev 3: Discussion FM panel sidebar Key Conversations tab (app.js).
*/

/* REVISION LOG — BATCH 119 (FG Opus Claude)
   No code changes. Revision log only.
   5 audit fixes in app.js: modal refresh, doc count, XSS escaping,
   behavioral documentation, stale comment update.
*/

/* ============================================================
   REVISION LOG — BATCH 120 (FG Opus Claude)
   No code changes. Revision log only.
   3 revisions in app.js: discussion comment input fix,
   discussion open reply access, discussion point dynamics.
*/

/* ============================================================
   REVISION LOG — BATCH 121 (FG Opus Claude)
   No code changes. Revision log only.
   5 audit fixes in app.js: discussion point qualification
   (savedPosts → followedForums), discussion report card branch,
   MAX_ACTIVE_FORUMS ×6, document URL _safeUrl, dead label branch.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 122 (FG Opus Claude)
   No code changes. Revision log only.
   2 audit fixes in app.js: dead 'Description' label branches
   (6 instances), Points/Rewards settings label 'save' → 'follow'.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 123 (FG Opus Claude)
   No code changes. Revision log only.
   5 revisions in app.js: Discussion menu Pin→Highlight,
   reply input + reply chain + focus preservation across poll refresh,
   community feed reply preservation, Key Conversation popup fix.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 124 (FG Opus Claude)
   No code changes. Revision log only.
   3 audit fixes in app.js: dead local variables removed from
   popup function, forum follow notification text "saves"→"follows",
   stale code comment "save"→"follow".
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 125 (FG Opus Claude)
   No code changes. Revision log only.
   3 revisions in app.js: reply input cleared before re-render to
   prevent save/restore persistence, trending card click handlers
   added (openGalleryOverlay/openPost), discussion sidebar flash
   on vote eliminated via early-return.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 126 (FG Opus Claude)
   No code changes. Revision log only.
   2 audit fixes in app.js: trending delegation check added img to
   prevent double modal on gallery image click; discussion report
   card qualification text "save (follow)" to "follow".

   REVISION LOG — BATCH 127 (FG Opus Claude)
   Rev 4 — overscroll-behavior: contain added to .modal-box.
   Prevents scroll bleed-through from any modal to the main page.
   Other revisions in app.js: scroll fix, ban enforcement, discussion
   panel continuation navigation, trending popup-only cards.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 128 (FG Opus Claude)
   No code changes. Revision log only.
   3 audit fixes in app.js: fullscreen ban filter (6 fix points),
   Key Conversation popup/sidebar ban filter, notification ban filter.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 129 (FG Opus Claude)
   Rev 1 — overflow: hidden added to .modal-overlay. Prevents
   scroll bleed-through from any modal to the main page body.
   Other revisions in app.js: openGalleryOverlay fix (3 sites),
   token color username restoration in _applyColorSchemeToPage.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 130 (FG Opus Claude)
   No code changes. Revision log only. Batch 128 header format
   corrected to established "REVISION LOG — BATCH 128" header.
   Fix 1 in app.js: openRepostModal → openQuoteRepost.
   ============================================================ */

/*  ============================================================
   REVISION LOG — BATCH 131 (FG Opus Claude)
   ============================================================
   No code changes. Revision log only.
   Rev 1: Polling cycle cleanup (app.js). Rev 2: SQL migration.
   Rev 3-4: forum_continuation handler + icon (app.js).
   Rev 5: Discussion report card redesign (app.js).
   Rev 6: _reconcileForumClosure guard fix (app.js).
   ============================================================ */

/*  ============================================================
   REVISION LOG — BATCH 132 (FG Opus Claude)
   ============================================================
   No code changes. Revision log only.
   Fix 1: Dead function removal (app.js). Fix 2: Discussion point
   award logic aligned with report card (app.js). Fix 3: _checkFsdExpiry
   restored to init path (app.js). Fix 4: _checkDebateNoticeTimeouts
   restored to init path (app.js). Fix 5: Documentation fix.
   ============================================================

   ============================================================
   REVISION LOG — BATCH 133 (FG Opus Claude)
   ============================================================
   No code changes. Revision log only.
   Rev 1: Retroactive forum_continuation notifications (app.js).
   No styles.css changes.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 134 (FG Opus Claude)
   ============================================================
   No style changes. Revision log only.
   Rev 1-5: Continuation notification dedup, reconciliation point awards,
   init-path guards, polling cycle purity (app.js). No styles.css changes.
   ============================================================ */
/* ============================================================
   REVISION LOG — BATCH 135 (FG Opus Claude)
   Rev 3 — Active community card:
     .feed-sidebar: position:sticky, top:5rem, max-height,
     overflow-y:auto, overscroll-behavior:contain. Independent
     scroll with thin scrollbar. Responsive override at 1024px:
     position:static, max-height:none.
     #activeMemberList scrollbar styling added.
     .member-tier rule removed (tier labels no longer displayed).
   ============================================================ */

/* REVISION LOG — BATCH 136
   No code changes. Revision log only. */
/* REVISION LOG — BATCH 137
   No code changes. autarchPinned infrastructure restored in app.js.
   Dead openNewBookListing removed from app.js. */

/* ============================================================
   REVISION LOG — BATCH 138 (FG Opus Claude)
   ============================================================
   No changes to this file.
   Autarch's Choice rework, interaction buttons, book sub-category,
   trending fallback, and sidebar info in app.js/db.js/index.html.
   ============================================================ */

/* REVISION LOG — BATCH 139 (FG Opus Claude)
   No code changes. Revision log only.
*/

/* REVISION LOG — BATCH 140 (FG Opus Claude) */
/* No code changes. Revision log only. */

/* REVISION LOG — BATCH 141 (FG Opus Claude)
   No changes to this file. _safeUrl completion, _esc defense-in-depth,
   announcement banner ordering fix in app.js.
*/

/* REVISION LOG — BATCH 142 (FG Opus Claude) */
/* No code changes. Revision log only. */
/* See app.js for Batch 142: 4 revisions. */

/* REVISION LOG — BATCH 143 (FG Opus Claude) */
/* No code changes. Revision log only. */
/* app.js: Fix 1 (_syncBodyOverflow), Fix 2 (dead openNewPostSelector removed), */
/*   Fix 3 (dead submitBookListing removed), Fix 4 (trending preload sort aligned). */
/* index.html: dead newPostSelectorModal + newBookListingModal HTML removed. */

/* REVISION LOG — BATCH 144 (FG Opus Claude) */
/* Rev 2 — .sticky-modal-bar and .sticky-modal-header CSS classes for opaque */
/*   sticky sections. background:var(--ash-dark) !important. */
/* Rev 4 — .autarch-choice-card + .post-tag sub-rule replaced with */
/*   #autarchChoiceCard vermilion styling. */
/* Rev 6 — .forum-square-card: position:relative added for A/star button. */
/* REVISION LOG — BATCH 145 (FG Opus Claude) */
/* No code changes. CSS 495/495 unchanged. See app.js revision log. */
/* REVISION LOG — BATCH 146 (FG Opus Claude) */
/* Fix 1 — .sticky-modal-bar background changed from var(--ash-dark) to var(--ash). */
/*   .sticky-modal-header background changed from var(--ash-dark) to var(--ash). */
/*   .comment-input-area margin-top reduced from 1rem to 0.25rem. */
/*   Bars remain opaque (Rule 2.28 satisfied); color now matches modal background. */
/* REVISION LOG — BATCH 147 (FG Opus Claude) */
/* No changes to styles.css. Audit-driven fixes in app.js only. */

/* ============================================================ */
/* REVISION LOG — BATCH 148 (FG Opus Claude) */
/* ============================================================ */
/* Rev 1 — .sticky-modal-bar padding-bottom 0.5rem → 2.5rem. */
/*   Full-coverage opacity: bar now covers modal-box 2rem bottom */
/*   padding zone. Background unchanged (var(--ash) !important). */
/* ============================================================ */

/* REVISION LOG — BATCH 149 (FG Opus Claude) */
/* No code changes. 7 audit-driven fixes in app.js only. */

/* REVISION LOG — BATCH 150 (FG Opus Claude) */
/* Rev 2 — .sticky-modal-bar and .sticky-modal-header: added */
/*   z-index:2 and transform:translateZ(0) for compositing layer. */
/* Rev 4d — CSS gate: html:not(.has-session) rules for */
/*   #forumSection, #shopSection, #announcementBanner. */
/* Rev 6 — .brand-name font-size 1.1rem→1.6rem, font-weight:700. */

/* REVISION LOG — BATCH 151 (FG Opus Claude) */
/* Fix 1 — Dead .brand-sub CSS class removed. HTML element removed */
/*   Batch 150 Rev 6. CSS bracket balance: 497/497. */

/* ============================================================ */
/* REVISION LOG — BATCH 152 (FG Opus Claude) */
/* Rev 1 — .brand-sub CSS class restored. .brand-full/.brand-short */
/*   containers added. html.has-session toggle rules for conditional */
/*   header brand (logged-out: full, logged-in: DMS.). */
/*   CSS bracket balance: 503/503. */
/* ============================================================ */

/* ============================================================ */
/* REVISION LOG — BATCH 153 (FG Opus Claude) */
/* ============================================================ */
/* No code changes to this file. Batch 153: 4 audit-driven fixes */
/* in app.js — video URL domain allowlist, video caption keyword */
/* scanning, video rendering in 5 additional gallery contexts, */
/* interaction count consistency in 2 contexts. */
/* ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 154 (FG Opus Claude)
   ============================================================
   No code changes to this file. Image height constraints applied
   via inline styles in app.js (max-height:500px;object-fit:contain).
   CSS bracket balance: 503/503 unchanged.
   ============================================================ */

/* REVISION LOG — BATCH 155 (FG Opus Claude) */
/* No code changes. CSS bracket balance: 503/503. */

/* REVISION LOG — BATCH 156 (FG Opus Claude) */
/* No code changes to this file. CSS bracket balance: 503/503. */
/* Batch 156: 5 fixes in app.js — AC sidebar video rendering, sticky */
/* comment bar compositing, video playback state management (3 sub-fixes), */
/* gallery image letterboxing, performance (DB_USER_MAP O(1) lookups). */

/* Batch 157 — No code changes. Revision log only. */

/* Batch 158 — No code changes to this file.
   Rev 1: Gallery image rendering restored (app.js only).
   Rev 2: RLS policy drop (SQL, no file changes).
   Rev 3: Session lock refresh redirect (app.js only). */

/* REVISION LOG — BATCH 159 (FG Opus Claude) */
/* Fix 3 — 27 JS-style (//) comments from Batches 149-153 and 157 */
/*   converted to proper CSS comments. Zero remaining // comments. */
/*   CSS bracket balance: 503/503 (unchanged). */

/* ============================================================
   REVISION LOG — BATCH 160 (FG Opus Claude)
   ============================================================
   Rev 3: .post-card-image-wrap max-height changed from 500px
     to 600px for forum post images in community feed. Forum tab
     rendering unchanged. Gallery/discover images use inline
     aspect-ratio:1/1 square format (app.js changes).
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 161 (FG Opus Claude)
   ============================================================
   No code changes to this file.
   See app.js for Batch 161: 6 audit-driven fixes (crop tool
   listener leak, comment max-height consistency, CORS canvas
   try/catch, profile feed block filter, notification panel
   block filter).
   CSS bracket balance: 503/503 (unchanged).
   ============================================================ */

/* ============================================================
   BATCH 162 — REVISION LOG (FG Opus Claude)
   ============================================================
   No code changes. Revision log entry only.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 163 (FG Opus Claude)
   ============================================================
   Added Saint angel-wings badge styling: .saint-winged (inline-flex wrapper),
   .saint-wing (em-scaled gold wing image), .saint-wing-left / .saint-wing-right
   (the right wing mirrored via scaleX(-1)). Wings flank the wrapped points-badge.
   CSS bracket balance: 507/507.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 164 (FG Opus Claude)
   No code changes to this file. Batch 164 (privilege restructure + audit
   Findings 2 and 8) is in app.js, db.js, and the SQL migration. CSS 507/507.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 165 (FG Opus Claude)
   No code changes to this file. Batch 165 (audit-driven fixes: Problem 2/3/4/5)
   lives in app.js, db.js, and supabase-batch165-migration.sql. A1 struck by the
   Autarch. CSS 507/507.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 166 (FG Opus Claude)
   Added .magisterium-universal-card (+ .post-tag variant): ultraviolet tint for a
   Magisterium member's deliberate universal post. Mirrors the council-favorite-card
   idiom. Applied via renderPostCard (category 'universal' + majesty author). The
   Saint "St." honorific (Batch 166) is text-only in app.js — no CSS. CSS 509/509.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 167 (FG Opus Claude)
   Added .forum-kind-tabs: the forum kind-filter tab bar made sticky + fully
   opaque (Rule 2.28). top:72px (flush beneath the 72px .site-header), opaque
   --ink background (page surface — not translucent/darker, Batch 146),
   transform:translateZ(0) compositing layer (Batch 150). Added #forumSection
   position:relative;z-index:1 so scrolled cards resolve below the bar's z-index:2
   (Batch 156). Header height stable across media queries (no responsive gap).
   Two new rules added: CSS 509/509 -> 511/511. No other CSS touched this batch.
   ============================================================ */


/* ============================================================
   REVISION LOG — BATCH 168 (FG Opus Claude)
   No code changes to this file. Batch 168 (fixes for the eight Batch 167 audit
   findings) is app.js + SQL only. CSS bracket balance unchanged at 511/511.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 169 (FG Opus Claude)
   REV 1 — Added the Collective Board two-column admin workspace styles:
   .admin-workspace (flex, side-by-side at all widths), .admin-collective-board
   (clamp(280px,30vw,420px), collapsible), .admin-tab-column, .admin-board-
   collapse-btn, .admin-board-spine (+ -arrow / -label, vertical writing-mode),
   and the .collapsed state (shrinks to spine, hides content + chevron). No
   responsive stacking — horizontal collapse is the small-screen affordance.
   12 rules added. CSS bracket balance 511/511 -> 523/523.
   REV 2 — No CSS changes for the membership-wording revision.
   ============================================================ */

/* ============================================================
   Batch 170 — No change to this file. Audit fixes were F-1
   (award_points_rpc whitelist completeness, in
   supabase-security-migration.sql) and F-2 (a Collective Board
   comment correction in app.js). No CSS changed. Bracket balance
   unchanged at 523/523.
   ============================================================ */

/* ============================================================
   Batch 171 — No change to this file. The Rev 1–5 sweep is in
   app.js / council.js / kemi-index.ts. Rev 3 (Reports section) and
   Rev 4 (relocated Applications card) reuse existing classes
   (.admin-section, .admin-scrollable-window, .violation-row,
   .violation-tag, .admin-metric-card, .stat-grid) — no new CSS,
   no rule removed. Bracket balance unchanged at 523/523.
   ============================================================ */

/* ============================================================
   Batch 172 — No change to this file. Audit fixes A–G touched app.js,
   council.js, index.html, kemi-index.ts, and supabase-security-migration.sql.
   Finding C removed a dead modal's markup (index.html) but it reused existing
   classes, so no CSS rule was orphaned or added. Bracket balance unchanged
   at 523/523.
   ============================================================ */

/* ============================================================
   Batch 173 — No change to this file. The Metra-Expanse + dead-composer
   teardown removed JS/markup only; the .book-card and comment classes the
   Metra cluster had reused remain in use by live features (shop/books,
   gallery/forum comments), so no CSS rule was orphaned. Bracket balance
   unchanged at 523/523.
   ============================================================ */

/* ============================================================
   Batch 174 — Post-card expansion fix (Autarch directive).
   Symptom: opening a post's three-dot (.post-menu) appeared to grow the
   post card. Root: .post-menu is position:absolute, but its nearest
   positioned ancestor was .post-card; with .post-card no longer clipping
   (overflow:hidden removed in an earlier pass) the tall dropdown painted
   past the short card's lower border, reading as expansion.
   Fix: added position:relative to .post-menu-btn so the absolute .post-menu
   anchors to the small kebab BUTTON and overlays as a true floating dropdown
   rather than appearing to extend the card body. Applied via the shared class,
   so it is retroactive across all 20 .post-menu render sites (Rule 2.21). The
   opened-post path already wraps button+menu in its own absolute container, so
   re-anchoring to the button is consistent there too.
   (An initial flex-wrap hypothesis was applied then fully reverted on
   re-analysis before the correct fix — no speculative rule left behind.)
   One declaration added; bracket balance unchanged at 523/523.
   ============================================================ */

/* ============================================================
   Batch 175 — No styles.css change. R1 report-persistence (db.js
   Prefer:return=minimal on kemi_flags insert), R2 dossier scroll-
   restore on the real .modal-box container (app.js), R3 Site Stats
   real-time after a verdict (app.js), R4 duplicate-correspondence
   guard (app.js), R5 commission 10% (app.js + supabase-patron-
   migration.sql), R6 patron tiers 2.75/3.50/4.25/5.00 (app.js) are
   logic/data only. Verified no stylesheet impact. Bracket balance
   unchanged at 523/523. No new rule established.
   ============================================================ */
/* ============================================================
   Batch 176 — No styles.css change. Tip-commission split is logic/data in
   app.js (_submitTip) and db.js (createTip) + a tips-table SQL migration.
   Bracket balance unchanged at 523/523. Rule 2.32 established (Outstanding-
   list discipline). Set is now 2.1–2.32.
   ============================================================ */
/* ============================================================
   REVISION LOG — BATCH 177 (FG Opus Claude)
   No styles.css change. The four Batch 177 defect fixes are app.js logic; the
   Defect 4 CODE FAULT panel uses inline styles, adding no new selectors.
   Bracket balance unchanged at 523/523. Set remains 2.1–2.32.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 178 (Audit-fix pass)
   No change to styles.css. Audit fixes are in app.js / council.js / batch169
   SQL. CSS bracket balance unchanged. Set remains 2.1–2.32.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 179
   No change to styles.css. Batch 179 changes are app.js / db.js / council.js
   logic; the Autarch Delete menu entry reuses existing .post-menu styling.
   CSS bracket balance unchanged (523/523). Set remains 2.1-2.32.
   ============================================================ */

/* ============================================================
   Batch 180 — No change to styles.css. Post-audit fixes (F1-F4) are app.js /
   db.js logic; the trending gallery Delete menu entry reuses existing .post-menu
   styling. CSS bracket balance unchanged (523/523). Set remains 2.1-2.32.
   ============================================================ */

/* ============================================================
   Batch 181 — No change to styles.css. All five Batch 181 changes (R1 image-post
   Delete gates, R2 verdict-commit scoping, R3 dossier scroll, R4 overlay z-index,
   R5 admin-panel redesign) are app.js logic; the added Delete entries and the
   library filter menu reuse existing .post-menu styling and overlay elevation is
   set inline at runtime. CSS bracket balance unchanged (523/523). Set remains 2.1-2.32.
   ============================================================ */

/* ============================================================
   Batch 182 — No change to styles.css. Post-audit cleanup (dead z-index writes,
   dead vars/function removal) is app.js logic; the SECURITY DEFINER search_path
   pin is in supabase-patron-migration.sql. No styling change. CSS bracket balance
   unchanged (523/523). Set remains 2.1-2.32.
   ============================================================ */

/* ============================================================
   Batch 183 — No change to styles.css. The Batch 183 revisions are app.js logic;
   the report-modal verdict apparatus reuses existing classes (verdict-gavel-btn,
   verdict-selected, btn-primary/btn-ghost/btn-danger, user-popup-section-title).
   No styling change. CSS bracket balance unchanged (523/523). Set remains 2.1-2.32.
   ============================================================ */

/* Batch 184 — No change to styles.css. Batch 184 fixed two app.js/db.js defects (F1 Death Row population gap; F2 forum post/comment edit + delete handlers); F2 reuses existing comment-edit and button classes. Log entry per Rule 2.5. Set remains 2.1-2.32. */

/* Batch 185 — No change to styles.css. Batch 185 R5 (History section + Re-Open Case modal) reuses existing classes only: violation-subblock, violation-tag, violation-preview-box, admin-scrollable-window, user-popup-section-title, verdict-gavel-btn, verdict-selected, modal-overlay/box/close/title, btn-ghost/danger/primary — all verified present. R1-R4 add no markup. CSS bracket balance unchanged. Log entry per Rule 2.5. Set remains 2.1-2.32. */

/* ============================================================
   REVISION LOG — BATCH 186 (FG Opus Claude)
   No code change. Batch 186 full-codebase audit; fixes landed in app.js,
   index.html, get-ip-index.ts, supabase-security-migration.sql. The Re-Open
   modal and History section (Batch 185 R5) reuse existing classes — all
   confirmed present, none added/removed. Rule set remains 2.1-2.32.
   ============================================================ */

/* ============================================================
   BATCH 187 — styles.css (R2, R3, R5).
   R5 — .toast-center + @keyframes centerToastIn (center-screen "Duplicates Cleared"
     toast, distinct from the corner .toast).
   R3 — .trending-refresh-btn (+ :hover, + .trending-refreshing fill/spin) and
     @keyframes trendingSpin.
   R2 — .load-new-discover-btn (+ :hover, + .discover-loading-new spin), reusing
     trendingSpin. No existing rule modified or removed. Rule set remains 2.1-2.32.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 188 (FG, audit-driven fixes)
   ============================================================
   No style change. Batch 188 fixed app.js audit findings F1/F2/F4 (logic only) and
   added an F2 retroactive detection diagnostic to supabase-schema.sql. No CSS rule
   added, modified, or removed. Rule set remains 2.1-2.32.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 189 (FG, Autarch-directed UI/integrity fixes)
   ============================================================
   Batch 189 (R1 verdict/judgement relabel; R2 badge counts = exact distinct holders;
   R3 Badge Distribution active-tab persistence; R4 AC forced-expand page-wipe
   resilience) is confined to app.js. R5 is an Autarch infra action (gallery_posts
   57014 timeout). No CSS rule added, modified, or removed. Rule set remains 2.1-2.32.
   ============================================================ */

/* ============================================================
   PENAL GUIDE ICON — Batch 191 (Autarch-directed)
   Paperwork/clipboard icon beside a violation code on the report
   investigation modal and the dossier violation card. Glows blue until
   its guide is first opened this session (.penal-guide-glow); after
   opening it stays clickable without the glow.
   ============================================================ */
.penal-guide-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius, 6px);
  background: var(--ash-mid);
  color: var(--sepia-light);
  cursor: pointer;
  vertical-align: middle;
  transition: background 0.15s, color 0.15s, box-shadow 0.2s, border-color 0.2s;
}
.penal-guide-icon:hover {
  color: var(--parchment);
  border-color: var(--border-strong);
  background: #46403600;
  background: var(--ash-mid);
}
.penal-guide-icon.penal-guide-glow {
  color: #7fb6ff;
  border-color: rgba(90, 150, 255, 0.6);
  box-shadow: 0 0 6px rgba(90, 150, 255, 0.55), 0 0 12px rgba(90, 150, 255, 0.35);
  animation: penalGuidePulse 1.8s ease-in-out infinite;
}
@keyframes penalGuidePulse {
  0%, 100% { box-shadow: 0 0 5px rgba(90, 150, 255, 0.45), 0 0 10px rgba(90, 150, 255, 0.25); }
  50%      { box-shadow: 0 0 9px rgba(90, 150, 255, 0.75), 0 0 18px rgba(90, 150, 255, 0.5); }
}

/* ============================================================
   SANCTION BUTTON — Batch 191 (Action Report)
   Green = not sanctioned (clickable to open the duration dropdown).
   Red   = active sanction (inert; duration auto-lifts it).
   ============================================================ */
.sanction-btn {
  font-family: 'Cinzel', serif;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  padding: 0.4rem 0.9rem;
  border-radius: var(--radius, 6px);
  cursor: pointer;
  transition: box-shadow 0.2s, background 0.15s, color 0.15s;
}
.sanction-btn.sanction-green {
  background: rgba(46, 107, 58, 0.18);
  color: #7ec98a;
  border: 1px solid rgba(90, 200, 120, 0.5);
  box-shadow: 0 0 6px rgba(70, 200, 110, 0.45);
}
.sanction-btn.sanction-green:hover {
  box-shadow: 0 0 10px rgba(70, 200, 110, 0.7);
}
.sanction-btn.sanction-red {
  background: rgba(224, 96, 96, 0.18);
  color: #e89090;
  border: 1px solid rgba(224, 96, 96, 0.6);
  box-shadow: 0 0 6px rgba(224, 96, 96, 0.5);
  cursor: default;
}

/* ============================================================
   REVISION LOG — BATCH 190 / 191 (FG, Autarch-directed)
   ------------------------------------------------------------
   Batch 190: no styles.css change (app.js-only audit fixes).
   Batch 191 (CSS CHANGE in this file):
     - .penal-guide-icon + .penal-guide-glow (paperwork icon beside a violation
       code; pulsing blue glow until its guide is first opened this session).
     - .sanction-btn / .sanction-green / .sanction-red (Action Report sanction
       control: green = not sanctioned, red = active sanction).
   No existing rule modified or removed. Rule set 2.1-2.32.
   Batch 192: no styles.css change. Findings A (SQL: user_sanctions table + RLS) and
   C (app.js report-cap parameter fix) add no styling. The existing .sanction-* rules
   above now have a working backing table. Rule set 2.1-2.32.
   Batch 193 (R2, R5 styling): R2 — .load-new-discover-btn.discover-loading-new now
   glows gold (box-shadow) in addition to the spin; this loading state is shared by the
   Discover and Community load-more icons. R5 — added .violation-check-row / .violation-check
   / .violation-check-box (empty outline → fills solid white when ticked) and the
   .violation-check-bitch red-tinted variant for the standalone B.I.T.C.H ban override.
   No existing rule removed. Rule set 2.1-2.32.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 194 (FG audit-fix round)
   ============================================================
   No styles.css change this batch. The audit-fix round touched app.js (Findings 1, 2,
   4, 5), supabase-schema.sql + supabase-auth-rls.sql (Findings 2, 3), and index.html
   (Finding 6 — documented the codeOnlyOverlay/session-warning-* class carryover; the
   session-warning-* rules here are retained intentionally, used only by that one card).
   Rule set 2.1-2.32.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 195 (FG feature/defect round, revs 1–11)
   ============================================================
   styles.css carries REV 2 and REV 10.
   REV 2 — .user-lib-badge.report-line: the User Library report badge drops to its own line
     below the strike badge and reads as one pill ("1 Report" / "2 Reports"); inline-block,
     margin-left:0, small margin-top. Previously the orange report badge crowded the yellow
     strike badge inline, wrapping the label awkwardly.
   REV 10 — .toast-verdict (+ .guilty / .acquit modifiers): centered verdict-confirmation toast
     at z-index 10002 so it sits ABOVE the dossier/report modals (10000) and overlays (10001);
     the old corner .toast (9999) was hidden behind the open window. Guilty tints crimson,
     acquit tints green, neutral stays sepia. Non-interactive.
   Rule set 2.1-2.32.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 196 (FG audit-fix round; Autarch-directed)
   ============================================================
   No styles.css change this batch. The round is a single security-hardening fix in app.js (S1):
   the WSM menu items escape post.id and the topic key via the new _jsq() helper before
   interpolating into the inline reportWSM onclick. No selectors, classes, or rules added or
   removed. Rule set 2.1-2.32.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 197 (FG; Autarch-directed "Proceed with the revisions")
   R3 — Added .brand-tab-link (About/App header tabs beside the wordmark) and .about-body
   (About-section prose layout). R5 — .discovery-trending-label given matching top border-radii
   so scrolling posts no longer bleed through the seam at the top of the Trending bar; no other
   selectors/rules added or removed. All other R-items were app.js/index.html. Rule set 2.1-2.32.
   ============================================================ */

/* ============================================================
   BATCH 198 — AUDIT-FIX ROUND (no code change to this file)
   ============================================================
   Three audit findings fixed, all in app.js: (1) SEC-1 — systemic _jsq() escaping of
   every inline-handler JS-string interpolation (549 wrapped, 0 raw remaining; latent,
   not exploitable in current data; render-time, retroactive); (2) dead-code removal of
   flagAsAI(); (3) B197 convention gap folded into the SEC-1 pass. No SQL this batch.
   No new numbered rule (set remains 2.1-2.32). Outstanding code defects: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 209 (FG audit-fix round; Autarch-directed "Proceed with the fixes")
   ============================================================
   No styles.css change this batch. Batch 209 fixed one verified Outstanding code defect in app.js
   (audit Finding 1): the Struck Users — Daily Record drilldown bucketed strikes by raw UTC date while
   Batch 208's _contiguousDateKeys walk emits Arizona-local (_azDateKey) day keys, so strikes
   timestamped 00:00-06:59 UTC rendered "0 users struck" on their real Phoenix day. Fixed by keying
   _struckUsersByDate and the struck metric-card today/yesterday lookups on _azDateKey (one shared key
   space); the posts/members UTC deltas were preserved; retroactive by construction. The audit's other
   surfaced items were correctly not actioned (2.14). Logged per 2.5. No SQL. Outstanding defects: NONE.
   ============================================================
   ============================================================
   REVISION LOG — BATCH 208 (FG; Autarch-directed "Proceed with the revisions")
   ============================================================
   Two CSS defects fixed this file (Items 1-2 of five; Items 3-5 were app.js).
   Item 1 — Sticky comment-bar bottom bleed. A sticky bottom:0 bar pins at its scroll
   container's CONTENT-box edge; when that container has bottom padding the bar sits above the
   painted bottom and content scrolling in the strip bleeds through beneath the bar. The shipped
   padding-bottom:2.5rem only pads the bar's content UP (proven insufficient — it was already
   present and the bleed persisted). Added margin-bottom:-2.5rem to .sticky-modal-bar: drops the
   painted box so its opaque --ash fill covers the strip; padding+margin cancel in layout
   (footprint unchanged). Seals notif-detail window, thread modal, gallery overlay, sidebar viewers
   in one rule. Scroll-axis overflow on overflow-y:auto parents is scrollable, not clipped.
   Item 2 — Trending section-label top bleed. The label stuck to top:0 (the wrapper content-box
   top, inside its 1px border), leaving the border band above where a scrolling card flashed
   through before pinning; the prior margin-top:-1px was sub-pixel only. Set top:-1px (anchor at the
   border edge) + margin-top:-2px (bleed the fill up over the band, scroll axis, not clipped).
   Removed the now-stale top:0. CSS braces 572/572. No new rule. Outstanding code defects: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 207 (FG audit-fix round; Autarch "Proceed with the fixes")
   ============================================================
   No styles.css change this batch. Batch 207 fixed seven findings in app.js: the residual overlay
   reply-chain auto-open defect (Items 1-2), removed the dead mc-row- selector (Item 4), three
   openProfile _jsq escaping sites (Items 5-7), and aligned the overlay comment sort to the canonical
   oldest-first order (Item 3, Autarch-reversible). Item 2 (feed-header centering, B205) REMAINS
   deferred pending the Autarch's A/B ruling — not touched. Logged here per 2.5. No SQL.
   Outstanding code defects: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 206 (FG audit-fix round; Autarch "Proceed with the fixes")
   ============================================================
   No styles.css change this batch. Batch 206 completed the Batch 205 overlay-scoping guard
   across all id-colliding gallery comment controls and fixed the delete re-render/orphan and
   reply count-badge defects (app.js, Items 1-6). Logged here per 2.5. No SQL. Item 2
   (feed-header centering, the styles.css item) REMAINS deferred pending the Autarch's A/B
   ruling — not touched. Outstanding code defects: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 205 (FG revision round; Autarch "Proceed with the revisions")
   ============================================================
   No styles.css change this batch. Batch 205 threaded the gallery post overlay's comments
   + added a working Reply path (app.js, Item 3), and folded the live gallery composite index
   into supabase-schema.sql (Item 1). Item 2 (feed-header centering, the styles.css item)
   is DEFERRED pending the Autarch's A/B ruling and was deliberately NOT applied (2.1 — no
   unauthorized aesthetic pick). Logged here per 2.5. No SQL produced this batch.
   No new numbered rule (set remains 2.1-2.32).
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 204 (FG audit-fix round; Autarch "Proceed with the fixes")
   ============================================================
   No styles.css change this batch. Batch 204 fixed six code defects from the Batch 203
   audit across app.js (F2, F3, F7, F8) and db.js (F1, F9). Logged here per 2.5.
   No SQL this batch. No new numbered rule (set remains 2.1-2.32). Outstanding code defects: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 203 (FG revision round; Autarch "Proceed with the revisions")
   ============================================================
   styles.css change this batch:
   R4 — .feed-header changed from a horizontal flex row (justify-content:space-between)
        to a centered vertical column (flex-direction:column; align-items:center;
        text-align:center), so the "Community Feed" label sits centered atop the main
        feed column with the filter chips centered directly beneath it. .feed-filters
        gained justify-content:center; .section-title bottom margin zeroed within
        .feed-header only (no change to the global .section-title rule). Layout-only;
        the Load New Posts control (already centered) and the post feed are untouched.
   No SQL this batch. No new numbered rule (set remains 2.1-2.32). Outstanding code defects: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 199 (FG feature/defect round; Autarch "Proceed with the revisions")
   ============================================================
   styles.css changes this batch:
   R1 — .brand-home-link { cursor:pointer } + hover color on .brand-name (wordmark reads
        as a clickable tab to the landing page).
   R4 — .about-body p { text-indent: 2em } (first-line indent on every About paragraph).
   R5 — .about-body p.about-signature (right-aligned italic Cinzel sepia, no indent) for
        the "—The Autarch." line.
   No SQL this batch. No new numbered rule (2.1-2.32). Outstanding code defects: NONE.
   ============================================================
   REVISION LOG — BATCH 202 (FG audit-fix round; Autarch "Proceed with the fixes")
   ============================================================
   styles.css change this batch:
   R1 — .discovery-trending-label corner-shoulder seal corrected. The B201 side-bleed
        (margin-left/right -1px + side padding + box-sizing) was UNREACHABLE: the wrapper is
        overflow-y:auto, which computes overflow-x to auto as well, so the -1px side margins were
        clipped by the scroll container and never reached the corner arcs. The reliable seal needs
        no side bleed — the wrapper's own overflow clip rounds every scrolling card to its shape
        (cards cannot paint into the corner arcs) and this label's matched top radii + opaque
        --ash-mid fill + z-index:6 cap the top band. Kept margin-top:-1px (scroll-axis seam closure,
        not clipped sideways); REMOVED the side margins, side padding, and box-sizing.
   (Other findings A/B/C/6 are app.js; R6 z-index correction is inline in index.html.)
   No SQL this batch. No new numbered rule (2.1-2.32). Outstanding code defects: NONE.
   ============================================================
   REVISION LOG — BATCH 201 (FG feature/defect round; Autarch "Proceed with the revisions")
   ============================================================
   styles.css changes this batch:
   R1 — .discovery-trending-label: sealed the Trending sticky-bar bleed. B197 R5 covered only
        the flat top edge; content still bled through the wrapper's rounded-corner shoulders and
        the seam above the sticky as cards scrolled under it (--ash-mid was already opaque — the
        gap was geometric). Now bleeds the opaque label to the wrapper's inner edges (margin-top
        -1px to close the seam; margin-left/right -1px + matching side padding to cover the 8px
        corner arcs; box-sizing:border-box so the text does not shift) and raised z-index 2 -> 6
        above any scrolling card.
   (R6 — the Discover search bar sticky treatment was applied inline in index.html
        #discoverySearchBar: position:sticky; top:72px; opaque --ash bg; z-index above the gallery.
        Noted here for cross-reference; no rule added to styles.css for it.)
   No SQL this batch. No new numbered rule (2.1-2.32). Outstanding code defects: NONE.
   ============================================================
   REVISION LOG — BATCH 200 (FG audit-fix round; Autarch "Proceed with the fixes")
   ============================================================
   styles.css change this batch:
   Finding 4 — .brand-home-link:hover .brand-name color changed --sepia -> --sepia-light.
        The brand name rests at --sepia (see .brand-name), so the B199 hover-to-sepia was a
        no-op; --sepia-light is a visibly brighter on-theme highlight giving real hover
        feedback. (The other seven Batch-200 findings are in app.js.)
   No SQL this batch. No new numbered rule (2.1-2.32). Outstanding code defects: NONE.
   ============================================================
   ============================================================
   REVISION LOG — BATCH 217 (Autarch: "Proceed with the revisions")

   No styles.css change this batch. Batch 217 (registration redesign) is confined
   to app.js (token-picker removal, typed-DOB parsing, the confirmation-gated flow
   + post-confirmation overlay), index.html (picker removal, DOB input type), db.js
   (detectSessionInUrl), and a new Edge Function (send-access-code-index.ts). The
   post-confirmation overlay (regFlowModal) reuses existing .modal-overlay/.modal-box
   classes — no new CSS needed. Set remains 2.1-2.32. Outstanding code defects: NONE.
   REVISION LOG — BATCH 216 (Autarch: "No, just do it. Seamless implementation; no hasty work.")

   No styles.css change this batch. Batch 216 is Stripe Phase 2 (the front-end): the
   Stripe Modal + all charge-surface rewiring live in app.js; the signupStep2 raw card
   fields are removed in index.html; the users_public rebuild is in the security SQL.
   NOTE FOR FUTURE PALETTE WORK: the Stripe Payment Element renders inside Stripe's
   iframe where CSS variables are unreachable, so app.js _stripeAppearance() MIRRORS
   the :root palette values (#c9a96e sepia, #2a2620 ash, #1e1b17 ash-dark, #f5eedf
   parchment, #c0392b crimson-light, #7a6e5f text-muted, #5a5248 ash-light, 4px
   radius, the rgba(201,169,110,...) borders) — if :root changes, that map must move
   in lockstep. Set remains 2.1-2.32. Outstanding code defects: NONE.
   REVISION LOG — BATCH 215 (Autarch rulings: grace period + cards-on-file; Phase 1)

   No styles.css change this batch. Batch 215 is the Stripe Phase 1 backend: five new
   Edge Functions + SQL folded to source-of-truth (guarded users stripe columns,
   service-role-only billing RPC, stripe_payment_intent_id provenance columns).
   Front-end (Phase 2) not started. Set remains 2.1-2.32.
   REVISION LOG — BATCH 214 (Autarch "Proceed with the revisions")

   No styles.css change this batch. Batch 214 (fee-on-top processing-fee disclosure)
   is inline-styled markup in app.js (tip-menu fee labels + footer, patron-tier fee
   lines, past-volumes fee line, purchase-button copy) and index.html (membership
   $5.46/mo charged copy, reusing the existing .form-note class). No new selectors.
   No SQL. Set remains 2.1–2.32. Outstanding code defects: NONE.
   ============================================================
   REVISION LOG — BATCH 213 (Autarch "Proceed with the revisions")

   No styles.css change this batch. Batch 213 R2 reuses the existing .sticky-modal-bar
   class (Batch-208 opacity/edge-seal) unchanged — the fix is in app.js, removing the
   nested 250px inner-scroll wrapper in the non-forum _buildCommentSection branch so the
   sticky bar anchors to the modal scrollport. The class rule was NOT modified. R1a is a
   SQL admin-gate; R1b (update_user_badge_rpc forgery hardening) shipped in the same SQL
   file. About-page copy edits in index.html (no styles.css change). Brace balance unchanged
   574/574. Set
   remains 2.1–2.32.

   REVISION LOG — BATCH 212 (Autarch "Proceed with the revisions")
   ============================================================
   No styles.css change this batch — Batch 212 is a render-layer fix in app.js only: the pop-up
   comment composer is now the fixed bottom row of its flex column across all four forum kinds
   (debate, story, discussion, open_house) and the notif-detail/Announcement comment thread
   (min-height:0 on the scroll box so it scrolls internally; bar position:sticky -> flex-shrink:0).
   The .sticky-modal-bar class rule was NOT modified. Brace balance unchanged 574/574.
   No SQL this batch. No new numbered rule (set remains 2.1-2.32). Outstanding code defects: NONE.
   ============================================================
   REVISION LOG — BATCH 211 (Autarch "Proceed with the fixes")
   ============================================================
   styles.css change this batch:
   Finding 1 (dead code) — the .author-controls rule (===== AUTHOR CONTROLS ===== section)
        was retired to a provenance comment. Its only two consumers (#authorControls,
        #loreKeeperControls) were empty permanently-hidden divs, removed from index.html this
        batch along with their inert updateUI ac/lc toggles in app.js. No element carries the
        class any longer. Brace balance re-counted 574/574 (the commented rule's braces remain a
        matched pair). No live style affected — the selector matched nothing visible.
   Findings 2 (metra teardown) and 3 (convention note) are SQL-side; no styles.css change.
   No new numbered rule (set remains 2.1-2.32). Outstanding code defects: NONE.
   ============================================================
   REVISION LOG — BATCH 210 (Autarch "Proceed with the revisions")
   ============================================================
   styles.css change this batch:
   Item 2 — .feed-main { min-width: 0 } added (after .feed-layout). The 1fr feed-main
        grid track inherited implicit min-width:auto, so wide children (YouTube embeds,
        full-bleed .post-card-image) forced the track wider than its share; the centered
        .feed-header then centered across the over-wide track and landed left of the posts.
        min-width:0 lets the track hold its computed width so the header centers over the
        actual feed column, aligned with the posts beneath. Layout-only; retroactive (2.21)
        — applies to every feed render. (Batch 210 Item 1 is an app.js-only markup fix to the
        gallery overlay comment bar; no styles.css change for it — the .sticky-modal-bar class
        seal was already correct and is untouched.)
   No SQL this batch. No new numbered rule (2.1-2.32). Outstanding code defects: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 217 AUDIT-FIX PASS (Autarch: "Proceed with the fixes")
   ============================================================
   No styles.css change this pass — the Batch 217 audit fixes are confined to
   app.js (boot-path flash, orphaned-Auth-user wedge, duplicate-email handling,
   dead-code removal, honest email-status node, modal-path toast). The new
   #regFlowEmailStatus node reuses inline styles; no CSS added. Set remains
   2.1-2.32. Outstanding code defects: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 217 ROUND-TRIP FIX (Autarch: live test)
   ============================================================
   No styles.css change this pass — confirmation round-trip fix is app.js only
   (cross-tab localStorage stash, session-parse retry, orphan-stash guard).
   Set remains 2.1-2.32. Outstanding code defects: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 218 (Autarch: "Proceed with the revisions" — registration
   redesign: email confirmation retired)
   ============================================================
   THREE additions (placed with the toast family after .toast-verdict):
   - .toast-center.error — crimson tint for signup validation rejections, which
     now land in the middle of the page per Autarch directive (app.js
     _centerToast gained the optional 'error' type).
   - .toast-sticky (+ .toast-sticky-x) — PERSISTENT center toast: no auto-
     dismiss, closed only by its X; z-index 10003 (above verdict 10002 and all
     modals/overlays). Carries the post-payment access-code instruction.
   - .gold-spinner + @keyframes dmsGoldSpin — the spinning circle of gold
     (border-top var(--gold), 0.9s linear) shown while the membership payment is
     confirmed and the access-code email dispatches.
   No new numbered rule (set remains 2.1-2.32). Outstanding code defects: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 219 (Autarch: "Proceed with the fixes" — live proving
   run Layer 5: Stripe API-version drift + source-of-truth reconciliation)
   ============================================================
   No styles.css change this batch. Fixes confined to
   create-subscription-index.ts (Stripe-Version pinned 2025-03-31.basil; expand
   moved to latest_invoice.confirmation_secret with legacy fallback;
   stripe_customer_id persisted immediately on creation — redeploy is an
   Autarch action), supabase-security-migration.sql (Layer-2 users.password
   ALTER folded in per 2.31), and supabase-schema.sql (password nullable at
   creation, retroactive rebuild fix). Other Stripe functions swept and
   confirmed Basil-stable. sovereign_log 42501: divergence theory later
   DISPROVEN (live policies match canon; true cause = db.js
   return=representation read-back, fixed Batch 220). Logged here per 2.5.
   No new numbered rule (set remains 2.1-2.32). Outstanding code defects: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 220 (Autarch: "Proceed with the fixes" — Layer 6 CSP
   + sovereign_log read-back + narrative corrections)
   ============================================================
   No styles.css code change this batch (one Batch 219 ledger line corrected —
   the sovereign_log divergence theory, disproven by the Autarch's pg_policies
   diagnostic). Fixes confined to index.html (CSP amended for Stripe:
   script-src, frame-src, connect-src) and db.js (writeSovereignLog →
   return=minimal; the read-back aborted non-admin inserts with 42501).
   Logged here per 2.5. No new numbered rule (set remains 2.1-2.32).
   Outstanding code defects: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 221 (Autarch: "Proceed with the fixes" — audit-fix
   round on the session self-audit findings)
   ============================================================
   No styles.css change this batch. F2 — index.html frame-src gains
   https://m.stripe.network (completing Stripe's documented CSP set).
   F3 — create-subscription-index.ts no-op p_customer_id removed from the
   second RPC call (redeploy is an Autarch action). F1 — legacy
   payment_intent fallback retained by audit recommendation.
   Logged here per 2.5. No new numbered rule (set remains 2.1-2.32).
   Outstanding code defects: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 222 (Autarch: "proceed with the fixes" — the §5a
   autocomplete/keychain defect, traced to root then executed)
   ============================================================
   No styles.css change this batch. Safari treated the page as one
   ambiguous login context (no autocomplete hints on password inputs;
   "off" ignored on credential fields), anchoring its keychain popup to
   whatever held focus — including Stripe's iframe card field. Fix landed
   in index.html (both login pairs -> "username"/"current-password", login
   autofill KEPT; four registration password inputs -> "new-password") and
   app.js (regflow modal's two password inputs -> "new-password").
   Access-code fields untouched. Hints, not commands — residue is
   Safari's. Logged here per 2.5. No new numbered rule (set remains
   2.1-2.32). Outstanding code defects: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 223 (Autarch: "Proceed with the revisions")
   ============================================================
   CHANGED IN THIS FILE: (R2/R3) Trouble Shoot panel styling added after the
   about-signature rule — .ts-body/.ts-option/.ts-option-head/.ts-chev/.ts-panel/
   .ts-disclaimer/.ts-optional/.ts-submit. .ts-submit rides the existing
   .btn-danger crimson for the red submit button. Braces balanced 591/591.
   No new numbered rule (set remains 2.1-2.32).
   Outstanding code defects: 1 (Finding 2 — _cancelMembership writes nothing).
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 225 (Autarch: "Proceed with the revisions")
   ============================================================
   CHANGED IN THIS FILE: cancellation-lifecycle styling appended before the
   RESPONSIVE block — (R2) .ts-context landing textarea; (R10) .cancel-refresh-btn
   + .cancel-refresh-icon (discover-feed gold spin via .spinning, reusing
   @keyframes dmsGoldSpin); (R3) .cancel-confirm-btn (+ .sent green state),
   .cancel-action-btn (stacked two-line Cancel Account / Delete Request, gold-on-
   hover), .cancel-context-box; (R4) .cancel-lea checkbox label; (R9) .cleared-search
   bar. Gray-gating on the action buttons is applied inline (opacity + pointer-
   events) from the render, lifted by reply_received OR lea. Braces balanced
   607/607. No new numbered rule (set remains 2.1-2.32).
   Outstanding code defects: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 225 (audit-fix: Finding A)
   ============================================================
   CHANGED IN THIS FILE: .cancel-reply-btn (+ :hover, + .set green latch) added
   beside .cancel-confirm-btn for the new "Mark Reply" / "Reply Received" control
   that closes Finding A. Braces balanced 610/610. No new numbered rule
   (set remains 2.1-2.32).
   Outstanding code defects: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 226 (Autarch: "Proceed with the fixes")
   ============================================================
   NO CODE CHANGE IN THIS FILE. The cancel-* and .cancel-refresh-btn.spinning
   rules were already correct; Finding 5 was a JS-timing defect (the spinning
   node was destroyed before paint), fixed in app.js, not here. Findings 1, 3,
   4, R1 landed in the Edge Functions, db.js, and app.js. Finding 2 struck.
   Logged per 2.5. No new numbered rule (set remains 2.1-2.32).
   Outstanding code defects: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 227 (Autarch: "Proceed with the revisions")
   ============================================================
   NO CODE CHANGE IN THIS FILE. Batch 227 delivered R1 (profile blank-page fix),
   R2 (Terms of Agreement clickwrap gate), R3 (Stripe billing portal), R4 (LEA
   auto-refund), R5 (full-footprint removal of cancelled/banned users). Changes
   live in app.js, db.js, index.html, cancel-account-index.ts, the new
   billing-portal-index.ts, and supabase-security-migration.sql. Logged per 2.5.
   OUTSTANDING CODE DEFECTS: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 227 AUDIT-FIX (Autarch: "Proceed with the fixes")
   ============================================================
   NO CODE CHANGE IN THIS FILE. Batch 227 self-audit fixes: Finding A/E (removed
   dead saved_posts/followed_forums scrub from purge_user_footprint), Finding B
   (replaced auth.role() gate with p_admin_assert param), Finding C (_toaAccepted
   reset), Finding D (dead toast arg). Changes in supabase-security-migration.sql,
   cancel-account-index.ts, app.js, db.js. OUTSTANDING CODE DEFECTS: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 228 (Autarch: "Proceed")
   ============================================================
   CHANGED IN THIS FILE (styles.css):
   R1 — Activity-feed sticky-header sliver. .profile-feed-header-sticky painted its
   opaque background:var(--ink) only through padding-bottom:0.6rem, then carried a
   transparent margin-bottom:0.75rem. Margins do not inherit the background, so a
   post image scrolling up under the sticky tab bar bled through that 0.75rem band
   — the visible sliver. Folded the margin into padding (0.6+0.75=1.35rem) and set
   margin-bottom:0, so the opaque band now covers the full seam down to the content.
   Identical total spacing; the gap is sealed. Same class as the Batch 197/198
   sticky-bar bleed. Render-time/CSS fix, retroactive on next render (2.21).
   (R2 is the liked-by window video thumbnail — see app.js.)
   Brace-balanced. DEPLOY: front-end. OUTSTANDING CODE DEFECTS: NONE.
   ============================================================ */

/* ============================================================
   REVISION LOG — BATCH 229 (Autarch: "Proceed with the fixes" — audit findings)
   ============================================================
   CHANGED IN THIS FILE (styles.css):
   F2 — Same sticky-bleed class as Batch 228 R1, found in the audit on .forum-kind-tabs.
   It paints opaque background:var(--ink) with padding-bottom:0 and a transparent
   margin-bottom:1.5rem, so a card scrolling under the sticky forum-tab bar could
   show through that band. Unlike R1, this element's border-bottom sits tight to the
   tabs and already visually masks the bleed; folding the margin into padding would
   push that border 1.5rem off the tabs (a design change). Hardened instead with
   box-shadow: 0 1.5rem 0 var(--ink) to paint the margin band opaque, leaving the
   border position and total 1.5rem spacing unchanged. Render-time/CSS fix.
   (F1 dead-branch removal and F3 thumbnail-nav unify are in app.js.)
   Brace-balanced (610/610). DEPLOY: front-end. OUTSTANDING CODE DEFECTS: NONE.
   ============================================================ */
