/* ── Fonts ────────────────────────────────────────────────────────────────────
   MUST be the first rule in this file. CSS ignores @import once any style rule
   has appeared, and both of this file's font imports previously sat ~2900 lines
   down — so NOTHING loaded. document.fonts.size was 0 on production and the whole
   site silently rendered in Segoe UI, the exact generic-system-default look the
   project's own design rules call out as a tell.

   Atkinson Hyperlegible is the default UI face (chosen for legibility: its
   letterforms are disambiguated, which matters in a chat room full of usernames).
   The remaining families belong to opt-in themes. Declaring them here is cheap —
   this costs ONE stylesheet request, and a font BINARY is only downloaded when a
   rule actually matches rendered text, so theme fonts stay unfetched until a user
   selects that theme. */
@import url('https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible:ital,wght@0,400;0,700;1,400&family=Almendra:wght@400;700&family=Cinzel:wght@600;700;900&family=Cormorant+Garamond:wght@500;600&family=IBM+Plex+Mono:wght@500&display=swap');

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

:root {
  /* Single height knob: governs BOTH the player max-height AND #main's height so
     they can't drift. The +/- resize JS mirrors its px value onto this var. */
  --player-cap: 62vh;
  /* Elevation stack: page → panel → card → input, ~5 lightness steps apart */
  --bg: #181E22;
  --bg-panel: #1E2730;
  --bg-card: #232D34;
  --bg-input: #232D34;
  --bg-chat: #1A2126;
  --border: #2A343B;
  --border-light: #425259;
  --text: #ffffff;
  --text-dim: #9d9d9d;
  --text-muted: #5a6570;
  --cta: #5ca2b9;
  --info: #7fb8cc;
  --accent: var(--cta);
  --green: #4caf50;
  --red: #e53935;
  --gold: #ffd700;
  --player-idle-text: var(--text-dim);
  --player-idle-font: inherit;
  --player-idle-size: 15px;
  --rank-siteadmin: #ff5c57;  /* was #cc0000 — 2.77:1 on chat bg */
  --rank-owner: #6f8cff;  /* was #0000cc — 1.45:1 on chat bg, unreadable */
  --rank-admin: #4fb0e8;  /* was #0077cc — 3.50:1 on chat bg */
  --rank-mod: #00aa00;
  --rank-leader: #ddaa00;
  --rank-guest: #888;
}

html, body { height: 100%; background: var(--bg); color: var(--text); font-family: 'Atkinson Hyperlegible', 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 14px; }
.hidden { display: none !important; }

/* ═══════════════════════════════════════
   COOL-MODAL — shared modal design system
   Variants by data domain; 3px top accent bar, opaque borders, Scale 1 type.
   ═══════════════════════════════════════ */
.cool-modal {
  background: var(--bg-card);
  border: 1px solid #2d3a43;
  border-top: 3px solid var(--cm-accent, var(--cta));
  border-radius: 2px;
  color: var(--text);
  font-size: 14px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
  animation: cm-pop 220ms cubic-bezier(0.34, 1.56, 0.64, 1.0);
}
/* Default pop used when the modal is positioned by normal flow (no translate). */
@keyframes cm-pop {
  from { opacity: 0; transform: scale(0.97); }
  to   { opacity: 1; transform: scale(1); }
}
/* When the modal is center-positioned via our bottom-modal CSS (top/left 50%
 * + translate(-50%, -50%)), the scale-only keyframe above clobbers the
 * centering translate during the 220ms animation — placing the panel's
 * top-left corner at the page center (visually: bottom-right of center).
 * Use a keyframe that keeps the translate in the transform throughout. */
#store-panel:not(.hidden).cool-modal,
#chat-opts-panel:not(.hidden).cool-modal,
#emote-picker:not(.hidden).cool-modal,
#job-panel:not(.hidden).cool-modal,
#leaderboard-panel:not(.hidden).cool-modal,
#charsheet-panel:not(.hidden).cool-modal,
#poll-modal:not(.hidden).cool-modal,
#feedback-modal:not(.hidden).cool-modal,
#history-panel:not(.hidden).cool-modal {
  animation: cm-pop-centered 220ms cubic-bezier(0.34, 1.56, 0.64, 1.0);
}
@keyframes cm-pop-centered {
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.97); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .cool-modal { animation-duration: 0.01ms; }
}

/* Variants */
.cool-modal--poll     { --cm-accent: var(--cta); }
.cool-modal--stats    { --cm-accent: #c8a84b; }  /* amber — charsheet/leaderboard accents */
.cool-modal--leader   { --cm-accent: var(--gold); }
.cool-modal--store    { --cm-accent: var(--text-muted); }
.cool-modal--hat      { --cm-accent: #c9b8ff; }
.cool-modal--options  { --cm-accent: var(--text-muted); }

/* Header row: title on left, close button on right. */
.cool-modal-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 14px 10px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.cool-modal-title {
  font-size: 16px; font-weight: 600; line-height: 1.2; color: var(--text);
}
.cool-modal-close {
  background: none; border: none; color: var(--text-muted);
  font-size: 18px; line-height: 1; cursor: pointer; padding: 2px 6px;
  border-radius: 2px;
}
.cool-modal-close:hover { color: var(--text); background: rgba(255,255,255,0.04); }

/* Body padding wrapper */
.cool-modal-body { padding: 12px 14px; }

/* Section: label + thin rule + content */
.cool-section { margin-top: 14px; }
.cool-section:first-child { margin-top: 0; }
.cool-section-label {
  font-size: 11px; font-weight: 600; color: var(--text-muted);
  padding-bottom: 4px; margin-bottom: 6px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

/* Inputs shared styling inside modals. */
.cool-modal input[type="text"],
.cool-modal input[type="password"],
.cool-modal input[type="number"],
.cool-modal input[type="url"],
.cool-modal textarea,
.cool-modal select {
  width: 100%;
  background: var(--bg-input);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 7px 9px;
  border-radius: 2px;
  font-size: 14px;
  font-family: inherit;
}
.cool-modal input:focus, .cool-modal textarea:focus, .cool-modal select:focus {
  outline: none; border-color: var(--cm-accent, var(--cta));
}
.cool-modal textarea { min-height: 72px; resize: vertical; }
.cool-modal label { font-size: 13px; color: var(--text-dim); }

/* Checkbox row: aligned baseline, accent to the modal's color. */
.cool-check {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 0; font-size: 13px; color: var(--text-dim); cursor: pointer;
}
.cool-check input[type="checkbox"] {
  width: auto; margin: 0;
  accent-color: var(--cm-accent, var(--cta));
}

/* Labeled control row (label left, select/input right) — replaces the ad-hoc
   inline flex used across the Options panel so every labeled row is consistent. */
.opt-row {
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; color: var(--text-dim); margin-top: 4px;
}
.opt-row > span { white-space: nowrap; }
.opt-row > select, .opt-row > input { flex: 1; width: auto; }
.opt-row.opt-row--stacked { flex-direction: column; align-items: stretch; gap: 3px; }

/* A control the current settings make irrelevant (e.g. sync tolerance while
   auto-sync is off). Dimmed + non-interactive, but still visible so the user
   sees it exists. */
.opt-row.is-disabled { opacity: 0.4; pointer-events: none; }

/* Options filter box — one line at the top of the panel; hides non-matching
   rows/sections as the user types. */
.opt-filter {
  width: 100%; margin-bottom: 12px;
}
.cool-section.opt-hidden, .cool-check.opt-hidden, .opt-row.opt-hidden { display: none; }

/* Button pair row */
.cool-actions {
  display: flex; gap: 8px; justify-content: flex-end;
  padding-top: 12px; margin-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

/* Buttons — inset border, no fill. Hover = faint fill in the accent hue. */
.cool-btn {
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.1);
  color: var(--text-dim);
  padding: 6px 14px;
  border-radius: 2px;
  font-size: 13px; font-weight: 500;
  cursor: pointer;
  transition: background 120ms ease-out, color 120ms ease-out, border-color 120ms ease-out;
}
.cool-btn:hover { color: var(--text); border-color: rgba(255, 255, 255, 0.22); }
.cool-btn:active { transform: translateY(1px); }

.cool-btn--primary {
  border-color: var(--cm-accent, var(--cta));
  color: var(--cm-accent, var(--cta));
}
.cool-btn--primary:hover {
  background: color-mix(in srgb, var(--cm-accent, var(--cta)) 12%, transparent);
  border-color: var(--cm-accent, var(--cta));
  color: var(--cm-accent, var(--cta));
}
/* Empty state — single line, muted, no icon. */
.cool-empty {
  padding: 16px 0; text-align: left;
  color: var(--text-muted); font-size: 13px;
}

/* Leaderboard row (used inside .cool-modal--leader) */
.cool-rank-row {
  display: grid; grid-template-columns: 28px 1fr auto;
  gap: 12px; align-items: baseline;
  padding: 5px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}
.cool-rank-row:last-child { border-bottom: none; }
.cool-rank-num {
  font-size: 12px; font-weight: 500; color: var(--text-muted);
  font-variant-numeric: tabular-nums; text-align: right;
}
.cool-rank-row.is-top .cool-rank-num { color: var(--gold); font-weight: 700; }
.cool-rank-row.is-silver .cool-rank-num { color: #c0c6cc; font-weight: 700; }
.cool-rank-row.is-bronze .cool-rank-num { color: #cd7f32; font-weight: 700; }
.cool-rank-name {
  font-size: 14px; font-weight: 600; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cool-rank-value {
  font-size: 14px; font-weight: 600; color: var(--gold);
  font-variant-numeric: tabular-nums; letter-spacing: -0.01em;
}

/* Section break rule (after top-3 leaderboard, before rest). */
.cool-rank-break {
  margin: 4px 0;
  border-top: 1px dashed rgba(255, 255, 255, 0.08);
  height: 0;
}
/* ═══════════════════════════════════════
   END cool-modal
   ═══════════════════════════════════════ */

/* Target picker: overlay that lists online users; used by Use-weapon flow. */
.target-picker-overlay {
  position: fixed; inset: 0;
  background: rgba(0, 0, 0, 0.55);
  z-index: 9800;
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
}
.target-picker-panel {
  width: 360px; max-width: 100%;
  max-height: 70vh;
  display: flex; flex-direction: column;
}
.target-picker-panel .cool-modal-body {
  display: flex; flex-direction: column; gap: 10px;
  min-height: 0; overflow: hidden;
}
.target-picker-search {
  width: 100%;
  background: var(--bg-input);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 7px 9px; border-radius: 2px;
  font-size: 14px;
}
.target-picker-search:focus { outline: none; border-color: var(--cta); }
.target-picker-list {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  display: flex; flex-direction: column; gap: 2px;
}
.target-picker-row {
  text-align: left;
  background: var(--bg-panel);
  border: 1px solid transparent;
  border-left: 2px solid var(--border);
  color: var(--text);
  padding: 7px 10px;
  font-size: 14px; font-weight: 500;
  cursor: pointer;
  transition: background 100ms, border-color 100ms;
}
.target-picker-row:hover {
  background: var(--bg-card);
  border-left-color: var(--red);
  color: #ff9999;
}
.target-picker-row:active { transform: translateY(1px); }
/* Rank coloring inside picker — matches userlist conventions. */
.target-picker-row[data-rank="255"] { color: var(--rank-siteadmin); font-weight: 700; }
.target-picker-row[data-rank="5"]   { color: var(--rank-owner); font-weight: 700; }
.target-picker-row[data-rank="4"]   { color: var(--rank-admin); font-weight: 700; }
.target-picker-row[data-rank="3"]   { color: var(--rank-mod); }
.target-picker-row[data-rank="2"]   { color: var(--rank-leader); }

@media (max-width: 640px) {
  .target-picker-panel { width: 100%; max-height: 90vh; }
}


/* ═══════════════════════════════════════
   NAVBAR — matches Bootstrap .navbar-inverse
   ═══════════════════════════════════════ */
#navbar {
  display: flex; align-items: center; gap: 10px;
  padding: 0 15px; height: 50px;
  background: var(--bg-panel); border-bottom: 1px solid #000;
  position: fixed; top: 0; left: 0; right: 0; z-index: 1000;
}
/* Day/night cycle band — a thin sky strip behind the navbar content. The orb
   (sun/moon) scrolls left→right as the cycle advances. Overlay, no clicks. */
#daynight-band {
  position: absolute; inset: 0; overflow: hidden; pointer-events: none;
  z-index: 0; opacity: 0.5; transition: background 4s linear;
  background: linear-gradient(180deg, #2a3a5a 0%, var(--bg-panel) 70%); /* default dusk */
}
#daynight-band.dn-day   { background: linear-gradient(180deg, #4a7fb5 0%, var(--bg-panel) 75%); }
#daynight-band.dn-night { background: linear-gradient(180deg, #15203a 0%, var(--bg-panel) 70%); }
#daynight-band.dn-moon  { background: linear-gradient(180deg, #241a3a 0%, var(--bg-panel) 65%); }
/* navbar content sits above the band */
#navbar > :not(#daynight-band) { position: relative; z-index: 1; }
/* color is explicit: any page whose logo is TEXT rather than the <img> would
   otherwise inherit the browser default link blue (profile.html did). */
.logo { display: flex; align-items: center; text-decoration: none; margin-right: 10px; color: var(--text); }
.logo img { display: block; }

#nav-links {
  display: flex; align-items: center; gap: 2px; flex: 1;
}
#nav-links a {
  color: #cfd1d4; text-decoration: none; padding: 6px 10px;
  font-size: 14px; border-radius: 2px;
}
#nav-links a:hover { color: #fff; background: rgba(255,255,255,0.06); }
.caret { font-size: 9px; }

.nav-dropdown { position: relative; }
.nav-dropdown-menu {
  position: absolute; top: 100%; left: 0;
  background: var(--bg); border: 1px solid var(--border-light);
  border-radius: 2px; min-width: 160px; z-index: 1001;
  padding: 5px 0;
}
.nav-dropdown-menu a {
  display: block; padding: 6px 14px; color: var(--text);
  font-size: 14px; text-decoration: none;
}
.nav-dropdown-menu a:hover { background: var(--bg-panel); color: #fff; }

#nav-auth { display: flex; align-items: center; gap: 6px; margin-left: auto; }
#login-bar { display: flex; gap: 4px; }
#login-bar input {
  background: var(--bg-input); border: 1px solid var(--border-light); color: var(--text);
  padding: 4px 8px; border-radius: 2px; font-size: 13px; width: 120px;
}
button {
  background: var(--bg-input); border: 1px solid transparent; color: var(--text);
  padding: 4px 10px; border-radius: 2px; cursor: pointer; font-size: 14px;
}
button:hover { background: #000; }
#user-info { display: flex; align-items: center; gap: 12px; }
#username { font-weight: 600; }
#points-display {
  color: var(--gold); font-weight: 600; font-size: 15px;
  font-variant-numeric: tabular-nums; letter-spacing: -0.01em;
  padding: 3px 10px;
  border: 1px solid rgba(255, 215, 0, 0.35);
  background: rgba(255, 215, 0, 0.06);
  border-radius: 2px;
  display: inline-flex; align-items: baseline; gap: 6px;
  transition: color 0.3s, border-color 0.3s, background 0.3s;
}
#points-display::before {
  content: 'cp';
  color: #c8a84b;          /* soft gold — readable on the dark chip, no more muted sink */
  font-weight: 500; font-size: 10px;
  letter-spacing: 0.06em; text-transform: uppercase;
}
#btn-logout { font-size: 11px; padding: 3px 8px; }

/* Small icon button in the user cluster (Feedback). Flat, header-matched. */
.nav-icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 3px; background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 2px; color: var(--text-dim); cursor: pointer;
}
.nav-icon-btn:hover { color: var(--text); border-color: rgba(255, 255, 255, 0.28); }
.nav-icon-btn .ch-icon { width: 15px; height: 15px; }

/* CP animations */
.cp-glow-green { color: #4caf50 !important; }
.cp-glow-red { color: #e53935 !important; }

/* ═══════════════════════════════════════
   MAIN PAGE
   ═══════════════════════════════════════ */
#mainpage {
  margin-top: 50px; /* navbar height */
  /* Full screen width — no cap. Full-height flex column so the WHOLE app fits in
     the viewport: #main flexes to fill, #bottom-bar is fixed at the bottom and is
     ALWAYS visible. The video caps to #main's height; the chat scrolls inside.
     This is what keeps the Add/Search/queue controls on screen no matter how big
     the video or how long the chat. */
  height: calc(100vh - 50px);
  display: flex;
  flex-direction: column;
  padding: 0 15px;
  overflow: hidden;
}
/* #main is CONTENT-height (player drives it), NOT stretch-to-fill — a flex:1 here
   forced both columns to full height while the player capped at 62vh, leaving a
   ~100px black void under the player. Now the player's cap defines #main's height
   (see #main rule); the bottom bar sits flush under it. */
/* #main FILLS the column and hands the surplus to chat. Previously it was a fixed
   calc(--player-cap + 30px) while #bottom-bar was also fixed, so whatever the two
   didn't use was simply dead below the bottom bar (measured ~200px at 1600x950,
   and far more once the player was idle).
   This is safe against the void this file warns about: #main already uses
   align-items:flex-start, the video column is pinned to the top and keeps EXACTLY
   its player-driven size, and only the chat column is told to stretch. The player
   never changes size here — that is the +/- control's job alone. */
#mainpage > #main { flex: 1 1 auto; min-height: 0; }
/* 0 1 auto (shrinkable), not 0 0 auto: inside #mainpage's overflow:hidden column a
   tall queue used to overrun the viewport and #plmeta became unreachable. This is
   the page column's flex, NOT the bottom-bar grid columns — flex-basis:0 on those
   is the banned change and is untouched. */
#mainpage > #bottom-bar { flex: 0 1 auto; min-height: 0; }
#bottom-queue { min-height: 0; }

/* ── MOTD row ── */
#motdrow { margin-bottom: 10px; }
.well {
  background: var(--bg-chat); border: none; border-radius: 2px;
  padding: 19px; position: relative; min-height: 20px;
  margin-bottom: 0;
}
.close-btn {
  float: right; background: none; border: none; color: var(--text-dim);
  font-size: 18px; cursor: pointer; padding: 0 4px; line-height: 1;
}
.close-btn:hover { color: var(--text); }
#motd { font-size: 13px; color: #c8a84b; white-space: pre-wrap; word-break: break-word; }

/* ── Drink bar ── */
#drinkbarwrap { margin-bottom: 10px; }
#drinkbar {
  background-color: #2a1f00;
  border: 2px solid #c8a84b;
  border-radius: 2px;
  padding: 5px 0;
}
#drinkcount {
  text-align: center; font-weight: bold; font-size: 30px;
  color: #c8a84b; text-shadow: 1px 1px #000;
  font-variant-numeric: tabular-nums; letter-spacing: -0.02em;
  margin: 0;
}

/* ═══════════════════════════════════════
   MAIN ROW — Chat (5/12) + Video (7/12)
   ═══════════════════════════════════════ */
#main {
  display: flex; gap: 0;
  min-height: calc(var(--player-cap, 62vh) + 30px);
  align-items: flex-start;                     /* columns size to content, no stretch (the void's cure) */
  height: auto; /* min-height above is the floor; surplus goes to chat, not to a void */
}
/* Layout option "synchtube" (original Coolhole parity): video on the left, chat
   on the right (flips the default chat-left order). Live via body.layout-synchtube. */
body.layout-synchtube #main { flex-direction: row-reverse; }

