/* chan-fe — imageboard-style catalog (4chan-X inspired, light theme)
   Goals:
   - light blue/grey background, dark text, near-invisible grid
   - drop shadow on images only
   - row flexes to hold the tallest (truncated) post
   - vanilla JS, no build step
*/

/* ---------------- 4chan-ish color tokens ---------------- */
:root {
  --bg:           #EEF2FF;            /* 4chan page background */
  --bg-hover:     #c8d0e8;            /* row hover */
  --bg-elev:      #ffffff;            /* control surfaces */
  --fg:           #000;
  --fg-dim:       #555;
  --fg-faint:     #888;
  --toolbar:      #34345C;
  --link:         var(--toolbar);
  --link-visited: #800080;
  --border:       #b7c5d9;
  --border-soft:  #d0d6e6;
  --accent:       #0f4d92;
  --red:          rgb(214, 0, 0);
  --shadow-img:   0 1px 3px rgba(0, 0, 0, 0.25);
  --shadow-img-hover: 0 2px 6px rgba(0, 0, 0, 0.35);

  --font-sans:   Arial, Helvetica, sans-serif;
  /* keep var name for compatibility, but use sans to avoid monospace UI */
  --font-mono:   Arial, Helvetica, sans-serif;

  --card-w:      180px;
  --gap:         4px;
  --per-page:    25;
}

/* Traditional orange/red imageboard palette. Disabling NSFW blur opts into
   this theme as a visible reminder that sensitive media is being shown. */
html.nsfw-theme {
  --bg:           #FFFFEE;
  --bg-hover:     #F0C0B0;
  --bg-elev:      #FFFFEE;
  --fg:           #800000;
  --fg-dim:       #800000;
  --fg-faint:     #9b5b50;
  --toolbar:      #800000;
  --link:         #000080;
  --link-visited: #800080;
  --border:       #D9BFB7;
  --border-soft:  #E8D2C8;
  --accent:       #CC1105;
}

html.nsfw-theme .topbar,
html.nsfw-theme .profile-bio,
html.nsfw-theme .post-block,
html.nsfw-theme .post-preview,
html.nsfw-theme .profile-preview,
html.nsfw-theme .emoji-picker-head,
html.nsfw-theme .compose-modal-head,
html.nsfw-theme .poll-composer-head,
html.nsfw-theme .board-create-head,
html.nsfw-theme .subscribe-modal-head,
html.nsfw-theme .watcher-head,
html.nsfw-theme .config-modal-head {
  background: #F0E1D7;
  border-color: var(--border);
}
html.nsfw-theme .post-block { background: #F0E0D6; }
html.nsfw-theme .post-block.is-op { background: transparent; }
html.nsfw-theme .post-block.is-inline { background: #F0E0D6; }
html.nsfw-theme .post-block.is-linked { background: #F0C0B0; }
html.nsfw-theme .post-block.is-own { border-left-color: rgba(221, 0, 0, .8); }
html.nsfw-theme .post-block.is-replying-to-you { border-left-color: rgba(221, 0, 0, .8); }
html.nsfw-theme .board-title,
html.nsfw-theme .thread-subject,
html.nsfw-theme .auth-dialog-box h2,
html.nsfw-theme .emoji-picker-head h2,
html.nsfw-theme .compose-modal-head h2,
html.nsfw-theme .poll-composer-head h2,
html.nsfw-theme .board-create-head h2,
html.nsfw-theme .subscribe-modal-head h2,
html.nsfw-theme .watcher-title,
html.nsfw-theme .config-modal-head h2 {
  color: #CC1105;
}
html.nsfw-theme .post-reply-to .reply-to-link,
html.nsfw-theme .compose-reply-to .reply-to-link { color: #000080; }
html.nsfw-theme .config-tab.is-active { box-shadow: inset 0 -2px 0 0 #CC1105; }

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--bg);            /* #EEF2FF */
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.35;
  min-height: 100vh;
}

a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ---------------- topbar ---------------- */
.topbar {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 2px 4px 0;
  background: #D6DAF0;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
  position: sticky;
  top: 0;
  z-index: 10;
}
.brand { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.instance {
  color: var(--fg);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
a.instance:hover { text-decoration: underline; }
/* One wrappable line of bracketed board groups:
 * [/home/twkn/local/likes/][/custom1/custom2/][+]
 * Inline flow (not flex) so long custom-board lists wrap at the <wbr>
 * break points renderBoardNav() inserts after separators. */
.boards {
  flex: 1;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 14px;
  line-height: 1.5;
}
.board-group { margin-right: 4px; }
.board-sep { color: var(--fg-faint); }

/* ---------------- toolbar (icon buttons to the left of the user pill) ---------------- */
.toolbar {
  display: flex;
  align-items: center;
  gap: 4px;
}
.toolbar-btn {
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1;
  background: transparent;
  color: var(--toolbar);
  border: 0;
  padding: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
}
.toolbar-btn:hover:not(:disabled) {
  color: var(--red);
}
.toolbar-btn:disabled {
  color: var(--fg-faint);
  cursor: not-allowed;
}
/* Spin the refresh icon while a fetch is in flight. The class is toggled
 * from render() based on state.loading, so it covers manual refresh clicks
 * and the auto-refresh timer firing in the background. */
@keyframes toolbar-refresh-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
.toolbar-btn.is-loading i {
  animation: toolbar-refresh-spin 1s linear infinite;
  transform-origin: 50% 50%;
}
@media (prefers-reduced-motion: reduce) {
  .toolbar-btn.is-loading i { animation-duration: 2.5s; }
}

.toolbar-btn.has-unread {
  color: var(--red);
}

/* ---------------- top loading bar ----------------
 * Thin NProgress-style bar pinned to the very top of the viewport. The fill
 * width is driven from JS (syncLoadingBar) tied to state.loading — it fills
 * toward ~90% while a view load (board/profile/tag/search/thread) is in
 * flight, snaps to 100% the instant the load finishes, then fades out.
 * pointer-events:none + aria-hidden keep it purely decorative. */
#loading-bar {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 3px;
  z-index: 1000;
  pointer-events: none;
}
#loading-bar .loading-bar-fill {
  display: block;
  height: 100%;
  width: 0;
  background: var(--accent);
  box-shadow: 0 0 4px rgba(0, 0, 0, 0.25);
  opacity: 0;
  transition: width 300ms ease, opacity 300ms ease;
}
/* Source order matters: when both classes are present during the fade-out,
 * .is-done (declared later) wins so opacity goes to 0. */
#loading-bar.is-loading .loading-bar-fill { opacity: 1; }
#loading-bar.is-done    .loading-bar-fill { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  #loading-bar .loading-bar-fill { transition-duration: 0.01ms; }
}
.board-link {
  font-family: var(--font-mono);
  font-size: 14px;
  padding: 2px 0;
  color: var(--fg);
}
.board-link:hover { color: var(--red); text-decoration: none; }
.board-link.active { color: var(--accent); }

.user-pill,
.auth-pill {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-sans);
  font-size: 12px;
}
.auth-signin {
  font-family: var(--font-sans);
  font-size: 12px;
  background: var(--accent);
  color: white;
  border: 1px solid var(--accent);
  border-radius: 1px;
  padding: 3px 10px;
  cursor: pointer;
}
.auth-signin:hover:not(:disabled) { background: #0a3a72; }
.auth-signin:disabled {
  background: var(--bg-elev);
  color: var(--fg-faint);
  border-color: var(--border);
  cursor: not-allowed;
}
.auth-pill-link {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--fg);
  text-decoration: none;
  font-size: 12px;
  font-weight: 500;
  padding: 2px 4px 2px 2px;
  border-radius: 1px;
}
.auth-pill-link:hover { background: var(--bg-hover); text-decoration: none; }
.auth-avatar {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--bg);
  flex-shrink: 0;
}
.auth-name {
  white-space: nowrap;
  max-width: 140px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.auth-signout {
  font-family: var(--font-sans);
  font-size: 11px;
  color: var(--fg-dim);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 1px;
  padding: 2px 7px;
  cursor: pointer;
}
.auth-signout:hover { background: var(--bg-hover); color: var(--fg); }

/* ---------------- auth dialog ---------------- */
.auth-dialog {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  z-index: 100;
  align-items: flex-start;
  justify-content: center;
  padding-top: 12vh;
}
.auth-dialog.is-open { display: flex; }
.auth-dialog-box {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 2px;
  width: 420px;
  max-width: calc(100% - 24px);
  padding: 14px 16px 16px;
  box-shadow: 0 4px 24px rgba(0,0,0,0.4);
}
.auth-dialog-box h2 {
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 700;
  color: #0F4D92;
  margin: 0 0 8px;
}
.auth-steps {
  margin: 0 0 10px;
  padding-left: 18px;
  font-size: 12px;
  color: var(--fg);
  line-height: 1.45;
}
.auth-steps li { margin-bottom: 2px; }
.auth-steps b { color: var(--fg); font-weight: 600; }
.auth-dialog-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border-soft);
}
.auth-open {
  font-family: var(--font-sans);
  font-size: 12px;
  background: var(--accent);
  color: white;
  border: 1px solid var(--accent);
  border-radius: 1px;
  padding: 4px 10px;
  cursor: pointer;
}
.auth-open:hover { background: #0a3a72; }
.auth-instance-row {
  margin-bottom: 12px;
}
.auth-instance-row label {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-faint);
  margin-bottom: 3px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.auth-instance-row input[type="text"] {
  width: 100%;
  font-family: var(--font-mono);
  font-size: 12px;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  padding: 5px 8px;
  border-radius: 3px;
}
.auth-instance-link {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-dim);
}
.auth-form label {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-faint);
  margin-bottom: 3px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.auth-form input[type="text"] {
  width: 100%;
  font-family: var(--font-mono);
  font-size: 12px;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 1px;
  padding: 5px 7px;
  box-sizing: border-box;
  outline: none;
  word-break: break-all;
}
.auth-form input[type="text"]:focus { border-color: var(--accent); }
.auth-form-row {
  display: flex;
  gap: 8px;
  margin-top: 8px;
}
.auth-submit {
  font-family: var(--font-sans);
  font-size: 12px;
  background: var(--accent);
  color: white;
  border: 1px solid var(--accent);
  border-radius: 1px;
  padding: 4px 12px;
  cursor: pointer;
}
.auth-submit:hover { background: #0a3a72; }
.auth-cancel {
  font-family: var(--font-sans);
  font-size: 12px;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 1px;
  padding: 4px 12px;
  cursor: pointer;
}
.auth-cancel:hover { background: var(--bg-hover); }
.auth-clear-cache {
  margin-left: auto;
  font-family: var(--font-sans);
  font-size: 11px;
  background: var(--bg);
  color: var(--fg-dim);
  border: 1px solid var(--border);
  border-radius: 1px;
  padding: 4px 10px;
  cursor: pointer;
}
.auth-clear-cache:hover:not(:disabled) { background: var(--bg-hover); color: var(--fg); }
.auth-clear-cache:disabled { cursor: default; opacity: 0.4; }
.auth-error {
  font-family: var(--font-mono);
  font-size: 11px;
  color: #b00;
  margin-top: 8px;
  white-space: pre-wrap;
  word-break: break-word;
}

/* ---------------- catalog wrap ---------------- */
.catalog-wrap { max-width: 1500px; margin: 0 auto; padding: 10px 12px; }
.catalog-header { margin-bottom: 4px; }
.board-title {
  font-family: var(--font-sans);
  color: #AF0A0F;
  text-shadow: 1px 1px 1px rgba(105, 10, 15, 0.6);
  font-size: 28px;
  font-weight: 400;
  letter-spacing: -2px;
  text-align: center;
  margin: 4px 0 2px 0;
}
.catalog-meta {
  color: var(--fg-dim);
  font-size: 11px;
  font-family: var(--font-mono);
  text-align: center;
}
/* Full user@instance handle for a custom-board (/g/<name>) catalog, shown
 * between the board-title and catalog-meta. Mirrors .profile-acct but centered
 * to match the catalog header. */
.board-acct {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-faint);
  text-align: center;
  margin: 2px 0 4px;
  word-break: break-all;
}

/* ---------------- start thread row (its own line, centered) ---------------- */
.start-thread-row {
  display: flex;
  justify-content: center;
  gap: 8px;
  padding: 6px 0 8px;
}
.profile-follow.is-following {
  background: var(--bg-elev);
  color: var(--fg);
  border-color: var(--border);
}
.profile-follow.is-following:hover:not(:disabled) { background: #6a0a0f; color: #fff; border-color: #6a0a0f; }
.profile-moderation { position: relative; }
.profile-moderation-btn { min-width: 28px; }
.profile-moderation-menu {
  position: absolute; top: calc(100% + 3px); right: 0; z-index: 40;
  min-width: 175px; padding: 3px; background: var(--bg-elev);
  border: 1px solid var(--border); box-shadow: 0 2px 8px rgba(0,0,0,.25);
}
.profile-moderation-menu button {
  display: block; width: 100%; padding: 5px 8px; border: 0; background: transparent;
  color: var(--fg); font: 12px var(--font-sans); text-align: left; cursor: pointer;
}
.profile-moderation-menu button:hover { background: var(--bg-hover); }
.profile-moderation-menu button:disabled { color: var(--fg-faint); cursor: default; }

/* ---------------- account menu (multi-account switcher) ---------------- */
/* .user anchors the dropdown the same way .profile-moderation does. */
.user { position: relative; display: flex; align-items: center; gap: 4px; }
.account-menu-btn[hidden],
.account-signin-btn[hidden] { display: none; }
.account-menu {
  position: absolute; top: calc(100% + 3px); right: 0; z-index: 40;
  min-width: 200px; padding: 3px; background: var(--bg-elev);
  border: 1px solid var(--border); box-shadow: 0 2px 8px rgba(0,0,0,.25);
}
.account-menu[hidden] { display: none; }
.account-menu-item {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 5px 8px; border: 0; background: transparent; color: var(--fg);
  font: 12px var(--font-sans); text-align: left; cursor: pointer; white-space: nowrap;
}
.account-menu-item i { width: 14px; text-align: center; color: var(--fg-dim); }
.account-menu-item:hover { background: var(--bg-hover); }
.account-menu-item.is-active { font-weight: 600; }
.account-menu-sep { height: 1px; margin: 3px 0; background: var(--border-soft); }

/* ------- controls (view toggle + search left, filter + sort right) ------- */
.catalog-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;   /* search hugs the left, filter/sort the right */
  gap: 12px;
  padding: 4px 0 8px;
  border-bottom: 1px solid var(--border-soft);
  margin-bottom: -1px;
}
.catalog-controls-left {
  display: flex;
  align-items: center;
  gap: 10px;
}
/* Catalog ⇄ Index. Same shape as a thread's nav links: the brackets are
   plain text belonging to the page, only the word inside them is the link. */
