/* Live chat component — shared by the public viewer (public.css)
   and the admin Stream page (admin.css). Styles only the chat
   panel + its contents; the surrounding layout lives in each
   page's own stylesheet. Relies on the CSS variables from base.css. */

/* ── Live chat ──────────────────────────────────────────────────────── */
.chat-panel {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
  height: 100%;            /* fill the grid cell so it matches the
                              video column from top to bottom */
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.chat-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.75rem 0.875rem;
  border-bottom: 1px solid var(--border-soft);
  /* Subtle decorative glow stays blue regardless of the primary
     brand color — keeps the chat surface visually distinct from
     the amber action elements (send button etc.) below. */
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--nav-active-fg) 7%, var(--panel)),
    var(--panel));
}
.chat-title {
  display: inline-flex;
  align-items: center;
  gap: 0.4375rem;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text-strong);
  letter-spacing: 0.02em;
}
.chat-title svg { color: var(--brand); }
.chat-header-actions { display: inline-flex; align-items: center; gap: 0.5rem; }

/* Leave-chat button in the header (shown once joined). */
.chat-leave-btn {
  display: inline-grid;
  place-items: center;
  width: 1.875rem;
  height: 1.875rem;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
.chat-leave-btn:hover { background: var(--panel-2); color: var(--danger); }
.chat-leave-btn[hidden] { display: none !important; }

/* Talk button — viewers use their mic so everyone in the chat hears them.
   Off: ghost. On: green/active. Muted by host: red + disabled (no unmute). */
.chat-talk-btn {
  display: inline-grid;
  place-items: center;
  width: 1.875rem;
  height: 1.875rem;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  position: relative;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.chat-talk-btn:hover { background: var(--panel-2); color: var(--text-strong); }
.chat-talk-btn[hidden] { display: none !important; }
/* Unmuted + audio on → green normal mic: "you can speak". Shows whether or
   not they've actually started their mic; the pulse below marks live talk. */
.chat-talk-btn.is-live {
  color: var(--success, #22c55e);
  background: color-mix(in srgb, var(--success, #22c55e) 14%, transparent);
  border-color: color-mix(in srgb, var(--success, #22c55e) 40%, var(--border));
}
.chat-talk-btn.is-live:hover {
  color: var(--success, #22c55e);
  background: color-mix(in srgb, var(--success, #22c55e) 22%, transparent);
}
/* Pulse the mic while voice activity is actually being transmitted. */
.chat-talk-btn.is-speaking::after {
  content: "";
  position: absolute;
  inset: -0.125rem;
  border-radius: var(--radius-sm);
  border: 0.125rem solid color-mix(in srgb, var(--success, #22c55e) 70%, transparent);
  animation: chat-talk-pulse 1.1s ease-out infinite;
  pointer-events: none;
}
@keyframes chat-talk-pulse {
  0%   { opacity: 0.85; transform: scale(0.95); }
  100% { opacity: 0;    transform: scale(1.15); }
}
/* Red slashed mic = their mic is off (this is also how a host mute looks).
   Always clickable: the viewer can turn their own mic back on at will. Only
   the grey is-disabled state (audio off) blocks them. */
.chat-talk-btn.is-muted {
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  border-color: color-mix(in srgb, var(--danger) 35%, var(--border));
  cursor: pointer;
}
/* Host turned off participant audio for everyone — greyed/slashed, not red,
   since it's a global state rather than this viewer being singled out. */
.chat-talk-btn.is-disabled {
  color: var(--muted);
  background: transparent;
  border-color: transparent;
  opacity: 0.5;
  cursor: not-allowed;
}
.chat-talk-btn:disabled { cursor: not-allowed; }

/* ── Profile editor modal (rename + emoji picker) ─────────────────────── */
.chat-profile-body { display: flex; flex-direction: column; gap: 0.625rem; }
.chat-profile-field { display: grid; gap: 0.375rem; font-size: 0.8125rem; color: var(--muted); }
.chat-profile-field .chat-name-input { width: 100%; }
.chat-profile-emoji-label { font-size: 0.8125rem; font-weight: 600; color: var(--muted); }
.chat-emoji-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(2.5rem, 1fr));
  gap: 0.25rem;
  max-height: 16rem;
  overflow-y: auto;
  padding: 0.375rem;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  background: var(--bg-elev);
  scrollbar-width: thin;
}
.chat-emoji-option {
  aspect-ratio: 1 / 1;
  display: grid;
  place-items: center;
  font-size: 1.25rem;
  line-height: 1;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  cursor: pointer;
}
.chat-emoji-option:hover { background: var(--panel-2); }
.chat-emoji-option.is-selected {
  background: var(--brand-soft);
  border-color: var(--brand);
}
.chat-profile-footer {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  padding: 1rem 1.25rem;
  border-top: 1px solid var(--border-soft);
}
.chat-count {
  display: inline-flex;
  align-items: center;
  padding: 0.125rem 0.5rem;
  border-radius: 999px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  font-size: 0.6875rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

/* ── Empty state ─────────────────────────────────────────────────────
   Fills the message area until the first message lands, so a quiet
   chat never looks broken. chat.js shows exactly one of #chat-messages
   / #chat-empty at a time. */
.chat-empty {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 1.5rem 1.25rem;
  gap: 0.5rem;
}
.chat-empty[hidden] { display: none !important; }
.chat-empty-icon {
  font-size: 2.25rem;
  line-height: 1;
  filter: drop-shadow(0 0.25rem 0.625rem rgba(0,0,0,0.15));
}
.chat-empty-title { font-size: 1rem; font-weight: 600; color: var(--text-strong); }
.chat-empty-sub   { font-size: 0.8125rem; color: var(--muted); line-height: 1.45; }

/* ── Join bar ────────────────────────────────────────────────────────
   Pinned at the bottom until the viewer picks a name. Reading the chat
   above needs no join; sending does. Mirrors the compose row it sits
   in place of. */
.chat-join {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.75rem;
  border-top: 1px solid var(--border-soft);
  background: color-mix(in srgb, var(--panel-2) 70%, var(--panel));
}
.chat-join[hidden] { display: none !important; }
.chat-join-hint {
  font-size: 0.75rem;
  color: var(--muted);
  line-height: 1.35;
}
.chat-join-form {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}
.chat-name-input {
  flex: 1 1 auto;
  min-width: 0;
  height: 2.125rem;
  padding: 0 0.75rem;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--bg-elev);
  color: var(--text);
  font-size: 0.875rem;
}
.chat-name-input:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 25%, transparent);
}
.chat-name-input:disabled { opacity: 0.55; cursor: not-allowed; }
.chat-join-form .btn { flex: 0 0 auto; height: 2.125rem; }
.chat-error {
  font-size: 0.75rem;
  color: var(--danger);
  font-weight: 500;
}

/* ── Message list ──────────────────────────────────────────────────── */
.chat-messages {
  flex: 1 1 auto;
  min-height: 0;
  margin: 0;
  padding: 0.75rem;
  list-style: none;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}
.chat-messages::-webkit-scrollbar { width: 0.375rem; }
.chat-messages::-webkit-scrollbar-track { background: transparent; }
.chat-messages::-webkit-scrollbar-thumb {
  background: var(--border);
  border-radius: 999px;
}
.chat-msg {
  position: relative;
  display: grid;
  grid-template-columns: 2rem 1fr;
  gap: 0.5rem;
  padding: 0.125rem 0.25rem;
  border-radius: var(--radius-sm);
  animation: chat-msg-in 220ms cubic-bezier(.2,.7,.2,1);
}
/* When the current viewer is @-mentioned in a message, the whole
   bubble gets a soft brand tint + a left accent bar so it's instantly
   spottable while scrolling. */
.chat-msg.is-mention {
  background: color-mix(in srgb, var(--brand) 14%, transparent);
  box-shadow: inset 0.1875rem 0 0 var(--brand);
}
@keyframes chat-msg-in {
  from { opacity: 0; transform: translateY(0.25rem); }
  to   { opacity: 1; transform: translateY(0); }
}
.chat-avatar {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 1rem;
  line-height: 1;
  background: var(--panel-2);
  border: 1px solid var(--border);
  flex: 0 0 auto;
  user-select: none;
}
.chat-msg-body {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  min-width: 0;
}
.chat-msg-meta {
  display: flex;
  align-items: baseline;
  gap: 0.375rem;
  min-width: 0;
}
.chat-msg-name {
  font-size: 0.78125rem;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: 0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chat-msg-time {
  font-size: 0.6875rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
}
.chat-msg-text {
  font-size: 0.875rem;
  line-height: 1.4;
  color: var(--text);
  word-wrap: break-word;
  overflow-wrap: anywhere;
}
.chat-msg-text a {
  color: var(--brand);
  text-decoration: underline;
  text-underline-offset: 0.125rem;
}

/* Host removed a message. While removing, the original content slides
   down + fades; then it's replaced by the muted "removed" tombstone,
   which fades in. The bubble keeps its avatar + name so it's clear whose
   message was taken down. */
.chat-msg.is-removing .chat-msg-text,
.chat-msg.is-removing .chat-msg-quote {
  opacity: 0;
  transform: translateY(0.5rem);
  transition: opacity 240ms ease, transform 240ms ease;
  pointer-events: none;
}
.chat-msg-removed {
  color: var(--muted);
  font-style: italic;
  animation: chat-removed-in 220ms ease both;
}
@keyframes chat-removed-in {
  from { opacity: 0; transform: translateY(-0.25rem); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Inline @-mention tokens inside a message body. */
.chat-mention {
  color: var(--brand);
  font-weight: 600;
  border-radius: 0.1875rem;
}
.chat-mention--me {
  color: var(--brand-fg);
  background: var(--brand);
  padding: 0 0.25rem;
  font-weight: 700;
}

/* Quote header inserted above a threaded reply's content — shows
   who/what this is responding to. */
.chat-msg-quote {
  display: flex;
  align-items: baseline;
  gap: 0.3125rem;
  padding: 0.1875rem 0.4375rem;
  margin-bottom: 0.25rem;
  border-left: 0.1875rem solid color-mix(in srgb, var(--muted) 50%, transparent);
  background: color-mix(in srgb, var(--panel-2) 60%, transparent);
  border-radius: 0.25rem;
  font-size: 0.75rem;
  line-height: 1.35;
  color: var(--muted);
  min-width: 0;
  overflow: hidden;
}
.chat-msg-quote-arrow {
  flex: 0 0 auto;
  opacity: 0.6;
}
.chat-msg-quote-name {
  font-weight: 600;
  flex: 0 0 auto;
}
.chat-msg-quote-text {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

/* Hover-only action buttons — sit in the top-right of each bubble. Reply
   is available to anyone joined; Remove + Delete are host moderation. */
/* Reply + moderation now live inside the reaction bar as flat icon circles
   matching the quick-react buttons. */
.chat-msg-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.375rem;
  height: 1.375rem;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 50%;
  color: var(--muted);
  cursor: pointer;
  transition: color 120ms ease, background 120ms ease, transform 80ms ease;
}
.chat-msg-action svg { flex: 0 0 auto; }
.chat-msg-reply:hover { color: var(--text-strong); background: var(--panel-2); transform: scale(1.1); }
.chat-msg-remove:hover,
.chat-msg-delete:hover { color: var(--danger); background: var(--danger-soft); }

/* Thin separator between the reactions and the reply/moderation buttons. */
.chat-react-divider {
  width: 1px;
  align-self: stretch;
  margin: 0.0625rem 0.125rem;
  background: var(--border);
}

/* Quick reaction bar: six emojis + a "…" that opens the full palette,
   grouped in a single rounded pill. Floats just under the message text as
   an overlay (revealed on hover) so it never pushes the chat content down. */
.chat-react-bar {
  position: absolute;
  right: 0.25rem;          /* far right, level with the user name line */
  top: 0.125rem;
  z-index: 3;
  display: none;
  align-items: center;
  gap: 0.1875rem;
  padding: 0.25rem 0.4375rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 999px;
  box-shadow: var(--shadow-md);
}
.chat-msg:hover { z-index: 3; }
.chat-msg:hover .chat-react-bar,
.chat-msg:focus-within .chat-react-bar {
  display: inline-flex;
}
.chat-react-quick {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.375rem;
  height: 1.375rem;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 50%;
  font-size: 0.9375rem;
  line-height: 1;
  cursor: pointer;
  transition: background 120ms ease, transform 80ms ease;
}
.chat-react-quick:hover {
  background: var(--panel-2);
  transform: scale(1.2);
}
.chat-react-more {
  font-size: 1rem;
  font-weight: 700;
  color: var(--muted);
}
.chat-react-more:hover { transform: none; color: var(--text-strong); }

/* Persistent reaction chips beneath a message. */
.chat-reactions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  margin-top: 0.25rem;
}
.chat-reactions[hidden] { display: none !important; }
.chat-reaction {
  display: inline-flex;
  align-items: center;
  gap: 0.1875rem;
  padding: 0.0625rem 0.375rem;
  background: transparent;
  border: 1px solid rgba(128, 128, 128, 0.35);
  border-radius: 999px;
  cursor: pointer;
  line-height: 1.3;
  transition: background 120ms ease, border-color 120ms ease, transform 60ms ease;
}
.chat-reaction:hover { border-color: color-mix(in srgb, var(--brand) 40%, var(--border)); }
.chat-reaction:active { transform: scale(0.95); }
.chat-reaction.is-mine {
  background: transparent;
  border-color: color-mix(in srgb, var(--brand) 55%, transparent);
}
.chat-reaction-emoji { font-size: 0.875rem; line-height: 1; }
.chat-reaction-count {
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.chat-reaction.is-mine .chat-reaction-count { color: var(--brand); }

/* Floating full-emoji palette opened by "…", with live search. */
.chat-emoji-palette {
  position: fixed;
  z-index: 60;
  width: 17rem;
  max-width: calc(100vw - 1rem);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.5rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}
.chat-emoji-palette[hidden] { display: none !important; }
.chat-emoji-palette-search {
  width: 100%;
  padding: 0.375rem 0.5rem;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 0.8125rem;
}
.chat-emoji-palette-search:focus {
  outline: none;
  border-color: color-mix(in srgb, var(--brand) 50%, var(--border));
}
.chat-emoji-palette-grid {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 0.125rem;
  max-height: 13rem;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}
.chat-emoji-palette-item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  font-size: 1.125rem;
  line-height: 1;
  cursor: pointer;
  transition: background 100ms ease, transform 80ms ease;
}
.chat-emoji-palette-item:hover { background: var(--panel-2); transform: scale(1.15); }
.chat-emoji-palette-empty {
  grid-column: 1 / -1;
  padding: 1.25rem 0.5rem;
  text-align: center;
  color: var(--muted);
  font-size: 0.8125rem;
}

/* Reply preview that slides up above the compose row when armed. */
.chat-reply-banner {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4375rem 0.75rem;
  margin: 0 0.5rem;
  border-top: 1px solid var(--border-soft);
  background: color-mix(in srgb, var(--panel-2) 60%, transparent);
  border-left: 0.1875rem solid var(--brand);
  font-size: 0.75rem;
  color: var(--muted);
  min-width: 0;
  animation: chat-reply-slide 160ms ease;
}
.chat-reply-banner[hidden] { display: none !important; }
@keyframes chat-reply-slide {
  from { opacity: 0; transform: translateY(0.5rem); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) { .chat-reply-banner { animation: none; } }

/* Highlight the message currently selected by the /re reply-picker. */
.chat-msg.is-reply-target {
  background: color-mix(in srgb, var(--brand) 16%, transparent);
  outline: 2px solid color-mix(in srgb, var(--brand) 70%, transparent);
  border-radius: var(--radius-sm);
}

/* Key hint shown above the compose box while the /re picker is active. */
.chat-reply-hint {
  display: flex;
  align-items: center;
  gap: 0.3125rem;
  flex-wrap: wrap;
  padding: 0.375rem 0.75rem;
  margin: 0 0.5rem;
  border-top: 1px solid var(--border-soft);
  border-left: 0.1875rem solid var(--brand);
  background: color-mix(in srgb, var(--brand) 10%, transparent);
  font-size: 0.6875rem;
  color: var(--muted);
  animation: chat-reply-slide 160ms ease;
}
.chat-reply-hint[hidden] { display: none !important; }
.chat-reply-hint-keys {
  font-weight: 700;
  color: var(--text);
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 0.25rem;
  padding: 0 0.3125rem;
}
.chat-reply-banner-body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 0.3125rem;
  overflow: hidden;
}
.chat-reply-banner-arrow {
  flex: 0 0 auto;
  color: var(--brand);
  font-weight: 700;
}
.chat-reply-banner-text {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chat-reply-banner-name {
  font-weight: 600;
  color: var(--text);
}
.chat-reply-banner-snippet {
  color: var(--muted);
}
.chat-reply-banner-cancel {
  flex: 0 0 auto;
  width: 1.375rem;
  height: 1.375rem;
  display: grid;
  place-items: center;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--muted);
  cursor: pointer;
}
.chat-reply-banner-cancel:hover {
  background: var(--panel);
  color: var(--text);
}

/* System messages (join / leave / disabled) — no avatar, full-width
   italic gray text. */
.chat-msg--system {
  grid-template-columns: 1fr;
  font-size: 0.75rem;
  color: var(--muted);
  font-style: italic;
  text-align: center;
  padding: 0.125rem 0;
}
.chat-msg--system .chat-msg-text {
  color: inherit;
  font-style: italic;
  font-size: inherit;
}

/* ── Compose ───────────────────────────────────────────────────────── */
.chat-compose {
  position: relative;            /* anchor for the .chat-mention-menu */
  display: grid;
  grid-template-columns: 1.75rem 1fr 2rem;
  gap: 0.4375rem;
  align-items: center;
  padding: 0.625rem 0.75rem;
  border-top: 1px solid var(--border-soft);
  background: color-mix(in srgb, var(--panel-2) 70%, var(--panel));
}

/* @-mention autocomplete dropdown — sits above the compose input. */
.chat-mention-menu {
  position: absolute;
  bottom: calc(100% + 0.375rem);
  left: 0.5rem;
  right: 0.5rem;
  z-index: 10;
  max-height: 14rem;
  overflow-y: auto;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  padding: 0.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.0625rem;
  scrollbar-width: thin;
}
.chat-mention-menu[hidden] { display: none !important; }
.chat-mention-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.375rem 0.4375rem;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--text);
  cursor: pointer;
  text-align: left;
  font-size: 0.875rem;
  min-width: 0;
}
.chat-mention-item.is-active,
.chat-mention-item:hover {
  background: color-mix(in srgb, var(--brand) 14%, var(--panel-2));
}
.chat-mention-item-emoji {
  flex: 0 0 auto;
  width: 1.5rem;
  height: 1.5rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  font-size: 0.875rem;
  line-height: 1;
}
.chat-mention-item-name {
  flex: 1;
  min-width: 0;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chat-mention-item-hint {
  flex: 0 0 auto;
  font-size: 0.6875rem;
  color: var(--muted);
  letter-spacing: 0.04em;
}
.chat-compose-me {
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 0.875rem;
  line-height: 1;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  /* It's a <button> that opens the identity editor. */
  padding: 0;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  transition: box-shadow 0.15s ease, transform 0.1s ease;
}
.chat-compose-me:hover { box-shadow: 0 0 0 2px var(--brand-soft); }
.chat-compose-me:active { transform: scale(0.94); }
.chat-compose-me:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.chat-compose-input {
  min-width: 0;
  height: 2.125rem;
  padding: 0 0.75rem;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--bg-elev);
  color: var(--text);
  font-size: 0.875rem;
}
.chat-compose-input:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 22%, transparent);
}
.chat-compose-input:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}
.chat-send-btn {
  width: 2.125rem;
  height: 2.125rem;
  display: grid;
  place-items: center;
  border-radius: var(--radius-md);
  border: 0;
  background: var(--brand);
  color: var(--brand-fg);
  cursor: pointer;
  transition: background 120ms ease, transform 60ms ease;
}
.chat-send-btn:hover { background: color-mix(in srgb, var(--brand) 88%, white); }
.chat-send-btn:active { transform: scale(0.94); }
.chat-send-btn:disabled { background: var(--muted); opacity: 0.55; cursor: not-allowed; }

/* Disabled / banned banner */
.chat-banner {
  padding: 0.625rem 0.875rem;
  font-size: 0.8125rem;
  font-weight: 500;
  text-align: center;
  color: var(--muted);
  background: var(--panel-2);
  border-top: 1px solid var(--border-soft);
}
.chat-banner.is-banned { color: var(--danger); }

/* ── Video reactions (shared by viewer + broadcaster preview) ─────────── */
.reaction-control { position: relative; display: inline-flex; }
.reaction-control[hidden] { display: none !important; }
.reaction-control.is-open > button { background: var(--panel-2); }

/* Labeled "Reactions" toggle (viewer control bar + broadcaster preview) —
   outlined in brand yellow on a transparent background so it reads as
   available without being distracting. */
.reaction-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  height: 2.25rem;
  padding: 0 0.75rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--brand);
  background: transparent;
  color: var(--brand);
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: background 120ms ease;
}
.reaction-btn:hover { background: color-mix(in srgb, var(--brand) 14%, transparent); }
.reaction-btn-emoji { font-size: 1rem; line-height: 1; }
/* Subtle yellow wash while the palette is open (overrides the panel tint). */
.reaction-control.is-open > .reaction-btn { background: color-mix(in srgb, var(--brand) 18%, transparent); }
.reaction-palette {
  position: absolute;
  bottom: calc(100% + 0.5rem);
  right: 0;
  display: grid;
  grid-template-columns: repeat(7, auto);
  gap: 0.125rem;
  padding: 0.375rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  z-index: 8;
  animation: reaction-palette-in 140ms ease;
}
.reaction-palette[hidden] { display: none !important; }
@keyframes reaction-palette-in {
  from { opacity: 0; transform: translateY(0.375rem) scale(0.96); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.reaction-palette-emoji {
  width: 2.125rem;
  height: 2.125rem;
  display: grid;
  place-items: center;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 50%;
  font-size: 1.375rem;
  line-height: 1;
  cursor: pointer;
  transition: background 120ms ease, transform 100ms ease;
}
.reaction-palette-emoji:hover { background: var(--panel-2); transform: scale(1.25); }
.reaction-palette-emoji:active { transform: scale(1.05); }

.video-reactions {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 4;
}
.reaction-particle {
  position: absolute;
  top: 0;
  line-height: 1;
  user-select: none;
  pointer-events: none;
  will-change: transform, opacity;
  filter: drop-shadow(0 0.0625rem 0.125rem rgba(0, 0, 0, 0.45));
  animation-name: reaction-fall;
  animation-timing-function: cubic-bezier(0.3, 0.1, 0.4, 1);
  animation-fill-mode: both;
}
@keyframes reaction-fall {
  0%   { transform: translateY(-2rem) translateX(0) rotate(0deg); opacity: 0; }
  10%  { opacity: 1; }
  85%  { opacity: 1; }
  100% { transform: translateY(var(--fall, 24rem)) translateX(var(--drift, 0)) rotate(var(--rot, 0deg)); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .reaction-particle { animation-duration: 1.2s !important; }
}

/* "Who reacted" pill — slides down into the top-center of the video on each
   reaction, then slides back up. Independent of the falling particles. */
.reaction-sender {
  position: absolute;
  top: 0.75rem;
  left: 50%;
  z-index: 5;
  display: inline-flex;
  align-items: center;
  gap: 0.4375rem;
  max-width: 80%;
  padding: 0.3125rem 0.75rem 0.3125rem 0.375rem;
  background: rgba(11, 13, 20, 0.8);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 999px;
  color: #fff;
  font-size: 0.8125rem;
  font-weight: 600;
  white-space: nowrap;
  box-shadow: var(--shadow-md);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -160%);
  transition: opacity 200ms ease, transform 260ms cubic-bezier(0.22, 1, 0.36, 1);
}
.reaction-sender.is-visible {
  opacity: 1;
  transform: translate(-50%, 0);
}
.reaction-sender-avatar {
  display: inline-grid;
  place-items: center;
  width: 1.375rem;
  height: 1.375rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.16);
  font-size: 0.9375rem;
  line-height: 1;
  flex: 0 0 auto;
}
.reaction-sender-name {
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 11rem;
}
.reaction-sender-emoji { font-size: 1rem; line-height: 1; }

/* ── Broadcaster alert banner (shared by viewer + broadcaster preview) ── */
.video-alert {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 6;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 0.625rem;
  padding: 0.75rem 1rem;
  background: #ffbc00;
  color: #1b1206;
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.35;
  /* Park fully off-screen (extra 0.5rem) so no sub-pixel sliver of the
     banner is left at the top edge when it's taken down. The shadow only
     shows while visible, so it can't bleed past the top either. */
  transform: translateY(calc(-100% - 0.5rem));
  transition: transform 360ms cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none;
  max-height: 60%;
  overflow-y: auto;
}
.video-alert.is-visible {
  transform: translateY(0);
  box-shadow: 0 0.5rem 1.25rem rgba(0, 0, 0, 0.4);
}
.video-alert-icon { flex: 0 0 auto; }
.video-alert-text { white-space: pre-wrap; overflow-wrap: anywhere; }
@media (prefers-reduced-motion: reduce) {
  .video-alert { transition: none; }
}

/* One-time headphones tip — same drop-down behaviour as the broadcaster
   alert, but an info-blue look and dismissible. Sits above the alert. */
.voice-hint {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 7;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 0.625rem;
  padding: 0.75rem 2.75rem 0.75rem 1rem;   /* extra right padding for the × */
  background: #1d4ed8;
  color: #eaf1ff;
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.35;
  transform: translateY(calc(-100% - 0.5rem));
  transition: transform 360ms cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none;
}
.voice-hint.is-visible {
  transform: translateY(0);
  box-shadow: 0 0.5rem 1.25rem rgba(0, 0, 0, 0.4);
  pointer-events: auto;
}
.voice-hint-icon { flex: 0 0 auto; }
.voice-hint-close {
  position: absolute;
  top: 50%;
  right: 0.5rem;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: inherit;
  cursor: pointer;
  opacity: 0.85;
}
.voice-hint-close:hover { opacity: 1; background: rgba(255, 255, 255, 0.16); }
@media (prefers-reduced-motion: reduce) {
  .voice-hint { transition: none; }
}