/* ── CHAT WRAP (col-lg-5) ── */
#chatwrap {
  /* Width is the resize knob: the +/- controls in the video header shrink this
     column and the video takes the space, matching original Coolhole where the
     player grows AT THE EXPENSE of chat. Previously +/- scaled --player-cap, which
     grew the player and the chat container together, so chat never gave anything up
     and the control felt inert. */
  flex: 0 0 var(--chat-w, 500px); min-width: 280px; max-width: var(--chat-w, 500px);
  display: flex; flex-direction: column;
  min-height: 0;                   /* fit within the bounded #main; chatbody flexes */
  align-self: stretch;             /* take the surplus height instead of leaving it dead */
  height: auto;
  margin-right: 10px;
}
#chatheader {
  display: flex; align-items: center; gap: 6px;
  padding: 0 8px;
  background: var(--bg-panel);
  border: 1px solid var(--bg-panel);
  border-bottom: none;
  height: 30px; font-size: 13px;
}
#userlisttoggle {
  cursor: pointer; color: var(--text-dim); font-size: 12px;
  transition: transform 0.15s;
}
#userlisttoggle:hover { color: var(--text); }
#userlisttoggle.collapsed { transform: rotate(-90deg); }
#conn-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; flex-shrink: 0; }
.conn-ok { background: var(--green); }
.conn-off { background: var(--red); }
#usercount { color: var(--text-muted); white-space: nowrap; flex: 1; font-variant-numeric: tabular-nums; }

#chatbody {
  display: flex; flex: 1 1 auto;   /* fill the chat column within the bounded #main */
  border: 1px solid var(--bg-panel);
  min-height: 0;                   /* allow it to shrink so the input/controls fit */
  overflow: hidden;
}

/* ── Userlist ── */
#userlist {
  list-style: none;
  width: 120px; min-width: 120px;
  overflow-x: hidden; overflow-y: auto;
  border-right: 1px solid var(--bg-panel);
  font-size: 9pt; flex-shrink: 0;
  transition: width 0.15s, min-width 0.15s;
}
#userlist.collapsed { width: 0; min-width: 0; border-right: none; overflow: hidden; }
#userlist li {
  display: flex; align-items: center; gap: 4px;
  padding: 2px 6px; line-height: 1.6;
  cursor: pointer; white-space: nowrap; overflow: hidden;
}
#userlist li:hover { background: #282828; }
#userlist li.ignored { text-decoration: line-through; opacity: 0.5; }
.u-name { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 0 1 auto; min-width: 0; }
/* Right-aligned single-badge area — pushes to the right, truncates, never wraps. */
.u-badges { margin-left: auto; display: inline-flex; align-items: center; gap: 3px; flex: 0 0 auto; max-width: 42px; overflow: hidden; }
.u-badges > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Rank colors — matching original CyTube */
#userlist li[data-rank="255"] .u-name { color: var(--rank-siteadmin); font-weight: 700; }
#userlist li[data-rank="5"] .u-name { color: var(--rank-owner); font-weight: 700; }
#userlist li[data-rank="4"] .u-name { color: var(--rank-admin); font-weight: 700; }
#userlist li[data-rank="3"] .u-name { color: var(--rank-mod); }
#userlist li[data-rank="2"] .u-name { color: var(--rank-leader); }
#userlist li[data-rank="1"] .u-name { color: var(--text); }
#userlist li[data-rank="0"] .u-name { color: var(--rank-guest); }

/* ── Message buffer ── */
#messagebuffer {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto; overflow-x: hidden;
  /* was 1px — text sat flush against the panel border. Mobile already used 4px 6px. */
  padding: 4px 8px 6px; position: relative;
  background: var(--bg-chat);
  /* Bottom-anchor: a near-empty room used to render its 2 lines at the TOP of a
     ~700px void with the input far below. margin-top:auto on the first child (not
     justify-content:flex-end) keeps scrollTop/scrollHeight sane for the existing
     autoscroll + new-messages logic. */
  display: flex; flex-direction: column;
}
#messagebuffer > :first-child { margin-top: auto; }
/* Hanging indent: the name hangs left and wrapped text aligns under the message
   body, so a wrapped line can't be misread as a new message. */
.chat-line { line-height: 1.45; word-wrap: break-word; overflow-wrap: break-word; animation: chat-fade-in 100ms ease-out; padding-left: 12px; text-indent: -12px; }
@keyframes chat-fade-in { from { opacity: 0; } to { opacity: 1; } }
.chat-ts { color: #7b8892; font-size: 11px; margin-right: 2px; font-variant-numeric: tabular-nums; }
.chat-user-wrap { position: relative; display: inline-block; }
.chat-user-wrap .chat-user::after { content: ': '; font-weight: 400; color: var(--text-dim); }
.user-hat { position: absolute; display: block; top: -18px; left: 50%; transform: translateX(-50%); pointer-events: none; }
.chat-user { font-weight: 600; cursor: pointer; }
.chat-user::after { content: ': '; font-weight: 400; color: var(--text-dim); }
.chat-user[data-rank="255"] { color: var(--rank-siteadmin); }
.chat-user[data-rank="5"] { color: var(--rank-owner); }
.chat-user[data-rank="4"] { color: var(--rank-admin); }
.chat-user[data-rank="3"] { color: var(--rank-mod); }
.chat-user[data-rank="2"] { color: var(--rank-leader); }
.server-whisper .chat-user { color: var(--info); font-weight: 400; font-style: italic; }
.server-whisper .chat-text { color: var(--text-dim); font-style: italic; font-size: 8pt; }
.nick-hover { background: rgba(255, 255, 153, 0.08); }
.nick-highlight { background: rgba(221, 255, 221, 0.15); }
.chat-highlight { background: rgba(74,158,255,0.12); border-left: 2px solid var(--info); padding-left: 4px; }
.action-user { font-weight: 600; }
.action-user::after { content: ''; }
.chat-text strong { font-weight: 700; }
.chat-text em { font-style: italic; }
.chat-text s { text-decoration: line-through; }
.chat-text code { background: #2a2a2a; border: 1px solid #444; border-radius: 2px; padding: 0 3px; font-family: monospace; font-size: 12px; }
.chat-text .spoiler { color: var(--bg); background: var(--bg); border-radius: 2px; cursor: pointer; padding: 0 2px; }
.chat-text .spoiler:hover { color: var(--text); background: #333; }
.greentext { color: #6b7c34; }

/* Chat input */
#chatline {
  width: 100%; background: var(--bg-input); border: 1px solid var(--bg-panel);
  border-top: none; color: var(--text); padding: 6px 8px;
  font-size: 14px;
  transition: box-shadow 150ms;
}
#chatline:focus { box-shadow: inset 0 0 0 1px var(--cta); }
#chatline:disabled { opacity: 0.4; }
#chatline:focus { outline: none; border-color: #444; }

/* Autofill (@-mention / command) dropdown. Floats ABOVE the chat input instead
   of pushing it down — position:relative made it take layout space and shove the
   chatline around when it opened. #chatline-wrap is the positioning context;
   the dropdown sits at bottom:100% (directly above the input) as a typeahead. */
#chatline-wrap { position: relative; }
#autofill-dropdown {
  position: absolute; left: 0; right: 0; bottom: 100%;
  margin-bottom: 2px;
  background: var(--bg-panel); border: 1px solid var(--border);
  max-height: 180px; overflow-y: auto; z-index: 100;
  box-shadow: 0 -4px 12px rgba(0,0,0,0.4);
}
#autofill-dropdown .autofill-item { padding: 4px 8px; font-size: 12px; cursor: pointer; display: flex; gap: 8px; align-items: baseline; }
#autofill-dropdown .autofill-item:hover, #autofill-dropdown .autofill-active { background: var(--bg-hover, #2a2a2a); }
#autofill-dropdown .autofill-cmd { color: var(--accent); white-space: nowrap; }
#autofill-dropdown .autofill-desc { color: var(--text-dim); font-size: 11px; }

/* Black market overlay (item 27). Deliberately grimy — dark, one warning-red edge. */
.blackmarket-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.6); z-index: 200; display: flex; align-items: center; justify-content: center; }
.blackmarket-box { background: #141013; border: 1px solid #3a3036; border-top: 3px solid #a01f2b; width: min(420px, 92vw); max-height: 70vh; overflow-y: auto; box-shadow: 0 8px 30px rgba(0,0,0,0.6); }
.blackmarket-head { font-weight: 700; font-size: 14px; padding: 10px 12px; border-bottom: 1px solid #3a3036; position: relative; color: #e8e2d9; }
.blackmarket-close { position: absolute; right: 8px; top: 7px; background: none; border: none; color: #8a8079; font-size: 18px; cursor: pointer; }
.blackmarket-note { font-size: 11px; color: #8a8079; padding: 8px 12px; }
.blackmarket-list { padding: 0 12px 12px; }
.blackmarket-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 7px 0; border-bottom: 1px solid #241f24; font-size: 12px; color: #e8e2d9; }
.blackmarket-row button { padding: 3px 12px; font-size: 12px; cursor: pointer; background: #2a1418; color: #e07b6e; border: 1px solid #a01f2b; border-radius: 2px; }
.blackmarket-row button:hover { background: #3a1f1c; }

/* Vault section in the store (item 40). */
#store-vault { margin-bottom: 10px; }
#store-vault .vault-blurb { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
#store-vault input { background: var(--bg-input, #1c1c1c); color: var(--text); border: 1px solid var(--border); border-radius: 2px; padding: 3px 6px; }

/* Action-Prompt card — a timed decision the server demands (shakedown/appeal).
   Pinned above the chat input. Buttons fire /verbs; the bar is a cosmetic timer. */
.action-prompt {
  position: absolute; left: 0; right: 0; bottom: 100%;
  margin-bottom: 4px;
  background: var(--bg-panel); border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  padding: 9px 11px 0; z-index: 120;
  box-shadow: 0 -4px 14px rgba(0,0,0,0.5);
}
.action-prompt--danger { border-left-color: #c0392b; }
.action-prompt-title { font-weight: 600; font-size: 13px; margin-bottom: 3px; }
.action-prompt-body { font-size: 12px; color: var(--text-dim); margin-bottom: 8px; }
.action-prompt-btns { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 8px; }
.action-prompt-btn {
  padding: 5px 12px; font-size: 12px; cursor: pointer;
  background: var(--bg-hover, #2a2a2a); color: var(--text);
  border: 1px solid var(--border); border-radius: 2px;
}
.action-prompt-btn:hover { border-color: var(--accent); }
.action-prompt-btn--danger { border-color: #c0392b; color: #e07b6e; }
.action-prompt-btn--danger:hover { background: #3a1f1c; }
.action-prompt-bar { height: 3px; background: rgba(255,255,255,0.08); margin: 0 -11px; }
.action-prompt-bar-fill { height: 100%; width: 100%; background: var(--accent); transition: width 0.2s linear; }
.action-prompt--danger .action-prompt-bar-fill { background: #c0392b; }

/* Guest login row */
#guestlogin {
  display: flex; border: 1px solid var(--bg-panel); border-top: none;
}
.guest-label {
  background: var(--bg-panel); color: var(--text-dim);
  padding: 4px 12px; font-size: 13px; white-space: nowrap;
  border-right: 1px solid var(--border-light);
  display: flex; align-items: center;
  min-width: 100px;
}
#guestname {
  flex: 1; background: var(--bg-input); border: none;
  color: var(--text); padding: 4px 8px; font-size: 14px;
  border-radius: 0 0 4px 0;
}
#guestname:focus { outline: none; }

/* New messages indicator */
#newmessages-indicator {
  position: relative; margin-top: -28px; height: 28px;
  text-align: center; font-weight: bold; font-size: 11px;
  cursor: pointer; line-height: 28px;
  background: rgba(74,158,255,0.15); color: var(--accent);
  border: 1px solid var(--accent);
  z-index: 5;
}

/* ═══════════════════════════════════════
   VIDEO WRAP (col-lg-7)
   ═══════════════════════════════════════ */
#videowrap { flex: 1 1 auto; min-width: 0; max-width: 100%; overflow: hidden; height: auto; align-self: flex-start; display: flex; flex-direction: column; }
/* Collapsing the player frees height that nothing in the column could absorb, so
   the page just ended and left a void BELOW the bottom bar (measured: 530px). In
   this state ONLY, let #main take the remainder and give it to chat. This is not
   the banned default-state `flex:1 on #main` — with the player collapsed there is
   no player box to open a void, which is what that ban exists to prevent, and the
   mobile rules already do exactly this. The video column hugs its collapsed player
   via flex-start instead of stretching. */
/* No media: hide the video column entirely and give the room to chat, which is
   what mobile has always done (#player-wrap:has(#player.no-media){display:none}).
   Collapsing the player but keeping the column produced a second void — a 140px
   black bar with ~700px of empty column beneath it. An idle room should just be
   the chat room. The column returns the moment something is queued. */
/* The player keeps its size whether or not something is playing — a box that
   shrinks when idle and jumps back on play is disorienting, and the +/- control
   below is the only thing that should change it. Mobile still collapses it
   (@media max-width:640px) where vertical space is genuinely scarce. */
/* "Hide video (radio mode)" user option — collapse the player so chat/queue
   take the full width. Live-toggled via body.hide-video (no refresh). */
body.hide-video #videowrap { display: none; }

#videowrap-header {
  padding: 0 10px; font-size: 13px;
  background: var(--bg-panel);
  border: 1px solid var(--bg-panel);
  border-bottom: none;
  display: flex; flex-direction: row-reverse;
  align-items: center; gap: 6px;
  height: 30px;
}
#resize-video-smaller, #resize-video-larger {
  float: right; cursor: pointer; color: var(--text-dim);
  padding: 0 4px; font-size: 14px; user-select: none;
}
#resize-video-smaller:hover, #resize-video-larger:hover { color: var(--text); }
#currenttitle { flex: 1; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#upnext { color: var(--text-dim); font-size: 11px; opacity: 0.8; max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#upnext::before { content: "next: "; color: var(--accent); }
#leader-badge { color: #f0c040; font-size: 11px; flex-shrink: 0; }

#player-wrap {
  background: #000; position: relative;
  border: 1px solid var(--bg-panel); border-top: none;
  min-width: 0; max-width: 100%; overflow: hidden; /* a wide native <video> can't push an h-scrollbar */
}
/* Player is a 16:9 box that fills the column width BUT is height-capped so a
   full-width video never grows so tall it pushes the bottom controls off-screen.
   Two constraints: width:100% (fill the column) and max-height (cap to viewport).
   The box takes the SMALLER — when the height cap bites, width follows the 16:9
   ratio (box narrows) and centers in the black #player-wrap. --player-max-vh is
   overridable by the +/- resize buttons. */
#player-wrap {
  display: flex; justify-content: center; align-items: center;
  flex: 1 1 auto;            /* fill the video column below the 30px header — the
                               column is only header+player-cap tall, so the player
                               box fills it with NO black slack below (the void cure) */
}
#player {
  aspect-ratio: 16 / 9;
  max-height: var(--player-cap, 62vh);
  max-width: 100%;
  /* fill width up to the column, but the height cap wins when the column is wide:
     the box keeps 16:9 and narrows, centered in the black wrap. */
  width: 100%;
  height: auto;
}
#player iframe, #player video {
  width: 100%; max-width: 100%; height: 100%; border: 0; display: block;
  object-fit: contain; /* letterbox, never overflow the box */
}
#mobile-video-options-toggle { display: none; }
.player-idle {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%;
  color: var(--player-idle-text); font-family: var(--player-idle-font);
  font-size: var(--player-idle-size); letter-spacing: 0.02em;
}

/* ═══════════════════════════════════════
   CONTROLS ROW
   ═══════════════════════════════════════ */
/* Bottom bar mirrors #main's two columns: GAME/CHAT controls (#controlsrow) under
   the chat (left), VIDEO/QUEUE console (#bottom-queue) under the video (right).
   Same flex ratios as #main so each group sits under its column. */
/* GRID, not flex — explicit column tracks can't collapse to 0 the way
   flex-basis:0 children did when logged in (the game group has more items, the
   columns collapsed and every control wrapped to its own line). The chat track
   is clamped to the chat width (max 500); the video track takes the rest. */
#bottom-bar {
  display: grid;
  grid-template-columns: minmax(280px, 500px) 1fr;
  column-gap: 10px;
  margin-top: 6px;
  padding-top: 6px;
  padding-bottom: 4px;
  border-top: 1px solid var(--border); /* one rail ties the two zones into a footer */
  min-width: 0;
  overflow: hidden;          /* hard guard: never overflow the page width */
}
#controlsrow {
  grid-column: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  overflow: hidden;
}
#bottom-queue {
  grid-column: 2;
  min-width: 0;
}
/* #event-feed is a hidden host; keep it out of the grid flow. */
#event-feed { display: none; }
/* Synchtube flips video to the left → flip the bottom-bar columns to match. */
body.layout-synchtube #bottom-bar { grid-template-columns: 1fr minmax(280px, 500px); }
body.layout-synchtube #controlsrow { grid-column: 2; }
body.layout-synchtube #bottom-queue { grid-column: 1; }

/* Game/chat control group (Poll, Emote, Coolhole ▾, CP, job/HP, Work). Single
   row, hugs content; wraps gracefully in the narrow column, never scrolls. */
#leftcontrols {
  width: 100%;
  min-width: 0;
  display: flex;
  align-content: center;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 5px;
}
/* First-use loop: one quiet contextual action, not a tutorial modal. It sits in
   the existing game rail and retires once the user reaches the Store. */
#first-use-guide {
  order: -10; flex: 1 0 100%; min-width: 0;
  display: flex; align-items: center; gap: 6px;
  padding: 4px 5px 6px; box-sizing: border-box;
  border-bottom: 1px solid var(--border);
  color: var(--text-dim); font-size: 12px;
}
#first-use-guide.hidden { display: none; }
#first-use-copy { flex: 1 1 auto; min-width: 0; }
#leftcontrols #first-use-guide button {
  flex: 0 0 auto; height: 28px; padding: 3px 9px;
  border: 1px solid var(--accent); border-radius: 3px;
  background: transparent; color: var(--text); cursor: pointer;
}
#leftcontrols #first-use-guide button:hover { background: rgba(255,255,255,0.06); }
#leftcontrols #first-use-dismiss {
  width: 28px; padding: 0 !important;
  border-color: transparent !important; color: var(--text-dim) !important;
  font-size: 17px;
}
/* Job/Work bar stays on its own tidy line; never splits internally. */
#job-bar { flex-wrap: nowrap; }
/* Icon over label, matching #plcontrol (Add/Search/History) directly alongside.
   These seven buttons shipped as bare icons — a poll icon, a dizzy face, bars, a
   bag, people, a briefcase — with the meaning carried only by a title attribute
   that never appears on touch. */
#leftcontrols button {
  height: 38px; padding: 2px 8px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
}
#leftcontrols button .ch-icon { width: 17px; height: 17px; }
.ch-icon { width: 20px; height: 20px; flex-shrink: 0; }
.ch-icon svg { stroke-width: 1.5px; stroke-linecap: round; stroke-linejoin: round; }
#cp-bar-amount { color: var(--gold); font-weight: 600; font-size: 13px; white-space: nowrap; }
#btn-cp { display: flex; align-items: center; gap: 5px; }
#drink-display { color: #c8a84b; font-size: 13px; padding: 4px 8px; white-space: nowrap; }

#rightcontrols {
  width: 100%;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 6px;
}
#plcontrol { display: flex; gap: 3px; min-width: 0; }
#plcontrol button {
  height: 38px; padding: 3px 9px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px; min-width: 46px;
}
#plcontrol button .ch-icon { width: 17px; height: 17px; }
#leftcontrols button .btn-label,
#plcontrol button .btn-label {
  font-size: 10px; color: #888; font-weight: 500;
  letter-spacing: 0.2px;
  pointer-events: none;
}
#leftcontrols button:hover .btn-label,
#plcontrol button:hover .btn-label { color: #ccc; }
#plcontrol button.active { background: #333; border-color: #555; }
#plcontrol button.active .btn-label { color: #fff; }
#videocontrols { display: flex; gap: 2px; align-items: center; }
#videocontrols button { height: 38px; padding: 4px 9px; }