.catalog-view-toggle {
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--fg);
  white-space: nowrap;
}
.catalog-view-toggle[hidden] { display: none; }
.catalog-view-toggle button {
  font: inherit;
  background: none;
  border: 0;
  padding: 0;
  color: var(--link);
  cursor: pointer;
}
.catalog-view-toggle button:hover { color: var(--red); text-decoration: underline; }
.start-thread {
  font-family: var(--font-sans);
  font-size: 12px;
  background: var(--accent);
  color: white;
  border: 1px solid var(--accent);
  border-radius: 1px;
  padding: 4px 12px;
  cursor: pointer;
}
.start-thread:hover:not(:disabled) { background: #0a3a72; }
.start-thread:disabled {
  background: var(--bg-elev);
  color: var(--fg-faint);
  border-color: var(--border);
  cursor: not-allowed;
}
.catalog-controls-right {
  display: flex;
  align-items: center;
  gap: 8px;
}
.catalog-controls-right[hidden] { display: none; }
.catalog-media-filter {
  color: var(--fg-dim);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 1px;
  padding: 3px 6px;
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
}
.catalog-media-filter:hover { color: var(--accent); border-color: var(--accent); }
.catalog-media-filter.is-active { color: #fff; background: var(--accent); border-color: var(--accent); }
.catalog-media-filter[hidden] { display: none; }
/* Search box: input with a magnifying-glass submit button embedded on the
   right. Same proportions as the Filter input so the two flank the bar. */
.catalog-search {
  position: relative;
  display: flex;
  align-items: center;
}
.catalog-search-input {
  font-family: var(--font-sans);
  font-size: 12px;
  background: var(--bg-elev);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 1px;
  padding: 3px 28px 3px 6px;   /* leave room for the embedded button */
  width: 220px;
  outline: none;
}
.catalog-search-input:focus { border-color: var(--accent); }
.catalog-search-btn {
  position: absolute;
  right: 3px;
  top: 50%;
  transform: translateY(-50%);
  background: transparent;
  border: 0;
  padding: 2px 4px;
  font-size: 12px;
  line-height: 1;
  color: var(--fg-dim);
  cursor: pointer;
}
.catalog-search-btn:hover:not(:disabled) { color: var(--accent); }
.catalog-search-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.search-tabs {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 12px;
  border-bottom: 1px dotted var(--border-soft);
}
.search-tabs button {
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  padding: 6px 2px;
  color: var(--link);
  font: 12px var(--font-mono);
  cursor: pointer;
}
.search-tabs button[aria-selected="true"] { color: var(--accent); border-bottom-color: var(--accent); }
.search-tabs button:focus-visible { outline: 1px solid var(--accent); }
.search-results-panel {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--card-w), 1fr));
  grid-auto-rows: max-content;
  gap: inherit;
}
.search-results-panel[hidden] { display: none; }
.search-hashtags { grid-column: 1 / -1; }
.search-load-more {
  grid-column: 1 / -1;
  padding: 8px 0 16px;
  text-align: center;
}
.search-hashtag-links {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding: 2px 4px 5px;
}
.search-hashtag {
  color: var(--link);
  font: 12px var(--font-mono);
}
.search-hashtag:hover { color: var(--red); }
.catalog-filter {
  font-family: var(--font-sans);
  font-size: 12px;
  background: var(--bg-elev);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 1px;
  padding: 3px 6px;
  width: 200px;
  outline: none;
}
.catalog-filter:focus { border-color: var(--accent); }
.catalog-sort {
  font-family: var(--font-sans);
  font-size: 11px;
  color: var(--fg-dim);
}
.catalog-sort select {
  font-family: var(--font-sans);
  font-size: 11px;
  background: var(--bg-elev);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 1px;
  padding: 2px 4px;
  margin-left: 4px;
}

/* ---------------- catalog grid (4chan-X) ---------------- */
.catalog {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--card-w), 1fr));
  /* Size each row independently to its tallest card. `1fr` made every row
     adopt the height of the tallest card in the entire catalog. */
  grid-auto-rows: max-content;
  gap: var(--gap) var(--gap); /* row-gap col-gap */
}
.catalog.is-search {
  grid-auto-rows: max-content;
}

.empty {
  grid-column: 1 / -1;
  text-align: center;
  padding: 60px 0;
  color: var(--fg-faint);
  font-family: var(--font-mono);
  font-size: 12px;
}

/* ---------------- thread card (4chan-X style, near-invisible) ---------------- */
.thread-card {
  display: flex;
  flex-direction: column;
  position: relative;
  cursor: pointer;
  background: transparent;       /* invisible card */
  border: 0;
  padding: 4px;
  min-width: 0;                  /* allow text truncation */
}
.thread-card:hover {
  z-index: 7;
  background: transparent;       /* no hover bg — only the image reacts */
}
.thread-activity {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 6;
  padding: 5px 6px;
  color: var(--fg-dim);
  background: var(--bg);
  font-family: var(--font-mono);
  font-size: 10px;
  line-height: 1.35;
  text-align: left;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.08s ease;
}
.thread-card:hover .thread-activity {
  opacity: 1;
  pointer-events: auto;
}
.thread-activity-line {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.thread-activity-line i { width: 1.3em; text-align: center; }
.thread-activity-line a { color: var(--link); text-decoration: none; }
.thread-activity-line a:hover { text-decoration: underline; }

/* thumb: image (natural aspect) or avatar+banner composite */
.thread-thumb {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100px;
  max-height: 320px;
  overflow: visible;             /* drop shadow shows outside */
  background: transparent;
}
.thread-thumb img {
  display: block;
  max-width: 100%;
  max-height: 320px;
  width: auto;
  height: auto;
  object-fit: contain;
  filter: drop-shadow(var(--shadow-img));
  transition: filter 0.06s ease;
}
.thread-card:hover .thread-thumb img {
  filter: drop-shadow(var(--shadow-img-hover));
}

/* avatar+banner composite (imageless OP) */
.thread-thumb.is-avatar-banner {
  aspect-ratio: 4 / 3;
  max-height: none;
  background: var(--bg-elev);
  filter: drop-shadow(var(--shadow-img));
  transition: filter 0.06s ease;
  position: relative;
}
.thread-thumb.is-avatar-banner.is-anonymous,
.post-media.is-avatar-banner.is-anonymous {
  background: #d9f2d9;
}
.thread-thumb.is-avatar-banner.is-anonymous .thumb-avatar,
.post-media.is-avatar-banner.is-anonymous .thumb-avatar {
  border-color: #9acb9a;
}
.anonymous-mode [data-anon-masked="1"] {
  pointer-events: none;
  cursor: default;
  text-decoration: none;
}
.thread-card:hover .thread-thumb.is-avatar-banner {
  filter: drop-shadow(var(--shadow-img-hover));
}
.thread-thumb .banner-bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  filter: brightness(0.55) saturate(0.9);
  border-radius: 0;            /* shadow comes from parent filter */
}
.thread-thumb .thumb-avatar {
  position: relative;
  z-index: 1;
  width: 55%;
  aspect-ratio: 1;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid rgba(255,255,255,0.4);
  box-shadow: 0 2px 6px rgba(0,0,0,0.4);
}
.post-media.is-avatar-banner .thumb-avatar {
  position: relative;
  z-index: 1;
  width: 55%;
  aspect-ratio: 1;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid rgba(255,255,255,0.4);
  box-shadow: 0 2px 6px rgba(0,0,0,0.4);
}
.thread-thumb .thumb-avatar-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-elev);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: 32px;
  font-weight: 600;
}

/* catalog video thumbnail */
.thread-thumb-video {
  display: block;
  max-width: 100%;
  max-height: 320px;
  width: auto;
  height: auto;
  object-fit: contain;
  filter: drop-shadow(var(--shadow-img));
  transition: filter 0.06s ease;
}
.thread-card:hover .thread-thumb-video {
  filter: drop-shadow(var(--shadow-img-hover));
}

.thread-document-placeholder {
  width: min(100%, 230px);
  min-height: 150px;
  padding: 18px 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 7px;
  color: var(--fg-dim);
  background: var(--bg-elev);
  border: 1px dashed var(--border);
  font-family: var(--font-mono);
  text-decoration: none;
  filter: drop-shadow(var(--shadow-img));
  transition: filter 0.06s ease, color 0.06s ease;
}
.thread-document-placeholder i { font-size: 42px; }
.thread-document-label {
  color: var(--link);
  font-size: 12px;
  font-weight: 600;
}
.thread-document-name {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 10px;
  color: var(--fg-faint);
}
.thread-card:hover .thread-document-placeholder {
  color: var(--red);
  filter: drop-shadow(var(--shadow-img-hover));
}

/* Action overlay pinned to the bottom of the thumb, revealed on card hover.
   Reuses .post-action buttons (so toggleLike/toggleRepost work) but restyles
   them for legibility over a thumbnail. */