/* ── Control-group separation + harmony ──────────────────────────────────────
   Two visually-distinct clusters on the bottom bar: GAME+CHAT buttons (left) and
   VIDEO/QUEUE options (right). They sit close but each gets a faint inset frame
   so they read as separate groups, not one undifferentiated smear of buttons.
   All clusters share button height (38px) + 2px radius so everything baselines. */
#leftcontrols,
#plcontrol,
#videocontrols {
  background: rgba(255,255,255,0.018);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 4px;
  box-sizing: border-box;
  align-self: center;        /* hug content height; don't stretch to grid row */
}
/* The right side hosts TWO sub-groups (playlist + video). Tie them together with
   a slightly tighter gap so they read as one "video options" zone, but keep the
   left game/chat group clearly apart via the #controlsrow→#bottom-queue grid gap. */
#rightcontrols { gap: 6px; }
/* Buttons inside a framed group lose their own heavy ground so the FRAME carries
   the grouping — flatter, tighter, more harmonious. Hover still reads. */
#leftcontrols button,
#plcontrol button,
#videocontrols button {
  background: transparent;
  border: 1px solid transparent;
  border-radius: 3px;
}
#leftcontrols button:hover,
#plcontrol button:hover,
#videocontrols button:hover {
  background: rgba(255,255,255,0.06);
}
#plcontrol button.active { background: rgba(255,255,255,0.10); border-color: var(--border-light); }
/* CP pill + job bar are their own islands inside the game group — give them a
   matching inset chip so they harmonize with the buttons instead of floating. */
#btn-cp { border: 1px solid var(--border); border-radius: 3px; }

/* ── Two Menus, One Bar: overflow flyouts (zero-scrollbar redesign) ───────────
   Tier-3 controls (situational game buttons + mod playlist ops) live in two
   absolute flyouts so the inline bar holds a BOUNDED child count and physically
   cannot overflow. Each group owns its own "▾" trigger (Coolhole ▾ / Playlist ▾)
   as its last inline child, reinforcing the two-group separation.
   Grounded in the Artist-tower 3-tier data-dense canon. */

/* The two framed groups anchor their flyouts. */
#leftcontrols, #plcontrol { position: relative; }
/* The "Coolhole ▾" and "Playlist ▾" overflow flyouts were removed 2026-07-12;
   their controls are now inline icon buttons in the two control rails, gated by
   the same login/rank JS. No trigger/flyout styling needed. */

/* Status sub-zone: a 1px hairline divides the action buttons from the readouts
   (CP / job / HP) inside the left frame — the whole "buttons vs status" signal. */
.status-divider {
  width: 1px; height: 22px; margin: 0 4px; flex: 0 0 auto;
  background: var(--border); align-self: center;
}
/* If neither CP nor job/drink status is present (guest), hide the divider so the
   frame doesn't show an orphan hairline. */
#leftcontrols:has(#btn-cp[style*="display:none"]):has(#job-bar[style*="display:none"]):has(#drink-display[style*="display:none"]) .status-divider {
  display: none;
}
/* CP reads as a status READOUT, not a button — no hover affordance; tabular nums
   so the value width doesn't jitter the bar. Gold stays on the value only. */
#btn-cp { cursor: default; }
#btn-cp:hover { background: transparent; }
#cp-bar-amount, #hp-text, #drink-count, #voteskip-btn-count { font-variant-numeric: tabular-nums; }
#voteskip-bar {
  width: 60px;
  height: 6px;
  border-radius: 999px;
  overflow: hidden;
  background: #252525;
  border: 1px solid #3a3a3a;
}
#voteskip-fill {
  display: block;
  width: 0;
  height: 100%;
  background: var(--gold);
}
#voteskip-count { font-size: 11px; color: var(--text-dim); padding: 0 4px; }
#voteskip-btn-count { font-size: 10px; color: var(--accent); font-weight: 600; }
#bottom-queue {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
#bottom-queue #queue {
  /* scrolls inside its own box now that #bottom-bar can shrink; the old 46vh cap
     let a long queue overrun #mainpage's overflow:hidden and become unreachable */
  flex: 1 1 auto;
  /* The whole point of the queue is seeing the upcoming lineup, so let it grow
     tall and scroll only when there are many videos — not cap at ~2 rows.
     Content-sized: an empty queue collapses to nothing so #plmeta hugs the URL
     box instead of floating in a reserved void. A full queue grows to the cap
     then scrolls, and #plmeta follows the last row. */
  max-height: 46vh;
  overflow-y: auto;
}
#bottom-queue #addfromurl,
#bottom-queue #searchcontrol {
  margin-bottom: 4px;
}
#bottom-queue #plmeta {
  background: var(--bg-panel);
}

/* ═══════════════════════════════════════
   PLAYLIST ROW
   ═══════════════════════════════════════ */
#playlistrow {
  display: block;
}
#leftpane {
  width: 100%;
  min-width: 0;
}
#rightpane { width: 100%; min-width: 0; }

/* Add from URL / Search controls */
#addfromurl, #searchcontrol { margin-bottom: 6px; }
.url-input-group {
  display: flex; gap: 0;
}
.url-input-group input {
  flex: 1; background: var(--bg-input); border: 1px solid var(--border-light);
  color: var(--text); padding: 7px 10px; font-size: 13px;
  border-radius: 3px 0 0 3px;
}
.url-input-group input:focus { outline: none; border-color: var(--accent); }
.url-input-group button {
  border-radius: 0 3px 3px 0; border-left: none;
  padding: 7px 10px; white-space: nowrap;
  display: flex; align-items: center; justify-content: center;
}
#library-results {
  max-height: 200px; overflow-y: auto; font-size: 12px;
  margin-top: 4px;
}

/* ── Queue list ── */
/* Encode pills: compact chips for uploads loaded but not yet queued. Wrap in a
   row, truncate to a small width, reveal full title on hover (native tooltip).
   "A movie is loaded but hasn't been placed in the queue yet." */
#encode-pills { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 6px; }
#encode-pills:empty { display: none; }
.encode-pill {
  font-size: 11px; color: var(--info);
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 3px; padding: 2px 7px;
  max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  cursor: default; transition: background 0.12s, border-color 0.12s;
}
.encode-pill:hover { background: var(--bg-panel); border-color: var(--info); }
.videolist { list-style: none; padding: 0; margin: 0; max-height: 620px; overflow-y: auto; border-radius: 3px; }
.videolist li {
  font-size: 13px; line-height: 1.4;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  background: #272b30;
  display: grid;
  grid-template-columns: 20px 1fr;
  position: relative;
  cursor: default;
  transition: background 0.1s;
  min-width: 0;
}
.videolist li:last-child { border-bottom: none; }
.videolist li:hover { background: #2e3338; }
.videolist li.active {
  background: rgba(92,162,185,0.12);
  box-shadow: inset 3px 0 0 0 var(--accent);
}
.videolist li.q-dragging { opacity: 0.4; }
.videolist li.q-drop-above { box-shadow: inset 0 2px 0 0 var(--info); }
.videolist li.q-drop-below { box-shadow: inset 0 -2px 0 0 var(--info); }

/* Drag handle column */
.q-grab {
  grid-column: 1; grid-row: 1 / span 3;
  display: flex; align-items: center; justify-content: center;
  color: #555; cursor: grab; user-select: none;
  transition: color 0.1s;
}
.q-grab:hover { color: #999; }
.q-grab:active { cursor: grabbing; }
.q-grab svg { width: 16px; height: 16px; }
.videolist li:hover .q-grab { color: #888; }

/* Main row: thumb + title block + duration */
.q-row-main {
  grid-column: 2; grid-row: 1;
  display: grid;
  grid-template-columns: auto 72px 1fr auto;
  align-items: center; gap: 10px;
  padding: 8px 12px 8px 4px;
}

/* Type badge with per-type color */
.q-type {
  font-size: 10px; font-weight: 700; color: #bbb;
  background: rgba(255,255,255,0.06); border-radius: 3px;
  padding: 3px 7px; flex-shrink: 0; letter-spacing: 0.5px;
  text-align: center; min-width: 38px;
}
.q-type-yt   { background: rgba(230, 33, 23, 0.22);  color: #ff8a80; }
.q-type-fi   { background: rgba(92, 162, 185, 0.22); color: #9cd; }
.q-type-hl   { background: rgba(92, 162, 185, 0.22); color: #9cd; }
.q-type-cm   { background: rgba(140, 110, 200, 0.22); color: #c9b8ff; }
.q-type-em   { background: rgba(140, 110, 200, 0.22); color: #c9b8ff; }
.q-type-tw   { background: rgba(145, 70, 255, 0.22); color: #c8a8ff; }
.q-type-vi   { background: rgba(30, 180, 210, 0.22); color: #9fe0ef; }
.q-type-sc   { background: rgba(255, 140, 60, 0.22); color: #ffb488; }
.q-type-dm   { background: rgba(0, 160, 255, 0.22); color: #8cd5ff; }
.q-type-placeholder { background: rgba(200, 168, 75, 0.22); color: #c8a84b; }
.videolist li[data-type="placeholder"] .q-title { color: var(--text-muted); font-style: italic; }
.videolist li[data-type="placeholder"] .q-title::after { content: ' ⋯'; }
.videolist li.active .q-type {
  background: var(--accent); color: #fff;
  letter-spacing: 0.8px;
}

/* Thumbnail */
.q-thumb-box {
  width: 72px; height: 40px; flex-shrink: 0;
  background: #0c0f12; border-radius: 2px; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid rgba(255,255,255,0.05);
}
.q-thumb {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.q-thumb-placeholder {
  color: #3a4048; display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%;
}
.q-thumb-placeholder svg { width: 18px; height: 18px; }

/* Title column: title + sub-line (user, eta, perm pill) */
.q-title-col {
  display: flex; flex-direction: column; gap: 2px;
  min-width: 0; overflow: hidden;
}
.q-title {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: #d8d8d8; font-size: 13px; font-weight: 500;
}
.videolist li.active .q-title { color: #fff; font-weight: 600; }
.q-sub {
  display: flex; align-items: center; gap: 10px;
  font-size: 11px; color: var(--text-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.q-user { color: #9aa4ac; }
.q-eta { color: #7a8088; font-family: monospace; font-size: 10.5px; }
/* Scheduled anchor rows — pinned to an absolute start time */
.videolist li.q-anchor { box-shadow: inset 3px 0 0 0 #d9b96c; }
.q-sched { color: #e0c070; }
.q-perm-pill {
  display: inline-block; padding: 1px 5px;
  font-size: 9px; font-weight: 700; letter-spacing: 0.6px;
  background: rgba(217, 185, 108, 0.18); color: #e0c070;
  border-radius: 2px;
}

/* Duration */
.q-duration {
  color: var(--text-dim); font-family: monospace; font-size: 12px;
  flex-shrink: 0; text-align: right;
}
.videolist li.active .q-duration { color: #ddd; }

/* Playhead bar (active row only). Sits just under the now-playing row. Uses a gold
   fill on a defined dark track so it reads clearly against the blue active-row tint —
   an info-blue fill on that tint was effectively invisible (the "missing bar"). */
.q-playhead {
  grid-column: 2; grid-row: 2;
  height: 4px; background: rgba(0,0,0,0.35);
  margin: 1px 12px 3px 4px;
  border-radius: 2px; overflow: hidden;
}
.q-playhead-fill {
  height: 100%; width: 0;
  background: var(--gold);
  transition: width 0.8s linear;
}

/* Action row — hidden at rest, revealed on hover; always visible for active */
.q-actions {
  grid-column: 2; grid-row: 3;
  display: flex; justify-content: space-around; align-items: center;
  padding: 6px 10px;
  background: rgba(0,0,0,0.18);
  border-top: 1px solid rgba(255,255,255,0.04);
  gap: 4px;
  max-height: 0; overflow: hidden;
  opacity: 0;
  transition: max-height 0.15s ease, opacity 0.12s ease, padding 0.15s ease;
  padding-top: 0; padding-bottom: 0;
}
.videolist li:hover .q-actions,
.q-actions.q-actions-visible {
  max-height: 48px;
  opacity: 1;
  padding-top: 6px; padding-bottom: 6px;
}
.q-actions button {
  flex: 1; max-width: 180px;
  background: transparent; border: none; color: #9aa;
  font-size: 12px; padding: 6px 10px; border-radius: 3px;
  cursor: pointer; white-space: nowrap;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  transition: background 0.1s, color 0.1s;
}
.q-actions button:hover { background: rgba(255,255,255,0.06); color: #fff; }
.q-actions button svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; }
.q-actions .q-del { color: #c66; }
.q-actions .q-del:hover { background: rgba(180,70,70,0.15); color: #f88; }
.q-actions .q-rename { color: #7ab0c4; }
.q-actions .q-rename:hover { background: rgba(92,162,185,0.15); color: #9cd; }
.q-actions .q-perm-active { color: #d9b96c; }
.q-actions .q-perm-active:hover { background: rgba(217,185,108,0.12); color: #f0d080; }
#queue-title {
  width: 100%; margin-top: 4px; padding: 5px 10px; font-size: 12px;
  background: var(--bg-input); border: 1px solid var(--border-light);
  color: var(--text); border-radius: 3px; box-sizing: border-box;
}
#queue-title:focus { outline: none; border-color: var(--accent); }
#queue-schedule {
  width: 100%; margin-top: 4px; padding: 5px 10px; font-size: 12px;
  background: var(--bg-input); border: 1px solid var(--border-light);
  color: var(--text); border-radius: 3px; box-sizing: border-box;
  color-scheme: dark;
}
#queue-schedule:focus { outline: none; border-color: var(--accent); }

/* Playlist metadata footer */
#plmeta {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 10px; font-size: 11px; color: var(--text-dim);
  border-top: 1px solid rgba(255,255,255,0.04);
  border-radius: 0 0 3px 3px;
  background: #1a1e22;
}
#pllength { margin-left: auto; }
/* Video-menu toggle: text makes the action obvious on touch screens while the
   chevron provides a compact directional cue. */
#queue-collapse {
  display: inline-flex; align-items: center; justify-content: center;
  width: auto; height: 24px; padding: 0 6px; gap: 4px;
  background: none; border: none; color: var(--text-dim);
  cursor: pointer; border-radius: 3px;
  transition: background 0.08s, color 0.08s;
}
#queue-collapse:hover { background: rgba(255,255,255,0.06); color: var(--text); }
#queue-collapse .ch-icon {
  width: 14px; height: 14px;
  transition: transform 0.12s ease;
}
#queue-collapse-label { white-space: nowrap; font-size: 11px; }
/* Collapse the complete video console, not only its playlist rows. The summary
   remains visible so the menu is always one obvious tap away. */
#bottom-queue.queue-collapsed #queue-collapse .ch-icon { transform: rotate(-90deg); }
#bottom-queue.queue-collapsed #rightcontrols,
#bottom-queue.queue-collapsed #addfromurl,
#bottom-queue.queue-collapsed #searchcontrol,
#bottom-queue.queue-collapsed #encode-pills,
#bottom-queue.queue-collapsed #queue { display: none !important; }
#bottom-queue.queue-collapsed #plmeta { border-top: none; }

/* ═══════════════════════════════════════
   POLL
   ═══════════════════════════════════════ */
#poll-area { padding: 10px; }
#poll-area .poll-header { display: flex; align-items: center; gap: 6px; margin-bottom: 5px; }
#poll-area .poll-title { font-weight: 600; font-size: 13px; flex: 1; }
#poll-area .poll-status { font-size: 10px; color: var(--text-dim); border: 1px solid var(--border); border-radius: 2px; padding: 1px 4px; }
#poll-area .poll-status.closed { color: var(--red); border-color: var(--red); }
#poll-area .poll-status.gamble { color: var(--gold); border-color: var(--gold); }
#poll-area .poll-option { display: flex; align-items: center; gap: 6px; padding: 3px 0; font-size: 12px; }
#poll-area .poll-option-clickable { cursor: pointer; }
#poll-area .poll-option-clickable:hover { color: var(--accent); }
#poll-area .poll-option-winner .poll-opt-text { color: var(--accent); font-weight: 600; }
#poll-area .poll-option-winner .poll-bar { border: 1px solid var(--accent); }
#poll-area.poll-result-moment { animation: poll-result-moment 1.4s ease-out; }
@keyframes poll-result-moment {
  0% { box-shadow: inset 2px 0 0 var(--accent); background: rgba(92,162,185,0.10); }
  100% { box-shadow: inset 2px 0 0 transparent; background: transparent; }
}
#poll-area .poll-opt-text { min-width: 60px; }
#poll-area .poll-bar { flex: 1; height: 14px; background: #333; border-radius: 2px; position: relative; overflow: hidden; }
#poll-area .poll-bar-fill { height: 100%; background: var(--info); border-radius: 2px; transition: width 0.3s; }
#poll-area .poll-votes { font-size: 11px; color: var(--text-dim); min-width: 28px; text-align: right; white-space: nowrap; }
#poll-area .poll-option { flex-wrap: wrap; }
#poll-area .poll-voters { width: 100%; font-size: 10px; color: var(--text-dim); padding-left: 2px; margin-top: 1px; }
#poll-area .poll-gamble-input { display: flex; align-items: center; gap: 6px; margin-top: 4px; font-size: 12px; }
#poll-area .poll-gamble-input input { background: var(--bg-input); border: 1px solid var(--border); color: var(--text); padding: 2px 4px; border-radius: 2px; font-size: 11px; }
#poll-area .poll-admin { display: flex; gap: 4px; margin-top: 6px; flex-wrap: wrap; }

/* Poll creation modal */
#poll-modal { background: var(--bg-panel); border: 1px solid var(--border); border-radius: 2px; padding: 10px; margin-top: 10px; }
#poll-modal h4 { font-size: 12px; margin-bottom: 6px; }
#poll-modal input[type="text"], #poll-modal textarea { width: 100%; background: var(--bg-input); border: 1px solid var(--border); color: var(--text); padding: 4px 6px; border-radius: 2px; font-size: 12px; margin-bottom: 4px; }
#poll-modal textarea { height: 60px; resize: vertical; font-family: inherit; }
#poll-modal label { font-size: 11px; color: var(--text-dim); display: flex; align-items: center; gap: 4px; margin-bottom: 4px; }
#poll-modal input[type="checkbox"] { width: auto; margin: 0; accent-color: var(--accent); }
#btn-cancel-poll { color: var(--text-dim); }

/* Dynamic poll option rows — one field per option with a remove button. */
.poll-options-label {
  font-size: 11px; color: var(--text-dim); font-weight: 600;
  padding-bottom: 4px; margin-bottom: 6px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
#poll-options-list { display: flex; flex-direction: column; gap: 4px; margin-bottom: 6px; }
.poll-option-row { display: flex; align-items: center; gap: 4px; }
.poll-option-row .poll-option-input {
  flex: 1; width: auto !important; margin-bottom: 0 !important;
}
.poll-option-remove {
  flex-shrink: 0; width: 24px; height: 24px; padding: 0;
  background: transparent; border: 1px solid transparent; border-radius: 2px;
  color: var(--text-dim); cursor: pointer; font-size: 16px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
}
.poll-option-remove:hover { color: var(--red); border-color: rgba(229,57,53,0.3); }
.poll-add-btn {
  font-size: 11px; padding: 4px 8px; color: var(--text-dim);
  background: transparent; border: 1px dashed var(--border-light);
}
.poll-add-btn:hover { color: var(--text); border-color: var(--accent); }

/* ═══════════════════════════════════════
   EMOTE PICKER
   ═══════════════════════════════════════ */
#emote-picker {
  background: var(--bg-panel); border: 1px solid var(--border); border-radius: 2px;
  display: flex; flex-direction: column; margin-top: 10px;
  max-height: 520px;
}
#emote-search {
  width: 100%; padding: 8px 10px; border: none; border-bottom: 1px solid var(--border);
  background: var(--bg-input); color: var(--text); font-size: 13px;
}
#emote-search:focus { outline: none; }
#emote-grid {
  display: flex; flex-direction: column; gap: 6px;
  padding: 8px; overflow-y: auto;
  flex: 1 1 auto;
}
.emote-section {
  font-size: 10px;
  color: var(--text-dim); padding: 4px 2px 2px;
}
.emote-row {
  display: grid; gap: 4px;
  grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
}
.emote-tile {
  position: relative;
  display: flex; flex-direction: column; align-items: center;
  gap: 2px; padding: 4px 2px;
  background: var(--bg); border: 1px solid transparent;
  border-radius: 3px; cursor: pointer;
  overflow: hidden;
  transition: transform 0.08s, border-color 0.08s, background 0.08s;
}
.emote-tile img {
  height: 32px; max-width: 56px; object-fit: contain;
  image-rendering: -webkit-optimize-contrast;
  transition: transform 0.12s;
}
.emote-tile .et-name {
  font-size: 10px; color: var(--text-dim);
  width: 100%; text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.emote-tile mark { background: rgba(92,162,185,0.35); color: var(--text); padding: 0 1px; border-radius: 2px; }
.emote-tile:hover {
  background: var(--bg-panel);
  border-color: var(--accent);
  z-index: 1;
}
.emote-tile:hover img { transform: scale(1.6); }
.emote-tile:hover .et-name { color: var(--text); }
.emote-empty {
  padding: 20px; text-align: center; color: var(--text-dim);
  font-size: 12px; font-style: italic;
}

/* Store panel — compact icon grid. Cards are small so the whole catalog fits in
   view; each item leads with a glyph tile, then name/price, then the action. */
#store-panel { background: var(--bg-panel); border: 1px solid var(--border); border-radius: 3px; margin-top: 10px; overflow-y: auto; max-height: 420px; }
#store-tabs { display: flex; gap: 4px; padding: 8px 8px 0; flex-wrap: wrap; }
#store-tabs .store-tab { padding: 3px 10px; font-size: 11px; background: transparent; border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; cursor: pointer; text-transform: capitalize; transition: background 0.08s, color 0.08s, border-color 0.08s; }
#store-tabs .store-tab.active { background: var(--accent); color: #fff; border-color: var(--accent); }
#store-tabs .store-tab:hover:not(.active) { background: rgba(255,255,255,0.06); color: var(--text); }
#store-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 6px; padding: 8px; }
.store-item {
  background: var(--bg-input); border: 1px solid var(--border); border-radius: 3px;
  padding: 8px 6px 6px; display: flex; flex-direction: column; align-items: center;
  gap: 3px; font-size: 11px; text-align: center;
  transition: border-color 0.08s, transform 0.08s;
}
.store-item:hover { border-color: var(--border-light); transform: translateY(-1px); }
.store-item-equipped { border-color: var(--accent); }
.store-item-img { width: 44px; height: 44px; object-fit: contain; }
/* Glyph tile — the default "image" for items with no artwork. Category-tinted so
   weapons/food/gear/contraband read apart at a glance. */
/* .store-item-glyph and its per-category tints removed with the emoji fallback. */
.store-item-name { font-weight: 600; font-size: 11.5px; line-height: 1.2; }
.store-item-desc { color: var(--text-dim); font-size: 10px; line-height: 1.25; }
.store-item-price { color: var(--gold, #F8C341); font-size: 11px; font-weight: 600; }
.store-item-actions { display: flex; gap: 4px; margin-top: auto; flex-wrap: wrap; justify-content: center; }
.store-buy-btn, .store-sell-btn, .store-use-btn, .store-equip-btn { padding: 3px 9px; font-size: 11px; background: transparent; border: 1px solid var(--accent); color: var(--accent); border-radius: 3px; cursor: pointer; transition: background 0.08s; }
.store-buy-btn { width: 100%; }
.store-buy-btn:hover, .store-sell-btn:hover, .store-use-btn:hover, .store-equip-btn:hover { background: rgba(255,255,255,0.06); }
.store-sell-btn { border-color: var(--text-dim); color: var(--text-dim); }
.store-use-btn { border-color: var(--red); color: #ff6666; }
#store-inventory { border-top: 1px solid var(--border); padding: 6px 8px; }
#inventory-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); gap: 4px; }
/* Crafting recipes */
#recipe-grid { display: flex; flex-direction: column; gap: 6px; }
.recipe-item { background: var(--bg-input); border: 1px solid var(--border); border-radius: 2px; padding: 8px 10px; display: flex; flex-direction: column; gap: 4px; font-size: 12px; }
.recipe-item-locked { opacity: 0.6; }
.recipe-name { font-weight: 600; }
.recipe-io { color: var(--text-dim); font-size: 11px; }
.recipe-io .recipe-have { color: var(--green, #6c6); }
.recipe-io .recipe-missing { color: #ff6666; }
.recipe-craft-btn { align-self: flex-start; padding: 3px 12px; font-size: 11px; background: transparent; border: 1px solid var(--accent); color: var(--accent); border-radius: 2px; cursor: pointer; }
.recipe-craft-btn:hover:not(:disabled) { background: var(--bg-hover, #102030); }
.recipe-craft-btn:disabled { border-color: var(--text-dim); color: var(--text-dim); cursor: not-allowed; }

/* Job panel */
#job-panel { background: var(--bg-panel); border: 1px solid var(--border); border-radius: 2px; padding: 8px 10px; margin-top: 10px; }
/* padding-right reserves the corner for the JS-inserted .bottom-modal-close
   (absolute, right:8px). Without it, space-between pushes the status badge
   ("WEREWOLF") straight under the X. */
#job-panel-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 8px; padding-right: 30px; }
#job-panel-name { font-weight: 600; font-size: 13px; }
#job-panel-hp { font-size: 11px; color: var(--text-dim); }
#job-target-row { margin-bottom: 6px; }
#job-target-input { width: 100%; background: var(--bg-input); border: 1px solid var(--border); color: var(--text); padding: 3px 6px; border-radius: 2px; font-size: 12px; box-sizing: border-box; }
/* Abilities are a compact GRID of icon+name pills, not a stack of full-width
   bordered cards each carrying a sentence of help text. Five stacked cards read as
   a settings page; this reads as a HUD, which is what it is. The help text moved to
   #job-ab-hint (below) so it appears for the ability you're pointing at instead of
   all of them at once. */
#job-abilities {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: 4px; margin-bottom: 4px;
}
.job-ability-btn {
  display: flex; align-items: center; gap: 6px;
  background: var(--bg-input); border: 1px solid var(--border); color: var(--text);
  padding: 6px 8px; border-radius: 2px; cursor: pointer; text-align: left;
  position: relative; min-width: 0;
}
.job-ability-btn:hover:not(:disabled) { background: var(--bg-card); border-color: var(--accent); }
.job-ability-btn:disabled { opacity: 0.4; cursor: default; }
.job-ab-label { font-weight: 600; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Cooldown reads as the button's STATE (dimmed + a countdown in the corner) rather
   than as "(120s CD)" baked into a sentence. */
.job-ab-cd { font-size: 10px; color: #f80; margin-left: auto; font-variant-numeric: tabular-nums; }
/* Ability icons use the SAME stroke system as .ch-icon — one icon language, not
   two. Colour comes from currentColor so a disabled button dims the art with it. */
.ab-icon {
  width: 18px; height: 18px; flex: 0 0 auto;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
/* Work is the one FILLED glyph, and bigger than its siblings (24px vs 18px).
   The stroke rewrite lost art Sean liked; the actual complaint was SIZE, so it is
   restored and scaled up rather than simplified. .work-pick overrides the stroke
   defaults that .ab-icon sets. */
.work-pick { width: 24px; height: 24px; fill: initial; stroke: none; }
.wp-ore       { fill: #6b7683; }
.wp-ore-facet { fill: #8b97a5; }
.wp-handle    { fill: #7a5230; }
.wp-head      { fill: #c3ccd6; }
.wp-spark     { fill: #ffffff; opacity: 0; transition: opacity 180ms ease; }
.work-pick .wp-axe {
  transform-origin: 21px 29px;
  transition: transform 260ms cubic-bezier(.2,.7,.3,1), opacity 200ms ease;
}
/* READY — cocked back over the ore, steel bright, spark lit. */
.work-ready .wp-axe       { transform: rotate(-14deg); }
.work-ready .wp-head      { fill: #e6edf5; }
.work-ready .wp-spark     { opacity: 1; }
.work-ready .wp-ore-facet { fill: #a8b4c2; }
/* COOLING — dropped beside the ore, dull, no spark. */
.work-cooling .wp-axe       { transform: rotate(78deg) translate(1px, 0); opacity: .5; }
.work-cooling .wp-handle    { fill: #55483d; }
.work-cooling .wp-head      { fill: #79828c; }
.work-cooling .wp-ore       { fill: #4e565f; }
.work-cooling .wp-ore-facet { fill: #5c656f; }
.work-cooling .wp-spark     { opacity: 0; }
/* One line that fills with the hovered ability's description. Reserves its own
   height so the grid doesn't jump as it fills and empties. */
#job-ab-hint {
  font-size: 11px; color: var(--text-dim); min-height: 15px;
  margin-bottom: 8px; line-height: 1.35;
}
/* Section headings inside the ability GRID must span the full row, or they occupy
   cell 1 and shove the first ability of that section into column 2. */
#job-abilities .job-ab-section {
  grid-column: 1 / -1; font-size: 10px; color: var(--text-muted);
  margin: 4px 0 0; letter-spacing: 0.02em;
}
#job-switch-row { border-top: 1px solid var(--border); padding-top: 8px; margin-top: 4px; }
#job-switch-btns { display: flex; flex-wrap: wrap; gap: 4px; }
.job-switch-btn { padding: 2px 8px; font-size: 11px; background: transparent; border: 1px solid var(--border); color: var(--text-dim); border-radius: 2px; cursor: pointer; text-transform: capitalize; }
.job-switch-btn:hover:not(.job-switch-locked) { background: var(--bg-hover, #2a2a2a); color: var(--text); }
.job-switch-btn.active { border-color: var(--accent); color: var(--accent); }
/* A job the user can't currently take (limit full, or hidden/admin-only): dimmed
   + blurred + non-interactive. Hover tooltip (title) explains why. */
.job-switch-btn.job-switch-locked {
  opacity: 0.4; filter: blur(0.4px); cursor: not-allowed;
  color: var(--text-dim); border-style: dashed;
}

/* Jobs: health bar and job bar in controls */
/* The job strip is an ACTION bar, not a stray button next to a status readout.
   It deliberately does NOT merge into the rail above it: those seven buttons all
   OPEN A PANEL, while these fire a game action with a cooldown and live state.
   Putting a cooldown button in a row of panel-openers makes one item behave
   unlike every neighbour. #job-actions renders every non-targeted ability, so
   this row grows as jobs gain abilities — it is the action bar, not an exception.
   Grouping it visually with the job name and HP is what makes that legible. */
#job-bar {
  display: flex; align-items: center; gap: 8px; font-size: 11px;
  margin-left: 0; padding: 4px 8px;
  background: var(--bg-input); border: 1px solid var(--border); border-radius: 3px;
}
#job-name { color: var(--text-dim); }
#hp-bar-wrap { width: 60px; height: 8px; background: #333; border-radius: 2px; overflow: hidden; }
#hp-fill { height: 100%; width: 100%; background: #4c8; transition: width 0.3s, background-color 0.3s; }
#hp-text { font-size: 10px; color: var(--text-dim); min-width: 36px; }
/* Quick job-action buttons next to the HP bar (self/non-target abilities). */
#job-actions { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.job-action-btn {
  font-size: 10px; padding: 2px 8px; border-radius: 2px;
  background: var(--bg-input); border: 1px solid var(--border-light);
  color: var(--text); cursor: pointer; white-space: nowrap; line-height: 1.4;
  transition: background 0.1s, border-color 0.1s;
}
.job-action-btn:hover:not(:disabled) { background: var(--bg-card); border-color: var(--accent); color: var(--accent); }
.job-action-btn:disabled { opacity: 0.45; cursor: default; }

/* Job badge in userlist */
.job-badge { color: var(--text-dim); background: rgba(255,255,255,0.06); }
.compact-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 14px; height: 13px; padding: 0 3px; margin-left: 0;
  border-radius: 2px; font-size: 9px; line-height: 1; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0;
}

/* Dead user state */
.user-dead { opacity: 0.4; text-decoration: line-through; }

/* Jail: vertical "bars" over the name. A repeating gradient draws the bars on
   top of the name; the name stays readable behind them. Applied in the
   userlist (.user-jailed) and on the jailed user's chat lines (.chat-jailed). */
.user-jailed, .chat-jailed .chat-user {
  position: relative;
}
.user-jailed::after, .chat-jailed .chat-user::after {
  content: "";
  position: absolute;
  left: 0; top: -1px; right: 0; bottom: -1px;
  pointer-events: none;
  background: repeating-linear-gradient(
    to right,
    #9aa3ad 0, #9aa3ad 2px,
    transparent 2px, transparent 6px
  );
  opacity: 0.85;
}
.chat-jailed .chat-text { opacity: 0.7; }

/* Death indicator — compact, NON-BLOCKING. Your character is dead (no combat/
   abilities, enforced server-side), but you can still chat/watch/queue/vote.
   A small fixed badge in the corner; never covers the room, never eats clicks. */
#death-overlay {
  position: fixed; bottom: 12px; left: 50%; transform: translateX(-50%);
  z-index: 9000; pointer-events: none;
  display: flex; align-items: center; gap: 8px;
  padding: 6px 14px; border-radius: 3px;
  background: rgba(40,8,8,0.92); border: 1px solid #5a2020;
  box-shadow: 0 2px 10px rgba(0,0,0,0.5);
}
#death-overlay.hidden { display: none; }
#death-overlay-inner { display: flex; align-items: center; gap: 8px; text-align: left; }
#death-skull { font-size: 20px; line-height: 1; }
#death-msg { font-size: 13px; font-weight: 600; color: #f86b6b; }
#death-timer { font-size: 12px; color: var(--text-dim); }

/* Chat options panel */
#chat-opts-panel { background: var(--bg-panel); border: 1px solid var(--border); border-radius: 2px; padding: 8px 10px; margin-top: 10px; }

/* ═══════════════════════════════════════
   USER CONTEXT MENU
   ═══════════════════════════════════════ */
.user-menu {
  position: fixed; z-index: 9999;
  background: var(--bg-panel); border: 1px solid var(--border-light);
  border-radius: 3px; width: 300px; max-width: calc(100vw - 16px);
  max-height: calc(100vh - 16px); overflow-y: auto;
  box-shadow: 0 8px 24px rgba(0,0,0,0.6);
  padding: 0 0 6px;
}
.user-menu-title {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 8px 10px; font-size: 13px;
  color: var(--text); border-bottom: 1px solid var(--border);
  background: var(--bg); position: sticky; top: 0;
}
.user-menu-name {
  font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.user-menu-rank {
  flex-shrink: 0; font-size: 10px; font-weight: 600;
  padding: 1px 6px; border-radius: 2px;
  background: var(--border); color: var(--text-dim);
}
.user-menu-rank.rank-2 { background: rgba(92,162,185,0.18); color: #7cc0d6; }
.user-menu-rank.rank-3 { background: rgba(76,175,80,0.18); color: #6bc96f; }
.user-menu-rank.rank-4 { background: rgba(229,57,53,0.18); color: #ef6b68; }
/* Chip group: buttons flow left-to-right and wrap, so the menu stays short and
   wide instead of a tall single column. */
.user-menu-group {
  display: flex; flex-wrap: wrap; gap: 4px;
  padding: 5px 8px;
}
.user-menu-group:empty { display: none; padding: 0; }
.user-menu-btn {
  flex: 1 1 auto;
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--border);
  color: var(--text); text-align: center;
  padding: 6px 10px; font-size: 12px; white-space: nowrap;
  cursor: pointer; border-radius: 3px;
  transition: background 0.08s, border-color 0.08s, color 0.08s;
}
.user-menu-btn:hover:not(:disabled) {
  background: rgba(255,255,255,0.09); border-color: var(--accent); color: var(--accent);
}
.user-menu-btn:disabled { opacity: 0.4; cursor: default; }
.user-menu-section {
  padding: 5px 10px 0; font-size: 10px; color: var(--text-dim);
  margin-top: 3px; border-top: 1px solid var(--border);
}
.user-menu-group:first-of-type { border-top: none; }
/* Destructive mod/admin actions read in a warmer tone. */
.user-menu-btn.user-menu-btn--danger {
  border-color: rgba(229,57,53,0.25); color: #d98c8a;
}
.user-menu-btn.user-menu-btn--danger:hover:not(:disabled) {
  background: rgba(229,57,53,0.12); border-color: rgba(229,57,53,0.5); color: #ef6b68;
}

.factions-overlay {
  position: fixed; inset: 0; z-index: 10000; display: flex;
  align-items: center; justify-content: center; padding: 12px;
  background: rgba(0,0,0,0.62);
}
.factions-panel {
  width: min(420px, 100%); max-height: min(620px, calc(100dvh - 24px));
  overflow-y: auto; background: var(--bg-panel); border: 1px solid var(--border-light);
  border-radius: 2px; box-shadow: 0 10px 28px rgba(0,0,0,0.55);
}
.factions-head {
  position: sticky; top: 0; z-index: 1; display: flex; align-items: center;
  justify-content: space-between; min-height: 44px; padding: 0 8px 0 12px;
  background: var(--bg-panel); border-bottom: 1px solid var(--border);
}
.factions-close, .faction-join {
  min-height: 44px; min-width: 44px; border: 1px solid var(--border);
  border-radius: 2px; background: var(--bg); color: var(--text); cursor: pointer;
}
.factions-close { border: 0; background: transparent; font-size: 20px; }
.factions-note { margin: 0; padding: 10px 12px; color: var(--text-muted); font-size: 12px; border-bottom: 1px solid var(--border); }
.factions-list { padding: 4px 12px 12px; }
.faction-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 58px; border-bottom: 1px solid var(--border); }
.faction-row:last-child { border-bottom: 0; }
.faction-row--mine { border-left: 2px solid var(--accent); padding-left: 8px; }
.faction-name { color: var(--text); font-size: 13px; font-weight: 600; }
.faction-detail { color: var(--text-muted); font-size: 11px; margin-top: 2px; }
.faction-join { padding: 0 12px; }
.faction-join:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.faction-join:disabled { opacity: 0.5; cursor: default; }

/* ═══════════════════════════════════════
   MOTD / Gold / Shadow / PM / Emotes
   ═══════════════════════════════════════ */
/* Gold message: the shimmer letters (.text-lottery) carry it — no background
   tint/border. EVERYONE who says a gold text shimmers; only the FIRST claimant
   (.gold-name, set from meta.goldName) also gets the gold username + the
   one-time celebratory grow/glow burst on the text (like the secret-word mine). */
.gold-name .chat-user { color: #c8a84b !important; }
.gold-first-anim .chat-text {
  display: inline-block; transform-origin: left center;
  animation: gold-first 2.4s ease-out;
}
@keyframes gold-first {
  0%   { transform: scale(1);    text-shadow: none; }
  12%  { transform: scale(1.35); text-shadow: 0 0 14px #ffd76b, 0 0 28px #c8a84b; }
  35%  { transform: scale(1.25); text-shadow: 0 0 12px #ffd76b, 0 0 20px #c8a84b; }
  75%  { transform: scale(1.06); text-shadow: 0 0 6px  #c8a84b; }
  100% { transform: scale(1);    text-shadow: none; }
}
.shadow-msg { opacity: 0.5; font-style: italic; }
/* Mod option "Hide shadow-muted messages" — mods see shadowed lines by default;
   this hides them. Live CSS-class toggle. */
.hide-shadow .shadow-msg { display: none; }
/* "Hide images in chat" (strip_image parity) — hide the link-preview image and
   the 🐸 image hover-link. Keeps the link-preview text card. Emotes have their
   own toggle (.hide-emotes). */
.hide-images .lp-img,
.hide-images .chat-img-link { display: none; }
.afk-badge { color: #666; font-size: 10px; margin-left: 4px; }
.pm-msg { background: rgba(74,158,255,0.08); border-left: 2px solid #4a6eff; padding-left: 4px; font-style: italic; }
.pm-msg .chat-user { color: #6a9eff; }
.ch-emote { max-height: 100px; vertical-align: middle; }
/* "Disable emotes" (live toggle): hide emote images on existing AND future
   messages with no re-render. The img's alt (":name:") still conveys the emote
   to anyone who needs it. */
.hide-emotes .ch-emote { display: none; }

/* ═══════════════════════════════════════
   DANMU + SECRETARY
   ═══════════════════════════════════════ */
/* Full-viewport danmu layer — sits above everything, ignores clicks. */
#danmu-layer {
  position: fixed; inset: 0;
  pointer-events: none;
  z-index: 9500;
  overflow: hidden;
}
.danmu {
  position: absolute; white-space: nowrap;
  font-size: clamp(18px, 2.2vw, 30px);
  font-weight: 700; color: #fff;
  text-shadow: 1px 1px 3px #000, -1px -1px 3px #000, 0 0 6px rgba(0,0,0,0.7);
  pointer-events: none;
  left: 100vw;
  animation: danmu-scroll 10s linear forwards;
}
.danmu-user {
  color: var(--cta);
  font-weight: 600;
  margin-right: 6px;
  opacity: 0.9;
}
@keyframes danmu-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(-100vw - 100%)); }
}
.secretary-overlay {
  position: fixed; top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(0,0,0,0.85); z-index: 9999;
  display: flex; align-items: center; justify-content: center;
  animation: secretary-flash 0.3s ease-out;
}
.secretary-text {
  font-size: 32px; font-weight: 700; color: #fff;
  text-align: center; max-width: 80%; line-height: 1.4;
  text-shadow: 0 0 20px var(--accent);
}
@keyframes secretary-flash {
  0% { opacity: 0; transform: scale(1.2); }
  100% { opacity: 1; transform: scale(1); }
}
.chat-danmu { color: var(--accent); }
.chat-danmu .chat-user::before { content: '◈ '; }
.chat-secretary { color: var(--red); font-weight: 700; }
.chat-secretary .chat-user::before { content: '⚠ '; }

/* ═══════════════════════════════════════
   COOLHOLE EASTER EGGS (from original)
   ═══════════════════════════════════════ */
.text-lottery, .text-lottery a {
  font-weight: bold;
  background-image: repeating-linear-gradient(4deg, #462523 0, #cb9b51 22%, #f6e27a 45%, #f6f2c0 50%, #f6e27a 55%, #cb9b51 78%, #462523 100%);
  color: transparent;
  -webkit-background-clip: text;
  background-clip: text;
  animation: chGold 3.5s linear infinite;
}
@keyframes chGold { 0% { background-position: -15em; } 100% { background-position: 10em; } }

#messagebuffer .chat-line[data-text*="coolhole1"] {
  animation: shrink 5s forwards; overflow: hidden;
}
@keyframes shrink { from { font-size: inherit; max-height: 100px; } to { font-size: 0px; max-height: 0; } }

#messagebuffer .chat-line[data-text*="european"] .chat-text,
.danmu[data-text*="european"],
.secretary-overlay[data-text*="european"] .secretary-text {
  animation: rainbow 5s infinite;
}
@keyframes rainbow {
  0% { color: #6666ff; } 10% { color: #0099ff; }
  50% { color: #00ff00; } 75% { color: #ff3399; } 100% { color: #6666ff; }
}

/* Returnfire — a "shot" chat line fades out red (classic Coolhole). */
.shotKilled {
  color: red;
  animation: ch-shot-fade 2s forwards;
}
@keyframes ch-shot-fade {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* Flip emotes */
.ch-emote[title="/flip"]+.ch-emote { transform: scaleX(-1); }

/* ═══════════════════════════════════════
   SCROLLBAR
   ═══════════════════════════════════════ */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #3a3a3a; border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: #4a4a4a; }

/* ═══════════════════════════════════════
   RANK BAR — job panel XP progression
   ═══════════════════════════════════════ */
#job-panel-rank {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 10px 6px; font-size: 11px;
  color: var(--text-dim);
}
#job-panel-rank .rank-name {
  color: var(--gold); font-weight: 600; text-transform: uppercase;
  font-size: 10px; letter-spacing: 0.5px;
}
#job-panel-rank .rank-xp-bar {
  flex: 1; height: 4px; background: var(--bg-input);
  border-radius: 2px; overflow: hidden;
}
#job-panel-rank .rank-xp-fill {
  display: block; height: 100%; background: var(--gold);
  transition: width 0.3s;
}
#job-panel-rank .rank-xp-text { font-size: 10px; }

/* Promotion shimmer — added/removed by JS on tier up */
@keyframes rank-promoted {
  0%   { box-shadow: 0 0 0 0 rgba(212,175,55,0.7); }
  50%  { box-shadow: 0 0 0 6px rgba(212,175,55,0); }
  100% { box-shadow: 0 0 0 0 rgba(212,175,55,0); }
}
#job-panel-rank.promoted { animation: rank-promoted 1s ease-out 3; }
#job-panel-rank.promoted .rank-xp-fill { background: #fff; transition: none; }

/* Status badge inside job panel header */
#job-panel-status { font-size: 9px; }

/* Ability section separator */
.job-ab-section {
  font-size: 9px;
  color: var(--text-dim); padding: 6px 0 2px; margin-top: 4px;
  border-top: 1px solid var(--border);
}

/* Status badges in userlist */
.u-status {
  display: inline-block; padding: 0 4px; margin-left: 4px;
  border-radius: 2px; font-size: 9px; text-transform: uppercase;
  letter-spacing: 0.5px; font-weight: 600;
}
.u-status.compact-badge { display: inline-flex; padding: 0 3px; margin-left: 0; letter-spacing: 0; }
.u-status-vampire { background: #4a0010; color: #ff4466; }
.u-status-vhunter { background: #2a1810; color: #d4a050; }
.u-status-werewolf { background: #2a1a10; color: #ff8830; }
.u-status-infected { background: #1a2a10; color: #88cc44; }
.u-status-ghoul { background: #20001a; color: #cc4488; }
.u-status-skeleton { background: #1a1a1a; color: #ccc; }
.u-hybrid {
  color: var(--gold); font-size: 9px;
  display: inline-block; padding: 0 4px;
  border-radius: 2px; background: rgba(255,255,255,0.05);
}
.u-hybrid.compact-badge { display: inline-flex; padding: 0 3px; }

/* Day/night indicator */
#day-night {
  font-size: 10px; color: var(--text-dim);
  padding: 0 6px; display: inline-flex; align-items: center; gap: 4px;
}
#day-night.is-day::before { content: '☀'; color: #f8c341; }
#day-night.is-night::before { content: '☾'; color: #88aaff; }
#day-night.is-moon::before { content: '●'; color: #ff4466; }

/* ═══════════════════════════════════════
   TRADE DIALOG
   ═══════════════════════════════════════ */
#trade-dialog {
  background: var(--bg-panel); border: 1px solid var(--border-light);
  border-radius: 3px; padding: 10px; margin-bottom: 8px;
}
.trade-header { font-weight: 600; font-size: 13px; margin-bottom: 8px; }
.trade-row {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 6px; font-size: 13px;
}
.trade-row label { width: 60px; flex-shrink: 0; color: var(--text-dim); }
.trade-row select, .trade-row input { flex: 1; background: var(--bg-input); border: 1px solid var(--border-light); color: var(--text); padding: 3px 6px; border-radius: 2px; font-size: 13px; }
.trade-buttons { display: flex; gap: 6px; margin-top: 8px; justify-content: flex-end; }

/* ═══════════════════════════════════════
   KILL FEED
   ═══════════════════════════════════════ */
.kill-feed {
  color: var(--gold); font-size: 11px; font-style: italic;
  padding: 1px 8px; opacity: 0.85;
  animation: kf-fade 12s forwards;
}
.kill-feed-death_penalty { color: var(--red); }
.kill-feed-poll_win { color: var(--green); }
.kill-feed-major {
  margin: 2px 6px; padding: 3px 7px; border-left: 2px solid currentColor;
  background: rgba(255,255,255,0.035); font-style: normal;
}
@keyframes kf-fade { 0% { opacity: 0.85; } 80% { opacity: 0.85; } 100% { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  #poll-area.poll-result-moment, .kill-feed, .ef-line { animation: none; }
}

/* ═══════════════════════════════════════
   EVENT FEED (real-time HUD)
   ═══════════════════════════════════════ */
/* Event feed: a compact, SCROLLING HUD strip. Capped tight (3 lines) and
   scroll-not-grow so a burst of system/game messages can't push the queue off
   the bottom of the screen (user-reported). The game-msg visibility setting can
   also move these to chat or hide them entirely. */
#event-feed {
  width: 100%;
  /* Compact (mkklb): bottom should line up with the chat panel, not balloon.
     ~3 lines tall, scroll the rest. */
  min-height: 0;
  max-height: 58px;
  align-self: start;
  overflow-y: auto;
  background: var(--bg-chat);
  border: 1px solid var(--border-light);
  color: var(--text);
  padding: 2px 8px; font-size: 11px;
  font-family: 'Consolas', 'Monaco', monospace;
  line-height: 1.5;
}
.ef-line {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  animation: ef-in 0.2s ease-out;
}
.ef-line-damage { color: #ff6666; }
.ef-line-heal { color: #66cc88; }
.ef-line-rob, .ef-line-cp_loss { color: #ff9944; }
.ef-line-arrest { color: #6688ff; }
.ef-line-death { color: #ff4466; font-weight: 600; }
.ef-line-bite { color: #cc4466; }
.ef-line-stake { color: #d4a050; }
.ef-line-status { color: #aa66cc; }
.ef-line-trade { color: var(--gold); }
.ef-line-cp { color: var(--text); opacity: 0.82; }
.ef-line-phase { color: #88aaff; font-style: italic; }
@keyframes ef-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }

/* ═══════════════════════════════════════
   CHARACTER SHEET
   ═══════════════════════════════════════ */
#charsheet-panel { padding: 8px 10px; max-height: 400px; overflow-y: auto; }
.cs-section { margin-top: 14px; }
.cs-section:first-child { margin-top: 0; }
.cs-section-title {
  font-size: 11px; font-weight: 600;
  color: var(--text-muted);
  padding-bottom: 4px; margin-bottom: 6px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.cs-row {
  display: grid; grid-template-columns: 1fr auto; gap: 12px;
  align-items: baseline;
  font-size: 13px; padding: 3px 0;
}
.cs-label { color: var(--text-dim); }
.cs-val {
  font-weight: 600; font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em; color: var(--text);
}
.cs-val-red { color: var(--red); }
.cs-val-green { color: var(--green); }
.cs-val-gold { color: var(--gold); }
.cs-val-purple { color: #c9b8ff; }
.cs-txn { gap: 6px; }
.cs-txn-type { flex: 1; text-transform: capitalize; }
.cs-txn-bal { color: var(--text-dim); font-size: 11px; white-space: nowrap; }

/* ═══════════════════════════════════════
   LEADERBOARD
   ═══════════════════════════════════════ */
#leaderboard-panel { padding: 8px 0; }

/* ═══════════════════════════════════════
   MOBILE — stack layout below 768px
   ═══════════════════════════════════════ */
@media (max-width: 768px) {
  /* Navbar: wrap login inputs */
  #navbar { flex-wrap: wrap; height: auto; padding: 6px 10px; gap: 6px; }
  #nav-links {
    /* Wrap and shrink instead of hide so Options/Admin are reachable */
    flex-wrap: wrap; gap: 0; flex: 1 0 100%; order: 3;
    border-top: 1px solid var(--border); padding-top: 4px; margin-top: 2px;
  }
  #nav-links a { padding: 4px 8px; font-size: 12px; }
  #login-bar { flex-wrap: wrap; gap: 4px; }
  #login-bar input { width: 100px; font-size: 12px; }
  #nav-auth { margin-left: 0; }

  /* Modals: take full viewport on phones */
  .hat-modal-panel, .admin-modal-panel {
    width: 100vw !important; height: 100vh !important; max-width: none !important;
    max-height: none !important; border-radius: 0; top: 0; left: 0; transform: none;
  }

  /* Leader seek bar: smaller padding on phones */
  #leader-seekbar { padding: 4px 6px; }

  /* Pinned message: smaller padding */
  #pinnedwrap { padding: 6px 8px; }
  #pinned-text { font-size: 12px; }

  /* Queue rating chip hidden on tight screens to save room */
  .q-rating { display: none; }

  /* Now-playing title: wrap to save header space */
  #videowrap-header { flex-wrap: wrap; gap: 4px; padding: 4px 6px; }
  #currenttitle { flex: 1 0 100%; order: 1; font-size: 13px; }
  #currentby { flex: 0 0 auto; }
  #currentrating { flex: 0 0 auto; margin-left: auto; }

  /* Main padding */
  #mainpage { padding: 0 6px; }

  /* Stack video above chat on mobile (DOM has chat first for desktop layout).
     Neutralize the desktop player-driven height so the stacked column flow governs.
     #main must FILL #mainpage here (flex:1) so the chat below the video gets the
     remaining height — the desktop flex:0 0 auto would collapse the chat to 0. */
  #mainpage > #main { flex: 1 1 auto; }
  #main { flex-direction: column; height: auto; }
  #chatwrap, #videowrap { height: auto; }
  #player-wrap { flex: none; }

  /* Video wrap: show first, sized to aspect ratio */
  #videowrap {
    flex: none; width: 100%; min-width: 0;
    order: 1; margin-bottom: 8px;
  }
  #player-wrap { aspect-ratio: 16/9; height: auto !important; }

  /* Chat wrap: below video, fills remaining viewport height */
  #chatwrap {
    flex: none; max-width: 100%; min-width: 0;
    margin-right: 0; margin-bottom: 8px;
    order: 2;
    display: flex; flex-direction: column;
  }
  #chatbody {
    height: auto; min-height: 240px;
    flex: 1 1 auto;
  }

  /* Collapse userlist by default on mobile */
  #userlist { width: 0; min-width: 0; border-right: none; overflow: hidden; }
  #userlisttoggle { display: none; }

  /* Controls row: wrap and shrink */
  /* Stacked layout (video full-width above chat): bottom bar is full-width too —
     drop the chat-width spacer. */
  /* Desktop bottom-bar is a 2-col GRID; on mobile collapse to a single stacked
     column (grid-template-columns:1fr) so #controlsrow + #bottom-queue don't
     overlap. Order: queue console first, game controls below. */
  #bottom-bar { display: grid; grid-template-columns: 1fr; gap: 6px; border-top: none; }
  #controlsrow { grid-column: 1; order: 2; }
  #bottom-queue { grid-column: 1; order: 1; width: 100%; }
  #leftcontrols, #rightcontrols { flex-wrap: wrap; gap: 4px; }
  #leftcontrols button, #rightcontrols button { font-size: 12px; padding: 3px 7px; }
  #rightcontrols { margin-bottom: 4px; }
  /* Tablet: still show a real lineup, just a bit shorter than desktop.
     Content-sized (no min-height) so an empty queue doesn't reserve a void. */
  #bottom-queue #queue { max-height: 40vh; }

  /* Volume slider: full width */
  #volume-row { flex-wrap: wrap; }
  #volume-slider { width: 100px; }

  /* Playlist row: stack */
  #playlistrow { flex-direction: column; }
  #playlistwrap { width: 100%; min-width: 0; margin-right: 0; margin-bottom: 8px; }
  #rightpane { width: 100%; min-width: 0; }

  /* Store / poll panels: full width */
  #store-panel, #poll-panel, #leftpane { width: 100% !important; min-width: 0; }
}

/* ─────────────────────────────────────────────────────────────────────────
   PHONE (<= 640px): watch-party layout — video pinned top, chat fills below
   ───────────────────────────────────────────────────────────────────────── */
@media (max-width: 640px) {
  html, body {
    font-size: 14px;
    height: 100%;
    overflow: hidden; /* page itself doesn't scroll; chat does */
  }

  /* Prevent iOS auto-zoom by enforcing 16px on all inputs. */
  input, select, textarea, button { font-size: 16px !important; }

  #navbar {
    padding: 4px 8px; gap: 2px 4px; height: 72px; min-height: 72px;
    position: fixed; top: 0; left: 0; right: 0; z-index: 1000;
    flex-wrap: wrap; align-content: center;
  }
  #navbar .logo { order: 1; margin-right: 2px; flex: 0 0 auto; }
  #navbar .logo img { height: 18px; }
  #nav-auth {
    order: 2; margin-left: auto; min-width: 0; flex: 0 1 auto;
  }
  #nav-links {
    flex: 1 0 100%; order: 3; min-width: 0;
    border-top: 1px solid var(--border); padding: 2px 0; margin-top: 2px;
  }
  #nav-links a { padding: 3px 6px; font-size: 12px; }
  #user-info { gap: 5px; min-width: 0; }
  #username {
    max-width: clamp(64px, 24vw, 100px);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  #nav-feedback { width: 28px; height: 28px; padding: 4px; flex: 0 0 auto; }
  #btn-logout { font-size: 12px !important; padding: 4px 7px; flex: 0 0 auto; }

  /* Guests use row one for brand/navigation and row two for a real login form.
     Inputs previously flexed to width:0 between four fixed controls, rendering as
     two anonymous squares at 390px. */
  #navbar:has(#login-bar:not(.hidden)) #nav-links {
    order: 2; flex: 1 1 auto; justify-content: flex-end;
    border-top: none; padding: 0; margin: 0;
  }
  #navbar:has(#login-bar:not(.hidden)) #nav-auth {
    order: 3; flex: 1 0 100%; width: 100%;
  }
  #login-bar {
    width: 100%; display: grid;
    grid-template-columns: minmax(70px, 1fr) minmax(70px, 1fr) auto auto;
    gap: 4px;
  }
  #login-bar input {
    width: auto; min-width: 0;
    font-size: 16px !important; padding: 4px 6px;
  }
  #login-bar button { font-size: 13px !important; padding: 5px 8px; }

  /* Viewport-filling column layout: navbar → main → controlsrow → playlist. */
  #wrap {
    height: 100vh; height: 100dvh;
    display: flex; flex-direction: column;
    overflow: hidden;
  }
  #mainpage {
    padding: 0; margin-top: 0;
    flex: 1 1 auto; min-height: 0;
    display: flex; flex-direction: column;
    overflow: hidden;
    padding-top: 72px; /* below the fixed two-row phone navbar */
  }

  /* Hide decorative rows that eat vertical space on phones. */
  #motdrow { display: none !important; }
  #drinkbarwrap { display: none !important; }

  /* Main row stacks video → chat in every layout mode. The desktop Synchtube
     selector is more specific than a bare #main rule, so override it explicitly
     here or that preference can turn the phone layout back into a reversed row. */
  #mainpage > #main { flex: 1 1 auto; }  /* fill mainpage so chat gets remaining height */
  #main, body.layout-synchtube #main {
    flex: 1 1 auto; min-height: 0;
    flex-direction: column;
    display: flex;
    gap: 0;
    height: auto;            /* override desktop player-driven height */
  }

  /* Video: full width, 16:9 when playing, collapses when no media. */
  #videowrap {
    flex: none !important; width: 100%; min-width: 0; margin: 0;
    order: 1;
    background: #000;
    height: auto; display: block; position: relative; /* overlay owns mobile metadata */
  }
  /* Video metadata/actions stay out of the mobile layout until requested. They
     overlay the picture, so revealing them does not push chat or controls. */
  #videowrap-header {
    position: absolute; top: 0; left: 0; right: 0; z-index: 6;
    height: auto; min-height: 34px; padding: 4px 48px 4px 8px; font-size: 12px;
    background: rgba(19, 27, 34, 0.94); border-color: var(--border);
    opacity: 0; visibility: hidden; pointer-events: none;
    transform: translateY(-4px);
    transition: opacity 0.12s ease, transform 0.12s ease, visibility 0s linear 0.12s;
  }
  #videowrap.mobile-video-options-open #videowrap-header {
    opacity: 1; visibility: visible; pointer-events: auto;
    transform: translateY(0);
    transition-delay: 0s;
  }
  /* Closed: the transparent button covers the player so iframe taps can reveal
     options. Open: it shrinks to a small three-dot close/reopen affordance. */
  #mobile-video-options-toggle {
    display: flex; position: absolute; inset: 0; z-index: 7;
    align-items: flex-start; justify-content: flex-end;
    padding: 6px; border: 0; background: transparent; color: #fff;
    cursor: pointer;
  }
  #mobile-video-options-toggle span {
    display: flex; align-items: center; justify-content: center;
    min-width: 34px; height: 28px; padding: 0 6px;
    border: 1px solid rgba(255,255,255,0.18); border-radius: 3px;
    background: rgba(0,0,0,0.58); line-height: 1;
  }
  #videowrap.mobile-video-options-open #mobile-video-options-toggle {
    inset: 4px 6px auto auto; width: 36px; height: 30px; padding: 0;
  }
  #player-wrap:has(#player.no-media) #mobile-video-options-toggle { display: none; }
  #currenttitle { font-size: 12px; }
  #player-wrap {
    width: 100%; aspect-ratio: 16 / 9; height: auto;
    max-height: 40vh;
    background: #000;
    flex: none;              /* override desktop flex:1 — aspect-ratio drives it here */
  }
  /* When the player has no media, collapse the black box on mobile. */
  #player-wrap:has(#player.no-media) {
    display: none;
  }
  /* Fallback for browsers without :has (older iOS) — at least cap the height. */
  #player.no-media { max-height: 60px; }
  #player { width: 100%; height: 100%; }
  #player iframe, #player video { width: 100% !important; height: 100% !important; }

  /* Chat: takes remaining height, scrollable. */
  #chatwrap {
    flex: 1 1 0 !important; min-height: 0;
    order: 2;
    width: 100%; max-width: 100%; min-width: 0;
    margin: 0; padding: 0;
    display: flex; flex-direction: column;
    height: auto;            /* override desktop height:100% */
  }
  #chatheader { height: 26px; padding: 0 6px; font-size: 12px; flex: none; }
  #chatbody {
    flex: 1 1 auto; min-height: 0; height: auto !important;
    border: none; border-top: 1px solid var(--border);
  }
  #messagebuffer { padding: 4px 6px; font-size: 14px; flex: 1 1 auto; min-height: 0; }
  /* Guest login row: hide when the text field is disabled (already logged in)
     or when chatline is focused and we want max room. */
  #guestlogin { flex: none; padding: 4px 6px; font-size: 12px; }
  #guestlogin:empty { display: none; }

  /* Userlist: collapsed by default, expandable slide-over panel. */
  #userlist {
    position: fixed; top: 40px; left: 0;
    width: 180px; height: calc(100vh - 40px);
    background: var(--bg-panel); border-right: 1px solid var(--border);
    overflow-y: auto; z-index: 150;
    transform: translateX(-100%); transition: transform 0.2s;
  }
  #userlist.expanded {
    transform: translateX(0);
    width: 180px !important; min-width: 180px !important;
    border-right: 1px solid var(--border) !important;
    overflow-y: auto !important;
  }
  #userlisttoggle { display: inline-block !important; cursor: pointer; padding: 0 4px; }

  /* Chatline: stays inline above controls. 16px prevents iOS zoom. */
  #chatline {
    font-size: 16px !important;
    padding: 8px 10px;
    border-top: 1px solid var(--border);
  }

  /* Phone: single stacked column (override the desktop 2-col grid so columns
     don't overlap). Queue console first, game controls below. */
  #bottom-bar, body.layout-synchtube #bottom-bar {
    display: grid; grid-template-columns: 1fr;
    gap: 4px;
    padding: 4px 6px;
    border-top: none;
  }
  #bottom-queue, body.layout-synchtube #bottom-queue {
    grid-column: 1; grid-row: 1; order: 1; width: 100%;
  }
  #event-feed { display: none; }
  #controlsrow, body.layout-synchtube #controlsrow {
    grid-column: 1; grid-row: 2; order: 2;
    overflow: hidden; flex-wrap: nowrap !important;
    padding: 0; gap: 4px;
  }
  #leftcontrols {
    width: 100%; flex-wrap: wrap !important; flex-shrink: 1; gap: 4px;
  }
  #rightcontrols {
    flex-wrap: nowrap !important; flex-shrink: 0; gap: 4px;
  }
  #leftcontrols button, #rightcontrols button, #plcontrol button {
    flex-shrink: 0; height: 44px; padding: 6px 10px; font-size: 12px;
  }
  #first-use-guide { padding: 4px 2px 6px; }
  #leftcontrols #first-use-guide button { height: 44px; padding: 6px 12px; font-size: 13px !important; }
  #leftcontrols #first-use-dismiss { width: 44px; font-size: 18px !important; }
  /* Labels stay on phone. They were hidden here, which left the entire control
     field as unlabelled icons on the one device with no hover tooltips at all.
     #leftcontrols already wraps, so a second row is the correct outcome. */
  #leftcontrols button .btn-label, #rightcontrols button .btn-label,
  #plcontrol button .btn-label { font-size: 9px; }

  /* Keep identity/health/actions visible without making the icon rail scroll.
     Extra abilities may scroll inside their own strip; the identity itself never
     disappears off the right edge. */
  #job-bar {
    order: 10; flex: 1 0 100%; width: 100%; min-width: 0;
    margin-left: 0; padding: 4px 2px 0;
    border-top: 1px solid var(--border);
  }
  #job-name {
    max-width: 92px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    flex: 0 1 auto;
  }
  #hp-bar-wrap { flex: 1 1 60px; min-width: 44px; max-width: 96px; }
  #hp-text { flex: 0 0 auto; }
  #job-actions {
    flex: 1 1 auto; min-width: 0; flex-wrap: nowrap;
    overflow-x: auto; -webkit-overflow-scrolling: touch;
  }

  /* Queue: full width, tighter. */
  #playlistrow { flex-direction: column; padding: 0 6px; }
  #playlistwrap, #rightpane { width: 100%; min-width: 0; margin: 0; }
  #rightcontrols {
    overflow-x: auto;
    margin-bottom: 4px;
    -webkit-overflow-scrolling: touch;
  }
  #bottom-queue #queue { max-height: 80px; }
  .videolist li { padding: 6px 4px; }
  .q-thumb-box { width: 56px !important; height: 32px !important; }
  .q-title { font-size: 13px; }
  .q-sub, .q-eta { font-size: 10px; }
  .q-actions { max-height: none !important; opacity: 1 !important; } /* always visible */
  .q-grab { display: none; } /* no drag on touch */

  /* URL input row (Next/End): keep input flexible so buttons don't wrap. */
  .url-input-group { display: flex; gap: 4px; align-items: stretch; }
  .url-input-group input { flex: 1 1 0; min-width: 0; }
  .url-input-group button { flex: none; padding: 4px 10px; }
  #addfromurl, #searchcontrol { padding: 0; }

  /* Right controls (#rightcontrols): dock below main row, not stretch chat. */
  #rightcontrols { flex: none; }

  /* PM windows: stack vertically at bottom, full width. */
  #pm-stack {
    position: fixed; bottom: 0; left: 0; right: 0; top: auto;
    display: flex; flex-direction: column-reverse; z-index: 200;
  }
  .pm-window {
    width: 100% !important; max-width: 100% !important;
    border-radius: 0; border-left: none; border-right: none;
  }
  .pm-window:not(.minimized) { height: 50vh; }

  /* Autofill dropdown now anchors to #chatline-wrap (full width of the chat
     column), so it already stays within the viewport — no extra offset needed. */

  /* Points display: smaller. */
  #points-display { font-size: 13px !important; padding: 1px 6px !important; }
  #username { font-size: 13px; }

  /* Emote picker / poll modal / hat modal: near-full screen. */
  .hat-modal-panel, .admin-modal-panel, #poll-modal {
    width: 100vw !important; height: 100vh !important;
    max-width: none !important; max-height: none !important;
    border-radius: 0; top: 0 !important; left: 0 !important;
    transform: none !important; inset: 0;
  }

  /* Danmu: smaller on phones so more lines fit. */
  .danmu { font-size: clamp(14px, 4vw, 22px); }

  /* User menu: position so it doesn't overflow. */
  .user-menu { max-width: calc(100vw - 24px); }
}

/* Phone landscape: collapse video aspect so chat still gets space. */
@media (max-width: 900px) and (orientation: landscape) and (max-height: 500px) {
  #videowrap { position: static !important; }
  #player-wrap { max-height: 60vh; }
}

/* ─────────────────────────────────────────────────────────────────────────
   Secret word (mining) & Dirt word (haunting)
   ───────────────────────────────────────────────────────────────────────── */

/* Primary: first speaker mined the secret word. Gold grow + glow, then fade. */
.secret-mined {
  display: inline-block;
  animation: secret-mine 2.2s ease-out forwards;
  color: #ffd76b;
  font-weight: 700;
  transform-origin: center;
}
@keyframes secret-mine {
  0%   { transform: scale(1);    color: inherit;   text-shadow: none; }
  15%  { transform: scale(1.45); color: #fff3b0;   text-shadow: 0 0 12px #ffd76b, 0 0 24px #c8a84b; }
  40%  { transform: scale(1.35); color: #ffd76b;   text-shadow: 0 0 10px #ffd76b, 0 0 18px #c8a84b; }
  75%  { transform: scale(1.1);  color: #c8a84b;   text-shadow: 0 0 4px  #c8a84b; }
  100% { transform: scale(1);    color: inherit;   text-shadow: none; }
}

/* Echo: others within the 10s window. Smaller bump, cooler glow. */
.secret-mined-echo {
  display: inline-block;
  animation: secret-mine-echo 1.4s ease-out forwards;
  transform-origin: center;
}
@keyframes secret-mine-echo {
  0%   { transform: scale(1);    color: inherit;   text-shadow: none; }
  20%  { transform: scale(1.18); color: #e8c870;   text-shadow: 0 0 6px #c8a84b; }
  100% { transform: scale(1);    color: inherit;   text-shadow: none; }
}

/* Dirt word: the censored blocks. Red-tinged so readers feel the hex. */
.dirt-censored {
  color: #8a3030;
  letter-spacing: -1px;
  font-family: monospace;
}

/* Whole-message haunt: primary dirt trigger. Shake + desaturate + flicker. */
.dirt-haunted .chat-text {
  animation: dirt-haunt 1.8s ease-in-out;
  filter: saturate(0.3) contrast(0.85);
  color: #9a8888;
  text-shadow: 0 0 2px rgba(120,20,20,0.5);
}
.dirt-haunted {
  background: rgba(60,10,10,0.18);
  border-left: 2px solid #5a1a1a;
  padding-left: 4px;
}

/* Echo haunt: someone else said the dirt word within the 20s window. Lighter. */
.dirt-haunted-echo .chat-text {
  filter: saturate(0.55) contrast(0.92);
  color: #a89a9a;
}
.dirt-haunted-echo {
  background: rgba(60,10,10,0.08);
  border-left: 1px solid #4a2a2a;
  padding-left: 4px;
}

/* Lingering haunt: the primary triggerer's next 30s of messages. Very subtle. */
.dirt-haunted-lingering .chat-text {
  filter: saturate(0.75);
  color: #b0a5a5;
}

@keyframes dirt-haunt {
  0%   { transform: translateX(0); }
  10%  { transform: translateX(-2px) skewX(-0.5deg); }
  20%  { transform: translateX(2px)  skewX(0.5deg); }
  30%  { transform: translateX(-1px); }
  40%  { transform: translateX(1px); }
  50%  { transform: translateX(0); opacity: 0.6; }
  60%  { opacity: 1; }
  70%  { opacity: 0.7; }
  100% { transform: translateX(0); opacity: 1; }
}

@keyframes screen-shake {
  0%   { transform: translate(0, 0); }
  10%  { transform: translate(-4px, -2px); }
  20%  { transform: translate(4px, 2px); }
  30%  { transform: translate(-3px, 1px); }
  40%  { transform: translate(3px, -1px); }
  50%  { transform: translate(-2px, 2px); }
  60%  { transform: translate(2px, -2px); }
  70%  { transform: translate(-1px, 1px); }
  80%  { transform: translate(1px, -1px); }
  100% { transform: translate(0, 0); }
}
.screen-shaking { animation: screen-shake 0.5s ease-out; }

/* ═══════════════════════════════════════
   PM windows
   ═══════════════════════════════════════ */
#pm-stack {
  position: fixed; bottom: 0; right: 12px;
  display: flex; flex-direction: row-reverse; gap: 6px;
  z-index: 900; pointer-events: none;
}
.pm-window {
  width: 260px; background: var(--bg-panel); border: 1px solid var(--border-light);
  border-bottom: none; border-radius: 2px 2px 0 0;
  display: flex; flex-direction: column;
  pointer-events: auto;
}
.pm-window.minimized .pm-body,
.pm-window.minimized .pm-input { display: none; }
.pm-header {
  display: flex; align-items: center; justify-content: space-between;
  background: var(--bg-input); padding: 5px 8px; cursor: pointer;
  font-size: 12px; font-weight: 500;
}
.pm-header-title { color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.pm-header-title.has-unread { color: var(--gold); }
.pm-header-close { color: var(--text-dim); cursor: pointer; margin-left: 6px; padding: 0 4px; }
.pm-header-close:hover { color: var(--red); }
.pm-body {
  height: 220px; overflow-y: auto; padding: 6px 8px;
  font-size: 12px; line-height: 1.35; background: var(--bg-chat);
}
.pm-msg { margin-bottom: 3px; word-wrap: break-word; }
.pm-msg-from { color: var(--accent); font-weight: 500; margin-right: 4px; }
.pm-msg-from.is-me { color: var(--gold); }
.pm-msg-time { color: var(--text-dim); font-size: 10px; margin-right: 4px; }
.pm-input {
  border: none; border-top: 1px solid var(--border);
  background: var(--bg-input); color: var(--text);
  padding: 6px 8px; font-size: 12px; border-radius: 0;
}
.pm-input:focus { outline: 1px solid var(--accent); outline-offset: -1px; }

/* ═══════════════════════════════════════
   ADMIN MODAL (iframe of /admin)
   ═══════════════════════════════════════ */
.admin-modal { position: fixed; inset: 0; z-index: 2100; }
.admin-modal.hidden { display: none !important; }
.admin-modal-backdrop {
  position: absolute; inset: 0;
  background: rgba(0,0,0,0.65); backdrop-filter: blur(2px);
}
.admin-modal-panel {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: calc(100vw - 60px); height: calc(100vh - 60px);
  max-width: 1400px;
  background: var(--bg-panel); border: 1px solid var(--border-light);
  border-radius: 3px; display: flex; flex-direction: column;
  overflow: hidden;
}
.admin-modal-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 14px; background: var(--bg); border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.admin-modal-title { font-weight: 600; font-size: 14px; }
.admin-modal-close {
  background: none; border: none; color: var(--text-dim);
  font-size: 22px; cursor: pointer; padding: 0 6px; line-height: 1;
}
.admin-modal-close:hover { color: var(--text); }
.admin-modal-iframe {
  flex: 1; border: 0; width: 100%; background: var(--bg);
}

/* ═══════════════════════════════════════
   HAT PLACER MODAL
   ═══════════════════════════════════════ */
.hat-modal { position: fixed; inset: 0; z-index: 2000; }
.hat-modal.hidden { display: none !important; }
.hat-modal-backdrop {
  position: absolute; inset: 0;
  background: rgba(0,0,0,0.6); backdrop-filter: blur(2px);
}
.hat-modal-panel {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 520px; max-width: calc(100vw - 40px); max-height: calc(100vh - 60px);
  background: var(--bg-panel); border: 1px solid var(--border-light);
  border-radius: 2px; display: flex; flex-direction: column;
  overflow: hidden;
}
.hat-modal-close {
  background: none; border: none; color: var(--text-dim);
  font-size: 20px; line-height: 1; padding: 0 4px; cursor: pointer;
}
.hat-modal-close:hover { color: var(--red); background: none; }

.hat-modal-stage {
  position: relative; height: 130px;
  background: var(--bg-chat);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  border-bottom: 1px solid var(--border);
}
.hat-stage-hint {
  position: absolute; top: 8px; left: 0; right: 0;
  text-align: center; font-size: 11px; color: var(--text-dim);
  pointer-events: none; user-select: none;
}
#hat-stage-inner {
  position: relative; display: inline-block;
  user-select: none;
}
#hat-preview-name {
  font-size: 28px; font-weight: 600; color: var(--text);
  position: relative; z-index: 1;
}
#hat-preview-img {
  position: absolute; left: 50%; top: 0;
  transform: translate(-50%, 0);
  cursor: grab; z-index: 2; user-select: none;
}
#hat-preview-img:active { cursor: grabbing; }
.hat-empty-msg {
  position: absolute; left: 50%; top: -40px; transform: translateX(-50%);
  font-size: 12px; color: var(--text-dim); white-space: nowrap;
  pointer-events: none;
}

.hat-modal-actions {
  display: flex; align-items: center; gap: 8px;
  padding: 12px 14px; background: var(--bg-panel);
}
.hat-modal-actions button,
.hat-change-btn {
  padding: 8px 16px; font-size: 13px; border-radius: 2px;
  border: 1px solid var(--border-light); background: var(--bg-input);
  color: var(--text); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.hat-change-btn:hover,
.hat-modal-actions button:hover { background: #000; }
.hat-spacer { flex: 1; }
.hat-size-row {
  display: flex; align-items: center; gap: 6px; font-size: 12px;
  color: var(--text-dim);
}
.hat-size-row input[type=range] { width: 90px; }

.hat-fine-panel {
  padding: 10px 14px 14px; background: var(--bg-panel);
  border-top: 1px solid var(--border);
}
.hat-fine-row {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 6px; font-size: 12px;
}
.hat-fine-row label { width: 60px; color: var(--text-dim); }
.hat-fine-row input[type=range] { flex: 1; }
.hat-fine-row span {
  width: 36px; text-align: right; color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

/* Bottom-bar Hat button */
#btn-hat .ch-icon { width: 20px; height: 20px; }

/* Playlist lock button — active "locked" state */
#btn-pl-lock.locked { color: var(--red); border-color: var(--red); }
#btn-pl-lock.locked .ch-icon { color: var(--red); }

/* Pinned chat message — compact by default, expands on hover so it doesn't
   eat screen space. Collapsed: a single low-key line (📌 + truncated text).
   Hover: full panel, full wrapped text, "pinned by", and the mod clear button. */
#pinnedrow { margin-bottom: 6px; }
#pinnedwrap {
  display: flex; align-items: center; gap: 8px;
  background: transparent; border: 1px solid transparent;
  border-radius: 2px; padding: 2px 6px;
  position: relative; opacity: 0.7;
  transition: opacity 0.15s, background 0.15s, border-color 0.15s, padding 0.15s;
}
#pinnedwrap:hover {
  opacity: 1;
  background: var(--bg-panel); border-color: var(--accent);
  padding: 8px 12px;
}
.pinned-icon { font-size: 13px; flex-shrink: 0; }
.pinned-body { flex: 1; min-width: 0; }
#pinned-text {
  font-size: 12px; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
#pinnedwrap:hover #pinned-text { white-space: normal; overflow: visible; word-wrap: break-word; }
#pinned-by {
  display: none; color: var(--text-muted); font-size: 10px; margin-top: 2px;
}
#pinnedwrap:hover #pinned-by { display: block; }
/* Clear button only surfaces on hover (and only for mods, who get .hidden removed). */
#pinnedwrap #pinned-clear { opacity: 0; transition: opacity 0.15s; }
#pinnedwrap:hover #pinned-clear:not(.hidden) { opacity: 1; }

/* Rating buttons in now-playing header */
#currentby { color: var(--text-dim); font-size: 11px; margin-left: 8px; }
#currentrating { margin-left: 8px; display: inline-flex; gap: 4px; }
#currentrating.hidden { display: none !important; }
.rate-btn {
  background: var(--bg-input); border: 1px solid var(--border-light);
  color: var(--text-dim); padding: 2px 8px; border-radius: 2px;
  font-size: 11px; cursor: pointer;
  display: inline-flex; align-items: center; gap: 4px;
  font-variant-numeric: tabular-nums;
}
.rate-btn:hover { background: #000; color: var(--text); }
#btn-rate-up:hover { color: var(--green); border-color: var(--green); }
#btn-rate-down:hover { color: var(--red); border-color: var(--red); }

/* Tip system message */
.tip-msg .chat-user { color: var(--gold) !important; }

/* Video history */
#history-panel { background: var(--bg-panel); padding: 12px; }
#history-search {
  width: 100%; background: var(--bg-input); border: 1px solid var(--border-light);
  color: var(--text); padding: 6px 10px; border-radius: 2px;
  font-size: 13px; margin-bottom: 10px;
}
#history-list { display: flex; flex-direction: column; gap: 4px; }
.history-row {
  display: grid; grid-template-columns: 1fr auto;
  gap: 6px 10px; align-items: center;
  padding: 6px 10px;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: 2px;
}
.history-row:hover { background: var(--bg-card); }
.history-row .h-title { font-size: 12px; font-weight: 500; grid-column: 1; }
.history-row .h-meta { font-size: 10px; color: var(--text-dim); grid-column: 1; grid-row: 2; }
.history-row .h-requeue {
  grid-column: 2; grid-row: 1 / span 2;
  padding: 4px 10px; font-size: 11px; border-radius: 2px;
  background: var(--bg-input); border: 1px solid var(--accent);
  color: var(--accent); cursor: pointer;
}
.history-row .h-requeue:hover { background: var(--accent); color: #fff; }

/* Chat links + link previews */
.chat-link { color: var(--cta); text-decoration: underline; word-break: break-all; }
.chat-link:hover { color: var(--info); }

/* Image link collapsed to a frog icon; hover for preview */
.chat-img-link {
  display: inline-block; text-decoration: none; cursor: pointer;
  padding: 0 2px; border-radius: 2px;
  transition: background 0.1s;
}
.chat-img-link:hover { background: rgba(92, 162, 185, 0.2); }

/* Dropping an image into chat */
#chatwrap.chat-dragover { outline: 2px dashed var(--accent); outline-offset: -4px; }

/* Floating preview that follows the hovered frog */
.img-hover-preview {
  position: fixed; z-index: 2100;
  max-width: 280px; max-height: 280px;
  background: var(--bg); border: 2px solid var(--accent);
  border-radius: 3px; padding: 4px;
  box-shadow: 0 6px 24px rgba(0,0,0,0.6);
  pointer-events: none;
}
.img-hover-preview.hidden { display: none; }
.img-hover-preview img {
  max-width: 272px; max-height: 272px; display: block;
  border-radius: 2px;
}
.chat-link-preview {
  display: flex; gap: 8px; margin-top: 4px; margin-bottom: 4px;
  background: var(--bg-panel); border-left: 3px solid var(--accent);
  border-radius: 2px; padding: 6px 8px;
  text-decoration: none; color: var(--text);
  max-width: 420px; overflow: hidden;
  transition: background 0.12s;
}
.chat-link-preview:hover { background: var(--bg-card); }
.chat-link-preview .lp-img {
  width: 80px; height: 80px; object-fit: cover; border-radius: 2px; flex-shrink: 0;
}
.chat-link-preview .lp-body { flex: 1; min-width: 0; }
.chat-link-preview .lp-site {
  font-size: 10px; color: var(--text-muted);
}
.chat-link-preview .lp-title {
  font-size: 12px; font-weight: 600; color: var(--text);
  margin-top: 2px; overflow: hidden; text-overflow: ellipsis;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.chat-link-preview .lp-desc {
  font-size: 11px; color: var(--text-dim);
  margin-top: 3px; overflow: hidden;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}

/* Bottom-bar panels shown as centered modals instead of expanding the page.
   These IDs are opened by clicking bottom-row buttons; they used to sit inline
   below the controls, forcing users to scroll. Now they float as modals. */
#bottom-modal-backdrop {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.55); backdrop-filter: blur(2px);
  z-index: 1900;
}
#bottom-modal-backdrop.hidden { display: none !important; }

/* Centering applies whenever the panel is visible (not .hidden), not only when
 * body.bottom-modal-open is set. Previously the MutationObserver that toggles
 * the body class ran one microtask after the panel un-hid, so the first paint
 * showed the panel at its default bottom-right anchor before jumping to center.
 * Gating on :not(.hidden) removes the race. */
#store-panel:not(.hidden),
#chat-opts-panel:not(.hidden),
#emote-picker:not(.hidden),
#job-panel:not(.hidden),
#leaderboard-panel:not(.hidden),
#charsheet-panel:not(.hidden),
#poll-modal:not(.hidden),
#feedback-modal:not(.hidden),
#trade-dialog:not(.hidden),
#history-panel:not(.hidden) {
  position: fixed;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1950;
  width: 560px; max-width: calc(100vw - 40px);
  max-height: calc(100vh - 80px);
  margin: 0;
  overflow-y: auto;
  box-shadow: 0 12px 40px rgba(0,0,0,0.7);
  border: 1px solid var(--border-light);
  background: var(--bg-panel);
  padding: 16px;
}
#emote-picker:not(.hidden) { width: 640px; max-height: calc(100vh - 80px); }
#charsheet-panel:not(.hidden) { width: 680px; }
#leaderboard-panel:not(.hidden) { width: 480px; }
#poll-modal:not(.hidden) { width: 440px; }
#trade-dialog:not(.hidden) { width: 400px; }
#feedback-modal:not(.hidden) { width: 460px; }

/* Store chrome stays fixed while the catalog itself scrolls. Keeping the title
   and close control in view makes the tall store read as a contained surface
   instead of a page that happens to overflow. */
#store-panel:not(.hidden) {
  display: flex;
  flex-direction: column;
  padding: 0;
  overflow: hidden;
}
#store-panel > .cool-modal-header {
  flex: 0 0 auto;
  padding-right: 52px;
  background: var(--bg-panel);
}
#store-panel > .cool-modal-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
}

/* Close button inserted by JS at top-right of each modal-mode panel. */
.bottom-modal-close {
  position: absolute; top: 6px; right: 8px;
  background: none; border: none; color: var(--text-dim);
  font-size: 22px; line-height: 1; padding: 2px 8px; cursor: pointer;
  z-index: 2;
}
.bottom-modal-close:hover { color: var(--text); }

@media (max-width: 768px) {
  #store-panel:not(.hidden),
  #chat-opts-panel:not(.hidden),
  #emote-picker:not(.hidden),
  #job-panel:not(.hidden),
  #leaderboard-panel:not(.hidden),
  #charsheet-panel:not(.hidden),
  #poll-modal:not(.hidden),
  #history-panel:not(.hidden) {
    width: 100vw; max-width: none; height: 100vh; max-height: none;
    top: 0; left: 0; transform: none; border-radius: 0;
  }
  #store-panel:not(.hidden) { height: 100dvh; }
}

@media (max-width: 640px) {
  /* Store: two readable columns, real touch targets, and a permanently
     reachable close control while the catalog scrolls underneath. */
  #store-panel > .cool-modal-body {
    padding: 10px 10px max(24px, env(safe-area-inset-bottom));
  }
  #store-panel .bottom-modal-close {
    top: 0; right: 0; width: 44px; height: 44px; padding: 0;
  }
  #store-tabs { padding: 8px 0 0; gap: 6px; }
  #store-tabs .store-tab {
    min-width: 44px; min-height: 44px; padding: 8px 12px; font-size: 13px !important;
  }
  #store-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px; padding: 10px 0;
  }
  .store-item { padding: 10px 8px 8px; gap: 5px; }
  .store-item-name { font-size: 13px; }
  .store-item-desc { font-size: 12px; line-height: 1.35; }
  .store-item-price { font-size: 12px; }
  .store-buy-btn, .store-sell-btn, .store-use-btn, .store-equip-btn,
  .recipe-craft-btn, #store-panel .cool-section button {
    min-height: 44px; padding: 8px 10px; font-size: 13px !important;
  }
}

/* Queue-item rating chip */
.q-rating {
  font-size: 10px; color: var(--text-dim);
  background: var(--bg-input); border: 1px solid var(--border);
  padding: 1px 6px; border-radius: 2px;
  font-variant-numeric: tabular-nums;
  margin-right: 4px;
  flex-shrink: 0;
}

/* Leader seek bar (below the video, visible to mods+/leader) */
#leader-seekbar {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 10px; background: var(--bg-panel);
  border-top: 1px solid var(--border);
  font-size: 11px; color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
#leader-seekbar.hidden { display: none; }
#leader-seek-track {
  position: relative; flex: 1; height: 6px;
  background: var(--bg-input); border-radius: 3px;
  cursor: pointer;
}
#leader-seek-fill {
  position: absolute; top: 0; left: 0; height: 100%;
  background: var(--accent); border-radius: 3px;
  pointer-events: none;
}
#leader-seek-handle {
  position: absolute; top: 50%; width: 12px; height: 12px;
  margin-left: -6px; transform: translateY(-50%);
  background: var(--accent); border-radius: 50%;
  pointer-events: none;
  box-shadow: 0 0 0 2px var(--bg-panel);
}
#leader-seek-track:hover #leader-seek-handle { width: 14px; height: 14px; margin-left: -7px; }

/* ═══════════════════════════════════════════════════════════════════════════
   BATTLE.NET / WARCRAFT 2 THEME  (opt-in, per-user via Options → Theme)
   Palette + chrome ported from the Hoff WC2 dashboard (E:\Hoff/dashboard/v2):
   bg #0a0804, panels #2a2218/#1e1a10, parchment text #d0c090, gold #f0c850,
   beveled stone borders #6a5a3a/#1a1208, fonts Almendra (body) + Cinzel (title).
   Only active under html[data-theme="battlenet"] — default UI is untouched.
   ═══════════════════════════════════════════════════════════════════════════ */
/* Fonts for this theme are loaded by the single @import at the top of this file.
   An @import here would be silently ignored — CSS only honours @import before any
   style rule. */

html[data-theme="battlenet"] {
  --bg: #0a0804;
  --bg-panel: #2a2218;
  --bg-card: #1e1a10;
  --bg-input: #15110a;
  --bg-chat: #100c06;
  --border: #3a3018;
  --border-light: #6a5a3a;
  --text: #e8d8a8;
  --text-dim: #b09860;
  --text-muted: #8a7040;
  --cta: #f0c850;
  --info: #d0a850;
  --accent: #f0c850;
  --green: #68b050;
  --red: #e04848;
  --gold: #f0c850;
  --rank-siteadmin: #e04848;
  --rank-owner: #c08020;
  --rank-admin: #f0c850;
  --rank-mod: #68b050;
  --rank-leader: #e0c070;
  --rank-guest: #8a7040;
  --player-idle-text: #8a7040;
  --player-idle-font: 'Cinzel', 'Almendra', serif;
}

html[data-theme="battlenet"] body {
  font-family: 'Almendra', Georgia, serif;
  letter-spacing: 0.2px;
  /* Subtle dark-stone vignette — one decisive background, not layered gradients. */
  background:
    radial-gradient(ellipse at 50% 0%, #1a140a 0%, #0a0804 70%);
}

/* Cinzel display for titles/headers — the Warcraft "engraved gold" look. */
html[data-theme="battlenet"] .cool-modal-title,
html[data-theme="battlenet"] #navbar .logo,
html[data-theme="battlenet"] .cool-section-label,
html[data-theme="battlenet"] #job-panel-name,
html[data-theme="battlenet"] #charsheet-title {
  font-family: 'Cinzel', 'Almendra', serif;
  color: var(--gold);
  text-shadow: 1px 1px 2px #000;
  letter-spacing: 0.5px;
}

/* Beveled stone panels — raised outer bevel (light top-left, dark bottom-right). */
html[data-theme="battlenet"] #navbar,
html[data-theme="battlenet"] .cool-modal,
html[data-theme="battlenet"] #store-panel:not(.hidden),
html[data-theme="battlenet"] #job-panel,
html[data-theme="battlenet"] #chat-opts-panel:not(.hidden),
html[data-theme="battlenet"] .user-menu,
html[data-theme="battlenet"] #pinnedwrap,
html[data-theme="battlenet"] #motdwrap {
  border: 3px solid;
  border-color: #6a5a3a #1a1208 #1a1208 #6a5a3a;
  box-shadow: inset 0 0 0 1px #3a3018, 1px 1px 3px rgba(0,0,0,0.6);
  background: linear-gradient(180deg, #2a2218 0%, #1e1a10 100%);
}

/* Inputs/buttons get the inset (carved) bevel. */
html[data-theme="battlenet"] input[type="text"],
html[data-theme="battlenet"] input[type="password"],
html[data-theme="battlenet"] input[type="number"],
html[data-theme="battlenet"] textarea,
html[data-theme="battlenet"] select,
html[data-theme="battlenet"] #chatline {
  border: 2px solid;
  border-color: #1a1208 #5a4a2a #5a4a2a #1a1208;
  background: #15110a;
  color: var(--text);
  font-family: 'Almendra', serif;
}

html[data-theme="battlenet"] button,
html[data-theme="battlenet"] .cool-btn {
  border: 2px solid;
  border-color: #8a7038 #2a2008 #2a2008 #8a7038;
  background: linear-gradient(180deg, #4a3c20 0%, #2e2412 100%);
  color: var(--gold);
  font-family: 'Cinzel', serif;
  font-weight: 600;
  text-shadow: 1px 1px 1px #000;
}
html[data-theme="battlenet"] button:hover:not(:disabled),
html[data-theme="battlenet"] .cool-btn:hover:not(:disabled) {
  background: linear-gradient(180deg, #5e4c28 0%, #3a2e16 100%);
  color: #fff0c0;
}

/* Chat reads like aged parchment text; usernames stay gold-leaf. */
html[data-theme="battlenet"] #messagebuffer { background: var(--bg-chat); }
html[data-theme="battlenet"] .chat-user { font-family: 'Cinzel', serif; }

/* Classic Battle.net lobby variant. Keeps v1 intact under data-theme="battlenet";
   this aims at the Warcraft II Battle.net Edition era: slate stone, black panes,
   gold frames, and blue beveled command buttons. */
html[data-theme="battlenet-diablo"] {
  --bg: #151818;
  --bg-panel: #050607;
  --bg-card: #101519;
  --bg-input: #020304;
  --bg-chat: #000000;
  --border: #6d5428;
  --border-light: #c9a64d;
  --text: #f0e2b2;
  --text-dim: #c7b27a;
  --text-muted: #8f805a;
  --cta: #f2d06a;
  --info: #d6b757;
  --accent: #4f6f8c;
  --green: #62ef3c;
  --red: #e33b2f;
  --gold: #f2d06a;
  --rank-siteadmin: #e33b2f;
  --rank-owner: #d6b757;
  --rank-admin: #f2d06a;
  --rank-mod: #62ef3c;
  --rank-leader: #b7d6ff;
  --rank-guest: #8f805a;
  --player-idle-text: #8f805a;
  --player-idle-font: 'Cinzel', 'Almendra', serif;
}

html[data-theme="battlenet-diablo"] body {
  font-family: 'Almendra', Georgia, serif;
  letter-spacing: 0.1px;
  background:
    linear-gradient(180deg, rgba(63, 68, 66, 0.72) 0%, rgba(22, 26, 27, 0.96) 44%, rgba(7, 8, 9, 1) 100%),
    repeating-linear-gradient(135deg, rgba(255,255,255,0.026) 0 1px, rgba(0,0,0,0.02) 1px 5px),
    #151818;
}

html[data-theme="battlenet-diablo"] .cool-modal-title,
html[data-theme="battlenet-diablo"] #navbar .logo,
html[data-theme="battlenet-diablo"] .cool-section-label,
html[data-theme="battlenet-diablo"] #job-panel-name,
html[data-theme="battlenet-diablo"] #charsheet-title {
  font-family: 'Cinzel', 'Almendra', serif;
  color: #f2d06a;
  text-shadow: 1px 1px 0 #000, 0 0 4px rgba(47, 98, 156, 0.65);
  letter-spacing: 0.5px;
}

html[data-theme="battlenet-diablo"] #navbar,
html[data-theme="battlenet-diablo"] .cool-modal,
html[data-theme="battlenet-diablo"] #store-panel:not(.hidden),
html[data-theme="battlenet-diablo"] #job-panel,
html[data-theme="battlenet-diablo"] #chat-opts-panel:not(.hidden),
html[data-theme="battlenet-diablo"] .user-menu,
html[data-theme="battlenet-diablo"] #pinnedwrap,
html[data-theme="battlenet-diablo"] #motdwrap {
  border: 3px solid;
  border-color: #d4b55a #32220e #1a1208 #b58f3d;
  background:
    linear-gradient(180deg, #15191a 0%, #050607 52%, #000000 100%);
  box-shadow:
    inset 0 0 0 1px #d0c08a,
    inset 0 0 0 3px #151515,
    0 0 0 1px #000,
    0 2px 8px rgba(0,0,0,0.75);
}

html[data-theme="battlenet-diablo"] #navbar {
  background: linear-gradient(180deg, #2d3434 0%, #090b0d 100%);
  border-bottom-color: #d4b55a;
}

html[data-theme="battlenet-diablo"] input[type="text"],
html[data-theme="battlenet-diablo"] input[type="password"],
html[data-theme="battlenet-diablo"] input[type="number"],
html[data-theme="battlenet-diablo"] textarea,
html[data-theme="battlenet-diablo"] select,
html[data-theme="battlenet-diablo"] #chatline {
  border: 2px solid;
  border-color: #221608 #c9a64d #c9a64d #221608;
  background: #000000;
  color: #f0e2b2;
  font-family: 'Almendra', Georgia, serif;
  box-shadow: inset 0 0 0 1px #31220f, inset 0 1px 5px rgba(0,0,0,0.9);
}

html[data-theme="battlenet-diablo"] button,
html[data-theme="battlenet-diablo"] .cool-btn {
  border: 2px solid;
  border-color: #f0e4a0 #141414 #080808 #b58f3d;
  background:
    linear-gradient(180deg, rgba(255,255,255,0.12), rgba(0,0,0,0.18)),
    linear-gradient(180deg, #6f735e 0%, #3b3d33 48%, #181a16 100%);
  color: #ffffff;
  font-family: 'Cinzel', 'Almendra', serif;
  font-weight: 700;
  text-shadow: 1px 1px 0 #000;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.15), 0 0 0 1px #000;
}

html[data-theme="battlenet-diablo"] button:hover:not(:disabled),
html[data-theme="battlenet-diablo"] .cool-btn:hover:not(:disabled) {
  background:
    linear-gradient(180deg, rgba(90,124,168,0.42), rgba(0,0,0,0.1)),
    linear-gradient(180deg, #7e846c 0%, #484c3d 50%, #20231b 100%);
  color: #fff6c8;
  border-color: #fff0a0 #1b1b1b #090909 #d4b55a;
}

html[data-theme="battlenet-diablo"] #messagebuffer {
  background:
    linear-gradient(180deg, rgba(0, 0, 0, 0.98), rgba(2, 3, 4, 0.98));
  border-color: #d0c08a;
  box-shadow:
    inset 0 0 0 1px #000,
    inset 0 0 0 3px #2b2b2b,
    inset 0 0 0 4px #d0c08a,
    inset 0 0 12px rgba(0,0,0,0.9);
}

html[data-theme="battlenet-diablo"] .chat-user {
  font-family: 'Cinzel', 'Almendra', serif;
  color: #f2d06a;
  text-shadow: 1px 1px 0 #000;
}

html[data-theme="battlenet-diablo"] #job-bar,
html[data-theme="battlenet-diablo"] #job-actions,
html[data-theme="battlenet-diablo"] #event-feed,
html[data-theme="battlenet-diablo"] #playlistrow,
html[data-theme="battlenet-diablo"] #videowrap,
html[data-theme="battlenet-diablo"] #chatwrap {
  border-color: #6d5428;
}

html[data-theme="battlenet-diablo"] #chatwrap,
html[data-theme="battlenet-diablo"] #videowrap,
html[data-theme="battlenet-diablo"] #playlistrow {
  background:
    linear-gradient(180deg, rgba(24, 30, 32, 0.82), rgba(5, 6, 7, 0.96));
  box-shadow:
    inset 0 0 0 1px #d0c08a,
    inset 0 0 0 3px #151515,
    0 0 0 1px #000;
}

html[data-theme="battlenet-diablo"] a {
  color: #f2d06a;
}

html[data-theme="battlenet-diablo"] .server-whisper {
  color: #62ef3c;
}

html[data-theme="battlenet-diablo"] #event-feed,
html[data-theme="battlenet-diablo"] #userlist,
html[data-theme="battlenet-diablo"] #playlist {
  background: #000;
}

/* Diablo II era Battle.net variant: thorned metal, black glass panes,
   parchment form fields, and muted stone command buttons. */
html[data-theme="battlenet-d2"] {
  --bg: #090a08;
  --bg-panel: #040403;
  --bg-card: #0c0b08;
  --bg-input: #11100b;
  --bg-chat: #000000;
  --border: #4d4630;
  --border-light: #b5a06a;
  --text: #e6ddbd;
  --text-dim: #c8bc91;
  --text-muted: #8f8360;
  --cta: #d7c27a;
  --info: #b7a45e;
  --accent: #7f6bba;
  --green: #3fe232;
  --red: #c94536;
  --gold: #d7c27a;
  --rank-siteadmin: #c94536;
  --rank-owner: #d7c27a;
  --rank-admin: #b7a45e;
  --rank-mod: #3fe232;
  --rank-leader: #b9a9ff;
  --rank-guest: #8f8360;
  --player-idle-text: #8f8360;
  --player-idle-font: 'Cinzel', 'Almendra', serif;
}

html[data-theme="battlenet-d2"] body {
  font-family: 'Almendra', Georgia, serif;
  background:
    radial-gradient(circle at 50% 0%, rgba(93, 86, 61, 0.24), transparent 34rem),
    repeating-linear-gradient(122deg, rgba(255,255,255,0.035) 0 1px, transparent 1px 14px),
    repeating-linear-gradient(58deg, rgba(0,0,0,0.35) 0 2px, transparent 2px 18px),
    linear-gradient(180deg, #171714 0%, #070706 52%, #010101 100%);
  color: var(--text);
}

html[data-theme="battlenet-d2"] .cool-modal-title,
html[data-theme="battlenet-d2"] #navbar .logo,
html[data-theme="battlenet-d2"] .cool-section-label,
html[data-theme="battlenet-d2"] #job-panel-name,
html[data-theme="battlenet-d2"] #charsheet-title {
  font-family: 'Cinzel', 'Almendra', serif;
  color: #d7c27a;
  text-shadow: 1px 1px 0 #000, 0 0 5px rgba(127, 107, 186, 0.48);
  letter-spacing: 0.8px;
}

html[data-theme="battlenet-d2"] #navbar,
html[data-theme="battlenet-d2"] .cool-modal,
html[data-theme="battlenet-d2"] #store-panel:not(.hidden),
html[data-theme="battlenet-d2"] #job-panel,
html[data-theme="battlenet-d2"] #chat-opts-panel:not(.hidden),
html[data-theme="battlenet-d2"] .user-menu,
html[data-theme="battlenet-d2"] #pinnedwrap,
html[data-theme="battlenet-d2"] #motdwrap {
  border: 3px solid;
  border-color: #8d805a #17140e #070604 #afa06e;
  background:
    linear-gradient(90deg, rgba(127, 107, 186, 0.12), transparent 12%, transparent 88%, rgba(127, 107, 186, 0.1)),
    linear-gradient(180deg, #181711 0%, #050504 54%, #010101 100%);
  box-shadow:
    inset 0 0 0 1px #c5b57b,
    inset 0 0 0 3px #11100d,
    inset 0 0 20px rgba(0,0,0,0.88),
    0 0 0 1px #000,
    0 2px 12px rgba(0,0,0,0.82);
}

html[data-theme="battlenet-d2"] #navbar {
  background:
    linear-gradient(90deg, rgba(127,107,186,0.25), transparent 22%, transparent 78%, rgba(127,107,186,0.18)),
    linear-gradient(180deg, #28251a 0%, #070706 100%);
  border-bottom-color: #b5a06a;
}

html[data-theme="battlenet-d2"] input[type="text"],
html[data-theme="battlenet-d2"] input[type="password"],
html[data-theme="battlenet-d2"] input[type="number"],
html[data-theme="battlenet-d2"] textarea,
html[data-theme="battlenet-d2"] select,
html[data-theme="battlenet-d2"] #chatline {
  border: 2px solid;
  border-color: #221e14 #c7b778 #c7b778 #221e14;
  background:
    linear-gradient(180deg, rgba(255,255,255,0.06), rgba(0,0,0,0.16)),
    #11100b;
  color: #e9dfbd;
  font-family: 'Almendra', Georgia, serif;
  box-shadow: inset 0 0 0 1px #000, inset 0 0 8px rgba(0,0,0,0.8);
}

html[data-theme="battlenet-d2"] button,
html[data-theme="battlenet-d2"] .cool-btn {
  border: 2px solid;
  border-color: #c7b778 #16130d #080705 #8d805a;
  background:
    linear-gradient(180deg, rgba(255,255,255,0.13), rgba(0,0,0,0.24)),
    linear-gradient(180deg, #706b58 0%, #39372e 52%, #14130f 100%);
  color: #fff6d4;
  font-family: 'Cinzel', 'Almendra', serif;
  font-weight: 700;
  text-shadow: 1px 1px 0 #000;
  box-shadow:
    inset 0 0 0 1px rgba(255,255,255,0.12),
    inset 0 0 9px rgba(0,0,0,0.55),
    0 0 0 1px #000;
}

html[data-theme="battlenet-d2"] button:hover:not(:disabled),
html[data-theme="battlenet-d2"] .cool-btn:hover:not(:disabled) {
  background:
    linear-gradient(180deg, rgba(127,107,186,0.22), rgba(0,0,0,0.2)),
    linear-gradient(180deg, #827b65 0%, #474438 54%, #181712 100%);
  color: #ffffff;
  border-color: #eadca8 #1d1911 #0a0906 #b5a06a;
}

html[data-theme="battlenet-d2"] #chatwrap,
html[data-theme="battlenet-d2"] #videowrap,
html[data-theme="battlenet-d2"] #playlistrow {
  background:
    linear-gradient(90deg, rgba(127,107,186,0.1), transparent 10%, transparent 90%, rgba(127,107,186,0.08)),
    linear-gradient(180deg, rgba(16,15,12,0.96), rgba(2,2,2,0.98));
  border-color: #4d4630;
  box-shadow:
    inset 0 0 0 1px #b5a06a,
    inset 0 0 0 3px #0b0a08,
    inset 0 0 20px rgba(0,0,0,0.86),
    0 0 0 1px #000;
}

html[data-theme="battlenet-d2"] #messagebuffer {
  background: #000;
  color: #e6ddbd;
  border-color: #b5a06a;
  box-shadow:
    inset 0 0 0 1px #000,
    inset 0 0 0 3px #26231a,
    inset 0 0 0 4px #958655,
    inset 0 0 18px rgba(0,0,0,0.95);
}

html[data-theme="battlenet-d2"] .chat-user {
  font-family: 'Almendra', Georgia, serif;
  color: #b9a9ff;
  text-shadow: 1px 1px 0 #000;
}

html[data-theme="battlenet-d2"] .server-whisper {
  color: #3fe232;
}

html[data-theme="battlenet-d2"] a {
  color: #d7c27a;
}

html[data-theme="battlenet-d2"] #event-feed,
html[data-theme="battlenet-d2"] #userlist,
html[data-theme="battlenet-d2"] #playlist {
  background: #000;
}

html[data-theme="battlenet-d2"] #job-bar,
html[data-theme="battlenet-d2"] #job-actions,
html[data-theme="battlenet-d2"] #event-feed {
  border-color: #4d4630;
}

/* ═══════════════════════════════════════════════════════════════════════════
   COOLHOLE V2 — "BLACK SPRING"  (opt-in, per-user via Options → Theme)
   A pitch-black watering hole that is secretly a den. Ink + matte oxblood + bone.
   The gold lottery becomes the ONLY metallic/bright thing in the room — a coin
   surfacing from a black well. The day/night band turns oxblood at night (driven
   by the .dn-night class the vampire ARG already toggles) with a 2px waterline.
   Discipline: FLAT over glow (this is NOT battlenet — no carved bevels, no
   text-shadow), sentence-case, ONE saturated mark in the navbar (the play-▶ in
   the wordmark O), gold code byte-untouched. Only active under
   html[data-theme="blackspring"]; default UI is untouched.
   Full spec + rationale: docs/design/DESIGN_VERSIONS.md (V2).
   ═══════════════════════════════════════════════════════════════════════════ */
/* Fonts for this theme are loaded by the single @import at the top of this file. */

html[data-theme="blackspring"] {
  --bg: #0B0A0C;
  --bg-panel: #131015;
  --bg-card: #1A151A;
  --bg-input: #15111A;
  --bg-chat: #0E0C10;
  --border: #241C24;
  --border-light: #3A2E36;
  --text: #EDE6DA;
  --text-dim: #9A8F86;
  --text-muted: #6A6058;
  --cta: #7C1F23;
  --info: #A8454A;
  --accent: #7C1F23;
  --green: #5C9B52;
  --red: #C0392B;
  --gold: #ffd700;
  --rank-siteadmin: #C0392B;
  --rank-owner: #7C1F23;
  --rank-admin: #A8454A;
  --rank-mod: #5C9B52;
  --rank-leader: #C8A84B;
  --rank-guest: #6A6058;
  /* theme-local helpers */
  --bs-slate-moon: #3A4A5E;
  --bs-waterline: rgba(124,31,35,0.55);
  --bs-link-hover: #A8454A;
  --player-idle-text: var(--text-dim);
  --player-idle-font: 'Cormorant Garamond', Georgia, serif;
  --player-idle-size: 16px;
}

/* Flat ink field — NO radial vignette (flat over glow; unlike battlenet). */
html[data-theme="blackspring"] body {
  font-family: 'Atkinson Hyperlegible', 'Segoe UI', Helvetica, Arial, sans-serif;
  background: #0B0A0C;
}

/* Cormorant Garamond display on titles/labels — sentence-case, NO uppercase
   tracking, NO text-shadow (the de-slop fix vs battlenet's engraved gold). */
html[data-theme="blackspring"] .cool-modal-title,
html[data-theme="blackspring"] .cool-section-label,
html[data-theme="blackspring"] #job-panel-name,
html[data-theme="blackspring"] #charsheet-title {
  font-family: 'Cormorant Garamond', Georgia, serif;
  color: var(--text);
  letter-spacing: 0;
  text-transform: none;
  text-shadow: none;
  font-weight: 600;
}