.thread-thumb-foot {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 4;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 3px;
  padding: 5px 4px 4px;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0));
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.08s ease;
}
.thread-card:hover .thread-thumb-foot {
  opacity: 1;
  pointer-events: auto;
}
.thread-thumb-foot .post-action {
  font-size: 10px;
  padding: 1px 4px;
  gap: 3px;
  color: #fff;
  background: rgba(0, 0, 0, 0.45);
}
.thread-thumb-foot .post-action:hover:not(:disabled) {
  background: rgba(0, 0, 0, 0.7);
  color: var(--red);
}
.thread-thumb-foot .post-action.is-liked { color: var(--red); }
.thread-thumb-foot .post-action.is-reposted { color: #5fe06a; }
.thread-thumb-foot .post-action.is-watching,
.thread-thumb-foot .post-action.is-watching:hover:not(:disabled) { color: var(--red); }

/* legacy fallback for no media URL */
.thread-thumb-placeholder {
  width: 100%;
  height: 100%;
  min-height: 120px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background:
    repeating-linear-gradient(
      45deg,
      #1a1c20,
      #1a1c20 8px,
      #15171b 8px,
      #15171b 16px
    );
  color: var(--fg-dim);
  font-family: var(--font-mono);
  filter: drop-shadow(var(--shadow-img));
}
.thread-thumb-placeholder .label {
  letter-spacing: 1px;
  text-transform: uppercase;
  font-size: 9px;
  color: #aaa;
}

/* stats badge (top-right of thumb) — 4chan style: "R: N / I: N" */
.thread-stats {
  position: absolute;
  top: 4px;
  right: 4px;
  background: rgba(255,255,255,0.85);
  color: var(--red);
  font-family: var(--font-mono);
  font-size: 10px;
  padding: 1px 4px;
  border-radius: 1px;
  z-index: 2;
  letter-spacing: 0.3px;
}
.thread-stats b { color: #b00; font-weight: 600; }

.thread-badge {
  position: absolute;
  z-index: 2;
  font-family: var(--font-mono);
  font-size: 9px;
  padding: 1px 4px;
  border-radius: 1px;
  letter-spacing: 0.4px;
  line-height: 1.2;
}
.thread-badge.nsfw {
  top: 4px;
  left: 4px;
  background: rgba(180, 30, 30, 0.9);
  color: white;
  text-transform: uppercase;
}
.thread-badge.spoiler {
  bottom: 4px;
  left: 4px;
  right: 4px;
  background: rgba(0,0,0,0.75);
  color: #fff;
  font-style: italic;
  text-align: center;
}
/* Board admin buttons (shown only on boards the user moderates) */
.start-thread.board-delete { background: #7a1010; border-color: #7a1010; }
.start-thread.board-delete:hover { background: #9a1414; border-color: #9a1414; }

/* Group-post marker (status.group / group account cards); links to /g/… */
.thread-badge.board {
  top: 4px;
  left: 4px;
  background: rgba(0, 102, 0, 0.9);
  color: white;
  text-transform: uppercase;
  text-decoration: none;
}
a.thread-badge.board:hover { background: #060; color: #fff; }
/* NSFW owns the top-left corner; stack the board badge under it. */
.thread-badge.board.below-nsfw { top: 20px; }

/* A stickied post on its author's profile. The thumbtack alone: at card size a
   word competed with the thumbnail, and the shape carries the meaning.

   Hangs off the top edge rather than sitting inside it, so it reads as a pin
   holding the card up — it overlaps whatever badge is in the top-left corner
   by a few pixels without covering it. .thread-thumb is overflow: visible, so
   the negative offset isn't clipped. */
.thread-badge.sticky {
  top: -8px;
  left: 0px;
  padding: 0;
  background: none;
  color: #3F77CC;
  font-size: 15px;
  line-height: 1;
  z-index: 6;                    /* over the badges it rests on */
  /* A contrast ring that follows the thumbtack rather than boxing it: eight
     1px shadows, one per direction. A shadow cast by a glyph takes that
     glyph's own shape, so the white traces the pin's outline. Preferred over
     -webkit-text-stroke, which centres on the edge and eats into the blue. */
  text-shadow:
     1px  0   0 #fff, -1px  0   0 #fff,  0    1px 0 #fff,  0   -1px 0 #fff,
     1px  1px 0 #fff, -1px -1px 0 #fff,  1px -1px 0 #fff, -1px  1px 0 #fff;
}

/* Quote-post marker (hover shows the quoted post; not a link). Shares the
   top-left column with the NSFW and board badges, stacking below whichever
   of them the card is showing. */
.thread-badge.quote {
  top: 4px;
  left: 4px;
  background: rgba(58, 58, 138, 0.9);
  color: #fff;
  text-transform: uppercase;
}
.thread-badge.quote.below-1 { top: 20px; }
.thread-badge.quote.below-2 { top: 36px; }

/* Reply marker on flattened boards, where a reply gets its own card. Same
   hover-preview behaviour as the quote badge, one more slot down the stack. */
.thread-badge.reply {
  top: 4px;
  left: 4px;
  background: rgba(140, 74, 20, 0.9);
  color: #fff;
  text-transform: uppercase;
}
.thread-badge.reply.below-1 { top: 20px; }
.thread-badge.reply.below-2 { top: 36px; }
.thread-badge.reply.below-3 { top: 52px; }

/* Inline /boardname/ chip on a group thread's OP info row */
.post-board-chip {
  font-family: var(--font-mono);
  font-size: 9px;
  padding: 1px 4px;
  border-radius: 1px;
  letter-spacing: 0.4px;
  vertical-align: 1px;
  background: #006600;
  color: #fff;
  text-decoration: none;
  white-space: nowrap;
}
.post-board-chip:hover { background: #060; color: #fff; text-decoration: none; }

/* NSFW blur overlay — sits above the image, below badges and toggle btn */
.nsfw-blur-overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  background: rgba(0, 0, 0, 0.15); /* fallback tint when backdrop-filter unsupported */
  pointer-events: none;
}
.nsfw-revealed .nsfw-blur-overlay {
  display: none;
}

/* Toggle button — top-right corner, opposite the NSFW badge */
.nsfw-toggle-btn {
  position: absolute;
  top: 4px;
  right: 4px;
  z-index: 3;
}

/* Top-right control cluster on catalog thumbs (hide thread + NSFW toggle).
   Hover-revealed and styled like the footer overlay so the controls (the blur
   toggle especially) stay legible over any thumbnail. */
.thread-thumb-top {
  position: absolute;
  top: 4px;
  right: 4px;
  z-index: 5;
  display: flex;
  gap: 3px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.08s ease;
}
.thread-card:hover .thread-thumb-top {
  opacity: 1;
  pointer-events: auto;
}
.thread-thumb-top .post-action,
.nsfw-toggle-btn {
  font-size: 11px;
  line-height: 1;
  padding: 2px 5px;
  color: #fff;
  background: rgba(0, 0, 0, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 1px;
  cursor: pointer;
}
.thread-thumb-top .post-action,
.thread-thumb-top .nsfw-toggle-btn {
  position: static;
}
.thread-thumb-top .post-action:hover:not(:disabled),
.nsfw-toggle-btn:hover {
  background: rgba(0, 0, 0, 0.75);
}
.thread-thumb-top .post-action:hover:not(:disabled) {
  color: var(--red);
}
.thread-thumb-top .post-action { border: 0; }

/* Wrapper for NSFW images in thread view (provides positioning context) */
.nsfw-img-wrap {
  position: relative;
  display: inline-block;
  line-height: 0;
}

/* text info: stats line + subject + body (centered, 4chan-X style) */
.thread-info {
  position: relative;
  padding: 5px 4px 4px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  text-align: center;          /* 4chan centers card text */
}
.thread-stats-text {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  color: #000;
  letter-spacing: 0.5px;
}
.thread-stats-text .sep {
  color: var(--fg-faint);
  font-weight: 400;
  margin: 0 3px;
}
.thread-subject {
  font-size: 13px;
  font-weight: 700;
  color: #0F4D92;              /* 4chan subject/link dark blue */
  line-height: 1.25;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
}
.thread-subject a {
  color: inherit;
  cursor: pointer;
}
.thread-subject a:hover { text-decoration: underline; }

/* Poster name when used as the subject line (no summary/extracted h1). */
.thread-subject.is-poster,
.thread-subject.is-poster a {
  color: #117743;                 /* 4chan green name */
}

/* Poster name shown below the summary subject in catalog cards. */
.thread-poster-name {
  font-size: 12px;
  font-weight: 700;
  line-height: 1.25;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
}
.thread-poster-name a {
  color: #117743;                 /* 4chan green name */
  text-decoration: none;
}
.thread-poster-name a:hover {
  text-decoration: underline;
}

/* Subject line in thread view (OP only). Rendered inline inside .post-head
   matching 4chan's inline subject in the postInfo row. */
.post-subject {
  font-size: 14px;
  font-weight: 700;
  color: #0F0C5D;                 /* 4chan navy subject color */
}

.custom-emoji {
  display: inline-block;
  width: 1.1em;
  height: 1.1em;
  vertical-align: -0.22em;
  object-fit: contain;
}

.thread-body {
  font-size: 12px;
  font-weight: 400;
  line-height: 1.35;
  color: #000;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
  margin-top: 1px;
}
/* Content links render (mentions/hashtags/urls) but stay inert so the whole
   card remains one click target that opens the thread; keep them looking like
   the surrounding snippet text rather than standout links. */
.thread-body a {
  pointer-events: none;
  color: inherit;
  text-decoration: none;
}
/* Keep the clamped snippet compact — no big default <p> gaps. */
.thread-body p { margin: 0; }

/* No meta line / no handle in catalog cards — 4chan doesn't have them */

/* ---------------- catalog infinite-scroll footer ---------------- */
.catalog-sentinel {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 16px 0 24px;
  clear: both;
}
.catalog-load-more {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--link);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 1px;
  padding: 4px 14px;
  cursor: pointer;
}
.catalog-load-more:hover:not(:disabled) { background: var(--bg-hover); }
.catalog-load-more:disabled { color: var(--fg-faint); cursor: default; }

/* ---------------- thread view ---------------- */
.catalog.is-thread { display: block; }

/* ---------------- index rows (OP + its latest replies) ---------------- */
.catalog.is-index {
  display: block;
  /* Gutter for the hide buttons, which hang 22px to the left of the post
     they belong to. Without it they'd sit outside the catalog and be clipped
     once the page is wide enough for .catalog-wrap to stop centring. */
  padding-left: 22px;
}
/* A row is the same post blocks the thread view renders, so the styling here
   is only what separates one thread from the next. Replies sit indented under
   their OP the way a board index shows them. */
.index-thread {
  display: flow-root;   /* contains the OP's file float (see .thread-wrapper) */
  padding: 8px 0 10px;
  border-bottom: 1px solid var(--border-soft);
}
.index-thread:first-child { padding-top: 2px; }
.index-thread:last-child { border-bottom: none; }
.index-replies {
  margin: 4px 0 0;      /* no indent: the OP's file float offsets the replies
                           beside it, and the ones below it start at the left
                           edge, the way a board index reads */
}
.index-replies[hidden] { display: none; }
.index-omitted {
  display: inline-block;   /* an atomic inline sits beside the file float */
  margin: 2px 0 4px;
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--fg-dim);
}
.index-omitted:hover { color: var(--red); text-decoration: none; }

/* ---------------- about page ---------------- */
.catalog.is-about { display: block; max-width: 720px; }

.about-header {
  margin-bottom: 18px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border-soft);
}
.about-title {
  font-family: var(--font-sans);
  font-size: 24px;
  font-weight: 700;
  color: #0F4D92;
  margin: 0 0 2px;
}
.about-uri {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--fg-faint);
  margin-bottom: 6px;
}
.about-tagline {
  font-size: 13px;
  color: var(--fg);
  margin: 6px 0 0;
}

.about-block {
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  border-radius: 2px;
  padding: 10px 14px 12px;
  margin-bottom: 10px;
}
.about-block h2 {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 700;
  color: #0F4D92;
  margin: 0 0 8px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--border-soft);
}

.about-html { font-size: 13px; line-height: 1.5; color: var(--fg); }
.about-html p { margin: 0 0 0.5em; }
.about-html p:last-child { margin-bottom: 0; }
.about-html a { color: var(--link); }
.about-html ul { margin: 0; padding-left: 20px; }

.about-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}
.about-stat {
  background: var(--bg);
  border: 1px solid var(--border-soft);
  border-radius: 2px;
  padding: 8px 10px;
  text-align: center;
}
.about-stat-value {
  font-family: var(--font-mono);
  font-size: 18px;
  font-weight: 700;
  color: var(--fg);
}
.about-stat-label {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--fg-faint);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-top: 2px;
}

.about-config {
  margin: 0;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 4px 14px;
  font-family: var(--font-mono);
  font-size: 12px;
}
.about-config dt { color: var(--fg-faint); }
.about-config dd { margin: 0; color: var(--fg); word-break: break-word; }
.about-config a { color: var(--link); }

.contact-card {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-bottom: 8px;
}
.contact-avatar {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,0.2));
}
.contact-info { min-width: 0; }
.contact-name {
  font-size: 14px;
  font-weight: 700;
  color: var(--fg);
}
.contact-handle {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-faint);
  margin-bottom: 4px;
}
.contact-note {
  font-size: 12px;
  line-height: 1.4;
  color: var(--fg-dim);
}
.contact-note p { margin: 0; }

.contact-fields { margin-top: 4px; }

/* ---------------- profile bio (/user/ view) ---------------- */
.profile-bio {
  margin: 4px auto 8px;
  max-width: 640px;
  padding: 10px 12px;
  background: #D6DAF0;            /* match a post (4chan reply bg) */
  border: 1px solid #B7C5D9;
  border-radius: 2px;
}
.profile-card {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}
.profile-avatar {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.2));
}
.profile-info { min-width: 0; flex: 1; }
.profile-display-name {
  font-size: 15px;
  font-weight: 700;
  color: #117743;                 /* 4chan green name (matches catalog/replies) */
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.profile-display-name .custom-emoji { vertical-align: -2px; }
.profile-badge { color: var(--fg-dim); font-size: 12px; }
.profile-acct {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-faint);
  margin-bottom: 4px;
  word-break: break-all;
}
/* "Follows (You)" beside the handle. Quieter than the handle it trails, since
   it's a fact about the reader rather than part of the account's identity —
   but the (You) is in the quotelink red, which is what that marker means
   everywhere else in the frontend. */
.profile-follows-you {
  margin-left: 8px;
  padding: 0 4px;
  border: 1px solid var(--border);
  border-radius: 2px;
  color: var(--fg-faint);
  white-space: nowrap;
  word-break: normal;
}
.profile-follows-you .you { color: var(--red); }
.profile-stats {
  display: flex;
  gap: 12px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  color: var(--fg-dim);
  margin-bottom: 6px;
}
.profile-stat { cursor: default; }
.profile-note {
  font-size: 12px;
  line-height: 1.4;
  color: var(--fg-dim);
  word-break: break-word;
}
.profile-note p { margin: 0 0 4px; }
.profile-note p:last-child { margin-bottom: 0; }
.profile-note a { color: var(--link); }
.profile-note.is-collapsed {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.profile-fields {
  margin: 6px 0 0;
  font-size: 12px;
}
.profile-fields dt {
  font-weight: 700;
  color: var(--fg-dim);
  margin-top: 4px;
}
.profile-fields dd {
  margin: 0;
  color: var(--fg);
  word-break: break-word;
}
.profile-fields dd a { color: var(--link); }
.profile-fields dd.is-verified { color: #1a7f37; }
.profile-show-more {
  display: inline-block;
  margin-top: 4px;
  padding: 0;
  background: none;
  border: none;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--link);
  cursor: pointer;
}
.profile-show-more:hover { text-decoration: underline; }
/* The explicit display above otherwise overrides the [hidden] attribute, so
   the note's "[show more]" toggle would show even when it has nothing to
   reveal. Restore hidden's intent explicitly. */
.profile-show-more[hidden] { display: none; }
.profile-bio-toggle { display: block; }
.profile-extra { margin-top: 2px; }

/* ---- 4chan-style navigation bars (top & bottom of thread view) ---- */
.navLinks {
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--fg);
  margin: 4px 0;
  clear: both;
}
.navLinks a {
  color: var(--link);
  text-decoration: none;
}
.navLinks a:hover {
  color: var(--red);
  text-decoration: underline;
}
.navLinksTop {
  padding-bottom: 3px;
  border-bottom: 1px solid var(--border-soft);
  margin-bottom: 8px;
}
.navLinks .brackets-wrap::before { content: "["; }
.navLinks .brackets-wrap::after  { content: "]"; }
.navLinks .brackets-wrap a {
  color: var(--link);
  text-decoration: none;
}
.navLinks .brackets-wrap a:hover {
  color: var(--red);
  text-decoration: underline;
}
/* Floating [Top] bar for catalog views: fixed and centered at the bottom of
   the viewport, shown by app.js once the user scrolls a screen down. Threads
   keep their own in-flow [Top] nav instead. */
.scroll-top-bar {
  position: fixed;
  bottom: 14px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 40;
  /* Floats over the bottom-centre of the viewport — exactly where the last
     row of catalog cards is. Only the link itself should swallow clicks; the
     pill's padding lets them through to the card underneath. */
  pointer-events: none;
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--fg);
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  border-radius: 4px;
  padding: 4px 10px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.25);
}
.scroll-top-bar a {
  color: var(--link);
  text-decoration: none;
  pointer-events: auto;
}
.scroll-top-bar a:hover {
  color: var(--red);
  text-decoration: underline;
}

/* Floating "N new replies" affordance, thread views only. Appears when a
   background poll brings in replies the reader can't see (they've scrolled
   up); click jumps to the first one. Hidden while live-watching at the
   bottom. */
.new-replies-bar {
  position: fixed;
  bottom: 14px;
  right: 16px;
  z-index: 40;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--fg);
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  border-radius: 4px;
  padding: 6px 12px;
  text-decoration: none;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0,0,0,0.25);
}
.new-replies-bar:hover { background: var(--bg-hover); }
.new-replies-bar[hidden] { display: none !important; }
.new-replies-bar i { color: var(--link); }
.navLinksBot {
  margin: 4px 0;
  position: relative; /* anchor for .qr-link-container-bottom */
  border-style: solid;
  border-width: 1px 0;
  border-color: var(--border-soft);
  padding: 3px 0;
}
/* 4chan trick: pull the "Reply to OP" out of flow and pin it to the
   horizontal center, while the [Index][Catalog][Top][Update] links
   stay in normal flow on the left of the same row. */
.navLinksBot > .qr-link-container-bottom {
  width: 200px;
  position: absolute;
  left: -100px;
  margin-left: 50%;
  text-align: center;
}
.thread-section {
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 600;
  color: var(--fg);
  border-bottom: 1px solid var(--border-soft);
  padding-bottom: 3px;
  margin: 20px 0 8px;
}

/* Thread wrapper: OP + replies flowing (4chan style).
 *
 * The OP's file is a left float that deliberately escapes the OP itself, so
 * everything after it — the OP's own text, then the replies — flows around
 * it: replies beside the image are narrowed and offset, and the first one
 * past its bottom edge goes full width. That only works if nothing between
 * the float and the replies establishes a block formatting context, which is
 * why .post-block.is-op and the OP's body are plain blocks below. The
 * wrapper is the BFC that contains the float, so it can't reach the next
 * thread; .index-thread does the same job on the index. */
.thread-wrapper {
  display: flow-root;
}
.replies-wrap {
  margin-top: 4px;
}