/* Wordmark: swap to the bone-letters + oxblood-▶ SVG. The play-triangle is the
   ONLY saturated mark in the navbar (single-light-source discipline). */
html[data-theme="blackspring"] .logo img {
  content: url('/static/img/brand-blackspring.svg');
}

/* Navbar: wet-stone panel, flat. Nav links bone-dim → bone on hover (NOT
   oxblood — the only oxblood up here is the play-▶). */
html[data-theme="blackspring"] #navbar {
  background: var(--bg-panel);
  border-bottom: 1px solid #000;
}
html[data-theme="blackspring"] #nav-links a {
  color: var(--text-dim);
}
html[data-theme="blackspring"] #nav-links a:hover {
  color: var(--text);
  background: rgba(255,255,255,0.06);
}

/* Day/night band: keep the .dn-* phase hooks EXACTLY (coolhole.js toggles them);
   only re-skin gradients, raise opacity so the spring reads, and add the
   night-only oxblood waterline — the live-game-state signature. */
html[data-theme="blackspring"] #daynight-band {
  opacity: 0.72;
  background: linear-gradient(180deg, #2A1820 0%, var(--bg-panel) 70%); /* dusk: bruise-violet */
}
html[data-theme="blackspring"] #daynight-band.dn-day {
  background: linear-gradient(180deg, #3A4A5E 0%, var(--bg-panel) 75%); /* overcast slate — the den sleeps */
}
html[data-theme="blackspring"] #daynight-band.dn-night {
  background: linear-gradient(180deg, #2A0C10 0%, var(--bg-panel) 68%); /* oxblood-black — the water goes red */
  box-shadow: inset 0 -2px 0 var(--bs-waterline);                       /* the den's waterline */
}
html[data-theme="blackspring"] #daynight-band.dn-moon {
  background: linear-gradient(180deg, #1C1226 0%, var(--bg-panel) 65%); /* violet-black — full-moon window */
}
/* Phase readout (#day-night already prints day/night/moonrise) — hairline mono,
   welds the ARG clock to the brand voice. Sentence-case, no box. */
html[data-theme="blackspring"] #day-night {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.3px;
  color: var(--text-dim);
  text-transform: lowercase;
}

/* Inputs/buttons: flat, 2px radius, no carved bevel. Focus = low-alpha oxblood
   ring, no glow. */
html[data-theme="blackspring"] input[type="text"],
html[data-theme="blackspring"] input[type="password"],
html[data-theme="blackspring"] input[type="number"],
html[data-theme="blackspring"] textarea,
html[data-theme="blackspring"] select,
html[data-theme="blackspring"] #chatline {
  background: var(--bg-input);
  border: 1px solid var(--border-light);
  color: var(--text);
  border-radius: 2px;
}
html[data-theme="blackspring"] #chatline:focus,
html[data-theme="blackspring"] input[type="text"]:focus,
html[data-theme="blackspring"] textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}
html[data-theme="blackspring"] button,
html[data-theme="blackspring"] .cool-btn {
  background: var(--bg-input);
  border: 1px solid var(--border-light);
  color: var(--text);
  border-radius: 2px;
}
html[data-theme="blackspring"] button:hover,
html[data-theme="blackspring"] .cool-btn:hover {
  background: #000;
  border-color: var(--accent);
}

/* Chat: oxblood is allowed in the stream in exactly TWO places — your own name
   and the PM accent. Re-skin the existing blue PM so no competing blue exists. */