.post-block {
  background: #D6DAF0;            /* 4chan reply bg */
  border: 1px solid #B7C5D9;
  border-radius: 0;
  padding: 3px 6px 5px;
  margin: 0 0 6px;
  width: fit-content;
  max-width: calc(100% - 8px);
  display: flow-root;
  overflow: visible;
  position: relative;
}
.post-block.is-op {
  padding: 0;
  margin: 0 0 8px;
  border: none;
  background: transparent;
  max-width: 100%;
}
.post-block.is-op .post-body {
  margin-left: 0;                /* avatar banner already provides left
                                    gutter via its own 20px right margin;
                                    no need for an extra 40px gap */
}
/* An OP at the top of its thread lets its file float escape, so everything
   after it flows around the image. Excluded: an OP expanded inline inside
   somebody's reply, which is a self-contained card and keeps the default
   block behaviour. */
.post-block.is-op:not(.is-inline) {
  display: block;                /* NOT flow-root: a block formatting context
                                    would trap the file float inside the OP,
                                    and the replies below would clear it
                                    instead of flowing around it */
  width: auto;                   /* likewise — shrink-to-fit is a BFC */
}
.post-block.is-op:not(.is-inline) .post-body {
  display: block;                /* so the OP's own text wraps beside the file
                                    and then continues under it */
}
.post-block.is-op:not(.is-inline) .post-foot {
  width: fit-content;            /* the OP is full width now, so shrink the
                                    action row here instead — otherwise it
                                    stretches to the right edge of the page */
  max-width: 100%;
  clear: none;                   /* the OP's action row belongs beside the
                                    file, under the OP's own text — clearing
                                    it would push it (and every reply after
                                    it) below the image */
}
.post-block.is-op .post-foot-primary {
  margin-left: 0;
}
.post-block.is-op .post-reactions {
  justify-content: flex-start;  /* OPs: reaction chips align left to match
                                    the action row */
}
.post-block.is-op:not(.is-inline) .post-reactions {
  width: fit-content;           /* a block-level flex container fills the line
                                    it's on, so a single chip claimed the full
                                    width, couldn't fit beside the file, and
                                    dropped below it — taking every reply with
                                    it. Shrink to the chips instead. */
  max-width: 100%;
  clear: none;                  /* same reason as .post-foot: clearing here
                                    pushes the whole thread below the image */
}

.post-hide-btn {
  position: absolute;
  left: -22px;
  top: 3px;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  color: var(--fg-faint);
  font-size: 14px;
  line-height: 1;
}

.post-stub {
  margin: 0 -21px 6px;
}
.post-restore-link,
.post-restore-link:visited {
  color: var(--fg-dim);
  text-decoration: none;
  font-size: 14px;
}
.post-restore-link:hover {
  color: var(--red);
  text-decoration: none;
}

.post-head {
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--fg-dim);
  margin-bottom: 4px;
  line-height: 1.5;
}
.post-head .avatar {
  width: 20px;
  height: 20px;
  border-radius: 3px;          /* 4chan uses slightly squared avatars */
  object-fit: cover;
  vertical-align: middle;
  margin-right: 4px;
}
.post-head .poster-name {
  color: #117743;                 /* 4chan green name */
  font-weight: 700;
  text-decoration: none;
}
.post-head .poster-name:hover {
  text-decoration: underline;
}
.post-head .opname {
  color: #0f0c5d;                 /* OP gets a different shade (4chan does this) */
}
.post-head .post-datetime {
  font-size: 14px;
  color: var(--fg-dim);
  margin-left: 6px;
}
/* ---- link menu (original post / cross-thread markdown) ---- */
.post-link-menu-wrap {
  position: relative;
  display: inline-block;
  vertical-align: baseline;
}
.post-link-btn {
  padding: 0 2px;
  border: 0;
  background: transparent;
  color: var(--fg-dim);
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
}
.post-link-btn:hover,
.post-link-menu-wrap.is-open .post-link-btn { color: var(--accent); }
.post-link-menu {
  display: none;
  position: absolute; top: calc(100% + 3px); left: 0; z-index: 40;
  min-width: 220px; padding: 3px; background: var(--bg-elev);
  border: 1px solid var(--border); box-shadow: 0 2px 8px rgba(0,0,0,.25);
}
.post-link-menu-wrap.is-open .post-link-menu { display: block; }
.post-link-menu-item {
  display: block; width: 100%;
  padding: 5px 8px; background: transparent; color: var(--fg); text-decoration: none;
  font: 12px var(--font-sans); text-align: left; cursor: pointer; white-space: nowrap;
}
.post-link-menu-item:hover { background: var(--bg-hover); color: var(--fg); text-decoration: none; }
.post-link-menu-item.is-disabled { color: var(--fg-dim); cursor: default; }
.post-link-menu-item.is-disabled:hover { background: transparent; color: var(--fg-dim); }

.post-head .post-no {
  font-family: var(--font-mono);
  font-size: 14px;
  color: var(--fg-dim);
  cursor: pointer;
  text-decoration: none;
}
.post-head .post-no:hover {
  color: var(--accent);
  text-decoration: underline;
}
.post-head .post-id {
  font-family: var(--font-mono);
  font-size: 14px;
  color: var(--link);
  cursor: pointer;
  text-decoration: none;
}
/* Thumbtack marking a stickied thread, inline after the post number. */
.post-sticky-icon {
  font-size: 11px;
  color: var(--fg-dim);
  vertical-align: baseline;
}
.post-head .post-id:hover {
  color: var(--red);
  text-decoration: underline;
}

/* ---- 4chan-style reply reference links ---- */
.post-head-reply-from {
  margin-left: 8px;
  font-family: var(--font-mono);
  font-size: 14px;
}
.post-head-reply-from .reply-from-link {
  color: var(--toolbar);
  text-decoration: none;
  margin-right: 6px;
  white-space: nowrap;   /* keep each >>ref atomic */
  display: inline-block; /* atomic inline boxes have soft-wrap opportunities between them, so the group wraps when the row fills up */
}
.post-head-reply-from .reply-from-link:hover {
  color: #202047;
  text-decoration: underline;
}

.post-reply-to {
  margin: 0 40px 0;
  font-family: var(--font-mono);
  font-size: 14px;
  line-height: 1.35;
}
.post-reply-to .reply-to-link {
  color: var(--red);
  text-decoration: underline;
}
.post-reply-to .reply-to-link:hover {
  color: #ff2a2a;
}

/* ---- "you" indicators on post blocks in the thread view ---- */
/* A post authored by the signed-in user. Dashed left border.
 * Never applied to OPs — they aren't "replies" in the conversation
 * sense the user is tracking their own involvement in. */
.post-block.is-own {
  border-left: 3px dashed rgba(221, 0, 0, .8);
  padding-left: 4px;            /* compensate for the 2px thicker border */
}
/* A reply to one of the signed-in user's posts. Solid left border.
 * Also excluded for OPs (OPs don't have a parent in the thread). */
.post-block.is-replying-to-you {
  border-left: 3px solid rgba(221, 0, 0, .8);
  padding-left: 4px;
}
.post-block.is-dm {
  background-color: #dbf0d6;
  border-color: #aecfa8;
}
/* The DM tint sets border-color on all four sides, which repainted the red
   "you" indicators green. Keep the left border red on DMs too. */
.post-block.is-dm.is-own,
.post-block.is-dm.is-replying-to-you {
  border-left-color: rgba(221, 0, 0, .8);
}
.post-dm-badge {
  position: absolute;
  top: 4px;
  right: 6px;
  color: #4a7a44;
  font-size: 13px;
  line-height: 1;
  pointer-events: none;
}
/* The post block is width:fit-content, so on short posts its right edge sits
   right where the badge floats. Reserve room on the head's first line so the
   No./post-id/reply-from links never slide under the badge. */
.post-block.has-vis-badge .post-head { padding-right: 24px; }
.post-reply-to .post-mentions-toggle {
  margin-left: 2px;
  color: var(--red);
  vertical-align: center;
}
.post-mentions-toggle {
  font-family: var(--font-sans);
  font-size: 12px;
  line-height: 1;
  color: var(--red);
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  display: inline-block;
  transition: transform 0.1s ease;
  vertical-align: baseline;
}
.post-mentions-toggle:hover { color: #ff2a2a; }
.post-mentions-toggle:focus-visible { outline: 1px dotted var(--red); outline-offset: 2px; }
.post-mentions-toggle.is-open { transform: rotate(90deg); }
.post-mentions-toggle i { display: inline-block; }
/* Inline toggle for posts without a reply-to line (rare OPs with mentions) */
.post-body > .post-mentions-toggle {
  margin-right: 2px;
  font-size: 12px;
}

/* ---- media in thread view ---- */
.post-media {
  margin: 4px 27px 8px 0;
  float: left;
  clear: left;
}
.post-media .file-info {
  display: block;
  font-family: var(--font-mono);
  font-size: 14px;
  color: var(--fg-dim);
  margin-bottom: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 420px;
}
.post-media .file-info .file-link {
  color: var(--link);
  text-decoration: underline;
}
.post-media .file-info .file-link:hover {
  color: var(--red);
}

/* OP image: constrained (4chan style — not full width) */
.post-media .op-image {
  max-width: 250px;
  max-height: 250px;
  display: block;
  cursor: pointer;
  filter: drop-shadow(var(--shadow-img));
}

/* Reply thumbnail: small, expands on click (4chan-x behavior) */
.post-media .reply-thumb {
  max-width: 125px;
  max-height: 125px;
  display: block;
  filter: drop-shadow(var(--shadow-img));
  cursor: pointer;
}
.post-media .reply-thumb.expanded {
  max-width: 100%;
  max-height: 600px;
}

/* 4chan-x overlay image (hover preview) — the expanded image that pops
 * up next to the cursor when hovering a post thumbnail. The JS sets
 * style.left / style.top imperatively based on the pointer position, so
 * the element needs `position: fixed` (or absolute relative to a fixed
 * ancestor) for those to take effect; otherwise the image just flows
 * with the document and the inline coordinates are silently ignored,
 * causing the overlay to spawn at whatever natural Y the rest of the
 * body content leaves for it. */
.post-overlay-img,
.post-overlay-video {
  position: fixed;
  z-index: 150;
  pointer-events: none;
  max-width: calc(100vw - 16px);
  max-height: calc(100vh - 16px);
  width: auto;
  height: auto;
  object-fit: contain;
  box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.3);
}

/* Video attachments (collapsed muted preview + expanded player) */
.post-media.post-media-video {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.post-media.post-media-video .file-info {
  display: flex;
  align-items: baseline;
  gap: 8px;
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}

.post-video-collapsed-media {
  display: block;
  cursor: pointer;
  background: #000;
  filter: drop-shadow(var(--shadow-img));
}

.post-video-player {
  display: block;
  /* Was `min(100%, 760px)`. The 100% resolves against .post-media, which is
     itself an auto-width float — a percentage max-width on a child can't
     help size a shrink-to-fit ancestor, so browsers drop it for that pass
     and size the float from max-height/ratio alone instead. For a wide
     (width-bound) video, that intrinsic-only estimate is much wider than
     the 760px the video actually renders at, so the float claims that
     inflated width, shoving anything that can't fit beside it — OP text,
     then replies — down past the float's real bottom edge and leaving a
     gap where the phantom width used to be. Taller vh made the estimate
     worse (height/ratio grows with vh) hence the gap tracking devtools
     height; a vertical video is already height-bound so the estimate and
     the render agree and nothing is left over. calc(100vw - 60px) is
     viewport-relative like the max-height line below it, so it resolves in
     one definite pass with no such ancestor to depend on. */
  max-width: min(calc(100vw - 60px), 760px);
  max-height: min(72vh, 700px);
  width: auto;
  height: auto;
  background: #000;
  filter: drop-shadow(var(--shadow-img));
}
.post-video-player[hidden] {
  display: none !important;
}

.post-video-contract {
  font-family: var(--font-mono);
  font-size: 14px;
  color: var(--link);
  text-decoration: none;
  cursor: pointer;
}
.post-video-contract:hover {
  color: var(--red);
  text-decoration: underline;
}
.post-video-contract[hidden] {
  display: none !important;
}

.post-video-fallback {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--fg-faint);
  background: var(--bg);
  border: 1px dashed var(--border);
}
.post-video-fallback.is-op {
  width: 250px;
  height: 180px;
}
.post-video-fallback.is-reply {
  width: 125px;
  height: 95px;
}

.post-media.post-media-audio {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.audio-placeholder {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: var(--bg);
  border: 1px dashed var(--border);
  color: var(--fg-dim);
  font-size: 22px;
}
.audio-placeholder.is-op {
  width: 250px;
  height: 140px;
}
.audio-placeholder.is-reply {
  width: 125px;
  height: 80px;
}
.audio-mime-label {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-faint);
}
.post-audio-player {
  display: block;
  height: 30px;
  margin: 6px 0 4px;
}

.post-media.post-media-document {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.document-placeholder {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 10px;
  color: var(--fg-dim);
  background: var(--bg);
  border: 1px dashed var(--border);
  font-family: var(--font-mono);
  text-decoration: none;
  filter: drop-shadow(var(--shadow-img));
}
.document-placeholder:hover {
  color: var(--red);
  border-color: var(--red);
}
.document-placeholder i { font-size: 32px; }
.document-placeholder.is-op {
  width: 250px;
  height: 140px;
}
.document-placeholder.is-reply {
  width: 125px;
  height: 80px;
}
.document-mime-label {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--link);
  font-size: 11px;
}

.post-body {
  font-size: 14px;
  line-height: 1.5;
  color: var(--fg);
  overflow-wrap: anywhere;
  margin: 0 40px 13px;
  display: flow-root;          /* avoid the preceding media float without
                                  clipping enlarged inline emoji */
  overflow: visible;
}
.post-body p { margin: 0 0 0.5em 0; }
.post-body p:last-child { margin-bottom: 0; }
/* Inline quotes nest whole posts inside another post's body. Header avatars
   and attachments keep their own sizing and decoration, even when nested.
   Keep exclusions at zero specificity so custom emoji can override this. */
.post-body img:where(:not(.avatar, .post-media img)) {
  max-width: 100%;
  max-height: 480px;
  display: block;
  margin: 4px 0;
  filter: drop-shadow(var(--shadow-img));
}
.post-body img.custom-emoji {
  max-width: none;
  max-height: none;
  display: inline-block;
  margin: 0;
  filter: none;
}
.post-body a { color: var(--link); }

/* ---- inline media embeds (4chan-x style) in post bodies ---- */
.media-embed {
  display: block;
  margin: 4px 0;
}
.media-embed-line {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--font-mono);
  font-size: 12px;
}
.media-embed-icon { color: var(--fg-dim); }
.media-embed[data-kind="youtube"] .media-embed-icon { color: #ff0000; }
.media-embed[data-kind="peertube"] .media-embed-icon { color: #f1680d; }
.media-embed-link {
  color: var(--link);
  word-break: break-all;
  min-width: 0;
}
.media-embed-link:hover { text-decoration: underline; }
.media-embed-toggle {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-faint);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  white-space: nowrap;
}
.media-embed-toggle:hover { color: var(--link); text-decoration: underline; }
.media-embed-container { display: block; margin-top: 6px; }
.media-embed-container[hidden] { display: none; }
.media-embed-container iframe,
.media-embed-container img,
.media-embed-container video {
  max-width: 100%;
  max-height: 70vh;
  display: block;
  border: 0;
}
.media-embed-container audio { width: 100%; max-width: 560px; }

/* ---- polls ---- */
.post-poll {
  margin: 0 40px 13px;
  font-size: 14px;
  /* The post block is width:fit-content, so the poll needs an explicit width or
     it collapses to its longest option label and the result bars get squished.
     Pin a sensible width (capped to the available space on narrow screens). */
  width: 320px;
  max-width: 100%;
  /* Flow with the post text instead of as a block. The OP image is floated
     left; a block-level poll would place its box up beside the float (under
     the image) rather than after the text. inline-block makes it wrap to the
     next line below the floated image, sitting under the post content. */
  display: inline-block;
}
.post-poll .poll-options {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* The post-block is width:fit-content, so a flex container with width:auto
     resolves to shrink-to-fit and ignores .post-poll's declared width — the
     option rows then collapse to their longest label and the result bars get
     squished. Fill the parent so the bars span the full poll width. */
  width: 100%;
}
.post-poll .poll-option { margin: 0; }
/* Interactive (still votable) */
.poll-choice {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;             /* see .post-poll .poll-options: fill the poll width */
  padding: 3px 6px;
  border: 1px solid var(--border);
  border-radius: 3px;
  background: var(--bg-elev);
  cursor: pointer;
}
.poll-choice:hover { background: var(--bg-hover); }
.poll-choice input { margin: 0; cursor: pointer; }
/* Results view */
.poll-result {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;             /* see .post-poll .poll-options: fill the poll width */
  padding: 3px 6px;
  border: 1px solid var(--border);
  border-radius: 3px;
  background: var(--bg-elev);
}
.poll-result-bar {
  position: absolute;
  inset: 0 auto 0 0;
  background: #c5cdea;
  z-index: 0;
  transition: width 0.3s ease;
}
.poll-result.is-leading .poll-result-bar { background: #a9b6e4; }
.poll-result.is-own .poll-result-bar { background: rgb(214, 186, 208); }
.poll-result-label,
.poll-result-pct { position: relative; z-index: 1; }
.poll-result-pct { font-variant-numeric: tabular-nums; color: var(--fg); opacity: 0.8; }
.poll-check { color: var(--accent); }
.poll-vote-btn {
  margin-top: 6px;
  padding: 2px 12px;
  font-size: 13px;
  border: 1px solid var(--border);
  border-radius: 3px;
  background: var(--bg-elev);
  cursor: pointer;
}
.poll-vote-btn:hover:not(:disabled) { background: var(--bg-hover); }
.poll-vote-btn:disabled { opacity: 0.6; cursor: default; }
.poll-footer {
  margin-top: 5px;
  font-size: 12px;
  color: var(--fg);
  opacity: 0.65;
}
/* OP polls align flush-left like the OP body/action row. */
.post-block.is-op .post-poll { margin-left: 0; }
/* A poll post needs the body column at least as wide as the poll (320px) so the
   inline-block poll has room to sit beside the floated OP image, under the
   text. Without this, a short-text post leaves the poll wrapping below the
   image. Scoped via :has() so only posts that actually contain a poll widen —
   compact / inline-quote posts are unaffected. */
.post-block:has(.post-poll) .post-body { min-width: 320px; }
/* Same shape of problem for an expanded quote: the quoted post is a BFC root
   (.post-block is display:flow-root), so it can never overlap the floated
   media — it either fits in the space beside the image or is pushed below it.
   On a short post the fit-content .post-block is only as wide as the image
   plus a line of text, leaving too little room, and the quote drops under the
   media instead of continuing under the body. Widening the body column widens
   the block, which is what keeps the quote in the text column. Dropped on
   narrow screens, where stacking under the image is the right layout anyway. */
.post-block:has(> .post-media):has(> .post-body .is-quote-stack) > .post-body { min-width: 360px; }
@media (max-width: 520px) {
  .post-block:has(> .post-media):has(> .post-body .is-quote-stack) > .post-body { min-width: 0; }
}
.post-body blockquote {
  display: block;
  border-left: none;
  margin-block-start: 1em;
  margin-block-end: 1em;
  margin-inline-start: 40px;
  margin-inline-end: 40px;
  padding: 0;
  unicode-bidi: isolate;
  color: #789922;                  /* 4chan greentext color */
  font-weight: 400;
}
.post-body blockquote a {
  color: var(--red);               /* >>reply links in red like 4chan */
  text-decoration: none;
}
.post-body blockquote a:hover {
  text-decoration: underline;
}
.post-body code, .post-body pre {
  font-family: var(--font-mono);
  background: var(--bg);
  padding: 1px 4px;
  border-radius: 1px;
  font-size: 13px;
}
.post-block .spoiler {
  color: var(--fg-faint);
  font-style: italic;
  font-size: 13px;
  margin-bottom: 4px;
}
/* ---- emoji reactions row (above .post-foot) ---- */
.post-reactions {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  align-items: center;
  margin: 2px 0 0;
  justify-content: flex-end;
  clear: both;                     /* sit below floated .post-media, not beside it */
  min-height: 0;                   /* allow flex shrink when empty */
}
.post-reactions:empty { display: none; }   /* no row at all when no reactions */
.post-reaction-btn {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-faint);
  background-color: transparent;
  border: 0;
  border-radius: 0;
  padding: 2px 8px 2px 0px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  line-height: 1.2;
  user-select: none;
}
.post-reaction-btn:hover:not(:disabled) {
  /* background: var(--bg-hover); */
  color: var(--red);
  /* A chip has no background to light up any more, so it grows instead.
     Untransitioned on purpose: the row is dense and a chip that eases into
     size reads as sluggish when you're sweeping across several. transform
     doesn't reflow, so its neighbours don't budge. */
  transform: scale(1.18);
}
.post-reaction-btn:hover:not(:disabled) .post-reaction-count { color: var(--red); }
.post-reaction-btn:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}
/* Your own reaction reads as red text rather than a tinted chip — with the
   chip background gone there's nothing for a tint to sit in. The bold count
   below carries the rest of the distinction. */
.post-reaction-btn.is-mine {
  color: #b30000;
}
.post-reaction-btn.is-mine:hover:not(:disabled) {
  color: var(--red);
}
.post-reaction-emoji {
  display: inline-block;
  width: 1.1em;
  height: 1.1em;
  vertical-align: -0.22em;
  object-fit: contain;
  flex-shrink: 0;
}
.post-reaction-emoji-unicode {
  display: inline-block;
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
  font-size: 14px;
  line-height: 1;
  flex-shrink: 0;
}
.post-reaction-count {
  font-variant-numeric: tabular-nums;
  color: var(--fg-faint);
}
.post-reaction-btn.is-mine .post-reaction-count {
  color: #b30000;
  font-weight: 700;
}

.post-foot {
  display: flex;
  align-items: center;
  gap: 12px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-faint);
  width: 100%;
  margin-top: 6px;
  clear: both;                     /* sit below floated .post-media, not beside it */
}
.post-foot-owner,
.post-foot-primary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.post-foot-primary { margin-left: auto; }
.post-action {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-faint);
  background: transparent;
  border: 0;
  border-radius: 1px;
  padding: 2px 3px 0px 2px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  line-height: 1;
}
.post-action:hover:not(:disabled) {
  /* background: var(--bg-hover); */
  color: var(--red);
}
.post-action.is-liked {
  color: var(--red);
}
.post-action.is-liked:hover:not(:disabled) {
  color: #b30000;
}
/* Stickied reads as "on", like the liked and reposted states above, in the
   same blue as the thumbtack it puts on the card. Disabled when the post isn't
   public, which is all either server will sticky. */
.post-action-sticky.is-sticky {
  color: #3F77CC;
}
.post-action-sticky.is-sticky:hover:not(:disabled) {
  color: #2f5da3;
}
.post-action-sticky:disabled {
  opacity: 0.35;
  cursor: default;
}
.post-action.is-reposted {
  color: #0f6f00;
}
.post-action.is-reposted:hover:not(:disabled) {
  color: #0c5900;
}
.post-action.is-bookmarked {
  color: #9a7200;
}
.post-action.is-bookmarked:hover:not(:disabled) {
  color: #7d5d00;
}
.post-action.is-watching,
.post-action.is-watching:hover:not(:disabled) {
  color: var(--red);
}
.post-action-delete:hover:not(:disabled) {
  color: #b30000;
}
.post-action-count {
  font-variant-numeric: tabular-nums;
}
.post-foot .ext { margin-left: auto; color: var(--fg-dim); }

/* ---- inline/stacked toggled post previews (4chan-x-ish) ---- */
.inline-stack {
  margin: 8px 0 2px 12px;
  padding-left: 8px;
  border-left: 1px dashed #B7C5D9;
}
.inline-stack.is-reply-to-stack {
  margin-top: 4px;
  margin-bottom: 4px;
}
/* An expanded quote: a post from outside this thread, distinguished from a
   reply-to expansion by a solid border. */
.inline-stack.is-quote-stack {
  margin-top: 6px;
  border-left-style: solid;
}
.quote-body-preview {
  max-height: 12em;
  overflow: hidden;
}
.quote-show-more {
  display: block;
  border: 0;
  background: none;
  padding: 2px 0;
  color: var(--red);
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}
.quote-show-more[hidden] { display: none; }
.inline-post + .inline-post {
  margin-top: 6px;
}
.post-block.is-inline {
  max-width: calc(100% - 8px);
  background: #dbe2f8;
}

.post-block.is-new-reply {
  outline: 2px solid var(--red);
  outline-offset: 1px;
}

/* A post whose No. link has been followed — clicked in-thread, or opened via a
   permalink to a reply. Painted permanently so the linked-to post stays easy
   to spot. Placed after is-op/is-dm/is-inline so it wins the background. */
.post-block.is-linked {
  background: rgb(214, 186, 208);
}

/* 4chan-x style "unread line": marks where posts new since the last visit or
   refresh begin in a thread. */
.replies-wrap > .unread-line {
  border: none;
  border-top: 1px solid var(--red);
  margin: -3px 0 3px;
  width: auto;
  display: flow-root;   /* sits beside the OP's file float rather than being
                           drawn across it */
}

/* ---- >>reply links (quotelinks) inside post bodies ---- */
.post-body .quotelink {
  color: var(--red);
  text-decoration: underline;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: inherit;
}
.post-body .quotelink:hover {
  color: #f00;
}
/* What a reference to a post that can't be shown previews as. */
.post-preview .ppv-error {
  color: var(--red);
  font-family: var(--font-mono);
  font-size: 12px;
}
/* A reference lifted out of the trailing "RE:" line and put above the text,
   where an imageboard puts the posts you're answering. */
.post-body > .post-ref-line {
  margin: 0 0 2px;
}

/* ---- post avatar (same as post-head) ---- */
.post-media.is-avatar-banner {
  float: left;
  clear: left;
  margin: 4px 27px 4px 0;
  width: 200px;
  height: 150px;
  aspect-ratio: 4 / 3;
  max-height: none;
  background: var(--bg-elev);
  filter: drop-shadow(var(--shadow-img));
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}
.post-media.is-avatar-banner .banner-bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  filter: brightness(0.55) saturate(0.9);
}
.post-media.is-avatar-banner .thumb-avatar {
  position: relative;
  z-index: 1;
  width: 55%;
  aspect-ratio: 1;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid rgba(255,255,255,0.4);
  box-shadow: 0 2px 6px rgba(0,0,0,0.4);
}
/* Thread-view OP composite and the profile card avatar open the gallery
   lightbox on click. Not the anonymous placeholder — that click is a no-op. */
.post-media.is-avatar-banner:not(.is-anonymous) .thumb-avatar,
.post-media.is-avatar-banner:not(.is-anonymous) .banner-bg,
#profile-bio .profile-avatar {
  cursor: pointer;
}
.post-media.is-avatar-banner .thumb-avatar-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-elev);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: 32px;
  font-weight: 600;
}

/* ---- greentext (text lines starting with >, not links) ---- */
/* Not scoped to .post-body: the catalog card body and hover preview format
   content through the same pipeline and need the same green. */
.greentext {
  color: #789922;
}

/* ---- post preview popup (4chan-x hover preview) ---- */
.post-preview {
  display: none;
  position: fixed;
  z-index: 200;
  max-width: 500px;
  max-height: 400px;
  overflow: auto;
  background: #D6DAF0;
  border: 2px solid #B7C5D9;
  box-shadow: 2px 2px 6px rgba(0,0,0,0.25);
  pointer-events: none;
  padding: 6px 8px;
  font-size: 13px;
  line-height: 1.4;
}
.post-preview.is-visible {
  display: block;
}
.post-preview .ppv-head {
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--fg-dim);
  margin-bottom: 4px;
}
.post-preview .ppv-head .ppv-name {
  color: #117743;
  font-weight: 700;
}
.post-preview .ppv-head .ppv-id {
  color: var(--red);
  font-family: var(--font-mono);
}
.post-preview .ppv-body {
  color: var(--fg);
  word-break: break-word;
}
.post-preview .ppv-body p { margin: 0 0 0.5em; }
.post-preview .ppv-body p:last-child { margin-bottom: 0; }