html[data-theme="blackspring"] .pm-msg {
  background: rgba(124,31,35,0.07);
  border-left: 2px solid var(--accent);
}
html[data-theme="blackspring"] .pm-msg .chat-user {
  color: var(--info);
}
html[data-theme="blackspring"] .chat-highlight {
  background: rgba(124,31,35,0.12);
  border-left: 2px solid var(--accent);
}

/* Points/CP chip reads as your quiet wallet, not an event (the lottery is the
   event). Drop alpha so it recedes against ink. */
html[data-theme="blackspring"] #points-display {
  border-color: rgba(255,215,0,0.20);
  background: rgba(255,215,0,0.04);
}

/* Idle states: personality lives in the COPY register + the flat ink material,
   not decoration. (Copy is injected by JS; these just hold the type.) */
html[data-theme="blackspring"] #player-placeholder,
html[data-theme="blackspring"] .no-media {
  font-family: 'Cormorant Garamond', Georgia, serif;
  color: var(--text-dim);
  font-size: 16px;
}

/* Gold lottery (.text-lottery / .gold-name / gold-first burst) is intentionally
   NOT overridden here — it stays byte-exact so the shimmer remains the sole
   metallic, only-animated, brightest thing in the room. That's the whole point. */

/* Peace Pot readout — one line in the job panel. Deliberately plain: the number is
   the interface, so no icon, no gradient, no glow. Red only when there is an open
   case, which is the one state that means "act now". */