/* ---- profile hover card (shares the post-preview look) ---- */
.profile-preview {
  display: none;
  position: fixed;
  z-index: 200;
  width: 320px;
  max-width: 90vw;
  max-height: 400px;
  overflow: hidden;
  background: #D6DAF0;
  border: 2px solid #B7C5D9;
  box-shadow: 2px 2px 6px rgba(0,0,0,0.25);
  pointer-events: none;
  padding: 8px 10px;
  font-size: 13px;
  line-height: 1.4;
}
.profile-preview.is-visible { display: block; }
/* Compact tweaks for the reused .profile-card markup inside the popup. */
.profile-preview .profile-card { gap: 10px; }
.profile-preview .profile-avatar { width: 56px; height: 56px; }
.profile-preview .profile-display-name { font-size: 14px; }
.profile-preview .profile-note {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Pointer-following account list for likes, reposts and emoji reactions. */
.activity-hover {
  display: none;
  position: fixed;
  z-index: 161;
  width: max-content;
  min-width: 150px;
  max-width: min(280px, calc(100vw - 16px));
  max-height: min(360px, calc(100vh - 16px));
  overflow: hidden auto;
  padding: 5px 7px;
  color: var(--fg);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.3);
  font: 11px var(--font-mono);
  pointer-events: none;
}
.activity-hover.is-visible { display: block; }
.activity-hover-title {
  margin-bottom: 4px;
  padding-bottom: 3px;
  color: var(--fg-dim);
  border-bottom: 1px dashed var(--border-soft);
  font-weight: 700;
}
.activity-hover-account {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
  padding: 2px 0;
}
.activity-hover-account-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.activity-hover-name { color: #117743; font-weight: 700; }
.activity-hover-handle { color: var(--fg-faint); margin-left: 4px; }
.activity-hover-state { color: var(--fg-faint); padding: 3px 0; }

/* ---- emoji picker (4chan-x style, no border-radius) ---- */
.emoji-picker {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  z-index: 130;
  align-items: flex-start;
  justify-content: center;
  padding-top: 8vh;
}
.emoji-picker[hidden] { display: none !important; }
.emoji-picker.is-open { display: flex; }
.emoji-picker-box {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 0;
  width: 420px;
  max-width: calc(100% - 24px);
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 4px 24px rgba(0,0,0,0.4);
}
.emoji-picker-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px 6px 12px;
  border-bottom: 1px solid var(--border-soft);
  background: #D6DAF0;
}
.emoji-picker-head h2 {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 700;
  color: #0F4D92;
  margin: 0;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.emoji-picker-close,
.emoji-picker-refresh {
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1;
  color: var(--fg-dim);
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  border-radius: 0;
  padding: 3px 6px;
  cursor: pointer;
}
.emoji-picker-close:hover,
.emoji-picker-refresh:hover { background: var(--bg-hover); color: var(--fg); }

.emoji-picker-search-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px;
  border-bottom: 1px solid var(--border-soft);
  background: var(--bg);
}
.emoji-picker-search {
  flex: 1;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 12px;
  background: var(--bg-elev);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 0;
  padding: 4px 6px;
  outline: none;
}
.emoji-picker-search:focus { border-color: var(--accent); }
.emoji-picker-status {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-faint);
  white-space: nowrap;
  flex-shrink: 0;
}

.emoji-picker-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 4px;
  min-height: 0;
  max-height: 56vh;
}
.emoji-picker-body .empty {
  padding: 30px 12px;
  text-align: center;
  color: var(--fg-faint);
  font-family: var(--font-mono);
  font-size: 12px;
}

.emoji-picker-section {
  margin-bottom: 4px;
}
.emoji-picker-section-title {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  color: var(--fg-faint);
  text-transform: uppercase;
  letter-spacing: 0.6px;
  padding: 6px 4px 2px;
  border-bottom: 1px dashed var(--border-soft);
  margin-bottom: 4px;
}
.emoji-picker-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
  gap: 2px;
}
.emoji-picker-cell {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  padding: 4px 2px 3px;
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  border-radius: 0;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--fg-dim);
  min-height: 56px;
  text-align: center;
  line-height: 1.1;
  user-select: none;
}
.emoji-picker-cell:hover {
  background: var(--bg-hover);
  color: var(--fg);
  border-color: var(--border);
}
.emoji-picker-cell:active {
  background: #c0c8e0;
}
.emoji-picker-cell-img,
.emoji-picker-cell-unicode {
  display: block;
  width: 24px;
  height: 24px;
  object-fit: contain;
  flex-shrink: 0;
}
.emoji-picker-cell-unicode {
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
  font-size: 22px;
  line-height: 24px;
  text-align: center;
}
.emoji-picker-cell-name {
  width: 100%;
  font-size: 9px;
  color: var(--fg-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 0 2px;
}
.emoji-picker-cell:hover .emoji-picker-cell-name { color: var(--fg-dim); }
.emoji-picker-favorites { margin-bottom: 2px; }
.emoji-picker-favorites-edit {
  display: block;
  margin: 3px auto 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--link);
  font: 10px var(--font-mono);
  text-decoration: underline;
  cursor: pointer;
}
.emoji-picker-cell.is-editing {
  border-style: dotted;
  border-color: var(--fg-faint);
}
.emoji-picker-cell.is-editing > :not(.emoji-picker-cell-edit-prompt) {
  filter: grayscale(1);
  opacity: 0.18;
}
.emoji-picker-cell.is-replacing {
  border-color: var(--accent);
  background: var(--bg-hover);
}
.emoji-picker-cell-edit-prompt {
  position: absolute;
  inset: 2px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--fg-dim);
  font-size: 9px;
  line-height: 1.15;
  text-align: center;
  white-space: normal;
}

.emoji-picker-help {
  padding: 4px 10px 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--fg-faint);
  border-top: 1px solid var(--border-soft);
  background: var(--bg);
  min-height: 14px;
}
.emoji-picker-help code {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--fg-dim);
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  padding: 0 3px;
}

/* ---- compose modal (4chan-x style, no border-radius) ---- */
.compose-modal {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  z-index: 120;
  align-items: flex-start;
  justify-content: center;
  padding-top: 8vh;
}
.compose-modal[hidden] { display: none !important; }
.compose-modal.is-open { display: flex; }
.compose-modal-box {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 0;
  width: 520px;
  max-width: calc(100% - 24px);
  max-height: 84vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 4px 24px rgba(0,0,0,0.4);
}
.compose-modal-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px 6px 12px;
  border-bottom: 1px solid var(--border-soft);
  background: #D6DAF0;
}
.compose-modal-head h2 {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 700;
  color: #0F4D92;
  margin: 0;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.compose-modal-close {
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1;
  color: var(--fg-dim);
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  border-radius: 0;
  padding: 3px 6px;
  cursor: pointer;
}
.compose-modal-close:hover { background: var(--bg-hover); color: var(--fg); }

.compose-form {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1 1 auto;
}
.compose-reply-to {
  font-family: var(--font-mono);
  font-size: 14px;
  line-height: 1.35;
  padding: 6px 10px;
  border-bottom: 1px solid var(--border-soft);
  background: var(--bg);
}
.compose-reply-to .reply-to-link {
  color: var(--red);
  text-decoration: underline;
}
.compose-reply-to .reply-to-link:hover { color: #ff2a2a; }

.compose-subject {
  font-family: var(--font-sans);
  font-size: 14px;
  background: var(--bg-elev);
  color: var(--fg);
  border: 0;
  border-bottom: 1px solid var(--border-soft);
  border-radius: 0;
  padding: 8px 12px;
  outline: none;
  box-sizing: border-box;
}
.compose-subject:focus { background: #fff; }

/* Stands in for the textarea while the eye is on. Same box, so toggling
   doesn't move the toolbar under the pointer; scrolls rather than growing,
   since a long post shouldn't push the Post button off screen. */
.compose-preview {
  flex: 1 1 auto;
  min-height: 140px;
  max-height: 50vh;
  overflow-y: auto;
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.45;
  background: var(--bg-elev);
  color: var(--fg);
  border-bottom: 1px solid var(--border-soft);
  padding: 10px 12px;
  box-sizing: border-box;
}
.compose-preview-empty {
  color: var(--fg-faint);
  font-style: italic;
}
/* Shown only when the server renders posts itself and wouldn't tell us how —
   the preview is then our best guess rather than the real thing. */
.compose-preview-note {
  margin-top: 10px;
  padding-top: 6px;
  border-top: 1px dashed var(--border-soft);
  color: var(--fg-faint);
  font-family: var(--font-mono);
  font-size: 11px;
}
.compose-toolbar-btn.is-active {
  color: var(--red);
}

.compose-content {
  flex: 1 1 auto;
  min-height: 140px;
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.45;
  background: var(--bg-elev);
  color: var(--fg);
  border: 0;
  border-bottom: 1px solid var(--border-soft);
  border-radius: 0;
  padding: 10px 12px;
  outline: none;
  resize: vertical;
  box-sizing: border-box;
}
.compose-content:focus { background: #fff; }

/* ---- compose autocomplete (typeahead for :emoji: and @mention) ----
 * Rendered as a position:fixed dropdown attached to document.body, placed
 * at the caret by getCaretCoordinates(). */
.compose-autocomplete {
  position: fixed;
  z-index: 200;                 /* above the compose modal (z-index 120) */
  min-width: 220px;
  max-width: 320px;
  max-height: 240px;
  overflow-y: auto;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
  font-family: var(--font-sans);
  font-size: 13px;
  padding: 2px;
}
.compose-autocomplete[hidden] { display: none; }
.compose-autocomplete-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 3px 8px;
  cursor: pointer;
  color: var(--fg);
  border-radius: 0;
}
.compose-autocomplete-row.is-active,
.compose-autocomplete-row:hover {
  background: var(--bg-hover);
}
.compose-autocomplete-emoji {
  width: 18px;
  height: 18px;
  object-fit: contain;
  flex: 0 0 auto;
}
/* A unicode emoji occupies the same box as a custom emoji's image, so both
   kinds of row line their labels up. */
.compose-autocomplete-emoji-char {
  font-size: 15px;
  line-height: 18px;
  text-align: center;
}
.compose-autocomplete-label {
  font-family: var(--font-mono);
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.compose-autocomplete-avatar {
  width: 20px;
  height: 20px;
  object-fit: cover;
  flex: 0 0 auto;
}
.compose-autocomplete-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.2;
}
.compose-autocomplete-name {
  font-size: 13px;
  color: var(--fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.compose-autocomplete-name .custom-emoji { vertical-align: -2px; height: 14px; width: 14px; }
.compose-autocomplete-acct {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.compose-attachments {
  list-style: none;
  margin: 0;
  padding: 6px 10px;
  border-bottom: 1px solid var(--border-soft);
  background: var(--bg);
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.compose-attachments:empty,
.compose-attachments[hidden] { display: none; }
.compose-attachment {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 4px 2px 4px;
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  border-radius: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-dim);
  max-width: 220px;
}
.compose-attachment.is-uploading { opacity: 0.6; }
.compose-attachment.is-failed { border-color: var(--red); color: var(--red); flex-wrap: wrap; }

/* Drag-to-reorder. The chips are the running order of the media on the post,
   so from two attachments up they become draggable; touch-action: none keeps
   the browser from stealing the gesture as a scroll. */
.compose-attachments.is-sortable .compose-attachment {
  cursor: grab;
  touch-action: none;
  user-select: none;
}
.compose-attachments.is-sortable .compose-attachment:focus-visible {
  outline: 1px solid var(--accent);
  outline-offset: 1px;
}
.compose-attachments.is-dragging .compose-attachment { cursor: grabbing; }
/* The chip left in place marks the gap the drop would fill. */
.compose-attachment.is-dragging {
  opacity: 0.3;
  border-style: dashed;
}
.compose-attachment.is-ghost {
  position: fixed;
  z-index: 210;                 /* above the compose modal (120) */
  pointer-events: none;
  opacity: 0.9;
  border-color: var(--fg-dim);
  box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.35);
}
.compose-attachment-remove { touch-action: auto; }
.compose-attachment-thumb {
  width: 24px;
  height: 24px;
  object-fit: cover;
  background: var(--bg);
  flex-shrink: 0;
}
.compose-attachment-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 140px;
}
.compose-attachment-error {
  flex-basis: 100%;
  white-space: normal;
  word-break: break-word;
  line-height: 1.25;
}
.compose-attachment-remove {
  font-family: var(--font-sans);
  font-size: 12px;
  line-height: 1;
  color: var(--fg-dim);
  background: transparent;
  border: 0;
  padding: 0 2px;
  cursor: pointer;
}
.compose-attachment-remove:hover { color: var(--red); }

.compose-status {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-dim);
  padding: 4px 10px 0;
  min-height: 16px;
}
.compose-status.is-error { color: #b00; }
.compose-status.is-uploading { color: var(--accent); }

.compose-toolbar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px 8px;
  background: var(--bg);
}
.compose-nsfw {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: var(--font-sans);
  font-size: 11px;
  color: var(--fg-dim);
  cursor: pointer;
  user-select: none;
  margin-right: 4px;
  padding: 2px 4px;
  border: 1px solid var(--border-soft);
  border-radius: 0;
  background: var(--bg-elev);
}
.compose-nsfw:hover { background: var(--bg-hover); }
.compose-nsfw input { margin: 0; cursor: pointer; }
.compose-nsfw input:checked + span { color: #b30000; font-weight: 700; }

.compose-toolbar-btn {
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1;
  color: var(--fg-dim);
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  border-radius: 0;
  padding: 4px 6px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.compose-toolbar-btn:hover:not(:disabled) {
  background: var(--bg-hover);
  color: var(--fg);
  border-color: var(--border);
}
.compose-toolbar-btn:disabled { opacity: 0.4; cursor: not-allowed; }

/* Visibility scope picker (Public / Followers / Direct). */
.compose-visibility { position: relative; display: inline-flex; }
.compose-visibility-menu {
  position: absolute;
  bottom: calc(100% + 4px);
  left: 0;
  margin: 0;
  padding: 2px;
  list-style: none;
  min-width: 140px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  box-shadow: 0 2px 12px rgba(0,0,0,0.3);
  z-index: 10;
}
.compose-visibility-menu li {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--fg);
  padding: 5px 8px;
  cursor: pointer;
  white-space: nowrap;
}
.compose-visibility-menu li:hover { background: var(--bg-hover); }
.compose-visibility-menu li.is-selected { font-weight: 700; }
.compose-visibility-menu li i { width: 16px; text-align: center; color: var(--fg-dim); }

/* Direct messages get the same green hue used for DMs in thread view. */
.compose-modal.compose-is-direct .compose-modal-box {
  background: #dbf0d6;
  border-color: #aecfa8;
}
.compose-modal.compose-is-direct .compose-modal-head {
  background: #cfe8c9;
}
.compose-modal.compose-is-direct .compose-modal-head h2 { color: #2f6b29; }
.compose-modal.compose-is-direct .compose-toolbar { background: #d4ecce; }
.compose-modal.compose-is-direct .compose-visibility-btn {
  color: #2f6b29;
  border-color: #aecfa8;
}

/* ---- multi-attachment gallery ---- */
/* "Gallery" link centered under a multi-attachment post's thumbnail. */
.post-gallery-link {
  display: block;
  text-align: center;
  margin-top: 4px;
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--link);
  text-decoration: none;
  cursor: pointer;
}
.post-gallery-link:hover { color: var(--red); text-decoration: underline; }
.post-gallery-link i { margin-right: 4px; }
/* In the flex video/audio media boxes, stretch so text-align can center it. */
.post-media-video .post-gallery-link,
.post-media-audio .post-gallery-link,
.post-media-document .post-gallery-link { align-self: stretch; }

.gallery-modal {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  z-index: 130;
  align-items: center;
  justify-content: center;
  padding: 4vh 16px;
}
.gallery-modal[hidden] { display: none !important; }
.gallery-modal.is-open { display: flex; }
.gallery-modal-box {
  position: relative;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  width: 1000px;
  max-width: 100%;
  height: 92vh;
  display: flex;
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.5);
}
.gallery-modal-close {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 2;
  font-size: 14px;
  line-height: 1;
  color: var(--fg-dim);
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  padding: 4px 7px;
  cursor: pointer;
}
.gallery-modal-close:hover { background: var(--bg-hover); color: var(--fg); }

/* Left column of thumbnails. */
.gallery-thumbs {
  flex: 0 0 132px;
  overflow-y: auto;
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: var(--bg);
  border-right: 1px solid var(--border-soft);
}
.gallery-thumb {
  display: block;
  width: 116px;
  height: 116px;
  padding: 0;
  border: 1px solid var(--border-soft);
  background: var(--bg-elev);
  cursor: pointer;
  overflow: hidden;
  flex: 0 0 auto;
}
.gallery-thumb:hover { border-color: var(--border); }
.gallery-thumb.is-active { border-color: var(--accent); outline: 1px solid var(--accent); }
.gallery-thumb-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.gallery-thumb-video { position: relative; display: block; width: 100%; height: 100%; }
.gallery-thumb-play {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 1em;
  height: 1em;
  font-size: 22px;
  color: #fff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
  pointer-events: none;
}

/* Center stage: expanded media + file link underneath. */
.gallery-main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  padding: 12px;
  gap: 8px;
}
.gallery-stage {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.gallery-stage-img,
.gallery-stage-video {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}
.gallery-stage-audio {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}
.gallery-caption {
  flex: 0 0 auto;
  text-align: center;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 13px;
  line-height: 1.4;
  color: var(--fg-dim);
  max-width: 100%;
}
.gallery-caption[hidden] { display: none !important; }
.gallery-fileinfo {
  flex: 0 0 auto;
  text-align: center;
  white-space: normal;
}

/* Iconographic placeholder for audio / PDF / undisplayable files. */
.gallery-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  height: 100%;
  color: var(--fg-faint);
  background: var(--bg);
}
.gallery-placeholder i { font-size: 30px; }
.gallery-placeholder-ext {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.5px;
  color: var(--fg-dim);
}
.gallery-stage-placeholder {
  min-width: 220px;
  min-height: 220px;
  border: 1px solid var(--border-soft);
}
.gallery-stage-placeholder i { font-size: 64px; }
.gallery-stage-placeholder .gallery-placeholder-ext { font-size: 14px; }

.compose-spacer { flex: 1; }
.compose-count {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-faint);
  font-variant-numeric: tabular-nums;
  padding: 0 4px;
}
.compose-count.is-over { color: var(--red); font-weight: 700; }

.compose-submit {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 700;
  background: var(--accent);
  color: white;
  border: 1px solid var(--accent);
  border-radius: 0;
  padding: 5px 14px;
  cursor: pointer;
}
.compose-submit:hover:not(:disabled) { background: #0a3a72; }
.compose-submit:disabled {
  background: var(--bg-elev);
  color: var(--fg-faint);
  border-color: var(--border);
  cursor: not-allowed;
}
.compose-submit.is-loading { opacity: 0.6; cursor: progress; }

/* A toolbar button whose feature is currently attached (e.g. a poll). */
.compose-toolbar-btn.is-active {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}
.compose-toolbar-btn.is-active:hover:not(:disabled) { background: #0a3a72; color: #fff; }

/* ---- compose poll chip (shown when a poll is attached) ---- */
.compose-poll-chip {
  display: flex;
  align-items: stretch;
  gap: 4px;
  padding: 6px 10px;
  border-bottom: 1px solid var(--border-soft);
  background: var(--bg);
}
.compose-poll-chip[hidden] { display: none; }
.compose-poll-chip-edit {
  flex: 1 1 auto;
  text-align: left;
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--fg);
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  border-radius: 0;
  padding: 4px 8px;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.compose-poll-chip-edit:hover { background: var(--bg-hover); border-color: var(--border); }
.compose-poll-chip-edit i { color: var(--accent); margin-right: 4px; }
.compose-poll-chip-remove {
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--fg-dim);
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  border-radius: 0;
  padding: 4px 8px;
  cursor: pointer;
}
.compose-poll-chip-remove:hover { background: var(--bg-hover); color: #b00; border-color: var(--border); }

/* ---- poll composer dialog (sits above the compose modal) ---- */
.poll-composer {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  z-index: 140;
  align-items: flex-start;
  justify-content: center;
  padding-top: 10vh;
}
.poll-composer[hidden] { display: none !important; }
.poll-composer.is-open { display: flex; }
.poll-composer-box {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  width: 380px;
  max-width: calc(100% - 24px);
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
}
.poll-composer-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px 6px 12px;
  border-bottom: 1px solid var(--border-soft);
  background: #D6DAF0;
}
.poll-composer-head h2 {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 700;
  color: #0F4D92;
  margin: 0;
  flex: 1;
}
.poll-composer-close {
  font-size: 14px;
  color: var(--fg-dim);
  background: transparent;
  border: 0;
  cursor: pointer;
  padding: 4px 6px;
}
.poll-composer-close:hover { background: var(--bg-hover); color: var(--fg); }
.poll-composer-body {
  padding: 12px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.poll-composer-options {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.poll-composer-option {
  display: flex;
  align-items: center;
  gap: 6px;
}
.poll-composer-option-input {
  flex: 1 1 auto;
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--fg);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 0;
  padding: 5px 8px;
  outline: none;
  box-sizing: border-box;
}
.poll-composer-option-input:focus { background: #fff; border-color: var(--accent); }
.poll-composer-option-remove {
  font-size: 12px;
  color: var(--fg-dim);
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  border-radius: 0;
  padding: 5px 8px;
  cursor: pointer;
  flex-shrink: 0;
}
.poll-composer-option-remove:hover:not(:disabled) { background: var(--bg-hover); color: #b00; }
.poll-composer-option-remove:disabled { opacity: 0.35; cursor: not-allowed; }
.poll-composer-add {
  align-self: flex-start;
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--accent);
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  border-radius: 0;
  padding: 4px 10px;
  cursor: pointer;
}
.poll-composer-add:hover:not(:disabled) { background: var(--bg-hover); }
.poll-composer-add:disabled { opacity: 0.4; cursor: not-allowed; }
.poll-composer-settings {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 4px;
  border-top: 1px solid var(--border-soft);
  font-family: var(--font-sans);
  font-size: 13px;
}
.poll-composer-multiple {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
}
.poll-composer-duration-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.poll-composer-duration {
  font-family: var(--font-sans);
  font-size: 13px;
  padding: 4px 6px;
  border: 1px solid var(--border);
  border-radius: 0;
  background: var(--bg-elev);
  color: var(--fg);
}
.poll-composer-status { font-size: 12px; min-height: 14px; color: var(--fg-dim); }
.poll-composer-status.is-error { color: #b00; }
.poll-composer-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border-top: 1px solid var(--border-soft);
  background: var(--bg);
}
.poll-composer-spacer { flex: 1 1 auto; }
.poll-composer-remove {
  font-family: var(--font-sans);
  font-size: 12px;
  color: #b00;
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  border-radius: 0;
  padding: 5px 12px;
  cursor: pointer;
}
.poll-composer-remove:hover { background: var(--bg-hover); }
.poll-composer-cancel {
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--fg);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 0;
  padding: 5px 12px;
  cursor: pointer;
}
.poll-composer-cancel:hover { background: var(--bg-hover); }
.poll-composer-save {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 700;
  color: #fff;
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 0;
  padding: 5px 14px;
  cursor: pointer;
}
.poll-composer-save:hover { background: #0a3a72; }

/* ---- create-board dialog (opened from the [+] in the board nav) ---- */
.board-create {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  z-index: 140;
  align-items: flex-start;
  justify-content: center;
  padding-top: 10vh;
}
.board-create[hidden] { display: none !important; }
.board-create:not([hidden]) { display: flex; }
.board-create-box {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  width: 380px;
  max-width: calc(100% - 24px);
  display: flex;
  flex-direction: column;
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
}
.board-create-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px 6px 12px;
  border-bottom: 1px solid var(--border-soft);
  background: #D6DAF0;
}
.board-create-head h2 {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 700;
  color: #0F4D92;
  margin: 0;
  flex: 1;
}
.board-create-close {
  font-size: 14px;
  color: var(--fg-dim);
  background: transparent;
  border: 0;
  cursor: pointer;
  padding: 4px 6px;
}
.board-create-close:hover { background: var(--bg-hover); color: var(--fg); }
.board-create-form {
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-family: var(--font-sans);
  font-size: 13px;
}
.board-create-form input {
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--fg);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 0;
  padding: 5px 8px;
  outline: none;
  box-sizing: border-box;
}
.board-create-form input:focus { background: #fff; border-color: var(--accent); }
.board-create-form input:disabled { color: var(--fg-dim); background: var(--bg-hover); }
.board-create-form textarea {
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--fg);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 0;
  padding: 5px 8px;
  outline: none;
  resize: vertical;
}
.board-create-form textarea:focus { background: #fff; border-color: var(--accent); }
.board-create-optional { color: var(--fg-dim); font-weight: 400; }
.board-create-hint { color: var(--fg-dim); font-size: 12px; }
.board-create-status { font-size: 12px; min-height: 1em; }
.board-create-status.is-error { color: #b00; }
.board-create-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding-top: 4px;
}
.board-create-cancel {
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--fg);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 0;
  padding: 5px 12px;
  cursor: pointer;
}
.board-create-cancel:hover { background: var(--bg-hover); }
.board-create-save {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 700;
  color: #fff;
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 0;
  padding: 5px 14px;
  cursor: pointer;
}
.board-create-save:hover:not(:disabled) { background: #0a3a72; }
.board-create-save:disabled { opacity: 0.5; cursor: not-allowed; }

/* ---- subscribe (Mitra monero-subscription) modal ---- */
.subscribe-modal {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  z-index: 140;
  align-items: flex-start;
  justify-content: center;
  padding-top: 10vh;
}
.subscribe-modal[hidden] { display: none !important; }
.subscribe-modal:not([hidden]) { display: flex; }
.subscribe-modal-box {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  width: 380px;
  max-width: calc(100% - 24px);
  display: flex;
  flex-direction: column;
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
}
.subscribe-modal-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px 6px 12px;
  border-bottom: 1px solid var(--border-soft);
  background: #D6DAF0;
}
.subscribe-modal-head h2 {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 700;
  color: #0F4D92;
  margin: 0;
  flex: 1;
}
.subscribe-modal-close {
  font-size: 14px;
  color: var(--fg-dim);
  background: transparent;
  border: 0;
  cursor: pointer;
  padding: 4px 6px;
}
.subscribe-modal-close:hover { background: var(--bg-hover); color: var(--fg); }
.subscribe-modal-body {
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-family: var(--font-sans);
  font-size: 13px;
}
.subscribe-modal-body .empty { color: var(--fg-dim); text-align: center; padding: 8px 0; }
.subscribe-price {
  font-size: 16px;
  font-weight: 700;
}
.subscribe-price .price-subtext { font-size: 13px; font-weight: 400; color: var(--fg-dim); }
.subscribe-status-line { color: var(--fg-dim); }
.subscribe-pay-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
}
.subscribe-pay-row input[type="number"] {
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--fg);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 0;
  padding: 5px 8px;
  outline: none;
  width: 90px;
  box-sizing: border-box;
}
.subscribe-pay-row input[type="number"]:focus { background: #fff; border-color: var(--accent); }
.subscribe-pay-amount { margin-left: auto; }
.subscribe-modal-status { font-size: 12px; min-height: 1em; color: var(--fg-dim); }
.subscribe-modal-status.is-error { color: #b00; }
.subscribe-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding-top: 2px;
}
.subscribe-modal-actions button {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 700;
  color: #fff;
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 0;
  padding: 5px 14px;
  cursor: pointer;
}
.subscribe-modal-actions button.secondary {
  font-weight: 400;
  color: var(--fg);
  background: var(--bg-elev);
  border: 1px solid var(--border);
}
.subscribe-modal-actions button:hover:not(:disabled) { background: #0a3a72; }
.subscribe-modal-actions button.secondary:hover:not(:disabled) { background: var(--bg-hover); }
.subscribe-modal-actions button:disabled { opacity: 0.5; cursor: not-allowed; }
.subscribe-invoice { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.subscribe-invoice .payment-header { width: 100%; text-align: center; }
.subscribe-invoice .payment-address {
  font-family: var(--font-mono, monospace);
  font-size: 11px;
  word-break: break-all;
  text-align: center;
  width: 100%;
  color: var(--fg);
}
.subscribe-invoice .invoice-status { color: var(--fg-dim); font-size: 12px; text-align: center; }
.subscribe-invoice .qr-wrapper {
  width: min(220px, 100%);
  margin: 0 auto;
}
.subscribe-invoice .qr-wrapper svg { width: 100%; height: auto; display: block; }
.start-thread.profile-subscribe { background: #2a6b2a; border-color: #2a6b2a; }
.start-thread.profile-subscribe:hover:not(:disabled) { background: #1a4f1a; border-color: #1a4f1a; }

/* ---- 4chan-x style watcher panel ---- */
.watcher-panel {
  position: fixed;
  z-index: 118;
  width: min(430px, calc(100vw - 8px));
  max-height: 72vh;
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--bg-elev);
  box-shadow: 0 3px 12px rgba(0,0,0,0.25);
}

.watcher-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 2px 5px;
  background: #D6DAF0;
  border-bottom: 1px solid var(--border-soft);
  font-size: 12px;
}
.watcher-title { font-weight: 700; color: #0F4D92; }
.watcher-status { margin-left: auto; color: var(--fg-faint); font-size: 11px; }
.watcher-refresh,
.watcher-close {
  color: var(--fg-dim);
  text-decoration: none;
  line-height: 1;
}
.watcher-refresh:hover,
.watcher-close:hover { color: var(--red); }
.watcher-close { font-size: 16px; margin-left: 2px; }

.watcher-tabs {
  display: flex;
  border-bottom: 1px solid var(--border-soft);
  background: #f4f6ff;
}

.watcher-tab {
  border: 0;
  border-right: 1px solid var(--border-soft);
  background: transparent;
  color: var(--fg-dim);
  font-size: 12px;
  padding: 3px 7px;
  cursor: pointer;
}
.watcher-tab.is-active {
  background: var(--bg-elev);
  color: var(--fg);
  font-weight: 700;
}
.watcher-tab.has-unread {
  color: var(--red);
}

.watcher-toolbar {
  border-bottom: 1px solid var(--border-soft);
  padding: 3px 5px;
}
.watcher-toggle-current {
  font-size: 11px;
  border: 1px solid var(--border);
  background: var(--bg-elev);
  color: var(--fg);
  padding: 1px 6px;
  cursor: pointer;
}
.watcher-toggle-current:hover { background: var(--bg-hover); }

.watcher-content {
  max-height: calc(72vh - 62px);
  overflow: auto;
}
.watcher-inline-emoji {
  width: 14px;
  height: 14px;
  vertical-align: text-bottom;
  margin: 0 2px;
  transform-origin: center center;
  transition: transform .1s linear;
}
.watcher-pane[hidden] { display: none !important; }
.watcher-pane.is-active {
  display: block;
  width: 100%;
}

.watcher-row {
  display: flex;
  align-items: center;
  gap: 5px;
  width: 100%;
  padding: 2px 5px;
  border-bottom: 1px solid #eef2ff;
  font-size: 12px;
  line-height: 1.35;
}
.watcher-row:last-child { border-bottom: 0; }
.watcher-row.is-unread { background: #fff1f1; }

/* Unread notifications use color only. Keeping borders, padding and font
   weight identical prevents the line from shifting when it becomes read. */
.watcher-row-notif.is-unread {
  background: transparent;
}
.watcher-row-notif.is-unread .watcher-age,
.watcher-row-notif.is-unread .watcher-notif-user,
.watcher-row-notif.is-unread .watcher-notif-verb { color: var(--red); }
.watcher-row-notif:hover { background: #f4f6ff; }
.watcher-markread a { color: var(--fg-faint); }

.watcher-remove {
  color: var(--fg-dim);
  text-decoration: none;
  font-weight: 700;
  min-width: 10px;
  text-align: center;
}
.watcher-remove:hover { color: var(--red); }

.watcher-link {
  color: var(--fg);
  text-decoration: none;
  flex: 1;
  width: 100%;
  min-width: 0;
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
}
.watcher-link:hover { text-decoration: underline; }

.watcher-age {
  color: #4b5a79;
  flex: 0 0 auto;
}
.watcher-unread {
  color: var(--red);
  flex: 0 0 auto;
  min-width: 22px;
  text-align: right;
}
/* Notification line holds two links: the actor handle and the verb. The handle
   absorbs truncation (ellipsis) so the verb ("replied to (you)") stays readable. */
.watcher-sum {
  color: var(--fg);
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 5px;
}
.watcher-notif-user {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.watcher-notif-verb {
  flex: 0 0 auto;
  white-space: nowrap;
}
.watcher-notif-user,
.watcher-notif-verb {
  color: var(--fg);
  text-decoration: none;
}
a.watcher-notif-user:hover,
a.watcher-notif-verb:hover { text-decoration: underline; }
.watcher-request-actions {
  display: inline-flex;
  flex: 0 0 auto;
  gap: 3px;
}
.watcher-request-actions button {
  padding: 0 3px;
  border: 1px solid var(--border-soft);
  background: var(--bg-elev);
  color: var(--fg);
  font: 10px var(--font-sans);
  cursor: pointer;
}
.watcher-request-actions button:hover:not(:disabled) { background: var(--bg-hover); }
.watcher-request-actions button:disabled { color: var(--fg-faint); cursor: default; }

/* Emoji hover magnification (site-wide custom emoji + reaction emoji) */
.custom-emoji,
.post-reaction-emoji,
.watcher-inline-emoji,
.uni-emoji {
  position: relative;
  transform-origin: center center;
  transition: transform .1s linear;
}
.uni-emoji { display: inline-block; }
.custom-emoji:hover,
.post-reaction-emoji:hover,
.watcher-inline-emoji:hover,
.uni-emoji:hover {
  transform: scale(3);
  transition-delay: .2s;
  z-index: 1000;
}

.watcher-pager {
  border-top: 1px solid var(--border-soft);
  padding: 2px 0;
  text-align: center;
  font-size: 12px;
  line-height: 1.35;
}
.watcher-pager a {
  color: var(--link);
  text-decoration: none;
}
.watcher-pager a:hover { text-decoration: underline; }
.watcher-pager a.is-disabled {
  color: var(--fg-faint);
  pointer-events: none;
  text-decoration: none;
}

/* ---- config modal (tabbed, matches compose-modal aesthetic) ---- */
.config-modal {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  z-index: 125;             /* above compose (120) so the cog always wins */
  align-items: flex-start;
  justify-content: center;
  padding-top: 8vh;
}
.config-modal[hidden] { display: none !important; }
.config-modal.is-open { display: flex; }

.config-modal-box {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 0;
  width: 560px;
  max-width: calc(100% - 24px);
  max-height: 84vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
}
.config-modal-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px 6px 12px;
  border-bottom: 1px solid var(--border-soft);
  background: #D6DAF0;
}
.config-modal-head h2 {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 700;
  color: #0F4D92;
  margin: 0;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.config-modal-close {
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1;
  color: var(--fg-dim);
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  border-radius: 0;
  padding: 3px 6px;
  cursor: pointer;
}
.config-modal-close:hover { background: var(--bg-hover); color: var(--fg); }

.config-modal-body {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1 1 auto;
}
.config-tabs {
  display: flex;
  flex-direction: row;
  align-items: stretch;
  border-bottom: 1px solid var(--border-soft);
  background: var(--bg-elev);
  font-family: var(--font-sans);
  font-size: 12px;
}
.config-tab {
  font-family: var(--font-sans);
  font-size: 12px;
  line-height: 1.2;
  color: var(--fg-dim);
  background: transparent;
  border: 0;
  border-right: 1px solid var(--border-soft);
  border-radius: 0;
  padding: 8px 14px;
  cursor: pointer;
  text-align: center;
}
.config-tab:hover { background: var(--bg-hover); color: var(--fg); }
.config-tab.is-active {
  color: var(--fg);
  background: var(--bg);
  font-weight: 700;
  box-shadow: inset 0 -2px 0 0 #0F4D92;
}

.config-panels {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  background: var(--bg);
}
.config-panel {
  padding: 14px 16px 16px;
}
.config-panel[hidden] { display: none; }
.config-panel:not(.is-active) { display: none; }
.config-panel.is-active { display: block; }

.config-group {
  margin-bottom: 16px;
}
.config-group:last-child { margin-bottom: 0; }
.config-group-title {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 700;
  color: var(--fg);
  margin-bottom: 4px;
}
.config-group-desc {
  font-family: var(--font-sans);
  font-size: 11px;
  color: var(--fg-dim);
  margin-bottom: 10px;
  line-height: 1.4;
}
.config-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}
.config-row:last-child { margin-bottom: 0; }
.moderation-list { font: 12px var(--font-sans); }
.moderation-list-item { display: flex; align-items: center; gap: 8px; padding: 4px 0; border-bottom: 1px solid var(--border-soft); }
.moderation-list-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.moderation-list-remove { border: 0; background: transparent; color: var(--fg-dim); cursor: pointer; padding: 2px 5px; }
.moderation-list-remove:hover { color: var(--red); }
.moderation-list-remove:disabled { color: var(--fg-faint); cursor: not-allowed; opacity: 0.55; }
.profile-config-form { font-family: var(--font-sans); }
.profile-config-field { display: flex; flex-direction: column; gap: 3px; margin-bottom: 10px; font-size: 11px; color: var(--fg-dim); }
.profile-config-field > span { font-weight: 600; text-transform: uppercase; letter-spacing: .4px; }
.profile-config-field input[type="text"],
.profile-config-field textarea,
.profile-field-row input {
  box-sizing: border-box; width: 100%; padding: 5px 6px; color: var(--fg);
  background: var(--bg-elev); border: 1px solid var(--border-soft); border-radius: 0;
  font: 12px var(--font-sans); outline: none;
}
.profile-config-field textarea { resize: vertical; min-height: 80px; }
.profile-config-field input:focus,
.profile-config-field textarea:focus,
.profile-field-row input:focus { border-color: var(--accent); }
.profile-config-images { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.profile-config-field input[type="file"] { max-width: 100%; font: 11px var(--font-sans); color: var(--fg-dim); }
.profile-config-fields { display: flex; flex-direction: column; gap: 5px; margin-bottom: 8px; }
.profile-field-row { display: grid; grid-template-columns: minmax(80px, .7fr) minmax(120px, 1.3fr) 24px; gap: 5px; align-items: center; }
.profile-field-remove { border: 0; background: transparent; color: var(--fg-dim); cursor: pointer; padding: 3px; }
.profile-field-remove:hover { color: var(--red); }
.profile-field-add { padding: 4px 7px; color: var(--fg); background: var(--bg-elev); border: 1px solid var(--border-soft); font: 11px var(--font-sans); cursor: pointer; }
.profile-field-add:hover:not(:disabled) { background: var(--bg-hover); }
.profile-field-add:disabled { color: var(--fg-faint); cursor: default; }
@media (max-width: 520px) {
  .profile-config-images { grid-template-columns: 1fr; gap: 0; }
  .profile-field-row { grid-template-columns: 1fr 1fr 22px; }
}
.config-matrix {
  border-collapse: collapse;
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--fg);
}
.config-matrix th,
.config-matrix td {
  padding: 4px 10px 4px 0;
  text-align: left;
  font-weight: 400;
}
.config-matrix thead th {
  font-size: 11px;
  font-weight: 600;
  color: var(--fg-dim);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  border-bottom: 1px solid var(--border-soft);
}
.config-matrix thead th:not(:first-child),
.config-matrix td { text-align: center; width: 60px; }
.config-matrix tbody th { padding-right: 14px; white-space: nowrap; }
.config-matrix input[type="checkbox"] { margin: 0; cursor: pointer; vertical-align: middle; }

.config-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--fg);
  cursor: pointer;
  user-select: none;
}
.config-toggle input[type="checkbox"] {
  margin: 0;
  cursor: pointer;
}
.config-toggle.is-forced { color: var(--fg-faint); cursor: not-allowed; }
.config-toggle.is-forced input { cursor: not-allowed; }
.config-field {
  display: inline-flex;
  flex-direction: column;
  gap: 3px;
  font-family: var(--font-sans);
  font-size: 11px;
  color: var(--fg-dim);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.config-field-label { font-weight: 600; }
.config-field input[type="number"] {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--fg);
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  border-radius: 0;
  padding: 3px 6px;
  width: 90px;
  outline: none;
}
.config-field input[type="number"]:focus {
  border-color: var(--accent);
}
.config-field-hint {
  font-family: var(--font-sans);
  font-size: 11px;
  color: var(--fg-faint);
  font-style: italic;
}
.config-save {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 700;
  background: var(--accent);
  color: white;
  border: 1px solid var(--accent);
  border-radius: 0;
  padding: 4px 12px;
  cursor: pointer;
}
.config-save:hover { background: #0a3a72; }
.config-save-status {
  font-family: var(--font-sans);
  font-size: 11px;
  color: #117743;
  font-style: italic;
  min-height: 1em;
}
.config-save-status.is-error { color: var(--red); }

/* ---- floating, draggable, non-blocking panel modals ---------------------- *
 * makePanelModal() in app.js adds .draggable-panel to a modal's root overlay
 * to turn it from a dimmed, full-screen, click-capturing backdrop into a
 * transparent, click-through layer: the page underneath stays fully
 * interactive (no click-outside-to-close), and only the panel box itself
 * catches pointer events. The box is positioned absolutely and moved by
 * dragging its header; coordinates are set imperatively in JS. The compound
 * selectors keep specificity above each modal's base rules. */
.compose-modal.draggable-panel,
.config-modal.draggable-panel,
.subscribe-modal.draggable-panel,
.board-create.draggable-panel,
.emoji-picker.draggable-panel,
.poll-composer.draggable-panel {
  background: transparent;
  align-items: flex-start;
  justify-content: flex-start;
  padding: 0;
  pointer-events: none;
}
.compose-modal.draggable-panel > .compose-modal-box,
.config-modal.draggable-panel > .config-modal-box,
.subscribe-modal.draggable-panel > .subscribe-modal-box,
.board-create.draggable-panel > .board-create-box,
.emoji-picker.draggable-panel > .emoji-picker-box,
.poll-composer.draggable-panel > .poll-composer-box {
  pointer-events: auto;
  position: absolute;
  left: 0;
  top: 8vh;
  margin: 0;
}
/* The header bar is the drag handle. */
.compose-modal.draggable-panel .compose-modal-head,
.config-modal.draggable-panel .config-modal-head,
.subscribe-modal.draggable-panel .subscribe-modal-head,
.board-create.draggable-panel .board-create-head,
.emoji-picker.draggable-panel .emoji-picker-head,
.poll-composer.draggable-panel .poll-composer-head {
  cursor: grab;
  touch-action: none;
  user-select: none;
}
/* While a drag is in progress, show the grabbing cursor everywhere and
 * suppress text selection, so the pointer reads as "holding" the panel. */
body.is-panel-dragging { cursor: grabbing !important; user-select: none !important; }
body.is-panel-dragging .compose-modal-head,
body.is-panel-dragging .config-modal-head,
body.is-panel-dragging .subscribe-modal-head,
body.is-panel-dragging .board-create-head,
body.is-panel-dragging .emoji-picker-head,
body.is-panel-dragging .poll-composer-head { cursor: grabbing; }