#peace-line {
  font-size: 11px;
  padding: 3px 0 0 0;
  color: var(--text-secondary, #9aa0a6);
  letter-spacing: 0;
}
#peace-line.peace-hot {
  color: #e5534b;
}

/* ── Work pickaxe ────────────────────────────────────────────────────────────
   The civilian Work button reads its own state: pickaxe raised and lit when the
   swing is ready, flat on the ground and grey while it recharges. A Work button on
   cooldown used to be distinguishable only by a small seconds counter.

   The shine is a state signal, not decoration — it appears ONLY in the ready state
   and is what makes "primed" legible at a glance. */
/* .job-ability-work no longer needs padding for an absolute icon. */

/* Work's glyph is just another .ab-icon now — same 18px, same stroke system. The
   old absolute-positioned 26px rule made it the odd one out in a row of icons. */



/* Work button on the main job bar carries the same pickaxe as the job panel. */
.job-action-work { position: relative; display: inline-flex; align-items: center; gap: 4px; }
.job-action-work .work-pick { width: 24px; height: 24px; }

/* Targeted abilities in the click-a-name menu carry the same art as the job panel.
   This is the tightest path in the game loop — see someone holding contraband,
   click their name, frisk them — so the verbs should be as recognisable here as in
   the panel rather than reading as generic menu text. */
.user-menu-btn--icon { display: flex; align-items: center; gap: 7px; }
.user-menu-btn--icon .ab-icon,
.user-menu-btn--icon .work-pick { width: 17px; height: 17px; flex: 0 0 auto; position: static; margin: 0; }

/* ── Day / night / moonrise ──────────────────────────────────────────────────
   The room already runs a real cycle: minutes 0-29 day, 30-49 night, 50-59
   moonrise. It drives mechanics — a werewolf can only maul at moonrise, a vampire
   burns in daylight, a werewolf's chat garbles during a blood moon — but the only
   visual trace was a 50px gradient strip behind the navbar at half opacity.

   These shifts are small on purpose. The goal is a room that feels different at
   3am than at noon, not a theme switch; a big colour swing every 20 minutes would
   be a novelty that people turn off. Moonrise is the one that leans: it is the
   hunting window, and it should feel slightly wrong.

   Surfaces fade over 8s so the change is felt rather than watched. */
html[data-phase] body,
html[data-phase] #messagebuffer,
html[data-phase] #chatbody,
html[data-phase] #bottom-bar,
html[data-phase] #job-panel {
  transition: background-color 8s linear, border-color 8s linear;
}

html[data-phase="day"] {
  --bg:        #1A2126;
  --bg-chat:   #1C242A;
  --bg-panel:  #202932;
  --bg-card:   #253039;
}
html[data-phase="night"] {
  --bg:        #171C20;
  --bg-chat:   #171E23;
  --bg-panel:  #1C242C;
  --bg-card:   #212A31;
}
/* Moonrise — a cold violet cast over everything, and the accent goes with it. */
html[data-phase="moon"] {
  --bg:        #191622;
  --bg-chat:   #1B1826;
  --bg-panel:  #221E31;
  --bg-card:   #282338;
  --border:    #332B45;
  --cta:       #8f7ac4;
  --info:      #a898d8;
}

/* Video Slave badge — a VHS cassette at the lower-right of the name, mirroring
   where a hat sits above it. Same stroke language as every other icon. */
.u-vhs {
  display: inline-flex; align-items: center;
  margin-left: 3px; position: relative; top: 2px;
  color: var(--text-muted);
}
.u-vhs svg {
  width: 14px; height: 14px;
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}
#userlist li:hover .u-vhs { color: var(--text-dim); }
