@font-face {
  font-family: 'Rubik';
  src: url('./fonts/Rubik.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
}

@font-face {
  font-family: 'Rubik';
  src: url('./fonts/Rubik-Italic.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: italic;
}

/* Remove blue tap highlight on mobile (Android) */
* {
  -webkit-tap-highlight-color: transparent;
}

.icon {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: white;
  mask-size: contain;
  mask-repeat: no-repeat;
  mask-position: center;
  width: 65%;
  height: 65%;
  margin: auto;
}

.icon-add-user {
  mask-image: url("./icons/add-user.svg");
}

.icon-shield-filled {
  mask-image: url("./icons/shield-filled.svg");
}

.icon-channel-hash {
  mask-image: url("./icons/channel-hash.svg");
}

.icon-users-multi {
  mask-image: url("./icons/users-multi.svg");
}

.icon-battery-full {
  mask-image: url("./icons/battery-full.svg");
}

.icon-warning {
  mask-image: url("./icons/vector_warning.svg");
}

.icon-chats {
  mask-image: url("./icons/chats.svg");
}

.icon-mail {
  mask-image: url("./icons/mail.svg");
}

.icon-crown {
  mask-image: url("./icons/crown.svg");
}

.icon-new-msg {
  mask-image: url("./icons/new-chat.svg");
}

.icon-image {
  mask-image: url("./icons/image.svg");
}

.icon-add-file {
  mask-image: url("./icons/add-file.svg");
}

.icon-attachment {
  mask-image: url("./icons/attachment.svg");
}

.icon-file-search {
  mask-image: url("./icons/file-search.svg");
}
.icon-file-code {
  mask-image: url("./icons/file-code.svg");
}

.icon-file-unknown {
  mask-image: url("./icons/file-unknown.svg");
}

.icon-file {
  mask-image: url("./icons/file.svg");
}

.icon-folder {
  mask-image: url("./icons/folder.svg");
}

.icon-palette {
  mask-image: url("./icons/palette.svg");
}

.icon-globe {
  mask-image: url("./icons/globe.svg");
}

.icon-align-left {
  mask-image: url("./icons/align-left.svg");
}

.icon-skip-back { mask-image: url("./icons/skip-back.svg"); }
.icon-skip-forward { mask-image: url("./icons/skip-forward.svg"); }
.icon-shuffle { mask-image: url("./icons/shuffle.svg"); }
.icon-repeat { mask-image: url("./icons/repeat.svg"); }
.icon-repeat-one { mask-image: url("./icons/repeat-one.svg"); }
.icon-list-music { mask-image: url("./icons/list-music.svg"); }

.icon-grip {
  mask-image: url("./icons/grip.svg");
}

.icon-gamepad {
  mask-image: url("./icons/gamepad.svg");
}

.icon-film {
  mask-image: url("./icons/film.svg");
}

.icon-gift {
  mask-image: url("./icons/gift.svg");
}

.icon-info {
  mask-image: url("./icons/info.svg");
}

.icon-loading {
  mask-image: url("./icons/loading.svg");
}

.icon-refresh {
  mask-image: url("./icons/refresh.svg");
}

.icon-download {
  mask-image: url("./icons/download.svg");
}

.icon-locked {
  mask-image: url("./icons/locked.svg");
}

.icon-mic-on {
  mask-image: url("./icons/mic-on.svg");
}

.icon-mic-off {
  mask-image: url("./icons/mic-off.svg");
}

.icon-eye {
  mask-image: url("./icons/eye.svg");
}

.icon-eye-off {
  mask-image: url("./icons/eye-off.svg");
}

.icon-bulb {
  mask-image: url("./icons/bulb.svg");
}

.icon-video {
  mask-image: url("./icons/video.svg");
}

.icon-smile-face {
  mask-image: url("./icons/smile-face.svg");
}

.icon-wink-face {
  mask-image: url("./icons/wink-face.svg");
}

.icon-user-circle {
  mask-image: url("./icons/user-circle.svg");
}

.icon-plus {
  mask-image: url("./icons/plus.svg");
}

.icon-reply {
  mask-image: url("./icons/reply.svg");
}

.icon-bookmark {
  mask-image: url("./icons/bookmark.svg");
}

.icon-bot {
  mask-image: url("./icons/bot.svg");
}

.icon-chevron-down {
  mask-image: url("./icons/chevron-down.svg");
}

.icon-pin {
  mask-image: url("./icons/pin.svg");
}

.icon-chevron-double-left {
  mask-image: url("./icons/chevron-double-left.svg");
}

.icon-edit-user {
  mask-image: url("./icons/edit-user.svg");
}

.icon-file-plus {
  mask-image: url("./icons/file-plus.svg");
}

.icon-file-minus {
  mask-image: url("./icons/file-minus.svg");
}

.icon-cancel {
  mask-image: url("./icons/cancel.svg");
}

#chat-input-cancel .icon-cancel {
  background-color: var(--danger-color);
}

.icon-x {
  mask-image: url("./icons/x.svg");
}

.icon-edit-x {
  -webkit-mask: url('./icons/edit-x.svg') no-repeat center;
  mask: url('./icons/edit-x.svg') no-repeat center;
  -webkit-mask-size: contain;
  mask-size: contain;
  background-color: var(--danger-color);
  width: 16px;
  height: 16px;
  display: inline-block;
  flex-shrink: 0;
}

.icon-save {
  -webkit-mask: url('./icons/save.svg') no-repeat center;
  mask: url('./icons/save.svg') no-repeat center;
  -webkit-mask-size: contain;
  mask-size: contain;
  background-color: #33DB98;
  width: 18px;
  height: 18px;
  display: inline-block;
  flex-shrink: 0;
}

.icon-chat-circle {
  mask-image: url("./icons/chat-circle.svg");
}

.icon-chat-bubble {
  mask-image: url("./icons/chat-bubble.svg");
  -webkit-mask-image: url("./icons/chat-bubble.svg");
}

.icon-settings {
  mask-image: url("./icons/settings.svg");
}

.icon-volume-mute {
  mask-image: url("./icons/volume-mute.svg");
}

.icon-bell {
  mask-image: url("./icons/bell.svg");
}

.icon-chevron-right {
  mask-image: url("./icons/chevron-right.svg");
}

.icon-chevron-left {
  mask-image: url("./icons/chevron-left.svg");
}

.icon-volume-max {
  mask-image: url("./icons/volume-max.svg");
}

.icon-check-circle {
  mask-image: url("./icons/check-circle.svg");
}

.icon-clock {
  mask-image: url("./icons/clock.svg");
}

.icon-star {
  mask-image: url("./icons/star.svg");
}

.icon-search {
  mask-image: url("./icons/search.svg");
}

.icon-send {
  mask-image: url("./icons/send.svg");
}

.icon-share {
  mask-image: url("./icons/share.svg");
  -webkit-mask-image: url("./icons/share.svg");
  background-color: var(--icon-color-primary);
}

.icon-arrow-up {
  mask-image: url("./icons/arrow-up.svg");
}

.icon-pivx {
  mask-image: none !important;
  -webkit-mask-image: none !important;
  background-color: transparent !important;
  background-image: url("./icons/pivx.svg");
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}

.icon-nexus {
  mask-image: none !important;
  -webkit-mask-image: none !important;
  background-color: transparent !important;
  background-image: url("./icons/nexus.svg");
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}

.icon-message {
  mask-image: url("./icons/message.svg");
}

.icon-grid {
  mask-image: url("./icons/grid.svg");
}

.icon-help {
  mask-image: url("./icons/help.svg");
  -webkit-mask-image: url("./icons/help.svg");
  
}

:root {
  --danger-pink: #FF2EA9;
  --danger-red: #FC595C;
  --danger-color: #FC595C;  /* default: red for all themes */
  --danger-color-hover: color-mix(in srgb, var(--danger-color) 10%, transparent);
  --status-excellent: #59fcb3;
  --status-good: #FCE459;
  --status-fair: #FCA559;
  --status-poor: var(--danger-color);
  --text-color: #f6f6f6;
  font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
  font-size: 16px;
  line-height: 24px;
  font-weight: 400;

  color: var(--text-color);

  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-text-size-adjust: 100%;

  overscroll-behavior: none;
}

html, body {
  height: 100%;
  margin: 0;
}

::selection {
  background-color: rgba(255, 255, 255, 0.225);
  color: #ffffff;
}

::-webkit-scrollbar {
  /* `width` styles vertical scrollbars, `height` styles horizontal ones —
     setting only one leaves the other at the (chunky) browser default. */
  width: 10px;
  height: 10px;
  background-color: transparent;
}

::-webkit-scrollbar-track {
  background-color: transparent;
}

::-webkit-scrollbar-thumb {
  background-color: #474747;
  border-radius: 10px;
  border: 2px solid transparent;
  background-clip: content-box;
}

::-webkit-scrollbar-thumb:hover {
  background-color: rgba(180, 180, 180, 0.8);
}

.container {
  margin: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  text-align: center;
  height: calc(100% - var(--chrome-h));
  min-height: 0;
}

.settings-btn {
  position: absolute;
  height: 50px;
  width: 50px;
  top: 0;
  right: 0;
  padding: 0;
  border: none;
  background-color: transparent;
  opacity: 0.75;
}

.settings-btn:hover {
  opacity: 0.5;
}

.settings-footer span {
    color: #5B6060;
    font-size: 13px;
    cursor: pointer;
    transition: color 0.2s ease;
}

.settings-footer span:hover {
    color: #fff;
}

/* ===========================================================================
   Pack Details modal — opened via vector://emojis/pack/<naddr> deep link
   or from in-app entry points (share-pack copy, sidebar context menu).
   z-index sits above the picker (2000) but below popupConfirm (10002) so
   a confirm raised from within the modal (delete pack, etc.) still
   layers correctly.
   =========================================================================== */
.pack-details-overlay {
  position: fixed;
  inset: 0;
  z-index: 5000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(0, 0, 0, 0.78);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  animation: pack-details-fade 0.18s ease-out;
}
.pack-details-overlay[hidden] { display: none; }

@keyframes pack-details-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.pack-details-card {
  position: relative;
  width: 100%;
  max-width: 440px;
  background: #141414;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 18px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6);
  overflow: hidden;
  animation: pack-details-rise 0.24s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes pack-details-rise {
  from { opacity: 0; transform: translateY(12px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.pack-details-close {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 30px;
  height: 30px;
  /* Override the global `button { padding: 0.6em 1.2em }` so this
     button stays a tight circle instead of an oval. */
  padding: 0;
  box-sizing: border-box;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.08);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
  transition: background-color 0.15s, border-color 0.15s;
}
.pack-details-close:hover {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.18);
}
.emoji-picker .pack-details-close .icon,
.pack-details-close .icon {
  position: static;
  margin: 0;
  width: 14px;
  height: 14px;
  padding: 0;
  border-radius: 0;
  background-color: #fff;
}

.pack-details-body {
  padding: 28px 24px 20px;
}

/* Loading state */
.pack-details-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 48px 20px;
}
.pack-details-spinner {
  width: 28px;
  height: 28px;
  border: 2.5px solid rgba(89, 252, 179, 0.2);
  border-top-color: var(--icon-color-primary);
  border-radius: 50%;
  animation: pack-details-spin 0.9s linear infinite;
}
@keyframes pack-details-spin { to { transform: rotate(360deg); } }
.pack-details-loading-text {
  color: rgba(255, 255, 255, 0.55);
  font-size: 13px;
  margin: 0;
}

/* Pack content */
.pack-details-header {
  display: flex;
  gap: 14px;
  align-items: center;
  margin-bottom: 16px;
}
.pack-details-logo {
  width: 64px;
  height: 64px;
  border-radius: 14px;
  background-color: rgba(255, 255, 255, 0.04);
  flex-shrink: 0;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
.pack-details-logo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.pack-details-logo-fallback {
  font-size: 28px;
  font-weight: 700;
  color: var(--icon-color-primary);
}
.pack-details-title-block {
  flex: 1;
  min-width: 0;
}
.pack-details-title {
  font-size: 18px;
  font-weight: 700;
  color: #fff;
  margin: 0 0 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pack-details-meta {
  color: rgba(255, 255, 255, 0.5);
  font-size: 12px;
}
.pack-details-meta .emoji-count {
  color: var(--icon-color-primary);
  font-weight: 600;
}

.pack-details-desc {
  color: rgba(255, 255, 255, 0.7);
  font-size: 13px;
  line-height: 1.5;
  margin: 0 0 16px;
}

/* Emoji preview grid — mirrors the in-chat pack-preview thumb grid */
.pack-details-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 6px;
  padding: 12px;
  background: rgba(0, 0, 0, 0.35);
  border-radius: 12px;
  margin-bottom: 18px;
  /* Scroll within the modal when the pack has enough emojis to overflow.
     `overscroll-behavior: contain` stops a fingertip scroll from
     bouncing through to the page underneath. */
  max-height: 240px;
  overflow-y: auto;
  overscroll-behavior: contain;
  position: relative;
}
.pack-details-grid::-webkit-scrollbar { width: 6px; }
.pack-details-grid::-webkit-scrollbar-track { background: transparent; }
.pack-details-grid::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.12);
  border-radius: 3px;
}
.pack-details-grid::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, 0.22);
}
.pack-details-thumb {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6px;
  border-radius: 6px;
}
.pack-details-thumb img {
  width: 100%;
  height: 100%;
  /* `contain` (not `fill`) — preserves the emoji's natural aspect
     ratio so square sources stay square and foreign-client non-square
     emojis letterbox cleanly instead of distorting. */
  object-fit: contain;
}
.pack-details-empty {
  grid-column: 1 / -1;
  padding: 28px 12px;
  text-align: center;
  color: rgba(255, 255, 255, 0.4);
  font-size: 13px;
}

/* Action button */
.pack-details-action {
  width: 100%;
  padding: 14px;
  border: none;
  border-radius: 12px;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.04em;
  cursor: pointer;
  background-color: var(--icon-color-primary);
  color: #0a0a0a;
  transition: filter 0.15s ease, transform 0.1s ease;
}
.pack-details-action.is-subscribed {
  background-color: var(--danger-color-hover);
  color: var(--danger-color);
  border: 1px solid color-mix(in srgb, var(--danger-color) 40%, transparent);
}
.pack-details-action:hover { filter: brightness(1.08); }
.pack-details-action:active { transform: translateY(1px); }
.pack-details-action:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  filter: none;
}

/* Error state */
.pack-details-error {
  text-align: center;
  padding: 40px 20px;
}
.pack-details-error-title {
  font-size: 16px;
  font-weight: 700;
  color: var(--danger-color);
  margin: 0 0 8px;
}
.pack-details-error-detail {
  color: rgba(255, 255, 255, 0.55);
  font-size: 13px;
  margin: 0;
}

@media (max-width: 480px) {
  .pack-details-card { max-width: 100%; }
  .pack-details-body { padding: 24px 18px 18px; }
  .pack-details-grid { grid-template-columns: repeat(5, 1fr); }
}

.popup-container {
  z-index: 10002;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  position: fixed;
  top: 0;
  left: 0;
  pointer-events: none;
  background-color: rgba(0, 0, 0, 0);
  transition: background-color 0.2s ease;
}

.popup-container.active {
  pointer-events: auto;
  background-color: rgba(0, 0, 0, 0.85);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

/* Community logo in the join-invite popup: avatar-style circular crop. */
#popupIcon.popup-icon-circular {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  object-fit: cover;
}

.popup {
  position: fixed;
  z-index: 10001;
  /* Too many weird WebKit filter issues - disabling and going solid for now */
  /* backdrop-filter: blur(10px); */
  border-style: solid;
  border-radius: 8px;
  padding: 30px;
  border-width: 1px;
  top: 50%;
  transform: translate(0%, -50%);
  pointer-events: auto;
  /* Cap viewport width so unbreakable content (e.g. the Export Account
     nsec/seed strings) can't push the popup wider than the screen.
     Inner overflow-x: auto containers handle the scroll. */
  max-width: calc(100vw - 20px);
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  margin-left: 10px;
  margin-right: 10px;
}

.popup-content {
  overflow-y: auto;
  max-height: calc(90vh - 150px);
  margin-bottom: 10px;
  padding: 5px;
  word-break: keep-all;
}

#popupSubtext {
  word-break: keep-all;
}

.popup-buttons {
  margin-top: auto;
}

/* Downgrade block — a full takeover, not a popup: there is no "continue". */
.downgrade-block {
  position: fixed;
  inset: 0;
  z-index: 10010;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background-color: rgba(0, 0, 0, 0.92);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

.downgrade-card {
  max-width: 360px;
  width: 100%;
  text-align: center;
  padding: 28px 24px;
  border-radius: 12px;
  background-color: #0d0d0d;
  border: 1px solid color-mix(in srgb, var(--danger-color) 45%, transparent);
}

/* Vector's own dialog icon, matching how popupConfirm renders it large. The
   themed `.warning-icon` asset is pixel art drawn for 16px and stair-steps
   when enlarged. */
.downgrade-icon {
  width: 84px;
  height: 84px;
  margin-bottom: 12px;
}

.downgrade-card h2 {
  font-size: 20px;
  margin-bottom: 12px;
  color: var(--danger-color);
}

.downgrade-card p {
  font-size: 14px;
  line-height: 1.5;
  opacity: 0.8;
  margin-bottom: 20px;
}

.downgrade-versions {
  display: flex;
  align-items: stretch;
  margin-bottom: 20px;
  border-radius: 8px;
  background-color: rgba(255, 255, 255, 0.04);
}

.downgrade-version {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 12px 8px;
  min-width: 0;
}

.downgrade-version span {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  opacity: 0.45;
}

.downgrade-version strong {
  font-size: 14px;
  /* Long version strings wrap rather than widen the card. */
  overflow-wrap: anywhere;
}

/* The required build is the one the user has to act on. */
.downgrade-version:last-child strong {
  color: var(--danger-color);
}

.downgrade-versions-sep {
  width: 1px;
  margin: 10px 0;
  background-color: rgba(255, 255, 255, 0.1);
}

.downgrade-reassure {
  font-size: 13px !important;
  opacity: 0.55 !important;
}

/* Margins zeroed explicitly: `.cancel-btn` carries a global margin-left that
   would otherwise offset it from the button above at identical width. */
.downgrade-card .btn,
.downgrade-card .cancel-btn {
  display: block;
  width: 100%;
  margin: 0;
}

.downgrade-card .btn {
  margin-bottom: 10px;
}

.typing-indicator-text {
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: BreathingGradient 6s cubic-bezier(.46,.03,.52,.96) infinite;
}

.startup-subtext-gradient {
  /* Always uses Vector theme (brand colour) */
  background: linear-gradient(to right, #2b976c 13.87%, #59fcb3 60%);
  background-size: 200% 200%;
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: BreathingGradient 6s cubic-bezier(.46,.03,.52,.96) infinite;
}

@keyframes BreathingGradient {
  0% {background-position: 0% 50%;}
  50% {background-position: 100% 50%;}
  100% {background-position: 0% 50%;}
}

/* Spin the element smoothly around its own center */
@keyframes spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

.spin {
  /* keep the element in the normal document flow */
  display: inline-block;

  /* pivot in the middle rather than a corner */
  transform-origin: center center;

  /* drive the animation */
  animation: spin 2s linear infinite;
}

/* Encryption Migration Modal */
.encryption-migration-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.95);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 99999;
}

.encryption-migration-overlay.active {
  display: flex;
}

.encryption-migration-modal {
  background: linear-gradient(145deg, #1a1a1a, #0d0d0d);
  border: 2px solid #333;
  border-radius: 20px;
  padding: 40px;
  text-align: center;
  max-width: 400px;
  margin-left: 10px;
  margin-right: 10px;
  box-shadow: 0 0 60px rgba(0, 0, 0, 0.8);
}

.encryption-migration-icon {
  position: relative;
  width: 48px;
  height: 48px;
  margin: 0 auto 20px auto;
  opacity: 0.8;
}

.encryption-migration-modal h2 {
  margin: 0 0 10px 0;
  font-size: 22px;
  font-weight: 600;
}

.encryption-migration-phase {
  margin: 0 0 25px 0;
  opacity: 0.7;
  font-size: 14px;
}

.encryption-migration-modal .progress-bar-track {
  margin-bottom: 25px;
}

.encryption-migration-warning {
  margin: 0;
  font-size: 13px;
  opacity: 0.8;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.encryption-warning-icon {
  position: relative;
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}

.encryption-warning-icon .icon {
  background-color: #f5a623;
}

.credential-modal {
  width: 100%;
  box-sizing: border-box;
}

@media (max-width: 420px) {
  .credential-modal {
    padding-left: 20px;
    padding-right: 20px;
  }
  .credential-modal .pin-row input {
    width: 34px;
    height: 34px;
    margin: 3px;
    font-size: 16px;
  }
}

.credential-modal .pin-row {
  display: flex;
  justify-content: center;
  margin: 20px 0 0 0;
  border: none;
  background: none;
  padding: 0;
}

.credential-modal .pin-row input {
  background-color: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 8px;
  color: #fff;
  font-size: 18px;
}

.credential-modal .pin-row input:focus {
  border-color: var(--accent);
  outline: none;
}

#credential-modal-password {
  margin: 20px 0 0 0;
}

#credential-modal-password-input {
  width: 100%;
  box-sizing: border-box;
  text-align: center;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 8px;
  color: #fff;
  padding: 12px;
  font-size: 15px;
  font-family: inherit;
  box-shadow: none;
}

#credential-modal-password-input::placeholder {
  color: rgba(255, 255, 255, 0.35);
}

#credential-modal-password-input:focus {
  border-color: rgba(255, 255, 255, 0.4);
  outline: none;
  box-shadow: none;
}

.credential-modal-buttons {
  display: flex;
  gap: 10px;
  justify-content: center;
  margin-top: 24px;
}

.credential-modal-buttons .btn {
  min-width: 100px;
  padding: 10px 20px;
}


.pin-row input {
  text-align: center;
  width: 40px;
  height: 40px;
  margin: 5px;
  padding: 0;
  background-color: black;
}

.intro-anim {
  animation: Intro 0.5s cubic-bezier(.25,.1,.25,1) forwards;
}

@keyframes Intro {
  0% { opacity: 0; transform: translateY(50px); }
}

.fadeout-anim {
  animation: Fadeout 0.5s cubic-bezier(.46,.03,.52,.96) forwards;
}

@keyframes Fadeout {
  100% { opacity: 0; }
}

.fadein-anim {
  animation: fadeInWithoutTransform 0.25s cubic-bezier(.46,.03,.52,.96) forwards;
}

.fadein-subtle-anim {
  animation: fadeInWithoutTransform 0.4s ease-out;
}

@keyframes fadeInWithoutTransform {
  0% { opacity: 0; }
}

@keyframes fadeOutWithoutTransform {
  0% { opacity: 1; }
  100% { opacity: 0; }
}



/* Profile Header - Fixed at top */
.profile-header {
  position: fixed;
  top: var(--chrome-h);
  left: 0;
  right: 0;
  height: 60px;
  background-color: rgba(22, 22, 22, 0.85);
  z-index: 100;
  backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
  justify-content: center;
}

.profile-header .profile-header-info {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  max-width: 60%;
}

.profile-header #profile-name {
  cursor: default !important;
  opacity: 1 !important;
}


.profile-header-name-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 0;
  margin-top: 5px;
}

.account .profile-header-name-row {
  width: 100%;
}

.profile-header #profile-header-avatar-container {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.profile-header #profile-name {
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  max-width: 100%;
}

.profile-header #profile-status {
  width: 100%;
  text-align: center;
}

/* Profile Content - Scrollable */
.profile-content {
  position: fixed;
  top: calc(60px + var(--chrome-h));
  bottom: 65px;
  left: 0;
  right: 0px;
  overflow-x: hidden;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: none;
  touch-action: pan-y;
  box-sizing: border-box;
}

#profile:not(.is-own-profile) .profile-content {
  bottom: 6px;
}

/* Profile Banner */
.profile-banner.is-empty {
  background-color: #0a0a0a;
}

.profile-avatar.is-placeholder {
  background: url("icons/user-placeholder.svg") center / cover no-repeat;
}

.profile-banner {
  object-fit: cover;
  object-position: center;
  width: 100%;
  height: 200px;
  display: block;
}

.profile-edit-active #profile-banner-container::before {
  content: '';
  position: absolute;
  top: 70px;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 40px;
  height: 40px;
  -webkit-mask: url('./icons/add-photo.svg') no-repeat center;
  mask: url('./icons/add-photo.svg') no-repeat center;
  -webkit-mask-size: contain;
  mask-size: contain;
  background-color: rgba(255,255,255,0);
  transition: background-color 0.2s ease;
  z-index: 3;
  pointer-events: none;
}

.profile-edit-active #profile-banner-container:hover::before {
  background-color: #B2B2B2;
}

.profile-edit-active #profile-banner-container.avatar-hovered::before {
  background-color: rgba(255,255,255,0) !important;
}

/* Hide the add-photo glyph and dim the banner during upload so the
   injected progress ring reads cleanly. */
.profile-edit-active #profile-banner-container.uploading::before {
  display: none !important;
}
.profile-edit-active #profile-banner-container.uploading::after {
  background: rgba(0, 0, 0, 0.45) !important;
}
/* Progress ring for avatar/banner upload. Needs its own spin keyframe:
   the shared `inline-image-spin` rotates with a bare transform that
   would clobber the centering translate below. */
.profile-upload-ring {
  --progress: 5%;
  position: absolute;
  width: 48px;
  height: 48px;
  z-index: 4;
  pointer-events: none;
  border-radius: 50%;
  background: conic-gradient(
    var(--icon-color-primary) 0% var(--progress),
    rgba(255, 255, 255, 0.2) var(--progress) 100%
  );
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 4px), #fff calc(100% - 3px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 4px), #fff calc(100% - 3px));
  animation: profile-upload-ring-spin 2s linear infinite;
  transition: --progress 0.3s ease;
}
@keyframes profile-upload-ring-spin {
  from { transform: translate(-50%, -50%) rotate(0deg); }
  to   { transform: translate(-50%, -50%) rotate(360deg); }
}
#profile-banner-container .profile-upload-ring {
  top: 70px;
  left: 50%;
  transform: translate(-50%, -50%);
}
.profile-avatar-container .profile-upload-ring {
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

/* Banner Hover Overlay */
#profile-banner-container::after {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 200px;
  background: rgba(255,255,255,0);
  transition: background 0.2s ease;
  pointer-events: none;
  z-index: 1;
}

.profile-edit-active #profile-banner-container {
  cursor: pointer;
}

.profile-edit-active #profile-banner-container:hover::after {
  background: #03030380;
  cursor: pointer;
}

.profile-edit-active #profile-banner-container.avatar-hovered:hover::after {
  background: rgba(255,255,255,0) !important;
  cursor: default !important;
}

.profile-edit-active #profile-banner-container.avatar-hovered {
  cursor: default !important;
}

/* Avatar Container */
.profile-avatar-container {
  position: relative;
  z-index: 2;
  width: 175px;
  height: 175px;
  margin: -87px auto 10px;
  border-radius: 50%;
  border: 8px solid #030303;
  background-color: #030303;
}

.profile-edit-active .profile-avatar-container::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 40px;
  height: 40px;
  -webkit-mask: url('./icons/add-photo.svg') no-repeat center;
  mask: url('./icons/add-photo.svg') no-repeat center;
  -webkit-mask-size: contain;
  mask-size: contain;
  background-color: rgba(255,255,255,0);
  transition: background-color 0.2s ease;
  z-index: 3;
  pointer-events: none;
}

.profile-edit-active .profile-avatar-container:hover::before {
  background-color: #B2B2B2;
}

/* Avatar upload state — same hide-glyph + dim pattern as banner. */
.profile-edit-active .profile-avatar-container.uploading::before {
  display: none !important;
}
.profile-edit-active .profile-avatar-container.uploading::after {
  background: rgba(0, 0, 0, 0.45) !important;
}

.profile-avatar-container::after {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(255,255,255,0);
  border-radius: 50%;
  transition: background 0.2s ease;
  pointer-events: none;
}

.profile-edit-active .profile-avatar-container {
  cursor: pointer;
  pointer-events: all;
}

.profile-edit-active .profile-avatar-container:hover::after {
  background: #03030380;
}

#profile-edit-fields input {
  padding: 0 !important;
}

/* Avatar Image */
.profile-avatar {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  object-position: center;
}



.aggro-glitch-img {
  content: url('./icons/aggro-glitch-red.gif');
  width: 64px;
  height: 64px;
}

/* Dangerzone button group styling */
.danger-buttons {
  display: block;
}

/* Style each danger option like toggle-container */
.danger-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* Make dangerzone buttons thinner */
.danger-option button {
  padding-top: 0.4em;
  padding-bottom: 0.4em;
  margin-bottom: 0.5em;
  display: flex;
  align-items: center;
  gap: 6px;
}

/* Style the warning icon inside danger buttons */


/* Left side (info icon + label) stays together */
.danger-option .left-group {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
}

/* Set opacity for all info icons */
.icon-info {
  opacity: 0.5;
}

/* Add spacing to dangerzone info icons */
.danger-option .icon-info {
  margin-right: 5px;
}

/* ============================================================================
   Remote Signer — Security panel card
   ============================================================================ */
.remote-signer-card {
  margin: 0.5em 0 1em;
  padding: 12px 14px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.08);
}
.remote-signer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.remote-signer-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  font-size: 0.95em;
}
.remote-signer-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #9aa0a6;
  box-shadow: 0 0 0 0 transparent;
  transition: background 0.2s ease, box-shadow 0.2s ease;
  flex: 0 0 auto;
}
.remote-signer-dot.online {
  background: #2ecc71;
  box-shadow: 0 0 6px rgba(46, 204, 113, 0.55);
}
.remote-signer-dot.offline {
  background: #e74c3c;
  box-shadow: 0 0 6px rgba(231, 76, 60, 0.45);
}
.remote-signer-dot.connecting {
  background: #f1c40f;
  animation: remote-signer-dot-pulse 1.2s ease-in-out infinite;
}
@keyframes remote-signer-dot-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(241, 196, 15, 0); }
  50%      { box-shadow: 0 0 6px rgba(241, 196, 15, 0.6); }
}
.remote-signer-head button {
  padding: 0.35em 0.9em;
  font-size: 0.85em;
  margin: 0;
}
.remote-signer-meta {
  margin-top: 10px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.remote-signer-meta-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-size: 0.85em;
}
.remote-signer-meta-label {
  flex: 0 0 70px;
  color: rgba(255, 255, 255, 0.45);
}
.remote-signer-meta-value {
  flex: 1 1 auto;
  word-break: break-all;
  color: rgba(255, 255, 255, 0.85);
}
.remote-signer-mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.78em;
  letter-spacing: 0.02em;
}
.remote-signer-hint {
  margin: 10px 0 0;
  font-size: 0.78em;
  color: rgba(255, 255, 255, 0.45);
  line-height: 1.4;
}

.profile-banner-edit {
  position: absolute;
  top: 10px;
  left: -6px;
  display: none;
  margin: 0;
  transform: scale(0.75);
  transform-origin: top left;
  opacity: 1;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 8px 10px;
}

#profile-edit-btn .navbar-icon {
  width: 30px;
  height: 30px;
  position: relative;
  flex-shrink: 0;
  margin: 0;
  padding: 0;
  padding-top: 4px;
}

#profile-edit-cancel-btn,
#profile-edit-save-btn,
#profile-edit-mode-label,
#wallpaper-edit-cancel-btn,
#wallpaper-edit-save-btn,
#wallpaper-edit-mode-label {
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
}

#profile-edit-cancel-btn,
#profile-edit-save-btn,
#wallpaper-edit-cancel-btn,
#wallpaper-edit-save-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

#profile-edit-mode-label,
#wallpaper-edit-mode-label {
  color: rgba(255, 255, 255, 0.6);
  font-size: 12px;
}

#profile-edit-cancel-btn .icon,
#profile-edit-cancel-btn span,
#profile-edit-save-btn .icon,
#profile-edit-save-btn span,
#wallpaper-edit-cancel-btn .icon,
#wallpaper-edit-cancel-btn span,
#wallpaper-edit-save-btn .icon,
#wallpaper-edit-save-btn span {
  position: relative !important;
  inset: auto !important;
  margin: 0 !important;
  cursor: pointer !important;
}

#profile-edit-cancel-btn .icon-edit-x,
#wallpaper-edit-cancel-btn .icon-edit-x {
  background-color: var(--danger-color) !important;
  width: 14px !important;
  height: 14px !important;
}

#profile-edit-save-btn .icon-save,
#wallpaper-edit-save-btn .icon-save {
  background-color: #33DB98 !important;
  width: 16px !important;
  height: 16px !important;
}

#profile-edit-cancel-btn:hover,
#profile-edit-save-btn:hover,
#wallpaper-edit-cancel-btn:hover,
#wallpaper-edit-save-btn:hover {
  opacity: 0.75;
}

#profile-edit-bar {
  transition: opacity 0.25s ease;
}

/* Wallpaper edit-mode overlay — same chrome as the profile edit bar, layered
   over the chat header while a preview is staged. */
#wallpaper-edit-bar {
  position: absolute;
  inset: 0;
  align-items: center;
  justify-content: space-between;
  padding: 0 20px;
  background-color: rgb(22, 22, 22);
  z-index: 2;
  transition: opacity 0.25s ease;
}
#wallpaper-edit-mode-label {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
}

#profile-edit-fields {
  flex-direction: column;
  gap: 12px;
  width: 90%;
  margin: 16px auto 0;
}

.profile-edit-label {
  font-size: 11px;
  color: rgba(255,255,255,0.4);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 4px;
  display: block;
  margin-bottom: -4px !important;
}


.profile-edit-field-wrapper {
  background: rgba(255,255,255,0.05);
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 6px;
  padding: 6px 14px;
  width: 100%;
  box-sizing: border-box;
  cursor: pointer;
  text-align: center;
}

.profile-edit-field-wrapper:hover {
  border-color: rgba(255,255,255,0.25);
}

.profile-edit-field-wrapper:focus-within {
  border-color: var(--primary-color);
}

.profile-edit-field-bio {
  min-height: 80px;
}

.profile-edit-field-text {
  margin: 0;
  background: none;
  border: none;
  color: inherit;
  font-size: inherit;
  width: 100%;
}

#profile-edit-bio textarea::-webkit-scrollbar {
  display: none;
}

#profile-edit-fields input,
#profile-edit-fields textarea {
  box-sizing: border-box;
  width: 100% !important;
  overflow: hidden;
  padding: 0 !important;
  text-align: center;
}

.profile-share-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  display: none;
  margin: 0;
  transform: scale(0.75);
  transform-origin: top right;
  opacity: 1 !important;
}

/* Banner QR button — same scan glyph as the New Chat composer. Below Share
   on the own profile; takes Share's top-right spot on other profiles.
   Id selector: .profile-option would out-cascade a class rule's box sizing. */
#profile-qr-btn {
  top: 68px;
  padding: 10px;
  /* 50 + padding + border = the same 74px box as the Edit/Share pills. */
  width: 50px;
  height: 50px;
}

#profile:not(.is-own-profile) #profile-qr-btn {
  top: 10px;
}

.profile-qr-icon {
  width: 100%;
  height: 100%;
  color: #fff; /* stroke rides currentColor */
}

.profile-qr-icon svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* ============================================================
   Status dialog — compact neutral card: avatar + live pill on
   top, one input row, slim actions. Glides to the upper third
   while the emoji panel owns the bottom.
   ============================================================ */
.status-dialog-overlay {
  position: fixed;
  inset: 0;
  background-color: rgba(0, 0, 0, 0.72);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  z-index: 9000; /* below the emoji picker's status mode, above the app */
  display: none;
  align-items: center;
  justify-content: center;
}

.status-dialog-overlay.active { display: flex; }

/* Selectability polish: chrome is tappable, never text-selectable — eager
   clicks on the header, navbar, profile or modal furniture shouldn't paint
   selections (including the whitespace text nodes between elements). */
.account,
.chat-bookmarks-btn,
.new-chat-btn,
.navbar,
#profile,
.status-dialog-card,
.qr-overlay-card {
  -webkit-user-select: none;
  user-select: none;
}

/* Real content opts back in: the bio, the npub itself (it has a copy button,
   but manual selection stays honest), and every edit-mode field. */
#profile input,
#profile textarea,
#profile-description,
#profile-npub {
  -webkit-user-select: text;
  user-select: text;
}

/* Images additionally refuse the ghost-drag that reads as a grab/selection */
.profile-banner,
.profile-avatar,
#profile-badges img {
  -webkit-user-drag: none;
}

/* The one exception: the status composer is editable text. */
.status-input-host .rich-composer {
  -webkit-user-select: text;
  user-select: text;
}

/* Mirror of the open pop: the card recedes and the dim lifts together.
   Shared by every popping modal (Status dialog, QR overlay). */
@keyframes modal-card-pop-out {
  from { transform: scale(1); opacity: 1; }
  to { transform: scale(0.92); opacity: 0; }
}

@keyframes modal-overlay-fade-out {
  from { opacity: 1; }
  to { opacity: 0; }
}

.status-dialog-overlay.closing {
  animation: modal-overlay-fade-out 0.15s ease-out forwards;
  pointer-events: none;
}

.status-dialog-overlay.closing .status-dialog-card {
  animation: modal-card-pop-out 0.15s ease-out forwards;
}

/* Emoji panel open: yield the bottom — card glides to the upper third */
.status-dialog-overlay.panel-open {
  align-items: flex-start;
  padding-top: max(24px, 7vh);
}

.status-dialog-card {
  display: flex;
  flex-direction: column;
  width: min(92vw, 380px);
  background-color: #101010;
  border: 1px solid #2c2e2f;
  border-radius: 18px;
  padding: 14px 16px 12px;
  box-sizing: border-box;
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.55);
}

/* Applied AFTER the overlay is displayed: WebKit never starts an animation
   declared on a subtree that comes out of display:none, so the property has
   to appear on an already-rendered element to play. */
.status-dialog-card.pop-in { animation: qr-overlay-pop 0.16s ease-out; }

.status-dialog-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}

.status-dialog-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.45);
}

.status-dialog-x,
.status-dialog-x:hover,
.status-dialog-x:focus,
.status-dialog-x:active {
  background: none;
  border: none;
  box-shadow: none;
  outline: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.status-dialog-x {
  color: rgba(255, 255, 255, 0.4);
  font-size: 14px;
  line-height: 1;
  padding: 4px 6px;
  cursor: pointer;
  transition: color 0.15s ease;
}

.status-dialog-x:hover { color: #fff; }

/* Live row: your avatar beside the pill exactly as others see it */
.status-live-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
  min-width: 0;
}

.status-preview-avatar {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  overflow: hidden;
  background-color: #1a1a1a;
}

.status-preview-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.status-preview {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  padding: 7px 14px;
  background-color: #1a1a1a;
  border: 1px solid #262626;
  border-radius: 999px;
  box-sizing: border-box;
}

.status-preview-dot {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background-color: #59fcb3;
  box-shadow: 0 0 6px rgba(89, 252, 179, 0.6);
}

#status-preview-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 13.5px;
  color: #fff;
}

#status-preview-text .emoji {
  width: 16px;
  height: 16px;
  vertical-align: -3px;
}

.status-preview.status-preview-empty { opacity: 0.45; }
.status-preview.status-preview-empty .status-preview-dot { background-color: #555; box-shadow: none; }

.status-input-row {
  display: flex;
  align-items: center;
  width: 100%;
  background-color: #1a1a1a;
  border: 1px solid #262626;
  border-radius: 12px;
  padding: 2px 2px 2px 14px;
  box-sizing: border-box;
  transition: border-color 0.15s ease;
}

.status-input-row:focus-within { border-color: #3d4040; }

/* The icon sits equidistant from the field's top, right and bottom edges:
   a fixed 40px hit box centred in the 44px field leaves ~12px to the glyph
   on every side. Explicit size, not padding — global button styling
   collapses padding-driven boxes here. */
#status-emoji-btn {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: 0;
  margin-left: 2px;
  cursor: pointer;
  opacity: 0.7;
  transition: opacity 0.15s ease, transform 0.15s ease;
}

/* A bare floating glyph: the global button styling paints a container box
   (a backdrop blur reads as a subtle pale panel on the darker field), so
   every state pins ALL the chrome off. */
#status-emoji-btn,
#status-emoji-btn:hover,
#status-emoji-btn:focus,
#status-emoji-btn:active {
  background: none;
  border: none;
  box-shadow: none;
  outline: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

#status-emoji-btn:hover { opacity: 1; transform: scale(1.08); }

#status-emoji-btn .icon {
  /* The global .icon styling floats glyphs absolutely; this one is an
     ordinary flex child centred by the button. */
  position: static;
  margin: 0;
  width: 20px;
  height: 20px;
  display: block;
}

.status-input-host {
  flex: 1 1 auto;
  min-width: 0;
}

/* Mini composer: same machinery as the chat one, emoji-only grammar */
.status-input-host .rich-composer {
  position: relative;
  font-family: Rubik, sans-serif;
  font-size: 14.5px;
  font-weight: 400;
  line-height: 20px;
  min-height: 20px;
  max-height: 62px; /* ~3 lines, then scrolls — statuses stay short */
  overflow-y: auto;
  overflow-anchor: none;
  outline: none;
  color: #fff;
  text-align: start;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  padding: 9px 4px 9px 0;
  background: transparent;
}

.status-input-host .rich-composer[data-empty="1"]::before {
  top: 9px;
  color: rgba(255, 255, 255, 0.3);
}

.status-char-count {
  flex: 0 0 auto;
  font-size: 11px;
  color: rgba(255, 255, 255, 0.35);
  min-width: 22px;
  text-align: right;
}

.status-char-count.status-char-low { color: #ffb347; }

.status-dialog-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 12px;
}

.status-btn-clear {
  background: none;
  border: none;
  color: rgba(255, 255, 255, 0.4);
  font-size: 12.5px;
  cursor: pointer;
  padding: 6px 2px;
  transition: color 0.15s ease;
}

.status-btn-clear:hover { color: #fff; }
.status-btn-clear.hidden { visibility: hidden; } /* keeps Save right-aligned */

.status-btn-save {
  background-color: #59fcb3;
  color: #06130d;
  border: none;
  border-radius: 10px;
  padding: 9px 26px;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
  transition: filter 0.15s ease, transform 0.1s ease;
}

.status-btn-save:hover { filter: brightness(1.08); }
.status-btn-save:active { transform: scale(0.97); }

/* Emoji panel in status mode: above the dialog overlay, GIFs hidden, and
   flush to the bottom — the composer clearance it normally keeps doesn't
   exist here */
.emoji-picker.emoji-picker-status-mode { z-index: 10005; bottom: 10px !important; }
.emoji-picker.emoji-picker-no-gifs .picker-mode-btn[data-mode="gif"] { display: none; }

/* Fullscreen QR overlay (profile QR + bunker login QR) */
.qr-overlay {
  position: fixed;
  inset: 0;
  background-color: rgba(0, 0, 0, 0.88);
  z-index: 10000;
  display: none;
  align-items: center;
  justify-content: center;
}

.qr-overlay.active { display: flex; }

.qr-overlay-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 22px;
  background-color: #101010;
  border: 1px solid #2c2e2f;
  border-radius: 18px;
  padding: 24px;
}

/* Applied AFTER the overlay is displayed — WebKit never starts an animation
   declared on a subtree emerging from display:none. */
.qr-overlay-card.pop-in { animation: qr-overlay-pop 0.16s ease-out; }

.qr-overlay.closing {
  animation: modal-overlay-fade-out 0.15s ease-out forwards;
  pointer-events: none;
}

.qr-overlay.closing .qr-overlay-card {
  animation: modal-card-pop-out 0.15s ease-out forwards;
}

@keyframes qr-overlay-pop {
  from { transform: scale(0.92); opacity: 0; }
  to { transform: scale(1); opacity: 1; }
}

.qr-overlay-tile {
  /* Solid white tile: the padding doubles as the QR quiet zone for scanners.
     Height wraps the SVG (1:1 viewBox), so the tile stays square without an
     aspect-ratio/percentage-height cycle. */
  position: relative;
  width: min(72vw, 320px);
  padding: 16px;
  border-radius: 10px;
  background-color: #fff;
  box-sizing: border-box;
}

#qr-overlay-full svg {
  width: 100%;
  height: auto;
  display: block;
}


/* New Chat scan button — camera scanner entry, mobile-only (body.mobile) */
#chat-new-scan-btn {
  display: none;
  color: #b2b2b2;
  margin-right: 3px;
  cursor: pointer;
  /* Snug: the container's default 10px padding leaves the glyph tiny */
  padding: 6px;
}

body.mobile #chat-new-scan-btn { display: block; }
#chat-new-scan-btn:hover { color: #fff; }

#chat-new-scan-btn svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* Fullscreen QR scanner */
.qr-scanner {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: none;
  background-color: #000;
}

.qr-scanner.active { display: block; }

.qr-scanner-video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Hidden until the first real frame — the WebView's placeholder art
     (a giant play glyph) paints in the gap and must never be seen */
  opacity: 0;
  transition: opacity 0.18s ease;
}

.qr-scanner-video.live { opacity: 1; }

.qr-scanner-veil {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
}

.qr-scanner-window {
  width: min(70vw, 280px);
  aspect-ratio: 1;
  border: 2px solid var(--accent-color, #59fcb3);
  border-radius: 18px;
  /* Dims everything around the viewfinder in one paint */
  box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.55);
}

.qr-scanner-hint {
  color: rgba(255, 255, 255, 0.85);
  font-size: 14px;
}

/* Id selector: must outrank .qr-overlay-close's width:100% */
#qr-scanner-cancel {
  position: absolute;
  bottom: max(28px, env(safe-area-inset-bottom));
  left: 50%;
  transform: translateX(-50%);
  width: min(80vw, 360px);
}

.qr-overlay-close {
  width: 100%;
  padding: 12px 0;
  border: none;
  border-radius: 12px;
  background-color: var(--accent-color, #59fcb3);
  color: #000;
  font-size: 16px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
}

/* Style individual options within the danger zone */
.danger-option {
  display: flex;
  align-items: center;
  justify-content: space-between; /* left info+title, right button */
  gap: 8px;
  width: 100%;
}

/* Left side (info icon and label) should take remaining space */
.danger-option .left-group {
  display: flex;
  align-items: center;
  gap: 4px;
}

/* Ensure the button stays on the right */
.danger-option button {
  margin-left: auto;
}

/* Primary Device Status Dot */




.profile-options {
  display: flex;
  justify-content: center;
  height: 50px;
  margin-top: 30px;
  margin-bottom: 30px;
  margin-top: 15px;
  margin-bottom: 50px;
}

.profile-option {
  padding: 8px 10px 8px 10px;
  margin-left: 6px;
  margin-right: 6px;
  height: 50px;
  width: 50px;
  border-radius: 12px;
  border-color: #393B3C;
  border-style: solid;
  border-width: 2px;
  color: #b2b2b2;
  background-color: #030303;
  cursor: pointer;
}

.profile-option:hover {
  background-color: #171717;
  border-color: #959899;
  color: white;
  opacity: 1;
}

.profile-option.is-danger span { color: var(--danger-color); }
.profile-option.is-danger .icon { background-color: var(--danger-color); }
.is-danger-text { color: var(--danger-color); }
.is-danger-icon { background-color: var(--danger-color); }
.profile-more-item.is-danger:hover { background: var(--danger-color-hover); }

.unblock-btn {
  color: var(--danger-color);
  font-size: 13px;
  cursor: pointer;
  padding: 4px 10px;
  border: 1px solid var(--danger-color);
  border-radius: 6px;
}

.unblock-btn:hover {
  background-color: var(--danger-color-hover);
}

/* Profile "More" dropdown menu */
/* Reusable context menu — generalised from `.profile-more-dropdown`.
   Singleton appended to <body> + positioned absolutely so it can
   anchor to any element (cursor coords on right-click, button rect
   on click). z-index sits above the picker (2000) and toolbar overlays. */
.context-menu {
  position: fixed;
  background: #1e1e1e;
  border: 1px solid #393B3C;
  border-radius: 10px;
  /* Edge-to-edge items (rounded corners clip the first/last hover); consistent with .profile-more-dropdown. */
  padding: 0;
  min-width: 160px;
  overflow: hidden;
  z-index: 3000;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.5);
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 0.12s ease, transform 0.12s ease;
  pointer-events: none;
  /* Menu labels are commands, not content — highlighting them only makes
     the long-press that OPENED the menu look like a text selection. */
  user-select: none;
  -webkit-user-select: none;
}
.context-menu.is-visible {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.context-menu-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 14px;
  color: #b2b2b2;
  font-size: 14px;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
}
.context-menu-item:hover {
  background: #2a2a2a;
  color: #fff;
}
.context-menu-item.is-danger        { color: var(--danger-color); }
.context-menu-item.is-danger:hover  { background: var(--danger-color-hover); color: var(--danger-color); }
.context-menu-item .icon {
  position: relative;
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  margin: 0;
  padding: 0;
  border-radius: 0;
  background-color: currentColor;
}
.context-menu-divider {
  height: 1px;
  background-color: rgba(255, 255, 255, 0.06);
  margin: 4px 0;
}

/* Touch: no hover, so give press feedback + comfortable hit targets. */
.mobile .context-menu { min-width: 200px; }
.mobile .context-menu-item {
  padding: 13px 16px;
  font-size: 15px;
}
.context-menu-item:active { background: #2a2a2a; color: #fff; }
.context-menu-item.is-danger:active { background: var(--danger-color-hover); color: var(--danger-color); }

/* A section label inside the menu, e.g. NOTIFICATIONS above a set of levels. */
.context-menu-header {
  padding: 9px 14px 4px;
  color: #6f6f6f;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: default;
}

/* The row back out of a submenu: its chevron leads, so it reads as a return
   rather than another command. */
.context-menu-item.is-back {
  justify-content: flex-start;
  gap: 8px;
  color: #8b8b8b;
}
.context-menu-lead { flex-shrink: 0; }

/* A colour choice: the swatch leads and any tick sits at the far edge. */
.context-menu-swatch {
  flex-shrink: 0;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.18);
}
.context-menu-item:has(> .context-menu-swatch) {
  justify-content: flex-start;
  gap: 10px;
}
.context-menu-item:has(> .context-menu-swatch) > .icon { margin-left: auto; }

/* A setting the app-wide switch has already decided. Visible, so the reason a
   choice here would do nothing is readable, but not selectable. */
.context-menu-item.is-disabled {
  opacity: 0.45;
  cursor: default;
}
.context-menu-item.is-disabled:hover,
.context-menu-item.is-disabled:active {
  background: transparent;
  color: #b2b2b2;
}

/* Dimmed qualifier after an item label, e.g. Copy (plain). */
.context-menu-item-hint {
  margin-left: 6px;
  opacity: 0.5;
  font-size: 0.85em;
}

/* Swipe-to-reply affordance — a reply glyph in a glassy circular chip,
   revealed from under a row's right edge as it slides left. Scales + fades in
   with the drag (transform/opacity driven per-frame from JS), then IGNITES to
   the theme accent past the commit threshold: border + glow snap on, the glyph
   pops, and a ring pulse radiates out. All commit motion is one-shot, so the
   per-frame path stays compositor-only (60fps on budget devices). */
.dmsg-swipe-reply {
  position: absolute;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background-color: rgba(18, 18, 18, 0.82);
  border: 1px solid rgba(255, 255, 255, 0.10);
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.4);
  pointer-events: none;
  z-index: 1;
  opacity: 0;
  transform: scale(0.4);
  /* Only the ignite (glow/border) eases; transform + opacity are set inline
     per-frame and deliberately omitted here so the drag tracks 1:1. */
  transition: box-shadow 0.16s ease, border-color 0.16s ease, background-color 0.16s ease;
}
/* Themed chats paint a bg image via `.chat-messages::before` and lift content
   with `.chat-messages > * { position: relative; z-index: 1 }`. That ties our
   base rule's specificity and would knock the chip out of absolute positioning
   (and behind messages). Re-assert with matching specificity + a higher layer. */
.chat-messages > .dmsg-swipe-reply {
  position: absolute;
  z-index: 2;
}
.dmsg-swipe-reply__icon {
  background-color: rgba(255, 255, 255, 0.88);
  transition: background-color 0.16s ease;
}

/* Past threshold: ignite. */
.dmsg-swipe-reply.past {
  background-color: rgba(10, 10, 10, 0.9);
  border-color: var(--accent-color, #59fcb3);
  box-shadow:
    0 0 0 2px var(--accent-color, #59fcb3),
    0 0 18px 3px color-mix(in srgb, var(--accent-color, #59fcb3) 55%, transparent),
    0 3px 10px rgba(0, 0, 0, 0.4);
}
.dmsg-swipe-reply.past .dmsg-swipe-reply__icon {
  background-color: var(--accent-color, #59fcb3);
  animation: dmsg-swipe-pop 0.34s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* Ring pulse on commit — a thin accent ring expanding outward, re-fired each
   time the gesture (re)crosses the threshold. */
.dmsg-swipe-reply.past::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: 50%;
  border: 2px solid var(--accent-color, #59fcb3);
  animation: dmsg-swipe-ring 0.52s ease-out forwards;
}
@keyframes dmsg-swipe-pop {
  0%   { transform: scale(1); }
  45%  { transform: scale(1.38); }
  100% { transform: scale(1); }
}
@keyframes dmsg-swipe-ring {
  0%   { opacity: 0.22; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.95); }
}

/* On touch, press-and-hold drives the action menu, so suppress the WebView's
   native text-selection / callout on message rows (it would hijack the
   gesture). Inputs keep selection via their own rules. */
.mobile .dmsg,
.mobile .dmsg * {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
}
/* Media additionally exposes the OS image grab/save handler on long-press —
   kill the drag + callout so only our context menu fires (tap-to-open still
   works; that's a click, not a drag). */
.mobile .dmsg img,
.mobile .dmsg video {
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
}

.profile-more-dropdown {
  position: absolute;
  bottom: calc(100% + 8px);
  right: 0;
  background: #1e1e1e;
  border: 1px solid #393B3C;
  border-radius: 10px;
  padding: 0;
  min-width: 150px;
  overflow: hidden;
  z-index: 10;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
  opacity: 1 !important;
}

#profile-option-more:hover,
#profile-option-more.active {
  opacity: 1 !important;
  background-color: #171717;
  border-color: #959899;
  color: white;
}

.profile-more-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 14px;
  color: #b2b2b2;
  font-size: 14px;
  white-space: nowrap;
}

.profile-more-item .icon {
  position: relative;
  flex-shrink: 0;
  margin: 0;
}

.profile-more-item:hover {
  background: #2a2a2a;
  color: white;
  opacity: 1 !important;
}

.profile-more-item:first-child {
  border-radius: 10px 10px 0 0;
}

.profile-more-item:last-child {
  border-radius: 0 0 10px 10px;
}

@keyframes blockedFadeIn {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Profile npub Section */
.profile-npub-container {
  display: flex;
  align-items: center;
  width: 85%;
  gap: 4px;
  margin: 8px auto 40px auto;
  padding: 4px 4px 4px 8px;
}

.profile-npub {
  flex: 1;
  font-size: 14px;
  opacity: 0.9;
  display: block;
  white-space: nowrap;
  text-align: center;
}

.profile-npub-copy {
  align-self: center;
  flex-shrink: 0;
  padding: 4px;
  line-height: 1;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
}

.profile-npub-copy .icon-copy {
  width: 20px;
  height: 20px;
}

/* Icons */
.icon-edit {
  mask-image: url("./icons/edit.svg");
  -webkit-mask-image: url("./icons/edit.svg");
  width: 16px;
  height: 16px;
  background-color: var(--icon-color-primary);
}

.icon-plus-circle {
  mask-image: url("./icons/plus-circle.svg");
  -webkit-mask-image: url("./icons/plus-circle.svg");
  width: 18px;
  height: 18px;
  background-color: var(--icon-color-primary);
}

.icon-copy {
  mask-image: url("./icons/copy.svg");
  -webkit-mask-image: url("./icons/copy.svg");
  width: 16px;
  height: 16px;
  background-color: var(--icon-color-primary);
}

.icon-check {
  mask-image: url("./icons/check.svg");
  -webkit-mask-image: url("./icons/check.svg");
  width: 16px;
  height: 16px;
  background-color: var(--icon-color-primary);
}

.icon-vector-check {
  mask-image: url("./icons/vector-check.svg");
  -webkit-mask-image: url("./icons/vector-check.svg");
  width: 16px;
  height: 16px;
  background-color: var(--icon-color-primary);
}

.icon-vector-badge {
  mask-image: url("./icons/vector_badge_placeholder.svg");
  -webkit-mask-image: url("./icons/vector_badge_placeholder.svg");
  width: 16px;
  height: 16px;
  background-color: var(--icon-color-primary);
}

.icon-edit,
.icon-plus-circle,
.icon-copy,
.icon-check,
.icon-vector-check {
  background-color: var(--icon-color-primary);
}


.account {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 60px;
  background-color: rgba(22, 22, 22, 0.85);
  backdrop-filter: blur(8px);
  padding: 0 10px;
  /* The sync line tucks in right under the row. */
  margin-bottom: 1px;
}

.account #account-avatar-container,
.profile-header #profile-header-avatar-container,
.chat-header #chat-header-avatar-container,
#group-overview-header-avatar-container {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.account #account-avatar-container img,
.account #account-avatar-container .placeholder-avatar,
.profile-header #profile-header-avatar-container img,
.profile-header #profile-header-avatar-container .placeholder-avatar,
.chat-header #chat-header-avatar-container img,
.chat-header #chat-header-avatar-container .placeholder-avatar,
#group-overview-header-avatar-container img,
#group-overview-header-avatar-container .placeholder-avatar {
  object-fit: cover;
  object-position: center;
  height: 22px;
  width: 22px;
  border-radius: 100%;
  display: block;
}

.account #account-avatar-container .placeholder-avatar,
.profile-header #profile-header-avatar-container .placeholder-avatar,
.chat-header #chat-header-avatar-container .placeholder-avatar,
#group-overview-header-avatar-container .placeholder-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
}

.account #account-name {
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  max-width: 100%;
}

#account .profile-header-info {
  min-width: 0;
  width: 100%;
  overflow: hidden;
}

#account #account-status {
  display: block;
  width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  word-break: break-all;
}

#account #account-status.chat-contact-status {
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: block;
}

.account .placeholder-avatar span {
  margin: 0;
  line-height: 1;
  font-size: 8px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.75);
}

.account #account-status .emoji,
.profile-header #profile-status .emoji,
#chat-contact .emoji,
#profile-name .emoji,
#profile-secondary-name .emoji {
  height: 15px;
  width: 15px;
  border-radius: 0;
  vertical-align: -3px;
  margin-right: unset;
}

/* Bot indicator beside the profile-page name — inline so it flows with
   the text. Reset the base `.icon` absolute position + margin:auto. */
.profile-name-bot-icon {
  position: relative;
  width: 15px;
  height: 15px;
  margin: 0 0 0 6px;
  display: inline-block;
  vertical-align: -2px;
  background-color: var(--icon-color-primary, #59fcb3);
}

/* Larger profile-page status: allow wrap up to 3 lines, then ellipsize.
   Overrides the global .cutoff rule (nowrap + single-line ellipsis) which
   makes long statuses unreadable on narrow viewports. */
#profile-secondary-status.cutoff {
  white-space: normal;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
  word-break: break-word;
  line-height: 1.5;
  margin-top: 5px;
}

.account #account-name .emoji {
  height: 16px;
  width: 16px;
  border-radius: 0;
  vertical-align: -3px;
  margin-right: unset;
}

.placeholder-avatar span {
  margin: 0;
  line-height: 1;
  font-size: 16px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.75);
}

.new-chat-btn {
  display: flex;
  align-items: center;
  height: 40px;
  color: #fff;
  background-color: #17171750;
  border: 1px solid #80808033;
}

.new-chat-btn:hover {
  color: #fff;
  background-color: #171717;
  border-color: #B2B2B2;
}

.new-chat-btn.btn:hover {
    opacity: 1;
}

.new-chat-btn .icon {
  display: inline-block;
  position: relative;
  width: 30px;
  height: 30px;
  margin: 0;
  background: var(--primary-color);
}

.new-chat-btn .icon:hover {
  background: var(--accent-color);
}

#chat-list {
  position: fixed;
  top: calc(120px + var(--chrome-h));
  bottom: 70px;
  left: 0;
  right: 2px;
  overflow-y: auto;
  padding-left: 15px;
  padding-right: 15px;
}

/* Scroll clearance past the last row, which the fixed bottom chrome would otherwise cover. */
#chat-list > :last-child {
  margin-bottom: 50px;
}

/* Optimistic community join in flight: visible at the top of the list but non-interactive
 * + dimmed until the control-fold/sync makes it read/writeable. */
.chatlist-joining {
  pointer-events: none;
  opacity: 0.85;
}

/* "Start of the channel" placeholder for an empty Community (Discord-style) so a fresh/quiet
 * community never opens to a blank void. Removed the instant any message/system event lands. */
.chat-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  margin: auto;
  padding: 40px 24px;
  gap: 6px;
  color: rgba(255, 255, 255, 0.6);
}
.chat-empty-state-icon {
  position: relative; /* positioning context for the absolute .icon */
  width: 44px;
  height: 44px;
  margin-bottom: 6px;
  opacity: 0.5;
}
.chat-empty-state-icon .icon {
  background-color: var(--accent-color, #59fcb3);
}
.chat-empty-state h3 {
  margin: 0;
  font-size: 16px;
  color: rgba(255, 255, 255, 0.9);
}
.chat-empty-state p {
  margin: 0;
  font-size: 13px;
  max-width: 280px;
}
.chatlist-joining-spinner {
  position: relative; /* positioning context for the absolute .icon inside */
  display: inline-block;
  width: 12px;
  height: 12px;
  margin-right: 6px;
  vertical-align: middle;
}
.chatlist-joining-spinner .icon {
  background-color: var(--accent-color, #59fcb3);
}

.chatlist-contact {
  display: flex;
  border-style: solid;
  border-radius: 30px;
  border-width: 1px;
  cursor: pointer;
  /* No text in a chat row is worth selecting; disabling it stops a right-click
     (context menu) from also highlighting the name/preview behind it. */
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  justify-content: left;
  height: 50px;
  padding: 5px;
  margin-bottom: 4px;
  /* Skip layout/paint/decode for off-screen rows. The intrinsic size keeps
     scrollbar geometry stable: 50px row + 12px chrome (border + padding +
     margin-bottom) = 62px stride. */
  content-visibility: auto;
  contain-intrinsic-size: 62px;
}

.chatlist-contact.has-unread {
    border: none;
}

/* A row within reach of the viewport renders before it arrives (js/chatlist-window.js). */
.chatlist-contact.cv-near {
  content-visibility: visible;
}

.chatlist-contact p {
  font-size: 15px;
}

.chatlist-contact-preview {
  margin-top: auto;
  margin-bottom: auto;
  width: calc(100% - 120px);
}

/* Inline emoji inside the chatlist preview row. Explicit width AND height
   (with !important to beat the 50px avatar cap from `.chatlist-contact img`).
   The actual right-edge clipping is handled by `contain: paint` on the
   parent `.cutoff` below — overflow:hidden alone doesn't reliably clip
   compositor-promoted SVG <img> layers in WebKit, which is why the emoji
   sometimes paints on top of already-laid-out text until a layout-
   triggering event (hover, resize) flushes a fresh paint pass. */
.chatlist-contact-preview .emoji {
  width: 15px !important;
  height: 15px !important;
  display: inline-block;
  border-radius: 0;
  min-width: unset;
  min-height: unset;
  vertical-align: -3px;
}

/* Force paint containment on the chatlist preview's truncated lines so
   inline <img> emoji can't paint outside the box — the root cause of the
   "emoji on top of text" bug under content-visibility:auto. `contain: paint`
   creates a real clipping boundary that compositor layers respect, unlike
   plain `overflow: hidden`. Layout-affecting properties (white-space:nowrap,
   text-overflow:ellipsis) are still inherited from the global `.cutoff` rule. */
.chatlist-contact-preview .cutoff {
  contain: paint;
}

.chatlist-contact-timestamp {
  margin-top: auto;
  margin-bottom: auto;
  margin-left: auto;
  margin-right: auto;
  color: rgba(255, 255, 255, 0.5);
  line-height: normal;
}

.chatlist-contact-timestamp.read {
  font-size: 0.8em;
}

/* Community invite row: accent border + action-icon sizing/colors via design tokens
 * (was inline-styled). Accept = accent; decline = the theme danger token (pink on
 * Vector/Neon, red elsewhere) — matching YuurinBee's danger-color system. */
.chatlist-invite {
  border-color: var(--icon-color-primary);
}
.invite-action-btn .icon {
  width: 16px;
  height: 16px;
}
.invite-accept-btn .icon {
  background-color: var(--accent-color);
}
.invite-decline-btn .icon {
  background-color: var(--danger-color);
}
/* Ping pill — negative space (solid accent fill, dark numeral) so it cuts
 * through the row visually. Every count represents pings: full unread set
 * on unmuted chats, @mentions only on muted groups, nothing on muted DMs. */
.chatlist-contact-count {
    margin-top: auto;
    margin-bottom: auto;
    margin-left: auto;
    margin-right: 12px;
    padding: 2px 6px;
    border-radius: 999px;
    background: var(--primary-color);
    color: #000;
    font-size: 14px;
    font-weight: 600;
    line-height: 1.2;
    min-width: 18px;
    width: fit-content;
    text-align: center;
    flex-shrink: 0;
    box-sizing: border-box;
}

.chatlist-contact img {
  object-fit: cover;
  object-position: center;
  min-width: 50px;
  min-height: 50px;
  max-width: 50px;
  max-height: 50px;
  border-radius: 100%;
  margin-top: auto;
  margin-bottom: auto;
}

.placeholder-avatar {
  border-radius: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.avatar-status-icon {
  position: absolute;
  bottom: 0px;
  right: 0px;
  width: 12px;
  height: 12px;
  /* Use "background-color" to explicitly set the Status Icon color */
  border-radius: 100%;
  border-width: 4px;
  border-style: solid;
  border-color: black;
}

/* Header row inside the preview column: name + type icon + (inline
 * time-ago when unread). Flex layout so they sit on one line; the
 * h4 truncates and the trailing pieces never wrap. */
.chatlist-contact-header {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  margin-bottom: -4px;
  padding-right: 10px;
}
.chatlist-contact-header h4 {
  margin: 0 !important;
  padding-right: 0 !important;
  min-width: 0;
  flex: 0 1 auto;
}
/* Group / bot indicator chip — sized to the name's cap height so it
 * sits naturally beside the text. position: relative overrides the
 * base `.icon` absolute positioning, and margin: 0 overrides its
 * `margin: auto` (which inside a flex parent centers the item and
 * shoves siblings apart). */
.chatlist-contact-header .chatlist-type-icon {
  position: relative;
  width: 14px;
  height: 14px;
  margin: 0;
  flex-shrink: 0;
  display: inline-block;
  background-color: rgba(255, 255, 255, 0.55);
}
.chatlist-contact-header .chatlist-type-icon.icon-bot,
.chatlist-contact-header .chatlist-type-icon.icon-pin {
  background-color: var(--icon-color-primary, #59fcb3);
}
/* Inline time-ago (unread rows). Sits flush after the type icon, slightly
 * smaller than the name. Color set inline to the active theme accent. */
.chatlist-contact-inline-time {
  font-size: 12px;
  font-weight: 400;
  flex-shrink: 0;
  white-space: nowrap;
  color: rgba(255, 255, 255, 0.5);
}

.chatlist-contact.has-unread .chatlist-contact-inline-time {
    color: var(--primary-color);
}

.chatlist-contact.has-unread .chatlist-contact-header h4 {
    color: #fff;
}

.chatlist-contact-preview h4 {
  padding-left: 10px;
  padding-right: 15px;
  margin-top: auto;
  margin-bottom: -4px;
  text-align: left;
  color: rgba(255, 255, 255, 0.75);
  transition: color 0.2s ease;
}

.chatlist-contact-preview p {
  margin: 0;
  margin-left: 10px;
  text-align: left;
  font-family: 'Rubik';
  font-weight: 100;
  color: rgb(161, 161, 161);
  transition: color 0.2s ease;
}



.chatlist-contact:hover {
  background-color: rgba(255, 255, 255, 0.05);
}

.chatlist-contact:hover .chatlist-contact-preview h4 {
  color: rgba(255, 255, 255, 1);
}

.chatlist-contact:hover .chatlist-contact-preview p {
  color: rgba(255, 255, 255, 0.85);
}

.chats {
  display: flex;
  justify-content: center;
  flex-direction: column;
  position: relative;
}

#profile,
#group-overview {
  height: 100%;
  overflow: hidden;
}

/* ===== Nested community channel list (chat list) =====
   A multi-channel community renders one row, with its channels underneath. Rows are
   deliberately lighter than a `.chatlist-contact`: they're a level down the hierarchy,
   not peers of it. */
.chatlist-expander {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  flex-shrink: 0;
  margin-left: 2px;
  opacity: 0.45;
  transition: opacity 0.15s ease;
}

.chatlist-expander:hover {
  opacity: 1;
}

.chatlist-expander .icon {
  position: relative;
  width: 16px;
  height: 16px;
  margin: 0;
  background-color: rgba(255, 255, 255, 0.75);
  transition: transform 0.16s cubic-bezier(0.33, 1, 0.68, 1);
}

.chatlist-expander.expanded .icon {
  transform: rotate(180deg);
}

.chatlist-channels {
  display: flex;
  flex-direction: column;
  gap: 1px;
  /* Indented to the community avatar's centre line, so the group reads as one unit. */
  margin: 2px 0 8px 34px;
  padding-left: 10px;
  border-left: 1px solid rgba(255, 255, 255, 0.08);
}

.chatlist-channel {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 8px;
  border-radius: 7px;
  cursor: pointer;
  color: rgba(255, 255, 255, 0.6);
  font-size: 14px;
  -webkit-user-select: none;
  user-select: none;
  transition: background-color 0.12s ease, color 0.12s ease;
}

.chatlist-channel:hover {
  background-color: rgba(255, 255, 255, 0.045);
  color: rgba(255, 255, 255, 0.9);
}

/* A step brighter than the row's own hover (0.045), and neutral: the accent is
   reserved for live/yours signals, not for "you are here". */
.chatlist-channel.active {
  background-color: rgba(255, 255, 255, 0.09);
  color: #fff;
}

.chatlist-channel.has-unread {
  color: #fff;
  font-weight: 500;
}

/* The channel glyph, drawn at the row's text colour in every layout. */
.chatlist-channel-hash {
  position: relative;
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  font-size: 0;
  opacity: 0.55;
}

.chatlist-channel-hash .icon {
  width: 100%;
  height: 100%;
  margin: 0;
  background-color: currentColor;
}

.chatlist-channel-name {
  flex: 1;
  min-width: 0;
  text-align: left;
}

.chatlist-channel-badge {
  flex-shrink: 0;
  min-width: 18px;
  padding: 0 5px;
  border-radius: 9px;
  background-color: var(--primary-color, #33db98);
  color: #000;
  font-size: 11px;
  font-weight: 600;
  line-height: 18px;
  text-align: center;
}

.chatlist-channel-delete {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  opacity: 0;
  transition: opacity 0.12s ease;
}

.chatlist-channel:hover .chatlist-channel-delete {
  opacity: 0.6;
}

.chatlist-channel-delete:hover {
  opacity: 1 !important;
}

.chatlist-channel-delete .icon {
  position: relative;
  width: 12px;
  height: 12px;
  margin: 0;
  background-color: var(--danger-color, #ff2ea9);
}

.chatlist-channel-add {
  color: rgba(255, 255, 255, 0.4);
}

.chatlist-channel-add .chatlist-channel-hash {
  width: auto;
  height: auto;
  font-size: 16px;
}

/* Community overview metrics, in one place so the widescreen sidebar can retune
   them (a 300px column can't afford the full-screen lead-in). */
#group-overview {
  --group-overview-avatar: 100px;
  --group-overview-avatar-lead: 100px;
  --group-overview-tail: 75px;
}

.group-overview-avatar-frame {
  position: relative;
  margin: var(--group-overview-avatar-lead) auto 0;
  width: var(--group-overview-avatar);
  height: var(--group-overview-avatar);
  text-align: center;
}

#group-overview-avatar,
.group-overview-avatar-img {
  width: var(--group-overview-avatar);
  height: var(--group-overview-avatar);
}

#group-overview-avatar {
  border-radius: initial;
  display: inline-block;
}

.group-overview-avatar-img {
  object-fit: cover;
  border-radius: 50%;
}

.group-overview-body {
  padding-bottom: var(--group-overview-tail);
}

#group-mute-btn {
  position: absolute;
  right: 20px;
  top: 110px;
  z-index: 1;
  width: 42px;
  height: 42px;
  padding: 6px;
}

.back-btn {
  position: absolute;
  cursor: pointer;
  left: 10px;
  top: 18px;
  margin: 0;
  z-index: 10;
}

.chat-header {
  position: fixed;
  height: 60px;
  background-color: rgba(22, 22, 22, 0.9);
  backdrop-filter: blur(10px);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  z-index: 100;
}

.chat-header #chat-contact,
.chat-header #group-overview-name {
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  max-width: 100%;
}

.chat-header .profile-header-info {
  max-width: min(70%, calc(100% - 280px));
  overflow: hidden;
}

/* Centred while the name fits; past that each side shrinks to its own buttons and
   the name takes everything between them. */
#chat > .chat-header {
  display: grid;
  grid-template-columns: minmax(max-content, 1fr) minmax(0, max-content) minmax(max-content, 1fr);
}

#chat > .chat-header > #chat-back-btn {
  grid-column: 1;
  justify-self: start;
  position: relative;
  inset: auto;
}

#chat > .chat-header > .profile-header-info {
  grid-column: 2;
  max-width: none;
  padding: 0 10px;
}

#chat > .chat-header > .chat-header-actions {
  grid-column: 3;
  justify-self: end;
  position: relative;
  inset: auto;
  margin-right: 10px;
}

.chat-header #chat-contact-status,
.chat-header #group-overview-status {
  width: 100%;
  text-align: center;
}

.chat-header #chat-header-avatar-container {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.chat-contact-with-status {
  display: block;
  max-width: 60%;
  font-weight: bold;
  margin-top: 8px;
  margin-bottom: 0;
  margin-left: auto;
  margin-right: auto;
  transition: margin-top 0.3s cubic-bezier(0.34, 1, 0.64, 1), margin-bottom 0.3s cubic-bezier(0.34, 1, 0.64, 1);
}

.chat-contact {
  display: block;
  max-width: 60%;
  font-weight: bold;
  margin-top: 20px;
  margin-bottom: 12px;
  margin-left: auto;
  margin-right: auto;
  transition: margin-top 0.3s cubic-bezier(0.34, 1, 0.64, 1), margin-bottom 0.3s cubic-bezier(0.34, 1, 0.64, 1);
}

.chat h3 img {
  height: 20px;
  width: 20px;
  border-radius: 0;
  vertical-align: -3px;
  margin-right: unset;
}

/* `-# ` subtext: Discord's tiny text. Block so consecutive lines stack. */
.chat small.md-subtext {
  display: block;
  font-size: 0.8em;
  opacity: 0.7;
}

.chat-contact-status {
  opacity: 0.8;
  font-size: 14px;
  margin-bottom: 2px;
  display: block;
  width: 60%;
  margin-left: auto;
  margin-right: auto;
  margin-top: -1px;
  transition: opacity 0.25s cubic-bezier(0.22, 1, 0.36, 1), max-height 0.3s cubic-bezier(0.34, 1, 0.64, 1), margin-bottom 0.3s cubic-bezier(0.34, 1, 0.64, 1);
  max-height: none;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.chat-contact-status.status-hidden {
  opacity: 0;
  max-height: 0;
  margin-bottom: 0;
}

.chat-contact-status .emoji {
  width: 17px;
  height: 17px;
  vertical-align: -3px;
}

#chat {
  display: flex;
  flex-direction: column;
  height: calc(100vh - var(--chrome-h));
  overflow: hidden;
}

#chat .chat-header {
  flex-shrink: 0;
}

.chat-messages {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  overflow-y: scroll;
  overflow-x: hidden;
  /* Use flex: 1 to fill remaining space between header and chat-box */
  flex: 1;
  min-height: 0;
  padding-left: 10px;
  padding-right: 10px;
  /* Push scrollable content past the 60px fixed `.chat-header`. Putting the
   * clearance on the container (not the first child) guarantees the top of
   * the message list always lands below the header regardless of what
   * ends up as first-child or what rebuild pass ran last. */
  padding-top: 75px;
  font-family: 'Rubik';

  overscroll-behavior-y: contain;

  /* DOM windowing owns scroll-position math (manual scrollTop corrections on
     window slide). Chromium auto-anchors scroll on DOM mutation, WebKit does
     not — disabling it gives engine parity and avoids double-correction. */
  overflow-anchor: none;
}

.chat-messages > *:last-child {
  /* The last child should have extra margin from the bottom — covers
     both `.dmsg` divs and `.msg-inline-timestamp` paragraphs (system
     events, date dividers, unread marker). */
  margin-bottom: 20px;
}

/* CONTRACT for any OVERLAY added as a direct child of `.chat-messages`
   (hover toolbar, swipe-reply chip, future jump-to-bottom, typing dot, …):
     1. Insert it FIRST (prepend / before the first row), never append — an
        out-of-flow overlay landing as `:last-child` steals the 20px above
        from the real last message.
     2. Out-specify `.chat-messages > *` (the themed-bg layering rule sets
        `position: relative; z-index: 1` on every child). Use an id
        (`#dmsg-toolbar`) or `.chat-messages > .your-class`, and a z-index
        above 1, or themes knock the overlay out of position and behind rows.
   In-flow CONTENT (rows, separators, system events) wants the default
   `.chat-messages > *` treatment and is exempt. */

/* Chat background toggle */
body.chat-bg-disabled .chat-messages::before {
  display: none;
}

.msg-inline-timestamp {
  width: 100%;
  font-size: 12px;
  opacity: 0.7;
  /* Symmetric top + bottom so the divider is centered between its neighbours (flex column =
   * margins sum, never collapse). The following row's top-margin is zeroed below. */
  margin: var(--dmsg-system-gap) 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* A divider owns the gap on BOTH sides — kill the next row's streak/continuation top-margin so it
 * doesn't stack on the divider's bottom margin and re-introduce the lop-sidedness. The `.chat-messages >`
 * prefix lifts specificity to (0,3,0) so it beats `.dmsg[data-streak="…"]` (0,2,0) regardless of order. */
.chat-messages > .msg-inline-timestamp + .dmsg,
.chat-messages > .msg-inline-timestamp + .msg-inline-timestamp { margin-top: 0; }
.msg-inline-timestamp::before,
.msg-inline-timestamp::after {
  content: '';
  flex: 1;
  height: 1px;
  background: rgba(255, 255, 255, 0.1);
}
.msg-inline-timestamp::before { margin-right: 12px; }
.msg-inline-timestamp::after  { margin-left: 12px; }

/* Clickable user name in a system event ("<name> has joined") — opens the same
   mini-profile as a chat name/avatar tap. The whole name+suffix text is the hit area;
   the name is a touch bolder than the dim line, brightening + underline on hover. */
.system-event-text {
  cursor: pointer;
}
.system-event-avatar {
  /* inline-block so the placeholder variant (a display:flex div) flows inline beside the name
     like the real <img> does, rather than breaking to its own line. */
  display: inline-block;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  object-fit: cover;
  vertical-align: middle;
  margin-right: 5px;
  position: relative;
  top: -1px;
}
.system-event-name {
  font-weight: 600;
}
.system-event-text:hover .system-event-name {
  text-decoration: underline;
  opacity: 1;
}
/* A repeat folded into the run's first line ("… 4 times"). The row stays in the
   DOM so its id remains addressable; it just takes no space. */
.msg-inline-timestamp.system-event-merged {
  display: none;
}

/* "New" divider — same flexbox shape as inline timestamps but tinted with
   the active accent. Marks the boundary between read and unread messages. */
.msg-inline-timestamp.unread-divider {
  color: var(--accent-color);
  opacity: 1;
  font-weight: 700;
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: 0.08em;
  /* Symmetric like the base divider (the following row's top-margin is zeroed). */
  margin: var(--dmsg-system-gap) 0;
}
.msg-inline-timestamp.unread-divider::before,
.msg-inline-timestamp.unread-divider::after {
  background: var(--accent-color);
  height: 1px;
  opacity: 0.6;
}

.file-ext-badge {
  position: absolute;
  bottom: 8px;
  right: 1px;
  background-color: rgba(0, 0, 0, 0.25);
  color: white;
  padding: 0px 6px;
  border-radius: 14px;
  font-size: 10px;
  font-weight: 400;
  opacity: 1;
  pointer-events: none;
  user-select: none;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

/* Inline URL Images - images loaded from URLs in chat messages */
.inline-image-container {
  display: block;
  position: relative;
  max-width: 100%;
  margin: 0;
}

.inline-image {
  max-width: 100%;
  max-height: 400px;
  height: auto;
  border-radius: 8px;
  cursor: pointer;
  transition: opacity 0.2s ease;
}

.inline-image:hover {
  opacity: 0.9;
}

/* Define --progress as animatable percentage for smooth transitions */
@property --progress {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 5%;
}

.inline-image-loading {
  --progress: 5%;
  display: inline-block;
  width: 16px;
  height: 16px;
  margin-left: 6px;
  border-radius: 50%;
  vertical-align: middle;
  /* Progress ring using conic-gradient */
  background: conic-gradient(
    rgba(255, 255, 255, 0.85) 0% var(--progress),
    rgba(255, 255, 255, 0.25) var(--progress) 100%
  );
  /* Create ring effect with inner circle mask (thin stroke) */
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #fff calc(100% - 1.5px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #fff calc(100% - 1.5px));
  animation: inline-image-spin 2s linear infinite;
}

@keyframes inline-image-spin {
  to {
    transform: rotate(360deg);
  }
}

/* Profile upload progress spinner (avatar/banner uploads) */
.profile-upload-spinner {
  --progress: 5%;
  display: inline-block;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  /* Progress ring using conic-gradient */
  background: conic-gradient(
    rgba(255, 255, 255, 0.85) 0% var(--progress),
    rgba(255, 255, 255, 0.25) var(--progress) 100%
  );
  /* Create ring effect with inner circle mask (thin stroke) */
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 2.5px), #fff calc(100% - 2px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 2.5px), #fff calc(100% - 2px));
  animation: inline-image-spin 2s linear infinite;
}
/* Community avatar upload ring: same accent color as the attachment upload/download ring
   (var(--icon-color-primary)), keeping the thin small-ring proportions above. */
.community-upload-ring {
  background: conic-gradient(
    var(--icon-color-primary) 0% var(--progress),
    rgba(255, 255, 255, 0.2) var(--progress) 100%
  );
}



.msg-payment {
  display: flex;
  padding: 10px;
  border-radius: 16px;
  margin-top: 10px;
  border-style: solid;
  border-width: 1px;
}

.msg-payment img {
  height: 50px;
  margin-top: auto;
  margin-bottom: auto;
  margin-right: 10px;
}

.msg-payment button {
  margin-left: auto;
  background-color: rgba(0, 0, 0, 0.4);
}

/* The `highlightMessage` animation is defined in `/themes/*` */

/* Used for "New" elements, like new incoming messages, or other new elements added to a list.
   Grown from the bottom-left corner rather than slid up from below: a transformed box
   counts toward its scroller's scroll height, and one that starts below its own slot
   stretches the chat past its last message for the length of the animation, which the
   bottom hold chases and then gives back a pixel a frame. */
.new-anim {
  transform-origin: bottom left;
  animation: SlideIn 0.75s cubic-bezier(.46,.03,.52,.96) forwards;
}

@keyframes SlideIn {
  0% { opacity: 0; transform: translateX(-100px) scale(0); }
  100% { opacity: inherit; transform: translateX(0px) scale(1); }
}

/* Used for "Expiring" elements, like finished statuses, etc */
.slideout-anim {
  overflow: hidden; /* Prevents content from spilling out during animation */
  animation: SlideOut 0.75s cubic-bezier(.46,.03,.52,.96) forwards;
}

@keyframes SlideOut {
  0% {
    opacity: 1; /* Starting with full opacity */
    height: var(--initial-height, auto); /* Starting with original height */
    transform: translateY(0) scale(1);
  }
  100% {
    opacity: 0; /* Fade out completely */
    height: 0; /* Collapse to zero height */
    transform: translateY(-25px) scale(0);
    margin-top: 0; /* Optional: remove margins too */
    margin-bottom: 0;
    padding-top: 0; /* Optional: remove padding too */
    padding-bottom: 0;
  }
}

.favicon {
  height: 15px;
  width: auto;
  margin-right: 8px;
  position: relative;
  top: 1px;
}

/* Reaction hover summary (desktop, ~500ms). Right-click expands into the full
   .reaction-details-popup below. */
.reaction-hover-tip {
  position: fixed;
  z-index: 1001;
  pointer-events: none;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  background: #1a1a1a;
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.4);
  font-size: 13px;
  color: rgba(255,255,255,0.92);
  max-width: 320px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  animation: popupFadeIn 0.15s ease-out;
}

.reaction-hover-tip-emoji {
  display: inline-flex;
  align-items: center;
}
.reaction-hover-tip-emoji img.emoji,
.reaction-hover-tip-emoji img.reaction-custom-emoji {
  width: 16px;
  height: 16px;
  margin: 0;
}

.reaction-hover-tip-text {
  overflow: hidden;
  text-overflow: ellipsis;
}

.reaction-hover-tip-hint {
  color: rgba(255,255,255,0.4);
  font-size: 11px;
  margin-left: 4px;
  border-left: 1px solid rgba(255,255,255,0.1);
  padding-left: 8px;
  flex-shrink: 0;
}

/* Mini profile popup — Discord-style preview when an avatar/name is clicked */
.mini-profile-backdrop {
  /* Dim the rest of the UI so the popup reads as a focused modal. Click is
     intercepted in JS to dismiss. */
  position: fixed;
  inset: 0;
  z-index: 999;
  background: rgba(0,0,0,0.55);
  animation: popupFadeIn 0.18s ease-out;
}

.mini-profile-popup {
  position: fixed;
  z-index: 1000;
  width: 280px;
  max-height: calc(100vh - 32px);
  background: #1a1a1a;
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 12px;
  box-shadow: 0 10px 36px rgba(0,0,0,0.55);
  overflow: hidden;
  animation: popupFadeIn 0.18s ease-out;
  display: flex;
  flex-direction: column;
}

.mini-profile-banner {
  height: 60px;
  /* Soft theme-tinted fallback gradient (visible when the profile has no
   * custom banner). The accent fades into near-black so it reads as a
   * subtle wash rather than a loud color block. */
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--icon-color-primary, #59fcb3) 18%, #1a1a1a) 0%,
    #1a1a1a 100%);
  flex-shrink: 0;
  position: relative;
  overflow: hidden;
  /* Banner is decoration — text-select / drag would lift the image off the popup. */
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
}
.mini-profile-banner img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  -webkit-user-drag: none;
  pointer-events: none;
}

.mini-profile-avatar {
  position: absolute;
  top: 28px; /* banner=60px, avatar=64px → bottom of avatar 28+64=92 sits 32px below banner */
  left: 12px;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  border: 3px solid #1a1a1a;
  background: #1a1a1a;
  overflow: hidden;
  z-index: 2;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  transition: transform 0.1s ease;
}
.mini-profile-avatar:hover {
  transform: scale(1.04);
}
.mini-profile-avatar:active {
  transform: scale(0.98);
}
.mini-profile-avatar img,
.mini-profile-avatar .placeholder-avatar {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
  display: block;
  -webkit-user-drag: none;
  pointer-events: none;
}

.mini-profile-body {
  padding: 38px 12px 12px; /* top leaves room for the overlapping avatar */
  display: flex;
  flex-direction: column;
  gap: 5px;
  overflow-y: auto;
}

.mini-profile-name {
  font-size: 16px;
  font-weight: 700;
  color: #fff;
  word-break: break-word;
  line-height: 1.2;
}
/* Twemoji-injected emoji images render at natural size by default — constrain
   them to the local font-size so a palm-tree username doesn't blow up the box. */
.mini-profile-name img.emoji {
  width: 18px;
  height: 18px;
  vertical-align: -3px;
}
/* Bot indicator beside the name — inline so it flows with the text.
   Reset the base `.icon` absolute position + margin:auto. */
.mini-profile-bot-icon {
  position: relative;
  width: 14px;
  height: 14px;
  margin: 0 0 0 5px;
  display: inline-block;
  vertical-align: -2px;
  background-color: var(--icon-color-primary, #59fcb3);
}
.mini-profile-about img.emoji {
  width: 14px;
  height: 14px;
  vertical-align: -2px;
}
.mini-profile-status-text img.emoji {
  width: 13px;
  height: 13px;
  vertical-align: -2px;
}
.mini-profile-name-loading {
  color: rgba(255,255,255,0.4);
  font-weight: 500;
}

.mini-profile-sub {
  font-size: 11px;
  color: rgba(255,255,255,0.45);
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  margin-top: -10px; /* tuck it close under the name */
}

.mini-profile-about {
  font-size: 12.5px;
  color: rgba(255,255,255,0.78);
  line-height: 1.4;
  white-space: pre-wrap;
  word-break: break-word;
  /* Match the full profile screen — bio is centered. */
  text-align: center;
  /* Clamp long bios so the popup never grows unbounded. */
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
  /* Vertical padding gives inline mention pills clip-safe room — the clamp's
     overflow:hidden would otherwise shear the chip's top/bottom flat. The 3px
     top replaces the old margin so the gap above is unchanged. */
  padding: 3px 2px;
}

/* An inline chip's background paints the font's content area (plus padding),
   which outgrows the bio's 1.4 line-height and overlaps consecutive mention
   lines. inline-block + a sub-line line-height bounds the pill's paint box
   deterministically, immune to font metrics. */
.mini-profile-about .mention {
  padding-top: 0;
  padding-bottom: 0;
  display: inline-block;
  line-height: 1.2;
}

/* Status pill sits in the banner empty space to the right of the avatar.
   Absolute positioning + a translucent dark pill so it stays readable over
   any banner image. */
.mini-profile-status {
  position: absolute;
  /* avatar is at left:12 + width 64 + 3px border = ends at ~82px */
  left: 84px;
  right: 10px;
  top: 50px;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 9px;
  background: rgba(0,0,0,0.55);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 12px;
  font-size: 11.5px;
  color: rgba(255,255,255,0.92);
  max-width: calc(100% - 94px);
  width: fit-content;
}
.mini-profile-status-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #59fcb3;
  flex-shrink: 0;
}
.mini-profile-status-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.mini-profile-actions {
  display: flex;
  gap: 6px;
  margin-top: 4px;
}
.mini-profile-action {
  flex: 1;
  padding: 6px 10px;
  border-radius: 7px;
  border: 1px solid rgba(255,255,255,0.08);
  background: rgba(255,255,255,0.04);
  color: #fff;
  font-size: 12.5px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s ease, transform 0.05s ease;
}
.mini-profile-action:hover {
  background: rgba(255,255,255,0.10);
}
.mini-profile-action:active {
  transform: scale(0.98);
}
.mini-profile-action-primary {
  background: var(--icon-color-primary);
  border-color: transparent;
  color: #000;
}
.mini-profile-action-primary:hover {
  background: var(--icon-color-primary);
  filter: brightness(1.08);
}

/* A member's roles in the community the popup was opened within. A removable pill
   trades its dot for a cross on hover, so taking a role off is one press. */
.mini-profile-section-label {
  margin-top: 4px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.45);
  user-select: none;
  -webkit-user-select: none;
}
.mini-profile-roles {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  user-select: none;
  -webkit-user-select: none;
}
.mini-profile-role {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  max-width: 100%;
  height: 22px;
  padding: 0 8px 0 6px;
  border-radius: 6px;
  background: rgba(255,255,255,0.05);
  border: 1px solid rgba(255,255,255,0.07);
  font-size: 12px;
  font-weight: 500;
  color: rgba(255,255,255,0.88);
  box-sizing: border-box;
  transition: opacity 0.15s ease;
}
.mini-profile-role.is-busy {
  opacity: 0.45;
  animation: miniProfileRolePulse 1s ease-in-out infinite alternate;
}
@keyframes miniProfileRolePulse { to { opacity: 0.25; } }
.mini-profile-role-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mini-profile-role-hash {
  margin-right: 1px;
  color: rgba(255,255,255,0.4);
}
.mini-profile-role-dot {
  position: relative;
  flex-shrink: 0;
  width: 10px;
  height: 10px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--tint);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.15);
  transition: transform 0.12s ease;
}
button.mini-profile-role-dot { cursor: pointer; }
.mini-profile-role-dot .icon {
  width: 100%;
  height: 100%;
  background-color: #1a1a1a;
  opacity: 0;
  transition: opacity 0.12s ease;
}
.mini-profile-role.is-removable:hover .mini-profile-role-dot .icon { opacity: 1; }
.mini-profile-role.is-removable:hover .mini-profile-role-dot { transform: scale(1.2); }
.mini-profile-role.is-owner .icon {
  position: relative;
  width: 11px;
  height: 11px;
  margin: 0;
  background-color: #fce459;
}
.mini-profile-role.is-owner .mini-profile-role-name { color: #fce459; }
.mini-profile-role-add {
  position: relative;
  width: 22px;
  height: 22px;
  padding: 0;
  border-radius: 6px;
  border: 1px dashed rgba(255,255,255,0.18);
  background: transparent;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.mini-profile-role-add .icon {
  width: 12px;
  height: 12px;
  background-color: rgba(255,255,255,0.6);
}
.mini-profile-role-add:hover {
  background: rgba(255,255,255,0.08);
  border-color: rgba(255,255,255,0.3);
}
.mini-profile-role-add:hover .icon { background-color: #fff; }
.mini-profile-role-add:disabled,
button.mini-profile-role-dot:disabled { cursor: default; }

/* Mobile / no-anchor fallback: centered modal */
.mini-profile-popup.mini-profile-centered {
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: min(340px, calc(100vw - 32px));
}

/* Reaction Details Popup (long-press on reaction badge) */
.reaction-details-popup {
  position: fixed;
  z-index: 1000;
  -webkit-user-select: none;
  user-select: none;
  background: #1a1a1a;
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 12px;
  padding: 4px 0;
  min-width: 180px;
  max-width: 320px;
  max-height: 250px;
  overflow-y: auto;
  box-shadow: 0 8px 32px rgba(0,0,0,0.5);
  animation: popupFadeIn 0.2s ease-out;
}

.reaction-details-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px 8px;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  margin-bottom: 4px;
}

.reaction-details-emoji {
  display: flex;
  align-items: center;
}

.reaction-details-emoji img.emoji,
.reaction-details-emoji img.reaction-custom-emoji {
  width: 18px;
  height: 18px;
  margin: 0;
}

.reaction-details-label {
  font-size: 13px;
  color: rgba(255,255,255,0.45);
  /* Size to content; let it shrink + ellipsis when the popup hits max-width. */
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.reaction-detail-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 14px;
  cursor: default;
}

.reaction-detail-row:hover {
  background: linear-gradient(90deg, rgba(255,255,255,0.04), transparent);
}

.reaction-detail-row .reaction-avatar {
  width: 25px;
  height: 25px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

.reaction-detail-name {
  font-size: 13px;
  color: #e0e0e0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Edit History Popup */
.edit-history-popup {
  position: fixed;
  z-index: 1000;
  background: #1a1a1a;
  border-radius: 12px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
  min-width: 280px;
  max-width: 400px;
  max-height: 300px;
  overflow: hidden;
  animation: popupFadeIn 0.3s ease-out;
}

@keyframes popupFadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

.edit-history-content {
  padding: 8px;
  overflow-y: auto;
  max-height: 280px;
}

.edit-history-entry {
  padding: 8px 10px;
  background: #0f0f0f;
  border-radius: 6px;
  border-left: 3px solid #555;
  opacity: 0;
  animation: entryFadeIn 0.2s ease-out forwards;
  overflow: hidden;
}

@keyframes entryFadeIn {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}

.edit-history-entry + .edit-history-entry {
  margin-top: 6px;
}

.edit-history-entry.original {
  border-left-color: #666;
}

.edit-history-entry.current {
  border-left-color: var(--icon-color-primary);
}

.edit-history-time {
  font-size: 10px;
  color: rgba(255, 255, 255, 0.4);
  line-height: 1.2;
}

.edit-history-label {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: rgba(255, 255, 255, 0.3);
  margin-left: 6px;
}

.edit-history-text {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.85);
  word-wrap: break-word;
  overflow-wrap: break-word;
  word-break: break-word;
  line-height: 1.3;
  margin-top: 2px;
}

/* Scale custom/twemoji to the text run (mirrors `.dmsg .emoji`) — without this
   the natural image size blows the row up. */
.edit-history-text .emoji {
  width: 1.15em;
  height: 1.15em;
  vertical-align: -0.2em;
  display: inline-block;
}


#chat-box {
  width: 100%;
  flex-shrink: 0;
  padding-top: 5px;
  padding-bottom: 5px;
  position: relative;  /* anchor for #msg-bottom-fade hanging above */
}

/* ===== Reply bar (Discord-style, docks onto the composer's top edge) =====
   The bar is absolutely positioned and permanently tucked 12px BEHIND the
   composer's top edge; opening grows the box's padding-top (layout) while the
   bar's height grows on the identical curve (pixels), so it slides out from
   underneath the composer. The composer's rounded corners are always backed
   by the bar's surface while any of it shows, and never need to animate:
   both the themed-gap streak and the square-corner streak are impossible. */
#chat-box {
  transition: padding-top 0.22s cubic-bezier(0.33, 1, 0.68, 1);
}

#chat-box.replying {
  padding-top: 39px;   /* base 5px + the bar's 34px visible strip */
}

#chat-reply-bar {
  position: absolute;
  box-sizing: border-box;  /* height includes the tuck inset, so content
                              centers in the visible strip, not the full box */
  top: 5px;            /* rides the box's top edge (its base padding) */
  left: 8px;           /* matches the composer's side margins */
  right: 8px;
  height: 12px;        /* fully hidden behind the composer when closed */
  z-index: 0;          /* the composer paints over the tucked strip */
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 44px 12px 12px;  /* bottom inset = the tucked strip, so content
                                 centers in the visible part only; right inset
                                 clears the absolutely-anchored cancel button */
  -webkit-user-select: none;
  user-select: none;
  cursor: pointer;   /* the bar taps through to the replied-to message */
  background-color: #0d0d0d;
  background-color: color-mix(in srgb, var(--accent-color, #59fcb3) 6%, #060606);
  border-radius: 10px 10px 0 0;
  overflow: hidden;
  transition: height 0.22s cubic-bezier(0.33, 1, 0.68, 1);
}

#chat-box.replying #chat-reply-bar {
  height: 46px;        /* 34px visible + the 12px tuck */
}

/* Fade the CONTENT, never the bar surface: a translucent bar mid-collapse
   would let the themed composer background bleed through where the bar
   hasn't finished shrinking yet */
#chat-reply-bar::before,
#chat-reply-bar > * {
  opacity: 0;
  transition: opacity 0.18s ease;
}

#chat-box.replying #chat-reply-bar::before,
#chat-box.replying #chat-reply-bar > * {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  #chat-box,
  #chat-reply-bar,
  #chat-reply-bar::before,
  #chat-reply-bar > * {
    transition: none;
  }
}

#chat-reply-bar::before {
  content: '';
  width: 3px;
  height: 16px;
  border-radius: 2px;
  background-color: var(--accent-color, #59fcb3);
  flex-shrink: 0;
}

#chat-reply-bar-label {
  color: rgba(255, 255, 255, 0.85);
  font-size: 13.5px;
  white-space: nowrap;
  flex-shrink: 0;
}

#chat-reply-bar-name {
  color: var(--accent-color, #59fcb3);
  font-weight: 600;
}

#chat-reply-bar-snippet {
  flex: 1;
  min-width: 0;
  color: rgba(255, 255, 255, 0.45);
  font-size: 13px;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Twemoji + custom emoji must render at text size in the one-line bar
   (same constraint as .chatlist-contact-preview .emoji) */
#chat-reply-bar-name img,
#chat-reply-bar-snippet img {
  width: 15px !important;
  height: 15px !important;
  display: inline-block;
  vertical-align: -2px;
  border-radius: 0;
  min-width: unset;
  min-height: unset;
}

/* Anchored to the bar, not in its flex flow: the mic/send buttons below
   flex-shrink with window width, so the exact right-offset that centers the
   X over them is measured in JS each time the bar opens */
#chat-reply-bar-cancel {
  position: absolute;
  right: 10.5px;                   /* fallback until the live measure runs */
  top: calc((100% - 12px) / 2);    /* center of the visible strip */
  transform: translateY(-50%);
  width: 24px;
  height: 24px;
  padding: 3px;
  background: transparent;
  border: none;
  cursor: pointer;
}

#chat-reply-bar-cancel .icon-cancel {
  position: relative;
  display: block;
  width: 18px;
  height: 18px;
  margin: 0;
  background-color: var(--danger-color);
  transition: filter 0.15s ease;
}

#chat-reply-bar-cancel:hover .icon-cancel {
  filter: brightness(1.25);
}

.chat-input-container {
  position: relative;
  width: 100%;
  align-items: center;
  background-color: #030303;
  height: 45px;
  margin-top: auto;
  margin-bottom: auto;
  margin-left: 8px;
  margin-right: 8px;
  border-radius: 10px;
}

/* Chat box specific: allow container to grow with textarea */
#chat-box .chat-input-container {
  height: auto;
  min-height: 45px;
  align-items: flex-start;
  /* An unwrapped command-param's intrinsic width must never become this
     row's minimum (percentage max-widths don't count in min-content math) —
     without this the row overflows its side margins over the themed border. */
  min-width: 0;
}

.chat-input-container button {
  backdrop-filter: none;
  width: 45px;
  height: 45px;
  position: relative;
  background-color: transparent;
  padding: 10px;
}

#chat-input-file .icon {
  transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1),
              background-color 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

#chat-input-file.open .icon {
  transform: rotate(45deg);
  background-color: #B2B2B2;
}

/* Standard composer icons: muted by default, full white on hover. Stateful
   colors (send .active accent, voice-preview accent, edit-cancel danger) are
   declared later or more specifically, so they keep winning. The file button
   is color-only here: its own rule above carries the rotate transition, and
   redeclaring transition would clobber the spin. */
#chat-input-file .icon,
#chat-input-emoji .icon,
#chat-input-voice .icon,
#chat-input-send .icon {
  background-color: #B2B2B2;
}

#chat-input-emoji .icon,
#chat-input-voice .icon,
#chat-input-send .icon {
  transition: background-color 0.15s ease;
}

#chat-input-file:hover .icon,
#chat-input-emoji:hover .icon,
#chat-input-voice:hover .icon,
#chat-input-send:hover .icon {
  background-color: #FFFFFF;
}

/* Legacy fallback: the plain <textarea>, used when the rich composer is turned
   off in Display settings or refuses to construct. Verbatim from 6eddd0b8. */
textarea#chat-input {
  /* It used to be a direct flex child of the row; it now sits inside
     #chat-input-host. An inline-block on the baseline leaves the line box's
     descender gap under it (7px), which pushed the row from 47 to 54 and made
     the box look top-heavy. Block takes it out of inline layout. */
  display: block;
  width: -webkit-fill-available;
  line-height: inherit;
  resize: none;
  overflow: hidden;
  /* Collapsed box = one full 24px line + balanced padding, 45px total to match
     the row's buttons. The content box MUST fit the line: a shorter box leaves
     hidden overflow, and a textarea scrolls its own overflow on caret moves and
     drags even under overflow:hidden — the text rides 0-4px off center. */
  height: 24px;
  max-height: 150px;
  padding: 10.5px 10px 10.5px 0;
  background: transparent !important;
}

textarea#chat-input::placeholder {
  -webkit-user-select: none;
  user-select: none;
}

/* A div has no intrinsic width to fill the row the way the textarea did, so it
   has to claim the space as a flex item. Basis 0, NOT auto: an auto basis is the
   CONTENT width, so the box would grow with every character typed and squeeze
   the buttons beside it. Basis 0 makes it take the leftover space and wrap. */
#chat-input-host {
  flex: 1 1 0;
  min-width: 0;
}

/* The row's controls keep their size; only the composer flexes. 38px is what
   they actually rendered at before: the declared 45px never applied, because the
   old textarea claimed fill-available and flex shrank every button to pay for it.
   Pinned rather than left to shrink so the row can't resize as you type. */
.chat-input-container > button {
  flex-shrink: 0;
  width: 38px;
}

/* Pin the glyphs too, like the send button already does. The base `.icon` is 65%
   of its button, so icon size tracked button WIDTH and rode that same shrink
   down to 23px. Sizing it here decouples the two, so no later change to how the
   row flexes can resize an icon again. */
#chat-input-file .icon,
#chat-input-emoji .icon,
#chat-input-voice .icon {
  width: 23px;
  height: 23px;
}

#chat-input.rich-composer {
  /* Typographic parity with a message row, measured from `.dmsg-text`:
     Rubik 15px/400 on a 21px line. The composer used to be Inter 16px because a
     textarea inherits the document font while `.dmsg` overrides to Rubik, so
     what you typed never matched what you'd sent. Padding rises to 12px to keep
     the collapsed row at 45px (21 + 24) now that the line is 3px shorter. */
  font-family: Rubik, sans-serif;
  font-size: 15px;
  font-weight: 400;
  line-height: 21px;
  min-height: 21px;
  position: relative;
  width: -webkit-fill-available;
  outline: none;
  /* The row centres its buttons; a textarea's UA style blocked that from
     reaching the text, an editable div inherits it. */
  text-align: start;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  /* A contenteditable sizes itself, so the collapsed box is a MIN of one full
     24px line + balanced padding (45px total, matching the row's buttons) and
     it grows to the cap on its own. */
  max-height: 150px;
  overflow-y: auto;
  padding: 12px 10px 12px 0;
  background: transparent !important;
  /* Scroll anchoring inside an editable fights the caret on WKWebView. */
  overflow-anchor: none;
}

/* Absolute, not in-flow: an inline ::before is content the caret sits AFTER,
   which parks the cursor mid-box on an empty composer. Generic: every composer
   instance (chat + Status mini) shares the placeholder mechanics. */
.rich-composer[data-empty="1"]::before {
  content: attr(data-placeholder);
  position: absolute;
  left: 0;
  top: 12px;
  color: rgba(255, 255, 255, 0.38);
  pointer-events: none;
  -webkit-user-select: none;
  user-select: none;
}

/* Locked (a dissolved community, a blocked contact): the attach button is gone,
   so the reason needs an inset of its own. Set on the placeholder too, which is
   absolute and would otherwise ignore the padding and sit against the border. */
#chat-input.rich-composer[data-locked] {
  padding-left: 15px;
  cursor: default;
}
#chat-input.rich-composer[data-locked][data-empty="1"]::before {
  left: 15px;
}

/* Decorated source: markers stay visible but recede, so what you type is what
   gets sent and the caret has somewhere to go inside them. */
#chat-input.rich-composer .cmp-mark { opacity: 0.4; }
#chat-input.rich-composer .cmp-bold .cmp-body { font-weight: 700; }
/* Matches `.chat em` so italics read the same weight of grey as in a message. */
#chat-input.rich-composer .cmp-italic .cmp-body { font-style: italic; opacity: 0.7; }
#chat-input.rich-composer .cmp-bolditalic .cmp-body { font-weight: 700; font-style: italic; }
#chat-input.rich-composer .cmp-h1 .cmp-body,
#chat-input.rich-composer .cmp-h2 .cmp-body,
#chat-input.rich-composer .cmp-h3 .cmp-body,
#chat-input.rich-composer .cmp-h4 .cmp-body,
#chat-input.rich-composer .cmp-h5 .cmp-body,
#chat-input.rich-composer .cmp-h6 .cmp-body { font-weight: 700; }
#chat-input.rich-composer .cmp-h1 .cmp-body { font-size: 1.3em; }
#chat-input.rich-composer .cmp-h2 .cmp-body { font-size: 1.2em; }
#chat-input.rich-composer .cmp-h3 .cmp-body { font-size: 1.1em; }
#chat-input.rich-composer .cmp-subtext .cmp-body { font-size: 0.8em; opacity: 0.75; }
#chat-input.rich-composer .cmp-strike .cmp-body { text-decoration: line-through; }
#chat-input.rich-composer .cmp-spoiler .cmp-body {
  background: rgba(255, 255, 255, 0.16);
  border-radius: 3px;
}
#chat-input.rich-composer .cmp-code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.92em; }
#chat-input.rich-composer .cmp-code .cmp-body {
  background: rgba(255, 255, 255, 0.08);
  border-radius: 3px;
  padding: 1px 2px;
}
/* Now that the composer is the same Rubik 15px as a message row, the in-chat
   `.mention` weight of 500 matches exactly — no optical compensation needed. */
#chat-input.rich-composer .cmp-mention {
  color: var(--icon-color-primary, #59fcb3);
  background: rgba(89, 252, 179, 0.13);
  border-radius: 4px;
  padding: 0 2px;
  font-weight: 500;
  /* A caret parked at the pill's edge would otherwise take the pill's colour
     and read as "inside" a protected token. It's ordinary editable text. */
  caret-color: var(--text-color);
}
/* Both emoji kinds share one geometry, derived rather than eyeballed. Measured
   against the text baseline at 16px/24px: the text content box runs -16 to +6
   (centre -5) and the line box -17 to +7. A 1.25em glyph dropped 0.3em below the
   baseline spans -15.2 to +4.8 — centred on the text within 0.2px, and inside
   the line box, so adding an emoji cannot grow the row. The old 1.35em custom
   emoji reached -18.47 and pushed the line box to 24.48px, which is why the
   composer jumped a pixel the first time you typed one. */
/* The widget IS the <img> (no wrapper span — see composer.js on old-Android
   IME resets), so geometry and sizing live on one element. */
.rich-composer img.cmp-twemoji,
.rich-composer img.cmp-emoji {
  display: inline-block;
  vertical-align: -0.3em;
  width: 1.25em;
  height: 1.25em;
  object-fit: contain;
}

/* Vector Send Button - muted by default, stays visible when active */
#chat-input-send .icon {
  background-color: #B2B2B2;
  transition: background 0.3s ease;
  width: 20px !important;
  height: 20px !important;
}

#chat-input-send.active .icon {
  background-color: var(--icon-color-primary);
  opacity: 1;
}

/* Button swap animations for mic/send button transition */
.button-swap-out {
  animation: swapOut 0.15s ease-out forwards;
}

.button-swap-in {
  animation: swapIn 0.2s ease-out forwards;
}

@keyframes swapOut {
  0% {
    opacity: 1;
    transform: scale(1) rotate(0deg);
  }
  100% {
    opacity: 0;
    transform: scale(0.5) rotate(-90deg);
  }
}

@keyframes swapIn {
  0% {
    opacity: 0;
    transform: scale(0.5) rotate(90deg);
  }
  100% {
    opacity: 1;
    transform: scale(1) rotate(0deg);
  }
}

.emoji-button {
  font-size: 30px;
  padding: 0;
  padding-left: 8px; /* Reduced padding */
  padding-right: 8px; /* Reduced padding */
  background-color: transparent;
}

.emoji-picker {
  position: fixed;
  bottom: 65px;
  left: 50%;
  transform: translateX(-50%) translateY(100%);
  /* Suppress text/element selection across the whole panel so drag-to-
     reorder doesn't paint a selection trail across nearby chrome.
     <input> elements opt back in via the rule below. */
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  /* Solid bg, not backdrop-filter: blur(...). Any blur here forces
     WKWebView to re-rasterise the entire panel offscreen every frame
     when an animated child is visible — pinned the main thread at 99%
     paint with 50+ animated WebP pack emojis on screen. */
  background: rgb(14, 14, 14);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 12px;
  padding: 0;
  z-index: 2000;
  width: 90%;
  max-width: 400px;
  height: 350px;
  box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.5);
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease;
  pointer-events: none;
  opacity: 0;
  flex-direction: column;
  overflow: hidden;
  display: flex;
}

.emoji-picker.visible {
  transform: translateX(-50%) translateY(0);
  pointer-events: auto;
  opacity: 1;
}

.emoji-picker:has(.emoji-pack-creator-error:not([hidden])) {
  border: none;
}

/* Wide screens: anchor the picker to the right edge of the viewport, Discord-style.
 * Narrow screens stay centered (the centered position above) so the panel still
 * fits comfortably without crowding the input. */
@media (min-width: 700px) {
  .emoji-picker {
    left: auto;
    right: 16px;
    transform: translateY(100%);
  }
  .emoji-picker.visible {
    transform: translateY(0);
  }

  /* ...but that anchor belongs to the composer it sits under. Summoned from a
     centred dialog (Status), the picker has no input on the right edge to hug,
     so it lands in the far corner away from the field it's filling — recentre. */
  .emoji-picker.emoji-picker-status-mode {
    left: 50%;
    right: auto;
    transform: translateX(-50%) translateY(100%);
  }
  .emoji-picker.emoji-picker-status-mode.visible {
    transform: translateX(-50%) translateY(0);
  }
}

.emoji-picker-message-type {
  /* Keep all borders visible for message type */
  border: 1px solid rgba(73, 73, 73, 1);
  border-radius: 12px;
}

.emoji-picker-content {
  display: flex;
  flex: 1;
  overflow: hidden;
  position: relative;
  height: calc(100% - 50px);
  background-color: #0a0a0a;
}

.emoji-sidebar {
  width: 50px;
  padding: 8px 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  background-color: #202020;
  border-top: 4px solid var(--icon-color-primary);
  /* Scroll the rail when packs overflow rather than squishing the icons.
     Scrollbar hidden — the rail is only 50px wide. */
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: none;
}

.emoji-sidebar::-webkit-scrollbar { display: none; }

/* `[hidden]` is a UA-stylesheet rule, so ANY author `display` on a class defeats
   it and `el.hidden = true` leaves the element rendering. That silently produced a
   dead control twice (the category buttons, then the creator's delete button, which
   showed in create mode and no-op'd on click), so restore the invariant across the
   picker instead of chasing it per element. */
.emoji-picker [hidden] { display: none !important; }

.emoji-category-btn {
  position: relative;
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  margin: 3px 0;
  padding: 0;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 1;
  transition: opacity 0.2s ease;
  background: none;
  border: none;
  color: inherit;
  font-size: 16px;
  box-sizing: border-box;
}

.emoji-category-btn:hover {
  opacity:0.7;
}

.emoji-category-btn.active {
  opacity: 1;
}

/* Rectangular highlighter for active tab. Declared on the base class rather than
   `.active` so it has something to animate from — a pseudo-element that only
   exists while the class does is created and destroyed, never transitioned. */
.emoji-category-btn::before {
  content: '';
  position: absolute;
  left: -12px;
  top: 50%;
  transform: translateY(-50%) scaleY(0.35);
  width: 6px;
  height: 28px;
  background-color: var(--icon-color-primary);
  border-radius: 2px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 130ms ease-out, transform 130ms ease-out;
}

.emoji-category-btn.active::before {
  opacity: 1;
  transform: translateY(-50%) scaleY(1);
}

.emoji-category-btn .icon {
  width: 14px; 
  height: 14px; 
}

.emoji-main {
  margin-top: 0px;
  flex: 1;
  padding: 8px;
  padding-right: 5px;
  margin-left: 50px; 
  height: 280px;
  overflow-y: auto; 
  overflow-x: hidden;
  background-color: #0a0a0a;
  border-top-right-radius: 12px;
  border-bottom-right-radius: 0px;
  box-sizing: border-box;
}

.emoji-section[hidden] { display: none; }

.emoji-section {
  margin-bottom: 3px;
}

.emoji-section-header {
  padding: 4px 0;
  font-size: 14px;
  font-weight: 500;
  color: #fff;
  white-space: nowrap;
  text-align: left;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  width: 100%;
}

.emoji-section-header .section-icon {
  width: 6px;
  height: 6px;
  flex-shrink: 0;
  display: inline-block;
  position: relative;
  background-color: white;
  mask-size: contain;
  mask-repeat: no-repeat;
  mask-position: center;
  /* `.icon` base rule sets `margin: auto` for absolutely-positioned
     icons. As a flex item, those auto margins would consume free row
     space and shove the icon toward center — kill them. */
  margin: 0 8px 0 0;
}

/* `.emoji-picker` prefix outranks the legacy `.emoji-picker span
   { width:30px height:30px padding:6px border-radius:100% }` rule —
   without these resets the title <span> is forced into a 30×30 circle
   and ellipsizes to "Fav..." . */
.emoji-picker .emoji-section-header .header-text {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  width: auto;
  height: auto;
  padding: 0;
  border-radius: 0;
}

.emoji-section-header .icon-chevron-down {
  width: 6px;
  height: 6px;
  opacity: 0.5;
  transition: transform 0.2s ease, opacity 0.2s ease;
  flex-shrink: 0;
  display: inline-block;
  position: relative;
  background-color: white;
  mask-size: contain;
  mask-repeat: no-repeat;
  mask-position: center;
  /* Kill `.icon { margin: auto }` — see note on .section-icon above. */
  margin: 0 0 0 2px;
}

.emoji-section.collapsed .emoji-section-header .icon-chevron-down {
  transform: rotate(-90deg);
}

.emoji-section.collapsed .emoji-grid {
  display: none;
}

.emoji-section.collapsed .emoji-pack-canvas,
.emoji-section.collapsed canvas {
  display: none !important;
}

.emoji-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 4px;
  margin-top: 6px;
}

/* Mobile: 5 columns instead of 6 */
@media (max-width: 380px) {
  .emoji-grid {
    grid-template-columns: repeat(5, 1fr);
  }
}

.emoji-grid span {
  cursor: pointer;
  font-size: 24px;
  text-align: center;
  padding: 5px;
  border-radius: 6px;
  transition: background-color 0.15s ease;
}

.emoji-grid span:hover {
  background-color: rgba(255, 255, 255, 0.10);
  transform: scale(1.125);
}

.emoji-grid span img {
  pointer-events: none;
}

/* ============================================================================
   Custom emoji packs (NIP-30 / NIP-51)
   ============================================================================ */

.emoji-pack-tab {
  position: relative;
}

.emoji-pack-tab img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
  display: block;
}

/* Letter fallback (no logo URL): an inner plate sized like a stock icon's
   visible footprint, so the active highlight anchors identically. */
.emoji-pack-tab-letter-plate {
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  border-radius: 4px;
  background-color: #2a2a2a;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 600;
  color: #fff;
  text-transform: uppercase;
  line-height: 1;
}

/* Divider above the first pack tab to separate stock from packs. */
.emoji-pack-tab:first-of-type {
  margin-top: 10px;
}
.emoji-pack-tab:first-of-type::after {
  content: '';
  position: absolute;
  left: -6px;
  right: -6px;
  top: -7px;
  height: 1px;
  background-color: rgba(255, 255, 255, 0.08);
}

.emoji-pack-logo {
  width: 18px;
  height: 18px;
  border-radius: 4px;
  object-fit: cover;
  flex-shrink: 0;
}

.emoji-pack-section .emoji-section-header {
  gap: 6px;
}
.emoji-pack-section .emoji-section-header .emoji-pack-edit-pencil {
  margin-left: auto;
}
.emoji-pack-section .emoji-section-header .icon-chevron-down {
  margin-left: -2px;
}

/* Count chip — `.emoji-picker .…` prefix outranks the legacy
   `.emoji-picker span { width:30px ... }` so the count is an inline
   text fragment, not a forced 30×30 circle. */
.emoji-picker .emoji-pack-count {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.55);
  font-weight: 500;
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
  width: auto;
  height: auto;
  padding: 0;
  border-radius: 0;
}

.emoji-pack-section {
  /* Off-screen pack sections skip layout + paint until they intersect
     the viewport — Vector's chatlist FPS memo explicitly favours this
     over `loading="lazy"` for cached images. */
  content-visibility: auto;
  contain-intrinsic-size: 0 200px;
  /* Anchor for the canvas's per-cell tooltip (.emoji-pack-canvas-tooltip
     is absolutely positioned within the section). */
  position: relative;
}

/* Per-cell tooltip for canvas-rendered emojis. Parented to <body> +
   `position: fixed` so it escapes the section's `contain: paint`
   clip (implied by content-visibility: auto). Native HTML `title`
   doesn't refire when the cell changes under a still mouse anyway,
   so we manage it ourselves. */
.emoji-pack-canvas-tooltip {
  position: fixed;
  pointer-events: none;
  transform: translate(-50%, calc(-100% - 6px));
  padding: 4px 8px;
  background-color: rgba(20, 20, 20, 0.96);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 5px;
  color: #fff;
  font-size: 11px;
  font-weight: 500;
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.12s ease;
  z-index: 2100; /* above the picker (z-index: 2000) */
}

.emoji-pack-canvas-tooltip.is-visible {
  opacity: 1;
}

/* A section within reach of the viewport renders before it arrives (js/picker.js). */
.emoji-pack-section.cv-near {
  content-visibility: visible;
}

.emoji-pack-emoji {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 4px !important;
  border-radius: 6px;
  background-color: transparent;
  transition: background-color 0.15s ease !important;
}

.emoji-pack-emoji img {
  width: 28px;
  height: 28px;
  object-fit: contain;
  pointer-events: none;
}

/* Background-only highlight: raster `<img>` thumbnails are expensive to
   re-rasterise during transform: scale, so dropping the hover-scale that
   stock emoji grids use here saves significant paint on hover-over. */
.emoji-pack-emoji:hover {
  background-color: rgba(255, 255, 255, 0.08) !important;
  border-radius: 6px !important;
  transform: none !important;
}

#emoji-recents-grid .emoji-pack-emoji:hover {
  transform: scale(1.125) !important;
}

/* Inline custom emoji. Carries the shared `emoji` class for sizing, so this
   rule only adds the custom-specific behaviour. */
.custom-emoji-inline {
  /* Sizing + vertical-align come from the shared `.emoji` rules so custom
     emojis render identically to twemoji in every context (chat, chat list,
     reply previews, headers, profile, etc.). Only the fill behaviour is
     custom: foreign clients may publish non-square images; `fill` stretches
     them into the square emoji box rather than letting them break layout.
     Our own uploads are validated as square. */
  margin: 0;
  object-fit: fill;
}

/* Loading placeholder for cached emojis — `bindCachedEmojiImg` adds
   this class while waiting for `get_or_cache_image` to resolve. Paints
   a soft mint pulse instead of the browser's broken-img glyph. Applies
   uniformly across every render surface (inline chat emoji, reaction
   chip, preview thumb, pack tab, editor cell, modal, etc.). */
img.emoji-img-loading {
  background-color: rgba(255, 255, 255, 0.05);
  border-radius: 4px;
  animation: emoji-img-loading-pulse 1.4s ease-in-out infinite;
}

@keyframes emoji-img-loading-pulse {
  0%, 100% { background-color: rgba(255, 255, 255, 0.04); }
  50%      { background-color: rgba(255, 255, 255, 0.10); }
}

/* NIP-30 custom emoji inside a reaction chip — small enough to sit
   alongside the count, large enough to stay readable. */
.reaction-custom-emoji {
  display: inline-block;
  vertical-align: middle;
  width: 18px;
  height: 18px;
  margin-right: 2px;
  object-fit: fill;
  -webkit-user-drag: none;
  user-select: none;
  -webkit-user-select: none;
  pointer-events: none;
}

/* ============================================================================
   Pack Creator — sidebar "+" tab, in-panel view, size-error overlay
   ============================================================================ */

.emoji-pack-tab-create {
  position: relative;
  background-color: transparent;
  border: 1px dashed rgba(255, 255, 255, 0.18);
  color: rgba(255, 255, 255, 0.75);
  margin-top: 6px;
}
.emoji-pack-tab-create:hover {
  border-color: var(--icon-color-primary);
  color: var(--icon-color-primary);
}
.emoji-pack-tab-create .icon {
  width: 14px;
  height: 14px;
  background-color: currentColor;
}

/* Edit-pencil on own pack section headers. `.emoji-picker .…` prefix
   wins specificity against the legacy `.emoji-picker span { width:30px;
   border-radius:100% }` so the icon stays a tight 14×14 instead of being
   forced into a rounded 30×30 plate. */
.emoji-pack-edit-pencil {
  background: none;
  border: none;
  padding: 0 4px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  opacity: 0.6;
  flex-shrink: 0;
  transition: opacity 0.15s ease;
}
.emoji-pack-edit-pencil:hover { opacity: 1; }
.emoji-picker .emoji-pack-edit-pencil .icon {
  position: static;
  top: auto;
  left: auto;
  right: auto;
  bottom: auto;
  margin: 0;
  width: 16px;
  height: 16px;
  padding: 0;
  border-radius: 0;
  background-color: rgba(255, 255, 255, 0.9);
}

/* ============================================================================
   In-panel Pack Creator (replaces emoji sections inside .emoji-main)
   ============================================================================ */

.emoji-creator {
  padding: 4px 0 0;
  color: #fff;
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
}
.emoji-creator[hidden] { display: none; }

/* Two legacy rules collide inside the creator:
   1. `.emoji-picker span { width:30px height:30px padding:6px
      border-radius:100% }` forces every span in the picker to a circle
   2. `.icon { position:absolute top:0 left:0 right:0 bottom:0
      width:65% height:65% margin:auto }` makes every icon span fill its
      nearest positioned ancestor instead of flowing with text
   Selectors below carry an extra `.emoji-picker` prefix to outrank the
   legacy `.emoji-picker span` rule on specificity — without it we tie
   and source-order hands the legacy rule the win. */
.emoji-picker .emoji-creator span {
  width: auto;
  height: auto;
  padding: 0;
  border-radius: 0;
  display: inline-block;
}
.emoji-picker .emoji-creator .icon {
  position: static;
  top: auto;
  left: auto;
  right: auto;
  bottom: auto;
  margin: 0;
  width: 20px;
  height: 20px;
}
.emoji-picker .emoji-creator-dropzone-label {
  white-space: nowrap;
}

/* box-sizing: border-box for every descendant so per-button width/height
   declarations include padding + border (otherwise the global
   `button { padding: 0.6em 1.2em }` inflates everything past the
   declared 26×26 / 28×28 sizes). NB: this is the ONLY reset — per-button
   rules below set padding/border/border-radius themselves and would lose
   to a more specific reset block. */
.emoji-creator,
.emoji-creator *,
.emoji-creator *::before,
.emoji-creator *::after {
  box-sizing: border-box;
}

/* Header: logo + name input + count + done pencil */
.emoji-creator-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px 10px 0;
  position: sticky;
  top: 0;
  background-color: #0a0a0a;
  z-index: 2;
}
/* The scroll container (.emoji-main) has padding-top: 8px, so a sticky `top: 0` pins this header 8px
   BELOW the visible top — emoji scrolling up bleed through that strip. Cap it with an opaque band
   directly above the header (matches the panel bg, so it's invisible when the header is at rest). */
.emoji-creator-head::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  height: 12px;
  background-color: #0a0a0a;
}
.emoji-creator-logo {
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  border-radius: 6px;
  border: 1px dashed rgba(255, 255, 255, 0.18);
  background-color: rgba(255, 255, 255, 0.03);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  overflow: hidden;
  transition: border-color 0.15s ease;
}
.emoji-creator-logo:hover { border-color: var(--icon-color-primary); }
.emoji-creator-logo.has-image {
  border: none;
}
.emoji-creator-logo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.emoji-creator-logo .icon {
  width: 14px;
  height: 14px;
  background-color: rgba(255, 255, 255, 0.55);
}

.emoji-creator-name {
  flex: 1;
  min-width: 0;
  background: none;
  border: none;
  color: #fff;
  font-size: 15px;
  font-weight: 600;
  padding: 4px 0;
  font-family: inherit;
}
.emoji-creator-name:focus { outline: none; }
.emoji-creator-name::placeholder { color: rgba(255, 255, 255, 0.4); }

.emoji-creator-count {
  flex-shrink: 0;
  font-size: 11px;
  color: rgba(255, 255, 255, 0.45);
  font-variant-numeric: tabular-nums;
}

.emoji-creator-done {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-left: 4px;
}
.emoji-creator-done:hover { background-color: rgba(255, 255, 255, 0.06); }
.emoji-creator-done .icon {
  width: 14px;
  height: 14px;
  background-color: rgba(255, 255, 255, 0.7);
}
.emoji-creator-done:hover .icon { background-color: #fff; }

/* Spinner that replaces the pencil icon while the pack is publishing.
   Doesn't use a mask-image (the icon framework would tint a static
   asset); instead it's a pure-CSS conic-gradient ring matching the
   pack-details modal spinner's visual language. */
.emoji-picker .emoji-creator-done .emoji-creator-done-spinner {
  background-color: transparent;
  background-image: conic-gradient(
    var(--icon-color-primary) 0% 28%,
    rgba(255, 255, 255, 0.18) 28% 100%
  );
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 2.5px), #fff calc(100% - 2px));
          mask: radial-gradient(farthest-side, transparent calc(100% - 2.5px), #fff calc(100% - 2px));
  border-radius: 50%;
  animation: emoji-creator-done-spin 0.9s linear infinite;
}

@keyframes emoji-creator-done-spin { to { transform: rotate(360deg); } }

/* Freeze every cell while the publish is in flight: hover-shows-×,
   click-to-rename, and the pointer-driven drag-reorder all gate on
   the cell receiving pointer events. Killing those at the grid level
   prevents an edit from racing the save. The cells themselves dim
   slightly so the freeze reads visually. */
.emoji-creator-grid.is-saving .emoji-creator-cell {
  pointer-events: none;
  opacity: 0.6;
  transition: opacity 0.15s ease;
}

/* Emoji grid — same column rhythm as the regular pack grid so the
   transition between view modes doesn't shift cell width visually. */
.emoji-creator-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 4px;
  /* Top breathing room so the first row's remove badges (which sit at
     top:-4px / right:-4px) don't clip against the panel's edge. */
  padding-top: 8px;
  margin-bottom: 12px;
}
.emoji-creator-cell {
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: 8px;
  background-color: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.05);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease;
  /* `none` here forbade the browser from panning at all, so the grid could not be
     scrolled by touching a cell — which is most of the grid. `pan-y` keeps
     vertical scrolling native; the gesture arbiter flips this to `none` inline the
     moment a drag arms, which is legal because arming requires a still finger, so
     no pan has begun yet. */
  touch-action: pan-y;
}
.emoji-creator-cell.is-hovered { border-color: rgba(255, 255, 255, 0.18); }

/* The hold landed and a drag is possible — shown before any movement so the
   gesture is legible. Matches the pack rail's armed state. */
.emoji-creator-cell.is-drag-armed {
  transform: scale(1.12);
  border-color: var(--icon-color-primary);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
  transition: transform 120ms ease-out, box-shadow 120ms ease-out,
              border-color 120ms ease-out;
  z-index: 3;
}

/* Per-cell busy overlay — covers the emoji image with a scrim + ring
   while it's queued / uploading / deleting. Renders above the image
   and remove badge, never blocks the cell from re-rendering on render. */
.emoji-creator-cell-busy {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background-color: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 3;
  pointer-events: none;
}
.emoji-creator-cell-busy.is-deleting {
  background-color: rgba(60, 12, 20, 0.65);
}
.emoji-creator-cell-busy-ring {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: conic-gradient(
    rgba(255, 255, 255, 0.95) 0% 30%,
    rgba(255, 255, 255, 0.20) 30% 100%
  );
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 2.5px), #fff calc(100% - 2px));
          mask: radial-gradient(farthest-side, transparent calc(100% - 2.5px), #fff calc(100% - 2px));
  animation: emoji-creator-busy-spin 1s linear infinite;
}
.emoji-creator-cell-busy.is-pending .emoji-creator-cell-busy-ring {
  animation: none;
  opacity: 0.4;
}
.emoji-creator-cell-busy.is-deleting .emoji-creator-cell-busy-ring {
  background: conic-gradient(
    var(--danger-color) 0% 30%,
    color-mix(in srgb, var(--danger-color) 25%, transparent) 30% 100%
  );
}
@keyframes emoji-creator-busy-spin { to { transform: rotate(360deg); } }
.emoji-creator-cell.is-dragging { opacity: 0.3; }
.emoji-creator-cell-ghost {
  opacity: 0.9;
  transform: scale(0.6);
  transform-origin: center center;
  transition: none !important;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.55);
  background-color: rgba(40, 40, 40, 0.92);
}
.emoji-creator-cell.drop-before,
.emoji-creator-cell.drop-after { border-color: transparent; }
.emoji-creator-cell.drop-before::before,
.emoji-creator-cell.drop-after::after {
  content: '';
  position: absolute;
  top: 4px;
  bottom: 4px;
  width: 3px;
  border-radius: 2px;
  background-color: var(--icon-color-primary);
  box-shadow: 0 0 8px var(--icon-color-primary);
  pointer-events: none;
}
.emoji-creator-cell.drop-before::before { left: -4px; }
.emoji-creator-cell.drop-after::after  { right: -4px; }

/* Equipped-pack sidebar drag-to-reorder (vertical list). */
/* pan-y — `none` forbade the browser from panning at all, so the rail could not
   be scrolled by a finger starting on a pack tab (which is most of the rail).
   Vertical panning stays native; the gesture arbiter flips this to `none` inline
   the moment a drag ARMS — legal because arming requires a still finger, so no
   pan has begun yet (mirrors the creator grid's fix). Native image/text drag
   stays suppressed so a slow press starts our reorder, not an icon grab. */
.emoji-pack-tab {
  touch-action: pan-y;
}
.emoji-pack-tab,
.emoji-pack-tab img {
  -webkit-user-drag: none;
  user-select: none;
}
.emoji-pack-tab.is-dragging { opacity: 0.3; }

/* The hold landed and a drag is now possible. Shown before any movement so the
   gesture is legible: lift means "drag me", and letting go still just selects. */
.emoji-pack-tab.is-drag-armed {
  transform: scale(1.18);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
  transition: transform 120ms ease-out, box-shadow 120ms ease-out;
  z-index: 3;
}
.emoji-pack-tab-ghost {
  opacity: 0.9;
  transform: scale(0.85);
  transform-origin: center center;
  transition: none !important;
  border-radius: 50%;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.55);
}
.emoji-pack-tab.drop-above,
.emoji-pack-tab.drop-below { position: relative; }
/* BOTH markers ride ::after — ::before belongs to the active-tab highlighter.
   Sharing it made every un-marked tab flash the left active bar for 130ms as
   the drop line moved off it (the hijacked opacity:1 faded out through the
   highlighter's own transition). The two classes never coexist on one tab, so
   one pseudo covers both edges. */
.emoji-pack-tab.drop-above::after,
.emoji-pack-tab.drop-below::after {
  content: '';
  position: absolute;
  left: 4px;
  right: 4px;
  height: 3px;
  border-radius: 2px;
  background-color: var(--icon-color-primary);
  box-shadow: 0 0 8px var(--icon-color-primary);
  pointer-events: none;
}
.emoji-pack-tab.drop-above::after { top: -4px; }
.emoji-pack-tab.drop-below::after { bottom: -4px; }
.emoji-creator-cell img {
  width: 70%;
  height: 70%;
  object-fit: fill;
  pointer-events: none;
}

.emoji-creator-cell-remove {
  position: absolute;
  top: -4px;
  right: -6px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background-color: transparent;
  background: #0a0a0a;
  padding: 0;
  cursor: pointer;
  display: none;
  align-items: center;
  justify-content: center;
}
/* JS-managed hover state — `:hover` is unreliable in WKWebView during
   pointer-driven drags (see `mouseenter`/`mouseleave` wiring in picker.js).
   We own the `is-hovered` class so cells that the cursor crossed during
   a drag can never get stuck in an apparent hover state after drop. */
.emoji-creator-cell.is-hovered .emoji-creator-cell-remove { display: flex; }
.emoji-creator-cell-remove .icon {
  width: 10px;
  height: 10px;
  background-color: var(--danger-color);
}

/* Bottom dropzone — second affordance for uploading (mirrors mockup). */
.emoji-creator-dropzone {
  width: 100%;
  flex: 1;
  min-height: 78px;
  padding: 0;
  margin-bottom: 12px;
  background-color: rgba(255, 255, 255, 0.02);
  border: 1.5px dashed rgba(255, 255, 255, 0.18);
  border-radius: 10px;
  color: rgba(255, 255, 255, 0.7);
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 4px;
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease, color 0.15s ease;
  font-size: 13px;
  font-weight: 500;
}
.emoji-picker .emoji-creator-dropzone:hover,
.emoji-picker .emoji-creator-dropzone.is-dragover {
  border-color: var(--icon-color-primary);
  color: var(--icon-color-primary);
  background-color: rgba(89, 252, 179, 0.06);
}
.emoji-creator-dropzone.is-disabled {
  opacity: 0.4;
  pointer-events: none;
}
.emoji-creator-dropzone .icon {
  width: 22px;
  height: 22px;
  padding: 0;
  border-radius: 0;
  background-color: currentColor;
  flex-shrink: 0;
}

.emoji-creator-footer {
  display: flex;
  justify-content: flex-end;
  padding-top: 12px;
}

.emoji-creator-delete {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.emoji-creator-delete:hover {
  background-color: rgba(255, 255, 255, 0.06);
}

.emoji-creator-delete.btn:hover {
  opacity: 1;
}

.emoji-creator-delete .icon {
  width: 16px;
  height: 16px;
  background-color: var(--danger-color);
}

.emoji-creator-delete:hover .icon {
  background-color: var(--danger-color);
}

/* Confirm overlay — generalised question modal. Same in-panel chrome
   as error/naming/progress (lives inside .emoji-picker so clicks can't
   leak to the outside-close handler). Tone variants tint the background
   gradient + confirm button (`default`, `danger`). */
.emoji-pack-creator-confirm {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: rgba(8, 8, 8, 0.85);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 2px solid rgba(255, 255, 255, 0.12);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 50;
  padding: 24px;
  box-sizing: border-box;
}
.emoji-pack-creator-confirm[data-tone="danger"] {
  background:
    linear-gradient(to top, color-mix(in srgb, var(--danger-color) 22%, transparent) 0%, color-mix(in srgb, var(--danger-color) 0%, transparent) 38%),
    rgba(8, 8, 8, 0.82);
  border-color: var(--danger-color);
}
.emoji-pack-creator-confirm[hidden] { display: none; }
.emoji-pack-creator-confirm-body {
  text-align: center;
  max-width: 320px;
  width: 100%;
}
.emoji-picker .emoji-pack-creator-confirm-icon {
  display: block;
  margin: 0 auto 10px;
  width: 100px;
  height: 100px;
  padding: 0;
  border-radius: 0;
  background: none;
  object-fit: contain;
}
.emoji-pack-creator-confirm-icon[hidden] { display: none; }
.emoji-pack-creator-confirm-title {
  font-size: 18px;
  font-weight: 800;
  margin: 0 0 6px;
  color: #fff;
}
.emoji-pack-creator-confirm[data-tone="danger"] .emoji-pack-creator-confirm-title {
  color: var(--danger-color);
}
.emoji-pack-creator-confirm-detail {
  color: #fff;
  font-size: 12px;
  line-height: 1.5;
  margin: 0 0 18px;
}
.emoji-pack-creator-confirm-detail[hidden] { display: none; }
.emoji-pack-creator-confirm-actions {
  display: flex;
  gap: 8px;
  justify-content: center;
}
.emoji-pack-creator-confirm-cancel,
.emoji-pack-creator-confirm-ok {
  background-color: #171717;
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.2);
  padding: 12px 28px;
  border-radius: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  font-size: 14px;
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}

.emoji-pack-creator-confirm-ok {
  background-color: #58fcb3;
  border-color: #58fcb3;
  color: #000;
}

.emoji-pack-creator-confirm[data-tone="danger"] .emoji-pack-creator-confirm-ok {
  background-color: var(--danger-color);
  border-color: var(--danger-color);
  color: #000;
}

.emoji-pack-creator-confirm-cancel {
  border-color: var(--danger-color);
  background-color: #03030390;
}
/* Pack creator cropper overlay — square-crop UX for non-square static
   uploads. Mirrors the confirm overlay's chrome (backdrop, body, action
   buttons). Stage is a centered display rect; the crop box uses a
   massive outset box-shadow as the "darken outside" mask. */
.emoji-pack-creator-cropper {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: rgba(8, 8, 8, 0.86);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 2px solid rgba(255, 255, 255, 0.12);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 60;
  padding: 12px;
  box-sizing: border-box;
}
.emoji-pack-creator-cropper[hidden] { display: none; }
.emoji-pack-creator-cropper-body {
  text-align: center;
  width: 100%;
  max-width: 320px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.emoji-pack-creator-cropper-title {
  font-size: 16px;
  font-weight: 800;
  margin: 0;
  color: #fff;
}
.emoji-pack-creator-cropper-hint {
  font-size: 11px;
  color: rgba(255, 255, 255, 0.55);
  margin: 0;
}
.emoji-pack-creator-cropper-stage {
  position: relative;
  width: 100%;
  height: 200px;
  overflow: hidden;
  border-radius: 10px;
  background: #050505;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  /* Crosshair hints at the marquee-draw gesture for pointerdowns
     outside the current box. Box has its own `cursor: move` and the
     handles their own resize cursors — both win over this. */
  cursor: crosshair;
}
.emoji-pack-creator-cropper-img {
  position: absolute;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
}
.emoji-pack-creator-cropper-box {
  position: absolute;
  border: 2px solid #fff;
  box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.55);
  box-sizing: border-box;
  cursor: move;
  /* Disable WebView's tap-highlight + native gesture interception so
     pointermove fires cleanly during a drag. */
  -webkit-tap-highlight-color: transparent;
}
.epcc-handle {
  position: absolute;
  width: 14px;
  height: 14px;
  background: #fff;
  border-radius: 50%;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.55);
  /* Hit-target is larger than the visible dot — easier on touch. */
}
.epcc-handle::after {
  content: '';
  position: absolute;
}
/* Hit-target expands only OUTWARD from the corner. Expanding inward
   would eat the box's middle on tiny crops and steal the move-drag. */
.epcc-handle-tl { top: -7px; left: -7px; cursor: nwse-resize; }
.epcc-handle-tr { top: -7px; right: -7px; cursor: nesw-resize; }
.epcc-handle-bl { bottom: -7px; left: -7px; cursor: nesw-resize; }
.epcc-handle-br { bottom: -7px; right: -7px; cursor: nwse-resize; }
.epcc-handle-tl::after { inset: -10px 0     0     -10px; }
.epcc-handle-tr::after { inset: -10px -10px 0     0;     }
.epcc-handle-bl::after { inset: 0     0     -10px -10px; }
.epcc-handle-br::after { inset: 0     -10px -10px 0;     }
/* Live post-crop preview chip — pinned to the top-right of the stage,
   above the dark mask. Background-image trick: the full source image
   is set as the background and we offset/scale it via JS so the chip
   always renders the same square the user is about to commit. */
.epcc-preview {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 48px;
  height: 48px;
  background: #050505 center / cover no-repeat;
  border: 1px solid rgba(0, 0, 0, 0.3);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.55);
  pointer-events: none;
  z-index: 2;
}
.emoji-pack-creator-cropper-actions {
  display: flex;
  gap: 8px;
  justify-content: center;
  margin-top: 4px;
}
.emoji-pack-creator-cropper-cancel,
.emoji-pack-creator-cropper-ok {
  background-color: rgba(20, 20, 20, 0.85);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.14);
  padding: 8px 20px;
  border-radius: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  font-size: 12px;
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.emoji-pack-creator-cropper-ok {
  background-color: rgba(89, 252, 179, 0.18);
  border-color: var(--icon-color-primary);
  color: var(--icon-color-primary);
}
.emoji-pack-creator-cropper-cancel:hover { border-color: rgba(255, 255, 255, 0.28); background-color: #2a2a2a; }
.emoji-pack-creator-cropper-ok:hover     { background-color: rgba(89, 252, 179, 0.30); }
.emoji-pack-creator-cropper-ok[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

.emoji-pack-creator-confirm-cancel:active,
.emoji-pack-creator-confirm-ok:active {
  opacity: 0.8;
}

/* Progress overlay — in-panel chrome for long async ops (pack delete:
   per-emoji Blossom DELETE + Nostr tombstone). Lives inside .emoji-picker
   so clicks can't leak to the outside-close handler. Detail line is
   mutated from JS per step. */
.emoji-pack-creator-progress {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: rgba(8, 8, 8, 0.86);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 2px solid rgba(255, 255, 255, 0.08);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 50;
  padding: 24px;
  box-sizing: border-box;
}
.emoji-pack-creator-progress[hidden] { display: none; }
.emoji-pack-creator-progress-body {
  text-align: center;
  max-width: 280px;
}
.emoji-pack-creator-progress-ring {
  width: 56px;
  height: 56px;
  margin: 0 auto 14px;
  border-radius: 50%;
  background: conic-gradient(
    var(--icon-color-primary) 0% 28%,
    rgba(255, 255, 255, 0.18) 28% 100%
  );
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 5px), #fff calc(100% - 4.5px));
          mask: radial-gradient(farthest-side, transparent calc(100% - 5px), #fff calc(100% - 4.5px));
  animation: emoji-creator-busy-spin 1.1s linear infinite;
}
.emoji-pack-creator-progress-title {
  font-size: 16px;
  font-weight: 700;
  margin: 0 0 4px;
  color: #fff;
}
.emoji-pack-creator-progress-detail {
  color: rgba(255, 255, 255, 0.65);
  font-size: 12px;
  margin: 0;
  min-height: 1em;
}

/* Naming overlay — same in-panel chrome as the error overlay (so
   clicks can't leak to the outside-close handler) but theme-neutral.
   Used at upload time to name new emoji and on cell-click to edit. */
.emoji-pack-creator-naming {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background:
    linear-gradient(to top, rgba(89, 252, 179, 0.18) 0%, rgba(89, 252, 179, 0) 38%),
    rgba(8, 8, 8, 0.9);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 2px solid var(--icon-color-primary);
  box-shadow: 0 0 24px rgba(89, 252, 179, 0.18) inset;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 50;
  padding: 24px;
  box-sizing: border-box;
}
.emoji-pack-creator-naming[hidden] { display: none; }
.emoji-pack-creator-naming-body {
  text-align: center;
  max-width: 320px;
  width: 100%;
}
.emoji-picker:has(.emoji-pack-creator-naming:not([hidden])) {
  border: none;
}
.emoji-pack-creator-naming-batch {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--icon-color-primary);
  text-transform: uppercase;
  margin-bottom: 8px;
}
.emoji-pack-creator-naming-preview-wrap {
  width: 96px;
  height: 96px;
  margin: 0 auto 12px;
  border-radius: 14px;
  background-color: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.emoji-pack-creator-naming-preview {
  width: 72px;
  height: 72px;
  object-fit: fill;
  image-rendering: -webkit-optimize-contrast;
}
.emoji-pack-creator-naming-title {
  font-size: 16px;
  font-weight: 700;
  margin: 0 0 10px;
  color: #fff;
}
.emoji-pack-creator-naming-input-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  margin-bottom: 4px;
}
.emoji-picker .emoji-pack-creator-naming-colon {
  color: rgba(255, 255, 255, 0.4);
  font-size: 18px;
  font-weight: 700;
  width: auto;
  height: auto;
  padding: 0;
  border-radius: 0;
  cursor: default;
}
.emoji-pack-creator-naming-input {
  background-color: #03030390;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 8px;
  color: #fff;
  padding: 8px 12px;
  font-size: 14px;
  font-weight: 500;
  width: 200px;
  text-align: center;
  outline: none;
  font-family: inherit;
}
.emoji-pack-creator-naming-input:focus {
  border-color: var(--icon-color-primary);
  background-color: #030303;
}
.emoji-pack-creator-naming-input.is-invalid {
  border-color: var(--danger-color);
  background-color: var(--danger-color-hover);
}
.emoji-pack-creator-naming-error {
  color: var(--danger-color);
  font-size: 11px;
  font-weight: 500;
  margin: 4px 0 12px;
  min-height: 1em;
}
.emoji-pack-creator-naming-error[hidden] { display: none; }
.emoji-pack-creator-naming-actions {
  display: flex;
  gap: 8px;
  justify-content: center;
  margin-top: 12px;
}
.emoji-pack-creator-naming-skip,
.emoji-pack-creator-naming-save {
  background-color: #171717;
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.2);
  padding: 12px 28px;
  border-radius: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  font-size: 14px;
  cursor: pointer;
  transition: opacity 0.15s ease;
}
.emoji-pack-creator-naming-save {
  background-color: var(--primary-color);
  border-color: var(--primary-color);
  color: #000;
}
.emoji-pack-creator-naming-skip {
  border-color: rgba(255, 255, 255, 0.2);
  background-color: #03030390;
}
.emoji-pack-creator-naming-skip:active,
.emoji-pack-creator-naming-save:active { opacity: 0.8; }
.emoji-pack-creator-naming-save:disabled,
.emoji-pack-creator-naming-skip:disabled { opacity: 0.5; cursor: not-allowed; }

/* Size-error overlay — `position: absolute` keeps it inside the picker
   so it covers the panel (sidebar + main) instead of the whole window,
   and clicks inside it never count as "click outside to close". */
.emoji-pack-creator-error {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background:
    linear-gradient(to top, color-mix(in srgb, var(--danger-color) 32%, transparent) 0%, color-mix(in srgb, var(--danger-color) 0%, transparent) 38%),
    rgba(8, 8, 8, 0.85);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 2px solid var(--danger-color);
  box-shadow: 0 0 24px color-mix(in srgb, var(--danger-color) 25%, transparent) inset;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 50;
  padding: 24px;
  box-sizing: border-box;
}
.emoji-pack-creator-error[hidden] { display: none; }
.emoji-picker:has(.emoji-pack-creator-error:not([hidden])) {
  border-color: transparent;
}
.emoji-pack-creator-error-body {
  text-align: center;
  max-width: 320px;
  width: 100%;
}
.emoji-pack-creator-error-art {
  margin: 0 auto 10px;
}
.emoji-pack-creator-error-mascot {
  width: 96px;
  height: 96px;
  object-fit: contain;
  display: block;
  margin: 0 auto;
  image-rendering: -webkit-optimize-contrast;
}
.emoji-pack-creator-error-pretitle {
  color: var(--danger-color);
  font-weight: 700;
  font-size: 13px;
  margin: 4px 0 2px;
  letter-spacing: 0.02em;
}
.emoji-pack-creator-error-title {
  font-size: 26px;
  font-weight: 800;
  margin: 0 0 6px;
  color: #fff;
  line-height: 1.1;
}
.emoji-pack-creator-error-detail {
  color: rgba(255, 255, 255, 0.65);
  font-size: 12px;
  margin: 0 0 16px;
}
.emoji-pack-creator-error-retry {
  background-color: var(--danger-color);
  color: #000;
  border: none;
  padding: 12px 28px;
  border-radius: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  font-size: 14px;
  cursor: pointer;
  transition: opacity 0.15s ease;
}

.emoji-pack-creator-error-retry:active {
  opacity: 0.8;
}

/* ============================================================================
   Emoji pack preview card (in-chat, triggered by naddr paste)
   ============================================================================ */

/* ── In-chat Community invite card ──
   A pasted invite link renders as this card instead of a raw URL + generic OG
   preview (the link itself is stripped from the message body). Skeleton-loads,
   then pops to the resolved community + a Join / Open action. */
.community-invite-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 8px;
  padding: 12px;
  background-color: rgba(10, 10, 10, 0.85);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 10px;
  max-width: 420px;
}

.community-invite-card.cic-ready {
  animation: cic-pop-in 0.28s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes cic-pop-in {
  0%   { transform: scale(0.97); opacity: 0.6; }
  100% { transform: scale(1); opacity: 1; }
}

.cic-eyebrow {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.45);
}

.cic-body {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.cic-icon {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
  background-color: rgba(255, 255, 255, 0.05);
}

.community-invite-card.is-invalid .cic-icon {
  filter: grayscale(1);
  opacity: 0.5;
}

.cic-meta {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.cic-name {
  font-weight: 600;
  font-size: 14px;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.community-invite-card.is-invalid .cic-name {
  color: rgba(255, 255, 255, 0.6);
}

.cic-desc {
  font-size: 11px;
  line-height: 1.35;
  color: rgba(255, 255, 255, 0.55);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

.cic-btn {
  flex-shrink: 0;
  border: none;
  border-radius: 8px;
  padding: 9px 18px;
  font-weight: 600;
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
  color: #0e0e0e;
  background-color: var(--accent-color, #59fcb3);
  transition: filter 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}

.cic-btn:hover:not(:disabled) {
  filter: brightness(1.1);
  box-shadow: 0 0 12px color-mix(in srgb, var(--accent-color, #59fcb3) 45%, transparent);
}

.cic-btn:active:not(:disabled) {
  transform: scale(0.96);
}

.cic-btn:disabled {
  opacity: 0.55;
  cursor: default;
}

.cic-btn.cic-btn-open {
  background-color: rgba(255, 255, 255, 0.08);
  color: #fff;
  border: 1px solid color-mix(in srgb, var(--accent-color, #59fcb3) 60%, transparent);
}

.cic-btn.cic-btn-open:hover:not(:disabled) {
  background-color: color-mix(in srgb, var(--accent-color, #59fcb3) 18%, transparent);
  box-shadow: none;
}

/* Loading skeleton shapes (shimmer via the shared .pack-skel base) */
.cic-skel-name {
  display: block;
  height: 14px;
  width: 55%;
  border-radius: 4px;
}

.cic-skel-desc {
  display: block;
  height: 11px;
  width: 80%;
  border-radius: 4px;
  margin-top: 4px;
}

.emoji-pack-preview {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 140px;
  gap: 12px;
  margin-top: 8px;
  padding: 10px;
  background-color: rgba(10, 10, 10, 0.85);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 10px;
  max-width: 420px;
  align-items: start;
}

/* ── Pack preview skeleton (loading state) ──
   Each `.pack-skel` is a shimmering placeholder shape that matches the
   slot it'll occupy after resolution: thumb cells fill the grid, the
   logo circle + title bar sit in the meta column, plus a sub-line and
   a button-shaped block. Sub-elements use varying widths / shapes so
   the loader doesn't read as a flat grid of bars. */
.pack-skel {
  border-radius: 6px;
  background:
    linear-gradient(
      90deg,
      rgba(255, 255, 255, 0.03) 0%,
      rgba(255, 255, 255, 0.09) 50%,
      rgba(255, 255, 255, 0.03) 100%
    );
  background-size: 200% 100%;
  animation: pack-skel-shimmer 1.6s ease-in-out infinite;
}

@keyframes pack-skel-shimmer {
  0%, 100% { background-position: 200% 0; }
  50%      { background-position: -200% 0; }
}

.pack-skel-thumb { width: 100%; height: 100%; }

.pack-skel-logo {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  flex-shrink: 0;
}

.pack-skel-title {
  height: 14px;
  width: 60%;
  border-radius: 4px;
}

.pack-skel-sub {
  height: 11px;
  width: 38%;
  margin-top: 6px;
  border-radius: 4px;
}

.pack-skel-actions {
  height: 28px;
  width: 100%;
  margin-top: 10px;
  border-radius: 6px;
}

.emoji-pack-preview-grid {
  position: relative;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-auto-rows: 28px;
  align-content: start;
  gap: 4px;
  overflow: hidden;
  max-height: 96px; /* exactly 3 rows of 28px + 2 gaps of 4px = 92px, +slack */
  border-radius: 6px;
}

/* Canvas-batched variant: a single <canvas> renders the whole grid, so the
   container drops grid layout (the canvas handles internal cell positioning
   itself) and just clips + rounds the canvas. The overflow fade still works. */
.emoji-pack-preview-grid.is-canvas {
  display: block;
}

.emoji-pack-preview-grid.is-canvas .emoji-pack-canvas {
  width: 100%;
}

/* Bottom-row fade only triggers when the grid is bursting (>= 13 thumbs =
   third row is starting). Smaller packs sit flush so we don't fake "more
   below" when there isn't any. */
.emoji-pack-preview-grid.is-overflowing::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 32px;
  pointer-events: none;
  background: linear-gradient(to bottom, rgba(10, 10, 10, 0) 0%, rgba(10, 10, 10, 0.85) 100%);
}

.emoji-pack-preview-empty {
  grid-column: 1 / -1;
  color: rgba(255, 255, 255, 0.4);
  font-size: 13px;
  text-align: center;
  padding: 20px 0;
}

.emoji-pack-preview-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  min-width: 0;
  /* Only the meta column stretches to the full card height — the
     emoji grid stays at its natural size. Pairs with `margin-top:
     auto` on `.emoji-pack-preview-actions` to bottom-anchor the
     Copy + Add/Remove buttons. */
  align-self: stretch;
}

.emoji-pack-preview-title-row {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  width: 100%;
}

.emoji-pack-preview-logo {
  width: 22px;
  height: 22px;
  border-radius: 4px;
  object-fit: cover;
  flex-shrink: 0;
}

.emoji-pack-preview-title {
  font-weight: 600;
  font-size: 14px;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
  min-width: 0;
}

.emoji-pack-preview-sub {
  font-size: 11px;
  color: rgba(255, 255, 255, 0.5);
}

.emoji-pack-preview-desc {
  font-size: 11px;
  line-height: 1.35;
  color: rgba(255, 255, 255, 0.55);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

.emoji-pack-preview-actions {
  display: flex;
  gap: 6px;
  /* `margin-top: auto` consumes the meta column's leftover vertical
     space so the action row always pins to the card's bottom edge. */
  margin-top: auto;
  width: 100%;
  align-items: stretch;
}

.emoji-pack-preview-copy {
  flex-shrink: 0;
  width: 28px;
  background-color: rgba(255, 255, 255, 0.06);
  color: #fff;
  border: none;
  border-radius: 6px;
  padding: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 0.15s ease;
}

.emoji-pack-preview-copy:hover {
  background-color: rgba(255, 255, 255, 0.12);
}

.emoji-pack-preview-copy .icon {
  width: 14px;
  height: 14px;
  background-color: #fff;
}

.emoji-pack-preview-add {
  flex: 1;
  min-width: 0;
  background-color: var(--icon-color-primary);
  color: #000;
  border: none;
  border-radius: 6px;
  padding: 6px 10px;
  font-weight: 600;
  font-size: 12px;
  cursor: pointer;
  transition: opacity 0.15s ease, background-color 0.15s ease;
}

.emoji-pack-preview-add:hover {
  opacity: 0.85;
}

.emoji-pack-preview-add:disabled {
  opacity: 0.5;
  cursor: progress;
}

.emoji-pack-preview-add.is-subscribed {
  background-color: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.85);
}

.emoji-pack-preview.is-error {
  border-color: color-mix(in srgb, var(--danger-color) 25%, transparent);
  background-color: color-mix(in srgb, var(--danger-color) 5%, transparent);
}

@media (max-width: 480px) {
  .emoji-pack-preview {
    grid-template-columns: minmax(0, 1fr) 120px;
  }
  .emoji-pack-preview-grid {
    grid-template-columns: repeat(5, 1fr);
  }
}

.emoji-search-container {
  position: relative;
  padding: 10px;
  background-color: #0a0a0a;
  border-top-right-radius: 12px;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Wrapper for search input and icon */
.emoji-search-wrapper {
  position: relative;
  flex: 1;
}

.emoji-search-wrapper input {
  width: 100%;
  padding: 10px 12px 10px 44px;
  border-radius: 8px;
  background-color: #202020;
  border: none;
  color: #fff;
  font-size: 16px;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  box-sizing: border-box;
}

.emoji-search-icon {
  /* Override .icon base class positioning */
  position: absolute !important;
  left: 12px !important;
  right: auto !important;
  top: 50% !important;
  bottom: auto !important;
  transform: translateY(-50%);
  width: 24px !important;
  height: 24px !important;
  /* Kill inherited 6px padding from `.emoji-picker span` (which styles the
     emoji grid cells). With content-box sizing that padding inflates the
     icon's box and crowds the text. */
  padding: 0 !important;
  background-color: rgba(255, 255, 255, 0.4);
  mask-image: url("./icons/search.svg");
  -webkit-mask-image: url("./icons/search.svg");
  mask-size: contain;
  mask-repeat: no-repeat;
  mask-position: center;
  -webkit-mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-position: center;
  pointer-events: none;
  z-index: 1;
  transition: opacity 0.2s ease;
}

.emoji-search-container input::-webkit-search-decoration,
.emoji-search-container input::-webkit-search-cancel-button,
.emoji-search-container input::-webkit-search-results-button,
.emoji-search-container input::-webkit-search-results-decoration {
  display: none;
}

/* Legacy input rule - search input styles are in .emoji-search-wrapper input */

/* Fix for inputs directly in container (without wrapper) - ensure they take full width */
.emoji-search-container > input {
  flex: 1;
  min-width: 0;
}

/* Icons directly in container position inside input on the left */
.emoji-search-container > .emoji-search-icon {
  left: 12px !important;
  right: auto !important;
}

.emoji-picker span {
  cursor: pointer;
  opacity: 1;
  padding: 6px;
  width: 30px;
  height: 30px;
  border-radius: 6px;
  display: inline-block;
}

/* The GIF spinner is a <span> inside .emoji-picker, so the emoji-cell rule
   above would force it into a 30px padded rounded-square. Re-assert its circle. */
.emoji-picker .loading-spinner {
  width: 48px;
  height: 48px;
  padding: 0;
  border-width: 3px;
  border-radius: 50%;
}

/* Inputs need normal text selection so typing/editing works. */
.emoji-picker input,
.emoji-picker textarea {
  user-select: text;
  -webkit-user-select: text;
}

/* Picker Mode Toggle (Emoji/GIF) */
.picker-mode-toggle {
  display: flex;
  gap: 4px;
  margin-left: 8px;
  flex-shrink: 0;
}

.picker-mode-btn {
  padding: 6px 12px;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: rgba(255, 255, 255, 0.5);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s ease;
}

.picker-mode-btn:hover {
  color: rgba(255, 255, 255, 0.8);
}

.picker-mode-btn.active {
  background: var(--icon-color-primary);
  color: #000;
}

/* GIF Picker Content */
.gif-picker-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding: 8px;
  padding-top: 0;
  background-color: #0a0a0a;
}

.gif-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-auto-rows: min-content;
  gap: 6px;
  overflow-y: auto;
  flex: 1;
  padding-right: 4px;
  align-content: start;
}

.gif-grid::-webkit-scrollbar {
  width: 6px;
}

.gif-grid::-webkit-scrollbar-track {
  background: transparent;
}

.gif-grid::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.2);
  border-radius: 3px;
}

.gif-item {
  border-radius: 8px;
  overflow: hidden;
  cursor: pointer;
  background: #1a1a1a;
  width: 100%;
  height: 0;
  padding-bottom: 100%; /* 1:1 aspect ratio */
  position: relative;
  border: 2px solid transparent;
  transition: border-color 0.15s ease;
  box-sizing: border-box;
}

.gif-item:hover {
  border-color: var(--icon-color-primary);
}

.gif-item:active {
  border-color: var(--icon-color-primary);
  opacity: 0.8;
}

.gif-item img,
.gif-item video {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
}

.gif-item .gif-placeholder {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #1a1a1a;
}

.gif-item.gif-skeleton {
  background: linear-gradient(90deg, #1a1a1a 25%, #252525 50%, #1a1a1a 75%);
  background-size: 200% 100%;
  animation: gif-skeleton-pulse 1.5s ease-in-out infinite;
}

@keyframes gif-skeleton-pulse {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

.gif-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

.gif-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 150px;
  padding: 20px;
  color: rgba(255, 255, 255, 0.4);
  font-size: 13px;
  gap: 8px;
  grid-column: 1 / -1;
  text-align: center;
  width: 100%;
  box-sizing: border-box;
}

.gif-empty-state .icon {
  position: relative;
  width: 32px;
  height: 32px;
  opacity: 0.5;
  flex-shrink: 0;
  margin-bottom: 0;
  border-radius: 0;
}

.gif-empty-state span {
  width: auto;
}

/* Loading spinner for GIF placeholders */
.loading-spinner {
  width: 24px;
  height: 24px;
  border: 2px solid rgba(255, 255, 255, 0.1);
  border-top-color: var(--icon-color-primary);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

/* Attachment Panel Styles */
.attachment-panel {
  position: fixed;
  bottom: 65px;
  left: 50%;
  transform: translateX(-50%) translateY(100%);
  background: #0a0a0a;
  border: 1px solid var(--icon-color-primary);
  border-radius: 12px;
  padding: 16px;
  z-index: 2000;
  width: calc(100% - 55px);
  box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease;
  pointer-events: none;
  opacity: 0;
  display: flex;
  flex-direction: column;
}

.attachment-panel.visible {
  transform: translateX(-50%) translateY(0);
  pointer-events: auto;
  opacity: 1;
}

.attachment-panel-content {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: flex-start;
  align-items: flex-start;
}

/* Mini Apps view - column layout with top bar */
#attachment-panel-miniapps-view {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
}

/* Mini Apps top bar with Back button and Search */
.miniapps-topbar {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 10px;
  width: 100%;
}

.miniapps-back-btn {
  display: flex;
  align-items: center;
  gap: 4px;
  background: var(--icon-color-primary);
  border: none;
  border-radius: 20px;
  padding: 6px 14px 6px 10px;
  cursor: pointer;
  transition: transform 0.15s ease, filter 0.15s ease;
  flex-shrink: 0;
}

.miniapps-back-btn:hover {
  filter: brightness(1.1);
}

.miniapps-back-btn:active {
  transform: scale(0.95);
}

.miniapps-back-btn .icon {
  position: relative;
  top: auto;
  left: auto;
  right: auto;
  bottom: auto;
  margin: 0;
  width: 20px;
  height: 20px;
  background-color: #000;
  flex-shrink: 0;
}

.miniapps-back-btn span:last-child {
  font-size: 12px;
  font-weight: 600;
  color: #000;
}

.miniapps-search-container {
  display: flex;
  align-items: center;
  flex: 1;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 20px;
  padding: 0 12px;
  gap: 8px;
  transition: border-color 0.2s ease, background 0.2s ease;
}

.miniapps-search-container:focus-within {
  border-color: var(--icon-color-primary);
  background: rgba(255, 255, 255, 0.12);
}

.miniapps-search-container .icon {
  position: relative;
  top: auto;
  left: auto;
  right: auto;
  bottom: auto;
  margin: 0;
  width: 16px;
  height: 16px;
  background-color: var(--icon-color-primary);
  flex-shrink: 0;
}

.miniapps-search-input {
  flex: 1;
  background: none;
  border: none;
  outline: none;
  color: rgba(255, 255, 255, 0.9);
  font-size: 13px;
  padding: 6px 0;
}

.miniapps-search-input::placeholder {
  color: rgba(255, 255, 255, 0.4);
}

/* Mini Apps grid - responsive with ~10px snap threshold for 4-per-row default */
.miniapps-grid {
  display: grid;
  /* minmax(65px, 1fr) allows items to grow/shrink; higher min prevents 5-col reflow */
  grid-template-columns: repeat(auto-fill, minmax(65px, 1fr));
  gap: 12px;
  width: 100%;
  max-height: 195px;
  overflow-x: hidden;
  overflow-y: auto;
  /* Reserve the scrollbar gutter always. Edit-mode wobble (a rotation) grows
     each item's bounding box a hair, transiently overflowing and flickering
     the scrollbar; without a stable gutter that toggles the content width and
     the 1fr columns shake. Only bites with few apps (scrollbar otherwise
     absent); a full grid keeps it permanently shown. */
  scrollbar-gutter: stable;
  padding: 1px;
  padding-top: 6px; /* Room for delete badges in edit mode */
  padding-right: 6px;
}

/* Responsive item sizing within mini apps grid */
.miniapps-grid .attachment-panel-item {
  width: 100%;
  max-width: 80px; /* ~24px growth threshold above 56px base before reflow */
  justify-self: center;
}

/* Responsive button sizing - scales with grid cell */
.miniapps-grid .attachment-panel-btn {
  width: 100%;
  height: auto;
  aspect-ratio: 1;
}

/* Disabled/grayed-out state for hidden apps (e.g., removed PIVX shown in search) */
.miniapps-grid .attachment-panel-item.miniapp-disabled {
  opacity: 0.45;
}

/* Hidden state for search filtering */
.miniapps-grid .attachment-panel-item.hidden-by-search {
  display: none;
}

/* No results message for Mini Apps search */
.miniapps-no-results {
  grid-column: 1 / -1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 20px;
  text-align: center;
  color: rgba(255, 255, 255, 0.5);
}

.miniapps-no-results p {
  margin: 0;
  font-size: 14px;
  font-weight: 500;
}

.miniapps-no-results-hint {
  margin-top: 6px !important;
  font-size: 12px !important;
  font-weight: 400 !important;
  opacity: 0.7;
}

/* Mini Apps Edit Mode */
.miniapps-grid.edit-mode .attachment-panel-item:not(#attachment-panel-marketplace) {
  animation: miniapp-wobble 0.12s ease-in-out infinite alternate;
}

/* Stagger the wobble animation for a more organic feel */
.miniapps-grid.edit-mode .attachment-panel-item:nth-child(odd) {
  animation-delay: 0.06s;
}

@keyframes miniapp-wobble {
  0% { transform: rotate(-2deg); }
  100% { transform: rotate(2deg); }
}

/* Red X delete badge */
.miniapp-delete-badge {
  position: absolute;
  top: -6px;
  right: -6px;
  width: 20px;
  height: 20px;
  background: #e53935;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 10;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
  transition: transform 0.15s ease, background 0.15s ease;
}

.miniapp-delete-badge:hover {
  transform: scale(1.15);
  background: #c62828;
}

.miniapp-delete-badge:active {
  transform: scale(0.95);
}

.miniapp-delete-badge .icon {
  width: 12px;
  height: 12px;
  background-color: white;
}

.miniapp-update-badge {
  position: absolute;
  top: -6px;
  right: -6px;
  width: 20px;
  height: 20px;
  background: #59fcb3;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 10;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
  transition: transform 0.15s ease, background 0.15s ease;
}

.miniapp-update-badge:hover {
  transform: scale(1.15);
  background: #3ddb94;
}

.miniapp-update-badge:active {
  transform: scale(0.95);
}

.miniapp-update-badge .icon {
  position: relative;
  width: 12px;
  height: 12px;
  background-color: #000;
  margin: 0;
}

/* Make attachment-panel-item position relative for badge positioning */
.miniapps-grid .attachment-panel-item {
  position: relative;
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
}

.attachment-panel-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
  transition: transform 0.2s ease;
}

.attachment-panel-item:hover .attachment-panel-btn {
  background: rgba(255, 255, 255, 0.18);
  border-color: var(--icon-color-primary);
}

.attachment-panel-item:active {
  transform: scale(0.95);
}

.attachment-panel-btn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid transparent;
  transition: background 0.2s ease, border-color 0.2s ease;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
}

.attachment-panel-btn .icon {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 23px;
  height: 26px;
  margin: auto;
  background-color: rgba(255, 255, 255, 0.7);
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}

.attachment-panel-btn .icon-pivx {
  width: 65%;
  height: 65%;
}

.attachment-panel-svg {
  width: 23px;
  height: 23px;
  color: rgba(255, 255, 255, 0.7);
  pointer-events: none;
}

.attachment-panel-item.disabled {
  opacity: 0.4;
  cursor: default;
}
.attachment-panel-item.disabled:active {
  transform: none;
}
.attachment-panel-item.disabled:hover .attachment-panel-btn {
  background: rgba(255, 255, 255, 0.1);
  border-color: transparent;
}

.attachment-panel-label {
  position: relative;
  font-size: 11px;
  line-height: 1;
  color: rgba(255, 255, 255, 0.7);
  font-weight: 500;
  text-align: center;
  white-space: nowrap;
  max-width: 64px;
  user-select: none;
  -webkit-user-select: none;
  pointer-events: none;
}

.attachment-panel-label.cutoff {
  text-overflow: ellipsis;
  overflow: hidden;
}

/* Global tooltip - positioned absolutely in the viewport */
.global-tooltip {
  position: fixed;
  /* Translucent over a backdrop blur (frosted glass), with a layered shadow
     lifting it off busy chat content. WKWebView needs the -webkit- prefix. */
  background-color: rgba(10, 12, 11, 0.72);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  color: white;
  padding: 6px 10px;
  border-radius: 8px;
  border: 1px solid var(--icon-color-primary);
  font-size: 12px;
  /* Wrap-capable: link-destination tooltips carry long unbroken URLs that
     would otherwise render as one off-screen line. Short labels ("Bot")
     still fit one line and render exactly as before. */
  white-space: normal;
  word-break: break-all;
  max-width: min(90vw, 520px);
  width: max-content;
  text-align: center;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease;
  z-index: 100000;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55), 0 2px 6px rgba(0, 0, 0, 0.45);
}

.global-tooltip.visible {
  opacity: 1;
}

/* Dead emoji pack (revoked by its creator, or durably missing from relays):
   the section stays visible but blurred behind an explanation + Remove button,
   so the pack never silently vanishes from under the user. The section itself
   is the positioning anchor (it's already position: relative for the cell
   tooltip); the canvas stays a direct child so PackCanvasGrid's parent-based
   sizing keeps working. */
.emoji-pack-dead:not(.collapsed) {
  /* Room for the overlay even on a one-row pack. */
  min-height: 170px;
}
.emoji-pack-dead canvas {
  filter: grayscale(1) blur(2.5px) opacity(0.35);
  pointer-events: none;
}
/* Linux (WebKitGTK): blur doesn't render on the software fallback, so the
   canvas hides entirely and the section becomes the notice alone. */
.emoji-pack-dead-noblur canvas {
  filter: none;
  opacity: 0;
}
.emoji-pack-dead .emoji-section-header {
  filter: grayscale(1) opacity(0.6);
}
.emoji-pack-dead-notice {
  position: absolute;
  /* Below the section header, over the emoji grid. */
  top: 34px;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 12px;
  text-align: center;
  /* Header + collapse chevron stay clickable through the overlay;
     the Remove button re-enables itself. */
  pointer-events: none;
}
.emoji-section.collapsed .emoji-pack-dead-notice {
  display: none;
}
/* Prefixed with .emoji-picker to out-rank the legacy
   `.emoji-picker span { width: 30px; height: 30px }` cell catch-all,
   which otherwise squishes these labels into emoji-sized boxes. */
.emoji-picker .emoji-pack-dead-text {
  width: auto;
  height: auto;
  padding: 0;
  font-size: 13px;
  line-height: 1.35;
  color: rgba(255, 255, 255, 0.85);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
  cursor: default;
}
.emoji-picker .emoji-pack-dead-subtext {
  width: auto;
  height: auto;
  padding: 0;
  font-size: 11.5px;
  line-height: 1.35;
  color: rgba(255, 255, 255, 0.55);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
  margin-top: -4px;
  cursor: default;
}
.emoji-pack-dead-remove {
  padding: 6px 14px;
  border-radius: 8px;
  border: 1px solid var(--icon-color-primary, #59fcb3);
  background: rgba(0, 0, 0, 0.65);
  color: var(--icon-color-primary, #59fcb3);
  font-size: 12.5px;
  cursor: pointer;
  pointer-events: auto;
}
.emoji-pack-tab-dead {
  filter: grayscale(1) opacity(0.5);
}

/* Destination line in the link-open confirm: the full URL must wrap inside the
   popup, never overflow it. Accent color marks it as the load-bearing fact. */
.link-confirm-url {
  display: block;
  margin: 8px 0;
  word-break: break-all;
  color: var(--icon-color-primary, #59fcb3);
  font-size: 12px;
}

/* Empty state for attachment panel views */
.attachment-panel-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  padding: 16px;
  color: rgba(255, 255, 255, 0.5);
  font-size: 12px;
  text-align: center;
  margin-top: auto;
  margin-bottom: auto;
}

/* Mini App icon in attachment panel - fills the entire button */
.attachment-panel-miniapp-btn {
  overflow: hidden;
  padding: 0;
}

.attachment-panel-miniapp-icon {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 12px;
}

/* Staggered fade-in animation for attachment panel items */
@keyframes attachmentPanelItemFadeIn {
  from {
    opacity: 0;
    transform: translateX(-10px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

.attachment-panel-item.animate-in {
  opacity: 0;
  animation: attachmentPanelItemFadeIn 0.3s ease forwards;
}

.row {
  display: flex;
  justify-content: center;
}

.cutoff {
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
  display: block;
}

a {
  font-weight: 500;
  color: #646cff;
  text-decoration: inherit;
}

a:hover {
  color: #535bf2;
}

input[type=number]::-webkit-outer-spin-button,
input[type=number]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

input,
textarea,
button {
  outline: none;
  border-radius: 8px;
  border: 1px solid transparent;
  padding: 0.6em 1.2em;
  font-size: 1em;
  font-weight: 500;
  font-family: inherit;
  color: #ffffff;
  transition: border-color 0.25s;
}

select {
  width: 150px;
  font-size: large;
}

button {
  backdrop-filter: blur(6px);
  cursor: pointer;
  font-weight: 600;
}

button:active {
  opacity: 0.75;
}

audio {
  width: 200px;
}

.btn {
  cursor: pointer;
}

.btn:hover {
  opacity: 0.75;
}


.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
}

.btn-bounce:hover:not(:disabled) {
  transform: translateY(-1px);
}

.fadeout-top-msgs {
  position: absolute;
  z-index: 100;
  /* top: is handled in JS to match the messaging UI's spacing dynamically */
  left: 0;
  right: 0;
  height: 40px;
  background: linear-gradient(#03030380, #03030300 20%);
  pointer-events: none;
}

/* Mirror of .fadeout-top-msgs for the bottom of the chat — same softness,
 * gradient direction reversed (clear at top, fades into bg at bottom).
 * Anchored to its parent's TOP edge via `bottom: 100%`, hanging upward over
 * the last messages — so it tracks chat-box height even when the input grows. */
.fadeout-bottom-msgs {
  position: absolute;
  z-index: 100;
  bottom: 100%;
  left: 0;
  right: 0;
  height: 40px;
  background: linear-gradient(#03030300 80%, #03030380);
  pointer-events: none;
}

.fadeout-bottom {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 40px;
  background: linear-gradient(#03030300, #030303);
  pointer-events: none;
}

.corner-float {
  bottom: calc(100% + 16px);
  right: 20px;
  position: absolute;
  padding: 2.5px 12.5px;
  font-size: 30px;
  transform: scale(0) translateY(50px);
  transition-property: transform;
  transition-duration: 0.4s;
  transition-timing-function: cubic-bezier(0.95, 0.05, 0.795, 0.035);
}

.corner-float.visible {
  transform: scale(1) translateY(0px);
}

.progress-bar {
  height: 10px;
  border-radius: 16px;
  margin-top: 3px;
  transition-duration: 0.2s;
  transition-property: width;
  border-width: 1px;
}

.divider {
  height: 1px;
  width: 100%;
  margin-top: 15px;
  margin-bottom: 15px;
  border: none;
}




#settings {
  font-family: Rubik, sans-serif;
  position: fixed;
  top: calc(10px + var(--chrome-h));
  bottom: 70px;
  left: 0;
  right: 2px;
  overflow-y: auto;
  overscroll-behavior: none;
  -webkit-overflow-scrolling: touch;
  touch-action: pan-y;
  box-sizing: border-box;
  animation: fadeInWithoutTransform 0.4s ease-out;
}

.settings-section {
  padding: 20px 20px 0 20px;
  padding-top: 0;
  border-radius: 12px;
  margin-top: 15px;
}

/* Voice Settings Section Styles */

/* Dropdown Styles */
select {
  width: 75%;
  padding: 12px 15px;
  margin-left: auto;
  margin-right: auto;
  margin-bottom: 15px;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.5);
  color: white;
  font-size: 14px;
  appearance: none;
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 20px;
  transition: all 0.3s ease;
}


/* Download Button Styles */
#download-model {
  color: #000;
  border: none;
  padding: 12px;
  border-radius: 8px;
  font-weight: bold;
  cursor: pointer;
  transition: all 0.3s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 10px;
  width: 80%;
  margin-left: auto;
  margin-right: auto;
}

#download-model:active {
  transform: translateY(0);
}

/* Model Status Styles */
.model-status {
  margin-top: 0px;
  font-size: 14px;
  min-height: 20px;
}

#cancel-download {
  margin-top: 16px;
}

/* Inline spinner for model download status */
.spinner {
  display: inline-block;
  width: 12px;
  height: 12px;
  border: 2px solid rgba(255, 255, 255, 0.1);
  border-top-color: var(--icon-color-primary);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
  vertical-align: middle;
  margin-right: 6px;
}

/* Progress Bar Container */
.download-progress-container {
  margin-top: 20px;
  position: relative;
  height: 40px;
  animation: fadeIn 0.3s ease-out;
}

/* Progress Track */
.progress-bar-track {
  position: relative;
  height: 12px;
  width: 100%;
  background: rgba(30, 30, 30, 0.8);
  border-radius: 6px;
  overflow: hidden;
  box-shadow: 
    inset 0 1px 3px rgba(0, 0, 0, 0.4);
}

/* Progress Fill */
.progress-bar-fill {
  height: 100%;
  width: 0%;
  border-radius: 6px;
  position: relative;
  background: linear-gradient(90deg, #59fcb3 0%, #00d4ff 100%);
  transition: width 0.3s cubic-bezier(0.65, 0, 0.35, 1);
}

/* Animated Glow Effect */
.progress-bar-glow {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 20px;
  background: linear-gradient(90deg, 
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.75) 50%, 
    rgba(255, 255, 255, 0) 100%);
  animation: glowSlide 2s infinite linear;
  opacity: 0.7;
  transform: translateX(-100%);
}

/* Pulse Animation */


/* Percentage Text */
.progress-text {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  color: white;
  font-size: 11px;
  font-weight: bold;
  font-family: 'Courier New', monospace;
}

/* Animations */
@keyframes glowSlide {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(calc(100vw + 20px)); }
}


@keyframes fadeIn {
  from { opacity: 0; transform: translateY(5px); }
  to { opacity: 1; transform: translateY(0); }
}

.voice-progress-container {
  position: fixed;
  bottom: 80px;
  left: 50%;
  transform: translateX(-50%);
  width: 80%;
  max-width: 400px;
  background: rgba(22, 22, 22, 0.9);
  border-radius: 8px;
  padding: 12px;
  z-index: 1000;
  animation: fadeIn 0.3s ease-out;
}

.voice-progress-bar {
  height: 8px;
  width: 100%;
  background: rgba(30, 30, 30, 0.8);
  border-radius: 4px;
  margin-top: 8px;
  overflow: hidden;
}

.voice-progress-fill {
  height: 100%;
  width: 0%;
  border-radius: 4px;
  transition: width 0.3s ease;
}

.voice-progress-text {
  font-size: 13px;
  text-align: center;
  margin-bottom: 5px;
}

/* ===== Voice Recorder Mobile-first UI ===== */

/* When recording/previewing, force center alignment in chat input container */
.chat-input-container:has(.voice-recording-ui.active),
.chat-input-container:has(.voice-preview-ui.active) {
  align-items: center !important;
}

/* Recording UI - inline within chat input container */
.voice-recording-ui {
  display: none;
  flex: 1;
  align-items: center;
  justify-content: flex-start;
  gap: 12px;
  padding: 0 12px;
  min-height: 45px;
  animation: fadeIn 0.2s ease;
}

.voice-recording-ui.active {
  display: flex;
}

.voice-recording-ui.cancelling {
  animation: slideOutLeft 0.3s ease forwards;
}

@keyframes slideOutLeft {
  to {
    transform: translateX(-100%);
    opacity: 0;
  }
}

/* Recording status indicator */
.voice-recorder-status {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-right: auto;
}

.voice-recorder-status .recording-dot {
  width: 8px;
  height: 8px;
  background: #ff4444;
  border-radius: 50%;
  animation: recordingPulse 1s ease-in-out infinite;
}

@keyframes recordingPulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}

.voice-recorder-status .recording-text {
  font-size: 14px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.8);
}

/* Slide hint (positioned near the timer, slides left on gesture) */
.voice-recorder-slide-hint {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  color: rgba(255, 255, 255, 0.5);
  transition: opacity 0.1s ease;
  flex: 1;
  padding-right: 12px;
  user-select: none;
  cursor: default;
}

.voice-recorder-slide-hint .slide-icon-container {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  transform: translateX(0);
}

.voice-recorder-slide-hint .icon {
  width: 16px;
  height: 16px;
  background-color: rgba(255, 255, 255, 0.5);
  transition: background-color 0.15s ease;
  animation: slideLeftHint 1s ease-in-out infinite;
}

@keyframes slideLeftHint {
  0% { transform: translateX(0); }
  50% { transform: translateX(-5px); }
  100% { transform: translateX(0); }
}

.voice-recorder-slide-hint .slide-text {
  font-size: 13px;
  white-space: nowrap;
}

/* Timer display - positioned right next to the red circle */
.voice-recorder-timer {
  font-size: 16px;
  font-weight: 500;
  color: #fff;
  font-variant-numeric: tabular-nums;
  text-align: right;
  margin-left: auto;
  padding-right: 35px; /* Space for the absolutely positioned red circle */
  user-select: none;
  cursor: default;
}

/* Lock zone (positioned above the red circle button) */
.voice-recorder-lock-zone {
  position: absolute;
  right: 12px;
  bottom: 55px;
  display: none;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  opacity: 0;
  pointer-events: none;
  /* Above the message fades (z-index 100), same as the red circle. */
  z-index: 101;
  /* No transition during drag - opacity is controlled directly by JS */
}

.voice-recorder-lock-zone.active {
  display: flex;
  /* opacity controlled by JS based on drag distance */
}

.voice-recorder-lock-zone.fading-out {
  opacity: 0 !important;
  transition: opacity 0.2s ease;
}

.lock-indicator {
  width: 36px;
  height: 36px;
  background: rgba(255, 255, 255, 0.15);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.lock-indicator .icon {
  width: 16px;
  height: 16px;
  background-color: rgba(255, 255, 255, 0.7);
}

.lock-arrow {
  display: flex;
  align-items: center;
  justify-content: center;
}

.lock-arrow .arrow-icon-container {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  transform: rotate(90deg);
}

.lock-arrow .icon {
  width: 16px;
  height: 16px;
  background-color: rgba(255, 255, 255, 0.9);
  animation: bounceUp 1s ease-in-out infinite;
}

@keyframes bounceUp {
  0%, 100% { transform: translateX(0); }
  50% { transform: translateX(-3px); }
}

.voice-recorder-lock-zone.locked .lock-arrow {
  display: none;
}

/* Hold to record tooltip */
.voice-recorder-tooltip {
  position: absolute;
  bottom: 60px;
  right: 12px;
  background-color: rgba(0, 0, 0, 0.8);
  color: white;
  padding: 6px 12px;
  border-radius: 16px;
  font-size: 12px;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translateY(5px);
  transition: opacity 0.2s ease, transform 0.2s ease;
  z-index: 100;
}

.voice-recorder-tooltip.visible {
  opacity: 1;
  transform: translateY(0);
}

/* Red circle overlays the mic button during recording */
.voice-recorder-red-circle {
  position: absolute;
  width: 26px;
  height: 26px;
  background: #ff4444;
  border-radius: 50%;
  cursor: default;
  opacity: 0;
  transform: scale(0.5);
  /* No transform transition during drag - follows cursor exactly */
  transition: opacity 0.15s ease, border-radius 0.2s ease, width 0.15s ease, height 0.15s ease;
  pointer-events: none;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Position to overlay the mic button (right side of container) */
  right: 9.5px;
  top: 50%;
  margin-top: -13px;
  /* Above the .fadeout-bottom-msgs message fade (z-index 100) — the dot is
     dragged up into that band and would otherwise render behind it. */
  z-index: 101;
}

/* Smooth return animation when drag ends */
.voice-recorder-red-circle.returning {
  transition: opacity 0.15s ease, transform 0.15s ease-out, border-radius 0.2s ease, width 0.15s ease, height 0.15s ease;
}

.voice-recorder-red-circle.pending {
  opacity: 1;
  transform: scale(1);
  pointer-events: auto;
}

.voice-recorder-red-circle.active {
  opacity: 1;
  transform: scale(1);
  pointer-events: auto;
  animation: pulse 1.5s ease-in-out infinite;
}

.voice-recorder-red-circle.locked {
  animation: none;
  border-radius: 6px;
  width: 24px;
  height: 24px;
  cursor: pointer;
  margin-top: -12px;
}

.voice-recorder-red-circle:hover {
  transform: scale(1.05);
}

.voice-recorder-red-circle:active {
  transform: scale(0.95);
}

@keyframes pulse {
  0%, 100% {
    box-shadow: 0 0 0 0 rgba(255, 68, 68, 0.4);
  }
  50% {
    box-shadow: 0 0 0 8px rgba(255, 68, 68, 0);
  }
}

/* Mic button states */
#chat-input-voice {
  transition: opacity 0.15s ease, transform 0.15s ease;
  touch-action: none; /* Prevent browser gestures during voice recording */
}

#chat-input-voice.pending {
  opacity: 0;
  transform: scale(0.5);
  pointer-events: none;
}

#chat-input-voice.recording {
  display: none;
}

/* Fade-in animation for chat input elements when returning from voice preview */
.chat-input-fade-in {
  animation: fadeIn 0.2s ease;
}

/* ===== Voice Preview UI ===== */
.voice-preview-ui {
  display: none;
  flex: 1;
  align-items: center;
  justify-content: space-between;
  min-height: 45px;
  animation: fadeIn 0.2s ease;
}

.voice-preview-ui.active {
  display: flex;
}

.voice-preview-delete {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: transparent;
  border: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.voice-preview-delete .icon {
  background-color: var(--danger-color);
  transition: opacity 0.15s ease;
}

.voice-preview-delete:hover .icon {
  opacity: 0.7;
}

/* Center container for play, waveform, and time */
.voice-preview-center {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  flex: 1;
  max-width: 75%;
  margin: 0 auto;
}

.voice-preview-play {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: transparent;
  border: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.voice-preview-play .icon {
  background-color: #fff;
  transition: opacity 0.15s ease;
}

.voice-preview-play:hover .icon {
  opacity: 0.7;
}

.voice-preview-waveform {
  flex: 1;
  height: 10px;
  background: rgba(255, 255, 255, 0.08);
  border-radius: 5px;
  position: relative;
  cursor: pointer;
  overflow: visible;
  touch-action: none; /* Enable touch dragging for seek */
  -webkit-user-select: none;
  user-select: none;
}

.voice-preview-progress {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 0%;
  background: var(--primary-color);
  border-radius: 5px 0 0 5px;
  pointer-events: none;
}

.voice-preview-handle {
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 14px;
  height: 14px;
  background: #fff;
  border-radius: 50%;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
  z-index: 1;
  pointer-events: none;
}

.voice-preview-time {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.6);
  font-variant-numeric: tabular-nums;
  min-width: 32px;
  width: 45px;
  flex-shrink: 0;
}

/* Send button in voice preview mode */
#chat-input-send.voice-preview-send {
  display: flex !important;
}

#chat-input-send.voice-preview-send .icon {
  background-color: var(--icon-color-primary);
}

/* ============================================================================
   Tor settings card — animated onion glyph
   ============================================================================
   The card is one row: large animated SVG on the left, label + status in the
   middle, toggle on the right. State classes on `#settings-tor-card` drive
   color + animation transitions:
       .tor-state-disabled     → grey, static, low-opacity glyph
       .tor-state-bootstrapping → purple, rotating hops, drawing shells
       .tor-state-connected    → vibrant, gentle breath pulse + soft halo
       .tor-state-failed       → red tint
   The SVG itself never changes per state — CSS does all the work. */

.tor-card {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 16px;
  padding: 14px 16px;
  border-radius: 14px;
  background: rgba(255,255,255,0.03);
  border: 1px solid rgba(255,255,255,0.06);
  transition: background 0.4s ease, border-color 0.4s ease;
}
.tor-card.tor-state-bootstrapping {
  background: rgba(149,48,244,0.06);
  border-color: rgba(149,48,244,0.18);
}
.tor-card.tor-state-connected {
  background: rgba(149,48,244,0.10);
  border-color: rgba(149,48,244,0.30);
  box-shadow: 0 0 24px -4px rgba(149,48,244,0.18);
}
.tor-card.tor-state-failed {
  background: rgba(231,76,60,0.06);
  border-color: rgba(231,76,60,0.25);
}

.tor-glyph-wrap {
  flex-shrink: 0;
  width: 72px;
  height: 72px;
  position: relative;
}
.tor-glyph {
  width: 100%;
  height: 100%;
  overflow: visible;
  /* JS toggles inline opacity to 0 during state changes so the keyframe
     animation snap (comets stopping, orbits starting) is hidden behind a
     fade. The transition handles the cross-dissolve. */
  transition: opacity 0.22s ease;
}

/* Hidden symbol-defs SVG. We use it to share one glyph definition between
   the production card and the dev preview row. Visually invisible. */


/* Smooth state→state transitions on every animated property. The keyframe
   animations below re-target these as needed. */
.tor-ring,
.tor-comet,
.tor-dot,
.tor-core,
.tor-halo {
  transition: stroke 0.5s ease, fill 0.5s ease, opacity 0.5s ease;
}

/* Used by JS during the cross-dissolve swap (see settings.js
   applyTorCardState): kills inner transitions for one frame so the new
   state's stroke/fill values snap instantly behind the invisible glyph,
   instead of being mid-tween when the glyph fades back in. */
.tor-no-transition .tor-ring,
.tor-no-transition .tor-comet,
.tor-no-transition .tor-dot,
.tor-no-transition .tor-core,
.tor-no-transition .tor-halo {
  transition: none !important;
}

/* ---- Default (Disabled) ----------------------------------------------------
   Rings dim, no comets, no orbital dots, dim core, no halo. Reads as a
   deliberate-but-inactive piece of UI rather than missing/broken art. */
.tor-ring {
  fill: none;
  stroke: rgba(255,255,255,0.18);
  stroke-width: 1.6;
}
.tor-comet {
  fill: none;
  stroke: transparent;
  stroke-width: 2.6;
  stroke-linecap: round;
  /* Comet dasharray uses the SVG's pathLength=100 normalization so the same
     numbers apply across all three radii. The two values must SUM TO 100
     (the full path length) so the dashoffset cycle 0 → -100 returns to the
     same visual position; otherwise the arc snaps when the loop wraps.
     22 lit + 78 gap → ~80° lit arc, perfect seamless loop. */
  stroke-dasharray: 22 78;
  stroke-dashoffset: 0;
  transform-origin: 60px 60px;
}
.tor-dot {
  fill: rgba(255,255,255,0.22);
  opacity: 0;
}
.tor-dot-orbit {
  transform-origin: 60px 60px;
}
.tor-core {
  fill: rgba(255,255,255,0.4);
  transform-origin: 60px 60px;
}
.tor-halo {
  fill: none;
  stroke: rgba(149,48,244,0);
  stroke-width: 2;
  opacity: 0;
  transform-origin: 60px 60px;
}

/* ---- Bootstrapping ---------------------------------------------------------
   Rings tint to a dim purple "track" so the comets have visible lanes. Each
   ring's comet sweeps around at its own speed, alternating direction for
   visual harmony. Core pulses in time with the inner ring. No orbital dots
   yet — they appear only when truly connected. */
.tor-state-bootstrapping .tor-ring {
  stroke: rgba(194,133,255,0.18);
}
.tor-state-bootstrapping .tor-comet { stroke: #c285ff; }

/* Radial progress bar: comet "lit" length is driven by Arti's live bootstrap
   percentage. JS sets `--tor-bootstrap-progress` (0..=100) on the card from
   `tor_get_state.bootstrap_progress`. The dasharray's lit segment grows as
   bootstrap completes — so each ring acts as its own progress arc that fills
   to a full circle just before the transition to "connected". A min lit
   length of 8 path-units keeps something visible at 0–7%. The spin
   animation continues throughout, so until ~50% the comets read as moving
   trails; past that they read as stable fill arcs. */
.tor-state-bootstrapping .tor-comet {
  --p: max(8, var(--tor-bootstrap-progress, 8));
  stroke-dasharray: var(--p) calc(100 - var(--p));
}

.tor-state-bootstrapping .tor-comet-1 { animation: torCometSpin 1.4s linear infinite; }
.tor-state-bootstrapping .tor-comet-2 { animation: torCometSpin 2.1s linear infinite reverse; }
.tor-state-bootstrapping .tor-comet-3 { animation: torCometSpin 3.0s linear infinite; }

/* Register --tor-bootstrap-progress as a typed CSS custom property so it
   can be CSS-transitioned. Without @property, var() values are strings
   and don't tween — progress would jump in chunks at each poll (every
   1.5s) rather than smoothly filling. */
@property --tor-bootstrap-progress {
  syntax: '<number>';
  inherits: true;
  initial-value: 0;
}
.tor-card {
  transition: background 0.4s ease,
              border-color 0.4s ease,
              --tor-bootstrap-progress 1.2s ease-out;
}

.tor-state-bootstrapping .tor-core {
  fill: #c285ff;
  animation: torCorePulse 1.4s ease-in-out infinite;
}

/* The comet animation drives the dasharray "lit" segment around the path
   via dashoffset. pathLength=100 means a full revolution = 100 units. */
@keyframes torCometSpin {
  from { stroke-dashoffset: 0; }
  to   { stroke-dashoffset: -100; }
}
@keyframes torCorePulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.25); }
}

/* ---- Connected ------------------------------------------------------------
   Rings vibrant, all three orbital dots present and orbiting their OWN ring
   at distinct speeds (inner fastest, outer slowest — feels like a real
   orbital system). Halo breathes softly. Drop-shadow glow. */
.tor-state-connected .tor-ring  { stroke: rgba(178,102,255,0.55); }
.tor-state-connected .tor-dot   { fill: #d9b3ff; opacity: 1; }
.tor-state-connected .tor-core  { fill: #d9b3ff; }
.tor-state-connected .tor-halo  {
  stroke: rgba(149,48,244,0.45);
  opacity: 1;
  animation: torHaloBreathe 3.2s ease-in-out infinite;
}
.tor-state-connected .tor-glyph {
  filter: drop-shadow(0 0 8px rgba(149,48,244,0.45));
}

/* Subtle, stable per-ring orbits. Inner orbits fastest, outer slowest —
   like orbital mechanics. Counter-rotate the middle ring for visual
   liveliness without making the eye hunt. */
.tor-state-connected .tor-dot-orbit-1 { animation: torOrbit  7s linear infinite; }
.tor-state-connected .tor-dot-orbit-2 { animation: torOrbit 11s linear infinite reverse; }
.tor-state-connected .tor-dot-orbit-3 { animation: torOrbit 18s linear infinite; }

@keyframes torOrbit {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
@keyframes torHaloBreathe {
  0%, 100% { transform: scale(1);    opacity: 0.55; }
  50%      { transform: scale(1.06); opacity: 0.95; }
}

/* ---- Failed --------------------------------------------------------------- */
.tor-state-failed .tor-ring { stroke: rgba(255,120,96,0.45); }
.tor-state-failed .tor-core { fill: #ff7860; }
.tor-state-failed .tor-dot  { fill: #ff7860; opacity: 0.7; }


/* ---- Body text + toggle column ---- */
.tor-card-body {
  flex: 1;
  min-width: 0;
  text-align: center;
}
.tor-card-title {
  font-size: 15px;
  font-weight: 500;
  color: rgba(255,255,255,0.92);
  margin-bottom: 4px;
}
.tor-card-status {
  font-size: 12px;
  color: rgba(255,255,255,0.55);
  min-height: 16px;
  line-height: 1.4;
}
.tor-state-connected .tor-card-status {
  color: #d9b3ff;
}
.tor-state-failed .tor-card-status {
  color: #ff8c75;
}

.tor-card-toggle {
  margin: 0;
  flex-shrink: 0;
}
.tor-card-toggle .neon-toggle {
  /* slim down so the toggle doesn't dominate the card */
  margin: 0;
}

/* The card is only the toggle; Tor's settings follow it as ordinary rows. */
#settings-tor-card {
  box-shadow: none !important;
}


/* The circuit, once connected: a faint label with New beside it, the hops below. */
.tor-circuit { margin-top: 14px; }
.tor-circuits-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 0 6px;
}
.tor-circuits-head-label {
  color: #474747;
  font-size: 14px;
  line-height: 17px;
}

/* Rebuild button: subtle filled chip with conviction, not a ghost outline. */
.tor-circuits-refresh {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: rgba(149,48,244,0.12);
  border: 1px solid rgba(149,48,244,0.30);
  color: #c9a4ff;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 4px 11px 4px 9px;
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease, transform 0.18s ease;
}
.tor-circuits-refresh:hover {
  background: rgba(149,48,244,0.24);
  border-color: rgba(178,102,255,0.65);
  color: #f1e1ff;
}
.tor-circuits-refresh:active { transform: scale(0.97); }
.tor-circuits-refresh:disabled {
  opacity: 0.55;
  cursor: default;
  transform: none;
}
.tor-circuits-refresh svg {
  width: 10px;
  height: 10px;
  flex-shrink: 0;
  display: block;
}
.tor-circuits-refresh:disabled svg {
  animation: torRefreshSpin 0.85s linear infinite;
  transform-origin: center;
}
@keyframes torRefreshSpin { to { transform: rotate(360deg); } }

/* Circuit list — tightened from 32px/row to 26px/row by trimming hop
   padding 7→4. Mono alignment on addresses + fingerprints. */
.tor-circuits {
  list-style: none;
  margin: 0;
  padding: 2px 0 2px;
  position: relative;
  font-variant-numeric: tabular-nums;
}
/* Rail spans first-dot-bottom (15px = padding-top 2 + hop padding 4 + dot
   center 9) to last-dot-top (15px from bottom, mirror). */
.tor-circuits:has(.tor-hop)::before {
  content: "";
  position: absolute;
  left: 9px;
  top: 19px;
  bottom: 17px;
  width: 1px;
  transform: translateX(-50%);
  background: linear-gradient(to bottom,
    rgba(178,102,255,0.55) 0%,
    rgba(178,102,255,0.92) 100%);
  pointer-events: none;
  z-index: 0;
}
.tor-hop {
  display: grid;
  grid-template-columns: 18px 100px 1fr auto;
  grid-template-rows: 18px;
  line-height: 18px;
  align-items: center;
  gap: 12px;
  padding: 4px 0;
  position: relative;
}
.tor-hop-mark {
  position: relative;
  width: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 18px;
}

.tor-hop-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: rgba(20, 8, 36, 0.95);
  border: 1.5px solid rgba(178,102,255,0.65);
  position: relative;
  z-index: 1;
  transition: transform 0.18s ease;
}
.tor-hop[data-position="Middle"] .tor-hop-dot {
  background: #9530f4;
  border-color: rgba(217,179,255,0.85);
}
.tor-hop[data-position="Exit"] .tor-hop-dot {
  background: #d9b3ff;
  border-color: #c285ff;
}

/* Position label — bumped from 0.55 → 0.72 to read clearly without losing
   its eyebrow character. */
.tor-hop-pos {
  font-size: 9.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 700;
  color: rgba(255,255,255,0.72);
  white-space: nowrap;
  font-feature-settings: "ss01";
  overflow: hidden;
  text-overflow: ellipsis;
}
.tor-hop[data-position="Exit"] .tor-hop-pos { color: #d9b3ff; }

.tor-hop-addr {
  font-family: "JetBrains Mono", "SF Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
  color: rgba(255,255,255,0.94);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  letter-spacing: -0.005em;
  font-feature-settings: "tnum";
}

/* Fingerprint — bumped from 0.32 → 0.55 so the hex is actually readable
   instead of fading into the background. */
.tor-hop-fp {
  font-family: "JetBrains Mono", "SF Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 10.5px;
  color: rgba(255,255,255,0.55);
  white-space: nowrap;
  cursor: help;
  letter-spacing: 0;
  transition: color 0.18s ease;
}
.tor-hop-fp:hover { color: rgba(217,179,255,0.92); }

.tor-hop:hover .tor-hop-dot { transform: scale(1.18); }
.tor-hop:hover .tor-hop-addr { color: #fff; }

.tor-circuits-empty,
.tor-circuits-error {
  list-style: none;
  font-size: 11px;
  padding: 14px 0 8px;
  text-align: center;
  letter-spacing: 0.04em;
}
.tor-circuits-empty {
  color: rgba(255,255,255,0.55);
  font-style: italic;
}
.tor-circuits-empty.is-loading::before {
  content: "";
  display: inline-block;
  width: 11px;
  height: 11px;
  border: 1.5px solid rgba(178,102,255,0.22);
  border-top-color: rgba(217,179,255,0.90);
  border-radius: 50%;
  margin-right: 8px;
  vertical-align: -1px;
  animation: torCircuitSpin 0.85s linear infinite;
}
@keyframes torCircuitSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .tor-circuits-empty.is-loading::before { animation: none; }
}
.tor-circuits-error { color: #ff8c75; }

/* Pulse fades in parked at Guard, travels the rail, fades out parked at
   Exit. Anchors recalibrated for the tighter row padding. */
.tor-circuit .tor-circuits:has(.tor-hop)::after {
  content: "";
  position: absolute;
  left: 9px;
  top: 17px;
  width: 4px;
  height: 4px;
  margin-left: -2px;
  margin-top: -2px;
  border-radius: 50%;
  background: #f6e6ff;
  pointer-events: none;
  animation: torDataFlow 4.2s cubic-bezier(0.65, 0.05, 0.35, 0.95) infinite;
  opacity: 0;
  z-index: 2;
}
@keyframes torDataFlow {
  0%   { top: 19px;                opacity: 0;    }
  8%   { top: 19px;                opacity: 0.95; }
  92%  { top: calc(100% - 17px);   opacity: 0.95; }
  100% { top: calc(100% - 17px);   opacity: 0;    }
}
@media (prefers-reduced-motion: reduce) {
  .tor-circuit .tor-circuits:has(.tor-hop)::after { animation: none; opacity: 0; }
}

/* ---- Bridges: the editor under the Use Bridges row, while it is on ---- */
.tor-bridges-body {
  margin: 0 0 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  text-align: left;
}
.tor-bridges-textarea {
  width: 100%;
  min-height: 76px;
  resize: vertical;
  background: rgba(0,0,0,0.32);
  border: 1px solid rgba(255,255,255,0.07);
  border-radius: 8px;
  padding: 8px 10px;
  color: rgba(255,255,255,0.94);
  font-family: "JetBrains Mono", "SF Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px;
  line-height: 1.5;
  outline: none;
  box-sizing: border-box;
  box-shadow: inset 0 1px 0 rgba(0,0,0,0.18);
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
  white-space: pre;
  overflow-x: auto;
  overflow-y: auto;
}
.tor-bridges-textarea:focus {
  border-color: rgba(178,102,255,0.55);
  box-shadow:
    inset 0 1px 0 rgba(0,0,0,0.18),
    0 0 0 3px rgba(149,48,244,0.10);
}
.tor-bridges-textarea::placeholder {
  color: rgba(255,255,255,0.28);
}
.tor-bridges-help {
  font-size: 13px;
  color: rgba(255,255,255,0.55);
  line-height: 1.5;
}
/* obfs4 missing-binary banner — left-rule callout, not a boxed alert. */
.tor-obfs4-banner {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  background: rgba(255, 184, 99, 0.07);
  border: 0;
  border-left: 2px solid rgba(255, 184, 99, 0.55);
  border-radius: 0 6px 6px 0;
  padding: 7px 10px;
  font-size: 11px;
  line-height: 1.45;
  color: rgba(255, 220, 180, 0.95);
}
.tor-obfs4-banner-icon {
  flex-shrink: 0;
  color: #ffb863;
  font-size: 12px;
  line-height: 1;
  padding-top: 1px;
}
.tor-obfs4-banner code {
  background: rgba(255, 184, 99, 0.16);
  border-radius: 3px;
  padding: 0 4px;
  font-family: "JetBrains Mono", "SF Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 10px;
  color: #ffd9a8;
}
.tor-bridges-help code {
  background: rgba(149,48,244,0.12);
  border-radius: 3px;
  padding: 0 4px;
  font-family: "JetBrains Mono", "SF Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 10px;
}
.tor-bridges-help a {
  color: #c9a4ff;
  text-decoration: none;
  border-bottom: 1px dotted rgba(201,164,255,0.40);
  transition: color 0.18s ease, border-bottom-color 0.18s ease;
}
.tor-bridges-help a:hover {
  color: #ecd5ff;
  border-bottom-color: rgba(236,213,255,0.85);
}
.tor-bridges-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 2px;
}
.tor-bridges-status {
  font-size: 13px;
  color: rgba(255,255,255,0.62);
  flex: 1;
  min-width: 0;
  line-height: 1.4;
}
.tor-bridges-status.is-error { color: #ff8c75; }
.tor-bridges-status.is-ok    { color: #c9a4ff; }


/* "Via bridge" annotation on the guard hop when bridges are active. */
.tor-hop[data-bridge="true"] .tor-hop-pos::after {
  content: " · BRIDGE";
  color: #c9a4ff;
  font-weight: 700;
  letter-spacing: 0.10em;
}

/* Narrow viewports: stack the card vertically so the title gets the full
   card width and stops wrapping mid-phrase. Glyph becomes a centered hero,
   title + status sit below it center-aligned, and the toggle drops to the
   bottom row. The animated glyph is visually striking enough to deserve a
   full-width spotlight at this size anyway. */
@media (max-width: 520px) {
  .tor-card {
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 10px;
    padding: 16px 14px;
  }
  .tor-card-body {
    width: 100%;
  }
  .tor-card-title {
    justify-content: center;
  }
  .tor-card-status {
    text-align: center;
  }
  .tor-attribution {
    margin: 8px auto 0;
  }
}

/* Trademark superscript on "Tor" — small, slightly raised, dimmed. */
.tor-tm {
  font-size: 0.55em;
  vertical-align: super;
  opacity: 0.6;
  margin-left: 1px;
}

/* Attribution row: small unaltered Tor logo, links to torproject.org.
   Per Tor Project trademark policy: logo is unmodified, displayed as
   attribution, with ™ at first mention (in title) and full notice in
   the (i) info popup. */
.tor-attribution {
  display: inline-block;
  margin-top: 6px;
  opacity: 0.85;
  transition: opacity 0.2s ease;
}
.tor-attribution:hover {
  opacity: 1;
}
.tor-attribution-logo {
  height: 16px;
  width: auto;
  display: block;
  -webkit-user-drag: none;
  pointer-events: none;
}

/* Neon Green Gradient 3D Toggle */
.toggle-container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 15px;
  cursor: pointer;
}

.toggle-container span:first-child {
  margin-right: 10px;
  color: rgba(255, 255, 255, 0.8);
}

.toggle-container.disabled {
  opacity: 0.45;
  pointer-events: none;
}

/* Auto-Download Limit row greys out while Auto-Download is toggled off. */
#auto-download-limit-group.disabled {
  opacity: 0.45;
  pointer-events: none;
}

/* ===== Storage donut chart ===== */
#storage-donut-wrap {
  position: relative;
  width: 190px;
  margin: 14px auto 2px;
}

#storage-donut {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

.storage-slice {
  transform-box: view-box;
  transform-origin: 50% 50%;
  /* Dim/undim must feel instant so the chart and legend read as one control;
     only the pop transform gets a springy ride */
  transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.08s ease-out, filter 0.08s ease-out;
  animation: storage-slice-in 0.5s cubic-bezier(0.22, 1, 0.36, 1) backwards;
}

.storage-slice.pop {
  transform: scale(1.05);
  filter: brightness(1.12);
}

.storage-slice.dim {
  opacity: 0.35;
}

@keyframes storage-slice-in {
  from {
    opacity: 0;
    transform: scale(0.55);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

#storage-donut-center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

#storage-donut-value {
  font-size: 19px;
  font-weight: 600;
  color: #fff;
}
.st-page #storage-donut-value { font-size: 24px; line-height: 28px; font-weight: 700; }
.st-page #storage-donut-label { color: #5b6060; line-height: 14px; }

#storage-donut-label {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.55);
  margin-top: 1px;
  max-width: 100px;
}

/* Delete button for the selected slice; lives in the donut hole, so it must
   re-enable pointer events (the center overlay is click-through) */
#storage-donut-delete {
  pointer-events: auto;
  margin-top: 5px;
  padding: 4px 14px;
  font-size: 12px;
  font-weight: 600;
  color: #fff;
  background: color-mix(in srgb, var(--danger-color) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--danger-color) 55%, transparent);
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}

#storage-donut-delete:hover {
  background: color-mix(in srgb, var(--danger-color) 40%, transparent);
  border-color: var(--danger-color);
}

#storage-donut-delete:disabled {
  opacity: 0.5;
  pointer-events: none;
}

#storage-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px 20px;
  margin-top: 14px;
}

#storage-legend:empty {
  display: none;
}

.storage-legend-item {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  color: rgba(255, 255, 255, 0.85);
  font-size: 14px;
  transition: opacity 0.08s ease-out;
}

.storage-legend-item.dim {
  opacity: 0.4;
}

.storage-legend-swatch {
  width: 14px;
  height: 14px;
  border-radius: 4px;
  flex-shrink: 0;
}

@media (prefers-reduced-motion: reduce) {
  .storage-slice {
    animation: none;
  }
}

/* Notification Content Privacy row: label left, dropdown right (matches the
   toggle rows). Stacks vertically on narrow screens so the label can't squash. */
/* The (i) explainer inside a Network section title */
.network-info {
  width: 16px;
  height: 16px;
  position: relative;
  display: inline-block;
  margin-left: 8px;
}

/* The (i) explainer beside a settings toggle row's label */
.notif-info {
  width: 18px;
  height: 18px;
  position: relative;
  display: inline-block;
  vertical-align: text-top;
  margin-left: 5px;
}

.notif-allow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 15px;
}
.notif-allow .accept-btn {
  width: auto;
  padding: 8px 22px;
}
#notif-privacy-group {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 15px;
}
#notif-privacy-group .notif-privacy-label {
  color: rgba(255, 255, 255, 0.8);
  white-space: nowrap;
}
#notif-privacy-group .vselect {
  flex: 0 1 240px;
}
@media (max-width: 600px) {
  #notif-privacy-group {
    flex-direction: column;
    align-items: stretch;
  }
  #notif-privacy-group .vselect {
    flex: 1 1 auto;
    width: 100%;
  }
}

.neon-toggle {
  position: relative;
  display: inline-block;
  width: 50px;
  height: 24px;
  border-radius: 12px;
  background: #1a1a1a;
  box-shadow: 
    inset 0 1px 3px rgba(0, 0, 0, 0.3),
    0 2px 5px rgba(0, 0, 0, 0.2);
  transition: all 0.3s ease;
}

.neon-toggle:before {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #333;
  box-shadow: 
    0 1px 3px rgba(0, 0, 0, 0.4),
    inset 0 -1px 2px rgba(255, 255, 255, 0.1);
  transition: all 0.3s ease;
}

input[type="checkbox"] {
  display: none;
}

input[type="checkbox"]:checked + .neon-toggle:before {
  transform: translateX(26px);
  background: #fff;
  box-shadow: 
    0 1px 3px rgba(0, 0, 0, 0.4),
    inset 0 -1px 2px rgba(255, 255, 255, 0.8);
}

/* Add a subtle glow effect when hovering */
.neon-toggle:hover {
  box-shadow: 
    inset 0 1px 3px rgba(0, 0, 0, 0.3);
}

/* ── Select: the in-app dropdown (ui/Select.svelte) ── */
.vselect {
  position: relative;
  width: 230px;
}
.vselect-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  height: 36px;
  padding: 0 12px;
  border: 1px solid #333;
  border-radius: 6px;
  background-color: #0a0a0a;
  backdrop-filter: none;
  color: #fff;
  font-family: inherit;
  font-size: 14px;
  font-weight: 400;
  text-align: left;
  transition: border-color 0.12s ease;
}
.vselect-btn:hover,
.vselect.open .vselect-btn { border-color: #474747; }
.vselect-btn:focus { outline: none; }
.vselect-label { flex: 1; min-width: 0; }
.vselect-caret {
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 6px solid #888;
  flex-shrink: 0;
  transition: transform 0.2s ease;
}
.vselect.open .vselect-caret { transform: rotate(180deg); }
.vselect-lead {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}
.vselect-list {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  z-index: 50;
  padding: 4px;
  border: 1px solid #2a2a2a;
  border-radius: 8px;
  background-color: #111;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55);
  opacity: 0;
  transform: translateY(-4px);
  pointer-events: none;
  transition: opacity 0.14s ease, transform 0.18s cubic-bezier(0.22, 1, 0.36, 1);
}
.vselect.open .vselect-list {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}
.vselect-item {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 34px;
  padding: 0 8px;
  border-radius: 6px;
  color: #cfcfcf;
  font-size: 14px;
  text-align: left;
  cursor: pointer;
}
.vselect-item span.cutoff { flex: 1; min-width: 0; }
.vselect-item.hi { background-color: #1f1f1f; color: #fff; }
.vselect-item.selected { color: #fff; }
.vselect-item .icon-check {
  position: relative;
  width: 14px;
  height: 14px;
  margin: 0;
  flex-shrink: 0;
  background-color: var(--icon-color-primary);
}

.vselect-fill { width: 100%; }
.vselect-narrow { width: 120px; }
.vselect.disabled { opacity: 0.45; }
.vselect-btn:disabled { cursor: not-allowed; }
.vselect-item.disabled { opacity: 0.4; cursor: default; }
.vselect.up .vselect-list {
  top: auto;
  bottom: calc(100% + 6px);
  transform: translateY(4px);
}

/* The notification sound's preview: a bare icon beside its picker, as the model's delete is. */
.notif-preview-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  min-width: 28px;
  height: 36px;
  margin: 0;
  padding: 0;
  border: none;
  background: none;
  backdrop-filter: none;
  opacity: 0.75;
  transition: opacity 0.12s ease;
}
.notif-preview-btn:hover { opacity: 1; }
.notif-preview-btn img { width: 20px; height: 20px; }

/* A lead icon inside a picker, clear of the base class's absolute inset. */
.vselect-lead .icon {
  position: relative;
  inset: auto;
  width: 18px;
  height: 18px;
  margin: 0;
  background-color: #b2b2b2;
}

/* The theme picker's leads: Vector's logo, every other theme its palette. */
.theme-logo {
  width: 18px;
  height: 18px;
  object-fit: contain;
}
.theme-swatch {
  width: 16px;
  height: 16px;
  border-radius: 4px;
}
/* The phone layout's column measure, as the OS select it replaces had. */
.theme-picker {
  width: 75%;
  margin: 0 auto 15px;
}

/* For Windows specifically, override the forced focus color */
select:focus::-ms-value {
  background-color: transparent;
  color: white;
}

/* Model select container styles */
.model-select-container {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  margin: 10px auto 0;
}



/* Delete the downloaded model: a bare icon beside the picker, centred on its height. */
.btn-delete-model {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 36px;
  padding: 0;
  flex-shrink: 0;
  border: none;
  background: none;
  backdrop-filter: none;
  cursor: pointer;
  opacity: 0.75;
  transition: opacity 0.12s ease;
}

.btn-delete-model .icon-trash {
  width: 20px;
  height: 20px;
  background-color: var(--danger-color);
}

.btn-delete-model:hover {
  opacity: 1;
}

.icon-trash {
  display: inline-block;
  width: 20px;
  height: 20px;
  background-color: transparent;
  mask: url('./icons/trash.svg') no-repeat center;
  mask-size: contain;
}

.icon-disable {
    mask-image: url("./icons/disable.svg");
    -webkit-mask-image: url("./icons/disable.svg");
}

.icon-x-user {
  mask-image: url("./icons/x-user.svg");
}

.icon-dots-horizontal {
  mask-image: url("./icons/dots-horizontal.svg");
  transform: scale(1.1);
  filter: blur(0.5px);
}




/* Disabled select styles for visual feedback */
select:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  background-color: rgba(0, 0, 0, 0.3);
  color: rgba(255, 255, 255, 0.5);
  background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23666666'%3e%3cpath d='M7 10l5 5 5-5z'/%3e%3c/svg%3e");
  pointer-events: none;
  transition: all 0.3s ease;
}

select:disabled:hover {
  box-shadow: none;
  transform: none;
}


.cancel-btn:disabled,
.cancel-btn[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
  filter: grayscale(0.6);
}

#popupConfirm {
  background-color: #58fcb3;
  color: black;
}

.nav-icon {
  width: 30px;
  height: 30px;
  position: relative;
}

/* Icon-only "Back" Navigation button */
.nav-back-btn {
  display: flex;
  align-items: center;
  position: absolute;
  top: 10px;
  left: 0;
  margin-left: 5px;
  padding: 5px;
}

/* Mirror of nav-back-btn on the right side of the chat header — anchors
   the chat-options dropdown trigger. */
.nav-menu-btn {
  display: flex;
  align-items: center;
  position: absolute;
  top: 10px;
  right: 0;
  margin-right: 10px;
  padding: 5px;
}

.nav-menu-btn .icon-dots-horizontal {
  background-color: #b2b2b2;
}

/* ── Pinned Messages (Concord v2) ─────────────────────────────────────────── */

/* Header pin toggle — a twin of the ••• button (40px box at top:10), seated
   immediately to its left. Resting color matches the hamburger's gray; the
   accent green marks the drawer being OPEN, not the button existing. */
.pins-header-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  position: absolute;
  top: 10px;
  right: 50px;
  width: 40px;
  height: 40px;
  color: #b2b2b2;
  transition: color 0.15s ease;
}

.pins-header-btn.pins-open {
  color: var(--accent-color);
}

.pins-header-btn svg {
  width: 22px;
  height: 22px;
}

/* The drawer slides open directly under the fixed 60px chat header, laying
   over the messages like a sheet. */
.pins-drawer {
  position: fixed;
  top: calc(60px + var(--chrome-h));
  left: 0;
  right: 0;
  /* Above the chat's own chrome (msg-top-fade sits at 100) but below every
     real overlay (popups/menus live at 10000+). */
  z-index: 101;
  background-color: #0d0d0d;
  /* Gray chrome (not accent) with rounded bottom corners, sheet-style. */
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-top: none;
  border-radius: 0 0 12px 12px;
  transform-origin: top;
  animation: pins-drawer-open 0.18s ease-out;
}

/* Android lays this sheet out at a fractional width (412.19 CSS px on a 412 px
   viewport), and a right BORDER lands on the composited layer's clip boundary
   and paints nothing at all — the left one is unaffected, so the sheet reads as
   lopsided. An inset shadow paints inside the box, so it survives the clip;
   1.5px because the same boundary eats the outer half of it. */
.mobile .pins-drawer {
  border-right: none;
  box-shadow: inset -1.5px 0 0 rgba(255, 255, 255, 0.12);
}

@keyframes pins-drawer-open {
  from {
    transform: translateY(-10px);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

/* Close plays the slide in reverse and holds the end state (`forwards`) so
   there is no one-frame flash before JS lands display:none. */
.pins-drawer.pins-drawer-closing {
  animation: pins-drawer-shut 0.15s ease-in forwards;
}

@keyframes pins-drawer-shut {
  from {
    transform: translateY(0);
    opacity: 1;
  }
  to {
    transform: translateY(-10px);
    opacity: 0;
  }
}

/* Up to 40% of the screen, but never squeezed useless on short windows.
   Scrolls without a visible scrollbar — the drawer is a compact sheet and
   the bar reads as clutter (wheel/touch/trackpad all still scroll). */
.pins-drawer-list {
  max-height: max(200px, 40vh);
  overflow-y: auto;
  scrollbar-width: none;
}

.pins-drawer-list::-webkit-scrollbar {
  display: none;
}

/* Open drawer dims + blurs the conversation so the pins own the eye. A filter
   on the messages container, NOT backdrop-filter on a scrim — WKWebKit's
   backdrop-filter is unreliable (see the popup styles). The transition lives
   on the base rule so both directions animate in step with the drawer slide. */
#chat-messages {
  transition: filter 0.22s ease;
}

#chat.pins-focus #chat-messages {
  filter: blur(6px) brightness(0.45);
  /* A blurred chat is a backdrop, not a surface: no accidental scrolls,
     clicks, or hover toolbars while choosing a pin. */
  pointer-events: none;
}

/* The top fade belongs to the CHAT: over the drawer's rows it reads as a
   smudge, and over the blurred backdrop it means nothing. Ease it out with
   the same timing as the focus dim. */
#msg-top-fade {
  transition: opacity 0.22s ease;
}

#chat.pins-focus #msg-top-fade {
  opacity: 0;
}

.pins-drawer-row {
  display: flex;
  /* Top-aligned in BOTH states, with each control optically centered against
     the first text line by a fixed margin — so expanding a row cannot move
     the icon, date, chevron or ✕ by even a pixel. */
  align-items: flex-start;
  gap: 6px;
  padding: 8px 16px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
  font-size: 14px;
  cursor: pointer;
}

.pins-drawer-row:hover {
  background-color: rgba(255, 255, 255, 0.04);
}

/* No message on this device = nothing to jump to: an inert row, unless it's
   truncated — then the whole row is the expand toggle and stays clickable. */
.pins-drawer-row.pins-no-jump {
  cursor: default;
}

.pins-drawer-row.pins-no-jump:hover {
  background-color: transparent;
}

.pins-drawer-row.pins-no-jump.pins-expandable {
  cursor: pointer;
}

.pins-drawer-row.pins-no-jump.pins-expandable:hover {
  background-color: rgba(255, 255, 255, 0.04);
}

/* Twemoji in pinned previews — sized like inline text, not natural-size SVGs. */
.pins-drawer-row-text img.emoji {
  width: 18px;
  height: 18px;
  vertical-align: -4px;
}

.pins-drawer-row svg {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  color: var(--accent-color);
  /* Mirrored so the pin's point aims INTO the row it marks. */
  transform: scaleX(-1);
  /* Optically centered against the first text line (top-aligned row). */
  margin-top: 1.5px;
}

.pins-drawer-row-text {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* Explicit: an ancestor centers text, but rows read as a list —
     [pin][text……spacer][date][✕] with the text hugging the icon. */
  text-align: left;
  /* ONE line metric for both states: the collapsed preview line and the
     expanded render's first line must occupy identical pixels, or the
     content swap at expand-start reads as a jump. */
  line-height: 24px;
  /* Driven inline by the expander; animates the grow/shrink. */
  transition: max-height 0.25s ease;
}

/* Date/chevron/✕ float top-right INSIDE the text flow: collapsed rows look
   identical to the flex layout, but an expanded pin's lines wrap around this
   box and take the full drawer width below it — no dead right gutter. */
.pins-drawer-row-controls {
  float: right;
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: 10px;
  height: 24px;
}

/* An expanded pin reads in full INSIDE the drawer — a member without the
   original message in their DB has nowhere else to read it. */
.pins-drawer-row.pins-expanded .pins-drawer-row-text {
  white-space: normal;
  overflow-wrap: anywhere;
}

/* Headers demote to bold body text inside the drawer: chat-scale h1-h3 read
   as shouting in a compact sheet. Purely presentational — the renderer stays
   byte-identical to in-chat, so semantics and copy behavior are untouched. */
.pins-drawer-row-text h1,
.pins-drawer-row-text h2,
.pins-drawer-row-text h3,
.pins-drawer-row-text h4,
.pins-drawer-row-text h5,
.pins-drawer-row-text h6 {
  font-size: 14px;
  font-weight: 700;
  margin: 0 0 6px 0;
  /* Inherit the container's 24px line so a header-led first line sits on
     exactly the collapsed line's pixels. */
  line-height: inherit;
}

/* Show-more chevron: rendered only on rows whose text actually truncates. */
/* Same hit target as the ✕ beside it. */
.pins-drawer-row-expander {
  position: relative;
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  background-color: #b2b2b2;
  transition: transform 0.25s ease;
}

/* Keyed to its OWN class, not .pins-expanded: the collapse branch holds
   .pins-expanded until the height transition ends (the text must stay
   wrapped while shrinking), and the chevron must not wait for that. */
.pins-drawer-row-expander.pins-open {
  transform: rotate(180deg);
}

.pins-drawer-row-date {
  flex-shrink: 0;
  opacity: 0.55;
  font-size: 13px;
}

/* Reveal/Open for non-previewable file pins — fetches via the pin-only path,
   then hands off to the OS (file manager on desktop, ACTION_VIEW on Android). */
.pins-drawer-row-open {
  position: relative;
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  background-color: #b2b2b2;
}

.pins-drawer-row-open:hover {
  background-color: var(--accent-color);
}

/* Unpin ✕ — rendered only for PIN_MESSAGES holders. */
.pins-drawer-row-unpin {
  position: relative;
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  background-color: #b2b2b2;
}

.pins-drawer-row-unpin:hover {
  background-color: var(--danger-color);
}

/* Attachment kind — the reply-context wording ("Picture", "File", …); the
   row's leading type icon carries the visual. Medium weight: present, not
   shouting next to genuinely bold message text. */
.pins-drawer-row-kind {
  font-weight: 500;
}

/* File pins lead with the chat's type icon (image/film/mic/file) in the pin
   slot — same size, same accent, same seat as the pin icon it replaces. */
.pins-drawer-row-type-icon {
  position: relative;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  background-color: var(--accent-color);
  margin-top: 1.5px;
}

/* Inline media preview for image/video pins whose decrypted local copy
   exists. Block-level under the chip, bounded so one media pin can't own
   the whole drawer. */
.pins-drawer-row-media {
  display: block;
  max-width: 100%;
  max-height: 240px;
  margin-top: 8px;
  border-radius: 8px;
}

/* Sealed/empty notices reuse the row layout without actions. */
.pins-drawer-notice {
  padding: 16px;
  font-size: 13px;
  opacity: 0.6;
  text-align: center;
}

/* Inline centering on purpose — a plain text-align block is immune to the
   app's global span/flex quirks that mis-seated the flex version. */
.pins-drawer-close {
  display: block;
  text-align: center;
  padding: 8px 16px;
  font-size: 14px;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  user-select: none;
  -webkit-user-select: none;
}

.pins-drawer-close-icon {
  display: inline-block;
  width: 20px;
  height: 20px;
  margin: 0;
  vertical-align: -5px;
  color: var(--accent-color);
}

/* Text-accompanied "Back" Navigation button */
.chat-new-back-text-btn {
  display: flex;
  align-items: center;
  margin-left: 10px;
}

.chat-new-back-text {
  font-size: 18px;
  line-height: normal;
  margin-left: 2px;
}

.chat-new-header {
  position: fixed;
  top: var(--chrome-h);
  left: 0;
  right: 0;
  height: 60px;
  background-color: rgba(22, 22, 22, 0.85);
  z-index: 100;
  backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
}

.chat-new-content {
  margin-left: 50px;
  margin-right: 50px;
}

.chat-new-title {
  margin-top: 25px;
  margin-bottom: 5px;
  text-align: center;
  align-items: center;
  font-weight: 500;
  letter-spacing: 1px;
}

.chat-new-divider {
  margin-top: 8px;
  margin-bottom: 10px;
}

.chat-new-npub {
  opacity: 0.75;
  font-size: 14px;
  display: block;
  line-break: anywhere;
}

#chat-new {
  position: relative;
  height: calc(100vh - var(--chrome-h));
  overflow: hidden;
  user-select: none;
  -webkit-user-select: none;
}

#chat-new-input {
  user-select: text;
  -webkit-user-select: text;
}

.chat-new-center-content {
  position: absolute;
  top: 90px;
  bottom: 55px;
  left: 0;
  right: 0;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  text-align: center;
}

.chat-new-icon {
  width: 140px;
  margin: 0;
  height: 140px;
  position: relative;
  display: block;
  margin-left: auto;
  margin-right: auto;
  opacity: 0.35;
}

.chat-new-subtitle {
  margin-bottom: 0px;
  letter-spacing: 1px;
  text-align: center;
  align-items: center;
}

.chat-new-subtitle-divider {
  margin-top: 8px;
  margin-bottom: 10px;
  width: 325px;
  margin: 0.5rem auto;
}

.chat-new-description {
  opacity: 0.75;
  font-size: 14px;
  line-height: 18px !important;
}

#chat-new-box {
  position: absolute;
  bottom: 0;
  width: 100%;
  flex-shrink: 0;
  padding-top: 5px;
  padding-bottom: 5px;
}

#chat-new-input {
  width: -webkit-fill-available;
  height: 20px;
  padding: 10px;
  padding-left: 14px;
  line-height: inherit;
  border: none;
  background: transparent;
  color: white;
  font-size: 15px;
}

#chat-new-btn .icon {
  background-color: var(--primary-color);
}

.chat-new-help {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: auto;
  padding-bottom: 5px;
  gap: 6px;
}

.chat-new-help-icon {
  position: relative;
  width: 18px;
  height: 18px;
  min-width: 16px;
  opacity: 0.5;
  display: inline-block;
  background-color: currentColor;
  mask-size: contain;
  mask-repeat: no-repeat;
  mask-position: center;
  padding: 0;
  margin: 0;
  top: auto;
  left: auto;
  right: auto;
  bottom: auto;
}

.chat-new-help-title {
  font-size: 14px;
  opacity: 0.5;
  font-weight: 500;
}

.chat-new-help-steps {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin-top: 6px;
  padding-bottom: 25px;
}

.chat-new-help-step {
  color: #ffffff80;
  font-size: 13px;
  font-weight: 500;
}

.chat-new-help-step:hover {
  color: #ffffff;
}

.chat-new-help-arrow {
  opacity: 1;
  font-size: 14px;
  color: var(--icon-color-primary);
}

.chat-new-help-link {
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  color: #5B6060;
}

.chat-new-help-link:hover .chat-new-help-icon {
  background-color: var(--icon-color-primary);
  opacity: 1;
}

.settings-divider {
  margin-top: 40px;
  margin-bottom: 40px;
}

.danger-title {
  color: white;
}

.danger-subtitle {
  color: var(--danger-color);
  font-size: 12px;
  text-align: center;
  margin-top: 5px;
  margin-bottom: 15px;
  margin-top: 0px;
}

.cancel-btn {
  backdrop-filter: none;
  border: 1px solid rgba(255, 255, 255, 0.2);
  background-color: #00000000;
}

.danger-btn {
  backdrop-filter: none;
  border-color: var(--danger-color);
  background-color: #00000000;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding-top: 2px;
}

.danger-btn .icon-disable {
    position: relative;
    width: 16px;
    height: 16px;
    background-color: var(--danger-color);
    flex-shrink: 0;
    display: inline-block;
    margin: 0;
    margin-top: -2px;
}

.warning-icon {
  content: url('./icons/warning_red.png');
  width: 16px;
  height: 16px;
}

.navbar {
  position: fixed;
  bottom: 0px;
  height: 65px;
  width: 100%;
  background-color: #171717;
  justify-content: space-evenly;
}

.navbar-btn {
  margin-top: auto;
  margin-bottom: auto;
  position: relative;
}

.navbar-btn-inactive {
  opacity: 0.5;
}

.update-notification-dot {
  position: absolute;
  top: 2px;
  right: 2px;
  width: 8px;
  height: 8px;
  background-color: #59fcb3;
  border-radius: 50%;
  animation: pulse-notification 2s ease-in-out infinite;
}

/* Position notification dot on chat back button (bottom-right) */
#chat-back-btn .update-notification-dot {
  top: 10px;
  bottom: auto;
  right: 8px;
}

@keyframes pulse-notification {
  0% {
    transform: scale(1);
    opacity: 1;
    box-shadow: 0 0 0 0 rgba(89, 252, 179, 0.7);
  }
  50% {
    transform: scale(1.2);
    opacity: 0.8;
    box-shadow: 0 0 0 6px rgba(89, 252, 179, 0);
  }
  100% {
    transform: scale(1);
    opacity: 1;
    box-shadow: 0 0 0 0 rgba(89, 252, 179, 0);
  }
}

.navbar-icon {
  width: 30px;
  margin: 0;
  height: 30px;
  position: relative;
  display: block;
  margin-left: auto;
  margin-right: auto;
}

.navbar-text {
  font-size: 12px;
  margin-top: -2px;
  margin-bottom: 0;
}



@media (max-height: 500px) {




  .chat-new-center-content .chat-new-icon,
  .chat-new-center-content .chat-new-subtitle,
  .chat-new-center-content .divider {
    display: none;
  }
}






















































.security-type-selector {
  display: flex;
  gap: 8px;
  margin-bottom: 12px;
}

.security-type-btn {
  flex: 1;
  padding: 10px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.15);
  background: transparent;
  color: rgba(255, 255, 255, 0.7);
  cursor: pointer;
  font-size: 14px;
  font-family: inherit;
  transition: all 0.2s;
}

.security-type-btn.active {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.4);
  color: #fff;
}







.login-encrypt-description {
  color: rgba(255, 255, 255, 0.5);
  font-size: 13px;
  margin-bottom: 16px;
}







.login-skip-btn {
  background: transparent;
  border: 1px solid rgba(89, 252, 179, 0.3);
  color: rgba(255, 255, 255, 0.7);
  padding: 10px 20px;
  margin-top: 20px;
  margin-bottom: 20px;
  cursor: pointer;
  transition: all 0.3s ease;
}

.login-skip-btn:hover {
  border-color: rgba(89, 252, 179, 0.6);
  color: rgba(255, 255, 255, 0.9);
}



@keyframes welcomeContentSlide {
  0% { 
    opacity: 0; 
    transform: translateY(30px) scale(0.95); 
  }
  100% { 
    opacity: 1; 
    transform: translateY(0) scale(1); 
  }
}


@keyframes checkPulse {
  0%, 100% { 
    transform: scale(1); 
    box-shadow: 0 0 0 0 rgba(89, 252, 179, 0.4);
  }
  50% { 
    transform: scale(1.05); 
    box-shadow: 0 0 0 20px rgba(89, 252, 179, 0);
  }
}




@keyframes loadingPulse {
  0%, 100% { opacity: 0.6; }
  50% { opacity: 1; }
}

.scroll-return-btn {
  border-radius: 100%;
  height: 45px;
  width: 45px;
  /* Above #dmsg-toolbar (z-index 50) so a message hover-toolbar can't render over
     the floating button and swallow the click. */
  z-index: 60;
}

.scroll-return-badge {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 9px;
  background-color: #59fcb3;
  color: #0a0a0a;
  font-size: 11px;
  font-weight: 700;
  line-height: 18px;
  text-align: center;
  box-sizing: border-box;
  display: none;
  box-shadow: 0 0 0 2px #0a0a0a;
}

.scroll-return-badge.visible {
  display: inline-block;
}

.sync-line {
  height: 2px;
  width: 100%;
  margin-bottom: 10px;
  opacity: 1;
  transform: scaleX(0);
  transform-origin: center;
  transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}

.sync-line.active {
  transform: scaleX(1);
  animation: pulse-sync 1.5s ease-in-out infinite 0.4s;
}

/* Determinate fill rides a MASK, not the transform — `scaleX` stays reserved for
   the centre reveal/retract, so a determinate sync still opens from the middle
   instead of growing out of the left edge. `mask-size` is animatable; a
   gradient colour-stop driven by the variable is not. */
.sync-line.progress {
  animation: none;
  -webkit-mask-image: linear-gradient(#000, #000);
  mask-image: linear-gradient(#000, #000);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: calc(var(--sync-progress, 0) * 100%) 100%;
  mask-size: calc(var(--sync-progress, 0) * 100%) 100%;
  transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1),
              -webkit-mask-size 0.3s ease-out,
              mask-size 0.3s ease-out;
}

.sync-line.fade-out {
  transform: scaleX(0);
  animation: none;
  transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}

@keyframes pulse-sync {
  0%, 100% {
    opacity: 1;
  }
  50% {
    opacity: 0.5;
  }
}

.chat-bookmarks-btn {
  align-items: center;
  position: absolute;
  top: 15px;
  right: 25px;
}


/* Transcription highlighting styles */
.transcription-section {
  transition: background-color 0.3s ease, color 0.3s ease;
  border-radius: 4px;
  margin: 0 1px;
}



.transcription-section:hover {
  background-color: rgba(255, 255, 255, 0.1);
}

/* Enhanced highlighting for the current section */
.transcription-section.current {
  background-color: color-mix(in srgb, var(--icon-color-primary) 16%, transparent);
  color: var(--icon-color-primary);
}

.transcription-text {
  line-height: 1.5;
  color: var(--file-box-title, rgba(255, 255, 255, 0.92));
  user-select: text;
  cursor: text;
}

/* An inset in the card: darker, borderless, the text the brightest thing in it. */
.transcription-result {
  box-sizing: border-box;
  margin-top: 10px;
  padding: 12px 14px;
  border-radius: 10px;
  background-color: rgba(0, 0, 0, 0.3);
}

.transcription-lang {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 6px;
  font-size: 0.72em;
  color: var(--file-box-dim, rgba(255, 255, 255, 0.34));
}

.transcription-lang-name {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  color: var(--file-box-body, rgba(255, 255, 255, 0.55));
}

.transcription-lang-name img.emoji {
  width: 14px;
  height: 14px;
}

.transcription-result.hidden {
  display: none;
}

.transcribe-download {
    flex: 1;
    min-width: 0;
    margin-left: 10px;
    padding: 6px 10px;
    border-radius: 8px;
    background: rgba(0, 0, 0, 0.25);
}

.transcribe-progress-container {
    width: 100%;
    min-width: 120px;
}

.transcribe-progress-bar {
    height: 4px;
    width: 100%;
    background: rgba(60, 60, 60, 0.8);
    border-radius: 2px;
    margin-top: 4px;
    overflow: hidden;
}

.transcribe-progress-fill {
    height: 100%;
    width: 0%;
    background: var(--primary-color);
    border-radius: 2px;
    transition: width 0.3s ease;
}

.transcribe-progress-text {
    color: var(--icon-color-primary);
    font-size: 12px;
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.cancel-download-inline {
  display: block;
  margin: 6px auto 0;
  padding: 3px 12px;
  font-size: 11px;
  font-weight: 500;
  border-radius: 4px;
  border: 1px solid var(--danger-color);
  background: none;
  color: var(--danger-color);
  cursor: pointer;
  transition: background 0.2s ease;
}

.cancel-download-inline:hover {
  background: var(--danger-color-hover);
}

/* File attachment boxes.
   Neutral chrome on purpose: the box is furniture, and the only colour in it is the
   one carrying meaning. A file that is on this device takes the theme colour, one
   that failed takes the danger colour, and everything else stays grey. */
.file-box,
.thumb-download,
.custom-audio-player,
.media-popout {
    --file-box-bg: #161616;
    --file-box-bg-hover: #1d1d1d;
    --file-box-line: #262626;
    --file-box-line-hover: #363636;
    --file-box-title: rgba(255, 255, 255, 0.92);
    --file-box-body: rgba(255, 255, 255, 0.55);
    --file-box-dim: rgba(255, 255, 255, 0.34);
    --file-box-glyph: rgba(255, 255, 255, 0.7);
    --file-box-hover: rgba(255, 255, 255, 0.07);
}

.file-box {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    margin-bottom: 6px;
    width: fit-content;
    min-width: 240px;
    max-width: min(500px, calc(100vw - 100px));
    box-sizing: border-box;
    border: 1px solid var(--file-box-line);
    border-radius: 12px;
    background: var(--file-box-bg);
    cursor: pointer;
    transition: background-color 0.2s ease, border-color 0.2s ease;
}

.file-box:hover {
    background: var(--file-box-bg-hover);
    border-color: var(--file-box-line-hover);
}

/* A transfer in flight and a live Mini App session are both "already doing it". */
.file-box.is-static {
    cursor: default;
}

.file-box-icon {
    position: relative;
    flex: none;
    width: 40px;
    height: 40px;
}

.file-box-icon > .icon {
    width: 32px;
    height: 32px;
    background-color: var(--file-box-glyph);
}

.file-box-icon > img {
    width: 40px;
    height: 40px;
    border-radius: 8px;
    object-fit: cover;
    background-color: transparent;
}

.file-box[data-state="failed"] .file-box-icon > .icon {
    background-color: var(--danger-color);
}

.file-box-text {
    flex: 1;
    min-width: 0;
    line-height: 1.25;
}

.file-box-title {
    display: block;
    color: var(--file-box-title);
    font-weight: 400;
}

.file-box[data-state="local"] .file-box-title {
    color: var(--icon-color-primary);
}

/* Size and speed ride the title line while the bar owns the line below it. The name is
   what gives way to a narrow box; the size, speed and stage always stay in view. */
.file-box-title.is-moving {
    display: flex;
    align-items: baseline;
    white-space: nowrap;
}

.file-box-name {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.file-box-meta {
    flex: none;
    white-space: pre;
    color: var(--file-box-dim);
    font-size: 0.85em;
    font-variant-numeric: tabular-nums;
}

/* The rate is the one thing in the box that rewrites itself several times a second,
   and a Mini App's box is sized to its own text. Give the rate a slot wide enough for
   the longest it can read (a stage such as "Starting upload"), so the box stops
   resizing and the cancel button stops moving. */
.file-box-rate {
    display: inline-block;
    width: 8.8em;
    white-space: nowrap;
    margin-left: 0.35em;
    opacity: 0;
    transform: translateY(4px);
    transition: opacity 0.28s ease, transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}

.file-box-rate.is-visible {
    opacity: 1;
    transform: translateY(0);
}

.file-box-sub {
    display: block;
    margin-top: 3px;
    color: var(--file-box-body);
    font-size: 0.8em;
    font-weight: 400;
}

.file-box-bar {
    --progress: 0%;
    display: block;
    margin-top: 7px;
    height: 4px;
    border-radius: 999px;
    background: linear-gradient(
        to right,
        var(--icon-color-primary) 0% var(--progress),
        rgba(255, 255, 255, 0.12) var(--progress) 100%
    );
    transition: --progress 0.3s ease;
}

/* The icon buttons on files and players: one look, each sized where it sits. */
:is(.file-box-action, .audio-transcribe-btn, .popout-action, .album-toggle, .audio-skip) {
    position: relative;
    flex: none;
    padding: 0;
    border: none;
    border-radius: 8px;
    background: transparent;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}

:is(.file-box-action, .audio-transcribe-btn, .popout-action, .album-toggle, .audio-skip):hover {
    background: var(--file-box-hover);
}

.file-box-action,
.audio-transcribe-btn {
    width: 34px;
    height: 34px;
    border: 1px solid var(--file-box-line-hover);
}

.file-box-action > .icon {
    width: 17px;
    height: 17px;
    background-color: var(--icon-color-primary);
}

.file-box-action.is-failed {
    border-color: color-mix(in srgb, var(--danger-color) 45%, transparent);
}

.file-box-action.is-failed > .icon {
    background-color: var(--danger-color);
}

.file-box-action.is-failed:hover {
    background: var(--danger-color-hover);
    border-color: var(--danger-color);
}

/* Cancel is grey until you reach for it, then it says what it will do. */
.file-box-action.is-cancel > .icon {
    background-color: var(--file-box-glyph);
}

.file-box-action.is-cancel:hover {
    background: var(--danger-color-hover);
    border-color: var(--danger-color);
}

.file-box-action.is-cancel:hover > .icon {
    background-color: var(--danger-color);
}

/* The Mini App session line keeps its own colour: it reports a live thing, not a file state. */
.file-box-session {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 3px;
    font-size: 0.8em;
}

.file-box-peers {
    padding: 2px 8px;
    border-radius: 10px;
    background-color: color-mix(in srgb, var(--icon-color-primary) 15%, transparent);
    border: 0.5px solid color-mix(in srgb, var(--icon-color-primary) 30%, transparent);
    color: var(--icon-color-primary);
    font-size: 0.85em;
    font-weight: 500;
    display: inline-flex;
    align-items: center;
}

@media (max-width: 480px) {
    .file-box {
        gap: 10px;
        padding: 10px;
    }
}

/* The pending-download affordance over a thumbhash blur. Same language as the file
   box, sized off the picture underneath: a banner gets the icon and both lines, a
   sliver gets the icon alone rather than text it cannot show. */
.thumb-download {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    display: flex;
    align-items: center;
    gap: 12px;
    max-width: 94%;
    max-height: 94%;
    box-sizing: border-box;
    padding: 10px;
    border: 1px solid var(--file-box-line-hover);
    border-radius: 12px;
    background: rgba(12, 12, 12, 0.82);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    cursor: pointer;
    transition: background-color 0.2s ease, border-color 0.2s ease;
}

.thumb-download:not(.is-compact):hover {
    background: rgba(24, 24, 24, 0.88);
    border-color: #4a4a4a;
}

/* Too small for the wording, so the panel goes with it: the icon tile IS the button,
   and takes over the backdrop the panel was carrying. */
.thumb-download.is-compact {
    gap: 0;
    padding: 0;
    border: none;
    background: none;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
}

.thumb-download.is-compact .thumb-download-icon {
    width: 40px;
    height: 40px;
    border-radius: 10px;
    background: rgba(12, 12, 12, 0.82);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    transition: background-color 0.2s ease, border-color 0.2s ease;
}

.thumb-download.is-compact:hover .thumb-download-icon {
    background: rgba(24, 24, 24, 0.88);
    border-color: #4a4a4a;
}

.thumb-download-icon {
    position: relative;
    flex: none;
    width: 44px;
    height: 44px;
    /* Media smaller than the tile still has to fit it. */
    max-width: 100%;
    max-height: 100%;
    box-sizing: border-box;
    border: 1px solid var(--file-box-line-hover);
    border-radius: 10px;
}

/* Proportional, so the glyph keeps its weight when the tile shrinks to fit small media. */
.thumb-download-icon > .icon {
    width: 64%;
    height: 64%;
    background-color: var(--icon-color-primary);
}

.thumb-download.is-failed .thumb-download-icon {
    border-color: color-mix(in srgb, var(--danger-color) 45%, transparent);
}

.thumb-download.is-failed .thumb-download-icon > .icon {
    background-color: var(--danger-color);
}

.thumb-download-text {
    min-width: 0;
    padding-right: 4px;
    text-align: left;
    line-height: 1.25;
}

.thumb-download-title {
    display: block;
    color: var(--file-box-title);
    font-size: 14px;
    font-weight: 400;
}

.thumb-download-sub {
    display: block;
    margin-top: 2px;
    color: var(--file-box-dim);
    font-size: 12px;
}

/* The ring is unchanged; a line hangs under it carrying a transfer rate or a publish
   notice, and only where one fits. The overlay sizes its child against the media, so
   the pair takes the cap the bare ring would have taken. */
.attachment-progress-overlay > .media-progress {
    flex: none;
    max-width: 70cqmin;
    max-height: 70cqmin;
}

.media-progress {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
}

/* Hung off the ring rather than stacked with it, so the ring is placed once and
   nothing beside it (the cancel button centres on the same axis) is displaced. */
.media-progress-note {
    position: absolute;
    top: calc(100% + 7px);
    left: 50%;
    color: rgba(255, 255, 255, 0.82);
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
    white-space: nowrap;
    /* Rises the last few pixels into place as it fades, so it reads as settling out of
       the ring above rather than being switched on. */
    opacity: 0;
    transform: translateX(-50%) translateY(4px);
    transition: opacity 0.28s ease, transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}

.media-progress-note.is-visible {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}

/* The audio player wears the file box's chrome, grey throughout, with the theme's
   colour kept for the things that play: the button and the waveform. */
.custom-audio-player {
    gap: 12px;
    padding: 10px 12px;
    background: var(--file-box-bg);
    border: 1px solid var(--file-box-line);
    border-radius: 12px;
    position: relative;
    overflow: hidden;
    /* Bounded by the row it sits in, whatever the layout: one player for every screen. */
    width: min(100%, 500px);
    min-width: 240px;
    box-sizing: border-box;
    transition: background-color 0.2s ease, border-color 0.2s ease;
    margin-bottom: 6px;
}

/* Every file box is a tap target — open, download, retry — so the visible border
   has to be the whole of it. The wrapper carries the click handler, so it hugs the
   box too: a block-width wrapper leaves a dead strip beside the box that still
   opens the file when tapped. The transfer line is kept from resizing the box by
   the fixed slot on `.file-box-rate`. */
.file-attachment {
    width: fit-content;
    /* The same cap the box takes: without it a long filename stretches the wrapper
       past the box it is meant to hug, and the overhang is still tappable. */
    max-width: min(500px, calc(100vw - 100px));
}


.custom-audio-player-inner {
    display: flex;
    align-items: center;
}

.custom-audio-player:hover {
    border-color: var(--file-box-line-hover);
    background: var(--file-box-bg-hover);
}



.audio-play-btn {
    width: 40px;
    height: 40px;
    min-width: 40px;
    padding: 0;
    border-radius: 50%;
    background: var(--icon-color-primary);
    border: none;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: filter 0.2s ease, transform 0.1s ease;
    position: relative;
    overflow: hidden;
}

.audio-play-btn:hover {
    filter: brightness(1.08);
}

.audio-play-btn:active {
    transform: scale(0.95);
}

.audio-play-btn .icon {
    width: 28px;
    height: 28px;
    background-color: #0a0a0a;
    mask-size: 28px;
}

.icon-play {
    mask-image: url("./icons/play.svg");
    mask-position: 55% center;
}

.icon-pause {
    mask-image: url("./icons/pause.svg");
}

.audio-waveform {
    /* A drag across it scrubs; the chat must not scroll under the finger. */
    touch-action: none;
    display: flex;
    align-items: center;
    gap: 2px;
    height: 30px;
    opacity: 0.8;
    margin: 0 8px;
    position: relative;
    flex: 1;
    cursor: pointer;
}

.audio-waveform-wrapper {
    display: flex;
    flex-direction: column;
    flex: 1;
    width: 0;
    gap: 2px;
    margin: 0 8px;
}

.audio-waveform-wrapper .audio-waveform {
    margin: 0;
}

/* With art, the card is an album: the art beside the track, the controls under the words.
   A narrow card keeps the words beside a smaller tile and gives the controls the full row. */
.custom-audio-player:has(.audio-album) {
    container-type: inline-size;
    /* WebKit leaves the blurred art glow's square corners outside the rounding otherwise. */
    clip-path: inset(0 round 12px);
}

.audio-album {
    position: relative;
    display: grid;
    grid-template-columns: 128px minmax(0, 1fr);
    grid-template-rows: 1fr auto;
    grid-template-areas: "art text" "art controls";
    column-gap: 14px;
}

.audio-album .audio-cover-art { grid-area: art; }
.audio-album .audio-track-text { grid-area: text; align-self: center; }
.audio-album .custom-audio-player-inner { grid-area: controls; }

@container (max-width: 380px) {
    .audio-album {
        grid-template-columns: 64px minmax(0, 1fr);
        grid-template-rows: auto auto;
        grid-template-areas: "art text" "controls controls";
        row-gap: 10px;
    }
}

/* An album: its head beside the art, the tracklist, then the current song's controls. */
.audio-album.is-full {
    grid-template-rows: auto auto auto;
    grid-template-areas: "art text" "tracks tracks" "controls controls";
    row-gap: 12px;
}

.audio-album.no-art {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "text" "controls";
}

.audio-album.is-full.no-art {
    grid-template-areas: "text" "tracks" "controls";
}

.title-note {
    margin-left: 0.3em;
    font-weight: 400;
    opacity: 0.5;
}

.album-stats {
    margin-top: 1px;
    font-size: 0.76em;
    color: var(--file-box-dim);
}

.album-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 10px;
}

.album-play,
.album-shuffle {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 6px 14px 6px 10px;
    border-radius: 999px;
    font: inherit;
    font-size: 0.82em;
    font-weight: 600;
    cursor: pointer;
    transition: filter 0.15s ease, background-color 0.15s ease;
}

.album-play {
    border: none;
    background: var(--icon-color-primary);
    color: #0a0a0a;
}

.album-shuffle {
    border: 1px solid var(--file-box-line-hover);
    background: rgba(255, 255, 255, 0.04);
    color: var(--file-box-title);
}

.album-play:hover { filter: brightness(1.08); }
.album-shuffle:hover { background: rgba(255, 255, 255, 0.09); }
.album-play:disabled, .album-shuffle:disabled { opacity: 0.4; cursor: default; }

.album-play > .icon,
.album-shuffle > .icon {
    position: relative;
    inset: auto;
    margin: 0;
    width: 16px;
    height: 16px;
}

.album-play > .icon { background-color: #0a0a0a; }
.album-shuffle > .icon { width: 14px; height: 14px; background-color: var(--file-box-glyph); }

.album-toggles {
    display: inline-flex;
    gap: 2px;
    margin-left: auto;
}

.album-toggle,
.audio-skip {
    width: 30px;
    height: 30px;
}

.album-toggle > .icon {
    width: 16px;
    height: 16px;
    background-color: var(--file-box-dim);
}

.album-toggle.is-on > .icon {
    background-color: var(--icon-color-primary);
}

.audio-skip > .icon {
    width: 20px;
    height: 20px;
    background-color: var(--file-box-title);
}

.custom-audio-player-inner > .audio-skip {
    margin: 0 2px;
}

.album-tracks {
    grid-area: tracks;
    list-style: none;
    margin: 0;
    padding: 4px 0 0;
    border-top: 1px solid rgba(255, 255, 255, 0.06);
}

.album-track {
    display: grid;
    grid-template-columns: 26px minmax(0, 1fr) auto;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 7px 6px;
    border: none;
    border-radius: 8px;
    background: transparent;
    color: var(--file-box-title);
    font: inherit;
    font-size: 0.88em;
    text-align: left;
    cursor: pointer;
    transition: background-color 0.15s ease;
}

.album-track:hover {
    background: rgba(255, 255, 255, 0.05);
}

.album-track.is-current {
    color: var(--icon-color-primary);
    font-weight: 600;
}

.album-track-no,
.album-track-len {
    color: var(--file-box-dim);
    font-variant-numeric: tabular-nums;
    font-weight: 400;
}

.album-track-no {
    display: flex;
    justify-content: center;
}

.album-eq {
    display: inline-flex;
    align-items: flex-end;
    gap: 2px;
    height: 12px;
}

.album-eq > span {
    width: 3px;
    height: 100%;
    border-radius: 1px;
    background: var(--icon-color-primary);
    /* Scaled, not resized: a transform animates without a layout every frame. */
    transform-origin: bottom;
    animation: album-eq 0.9s ease-in-out infinite;
}

.album-eq > span:nth-child(2) { animation-delay: -0.3s; }
.album-eq > span:nth-child(3) { animation-delay: -0.6s; }

@keyframes album-eq {
    0%, 100% { transform: scaleY(0.3); }
    50% { transform: scaleY(1); }
}

.album-more {
    display: block;
    width: 100%;
    padding: 7px 0 3px;
    border: none;
    background: transparent;
    color: var(--file-box-body);
    font: inherit;
    font-size: 0.8em;
    cursor: pointer;
}

.album-more:hover {
    color: var(--file-box-title);
}

/* The art's own colour, blurred into the card, so the tile does not sit on flat grey. */
.audio-art-glow {
    position: absolute;
    inset: -30%;
    width: 160%;
    height: 160%;
    object-fit: cover;
    filter: blur(40px) saturate(1.4);
    opacity: 0.22;
    pointer-events: none;
}

/* No text selection while anything is being dragged (lib/draglock.js). */
body.drag-lock,
body.drag-lock * {
    user-select: none !important;
    -webkit-user-select: none !important;
}

/* The player's buttons sit over the art glow and scroll in lists: the global button's
   backdrop blur would cost a backdrop pass each, and shows nothing here. */
:is(.album-track, .album-more, .album-play, .album-shuffle, .album-toggle, .audio-skip,
    .popout-action, .popout-thumb, .audio-cover-art, .audio-transcribe-btn, .file-box-action, .audio-play-btn) {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
}

@media (prefers-reduced-motion: reduce) {
    .lyrics-panel,
    .lyrics-line {
        transition: none;
    }
}

/* Lyrics: a sheet that slides open under the card (and in the pop-out). */
.lyrics-panel {
    display: grid;
    grid-template-rows: 0fr;
    margin-top: 0;
    transition: grid-template-rows 0.4s cubic-bezier(0.22, 1, 0.36, 1), margin-top 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}

.lyrics-panel.is-open {
    grid-template-rows: 1fr;
    margin-top: 12px;
}

.lyrics-panel-inner {
    min-height: 0;
    overflow: hidden;
}

@property --fade-top {
    syntax: '<percentage>';
    inherits: false;
    initial-value: 0%;
}

@property --fade-bottom {
    syntax: '<percentage>';
    inherits: false;
    initial-value: 0%;
}

/* No padding to scroll past: the first line sits at the top at the start and the last at
   the bottom at the end. An edge fades only where more lines lie beyond it. */
.lyrics {
    position: relative;
    height: 260px;
    overflow-y: auto;
    scrollbar-width: none;
    padding: 0 4px;
    box-sizing: border-box;
    --fade-top: 0%;
    --fade-bottom: 0%;
    -webkit-mask-image: linear-gradient(transparent, #000 var(--fade-top), #000 calc(100% - var(--fade-bottom)), transparent);
    mask-image: linear-gradient(transparent, #000 var(--fade-top), #000 calc(100% - var(--fade-bottom)), transparent);
    transition: --fade-top 0.3s ease, --fade-bottom 0.3s ease;
    user-select: none;
    -webkit-user-select: none;
}

.lyrics.more-above {
    --fade-top: 18%;
}

.lyrics.more-below {
    --fade-bottom: 20%;
}

.lyrics::-webkit-scrollbar {
    display: none;
}

.lyrics-line {
    padding: 5px 2px;
    font-size: 1.3em;
    font-weight: 700;
    line-height: 1.3;
    color: #fff;
    opacity: 0.75;
    filter: blur(var(--blur, 0px));
    cursor: pointer;
    transition: opacity 0.6s ease, filter 0.6s ease;
}

/* Past lines are fully lit and faded, upcoming ones unlit and less faded: the same dimness
   either side of the sung line, so moving on is only a fade. */
.lyrics-line.is-past {
    opacity: 0.3;
}

.lyrics-line.is-current {
    opacity: 1;
}

@media (hover: hover) {
    .lyrics-line:hover {
        opacity: 1;
    }
}

.lyrics-line.is-break {
    opacity: 0.4;
}

.lyrics-line.is-break.is-current {
    opacity: 1;
}

@property --fill {
    syntax: '<number>';
    inherits: false;
    initial-value: 0;
}

/* A word lights from the left, a soft edge moving through it. */
.lyrics-word {
    background: linear-gradient(90deg,
        rgba(255, 255, 255, 1) calc(var(--fill) * 110% - 10%),
        rgba(255, 255, 255, 0.4) calc(var(--fill) * 110%));
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.lyrics-word.is-whole {
    transition: --fill 0.6s ease;
}

.lyrics-dots {
    display: inline-flex;
    gap: 7px;
    padding: 6px 0;
}

.lyrics-dots > span {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: currentColor;
}

.lyrics-line.is-current .lyrics-dots > span {
    animation: lyrics-dot 1.4s ease-in-out infinite;
}

.lyrics-line.is-current .lyrics-dots > span:nth-child(2) { animation-delay: 0.2s; }
.lyrics-line.is-current .lyrics-dots > span:nth-child(3) { animation-delay: 0.4s; }

@keyframes lyrics-dot {
    0%, 100% { transform: scale(0.75); opacity: 0.5; }
    50% { transform: scale(1.1); opacity: 1; }
}

/* Plain lyrics: text to read, nothing to follow. */
.lyrics:not(.is-synced) {
    padding: 4px;
}

.lyrics:not(.is-synced) .lyrics-line {
    padding: 0 2px;
    font-size: 0.95em;
    font-weight: 500;
    line-height: 1.55;
    color: rgba(255, 255, 255, 0.82);
    opacity: 1;
    cursor: default;
}

.lyrics:not(.is-synced) .lyrics-line.is-break {
    height: 0.8em;
}

/* The pop-out: media that left its chat still playing. One grid holds every form, so a
   video narrowed into sound-only moves its element instead of remounting it.
     sound:  [thumb][words      ][actions]
             [play ][wave       ][time   ]
     video:  [video                       ]
             [words             ][actions] */
.media-popout {
    position: fixed;
    z-index: 8500;
    box-sizing: border-box;
    display: grid;
    /* The header's buttons straddle the last two columns and the time needs only the last,
       so the waveform runs on under the buttons instead of the time sitting in their width. */
    grid-template-columns: 44px minmax(0, 1fr) auto auto;
    grid-template-areas: "thumb text actions actions" "play wave wave time";
    align-items: center;
    column-gap: 12px;
    /* Rows space themselves (see below): a closed panel is a zero-height row, and a row
       gap would still count it. */
    row-gap: 0;
    padding: 12px;
    background: var(--file-box-bg);
    border: 1px solid var(--file-box-line-hover);
    border-radius: 14px;
    box-shadow: 0 18px 50px rgba(0, 0, 0, 0.85), 0 4px 14px rgba(0, 0, 0, 0.7), 0 0 0 1px rgba(0, 0, 0, 0.6);
    overflow: hidden;
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
    cursor: grab;
}

.media-popout:active {
    cursor: grabbing;
}

/* Nothing in the box is a drag source. */
.media-popout img {
    -webkit-user-drag: none;
    user-select: none;
}

.media-popout > :not(.popout-glow, .popout-grip) {
    position: relative;
}

.popout-glow {
    position: absolute;
    inset: 0;
    overflow: hidden;
    border-radius: inherit;
    clip-path: inset(0 round 14px);
    pointer-events: none;
}

.media-popout.is-video {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "thumb thumb" "text actions";
    padding: 0 0 10px;
    row-gap: 8px;
}

.media-popout.is-sound > :is(.popout-play-group, .popout-wave, .popout-track, .popout-time) {
    margin-top: 10px;
}

.media-popout.is-video .popout-text {
    padding-left: 12px;
}

.media-popout.is-video .popout-actions {
    padding-right: 8px;
}

.popout-thumb {
    grid-area: thumb;
    width: 44px;
    height: 44px;
    padding: 0;
    border: none;
    border-radius: 8px;
    object-fit: cover;
    background: var(--file-box-line);
    overflow: hidden;
    display: block;
}

.popout-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

button.popout-thumb {
    cursor: zoom-in;
}

.popout-thumb.is-round {
    border-radius: 50%;
}

.popout-thumb.is-glyph > .icon {
    background-color: var(--file-box-glyph);
    width: 22px;
    height: 22px;
}

.media-popout.is-video .popout-video {
    width: 100%;
    height: auto;
    border-radius: 0;
    background: #000;
}

.popout-text {
    grid-area: text;
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.popout-title {
    font-size: 0.92em;
    font-weight: 600;
    color: var(--file-box-title);
    line-height: 1.25;
}

.popout-sub {
    font-size: 0.78em;
    color: var(--file-box-body);
    line-height: 1.25;
}

.popout-place {
    display: flex;
    align-items: center;
    gap: 5px;
    min-width: 0;
}

.popout-place > .cutoff {
    min-width: 0;
}

/* The community yields its width before the channel does: the channel is the finer place. */
.popout-place-community {
    flex: 0 3 auto;
}

.popout-place-icon {
    width: 14px;
    height: 14px;
    flex: none;
    border-radius: 4px;
    object-fit: cover;
}

.popout-place-sep {
    flex: none;
    opacity: 0.6;
}

.popout-actions {
    grid-area: actions;
    justify-self: end;
    display: flex;
    gap: 4px;
    align-self: start;
}

.popout-action {
    width: 32px;
    height: 32px;
}

.popout-action > .icon {
    width: 20px;
    height: 20px;
    background-color: var(--file-box-glyph);
}

.popout-play {
    width: 36px;
    height: 36px;
    min-width: 36px;
}

.media-popout .popout-play .icon {
    width: 24px;
    height: 24px;
    mask-size: 24px;
}

.popout-wave {
    grid-area: wave;
    width: 100%;
    height: 26px;
    color: var(--icon-color-primary);
    cursor: pointer;
    touch-action: none;
}

.popout-track {
    grid-area: wave;
    height: 18px;
    display: flex;
    align-items: center;
    cursor: pointer;
    touch-action: none;
    background: linear-gradient(var(--file-box-line-hover), var(--file-box-line-hover)) center / 100% 4px no-repeat;
}

.popout-track-fill {
    height: 4px;
    border-radius: 2px;
    background: var(--icon-color-primary);
}

.popout-time {
    grid-area: time;
    justify-self: end;
    font-size: 0.75em;
    color: var(--file-box-body);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* An album in the pop-out: previous and next flank play, and the songs slide open. The
   controls take a second column under the words, so the cover's column stays its own size. */
.media-popout.is-album {
    grid-template-columns: 44px auto minmax(0, 1fr) auto auto;
    grid-template-areas: "thumb text text actions actions" "play play wave wave time";
}

.popout-play-group {
    grid-area: play;
    display: inline-flex;
    align-items: center;
    gap: 2px;
}

.media-popout:not(.is-album) .popout-play-group {
    justify-self: center;
}

.media-popout .popout-tracks .album-tracks {
    max-height: 200px;
    overflow-y: auto;
    border-top: none;
    padding: 0;
}

.media-popout .popout-tracks .album-track {
    padding: 6px 4px;
    font-size: 0.82em;
}

/* Lyrics and the song list span the pop-out, spaced only while open. */
.media-popout .lyrics-panel {
    grid-column: 1 / -1;
}

.media-popout .lyrics-panel.is-open {
    margin-top: 10px;
}

.media-popout .lyrics {
    height: 200px;
}

.media-popout .lyrics-line {
    font-size: 1.1em;
}

/* A transcript spans the box under the controls, capped so a long one scrolls. */
.media-popout .transcription-result {
    grid-column: 1 / -1;
    margin-top: 10px;
    max-height: 180px;
    overflow-y: auto;
    font-size: 0.9em;
    cursor: auto;
    touch-action: pan-y;
}

.popout-action.is-open > .icon {
    background-color: var(--icon-color-primary);
}

.popout-action:disabled {
    opacity: 0.4;
    cursor: default;
}

@media (pointer: coarse) {
    .popout-grip {
        width: 28px;
        height: 28px;
    }
}

/* Keyboard focus shows on the players' buttons; the global button rule removes it. */
:is(.file-box-action, .audio-transcribe-btn, .popout-action, .album-toggle, .audio-skip, .album-play,
    .album-shuffle, .album-track, .album-more, .audio-play-btn, .popout-thumb, .audio-cover-art):focus-visible {
    outline: 2px solid var(--icon-color-primary);
    outline-offset: 2px;
}

/* Resize grips on every corner; the corner opposite the grabbed one stays put. */
.popout-grip {
    position: absolute;
    width: 18px;
    height: 18px;
    z-index: 2;
}

.popout-grip[data-corner="tl"] { top: 0; left: 0; cursor: nwse-resize; }
.popout-grip[data-corner="tr"] { top: 0; right: 0; cursor: nesw-resize; }
.popout-grip[data-corner="bl"] { bottom: 0; left: 0; cursor: nesw-resize; }
.popout-grip[data-corner="br"] { bottom: 0; right: 0; cursor: nwse-resize; }

.popout-grip::before {
    content: '';
    position: absolute;
    width: 7px;
    height: 7px;
    border: 0 solid rgba(255, 255, 255, 0.55);
    opacity: 0;
    transition: opacity 0.2s ease;
}

.popout-grip[data-corner="tl"]::before { top: 5px; left: 5px; border-top-width: 2px; border-left-width: 2px; border-top-left-radius: 3px; }
.popout-grip[data-corner="tr"]::before { top: 5px; right: 5px; border-top-width: 2px; border-right-width: 2px; border-top-right-radius: 3px; }
.popout-grip[data-corner="bl"]::before { bottom: 5px; left: 5px; border-bottom-width: 2px; border-left-width: 2px; border-bottom-left-radius: 3px; }
.popout-grip[data-corner="br"]::before { bottom: 5px; right: 5px; border-bottom-width: 2px; border-right-width: 2px; border-bottom-right-radius: 3px; }

/* Faint on the corner nearest the pointer, full on the grip under it. */
.popout-grip.is-near::before {
    opacity: 0.45;
}

.popout-grip.is-near:hover::before {
    opacity: 1;
}


.audio-cover-art {
    width: 100%;
    aspect-ratio: 1;
    align-self: start;
    padding: 0;
    border: none;
    border-radius: 10px;
    overflow: hidden;
    background: var(--file-box-line);
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
    cursor: zoom-in;
    transition: filter 0.2s ease;
}

.audio-cover-art:hover {
    filter: brightness(1.1);
}

.audio-cover-art img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.audio-track-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.audio-track-title {
    font-size: 1.05em;
    font-weight: 600;
    color: var(--file-box-title);
    line-height: 1.25;
}

.audio-track-byline {
    font-size: 0.8em;
    color: var(--file-box-body);
    line-height: 1.25;
}

.audio-filename {
    font-size: 0.8em;
    color: var(--file-box-title);
    line-height: 1.25;
}

/* Visual feedback on hover */
.audio-waveform:hover {
    opacity: 1;
}

.audio-waveform:hover .waveform-bar {
    transition: opacity 0.15s ease;
}

.waveform-bar {
    flex: 1;
    min-width: 2px;
    background: var(--icon-color-primary);
    border-radius: 1.5px;
    height: 20px;
    transform: translateY(-9px) scaleY(0.15);
    transform-origin: bottom center;
    will-change: transform;
    transition: opacity 0.1s ease, box-shadow 0.2s ease;
}

.custom-audio-player-inner.playing .audio-waveform {
    opacity: 1;
}

.audio-time-display {
    font-size: 0.72em;
    color: var(--file-box-body);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
    min-width: 72px;
    text-align: right;
}

.audio-time-display .current-time {
    color: var(--file-box-dim);
    transition: color 0.2s ease;
}

.audio-time-display .current-time.is-moving {
    color: var(--file-box-title);
}

.audio-time-sep {
    margin: 0 4px;
    color: var(--file-box-dim);
}

/* The file box's action button: grey until the transcript it opens is showing. */
.audio-transcribe-btn {
    margin-left: 10px;
}

.audio-transcribe-btn .icon {
    background-color: var(--file-box-glyph);
    width: 17px;
    height: 17px;
}

.audio-transcribe-btn.is-open .icon {
    background-color: var(--icon-color-primary);
}

.audio-transcribe-btn.loading {
    cursor: default;
}

.network-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 15px;
}

.relay-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 15px;
  /* A row is a button, not a paragraph. */
  -webkit-user-select: none;
  user-select: none;
  background: rgba(0, 0, 0, 0.3);
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  transition: all 0.3s ease;
}

.relay-item:hover {
  background: rgba(0, 0, 0, 0.5);
  border-color: rgba(255, 255, 255, 0.2);
}

.relay-url {
  flex: 1;
  font-size: 14px;
  color: rgba(255, 255, 255, 0.8);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-right: 15px;
  min-width: 0;
}

.relay-status {
  font-size: 12px;
  font-weight: 600;
  padding: 2px 10px;
  border-radius: 20px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  white-space: nowrap;
}

.relay-status.connected {
  background: rgba(90, 252, 180, 0.1);
  color: #59fcb3;
  border: 1px solid rgba(90, 252, 180, 0.5);
}

.relay-status.connecting {
  background: rgba(255, 193, 7, 0.2);
  color: #ffc107;
  border: 1px solid rgba(255, 193, 7, 0.3);
}

.relay-status.disconnected {
  background: rgba(255, 82, 82, 0.2);
  color: #ff5252;
  border: 1px solid rgba(255, 82, 82, 0.3);
}

.relay-status.pending {
  background: rgba(156, 39, 176, 0.2);
  color: #9c27b0;
  border: 1px solid rgba(156, 39, 176, 0.3);
}

.relay-status.initialized,
.relay-status.terminated,
.relay-status.banned {
  background: rgba(128, 128, 128, 0.2);
  color: #808080;
  border: 1px solid rgba(128, 128, 128, 0.3);
}

.relay-status.disabled {
  background: rgba(128, 128, 128, 0.15);
  color: #666;
  border: 1px solid rgba(128, 128, 128, 0.2);
}

.relay-status.sleeping {
  background: rgba(100, 149, 237, 0.2);
  color: #6495ed;
  border: 1px solid rgba(100, 149, 237, 0.3);
}

/* Relay Item Interactive Controls */
.relay-item {
  cursor: pointer;
}

.relay-item.disabled {
  opacity: 0.5;
}

.relay-item-content {
  display: flex;
  align-items: center;
  flex: 1;
  gap: 10px;
  min-width: 0;
  overflow: hidden;
}

.relay-mode-badge {
  font-size: 10px;
  font-weight: 600;
  padding: 2px 6px;
  border-radius: 4px;
  background: rgba(139, 92, 246, 0.2);
  color: #8b5cf6;
  text-transform: uppercase;
}

.relay-item-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: 10px;
}

.relay-toggle {
  position: relative;
  width: 36px;
  height: 20px;
  appearance: none;
  background: rgba(128, 128, 128, 0.3);
  border-radius: 10px;
  cursor: pointer;
  transition: background 0.3s ease;
}

.relay-toggle:checked {
  background: rgba(90, 252, 180, 0.4);
}

.relay-toggle::before {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  background: #fff;
  border-radius: 50%;
  transition: transform 0.3s ease;
}

.relay-toggle:checked::before {
  transform: translateX(16px);
}

.relay-default-badge {
  font-size: 9px;
  padding: 2px 5px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.1);
  color: rgba(255, 255, 255, 0.5);
  text-transform: uppercase;
}

/* Relay Dialog Styles */
.relay-dialog-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.85);
  backdrop-filter: blur(8px);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 10000;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.relay-dialog-overlay.active {
  display: flex;
  opacity: 1;
}

.relay-dialog {
  background: rgba(20, 20, 20, 0.95);
  border: 1px solid #262626;
  border-radius: 16px;
  width: min(420px, calc(100vw - 40px));
  max-height: calc(100vh - 80px);
  overflow: hidden;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}

.relay-dialog-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15px 20px;
  border-bottom: 1px solid #333333;
  background: #262626;
}

.relay-dialog-header h3 {
  margin: 0;
  font-size: 18px;
  color: #fff;
  word-break: break-all;
  padding-right: 10px;
}

.relay-dialog-close {
  background: none;
  border: none;
  font-size: 24px;
  color: rgba(255, 255, 255, 0.6);
  cursor: pointer;
  padding: 0;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  transition: all 0.2s ease;
  flex-shrink: 0;
}

.relay-dialog-close:hover {
  background: rgba(255, 255, 255, 0.1);
  color: #fff;
  transform: scale(1.1);
  transition: transform 0.25s ease;
}

.relay-info-header-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

#relay-info-done {
  border: 2px solid #474747;
}

.relay-dialog-content {
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 15px;
  overflow-y: auto;
  max-height: calc(100vh - 200px);
}

.relay-form-group {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

.relay-form-group select {
    width: 160px;
    margin: 0;
    margin-left: auto;
}

.relay-form-label {
  font-size: 13px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.8);
  flex-shrink: 0;
}

.relay-form-input,
.relay-form-select {
  padding: 10px 12px;
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid #262626;
  border-radius: 8px;
  color: #fff;
  font-size: 14px;
  transition: border-color 0.2s ease;
}

.relay-form-input:focus,
.relay-form-select:focus {
  outline: none;
  border-color: var(--primary-color);
}

.relay-form-select {
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M2.5 4.5L6 8L9.5 4.5' stroke='rgba(255,255,255,0.6)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
  flex: none;
  padding: 6px 24px 6px 14px;
  font-size: 13px;
  width: 160px;
}

.relay-form-select:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.relay-form-hint {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.5);
  margin: 0;
}

.relay-dialog-buttons {
  display: flex;
  gap: 10px;
  margin-top: 10px;
}

.relay-dialog-buttons .btn {
  flex: 1;
  padding: 10px 15px;
}

.relay-dialog-buttons .primary-btn:hover {
  opacity: 0.8;
}

/* Relay Info Dialog Specific */
.relay-metrics-section h4,
.relay-logs-section h4 {
  font-size: 13px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.7);
  margin: 0;
}

.relay-metrics-header,
.relay-logs-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.relay-metrics-inline {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 13px;
}

.relay-metric-inline-label {
    color: rgba(255, 255, 255, 0.5);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.relay-metric-inline-value {
    color: #59fcb3;
    font-weight: 600;
}

.relay-metric-inline-divider {
    color: rgba(255, 255, 255, 0.2);
}

.relay-metric-inline-right {
    margin-left: auto;
}

.relay-status-small {
  font-size: 10px;
  padding: 2px 8px;
}

.relay-logs-copy-btn {
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  background: transparent;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  transition: background 0.2s ease;
}

.relay-logs-copy-btn:hover {
  background: rgba(255, 255, 255, 0.1);
}

.relay-logs-copy-btn .icon {
  width: 14px;
  height: 14px;
  background-color: rgba(255, 255, 255, 0.5);
}

.relay-logs-copy-btn:hover .icon {
  background-color: rgba(255, 255, 255, 0.8);
}

.relay-metric-value {
  display: block;
  font-size: 16px;
  font-weight: 500;
}

.relay-metric-inline-right {
    margin-left: auto;
}

.relay-metric-label {
  display: block;
  font-size: 10px;
  color: rgba(255, 255, 255, 0.5);
  text-transform: uppercase;
  margin-top: 4px;
}

.relay-logs-list {
  list-style: none;
  padding: 0;
  margin: 0;
  background: rgba(0, 0, 0, 0.3);
  border-radius: 8px;
  max-height: 150px;
  overflow-y: auto;
  margin-top: 10px;
}

.relay-logs-list li {
  padding: 6px 12px;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.7);
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.relay-logs-list li:last-child {
  border-bottom: none;
}

.relay-log-empty {
  color: rgba(255, 255, 255, 0.4);
  font-style: italic;
  text-align: center;
}

.relay-log-time {
  font-size: 10px;
  color: rgba(255, 255, 255, 0.4);
  margin-right: 8px;
}

.relay-log-message.info { color: rgba(255, 255, 255, 0.7); }
.relay-log-message.warn { color: #ffc107; }
.relay-log-message.error { color: #ff5252; }

/* Add Relay Button */
.relay-section-header {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
}

.blossom-cap-blurb {
  margin: 8px 0 12px;
  font-size: 12px;
  line-height: 1.45;
  opacity: 0.55;
}
.blossom-cap-slot {
  margin-top: 8px;
  font-size: 13px;
  opacity: 0.85;
}
.blossom-cap-group-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-top: 8px;
  margin-bottom: 4px;
  opacity: 0.7;
}
.blossom-cap-accepted { color: #59fcb3; }
.blossom-cap-limited { color: #ffc107; }
.blossom-cap-rejected { color: var(--danger-color); }
.blossom-cap-list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.blossom-cap-list li {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 4px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  font-size: 13px;
}
.blossom-cap-list li:last-child { border-bottom: none; }
.blossom-cap-marker {
  display: inline-block;
  width: 14px;
  text-align: center;
  font-weight: 700;
  font-size: 12px;
  flex-shrink: 0;
}
.blossom-cap-marker.blossom-cap-accepted { color: #59fcb3; }
.blossom-cap-marker.blossom-cap-rejected { color: var(--danger-color); }
.blossom-cap-mime {
  flex: 1;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
  opacity: 0.85;
}
.blossom-cap-rejected-row .blossom-cap-mime { opacity: 0.5; }
.blossom-cap-limited-row .blossom-cap-mime { opacity: 0.75; }
.blossom-cap-context {
  font-size: 9px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  padding: 2px 5px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.55);
}
.blossom-cap-size {
  font-size: 11px;
  opacity: 0.5;
  margin-left: auto;
}

/* Media server: the account's plan, from the server's own document */
.blossom-plan {
  border-radius: 12px;
  padding: 12px 14px 14px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.08);
  display: flex;
  flex-direction: column;
  gap: 12px;
  transition: border-color 0.25s ease;
}
.blossom-plan-skeleton {
  opacity: 0.5;
}
.blossom-plan-skeleton .blossom-plan-name,
.blossom-plan-skeleton .blossom-plan-perfile-value,
.blossom-plan-skeleton .blossom-meter-value {
  display: inline-block;
  min-width: 4em;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.06);
}
.blossom-plan-premium {
  border-color: transparent;
  background:
    linear-gradient(rgba(20, 20, 20, 0.98), rgba(20, 20, 20, 0.98)) padding-box,
    linear-gradient(135deg, var(--primary-color, #33DB98), var(--accent-color, #59fcb3)) border-box;
}
.blossom-plan-none {
  border-color: rgba(252, 89, 92, 0.35);
}
.blossom-plan-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}
.blossom-plan-title {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.blossom-plan-eyebrow {
  color: rgba(255, 255, 255, 0.5);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.blossom-plan-name {
  font-size: 22px;
  font-weight: 700;
  line-height: 1.1;
  color: #fff;
}
.blossom-plan-premium .blossom-plan-name {
  background: linear-gradient(135deg, var(--primary-color, #33DB98), var(--accent-color, #59fcb3));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.blossom-plan-none .blossom-plan-name { color: var(--danger-color); }
.blossom-plan-blurb {
  font-size: 12px;
  line-height: 1.4;
  opacity: 0.55;
  margin-top: 4px;
}
.blossom-plan-perfile {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  flex-shrink: 0;
  text-align: right;
}
.blossom-plan-perfile-value {
  font-size: 22px;
  font-weight: 700;
  line-height: 1.1;
  color: var(--accent-color, #59fcb3);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.blossom-plan-perfile-label {
  color: rgba(255, 255, 255, 0.5);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-top: 3px;
}
.blossom-plan-meters {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.blossom-plan-refused {
  margin: 0;
  font-size: 13px;
  line-height: 1.45;
  color: rgba(255, 255, 255, 0.75);
}
.blossom-meter {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.blossom-meter-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
/* The number never wraps; the label gives way and ellipsises its note. */
.blossom-meter-label {
  color: rgba(255, 255, 255, 0.5);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.blossom-meter-value {
  color: #fff;
  font-weight: 600;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  flex-shrink: 0;
}
.blossom-meter-of {
  color: rgba(255, 255, 255, 0.45);
  font-weight: 400;
}
.blossom-meter-track {
  height: 6px;
  width: 100%;
  background: rgba(255, 255, 255, 0.08);
  border-radius: 3px;
  overflow: hidden;
}
.blossom-meter-fill {
  height: 100%;
  min-width: 3px;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--primary-color, #33DB98) 0%, var(--accent-color, #59fcb3) 100%);
  transition: width 0.3s cubic-bezier(0.65, 0, 0.35, 1);
}
.blossom-meter-fill.blossom-meter-high {
  background: linear-gradient(90deg, #ffc107 0%, #ff9800 100%);
}
.blossom-meter-fill.blossom-meter-full {
  background: var(--danger-color);
}
.blossom-meter-note {
  text-transform: none;
  letter-spacing: 0;
  opacity: 0.7;
}
.relay-latency {
  font-size: 11px;
  opacity: 0.45;
  font-variant-numeric: tabular-nums;
  margin-right: 8px;
  white-space: nowrap;
}

/* Network dialogs: the QR overlay's pop in and out, on the relay dialog's card */
.pop-dialog-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.85);
  backdrop-filter: blur(8px);
  z-index: 10000;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* Hidden until `pop-in` lands a frame later, so the card's first painted
   frame is the start of its animation, not a flash of the end state. */
.pop-dialog { opacity: 0; }
.pop-dialog.pop-in { animation: qr-overlay-pop 0.16s ease-out forwards; }
.pop-dialog-overlay.closing {
  animation: modal-overlay-fade-out 0.15s ease-out forwards;
  pointer-events: none;
}
.pop-dialog-overlay.closing .pop-dialog {
  animation: modal-card-pop-out 0.15s ease-out forwards;
}

/* The server or relay this dialog is about, in place of a titled header bar. */
.pop-dialog-host {
  flex-shrink: 0;
  margin: 0;
  min-width: 0;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.5);
  font-variant-numeric: tabular-nums;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Media server dialog: no header bar — the host is a quiet line above the tiles */
.blossom-dialog-content {
  padding-top: 16px;
  gap: 10px;
  /* A tooltip past the edge must not summon a horizontal scrollbar. */
  overflow-x: hidden;
}
/* Media server: shown only when something is wrong */
.blossom-notice {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 10px;
  font-size: 13px;
  line-height: 1.4;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.08);
}
.blossom-notice-connecting { border-color: rgba(255, 193, 7, 0.35); background: rgba(255, 193, 7, 0.06); }
.blossom-notice-disconnected { border-color: rgba(255, 82, 82, 0.35); background: rgba(255, 82, 82, 0.06); }
.blossom-notice .relay-status { flex-shrink: 0; }
.blossom-notice-text { opacity: 0.8; }

/* Media server: this device's history with it */
.blossom-perf {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}
.blossom-perf-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 10px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.06);
  min-width: 0;
}
.blossom-perf-label {
  color: rgba(255, 255, 255, 0.5);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.blossom-perf-value {
  color: #fff;
  font-size: 16px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.blossom-perf-unit {
  color: rgba(255, 255, 255, 0.45);
  font-size: 11px;
  font-weight: 400;
}
.blossom-perf-chart {
  position: relative;
  margin-top: 8px;
  touch-action: pan-y;
}
.blossom-perf-spark {
  display: block;
  width: 100%;
  height: 44px;
  overflow: visible;
}
/* The hovered upload's numbers, floated above its dot. */
.blossom-perf-dot { transition: stroke-width 0.15s ease-out; }
.blossom-perf-tip {
  position: absolute;
  transform: translate(-50%, calc(-100% - 8px));
  /* Position is driven per frame by the component; only the edge anchoring eases here. */
  transition: transform 0.15s ease-out;
  padding: 3px 7px;
  border-radius: 6px;
  background: rgba(20, 20, 20, 0.96);
  border: 1px solid rgba(255, 255, 255, 0.12);
  font-size: 11px;
  white-space: nowrap;
  pointer-events: none;
  font-variant-numeric: tabular-nums;
}
.blossom-perf-tip b { color: var(--accent-color, #59fcb3); font-weight: 600; }
.blossom-perf-tip-left { transform: translate(-4px, calc(-100% - 8px)); }
.blossom-perf-tip-right { transform: translate(calc(-100% + 4px), calc(-100% - 8px)); }
.blossom-perf-foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 2px 8px;
  font-size: 11px;
  opacity: 0.45;
  margin-top: 4px;
  font-variant-numeric: tabular-nums;
}

.relay-add-btn {
  width: 24px;
  height: 24px;
  border-radius: 200px;
  padding: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  font-weight: bold;
  transition: all 0.2s ease;
}


/* Invites Tab Styles */
#invites {
  overflow-y: auto;
  height: -webkit-fill-available;
  overscroll-behavior: none;
  animation: fadeInWithoutTransform 0.4s ease-out;
}

.invites-container {
  max-width: 600px;
  margin: 0 auto;
  padding: 20px;
}

.invites-header {
  text-align: center;
  margin-bottom: 20px;
  margin-top: 20px;
}

.invites-main-icon {
  width: 80px;
  height: 80px;
  margin: 0 auto 20px;
  display: block;
  position: relative;
  opacity: 0.9;
  background-color: #59fcb3;
}

.invites-title {
  font-size: 32px;
  font-weight: 600;
  margin: 0;
  letter-spacing: 1px;
  color: #59fcb3;
}

.invites-content {
  text-align: center;
}

.invites-description {
  font-size: 16px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.8);
  margin-bottom: 28px;
}

.invite-code-section {
  background: rgba(0, 0, 0, 0.3);
  border-radius: 16px;
  padding: 30px;
  border: 1px solid rgba(89, 252, 179, 0.2);
}

.invite-code-label {
  font-size: 18px;
  margin-bottom: 20px;
  color: rgba(255, 255, 255, 0.9);
  margin-top: 0px;
}

.invite-code-container {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 15px;
  margin-bottom: 15px;
}

.invite-code {
  font-size: 36px;
  font-weight: 700;
  letter-spacing: 4px;
  color: #59fcb3;
  font-family: 'Courier New', monospace;
  text-transform: uppercase;
  user-select: all;
}

.invite-code-copy-btn {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: linear-gradient(135deg, #59fcb3 0%, #2b976c 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.3s ease;
  border: none;
  flex-shrink: 0;
}

.invite-code-copy-btn:hover {
  transform: scale(1.1);
  box-shadow: 0 0 20px rgba(89, 252, 179, 0.5);
}

.invite-code-copy-btn .icon {
  width: 25px;
  height: 25px;
  background-color: #000;
}

.invite-code-twitter-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 16px;
  padding: 12px 24px;
  color: white;
  text-decoration: none;
  border-radius: 25px;
  font-weight: 600;
  transition: all 0.3s ease;
  font-size: 16px;
}

.invite-code-twitter-link:hover {
  transform: scale(1.05);
  color: white;
}

.invite-code-twitter-link svg {
  width: 20px;
  height: 20px;
  fill: white;
}

.invite-code-hint {
  font-size: 14px;
  color: rgba(255, 255, 255, 0.6);
  margin: 0;
}






/* Update UI Layout Styles */
.update-info {
  text-align: center;
}

.update-info p {
  margin-bottom: 15px;
  font-size: 16px;
}

/* Shown only on preview builds; `display` is toggled to block from JS. */
.update-preview-notice {
  margin-bottom: 15px;
  padding: 12px 14px;
  text-align: left;
  border-radius: 8px;
  background-color: var(--danger-color-hover);
  border: 1px solid color-mix(in srgb, var(--danger-color) 40%, transparent);
}

/* Rides the text baseline so it reads as the first word, not a gutter icon. */
.update-preview-notice .warning-icon {
  vertical-align: -3px;
  margin-right: 6px;
}

/* margin-top too: there's no global `p` reset, so the UA's 1em block-start
   would otherwise stack on top of the card's own padding. */
.update-preview-notice p {
  margin-top: 0;
  margin-bottom: 8px;
  font-size: 13px;
  line-height: 1.45;
  opacity: 0.85;
}

.update-preview-notice p:last-child {
  margin-bottom: 0;
}

.update-preview-notice strong {
  color: var(--danger-color);
  opacity: 1;
}

#check-updates-btn {
  margin: 0 auto;
  padding: 8px 24px;
}

#update-status-text {
  font-size: 14px;
  margin-top: 10px;
  transition: color 0.3s ease;
}

#update-progress-container {
  margin: 20px auto;
  max-width: 300px;
}

#download-update-btn,
#restart-update-btn {
  margin: 10px auto;
  font-weight: bold;
  transition: all 0.3s ease;
}

#download-update-btn:hover,
#restart-update-btn:hover {
  transform: translateY(-2px);
}

#download-update-btn:active,
#restart-update-btn:active {
  transform: translateY(0);
}

#changelog-content {
  text-align: left;
  padding: 15px;
  border-radius: 8px;
  max-height: 200px;
  overflow-y: auto;
  font-size: 14px;
  line-height: 1.6;
}

/* Update Changelog Styles */
#update-changelog {
  max-width: 400px;
  margin-left: auto;
  margin-right: auto;
}

#changelog-content {
  max-width: 400px;
  margin-left: auto;
  margin-right: auto;
}

/* Member picker row (shared by Create Group & Invite modals) */
.member-pick-row {
    display: flex;
    align-items: center;
    padding: 5px 10px;
    border-radius: 6px;
    transition: background 0.2s ease;
    isolation: isolate;
    cursor: pointer;
    position: relative;
    /* A row is a control, not text: a drag or a double-click selects a person, not
       their name. Every people list shares this row. */
    -webkit-user-select: none;
    user-select: none;
}

/* Right-aligned moderation handle on a member row: a "\22EF" that opens the same menu as
   right-click. Always visible by default (so it works on touch, which has no hover). On
   pointer devices it stays out of the way until the row is hovered, keeping the list
   scannable while the tools are one hover away. */
.member-pick-actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
}
@media (hover: hover) {
    /* pointer-events, not just opacity: an invisible element still hit-tests, so a
       pointer crossing the hidden handle lit its :hover and the reveal then faded a
       BRIGHT icon in, which dimmed again as the pointer moved on. Appearing without
       a transition is the rest of it — the handle is a hint, and a hint that ramps
       is a hint you watch. */
    .member-pick-actions {
        opacity: 0;
        pointer-events: none;
    }
    .member-pick-row:hover .member-pick-actions,
    .member-pick-actions:focus-within {
        opacity: 1;
        pointer-events: auto;
    }
}

/* `.icon` is absolutely positioned to fill its nearest positioned ancestor, so the handle
   has to BE that box rather than inheriting the row's. Mask icons tint via background. */
.member-pick-more {
    position: relative;
    width: 24px;
    height: 24px;
    border-radius: 6px;
    flex-shrink: 0;
    z-index: 1;
    cursor: pointer;
    transition: background-color 0.12s ease;
}

/* Rests dim and only brightens under the pointer — the handle is a hint, not a
   third thing competing with the name and avatar for attention. */
.member-pick-more .icon {
    background-color: rgba(255, 255, 255, 0.4);
    transition: background-color 0.12s ease;
}

/* Safe to brighten the glyph again now the hidden handle is pointer-events: none —
   :hover can only fire on one that is actually on screen, so this can't run during
   the reveal. Eased rather than snapped, in step with the plate behind it. */
.member-pick-more:hover {
    background-color: rgba(255, 255, 255, 0.09);
}

.member-pick-more:hover .icon {
    background-color: #fff;
}

.member-pick-row .member-pick-hover {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    border-radius: 6px;
    opacity: 0;
    transition: opacity 0.2s ease;
    pointer-events: none;
    z-index: 0;
}

.member-pick-row:hover .member-pick-hover {
    opacity: 1;
}

.member-pick-row .member-pick-avatar {
    margin-right: 10px;
    flex-shrink: 0;
    position: relative;
    z-index: 1;
}

/* Name + inline markers (bot chip) for a member-style row. The cell owns the flex
   growth and, crucially, `min-width: 0`: a flex item defaults to `min-width: auto`,
   which refuses to shrink below its content and shoved the Kick/Ban buttons clean out
   of the member list. The name truncates here so the row's trailing controls always
   keep their space. */
.member-pick-identity {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 1;
    min-width: 0;
    position: relative;
    z-index: 1;
}

/* Name and its marks (rank, bot) share one line; the identity cell stacks this over
   any status line. Without it the marks wrap under the name. */
.member-pick-nameline {
    display: flex;
    align-items: center;
    gap: 4px;
    min-width: 0;
    max-width: 100%;
}

.compact-member-name {
    color: #f7f4f4;
    font-size: 14px;
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-align: left;
    position: relative;
    z-index: 1;
}

/* Bot marker, same glyph + accent the chat list and profile use. Sized to the name's
   cap height; `position: relative` and `margin: 0` undo the base `.icon` absolute
   inset + auto margins (which would centre it and push its siblings apart). */
/* The key beside a name: quiet, but there to check against. */
.member-pick-keyhint {
    flex-shrink: 0;
    margin-left: 8px;
    color: #6a6a6a;
    font-family: var(--font-mono);
    font-size: 11.5px;
    white-space: nowrap;
}

.member-pick-bot {
    position: relative;
    width: 14px;
    height: 14px;
    margin: 0;
    flex-shrink: 0;
    display: inline-block;
    background-color: var(--icon-color-primary, #59fcb3);
}

/* Compact member list emoji styling */
.compact-member-name img.emoji {
    width: 15px !important;
    height: 15px !important;
    vertical-align: middle !important;
}

/* A roster row: name and rank on top, the person's status beneath, one line that
   ellipsises. Every layout, not just widescreen: the row is the same list narrow. */
#group-overview .member-pick-identity {
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
}

#group-overview .member-pick-status {
    font-size: 12px;
    line-height: 1.35;
    color: rgba(255, 255, 255, 0.38);
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Roster sections (Admin, Members, a role): same language as the channel pane's.
   Every layout: the roster is the same list narrow. */
.member-section + .member-section {
  margin-top: 12px;
}

.member-section-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 6px 4px;
  border-radius: 6px;
  opacity: 1;
}

.member-section-label {
  font-family: 'Rubik', Inter, Avenir, Helvetica, Arial, sans-serif;
  font-size: 15px;
  color: rgba(255, 255, 255, 0.38);
  transition: color 0.12s ease;
}

.member-section-count {
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: rgba(255, 255, 255, 0.24);
}

.member-section-head:hover .member-section-label {
  color: rgba(255, 255, 255, 0.7);
}

.member-section-caret {
  position: relative;
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  margin-left: auto;
}

.member-section-caret .icon {
  background-color: rgba(255, 255, 255, 0.32);
  transition: transform 0.15s ease, background-color 0.12s ease;
}

.member-section.is-closed .member-section-caret .icon {
  transform: rotate(-90deg);
}

.member-section.is-closed .member-section-body {
  display: none;
}

/* The windowed roster: a spacer of the whole list's height, with only the items
   near the viewport mounted and placed absolutely. Fixed item heights are what the
   layout is computed from (MemberRoster.svelte). */
.member-roster-window {
  position: relative;
}
.member-roster-window .member-section-head,
.member-roster-window .member-roster-slot {
  position: absolute;
  left: 0;
  right: 0;
  box-sizing: border-box;
}
.member-roster-window .member-section-head {
  height: 36px;
}
.member-roster-window .member-section-head.is-closed .member-section-caret .icon {
  transform: rotate(-90deg);
}
.member-roster-window .member-roster-slot,
.member-roster-window .member-pick-row {
  height: 46px;
}
.member-roster-window .member-pick-row {
  box-sizing: border-box;
}

/* An emoji in a name or status is inline text, not a picture: unconstrained it
   renders at its natural size and stretches the row around it. */
#group-overview .member-pick-status .emoji,
#group-overview .member-pick-nameline .emoji {
    width: 14px !important;
    height: 14px !important;
    min-width: 0 !important;
    min-height: 0 !important;
    display: inline-block;
    vertical-align: -2px;
    border-radius: 0;
    object-fit: contain;
}

.member-pick-indicator {
    width: 18px;
    height: 18px;
    border-radius: 4px;
    border: 2px solid var(--icon-color-primary);
    position: relative;
    z-index: 1;
    flex-shrink: 0;
    margin-left: auto;
    transition: background-color 0.2s ease;
}

.member-pick-indicator.selected {
    background-color: var(--icon-color-primary);
}

/* An action in flight pins its own indicator visible: the spinner is the ONLY signal that a
   kick/ban/role change is still running, and the hover-reveal above would otherwise hide it the
   moment the pointer leaves the row — reading as though nothing had happened. Must stay after the
   hover rules; both sides carry equal specificity, so source order decides. */
.member-pick-row.is-acting .member-pick-actions { opacity: 1; }

/* Group Overview Edit Controls */
.group-editable {
  cursor: pointer;
  position: relative;
  transition: color 0.15s ease;
  border-radius: 4px;
}
.group-editable:hover {
  color: var(--icon-color-primary) !important;
}
.group-editable::after {
  content: '';
  position: absolute;
  width: 12px;
  height: 12px;
  margin-left: 6px;
  top: 50%;
  transform: translateY(-50%);
  mask-image: url("./icons/edit.svg");
  -webkit-mask-image: url("./icons/edit.svg");
  mask-size: contain;
  -webkit-mask-size: contain;
  mask-repeat: no-repeat;
  -webkit-mask-repeat: no-repeat;
  background-color: currentColor;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.group-editable:hover::after {
  opacity: 0.6;
}
/* Multi-line description: the shared pencil's mid-height absolute anchor floats it
   mid-paragraph on a tall block (and the span's overflow clips any outside pin) —
   let it FLOW inline instead, landing right after the last character of the text. */
#group-overview-description.group-editable::after {
  position: static;
  display: inline-block;
  transform: none;
  vertical-align: -1px;
}


.group-name-input {
  font-size: 18px;
  font-weight: 600;
  color: #f7f4f4;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 6px;
  padding: 6px 12px;
  width: 80%;
  max-width: 300px;
  text-align: center;
  outline: none;
  font-family: inherit;
  transition: border-color 0.2s ease;
}
.group-name-input:focus {
  border-color: var(--icon-color-primary);
}
.group-desc-textarea {
  font-size: 13px;
  color: #f7f4f4;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 6px;
  padding: 8px 12px;
  width: 85%;
  min-height: 60px;
  max-height: 200px;
  resize: none;
  outline: none;
  font-family: inherit;
  line-height: 1.4;
  display: block;
  margin: 10px auto 0;
  transition: border-color 0.2s ease;
}
.group-desc-textarea:focus {
  border-color: var(--icon-color-primary);
}
.group-avatar-edit-overlay {
  position: absolute;
  bottom: 0;
  right: 0;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background-color: rgba(0, 0, 0, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.2s ease;
}
*:hover > .group-avatar-edit-overlay {
  opacity: 1;
}










/* MLS Group Invite Styles */
.chatlist-invite {
  cursor: default !important;
  background-color: rgba(255, 255, 255, 0.05);
}

.chatlist-invite .chatlist-contact-preview h4 {
  color: rgba(255, 255, 255, 1);
}

.chatlist-invite .chatlist-contact-preview p {
  color: rgba(255, 255, 255, 0.85);
}

.chatlist-invite {
    border-color: var(--primary-color);
}

.invite-accept-btn .icon {
    width: 16px;
    height: 16px;
    background-color: #59fcb3;
}

.invite-decline-btn .icon {
    width: 16px;
    height: 16px;
    background-color: var(--danger-color);
}

.invite-action-buttons {
  display: flex;
  flex-direction: row;
  gap: 8px;
  margin-top: auto;
  margin-bottom: auto;
  margin-left: auto;
  padding-right: 10px;
}

.invite-action-btn {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s ease;
  padding: 0;
}

.invite-accept-btn {
  border: 2px solid #59fcb3;
}

.invite-decline-btn {
  border: 2px solid var(--danger-color);
}

.invite-action-btn:hover {
  transform: scale(1.1);
}

.invite-action-btn:active {
  transform: scale(0.95);
}

.invite-accept-btn:hover {
  background: rgba(89, 252, 179, 0.2);
}

.invite-decline-btn:hover {
    background: var(--danger-color-hover);
}


.empty-state {
  text-align: center;
  color: rgba(255, 255, 255, 0.5);
  font-style: italic;
  padding: 20px;
}

/* Create Group - Flexbox layout for responsive list */
.create-group-container {
  display: flex;
  flex-direction: column;
  height: calc(100vh - var(--chrome-h));
  overflow: hidden;
}

.create-group-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding-top: 18px; /* no Back header — Cancel is the way out */
  min-height: 0;
}

/* Override absolute positioning for create-group panel */
.create-group-content .chat-new-center-content {
  position: static;
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

.create-group-content .chat-new-center-content .chat-new-content {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

.create-group-content .chat-new-content:first-child {
  margin-top: 0; /* Remove default margin since padding-top handles it */
  flex-shrink: 0; /* Don't shrink the title/input section */
  /* The shared 50px side margins strangle the name row on narrow windows —
     the row's own 15px padding is the panel's gutter */
  margin-left: 0;
  margin-right: 0;
}

.create-group-list {
  margin-top: 10px;
  flex: 1;
  overflow-y: auto;
  width: 100%;
  box-sizing: border-box;
  min-height: 0; /* Critical for flex children with overflow */
  padding: 6px;
}

.create-group-footer {
  display: flex;
  gap: 10px;
  padding: 15px;
  justify-content: center;
  flex-wrap: wrap;
  flex-shrink: 0;
}
.create-group-footer .btn {
  flex: 1;
  max-width: 220px;
}
/* Solid fill like popupConfirm's Confirm — the one primary action on the panel */
#create-group-create-btn {
  background-color: #58fcb3;
  color: #000;
  font-weight: 600;
}
#create-group-create-btn[disabled] {
  opacity: 0.45;
}

/* Dim placeholders — hints, not content */
#create-group-name::placeholder,
#create-group-filter::placeholder,
#cmt-npub::placeholder {
  color: rgba(255, 255, 255, 0.35);
}

/* Solid subtle hover — not the gradient sweep the other pickers use */
#create-group-list .member-pick-hover,
#cmt-contacts .member-pick-hover,
.cs-bans .member-pick-hover {
  background: rgba(255, 255, 255, 0.085);
}

/* Quiet until chosen: unselected boxes are gray, only a selection earns the green */
#create-group-list .member-pick-indicator,
#cmt-contacts .member-pick-indicator,
.cs-bans .member-pick-indicator {
  border-color: rgba(255, 255, 255, 0.28);
}
#create-group-list .member-pick-indicator.selected,
#cmt-contacts .member-pick-indicator.selected,
.cs-bans .member-pick-indicator.selected {
  border-color: var(--icon-color-primary);
  background-color: var(--icon-color-primary);
}

/* Tighter header rhythm than the shared chat-new panels */
.create-group-content .chat-new-title {
  margin-top: 10px;
}
.create-group-content .chat-new-divider {
  margin-bottom: 16px;
  /* Re-inset: the section's 50px margins were zeroed for the name row.
     width:auto — a 100% divider plus side margins bleeds off the panel edge. */
  width: auto;
  margin-left: 40px;
  margin-right: 40px;
}

/* Status line rides ABOVE the buttons and reveals smoothly (mirrors .cmt-status):
   content-driven — JS only sets textContent, empty collapses shut. */
.create-group-footer .chat-contact-status {
  width: 100%;
  text-align: center;
  font-size: 13px;
  font-style: normal;
  opacity: 0;
  max-height: 0;
  margin: 0;
  overflow: hidden;
  transition: max-height 0.25s ease, opacity 0.2s ease, margin 0.25s ease;
}
.create-group-footer .chat-contact-status:not(:empty) {
  opacity: 0.85;
  max-height: 60px;
  margin: 0 0 8px;
}
/* Selection-counter pill: two-user glyph + count in a soft capsule */
.create-group-selection-pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 14px 5px 12px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.04);
}
.cg-pill-glyph {
  position: relative; /* the absolute base .icon fills THIS box, not an ancestor */
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}
.cg-pill-glyph .icon {
  width: 100%;
  height: 100%;
  background-color: var(--icon-color-primary);
}

/* Create Group Avatar Picker */
.create-group-avatar-picker {
  width: 56px;
  height: 56px;
  min-width: 56px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  overflow: visible;
  transition: opacity 0.2s;
  position: relative;
  opacity: 0.7;
}
.create-group-avatar-picker:hover {
  opacity: 1;
}
.create-group-avatar-picker.has-image {
  opacity: 1;
}
.create-group-avatar-placeholder {
  /* The SVG carries its own disc — sizing it down would draw a second ring.
     Grayed until a real image is picked; the mint accent belongs to the (+). */
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
  /* contrast<1 pulls toward mid-gray: lifts the near-black disc into a visible
     gray while dimming the mint glyph — two tones from one shared SVG */
  filter: grayscale(1) contrast(0.7) brightness(0.5);
}
.create-group-avatar-preview {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
}
.create-group-avatar-edit {
  position: absolute;
  bottom: -1px;
  right: -1px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background-color: rgba(0, 0, 0, 0.85);
  display: flex;
  align-items: center;
  justify-content: center;
}
.create-group-avatar-edit .icon {
  width: 19px;
  height: 19px;
  background-color: var(--icon-color-primary);
}

/* The centered flex column shrink-wraps children — the name row must span it */
.create-group-name-row {
  width: 100%;
  box-sizing: border-box;
}

/* Name field wraps the input so the requirement hint can sit inside it */
.create-group-name-wrap {
  position: relative;
  flex: 1;
  display: flex;
  align-items: center;
  min-width: 0;
}
.create-group-name-wrap .chat-new-input {
  width: 100%;
  box-sizing: border-box;
  height: auto;
  margin: 0; /* chat-input-container's 8px margins break alignment with the search box */
  padding: 10px 86px 10px 12px; /* right inset: typed text never runs under the hint */
  background-color: transparent;
  border: 1px solid rgba(57, 57, 57, 0.5);
  border-radius: 8px;
  color: #fff;
  font-size: 16px;
}
.create-group-name-required {
  position: absolute;
  right: 14px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 12px;
  font-weight: 600;
  color: #ff2e88;
  opacity: 0;
  transition: opacity 0.2s ease;
  pointer-events: none;
}
/* The hint surfaces only once members are picked (JS toggles .needs-name) while
   the name is still empty — and fades the moment a name exists */
.create-group-name-wrap.needs-name input:placeholder-shown + .create-group-name-required {
  opacity: 0.9;
}

/* Disable resize grip on touch devices (Android WebView renders it as a broken 1px dot) */
@media (pointer: coarse) {
  .publish-app-field textarea {
    resize: none;
  }
}

/* Override: show real checkboxes inside Create Group panel only */
#create-group input[type="checkbox"] {
  display: inline-block !important;
  -webkit-appearance: checkbox;
  appearance: checkbox;
}

/* Image Viewer Modal */
.image-viewer-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.95);
  z-index: 10000;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: zoom-out;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.image-viewer-overlay.active {
  opacity: 1;
}

.image-viewer-container {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  touch-action: none;
}

.image-viewer-image {
  position: absolute;
  top: 0;
  left: 0;
  max-width: 95vw;
  max-height: 95vh;
  object-fit: contain;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  -moz-user-drag: none;
  user-drag: none;
  pointer-events: auto;
  transition: transform 0.1s ease-out;
  transform-origin: top left;
}

.image-viewer-image.dragging {
  cursor: grabbing !important;
  transition: none;
}

.image-viewer-image.no-anim {
  transition: none !important;
}

.image-viewer-image.zoomed {
  cursor: grab;
}

.image-viewer-close {
  position: absolute;
  top: 20px;
  right: 20px;
  width: 40px;
  height: 40px;
  background-color: #17171770;
  border: none;
  border-radius: 50%;
  cursor: pointer;
  z-index: 10001;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 0.2s ease;
}

.image-viewer-close:hover {
  background-color: #171717;
}

/* Viewer options — vertical column stacked below the close button */
.image-viewer-controls {
  position: absolute;
  top: 70px; /* close btn: 20px top + 40px tall + 10px gap */
  right: 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  z-index: 10001;
}
.image-viewer-ctrl-btn {
  width: 40px;
  height: 40px;
  padding: 9px;
  background-color: #17171770;
  border: none;
  border-radius: 50%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  transition: background-color 0.2s ease;
}
.image-viewer-ctrl-btn:hover {
  background-color: #171717;
}
.image-viewer-ctrl-btn svg {
  width: 100%;
  height: 100%;
}

.image-viewer-close::before,
.image-viewer-close::after {
  content: '';
  position: absolute;
  width: 20px;
  height: 2px;
  background-color: white;
}

.image-viewer-close::before {
  transform: rotate(45deg);
}

.image-viewer-close::after {
  transform: rotate(-45deg);
}

.image-viewer-zoom-info {
  position: absolute;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  background-color: rgba(0, 0, 0, 0.7);
  color: white;
  padding: 8px 16px;
  border-radius: 20px;
  font-size: 12px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease;
  z-index: 10001;
}

.image-viewer-zoom-info.visible {
  opacity: 1;
}

.image-viewer-tip {
  position: absolute;
  top: 20px;
  left: 50%;
  transform: translateX(-50%);
  background-color: rgba(0, 0, 0, 0.7);
  color: white;
  padding: 8px 16px;
  border-radius: 20px;
  font-size: 12px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.3s ease;
  z-index: 10001;
}

.image-viewer-tip.visible {
  opacity: 1;
}

/* Markdown Rendering Styles */

/* Collapsible sections (details/summary) */
details {
  margin: 8px 0;
  padding: 10px;
  border: 1px solid rgba(89, 252, 179, 0.2);
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.2);
  overflow: hidden;
}

details summary {
  cursor: pointer;
  padding: 10px 12px;
  border-radius: 8px;
  background: rgba(89, 252, 179, 0.1);
  font-weight: 500;
  user-select: none;
  list-style: none;
  opacity: 0.8;
  transition: opacity 0.2s ease;
}

details summary::-webkit-details-marker {
  display: none;
}

details summary::before {
  content: '▶';
  display: inline-block;
  margin-right: 8px;
  transition: transform 0.2s ease;
  color: #59fcb3;
}

details[open] summary::before {
  transform: rotate(90deg);
}

details summary:hover {
  opacity: 1;
}

details[open] summary {
  border-bottom: 1px solid rgba(89, 252, 179, 0.2);
  margin-bottom: 10px;
  border-bottom-right-radius: 0;
  border-bottom-left-radius: 0;
}

details > *:not(summary) {
  padding: 12px !important;
  margin-top: 8px;
}

details > p,
details > div,
details > ul,
details > ol {
  padding: 12px !important;
}

/* Code blocks inside details need horizontal scroll */
details pre {
  overflow-x: auto;
  margin: 8px 0 !important;
  padding: 12px !important;
}

/* Emojis in Detaol blocks shouldn't have any padding */
details .emoji {
  padding: 0 !important;
  margin: 0;
}

/* Code block wrapper with copy button */
/* Browsers hoist this block element out of the parent <p> tag, so we must
   cap width explicitly to prevent overflow. */
.code-block-wrapper {
  position: relative;
  margin: 8px 0;
  max-width: min(470px, calc(100vw - 100px));
  min-width: 0;
  box-sizing: border-box;
}

.code-copy-btn {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 32px;
  height: 32px;
  padding: 0;
  background: rgba(0, 0, 0, 0.6);
  border: 1px solid color-mix(in srgb, var(--icon-color-primary, #59fcb3) 30%, transparent);
  border-radius: 6px;
  cursor: pointer;
  opacity: 0.7;
  transition: all 0.2s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
}

.code-copy-btn:hover {
  opacity: 1;
  background: rgba(0, 0, 0, 0.8);
  border-color: color-mix(in srgb, var(--icon-color-primary, #59fcb3) 50%, transparent);
  transform: scale(1.05);
}

.code-copy-btn.copied {
  background: color-mix(in srgb, var(--icon-color-primary, #59fcb3) 20%, transparent);
  border-color: var(--icon-color-primary, #59fcb3);
}

.code-copy-btn .icon {
  width: 16px;
  height: 16px;
  background-color: var(--icon-color-primary, #59fcb3);
}

/* Code block styling */
pre {
  background: rgba(0, 0, 0, 0.4);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 6px;
  overflow-x: auto;
  margin: 0;
}

pre code {
  background: none;
  padding: 0;
  border: none;
  font-size: 13px;
  line-height: 1.5;
}

/* Inline code — accent tint follows the active theme. */
code {
  background: color-mix(in srgb, var(--icon-color-primary, #59fcb3) 5%, transparent);
  border: 1px solid color-mix(in srgb, var(--icon-color-primary, #59fcb3) 45%, transparent);
  border-radius: 3px;
  padding: 2px 6px;
  font-size: 0.9em;
}

/* Inline code in messages is tap/click-to-copy (the mobile copy path now that
   row text selection is off). Signal it's interactive + give press feedback. */
.dmsg-text .inline-code {
  cursor: pointer;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}
.dmsg-text .inline-code:hover {
  background: color-mix(in srgb, var(--icon-color-primary, #59fcb3) 12%, transparent);
  border-color: color-mix(in srgb, var(--icon-color-primary, #59fcb3) 70%, transparent);
}
.dmsg-text .inline-code:active {
  background: color-mix(in srgb, var(--icon-color-primary, #59fcb3) 22%, transparent);
}

/* Thinner scrollbars for code blocks */
pre::-webkit-scrollbar,
details pre::-webkit-scrollbar,
pre code.hljs::-webkit-scrollbar {
  height: 10px;
  width: 10px;
}

pre::-webkit-scrollbar-track,
details pre::-webkit-scrollbar-track,
pre code.hljs::-webkit-scrollbar-track {
  background-color: transparent;
}

pre::-webkit-scrollbar-thumb,
details pre::-webkit-scrollbar-thumb,
pre code.hljs::-webkit-scrollbar-thumb {
  background-color: rgba(71, 71, 71, 0.6);
  border-radius: 4px;
}

pre::-webkit-scrollbar-thumb:hover,
details pre::-webkit-scrollbar-thumb:hover,
pre code.hljs::-webkit-scrollbar-thumb:hover {
  background-color: rgba(180, 180, 180, 0.8);
}

/* Lists */
ul, ol {
  margin: 8px 0;
  padding-left: 24px;
}

li {
  margin: 4px 0;
}

/* Blockquotes */
blockquote {
  border-left: 3px solid var(--icon-color-primary, #59fcb3);
  padding-left: 12px;
  margin: 8px 0;
  color: rgba(255, 255, 255, 0.8);
  font-style: italic;
}

/* Tables — Vector-style card: rounded outer border, no internal grid,
 * accent header, soft row separators, subtle hover. */
.table-wrapper {
  overflow-x: auto;
  margin: 10px 0;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.015);
}

table {
  border-collapse: collapse;
  width: max-content;
  min-width: 100%;
  margin: 0;
  font-size: 0.95em;
}

/* Emphasis */
.chat em {
  opacity: 0.7;
}

th, td {
  padding: 10px 14px;
  text-align: left;
  border: none;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  vertical-align: middle;
}

th {
  background: color-mix(in srgb, var(--icon-color-primary, #59fcb3) 4%, transparent);
  color: var(--icon-color-primary, #59fcb3);
  font-weight: 600;
  font-size: 0.82em;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border-bottom: 1px solid color-mix(in srgb, var(--icon-color-primary, #59fcb3) 18%, transparent);
}

/* Last body row sits flush with the wrapper's bottom edge. */
tbody tr:last-child td { border-bottom: none; }

/* Subtle row hover for scanability. */
tbody tr:hover td { background: rgba(255, 255, 255, 0.025); }

/* Honour markdown column alignment (marked emits align="right|center|left"). */
th[align="right"], td[align="right"]   { text-align: right; }
th[align="center"], td[align="center"] { text-align: center; }
th[align="left"], td[align="left"]     { text-align: left; }

/* Inline code in cells: softer chip so it doesn't fight the row. */
td code, th code { background: rgba(255, 255, 255, 0.06); padding: 1px 6px; border-radius: 4px; }

/* Horizontal rules */
hr {
  border: none;
  border-top: 1px solid rgba(255, 255, 255, 0.2);
  margin: 16px 0;
}

/* Spoilers */
.spoiler-wrapper {
  display: inline;
}

.spoiler {
  position: relative;
  display: inline;
  cursor: pointer;
  padding: 2px 4px;
  user-select: none;
  -webkit-user-select: none;
  border-radius: 3px;
  /* Ensure backgrounds clone across line breaks */
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

/* Non-interactive spoilers in previews (reply context, chat list) */
.spoiler-preview {
  cursor: default;
}

/* Prevent clicks on child elements (mentions, links) until revealed */
.spoiler:not(.revealed) > * {
  pointer-events: none;
}

/* Blurred, glassy cover effect - animated gradient shows through the blur */
.spoiler:not(.revealed) {
  background: linear-gradient(to right,
    var(--icon-color-primary, #59fcb3),
    #04110d,
    var(--icon-color-primary, #59fcb3));
  background-size: 200% 200%;
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  /* Glow follows the active theme accent. */
  text-shadow: 0 0 14px color-mix(in srgb, var(--icon-color-primary, #59fcb3) 70%, transparent);
  filter: blur(4px);
  -webkit-filter: blur(4px);
  /* Dark background for matte glass effect */
  background-color: rgba(0, 0, 0, 0.35);
  /* Fast gradient animation - constant speed */
  animation: spoilerGradientShift 4s linear infinite;
}

/* Gradient position animation - seamless loop */
@keyframes spoilerGradientShift {
  0% {
    background-position: 0% 50%;
  }
  100% {
    background-position: 200% 50%;
  }
}

/* Revealed state - normal text, selectable, stays revealed */
.spoiler.revealed {
  color: inherit;
  background: transparent;
  cursor: text;
  filter: none;
  -webkit-filter: none;
  text-shadow: none;
  user-select: text;
  -webkit-user-select: text;
  animation: spoilerReveal 0.3s ease-out;
}

/* Smooth reveal animation — accent follows the active theme. */
@keyframes spoilerReveal {
  0% {
    filter: blur(4px);
    -webkit-filter: blur(4px);
    color: color-mix(in srgb, var(--icon-color-primary, #59fcb3) 70%, transparent);
    text-shadow: 0 0 16px color-mix(in srgb, var(--icon-color-primary, #59fcb3) 60%, transparent);
  }
  100% {
    filter: blur(0px);
    -webkit-filter: blur(0px);
    color: inherit;
    text-shadow: none;
  }
}

/* File Preview Overlay */
.file-preview-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.85);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  z-index: 10000;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}

.file-preview-overlay.active {
  opacity: 1;
  pointer-events: auto;
}

.file-preview-container {
  background: rgba(20, 20, 20, 0.95);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 16px;
  padding: 24px;
  width: min(500px, calc(100vw - 40px));
  height: min(650px, calc(100vh - 40px));
  height: min(650px, calc(100dvh - 40px));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
  overflow: hidden;
  box-sizing: border-box;
}

.file-preview-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  flex: 1;
  width: 100%;
  min-height: 0;
  overflow: auto;
}

#file-preview-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  gap: 12px;
}

.file-preview-image-container {
  max-width: 100%;
  max-height: 40vh;
  min-height: 100px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: 8px;
  flex-shrink: 1;
  position: relative;
}

.file-preview-image {
  max-width: 100%;
  max-height: 40vh;
  object-fit: contain;
  border-radius: 8px;
}

/* Spoiler toggle button in file preview overlay */
.spoiler-toggle {
  position: absolute;
  bottom: 8px;
  right: 8px;
  width: 32px;
  height: 32px;
  border: none;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.55);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 3;
  transition: background 0.15s;
  padding: 0;
}
.spoiler-toggle:hover {
  background: rgba(0, 0, 0, 0.75);
}
.spoiler-toggle .icon {
  position: static;
  width: 18px;
  height: 18px;
  margin: 0;
  background-color: white;
}

/* Fallback blur if thumbhash generation fails in file preview */
.file-preview-image.spoiler-blur {
  filter: blur(20px);
  transform: scale(1.1);
}

/* Spoiler overlay on message images (click to reveal) */
.spoiler-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 2;
  gap: 4px;
  border-radius: 8px;
}
.spoiler-overlay .spoiler-label {
  color: white;
  font-size: 13px;
  font-weight: 500;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
  pointer-events: none;
}
.spoiler-overlay .icon {
  position: static;
  width: 22px;
  height: 22px;
  margin: 0;
  background-color: white;
  filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.6));
  pointer-events: none;
}

/* Spoiler image in message — blurred thumbhash (no extra blur needed, thumbhash is inherently low-res) */
.spoiler-img {
  display: block;
  transition: opacity 0.3s;
}

.file-preview-video-container {
  max-width: 100%;
  max-height: 40vh;
  min-height: 100px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: 8px;
  flex-shrink: 1;
}

.file-preview-video {
  max-width: 100%;
  max-height: 40vh;
  object-fit: contain;
  border-radius: 8px;
  background: #000;
}

.file-preview-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100px;
  color: rgba(255, 255, 255, 0.5);
  font-size: 14px;
}

.file-preview-icon-container {
  width: 120px;
  height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 16px;
  margin-bottom: 8px;
}

.file-preview-icon {
  position: relative;
  width: 64px;
  height: 64px;
}

.file-preview-icon.icon {
  position: relative;
  width: 64px;
  height: 64px;
  background-color: rgba(255, 255, 255, 0.7);
  mask-size: contain;
  mask-repeat: no-repeat;
  mask-position: center;
}

.file-preview-info {
  text-align: center;
  color: rgba(255, 255, 255, 0.9);
  flex-shrink: 0;
}

.file-preview-name-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-bottom: 8px;
  max-width: 400px;
}
.file-preview-name {
  font-size: 18px;
  font-weight: 500;
  word-break: break-word;
  cursor: pointer;
  position: relative;
  transition: color 0.15s ease;
  border-radius: 4px;
}
.file-preview-name:hover {
  color: var(--icon-color-primary) !important;
}
.file-preview-ext-badge {
  font-size: 11px;
  font-weight: 600;
  color: var(--icon-color-primary);
  background-color: color-mix(in srgb, var(--icon-color-primary) 15%, transparent);
  padding: 2px 6px;
  border-radius: 4px;
  white-space: nowrap;
  flex-shrink: 0;
  letter-spacing: 0.3px;
}
.file-preview-name-input {
  font-size: 18px;
  font-weight: 500;
  color: #f7f4f4;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 6px;
  padding: 4px 8px;
  outline: none;
  width: 100%;
  max-width: 350px;
  text-align: center;
  font-family: inherit;
  transition: border-color 0.2s ease;
}
.file-preview-name-input:focus {
  border-color: var(--icon-color-primary);
}

.file-preview-details {
  font-size: 14px;
  color: rgba(255, 255, 255, 0.6);
  display: flex;
  gap: 16px;
  justify-content: center;
  flex-wrap: wrap;
}

.file-preview-detail {
  display: flex;
  align-items: center;
  gap: 6px;
}

.file-preview-options {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
  max-width: 300px;
  flex-shrink: 0;
}

.file-preview-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 8px;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
}

.file-preview-option-label {
  font-size: 14px;
  color: rgba(255, 255, 255, 0.9);
  line-height: 16px;
}

.file-preview-option-sublabel {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.5);
  margin-top: 2px;
  line-height: 16px;
}

.file-preview-buttons {
  display: flex;
  gap: 12px;
  margin-top: auto;
  flex-shrink: 0;
}

.file-preview-btn {
  padding: 12px 32px;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
  border: none;
}

.file-preview-btn-cancel {
  background: rgba(255, 255, 255, 0.1);
  color: rgba(255, 255, 255, 0.9);
}

.file-preview-btn-cancel:hover {
  background: rgba(255, 255, 255, 0.15);
}

.file-preview-btn-send {
  background: linear-gradient(90deg, #33db98, #00ff9d);
  color: #000;
}

.file-preview-btn-send:hover {
  opacity: 0.9;
}

.file-preview-btn-send:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.file-preview-btn-publish {
  background-color: rgba(255, 255, 255, 0.15);
  color: #fff;
  display: none;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.file-preview-btn-publish .icon {
  position: relative;
  width: 16px;
  height: 16px;
  background-color: #fff;
}

.file-preview-btn-publish:hover {
  background-color: rgba(255, 255, 255, 0.25);
}

/* Mini App Preview Styles */
.file-preview-miniapp {
  position: relative;
}

.file-preview-miniapp-icon {
  border-radius: 16px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

.file-preview-miniapp-badge {
  display: block;
  margin-top: 8px;
  font-size: 12px;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.file-preview-icon-container.file-preview-miniapp {
  flex-direction: column;
}

/* Zip compression progress spinner (conic-gradient, same as miniapp download) */
.zip-progress-spinner {
  --progress: 5%;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: conic-gradient(
    var(--icon-color-primary) 0% var(--progress),
    rgba(255, 255, 255, 0.15) var(--progress) 100%
  );
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 5px), #fff calc(100% - 4px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 5px), #fff calc(100% - 4px));
  animation: spin 2s linear infinite;

}

.file-preview-zip-label {
  color: rgba(255, 255, 255, 0.6);
  font-size: 13px;
  text-align: center;
}

/* File list in zip preview */
.file-preview-file-list {
  width: 100%;
  max-height: 30vh;
  overflow-y: auto;
  overflow-x: hidden;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.03);
  padding: 8px 8px 8px 4px;
  box-sizing: border-box;
}

/* Directory tree */
.zip-tree-dir {
  width: 100%;
}

.zip-tree-dir-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 3px 4px;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.8);
  border-radius: 4px;
}

.zip-tree-dir-header.zip-tree-toggle {
  cursor: pointer;
}

.zip-tree-dir-header.zip-tree-toggle:hover {
  background: rgba(255, 255, 255, 0.05);
}

.zip-tree-chevron {
  position: relative;
  width: 12px;
  height: 12px;
  flex-shrink: 0;
  background-color: rgba(255, 255, 255, 0.35);
  transform: rotate(-90deg);
  transition: transform 0.15s ease;
}

.zip-tree-chevron.zip-tree-chevron-open {
  transform: rotate(0deg);
}

.zip-tree-children {
  width: 100%;
  overflow: hidden;
  max-height: 0;
  transition: max-height 0.2s ease;
}

/* File entries */
.zip-file-entry {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 3px 4px;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.7);
}

.zip-file-icon {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  background-color: rgba(255, 255, 255, 0.5);
  mask-size: contain;
  mask-repeat: no-repeat;
  mask-position: center;
}

.zip-file-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.zip-file-size {
  flex-shrink: 0;
  color: rgba(255, 255, 255, 0.4);
  font-size: 11px;
}

.zip-file-more {
  text-align: center;
  color: rgba(255, 255, 255, 0.4);
  font-size: 12px;
  padding: 8px 0 4px;
}

/* Mini App Launch Dialog */
.miniapp-launch-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.85);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}

.miniapp-launch-overlay.active {
  opacity: 1;
  pointer-events: auto;
}

.miniapp-launch-container {
  background: rgba(20, 20, 20, 0.95);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 16px;
  padding: 24px;
  width: min(360px, calc(100vw - 40px));
  max-width: calc(100vw - 40px);
  max-height: calc(100vh - 40px);
  max-height: calc(100dvh - 40px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
  box-sizing: border-box;
  overflow: hidden;
}

.miniapp-launch-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  width: 100%;
}

.miniapp-launch-icon-container {
  width: 100px;
  height: 100px;
  border-radius: 20px;
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.miniapp-launch-icon-container img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 20px;
}

.miniapp-launch-icon-container .icon {
  position: relative;
  top: auto;
  left: auto;
  right: auto;
  bottom: auto;
  margin: 0;
  width: 50px;
  height: 50px;
  background-color: rgba(255, 255, 255, 0.7);
}

.miniapp-launch-info {
  text-align: center;
}

.miniapp-launch-name {
  font-size: 18px;
  font-weight: 600;
  color: #fff;
  word-break: break-word;
}

/* Scoped button styles for Mini App launch dialog */
.miniapp-launch-container .file-preview-btn {
  padding: 10px 16px;
}

#miniapp-launch-cancel {
  opacity: 0.6;
}

#miniapp-launch-cancel:hover {
  opacity: 0.8;
}

/* Loading spinner inside Mini App launch buttons */
.miniapp-launch-container .file-preview-btn .icon {
  display: inline-block;
  position: relative;
  top: auto;
  left: auto;
  right: auto;
  bottom: auto;
  margin: 0;
  width: 18px;
  height: 18px;
  background-color: #000;
  animation: spin 1s linear infinite;
}

/* ============================================================================
   Mini Apps Marketplace Styles
   ============================================================================ */

/* Marketplace button in attachment panel */
.attachment-panel-marketplace-btn {
  background-color: #131313;
  border-color: #59fcb3 !important;
}

.attachment-panel-marketplace-btn .icon-nexus {
  width: 70% !important;
  height: 70% !important;
}

#attachment-panel-marketplace .attachment-panel-label {
  color: #59fcb3 !important;
}

/* PIVX button in attachment panel */
.attachment-panel-pivx-btn {
  background: linear-gradient(to bottom, #452568, #110B1B) !important;
  border: 1px solid #110B1B;
}

/* ============================================================================
   PIVX WALLET STYLES
   ============================================================================ */

/* Attachment panel border when PIVX is active */
.attachment-panel.pivx-active {
  border: 1px solid #262626;
}

/* PIVX Wallet Panel Content */
.pivx-wallet-panel {
  flex-direction: column !important;
  align-items: stretch !important;
  gap: 0 !important;
  position: relative;
}

.pivx-close-btn {
  position: absolute;
  top: -5px;
  right: 0;
  width: 22px;
  height: 22px;
  border: 2px solid #323232;
  background: #000;
  border-radius: 4px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  z-index: 10;
  transition: background 0.2s ease;
}

.pivx-close-btn:hover {
  background: #1a1a1a;
}

.pivx-close-btn .icon {
  width: 14px;
  height: 14px;
  background-color: #642D8F;
  position: relative;
  margin: 0;
}

.pivx-wallet-content {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  gap: 15px;
  padding-top: 28px;
}

.pivx-balance-section {
  position: relative;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px 20px;
  background: linear-gradient(to right, #452568 0%, #110B1B 100%);
  border-radius: 12px;
  border: 2px solid #642D8F;
  opacity: 0; /* Hidden by default, animated in when panel opens */
}

.pivx-balance-logo {
  width: 80px;
  height: 80px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.pivx-balance-logo img {
  width: 80px;
  height: 80px;
}

.pivx-balance-info {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  padding-right: 12px;
}

.pivx-balance-amount {
  font-size: 30px;
  font-weight: 600;
  color: #fff;
  font-family: var(--font-mono);
  line-height: 1;
}

.pivx-balance-fiat {
  font-size: 18px;
  color: rgba(255, 255, 255, 0.4);
  font-family: var(--font-mono);
  margin-top: 4px;
  line-height: 1;
}

/* Balance fade-in animation */
@keyframes pivx-fade-in-left {
  from {
    opacity: 0;
    transform: translateX(-8px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

.pivx-balance-amount.pivx-fade-in,
.pivx-balance-fiat.pivx-fade-in {
  animation: pivx-fade-in-left 0.3s ease-out forwards;
}

/* PIVX Panel open animations */
@keyframes pivx-panel-fade-in {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes pivx-panel-slide-in {
  from {
    opacity: 0;
    transform: translateX(-12px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

.pivx-balance-section.pivx-panel-animate {
  animation: pivx-panel-fade-in 0.25s ease-out forwards;
}

.pivx-dock-btn.pivx-panel-animate {
  animation: pivx-panel-slide-in 0.25s ease-out forwards;
}

.pivx-balance-loading {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 34px;
}

.pivx-balance-label {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.6);
  margin-top: 5px;
}

/* PIVX Action Dock (compact icon + label style) */
.pivx-actions-dock {
  display: flex;
  gap: 20px;
  justify-content: center;
  padding: 8px 0;
}

.pivx-dock-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 0;
  background: transparent;
  border: none;
  color: white;
  cursor: pointer;
  transition: transform 0.15s ease, opacity 0.15s ease;
  opacity: 0; /* Hidden by default, animated in when panel opens */
}

.pivx-dock-btn:hover {
  transform: scale(1.08);
}

.pivx-dock-btn:active {
  transform: scale(0.95);
}

.pivx-dock-btn.disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.pivx-dock-btn.disabled:hover {
  transform: none;
}

.pivx-dock-btn.disabled:hover .pivx-dock-icon {
  background: rgba(255, 255, 255, 0.12);
  box-shadow: none;
}

.pivx-dock-btn.disabled:hover .pivx-dock-icon .icon {
  background-color: rgba(255, 255, 255, 0.7);
}

.pivx-dock-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  background: rgba(255, 255, 255, 0.12);
  border-radius: 12px;
}

.pivx-dock-btn:hover .pivx-dock-icon {
  background: rgba(255, 255, 255, 0.18);
}

.pivx-dock-icon .icon {
  position: relative;
  width: 20px;
  height: 20px;
  margin: 0;
  background-color: rgba(255, 255, 255, 0.7);
}

.pivx-dock-btn:hover .pivx-dock-icon .icon {
  background-color: white;
}

/* Primary button (Send) - purple highlight */
.pivx-dock-btn-primary .pivx-dock-icon {
  background: #642D8F;
}

.pivx-dock-btn-primary .pivx-dock-icon .icon {
  background-color: white;
  transition: transform 0.15s ease;
}

.pivx-dock-btn-primary:hover .pivx-dock-icon {
  background: #642D8F;
  transform: scale(1.1);
}

.pivx-dock-label {
  font-size: 11px;
  color: rgba(255, 255, 255, 0.7);
  transition: color 0.15s ease;
}

.pivx-dock-btn:hover .pivx-dock-label {
  color: white;
}

/* PIVX Dialog Overlay */
.pivx-dialog-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.90);
  backdrop-filter: blur(4px);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 10000;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.pivx-dialog-overlay.active {
  display: flex;
  opacity: 1;
}

.pivx-dialog {
  background: rgba(20, 20, 20, 0.95);
  border: 2px solid #262626;
  border-radius: 16px;
  width: min(400px, calc(100vw - 40px));
  overflow: hidden;
  box-shadow: 0 0 40px rgba(0, 0, 0, 0.9);
}

.pivx-dialog-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15px 20px;
  border-bottom: 2px solid #262626;
  background: linear-gradient(to right, #110B1B 0%, #452568 100%);
}

.pivx-dialog-header h3 {
  margin: 0;
  font-size: 18px;
  color: #fff;
}

.pivx-dialog-close {
  background: none;
  border: none;
  font-size: 24px;
  color: rgba(255, 255, 255, 0.6);
  cursor: pointer;
  padding: 0;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  transition: all 0.2s ease;
}

.pivx-dialog-close:hover {
  background: rgba(255, 255, 255, 0.1);
  color: #fff;
}

.pivx-dialog-content {
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 15px;
}

.pivx-deposit-instructions {
  margin: 0;
  color: rgba(255, 255, 255, 0.7);
  font-size: 14px;
}

.pivx-address-display,
.pivx-promo-code-display {
  font-family: var(--font-mono);
  font-size: 14px;
  padding: 12px;
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  word-break: break-all;
  text-align: center;
  color: #fff;
}

.pivx-promo-code-display {
  font-size: 18px;
  font-weight: 600;
  letter-spacing: 2px;
  color: #8b5cf6;
}

.pivx-copy-btn {
  width: 100%;
  padding: 14px;
  background: #642D8F;
  border: none;
  border-radius: 8px;
  color: white;
  font-size: 16px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
}

.pivx-copy-btn:hover {
  opacity: 0.9;
  transform: scale(1.02);
}

.pivx-copy-btn .icon {
  width: 16px;
  height: 16px;
  position: relative;
  margin: 0;
}

/* PIVX Deposit Status */
.pivx-deposit-status {
  margin-top: 10px;
  padding-top: 15px;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  text-align: center;
  position: relative;
}

.pivx-awaiting-deposit {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 15px;
  color: rgba(255, 255, 255, 0.7);
  font-size: 14px;
}

.pivx-deposit-received {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 15px;
  color: #59fcb3;
  font-size: 14px;
  font-weight: 500;
}

.pivx-deposit-received .icon {
  position: relative;
  width: 20px;
  height: 20px;
  min-width: 20px;
  flex-shrink: 0;
  background-color: #59fcb3;
}

.pivx-spinner {
  width: 28px;
  height: 28px;
  border: 3px solid rgba(255, 255, 255, 0.2);
  border-top-color: #B359FC;
  border-radius: 50%;
  animation: pivx-spin 1s linear infinite;
}

@keyframes pivx-spin {
  to {
    transform: rotate(360deg);
  }
}

/* PIVX Send Dialog */
.pivx-send-promo-section {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.pivx-send-promo-label {
  font-size: 13px;
  color: rgba(255, 255, 255, 0.6);
}

.pivx-send-promo-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 180px;
  overflow-y: auto;
  padding-right: 8px;
}

.pivx-send-promo-list::-webkit-scrollbar {
  width: 6px;
}

.pivx-send-promo-list::-webkit-scrollbar-track {
  background: rgba(255, 255, 255, 0.05);
  border-radius: 3px;
}

.pivx-send-promo-list::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.2);
  border-radius: 3px;
}

.pivx-send-promo-list::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, 0.3);
}

.pivx-send-promo-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 20px;
  color: rgba(255, 255, 255, 0.5);
  font-size: 13px;
}

.pivx-send-promo-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 15px;
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
  opacity: 0;
}

.pivx-send-promo-item.animate-in {
  animation: pivx-promo-fade-in 0.2s ease-out forwards;
}

@keyframes pivx-promo-fade-in {
  from {
    opacity: 0;
    transform: translateY(-8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.pivx-send-promo-item:hover {
  background: rgba(92, 77, 154, 0.3);
  border-color: rgba(92, 77, 154, 0.5);
}

.pivx-send-promo-item.selected {
  background: rgba(92, 77, 154, 0.4);
  border-color: #B359FC;
}

.pivx-send-promo-item-amount {
  font-size: 16px;
  font-weight: 600;
  font-family: var(--font-mono);
  color: #fff;
}

.pivx-send-promo-item-code {
  font-size: 11px;
  color: rgba(255, 255, 255, 0.4);
  font-family: var(--font-mono);
}

.pivx-send-promo-empty {
  padding: 20px;
  text-align: center;
  color: rgba(255, 255, 255, 0.4);
  font-size: 13px;
}

.pivx-send-custom-toggle,
.pivx-send-back-toggle {
  font-size: 16px;
  color: #B359FC;
  cursor: pointer;
  text-align: center;
  padding: 8px;
  padding-top: 0;
  transition: color 0.2s ease;
}

.pivx-send-custom-toggle:hover,
.pivx-send-back-toggle:hover {
  color: #B359FC80;
}

.pivx-send-custom-warning {
  font-size: 12px;
  color: #B2B2B2;
  border-radius: 6px;
  padding: 10px 12px;
  margin-bottom: 20px;
  background: #56565620;
  display: flex;
  align-items: flex-start;
  gap: 8px;
  text-align: left;
}

.pivx-send-custom-warning .icon {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  position: relative;
  padding: 0 4px 0 0;
}

.pivx-amount-input-container {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  padding: 15px;
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 8px;
}

#pivx-send-amount,
#pivx-withdraw-amount {
  background: none;
  border: none;
  font-size: 24px;
  font-family: var(--font-mono);
  color: #fff;
  outline: none;
  text-align: right;
  width: 150px;
  padding-right: 0;
}

#pivx-send-amount::placeholder,
#pivx-withdraw-amount::placeholder {
  color: rgba(255, 255, 255, 0.3);
}

.pivx-amount-label {
  font-size: 18px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.6);
}

.pivx-send-available {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.5);
  padding: 6px 0 20px 0;
}

.pivx-send-available #pivx-send-available-amount {
  font-family: var(--font-mono);
  font-weight: 600;
}



.pivx-send-info {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.6);
  text-align: center;
  margin: -6px;
}

#pivx-send-recipient {
  color: #B359FC;
  font-weight: 500;
}

.pivx-send-confirm-btn {
  width: 100%;
  padding: 14px;
  background: #642D8F;
  border: none;
  border-radius: 8px;
  color: white;
  font-size: 16px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
}

.pivx-send-confirm-btn:hover {
  opacity: 0.9;
  transform: scale(1.02);
}

.pivx-send-confirm-btn:active {
  transform: scale(0.98);
}

.pivx-send-confirm-btn:disabled {
  background: rgba(255, 255, 255, 0.2);
  cursor: not-allowed;
  transform: none;
}

.pivx-send-confirm-btn.loading {
  opacity: 0.5;
  cursor: default;
  pointer-events: none;
}

/* PIVX Dock Button Loading State */
.pivx-dock-btn.loading {
  pointer-events: none;
  opacity: 0.6;
}

.pivx-dock-btn.loading .pivx-dock-icon .icon {
  visibility: hidden;
}

.pivx-dock-btn.loading .pivx-dock-icon::after {
  content: '';
  position: absolute;
  width: 18px;
  height: 18px;
  border: 2px solid rgba(255, 255, 255, 0.3);
  border-top-color: white;
  border-radius: 50%;
  animation: pivx-spin 1s linear infinite;
}

.pivx-dock-btn.loading .pivx-dock-icon {
  position: relative;
}

/* PIVX Withdraw Dialog */
.pivx-withdraw-section {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 16px;
}

.pivx-withdraw-available {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.5);
  padding: 4px 0;
}

.pivx-withdraw-available #pivx-withdraw-available-amount {
  font-family: var(--font-mono);
  font-weight: 600;
}

.pivx-withdraw-max-btn {
  background: #B359FC30;
  color: #B359FC;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s ease;
}

.pivx-withdraw-max-btn:hover {
  background: #B359FC50;
}

.pivx-withdraw-fee-info {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.4);
  text-align: center;
  margin-bottom: -6px;
}

/* PIVX Settings Dialog */
.pivx-settings-section {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.pivx-settings-label {
  font-size: 14px;
  color: #fff;
  font-weight: 500;
}

.pivx-settings-input {
  padding: 12px;
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  color: #fff;
  font-size: 14px;
  font-family: var(--font-mono);
  outline: none;
}

.pivx-settings-input:focus {
  border-color: #B359FC;
}

.pivx-settings-input::placeholder {
  color: rgba(255, 255, 255, 0.3);
}

.pivx-settings-select {
  padding: 12px;
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid #383838 !important;
  border-radius: 8px;
  color: #fff;
  font-size: 14px;
  outline: none;
  cursor: pointer;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.5)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 36px;
  margin-bottom: 0;
}

.pivx-settings-select:focus {
  border-color: #B359FC !important;
}

.pivx-settings-select option {
  background: #1a1a2e;
  color: #fff;
}

.pivx-settings-hint {
  margin: 0;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.5);
  line-height: 1.3;
}

.pivx-settings-save-btn {
  width: 100%;
  padding: 14px;
  background: #642D8F;
  border: none;
  border-radius: 8px;
  color: white;
  font-size: 16px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
}

.pivx-settings-save-btn:hover {
  opacity: 0.9;
  transform: scale(1.02);
}

/* PIVX Payment Message Card - matches .msg-payment style */
.msg-pivx-payment {
  display: flex;
  align-items: center;
  padding: 10px;
  border-radius: 16px;
  margin-top: 10px;
  border: 1px solid #9530f4;
  background-color: #9530f440;
  cursor: pointer;
  transition: background-color 0.2s ease;
}

.msg-pivx-payment:hover {
  background-color: #9530f460;
}

.msg-pivx-payment.syncing {
  opacity: 0.6;
  cursor: wait;
  pointer-events: none;
}

.msg-pivx-payment.claiming {
  opacity: 0.7;
  cursor: wait;
  pointer-events: none;
}

.msg-pivx-payment.claimed {
  opacity: 0.5;
  cursor: default;
}

.msg-pivx-payment.claimed:hover {
  background-color: #9530f440;
}

.msg-pivx-payment img {
  height: 50px;
  margin-right: 10px;
  flex-shrink: 0;
}

.msg-pivx-payment-info {
  margin-left: auto;
  text-align: right;
}

.msg-pivx-payment-amount {
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
  line-height: 1;
}

.msg-pivx-payment-fiat {
  font-size: 11px;
  color: var(--text-secondary);
  opacity: 0.7;
}

.msg-pivx-payment-hint {
  font-size: 11px;
  color: var(--text-secondary);
  margin-top: 2px;
  line-height: 1.4;
}

/* Marketplace Panel */
.marketplace-panel {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: #0a0a0a;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  animation: fadeInWithoutTransform 0.5s cubic-bezier(.46,.03,.52,.96) forwards;
}

.marketplace-panel.closing {
  animation: fadeOutWithoutTransform 0.3s cubic-bezier(.46,.03,.52,.96) forwards;
}

.marketplace-header {
  position: relative;
  height: 60px;
  min-height: 60px;
  background-color: rgba(22, 22, 22, 0.85);
  backdrop-filter: blur(10px);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  z-index: 100;
}

.marketplace-header .marketplace-back-btn {
  /* Uses nav-back-btn styles */
}

.marketplace-title {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
  color: #fff;
  text-align: center;
}

/* Marketplace Search */
.marketplace-search-container {
  margin: 12px 16px 16px;
  position: relative;
  display: flex;
  align-items: center;
}

.marketplace-search-icon {
  position: relative;
  margin-left: 12px;
  width: 20px;
  height: 20px;
  background-color: rgba(255, 255, 255, 0.4);
  flex-shrink: 0;
  z-index: 1;
}

.marketplace-search-container input {
  flex: 1;
  background-color: transparent;
  border: 1px solid rgba(57, 57, 57, 0.5);
  border-radius: 8px;
  padding: 10px 14px 10px 40px;
  font-size: 14px;
  color: #fff;
  font-family: inherit;
  transition: border-color 0.2s ease, background-color 0.2s ease;
  margin-left: -32px;
  width: 100%;
}

.marketplace-search-container input:focus {
  outline: none;
  border-color: rgba(255, 255, 255, 0.4);
  background-color: rgba(255, 255, 255, 0.05);
}

.marketplace-search-container input::placeholder {
  color: rgba(255, 255, 255, 0.4);
}

/* Marketplace Filters */
.marketplace-filters {
  padding: 8px 16px 12px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.marketplace-active-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.marketplace-filter-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: 16px;
  font-size: 12px;
  font-weight: 500;
  background-color: rgba(255, 255, 255, 0.1);
  color: rgba(255, 255, 255, 0.9);
  cursor: pointer;
  transition: background-color 0.2s ease;
}

.marketplace-filter-tag:hover {
  background-color: rgba(255, 255, 255, 0.15);
}

.marketplace-filter-tag .icon {
  position: relative;
  width: 12px;
  height: 12px;
  background-color: rgba(255, 255, 255, 0.6);
}

.marketplace-filter-tag .icon:hover {
  background-color: rgba(255, 255, 255, 0.9);
}

/* Featured Categories Section */
.marketplace-featured {
  padding: 16px;
  padding-bottom: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.marketplace-featured:empty {
  display: none;
}

.marketplace-featured-category {
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0.03) 100%);
  border-radius: 16px;
  padding: 16px;
  cursor: pointer;
  transition: transform 0.2s, box-shadow 0.2s;
  border: 1px solid rgba(255, 255, 255, 0.1);
}

.marketplace-featured-category:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
}

.marketplace-featured-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}

.marketplace-featured-title {
  font-size: 18px;
  font-weight: 600;
  color: #fff;
  display: flex;
  align-items: center;
  gap: 8px;
}

.marketplace-featured-count {
  font-size: 13px;
  color: rgba(255, 255, 255, 0.5);
  font-weight: 400;
}

.marketplace-featured-description {
  font-size: 13px;
  color: rgba(255, 255, 255, 0.6);
  margin-bottom: 8px;
  line-height: 1.4;
}

/* Card Spread Preview */
.marketplace-card-spread {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px 0;
  position: relative;
  height: 72px;
}

.marketplace-card-spread-item {
  width: 56px;
  height: 56px;
  border-radius: 12px;
  background-color: rgba(255, 255, 255, 0.1);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  position: absolute;
  transition: transform 0.3s ease, box-shadow 0.3s ease;
  border: 2px solid rgba(255, 255, 255, 0.15);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

.marketplace-card-spread-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.marketplace-card-spread-item .icon {
  width: 28px;
  height: 28px;
  background-color: rgba(255, 255, 255, 0.5);
}

/* Card spread positions - stacked with subtle rainbow arc curve */
.marketplace-card-spread-item:nth-child(1) {
  transform: translateX(-24px) translateY(6px) rotate(-6deg);
  z-index: 1;
}

.marketplace-card-spread-item:nth-child(2) {
  transform: translateX(-8px) translateY(-2px) rotate(-2deg);
  z-index: 2;
}

.marketplace-card-spread-item:nth-child(3) {
  transform: translateX(8px) translateY(-2px) rotate(2deg);
  z-index: 3;
}

.marketplace-card-spread-item:nth-child(4) {
  transform: translateX(24px) translateY(6px) rotate(6deg);
  z-index: 4;
}

/* Hover effect - cards fan out while maintaining rainbow curve */
.marketplace-featured-category:hover .marketplace-card-spread-item:nth-child(1) {
  transform: translateX(-48px) translateY(8px) rotate(-10deg);
}

.marketplace-featured-category:hover .marketplace-card-spread-item:nth-child(2) {
  transform: translateX(-16px) translateY(-4px) rotate(-3deg);
}

.marketplace-featured-category:hover .marketplace-card-spread-item:nth-child(3) {
  transform: translateX(16px) translateY(-4px) rotate(3deg);
}

.marketplace-featured-category:hover .marketplace-card-spread-item:nth-child(4) {
  transform: translateX(48px) translateY(8px) rotate(10deg);
}

/* Popular Categories Pills */
.marketplace-popular-categories {
  display: flex;
  flex-wrap: nowrap;
  gap: 8px;
  padding: 0 16px 12px 16px;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.2) transparent;
}

.marketplace-popular-categories::-webkit-scrollbar {
  height: 4px;
}

.marketplace-popular-categories::-webkit-scrollbar-track {
  background: transparent;
}

.marketplace-popular-categories::-webkit-scrollbar-thumb {
  background-color: rgba(255, 255, 255, 0.446);
  border-radius: 2px;
}

.marketplace-popular-categories::-webkit-scrollbar-thumb:hover {
  background-color: rgba(255, 255, 255, 0.689);
}

.marketplace-popular-category {
  padding: 6px 14px;
  background-color: rgba(255, 255, 255, 0.08);
  border-radius: 20px;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.8);
  cursor: pointer;
  transition: background-color 0.15s, transform 0.15s;
  border: 1px solid rgba(255, 255, 255, 0.1);
  flex-shrink: 0;
  white-space: nowrap;
}

.marketplace-popular-category:hover {
  background-color: rgba(255, 255, 255, 0.15);
}

/* Scrollable container for featured + content */
.marketplace-scroll-container {
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}

.marketplace-content {
  flex: 1;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Staggered fade-in animation for marketplace items */
@keyframes marketplaceFadeInDown {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.marketplace-animate-in {
  opacity: 0;
  animation: marketplaceFadeInDown 0.3s ease forwards;
}

/* Section Title */
.marketplace-section-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 15px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.7);
  padding-bottom: 4px;
}

.marketplace-section-title .icon {
  width: 16px;
  height: 16px;
  background-color: rgba(255, 255, 255, 0.5);
  position: relative;
  margin: 0;
}

/* Switch to grid layout on larger screens */
@media (min-width: 800px) {
  .marketplace-content {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
    align-content: start;
  }
  
  .marketplace-section-title {
    grid-column: 1 / -1;
  }
}

/* Marketplace App Card */
.marketplace-app-card {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px;
  background-color: #171717;
  border-radius: 12px;
  cursor: pointer;
  transition: transform 0.2s, box-shadow 0.2s;
  border: 1px solid rgba(255, 255, 255, 0.08);
}

.marketplace-app-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

.marketplace-app-card:active {
  transform: translateY(0);
}

.marketplace-app-icon-container {
  width: 56px;
  height: 56px;
  border-radius: 12px;
  background-color: rgba(255, 255, 255, 0.1);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  overflow: hidden;
  /* Contain the absolutely-positioned .icon-play placeholder when an icon is missing/fails. */
  position: relative;
}

.marketplace-app-icon {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 12px;
}

.marketplace-app-icon-placeholder {
  width: 28px;
  height: 28px;
  background-color: rgba(255, 255, 255, 0.5);
}

.marketplace-app-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.marketplace-app-header {
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.marketplace-app-name {
  font-size: 16px;
  font-weight: 600;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.marketplace-app-version {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.5);
  flex-shrink: 0;
}

.marketplace-app-description {
  font-size: 13px;
  color: rgba(255, 255, 255, 0.7);
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-align: left;
}

.marketplace-app-categories {
  display: flex;
  flex-wrap: nowrap;
  gap: 6px;
  margin-top: 4px;
}

.marketplace-app-category {
  font-size: 11px;
  padding: 2px 8px;
  background-color: rgba(255, 255, 255, 0.1);
  color: rgba(255, 255, 255, 0.7);
  border-radius: 10px;
  text-transform: capitalize;
  cursor: pointer;
  transition: background-color 0.15s ease, transform 0.15s ease;
  white-space: nowrap;
  flex-shrink: 0;
}

.marketplace-app-category:hover {
  background-color: rgba(255, 255, 255, 0.2);
  transform: scale(1.05);
}

.marketplace-app-category-overflow {
  font-size: 11px;
  padding: 2px 8px;
  background-color: rgba(255, 255, 255, 0.15);
  color: rgba(255, 255, 255, 0.8);
  border-radius: 10px;
  cursor: default;
  flex-shrink: 0;
  white-space: nowrap;
}

.marketplace-app-meta {
  display: flex;
  gap: 12px;
  font-size: 11px;
  color: rgba(255, 255, 255, 0.4);
  margin-top: 4px;
}

/* Install/Play Button */
.marketplace-install-btn {
  padding: 10px 20px;
  border: none;
  border-radius: 20px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s;
  flex-shrink: 0;
}

.marketplace-install-btn:hover {
  transform: scale(1.05);
}

.marketplace-install-btn:active {
  transform: scale(0.98);
}

.marketplace-install-btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
  transform: none;
}

.marketplace-install-btn.installed {
  background-color: rgba(255, 255, 255, 0.2);
}

.marketplace-install-btn.installing {
  background-color: rgba(255, 200, 100, 0.3);
}

.marketplace-install-btn.failed {
  background-color: var(--danger-color-hover);
}

/* Loading spinner in install/update buttons */
.marketplace-install-btn .icon-loading,
.app-details-action-btn .icon-loading {
  width: 18px;
  height: 18px;
  background-color: rgba(255, 255, 255, 0.8);
  animation: spin 1s linear infinite;
}

/* Hide button styling when showing spinner */
.marketplace-install-btn.installing,
.marketplace-install-btn.updating {
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
  padding: 10px;
}

.app-details-action-btn.installing,
.app-details-update-btn.updating {
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
}

.marketplace-install-btn.update-available {
  background: linear-gradient(135deg, rgba(89, 252, 179, 0.3), rgba(43, 151, 108, 0.3));
  border: 1px solid rgba(89, 252, 179, 0.5);
  animation: update-glow 2s ease-in-out infinite;
}

.marketplace-install-btn.updating {
  background: linear-gradient(135deg, rgba(89, 252, 179, 0.2), rgba(43, 151, 108, 0.2));
}

@keyframes update-glow {
  0%, 100% {
    box-shadow: 0 0 8px rgba(89, 252, 179, 0.3);
  }
  50% {
    box-shadow: 0 0 16px rgba(89, 252, 179, 0.5);
  }
}

/* Marketplace download progress spinner (conic gradient style) */
.marketplace-progress-spinner {
  --progress: 5%;
  display: inline-block;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  /* Progress ring using conic-gradient */
  background: conic-gradient(
    rgba(255, 255, 255, 0.9) 0% var(--progress),
    rgba(255, 255, 255, 0.2) var(--progress) 100%
  );
  /* Create ring effect with inner circle mask */
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #fff calc(100% - 2.5px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #fff calc(100% - 2.5px));
  animation: inline-image-spin 2s linear infinite;

}

/* Mini Apps panel - downloading app overlay */
.miniapp-downloading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.6);
  border-radius: 12px;
  z-index: 5;
}

.attachment-progress-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 2;
  container-type: size;
}

/* The ring stays round on any media: it shrinks on both axes to fit the
   shorter side rather than letting flex squash its width. */
.attachment-progress-overlay > .miniapp-downloading-spinner,
.attachment-progress-overlay > .upload-cancel-btn {
  flex: none;
  max-width: 70cqmin;
  max-height: 70cqmin;
}

/* Cancel button for image/video uploads — sized to fill the spinner's donut
 * hole exactly. Spinner is 48x48 with a ~4px ring (mask leaves an inner 40px
 * hole), so a 40x40 inner button kisses the ring without covering it. */
.attachment-progress-overlay > .upload-cancel-btn {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.2s ease;
  cursor: pointer;
  background: rgba(0, 0, 0, 0.6);
  border-radius: 50%;
  pointer-events: auto;
  z-index: 3;
}
/* Cancel button for audio uploads — positioned over spinner in the play button wrapper */
.audio-upload-cancel {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.2s ease;
  cursor: pointer;
  background: rgba(0, 0, 0, 0.6);
  border-radius: 50%;
  z-index: 3;
}
.upload-cancel-btn.audio-upload-cancel::after {
  content: '\2715';
  color: white;
  font-size: 14px;
}
.custom-audio-player:hover .audio-upload-cancel {
  opacity: 1;
}
.upload-cancel-btn::after {
  content: '\2715';
  color: white;
  font-size: 16px;
}
.attachment-progress-overlay:hover > .upload-cancel-btn {
  opacity: 1;
}
/* Re-enable pointer events on overlay when it has a cancel button */
.attachment-progress-overlay:has(.upload-cancel-btn) {
  pointer-events: auto;
}

.miniapp-downloading-spinner {
  --progress: 5%;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: conic-gradient(
    var(--icon-color-primary) 0% var(--progress),
    rgba(255, 255, 255, 0.2) var(--progress) 100%
  );
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 4px), #fff calc(100% - 3px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 4px), #fff calc(100% - 3px));
  animation: inline-image-spin 2s linear infinite;
  transition: --progress 0.3s ease;
}

/* Version update indicator */
.marketplace-app-version.update-available {
  color: #59fcb3;
  font-size: 11px;
}

/* Loading State */
.marketplace-loading {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  height: 200px;
  gap: 8px;
  color: rgba(255, 255, 255, 0.6);
}

.marketplace-loading-icon {
  position: relative;
  top: auto;
  left: auto;
  right: auto;
  bottom: auto;
  margin: 0;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  background-color: rgba(255, 255, 255, 0.6);
  animation: spin 1s linear infinite;
}

/* Empty State */
.marketplace-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 200px;
  gap: 12px;
  text-align: center;
  color: rgba(255, 255, 255, 0.6);
}

.marketplace-empty-icon {
  position: relative;
  width: 48px;
  height: 48px;
  background-color: rgba(255, 255, 255, 0.3);
}

.marketplace-empty-hint {
  font-size: 13px;
  color: rgba(255, 255, 255, 0.4);
}

/* Error State */
.marketplace-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 200px;
  gap: 12px;
  text-align: center;
  color: rgba(255, 255, 255, 0.6);
}

.marketplace-error-icon {
  position: relative;
  width: 48px;
  height: 48px;
  background-color: #f45c43;
}

.marketplace-error-hint {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.4);
  max-width: 300px;
  word-break: break-word;
}

.marketplace-retry-btn {
  margin-top: 8px;
  padding: 10px 24px;
  border: none;
  border-radius: 20px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  background-color: rgba(255, 255, 255, 0.15);
  color: #fff;
  transition: transform 0.2s, background-color 0.2s;
}

.marketplace-retry-btn:hover {
  transform: scale(1.05);
  background-color: rgba(255, 255, 255, 0.25);
}

/* Mobile Responsive */
@media (max-width: 480px) {
  .marketplace-app-card {
    padding: 12px;
  }

  .marketplace-app-icon-container {
    width: 48px;
    height: 48px;
  }

  .marketplace-app-name {
    font-size: 15px;
  }

  .marketplace-app-description {
    font-size: 12px;
    -webkit-line-clamp: 1;
  }

  .marketplace-install-btn {
    padding: 8px 16px;
    font-size: 13px;
  }
}

/* Publish App Dialog */
.publish-app-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.7);
  display: none;
  justify-content: center;
  align-items: center;
  z-index: 10000;
  opacity: 0;
  transition: opacity 0.3s ease;
}

.publish-app-overlay.active {
  opacity: 1;
}

.publish-app-container {
  background-color: #171717;
  border-radius: 16px;
  width: 90%;
  max-width: 500px;
  max-height: 85vh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.1);
  transform: scale(0.9);
  transition: transform 0.3s ease;
}

.publish-app-overlay.active .publish-app-container {
  transform: scale(1);
}

.publish-app-header {
  padding: 20px 24px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.publish-app-header h2 {
  margin: 0;
  font-size: 20px;
  font-weight: 600;
  color: #fff;
}

.publish-app-content {
  padding: 24px;
  overflow-y: auto;
  flex: 1;
}

.publish-app-icon-container {
  display: flex;
  justify-content: center;
  margin-bottom: 20px;
}

.publish-app-icon {
  width: 80px;
  height: 80px;
  border-radius: 16px;
  object-fit: cover;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

.publish-app-icon-placeholder {
  width: 80px;
  height: 80px;
  border-radius: 16px;
  background-color: rgba(255, 255, 255, 0.1);
  display: flex;
  align-items: center;
  justify-content: center;
}

.publish-app-icon-placeholder .icon {
  position: relative;
  width: 40px;
  height: 40px;
}

.publish-app-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.publish-app-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.publish-app-field label {
  font-size: 13px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.8);
}

.publish-app-field input,
.publish-app-field textarea {
  background-color: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 8px;
  padding: 12px 14px;
  font-size: 14px;
  color: #fff;
  font-family: inherit;
  transition: border-color 0.2s ease, background-color 0.2s ease;
}

.publish-app-field input:focus,
.publish-app-field textarea:focus {
  outline: none;
  border-color: rgba(255, 255, 255, 0.4);
  background-color: rgba(255, 255, 255, 0.12);
}

.publish-app-field input::placeholder,
.publish-app-field textarea::placeholder {
  color: rgba(255, 255, 255, 0.4);
}

.publish-app-field textarea {
  min-height: 80px;
  resize: vertical;
}

.publish-app-hint {
  font-size: 11px;
  color: rgba(255, 255, 255, 0.5);
}

/* Toggle field styling for publish dialog */
.publish-app-toggle-field {
  padding: 8px 0;
}

.publish-app-toggle-field .toggle-container {
  margin-bottom: 4px;
}

.publish-app-toggle-field .toggle-container span:first-child {
  font-size: 14px;
  font-weight: 500;
}

/* Permissions selector in publish dialog */
.publish-app-permissions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 8px;
}

.publish-app-permission-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 8px;
  cursor: pointer;
  transition: background-color 0.15s ease, opacity 0.15s ease;
  user-select: none;
  position: relative;
  z-index: 1;
  opacity: 0.5;
}

.publish-app-permission-item:hover {
  background: rgba(255, 255, 255, 0.08);
}

/* When checked, full opacity and green accent */
.publish-app-permission-item.checked {
  opacity: 1;
}

/* Hide the actual checkbox */
.publish-app-permission-item input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  pointer-events: none;
}

/* Custom checkbox indicator */
.publish-app-permission-check {
  width: 20px;
  height: 20px;
  min-width: 20px;
  border-radius: 4px;
  border: 2px solid rgba(255, 255, 255, 0.3);
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.15s ease;
  flex-shrink: 0;
}

.publish-app-permission-item.checked .publish-app-permission-check {
  background: var(--icon-color-primary, #59FCB3);
  border-color: var(--icon-color-primary, #59FCB3);
}

/* Checkmark icon */
.publish-app-permission-check::after {
  content: '';
  width: 6px;
  height: 10px;
  border: solid transparent;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) translateY(-1px);
  transition: border-color 0.15s ease;
}

.publish-app-permission-item.checked .publish-app-permission-check::after {
  border-color: #000;
}

.publish-app-permission-text {
  display: flex;
  flex: 1;
  gap: 10px;
  cursor: pointer;
}

.publish-app-permission-label {
  font-size: 13px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.9);
  min-width: 100px;
}

.publish-app-permission-desc {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.5);
  flex: 1;
}

.publish-app-buttons {
  display: flex;
  gap: 12px;
  padding: 20px 24px;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  justify-content: flex-end;
}

.publish-app-buttons button {
  min-width: 100px;
}

.publish-app-buttons .file-preview-btn-send {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.publish-app-buttons .file-preview-btn-send .icon {
  position: relative;
  width: 16px;
  height: 16px;
}
@media (max-width: 480px) {
  .publish-app-container {
    width: 95%;
    max-height: 90vh;
  }

  .publish-app-header {
    padding: 16px 20px;
  }

  .publish-app-content {
    padding: 20px;
  }

  .publish-app-buttons {
    padding: 16px 20px;
  }
}

/* ============================================================================
   App Details Panel
   ============================================================================ */

.app-details-panel {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: #0a0a0a;
  z-index: 9999;
  display: flex;
  flex-direction: column;
  animation: fadeInWithoutTransform 0.3s cubic-bezier(.46,.03,.52,.96);
}

.app-details-panel.closing {
  animation: fadeOutWithoutTransform 0.3s cubic-bezier(.46,.03,.52,.96) forwards;
}

.app-details-header {
  position: relative;
  height: 60px;
  min-height: 60px;
  background-color: rgba(22, 22, 22, 0.85);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 15px;
  width: 100%;
  box-sizing: border-box;
  z-index: 100;
}

.app-details-title {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
  color: #fff;
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
}

.app-details-header-spacer {
  width: 40px;
}

.app-details-content {
  flex: 1;
  overflow-y: auto;
  padding: 0;
}

/* App Details Hero Section */
.app-details-hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 40px 20px 30px;
  text-align: center;
}

.app-details-icon-container {
  width: 120px;
  height: 120px;
  border-radius: 24px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: rgba(255, 255, 255, 0.1);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
  margin-bottom: 20px;
  /* Contain the absolutely-positioned .icon-play placeholder when an icon is missing/fails. */
  position: relative;
}

.app-details-icon {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.app-details-icon-placeholder {
  position: relative;
  width: 60px;
  height: 60px;
  background-color: rgba(255, 255, 255, 0.3);
}

.app-details-name {
  font-size: 24px;
  font-weight: 700;
  color: #fff;
  margin: 0 0 8px 0;
}

.app-details-version {
  font-size: 14px;
  color: rgba(255, 255, 255, 0.5);
  margin-bottom: 16px;
}

.app-details-categories {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
  margin-bottom: 24px;
}

.app-details-category {
  font-size: 12px;
  padding: 6px 14px;
  border-radius: 20px;
  background-color: rgba(255, 255, 255, 0.1);
  color: rgba(255, 255, 255, 0.8);
  cursor: pointer;
  transition: background-color 0.15s ease, transform 0.15s ease;
}

.app-details-category:hover {
  background-color: rgba(255, 255, 255, 0.2);
  transform: scale(1.05);
}

/* App Details Action Button */
.app-details-action-btn {
  padding: 14px 48px;
  border-radius: 12px;
  font-size: 16px;
  font-weight: 600;
  border: none;
  cursor: pointer;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.app-details-action-btn:hover {
  transform: scale(1.05);
}

.app-details-action-btn:active {
  transform: scale(0.98);
}

.app-details-action-btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
  transform: none;
}

.app-details-action-btn.installed {
  background-color: rgba(255, 255, 255, 0.2);
  color: #fff;
}

.app-details-action-btn.installing {
  background-color: rgba(255, 200, 100, 0.3);
}

/* App Details Actions Container */
.app-details-actions {
  display: flex;
  gap: 12px;
  justify-content: center;
  width: 100%;
}

/* App Details Play Button */
.app-details-play-btn {
  flex: 1;
  background-color: #58fcb3;
  color: #000;
}

.app-details-play-btn:hover {
  box-shadow: 0 4px 16px rgba(89, 252, 179, 0.4);
}

/* App Details Uninstall Button */
.app-details-uninstall-btn {
  flex: 1;
  background-color: var(--danger-color-hover);
  color: var(--danger-color);
  border: 1px solid color-mix(in srgb, var(--danger-color) 50%, transparent);
}

.app-details-uninstall-btn:hover {
  background-color: color-mix(in srgb, var(--danger-color) 40%, transparent);
  box-shadow: 0 4px 16px color-mix(in srgb, var(--danger-color) 30%, transparent);
}

/* App Details Update Button */
.app-details-update-btn {
  flex: 1;
  background: linear-gradient(135deg, rgba(89, 252, 179, 0.3), rgba(43, 151, 108, 0.3));
  color: #59fcb3;
  border: 1px solid rgba(89, 252, 179, 0.5);
  animation: update-glow 2s ease-in-out infinite;
}

.app-details-update-btn:hover {
  background: linear-gradient(135deg, rgba(89, 252, 179, 0.4), rgba(43, 151, 108, 0.4));
  box-shadow: 0 4px 16px rgba(89, 252, 179, 0.4);
}

/* App Details Version Update Display */
.app-details-version-installed {
  color: rgba(255, 255, 255, 0.5);
}

.app-details-version-arrow {
  color: #59fcb3;
  margin: 0 6px;
}

.app-details-version-new {
  color: #59fcb3;
  font-weight: 600;
}

/* App Details Info Sections */
.app-details-section {
  padding: 24px 20px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.app-details-section-title {
  font-size: 14px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.5);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin: 0 0 12px 0;
}

.app-details-description {
  font-size: 15px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.85);
  white-space: pre-wrap;
  word-wrap: break-word;
}

.app-details-changelog {
  font-size: 14px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.7);
  white-space: pre-wrap;
  word-wrap: break-word;
}

/* App Details Meta Info */
.app-details-meta {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.app-details-meta-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.app-details-meta-label {
  font-size: 14px;
  color: rgba(255, 255, 255, 0.5);
}

.app-details-meta-value {
  font-size: 14px;
  color: rgba(255, 255, 255, 0.9);
  font-weight: 500;
}

/* Publisher Section */
.app-details-publisher {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  background-color: rgba(255, 255, 255, 0.05);
  border-radius: 10px;
  cursor: pointer;
  transition: background-color 0.2s ease;
}

.app-details-publisher:hover {
  background-color: rgba(255, 255, 255, 0.08);
}

.app-details-publisher-avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background-color: rgba(255, 255, 255, 0.1);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  flex-shrink: 0;
}

.app-details-publisher-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.app-details-publisher-avatar .icon {
  position: relative;
  width: 20px;
  height: 20px;
  background-color: rgba(255, 255, 255, 0.4);
}

.app-details-publisher-info {
  flex: 1;
  min-width: 0;
  line-height: 1.2;
}

.app-details-publisher-name {
  font-size: 14px;
  font-weight: 600;
  color: #fff;
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.app-details-publisher-npub {
  font-size: 11px;
  color: rgba(255, 255, 255, 0.5);
  font-family: monospace;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-top: 1px;
}

.app-details-publisher-arrow-container {
  position: relative;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.app-details-publisher-arrow-container .icon {
  position: relative;
  width: 20px;
  height: 20px;
  background-color: rgba(255, 255, 255, 0.3);
  transform: rotate(180deg);
}

/* Developer Section */
.app-details-developer {
  padding: 10px 12px;
  background-color: rgba(255, 255, 255, 0.05);
  border-radius: 10px;
}

.app-details-developer-name {
  font-size: 14px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.9);
}

/* Source Link */
.app-details-meta-link {
  font-size: 13px;
  color: rgba(255, 255, 255, 0.7);
  text-decoration: none;
  cursor: pointer;
  transition: color 0.15s ease;
}

.app-details-meta-link:hover {
  color: #fff;
  text-decoration: underline;
}

/* Mobile Responsive */
@media (max-width: 480px) {
  .app-details-hero {
    padding: 30px 16px 24px;
  }

  .app-details-icon-container {
    width: 100px;
    height: 100px;
    border-radius: 20px;
  }

  .app-details-name {
    font-size: 22px;
  }

  .app-details-action-btn {
    padding: 12px 40px;
    font-size: 15px;
  }

  .app-details-section {
    padding: 20px 16px;
  }
}

/* App Details - Permissions Section */
.app-details-permissions-hint {
  font-size: 13px;
  color: rgba(255, 255, 255, 0.5);
  margin-bottom: 12px;
}

.app-details-permissions {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.app-details-permissions-loading {
  color: rgba(255, 255, 255, 0.5);
  font-size: 13px;
  font-style: italic;
}

.app-details-permission-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 14px;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 10px;
  gap: 12px;
}

.app-details-permission-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
}

.app-details-permission-label {
  font-size: 14px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.9);
}

.app-details-permission-desc {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.5);
}

.app-details-permission-toggle {
  flex-shrink: 0;
}

.toggle-container.app-details-permission-toggle {
  margin-bottom: 0;
}

.app-details-reset-permissions-btn {
  margin-top: 12px;
  padding: 8px 16px;
  background: color-mix(in srgb, var(--danger-color) 15%, transparent);
  border: 1px solid color-mix(in srgb, var(--danger-color) 30%, transparent);
  border-radius: 8px;
  color: var(--danger-color);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
  width: 100%;
}

.app-details-reset-permissions-btn:hover {
  background: color-mix(in srgb, var(--danger-color) 25%, transparent);
  border-color: color-mix(in srgb, var(--danger-color) 50%, transparent);
  color: var(--danger-color);
}

/* Permission Prompt Overlay */
.permission-prompt-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.7);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 10001;
  opacity: 0;
  transition: opacity 0.3s ease;
}

.permission-prompt-overlay.active {
  opacity: 1;
}

.permission-prompt-container {
  background: linear-gradient(180deg, rgba(40, 40, 50, 0.98) 0%, rgba(30, 30, 40, 0.98) 100%);
  border-radius: 16px;
  border: 1px solid rgba(255, 255, 255, 0.15);
  width: 90%;
  max-width: 420px;
  max-height: 80vh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}

.permission-prompt-header {
  padding: 20px 24px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.permission-prompt-header h2 {
  font-size: 18px;
  font-weight: 600;
  color: #fff;
  margin: 0 0 8px 0;
}

.permission-prompt-subtitle {
  font-size: 14px;
  color: rgba(255, 255, 255, 0.7);
  margin: 0;
}

.permission-prompt-content {
  padding: 20px 24px;
  overflow-y: auto;
  flex: 1;
}

.permission-prompt-hint {
  font-size: 13px;
  color: rgba(255, 255, 255, 0.5);
  margin: 0 0 16px 0;
}

.permission-prompt-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.permission-prompt-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 10px;
}

.permission-prompt-toggle {
  flex-shrink: 0;
  margin-bottom: 0;
}

.permission-prompt-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
}

.permission-prompt-label {
  font-size: 14px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.9);
}

.permission-prompt-desc {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.5);
}

.permission-prompt-buttons {
  display: flex;
  gap: 12px;
  padding: 16px 24px;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  justify-content: flex-end;
}

.permission-prompt-buttons button {
  min-width: 100px;
}

/* Notification Sound Chip */
.notif-custom-container {
  display: flex;
  align-items: center;
  text-align: center;
  margin-top: 20px;
  margin-bottom: 10px;
}

.notif-sound-chip {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 12px 12px 15px;
  background: rgba(255, 255, 255, 0.08);
  border-radius: 25px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  flex: 1;
  min-width: 0;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}

.notif-sound-chip:hover {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.2);
}

.notif-sound-chip-icon {
  font-size: 20px;
  opacity: 0.7;
  flex-shrink: 0;
}

.notif-sound-chip-name {
  font-size: 14px;
  color: rgba(255, 255, 255, 0.85);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
  min-width: 0;
}

.notif-sound-chip-clear {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border: none;
  background: rgba(255, 255, 255, 0.15);
  color: rgba(255, 255, 255, 0.7);
  border-radius: 50%;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.15s, color 0.15s;
  padding: 0;
}

.notif-sound-chip-clear:hover {
  background: var(--danger-color-hover);
  color: var(--danger-color);
}

/* --- Mention Selector --- */
.mention-selector {
  position: fixed;
  background: #0a0a0a;
  border: 1px solid var(--icon-color-primary);
  border-radius: 12px;
  z-index: 2000;
  width: calc(100% - 55px);
  max-width: 340px;
  overflow: hidden;
  box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  padding: 6px 0;
  transform: translateY(8px);
  opacity: 0;
  pointer-events: none;
  transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1),
              opacity 0.2s ease;
}

.mention-selector.visible {
  transform: translateY(0);
  opacity: 1;
  pointer-events: auto;
}

.mention-selector-header {
  padding: 6px 12px 4px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--icon-color-primary, #59fcb3);
  opacity: 0.7;
}

.mention-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  cursor: pointer;
  font-size: 14px;
  color: rgba(255, 255, 255, 0.9);
  transition: background-color 0.15s ease;
  position: relative;
}

.mention-item:hover,
.mention-item.active {
  background: rgba(255, 255, 255, 0.08);
}

.mention-item.active::before {
  content: '';
  position: absolute;
  left: 0;
  top: 4px;
  bottom: 4px;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--icon-color-primary, #59fcb3);
}

.mention-item img {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
  border: 1.5px solid rgba(255, 255, 255, 0.1);
}

.mention-item-avatar-placeholder {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  border: 1.5px solid rgba(255, 255, 255, 0.1);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 600;
  color: var(--icon-color-primary, #59fcb3);
  flex-shrink: 0;
}

.mention-item-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
}

/* Inline mention highlight in rendered messages */
.mention {
  background: rgba(255, 255, 255, 0.06);
  color: var(--icon-color-primary, #59fcb3);
  border-radius: 4px;
  padding: 1px 4px;
  cursor: pointer;
  font-weight: 500;
  transition: background-color 0.15s ease;
}

.mention:hover {
  background: rgba(255, 255, 255, 0.12);
  text-decoration: none;
}

/* --- Slash Command Selector --- */
.command-selector {
  position: fixed;
  background: #0a0a0a;
  border: 1px solid var(--icon-color-primary);
  border-radius: 12px;
  z-index: 2000;
  width: calc(100% - 55px);
  max-width: min(420px, calc(100vw - 20px));
  max-height: min(320px, 45vh);
  overflow-y: auto;
  box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  /* No top padding: section headers stick flush to the scrollport edge, so a
     padding gap would let rows peek above the stuck header. */
  padding: 0 0 6px;
  transform: translateY(8px);
  opacity: 0;
  pointer-events: none;
  transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1),
              opacity 0.2s ease;
}

.command-selector.visible {
  transform: translateY(0);
  opacity: 1;
  pointer-events: auto;
}

.command-section-header {
  /* Sticks to the panel top while its own section scrolls; the NEXT section's
     header pushes it away (sticky is clamped to the .command-section wrapper). */
  position: sticky;
  top: 0;
  z-index: 1;
  background: #0a0a0a;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px 4px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--icon-color-primary, #59fcb3);
}

.command-section-header > span {
  opacity: 0.7;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.command-section-header img {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  object-fit: cover;
}

/* Inline "Checking for Updates" on the header's right — replaces the footer that
   shifted the panel's layout while a bot's manifest REQ was in flight. */
.command-section-header .command-section-refresh {
  margin-left: auto;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  opacity: 1;
  font-size: 10px;
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0;
  color: rgba(255, 255, 255, 0.42);
}

.command-section-header .command-section-refresh .command-spinner {
  width: 9px;
  height: 9px;
  border-width: 1.5px;
}

.command-item {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 2px 8px;
  padding: 8px 12px;
  cursor: pointer;
  font-size: 14px;
  color: rgba(255, 255, 255, 0.9);
  transition: background-color 0.15s ease;
  position: relative;
  min-width: 0;
  /* Keyboard nav scrolls rows into view; keep them clear of the stuck header. */
  scroll-margin-top: 32px;
}

.command-item:hover,
.command-item.active {
  background: rgba(255, 255, 255, 0.08);
}

.command-item.active::before {
  content: '';
  position: absolute;
  left: 0;
  top: 4px;
  bottom: 4px;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--icon-color-primary, #59fcb3);
}

.command-item-name {
  font-weight: 600;
  color: #fff;
  flex-shrink: 0;
}

/* Recents rows wear their owning bot's face (mixed-bot section). */
.command-item img.command-item-bot {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

/* --- Command invocation line (in-chat render of a sent command) ---
   Passive prose, no bubble: "JSKitty ran /roll with ◎ Concordia".
   Inline flow, not flex: the sentence must wrap at word boundaries on narrow
   widths (flex + the content slot's overflow-wrap:anywhere squishes each word
   into a vertical column). break-word overrides `anywhere` so words stay whole. */
.dmsg-command-line {
  font-size: 13px;
  font-style: italic;
  color: rgba(255, 255, 255, 0.45);
  overflow-wrap: break-word;
  -webkit-user-select: none;
  user-select: none;
}

.dmsg-command-author {
  color: rgba(255, 255, 255, 0.8);
  font-weight: 600;
  font-style: normal;
}

.dmsg-command-line .dmsg-command-name {
  color: var(--icon-color-primary, #59fcb3);
  font-weight: 600;
  font-style: normal;
}

/* Avatar + name wrap as one unit: no-whitespace alone doesn't glue them,
   since WebKit allows soft breaks around replaced elements. */
.dmsg-command-bot-unit {
  white-space: nowrap;
}

.dmsg-command-bot-avatar {
  width: 15px;
  height: 15px;
  border-radius: 50%;
  object-fit: cover;
  vertical-align: text-bottom;
  margin-right: 4px;
}

.dmsg-command-bot {
  color: var(--icon-color-primary, #59fcb3);
  font-weight: 600;
  font-style: normal;
  display: inline-block;
  vertical-align: bottom;
  max-width: 22ch;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Accent-colored words read as actionable via an underline, not a dim —
   dimming a green word looks like a glitch. The bot avatar keeps .btn's dim. */
.dmsg-command-line .dmsg-command-name.btn:hover,
.dmsg-command-bot.btn:hover {
  opacity: 1;
  text-decoration: underline;
}

.command-item-arg {
  font-size: 12px;
  padding: 1px 6px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.75);
  flex-shrink: 0;
}

.command-item-arg.optional {
  opacity: 0.55;
}

.command-item-arg.current {
  background: var(--icon-color-primary, #59fcb3);
  color: #0a0a0a;
  font-weight: 600;
}

.command-item-desc {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.45);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
  min-width: 0;
  text-align: right;
}

/* Narrow windows/phones: the command + arg chips form the top line and the
   description tucks below it (flex-wrap sends a 100%-basis item to its own
   row), wrapping up to two lines instead of vanishing. */
@media (max-width: 520px) {
  .command-item-desc {
    flex-basis: 100%;
    text-align: left;
    white-space: normal;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    /* Tuck tight under the command line: cancel the row-gap so the
       description reads as the command's subtitle, not a separate row. */
    margin-top: -1px;
    line-height: 1.25;
  }
}

.command-loading {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.65);
}

/* Single informational states (loading / checking / empty): center the one
   row in the panel instead of letting it hug the top — the panel's own padding
   is bottom-only, so a top-anchored row reads as off-center. */
.command-selector.command-selector--message {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 56px;
  /* padding:0 (not the base's bottom-only 6px) — horizontal padding would add
     to position()'s inline width and overflow the right edge. */
  padding: 0;
}

/* Bots present but no command manifest — a terminal empty state, dimmer. */
.command-empty {
  font-size: 13px;
  color: rgba(255, 255, 255, 0.5);
  text-align: center;
}

/* The converging-list footer: quieter than the initial loading state. */
.command-refreshing {
  padding: 6px 12px;
  font-size: 11px;
  color: rgba(255, 255, 255, 0.4);
}

.command-refreshing .command-spinner {
  width: 10px;
  height: 10px;
}

.command-spinner {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.15);
  border-top-color: var(--icon-color-primary, #59fcb3);
  animation: command-spin 0.8s linear infinite;
  flex-shrink: 0;
}

@keyframes command-spin {
  to { transform: rotate(360deg); }
}

.command-hint {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  padding: 8px 12px 4px;
  font-size: 14px;
}

.command-hint-desc {
  padding: 0 12px 6px;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.5);
}

.command-hint-choices {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 2px 12px 8px;
}

.command-choice {
  font-size: 12px;
  padding: 2px 8px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: rgba(255, 255, 255, 0.85);
  cursor: pointer;
  transition: background-color 0.15s ease;
}

.command-choice:hover {
  background: var(--icon-color-primary, #59fcb3);
  color: #0a0a0a;
}

/* --- Structured command composer (replaces the textarea while filling) --- */
.command-composer {
  /* EXACTLY the textarea's width model (fill-available base + shrink), so the
     row's flex math is byte-identical between modes — the buttons render
     shrunken (~38px) in normal chat because the textarea's base over-fills
     the row, and a 0-basis bar would let them spring to 45px on command
     entry. Content stays contained by the pill-level caps (max-width,
     overflow hidden, the row's min-width: 0), wrapping when out of room.
     Centered within the row's 45px band so single-line pills share the
     buttons' midline. */
  display: flex;
  align-items: center;
  align-content: center;
  flex-wrap: wrap;
  gap: 5px;
  flex: 0 1 auto;
  width: -webkit-fill-available;
  min-width: 0;
  box-sizing: border-box;
  min-height: 45px;
  /* Left inset stands in for the hidden "+" button, which used to supply it;
     the send button still frames the right, so the row stays balanced. */
  padding: 6px 10px 6px 12px;
  margin: 0;
  outline: none;
}

/* The command context bar: the reply bar's tucked-strip design, showing
   "Using /cmd with Bot" above the composer while the parts are filled. The
   strip is only half the illusion — the box's padding-top grows by the same
   34px on the same curve (see the reply bar notes above), so the strip slides
   out from underneath the composer. With BOTH strips open, reply keeps its
   usual dock and the command strip (closest context to the parts) sits
   between it and the composer. */
#chat-box.commanding {
  padding-top: 39px;   /* base 5px + the strip's 34px visible band */
}

#chat-box.commanding.replying {
  padding-top: 73px;   /* both strips: 5px + 34px + 34px */
}

/* The structured command IS the whole message: attaching a file or inserting
   an emoji doesn't apply, so hide those buttons while the composer is open. */
#chat-box.commanding #chat-input-file,
#chat-box.commanding #chat-input-emoji {
  display: none;
}

#chat-command-bar {
  position: absolute;
  box-sizing: border-box;
  top: 5px;
  left: 8px;
  right: 8px;
  height: 12px;
  z-index: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 44px 12px 12px;
  -webkit-user-select: none;
  user-select: none;
  cursor: default;
  background-color: #0d0d0d;
  background-color: color-mix(in srgb, var(--accent-color, #59fcb3) 6%, #060606);
  border-radius: 10px 10px 0 0;
  overflow: hidden;
  transition: height 0.22s cubic-bezier(0.33, 1, 0.68, 1),
              top 0.22s cubic-bezier(0.33, 1, 0.68, 1);
}

#chat-box.commanding #chat-command-bar {
  height: 46px;
}

/* Whenever the reply strip is open the command bar rides at the composer's
   MOVED position — open (both strips stacked: reply keeps its top dock, the
   command strip paints over reply's tucked tail) or closed (still tucked
   behind the composer; at top 5px its closed band would float exposed in
   the reply strip's zone and clip the reply cancel button). */
#chat-box.replying #chat-command-bar {
  top: 39px;
}

#chat-command-bar > * {
  opacity: 0;
  transition: opacity 0.18s ease;
}

#chat-box.commanding #chat-command-bar > * {
  opacity: 1;
}

#chat-command-bar-label {
  color: rgba(255, 255, 255, 0.85);
  font-size: 13.5px;
  white-space: nowrap;
  flex-shrink: 0;
}

#chat-command-bar-cmd {
  color: var(--accent-color, #59fcb3);
  font-weight: 600;
}

#chat-command-bar-bot {
  flex: 0 1 auto;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--accent-color, #59fcb3);
  font-size: 13px;
  font-weight: 600;
}

/* The focused param's manifest description, right-aligned and truncating —
   the visible twin of the hover tooltip (mobile has no hover). */
#chat-command-bar-hint {
  flex: 1;
  min-width: 0;
  text-align: right;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.45);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

#chat-command-bar-bot span {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

#chat-command-bar-bot img {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

#chat-command-bar-cancel {
  position: absolute;
  right: 10.5px;
  top: calc((100% - 12px) / 2);
  transform: translateY(-50%);
  width: 24px;
  height: 24px;
  padding: 3px;
  background: transparent;
}

#chat-command-bar-cancel .icon-cancel {
  position: relative;
  display: block;
  width: 18px;
  height: 18px;
  margin: 0;
  background-color: var(--danger-color);
  transition: filter 0.15s ease;
}

#chat-command-bar-cancel:hover .icon-cancel {
  filter: brightness(1.25);
}

@media (prefers-reduced-motion: reduce) {
  #chat-command-bar,
  #chat-command-bar > * {
    transition: none;
  }
}

.command-part {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 3px 2px 8px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.1);
  min-width: 0;
  max-width: 100%;
  /* Never squish beside a growing neighbor — wrap to the next line instead
     (the pill's own max-width still caps it at the bar's width) — and never
     paint past the pill's own bounds. */
  flex-shrink: 0;
  overflow: hidden;
}

/* A wrapped (multi-line) value: the pill expands downward while the label
   stays pinned at its top, aligned with the first text line. */
.command-part.multiline {
  align-items: flex-start;
}

.command-part.multiline .command-part-name {
  padding-top: 4px;
}

.command-part.required .command-part-name::after {
  content: '*';
  color: var(--danger-color, #ff5d5d);
}

.command-part.invalid {
  border-color: #ff5d5d;
  box-shadow: 0 0 0 1px rgba(255, 93, 93, 0.4);
}

.command-part-name {
  font-size: 11px;
  font-weight: 600;
  text-transform: lowercase;
  color: rgba(255, 255, 255, 0.55);
  flex-shrink: 0;
  transition: color 0.15s ease;
}

/* The param being edited announces itself; the rest stay dim. */
.command-part:focus-within .command-part-name {
  color: #fff;
}

.command-part-input {
  background: rgba(0, 0, 0, 0.35);
  border: none;
  outline: none;
  border-radius: 7px;
  color: #fff;
  font-size: 13px;
  line-height: 1.3;
  padding: 2px 8px;
  /* border-box so the JS `height = scrollHeight` fit is exact — scrollHeight
     includes the padding, and writing it as a content-box height left every
     field 4px too tall with its text riding the top. */
  box-sizing: border-box;
  width: 11ch;
  min-width: 8ch;
}

.command-part-input:focus {
  box-shadow: 0 0 0 1.5px var(--icon-color-primary, #59fcb3);
}

/* Free-text values get room; selects size to their options. */
input.command-part-input {
  field-sizing: content;
  max-width: 34ch;
}

/* String params are 1-row textareas: wide-then-wrap growth. The width comes
   from JS ch-sizing; the max-width clamp is what turns further growth into
   line wrapping (height then follows scrollHeight). */
textarea.command-part-input {
  resize: none;
  overflow: hidden;
  line-height: 1.3;
  max-width: 100%;
  flex-shrink: 1;
}

/* Choice trigger: a button that dresses as a field (same 21px box, same bg)
   with a slim chevron — the drop-up replaces native selects, which render
   inconsistently per platform (Android opens an OS modal). Scoped under
   .command-part to outrank `.chat-input-container button` (the 45px
   composer-icon sizing), which otherwise drafts the trigger into icon duty. */
.command-part button.command-choice-trigger {
  display: inline-flex;
  align-items: center;
  margin: 0;
  border: none;
  width: auto;
  height: 21px;
  max-width: 22ch;
  overflow: hidden;
  cursor: pointer;
  font-family: inherit;
  font-size: 13px;
  color: #fff;
  text-align: left;
  padding: 2px 20px 2px 8px;
  border-radius: 7px;
  background-color: rgba(0, 0, 0, 0.35);
  background-image:
    linear-gradient(45deg, transparent 50%, rgba(255, 255, 255, 0.55) 50%),
    linear-gradient(135deg, rgba(255, 255, 255, 0.55) 50%, transparent 50%);
  background-position: calc(100% - 12px) calc(50% - 1px), calc(100% - 8px) calc(50% - 1px);
  background-size: 4px 4px, 4px 4px;
  background-repeat: no-repeat;
}

.command-part button.command-choice-trigger span {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.command-part button.command-choice-trigger.placeholder span {
  color: rgba(255, 255, 255, 0.4);
}

/* The drop-up itself: the picker family's panel language, compact. */
.command-choice-menu {
  position: fixed;
  z-index: 2001;
  background: #0a0a0a;
  border: 1px solid var(--icon-color-primary, #59fcb3);
  border-radius: 10px;
  box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.5);
  /* No inner padding: options run edge-to-edge (clipped to the radius by
     overflow), matching every other Vector dropdown. */
  padding: 0;
  max-height: 40vh;
  max-width: calc(100vw - 16px);
  overflow-y: auto;
  display: none;
}

.command-choice-menu.visible {
  display: block;
}

.command-choice-option {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 14px;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.9);
  cursor: pointer;
  max-width: 40ch;
}

.command-choice-option span {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.command-choice-option img {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

.command-choice-option:hover,
.command-choice-option.active {
  background: rgba(255, 255, 255, 0.08);
}

.command-choice-option.skip {
  color: rgba(255, 255, 255, 0.4);
}

/* The trailing free-text arg (the wire's greedy tail) takes the row's rest.
   The 150px basis is its usefulness floor: when a growing neighbor leaves
   less than that on the line, the pill wraps to its OWN line and expands
   full-width instead of surviving as a sliver (a 0 basis would always
   "fit" and never wrap). The field flexes into the pill's leftover space
   beside the label — a width percentage would resolve against the WHOLE
   pill and overflow it by the label's width. */
.command-part.grow {
  flex: 1 0 150px;
}

.command-part.grow .command-part-input {
  flex: 1 1 0%;
  width: auto;
  max-width: none;
  min-width: 0;
}

/* A picked member: the field shows their NAME, mention-styled, while the
   canonical npub rides in data-npub for the wire. */
.command-part-input.user-resolved {
  color: var(--icon-color-primary, #59fcb3);
  font-weight: 600;
}

/* --- Emoji Shortcode Selector --- */
.emoji-shortcode-selector {
  position: fixed;
  background: #0a0a0a;
  border: 1px solid var(--icon-color-primary);
  border-radius: 12px;
  z-index: 2000;
  width: calc(100% - 55px);
  max-width: 340px;
  overflow: hidden;
  box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  padding: 6px 0;
  transform: translateY(8px);
  opacity: 0;
  pointer-events: none;
  transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1),
              opacity 0.2s ease;
}

.emoji-shortcode-selector.visible {
  transform: translateY(0);
  opacity: 1;
  pointer-events: auto;
}

.emoji-shortcode-header {
  padding: 6px 12px 4px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--icon-color-primary, #59fcb3);
  opacity: 0.7;
}

.emoji-shortcode-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  cursor: pointer;
  font-size: 14px;
  color: rgba(255, 255, 255, 0.9);
  transition: background-color 0.15s ease;
  position: relative;
}

.emoji-shortcode-item:hover,
.emoji-shortcode-item.active {
  background: rgba(255, 255, 255, 0.08);
}

.emoji-shortcode-item.active::before {
  content: '';
  position: absolute;
  left: 0;
  top: 4px;
  bottom: 4px;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--icon-color-primary, #59fcb3);
}

.emoji-shortcode-item img,
.emoji-shortcode-item-fallback {
  width: 28px;
  height: 28px;
  object-fit: contain;
  flex-shrink: 0;
  font-size: 22px;
  line-height: 28px;
  text-align: center;
}

.emoji-shortcode-item-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
}

/* ============================================================================
 * Discord-style message rows (.dmsg-*)
 *
 * Row skeleton: avatar gutter, author/time header, content slot, reactions row.
 * Streak collapse (data-streak="continuation"), floating hover toolbar, and
 * attribute-driven highlight states (data-pinged / data-replying-to / data-jumped).
 * ============================================================================ */

.chat-messages {
  /* Scoped tokens — override via theme to retune. */
  --dmsg-row-padding-y: 0px;
  --dmsg-row-padding-x: 2px;
  /* -2px each to offset the row's new 2px padding-bottom, so content-to-content spacing is
   * unchanged — the 2px just moves from transparent margin into the row's highlighted padding. */
  --dmsg-streak-gap: 14px;
  --dmsg-continuation-gap: 1px;
  /* Symmetric breathing room for inline separators (timestamps, date + unread dividers,
   * system events). Applied top AND bottom so the divider sits centered between its
   * neighbours — the following row's top-margin is zeroed so it doesn't double up. */
  --dmsg-system-gap: 10px;
  --dmsg-gutter-width: 56px;
  --dmsg-avatar-size: 40px;
  /* Discord-style reply: how far to push the big avatar down so it aligns with the name instead of
   * the reply line above it, and the colour of the elbow connector. Tune the lift to the reply's
   * rendered height (padding + one text line + margin-bottom). */
  --dmsg-reply-lift: 24px;
  --dmsg-reply-spine: rgba(255, 255, 255, 0.25);
  --dmsg-author-color: #ffffff;
  --dmsg-text-color: rgba(255, 255, 255, 0.85);
  --dmsg-time-color: rgba(255, 255, 255, 0.4);
  --dmsg-pending-opacity: 0.6;
  --dmsg-hover-bg: rgba(255, 255, 255, 0.03);
  --dmsg-pinged-bg: color-mix(in srgb, var(--icon-color-primary, #59fcb3) 13%, transparent);
  --dmsg-pinged-border: var(--icon-color-primary, #59fcb3);
  --dmsg-replying-bg: rgba(255, 255, 255, 0.05);
  --dmsg-replying-border: rgba(255, 255, 255, 0.5);
  --dmsg-failed-bg: color-mix(in srgb, var(--danger-color) 10%, transparent);
  --dmsg-failed-border: var(--danger-color);
}

.dmsg {
  display: grid;
  grid-template-columns: var(--dmsg-gutter-width) 1fr;
  align-items: start;
  /* Extend the row's BOX past the .chat-messages 10px padding on each side so
   * the hover/pinged/replying/jumped highlight fills the chat panel wall-to-wall.
   * Padding compensates so the content lands at the original comfortable inset. */
  width: calc(100% + 20px);
  max-width: calc(100% + 20px);
  margin-left: -10px;
  margin-right: -10px;
  padding-top: var(--dmsg-row-padding-y);
  /* +2px so a short row's 40px avatar (and the last text line) don't touch the highlight's bottom
   * edge; the top's breathing room already comes from the avatar's margin-top. */
  padding-bottom: calc(var(--dmsg-row-padding-y) + 2px);
  padding-left: calc(10px + var(--dmsg-row-padding-x));
  padding-right: calc(10px + var(--dmsg-row-padding-x));
  border-left: 3px solid transparent;
  background: transparent;
  transition: background-color 0.08s ease, border-left-color 0.08s ease;
  box-sizing: border-box;
  text-align: left;
  font-family: Rubik, sans-serif;
}

.dmsg[data-streak="first"]        { margin-top: var(--dmsg-streak-gap); }
.dmsg[data-streak="continuation"] { margin-top: var(--dmsg-continuation-gap); }
/* Last-child bottom clearance is unified on `.chat-messages > *:last-child`
 * (covers dmsg rows + system-event paragraphs alike) — see that rule. */
/* Top clearance for the fixed chat-header is handled by `.chat-messages`'s
 * padding-top — see that rule. Putting it on the container instead of on
 * `:first-child` means whatever ends up at the top of the list (date
 * divider, dmsg, unread divider, system event) gets the same clearance
 * without depending on which element won the first-child race. */

.dmsg:hover                            { background: var(--dmsg-hover-bg); }
.dmsg[data-pinged="true"]              { background: var(--dmsg-pinged-bg); border-left-color: var(--dmsg-pinged-border); }
.dmsg[data-replying-to="true"]         { background: var(--dmsg-replying-bg); border-left-color: var(--dmsg-replying-border); }
/* A file box in flight carries its own progress bar and cancel button, which say
   "not sent yet" better than a wash does. Opacity composites the whole subtree, so the
   box cannot opt out from inside — the row has to stop dimming instead. */
.dmsg[data-status="pending"] .dmsg-content:not(:has(.file-attachment)) { opacity: var(--dmsg-pending-opacity); }
/* Failed sends need a row-level signal — the inline "Failed" text alone
   gets lost on rich attachments. Red left border + soft red wash matches
   the ping/reply pattern, and the attachment gets dimmed so a failed image
   doesn't read as "delivered". */
.dmsg[data-status="failed"] {
  background: var(--dmsg-failed-bg);
  border-left-color: var(--dmsg-failed-border);
}
.dmsg[data-status="failed"] .dmsg-content { opacity: var(--dmsg-pending-opacity); }
/* Ephemeral flash when scrolling to a message (e.g., clicking a reply context).
 * Reuses the per-theme `highlightMessage` keyframes (themes/<name>/dark.css).
 * No `forwards` fill mode — without it, animation reverts to base CSS at end,
 * letting `[data-pinged="true"]` etc. backgrounds reassert immediately after
 * the flash instead of being stuck transparent until the data-jumped attribute
 * is removed. */
.dmsg[data-jumped="true"] {
  animation: highlightMessage 1.5s ease-out;
}

/* Gutter: avatar (first) + hover-time (continuation, absolutely positioned). */
.dmsg-gutter {
  position: relative;       /* anchor for .dmsg-time-hover */
  width: var(--dmsg-gutter-width);
  padding-right: 16px;
  box-sizing: border-box;
  display: flex;
  align-items: flex-start;
}

.dmsg-avatar {
  width: var(--dmsg-avatar-size) !important;
  height: var(--dmsg-avatar-size) !important;
  min-width: var(--dmsg-avatar-size) !important;
  min-height: var(--dmsg-avatar-size) !important;
  border-radius: 50%;
  margin-top: 2px !important;
  cursor: pointer;
  object-fit: cover;
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
}

/* Absolute so showing/hiding it does NOT push the row's height.
 * Line-height matches the body content's first line (15px × 1.4 = 21px) so
 * the time vertically aligns with the message text. Right edge stops at the
 * gutter's padding-right (16px), so the centered text sits directly under
 * the avatar column. */
.dmsg-time-hover {
  position: absolute;
  top: 0;
  left: 0;
  right: 16px;
  font-size: 10px;
  line-height: 21px;
  color: var(--dmsg-time-color);
  text-align: center;
  white-space: nowrap;
  user-select: none;
  -webkit-user-select: none;
  pointer-events: none;
  visibility: hidden;
}

/* Streak continuation: hide avatar+header; reveal hover-time only on row hover */
.dmsg[data-streak="continuation"] .dmsg-avatar,
.dmsg[data-streak="continuation"] .dmsg-header { display: none; }
.dmsg[data-streak="continuation"]:hover .dmsg-time-hover { visibility: visible; }

/* ---- Self-Destruct Timer -------------------------------------------------
   A subtle accent clock on messages carrying a NIP-40 expiry, and the "derez"
   dissolve played when one hits zero. Lives in .dmsg-content so it shows on
   every row (the header/avatar collapse on streak continuations). */
.dmsg-selfdestruct {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  white-space: nowrap;
  margin-left: 6px;
  vertical-align: middle;
  opacity: 0.4;
  color: var(--accent-color, #59fcb3);
  cursor: default;
  transition: opacity 0.15s ease;
}
.dmsg-selfdestruct:hover { opacity: 1; }
.dmsg-selfdestruct svg { display: block; }
.dmsg-selfdestruct-time {
  font-size: 0.82em;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

/* "Temporary send" badge: a small accent clock at the top-right corner of the
   Send / voice button while the chat has an active Self-Destruct Timer. Drawn
   as an inline SVG (not an .icon) so the composer's icon-sizing rules can't
   inflate it, and the button is made position:relative to anchor it. */
.self-destruct-badge {
  position: absolute;
  top: 7px;
  right: 6px;
  width: 11px;
  height: 11px;
  color: var(--accent-color, #59fcb3);
  line-height: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease-out;
  filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.7));
  z-index: 3;
}
.self-destruct-badge.is-visible { opacity: 1; }
.self-destruct-badge svg { display: block; }

/* Derez: a short Tron-style dissolve, accent-lit. */
/* Self-destruct dissolve is a JS-driven squash + fade (max-height + opacity in
   _derezRowDom) — renders identically on WebKit and Chromium, no keyframes. */

/* Body: header + content + reactions */
.dmsg-body {
  min-width: 0;
  max-width: 100%;
  padding-right: 12px;  /* breathing room between content and the row's right edge / highlight bg */
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
}

.dmsg-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: -2px;
  flex-wrap: wrap;
}

.dmsg-author {
  font-weight: 700;
  font-size: 15px;
  color: var(--dmsg-author-color);
  cursor: pointer;
  line-height: 1.3;
  user-select: none;
  -webkit-user-select: none;
  position: relative;
  top: 1px;
}
.dmsg-author:hover { text-decoration: underline; }

/* Bot indicator next to the author name. Same shape + accent tint as
 * the chatlist version, sized to sit alongside the bold name. Reset
 * the base `.icon` margin:auto so flex doesn't stretch it. */
.dmsg-author-bot-icon {
  position: relative;
  width: 14px;
  height: 14px;
  margin: 0;
  flex-shrink: 0;
  display: inline-block;
  background-color: var(--icon-color-primary, #59fcb3);
}

.dmsg-author-badge {
  display: inline-block;
  font-size: 9px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding: 2px 5px;
  border-radius: 3px;
  line-height: 1;
  vertical-align: center;
}
.dmsg-author-badge.admin {
  background: var(--icon-color-primary, #59fcb3);
  color: #000;
}
.dmsg-author-badge.owner {
  background: #fce459;
  color: #000;
}

.dmsg-time {
  font-size: 12px;
  color: var(--dmsg-time-color);
  user-select: none;
  -webkit-user-select: none;
}

/* Content slot */
.dmsg-content {
  color: var(--dmsg-text-color);
  line-height: 1.4;
  font-size: 15px;
  word-wrap: break-word;
  overflow-wrap: anywhere;
}

.dmsg-text {
  display: inline;
}
.dmsg-text p:first-child { margin-top: 0; }
.dmsg-text p:last-child  { margin-bottom: 0; }
.dmsg-text a,
.pins-drawer-row-text a  { color: var(--icon-color-primary, #59fcb3); }
/* .dmsg-text is inline, so a divider-only message gives the <hr> a zero-width containing block
   and it disappears. Pin a visible block width (expands with surrounding text when present). */
.dmsg-text hr            { display: block; width: 100%; min-width: 12rem; }
/* Unavailable custom emoji shown inline as a tappable :shortcode: chip (tap explains why). */
.emoji-unavailable       { cursor: pointer; opacity: 0.6; text-decoration: underline dotted; text-underline-offset: 2px; }
/* Editor only: a failed emoji (oversized / unavailable) stays visible (⚠) so its creator can remove it. */
.emoji-creator-cell-broken { position: relative; }
.emoji-creator-cell-broken img { visibility: hidden; }
.emoji-creator-cell-broken::after { content: '\26A0'; position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 1.2em; color: #f0a500; pointer-events: none; }

/* ===== Profile badge: 3D holographic achievement card ===== */
.badge-card-overlay {
  position: fixed; inset: 0; z-index: 4000;
  display: none; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, 0.72);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  opacity: 0; transition: opacity 0.22s ease;
}
.badge-card-overlay.is-visible { opacity: 1; }
.badge-card-scene {
  perspective: 900px;
  transform: scale(0.86) translateY(10px);
  opacity: 0;
  transition: transform 0.42s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.3s ease;
}
.badge-card-overlay.is-visible .badge-card-scene { transform: scale(1) translateY(0); opacity: 1; }
.badge-card {
  --rx: 0deg; --ry: 0deg; --mx: 50%; --my: 50%; --holo: 0;
  position: relative;
  width: min(78vw, 300px);
  padding: 36px 28px 28px;
  border-radius: 22px;
  background: linear-gradient(160deg, #171717, #16181a);
  border: 1px solid #393B3C;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.6);
  transform-style: preserve-3d;
  transform: rotateX(var(--rx)) rotateY(var(--ry));
  transition: transform 0.1s ease-out;
  text-align: center;
  touch-action: none;   /* a finger drag tilts the card instead of scrolling the page */
}
.badge-card.badge-card--idle { transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1); }
.badge-card-holo, .badge-card-glare { position: absolute; inset: 0; border-radius: inherit; pointer-events: none; }
/* White light sheen: a soft specular streak that sweeps with the tilt, like light catching a glossy
   surface. Blooms (via --holo) only while interacting. */
.badge-card-holo {
  background: linear-gradient(110deg,
    transparent 32%, rgba(255,255,255,0.08) 45%, rgba(255,255,255,0.38) 50%, rgba(255,255,255,0.08) 55%, transparent 68%);
  background-size: 250% 250%;
  background-position: var(--mx) var(--my);
  mix-blend-mode: screen;
  opacity: calc(0.42 * var(--holo));
  transition: opacity 0.3s ease;
}
.badge-card-glare {
  background: radial-gradient(circle at center, rgba(255,255,255,0.32), rgba(255,255,255,0.04) 35%, transparent 60%);
  background-size: 200% 200%;
  background-position: var(--mx) var(--my);
  mix-blend-mode: soft-light;
  opacity: calc(0.5 * var(--holo));
  transition: opacity 0.25s ease;
}
/* On release, ease the sheen position + fade in sync with the card's 0.6s spring-back, so it retracts
   smoothly toward center instead of snapping there and flashing. */
.badge-card--idle .badge-card-holo,
.badge-card--idle .badge-card-glare {
  transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.6s ease;
}
.badge-card-badge {
  width: 132px; height: 132px; object-fit: contain;
  display: block; margin: 0 auto 18px;
  transform: translateZ(46px);
  filter: drop-shadow(0 10px 18px rgba(0, 0, 0, 0.45));
  pointer-events: none; user-select: none;
}
.badge-card-title { transform: translateZ(26px); font-size: 20px; font-weight: 700; color: #fff; margin-bottom: 10px; }
.badge-card-desc  { transform: translateZ(14px); font-size: 14px; line-height: 1.5; color: #b2b2b2; }
.badge-card-perks { transform: translateZ(10px); margin-top: 18px; padding-top: 15px; border-top: 1px solid rgba(255,255,255,0.08); text-align: left; }
.badge-card-perks-label { font-size: 11px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase; color: var(--accent-color, #59fcb3); opacity: 0.85; margin-bottom: 9px; }
.badge-card-perk { display: flex; align-items: baseline; gap: 8px; font-size: 13.5px; color: #d8d8d8; margin-bottom: 7px; }
.badge-card-perk:last-child { margin-bottom: 0; }
.badge-card-perk::before { content: '\2713'; color: var(--accent-color, #59fcb3); font-weight: 700; flex-shrink: 0; }
.badge-card-perk-sub { color: #777; font-size: 12px; margin-left: auto; padding-left: 8px; white-space: nowrap; }
/* Tiered "Bug Hunter" extras: muted subtitle, the progress rail, and the access label. */
.badge-card-subtitle { transform: translateZ(22px); font-size: 13px; letter-spacing: 0.06em; color: #8a8a8a; margin: -4px 0 12px; }
.badge-card-tiers { transform: translateZ(12px); display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 18px; }
/* Default node = not-yet-held: dimmed + desaturated. .is-filled restores full colour + accent ring. */
.badge-card-tier-node { position: relative; width: 40px; height: 40px; flex-shrink: 0; opacity: 0.35; filter: grayscale(1); transition: opacity 0.2s ease, filter 0.2s ease; }
.badge-card-tier-node img { width: 100%; height: 100%; object-fit: contain; display: block; pointer-events: none; user-select: none; }
.badge-card-tier-node.is-filled { opacity: 1; filter: none; }
.badge-card-tier-conn { width: 20px; height: 3px; border-radius: 2px; flex-shrink: 0; background: rgba(255, 255, 255, 0.15); }
.badge-card-tier-conn.is-filled { background: var(--accent-color, #59fcb3); }
.badge-card-access { transform: translateZ(14px); display: flex; align-items: center; justify-content: center; gap: 7px; margin-top: 20px; color: var(--accent-color, #59fcb3); font-size: 15px; font-weight: 500; }
.badge-card-access-icon { width: 16px; height: 16px; flex-shrink: 0; }
.badge-card-access-text { line-height: 1; }
@media (prefers-reduced-motion: reduce) {
  .badge-card-scene, .badge-card { transform: none; }
  .badge-card-scene { transition: opacity 0.3s ease; }
  .badge-card-holo, .badge-card-glare { opacity: 0; }
}

/* Markdown headers in chat: browser defaults (0.67em–2.33em) leave huge
 * gaps between consecutive headers. Tighten to chat-appropriate spacing
 * and let line-height stay close to the header for a denser feel. */
.dmsg-text h1, .dmsg-text h2, .dmsg-text h3,
.dmsg-text h4, .dmsg-text h5, .dmsg-text h6 {
  margin: 0.4em 0 0.15em;
  line-height: 1.2;
}
.dmsg-text > h1:first-child, .dmsg-text > h2:first-child,
.dmsg-text > h3:first-child, .dmsg-text > h4:first-child,
.dmsg-text > h5:first-child, .dmsg-text > h6:first-child { margin-top: 0; }
.dmsg-text > h1:last-child, .dmsg-text > h2:last-child,
.dmsg-text > h3:last-child, .dmsg-text > h4:last-child,
.dmsg-text > h5:last-child, .dmsg-text > h6:last-child { margin-bottom: 0; }

/* Inline emoji baseline (header authors, message text, reply previews, etc.).
 * em units so emojis scale with the surrounding font (markdown h1/h2/h3 etc).
 * Without an explicit cap, twemoji SVGs render at their natural intrinsic size. */
.dmsg .emoji {
  width: 1.15em;
  height: 1.15em;
  vertical-align: -0.2em;
  display: inline-block;
}

/* Emoji-only: keep larger glyphs but smaller than legacy 75px (locked at 50px).
 * inline-block (rather than block) so the inline `Sent ✓` indicator can sit
 * immediately to the right of the emoji rather than stretching far across to
 * the body edge. Higher specificity + !important on .emoji wins over baseline. */
.dmsg-text.dmsg-emoji-only          { display: inline-block; padding: 4px 0; vertical-align: bottom; }
/* Custom emojis share the `emoji` class, so this sizes both. Base
   `.custom-emoji-inline { margin: 0 }` keeps chained multi-cell artwork
   stitching pixel-perfectly. */
.dmsg-text.dmsg-emoji-only .emoji   { width: 50px !important; height: 50px !important; vertical-align: middle; }
/* Sent indicator next to an emoji-only message — anchor to the emoji's floor
 * (line box bottom) rather than text baseline so it visually rests at the
 * bottom of the big glyph instead of floating up near its middle. */
.dmsg-text.dmsg-emoji-only + .dmsg-status { vertical-align: bottom; padding-bottom: 4px; }

/* Attachments — wrapper just provides vertical rhythm. Inner elements style themselves. */
.dmsg-attachments {
  display: block;
  margin-top: 4px;
}
.dmsg-attachments > * { margin-top: 4px; max-width: 100%; }
.dmsg-attachments > *:first-child { margin-top: 0; }
/* Raw image attachments + video previews: fit inside a 450×350 box,
 * preserving aspect ratio. Scoped to a dedicated class so unrelated
 * <img>s inside the attachment area (audio cover art, mini-app icons,
 * file thumbnails) keep their own sizing. width/height BOTH auto !important
 * so a tall portrait clamped by max-height drops its width to match. */
.dmsg-attachments img.dmsg-image-attachment,
.dmsg-attachments video {
  max-width: min(100%, 450px) !important;
  max-height: 350px !important;
  width: auto !important;
  height: auto !important;
}
/* Spoiler placeholders are a tiny base64 thumbhash sized up via HTML
 * width/height attributes plus aspect-ratio. Keep the box constraints
 * but DON'T force width: auto — that would collapse the placeholder
 * back to the thumbhash's intrinsic ~32px size. */
.dmsg-attachments img.spoiler-img {
  max-width: min(100%, 450px) !important;
  max-height: 350px !important;
  height: auto;
}

/* Reply context inside a row — left-bordered quote block.
 * user-select: none — the quote is a UI affordance, not real content; users
 * shouldn't accidentally select the truncated preview when trying to copy
 * the actual message body. */
.dmsg-reply {
  /* One-liner ABOVE the name (Discord-style): avatar + name + snippet on a single row, the snippet
   * ellipsis-truncated, with an elbow connector rising from the big avatar (see ::before). */
  position: relative;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 2px 0 2px 3px;
  margin-bottom: 4px;
  margin-top: 0;
  line-height: 1.3;
  font-size: 13px;
  background: transparent;
  max-width: 100%;
  user-select: none;
  -webkit-user-select: none;
  transition: opacity 0.125s ease;
}
/* Elbow connector: curves up from the big avatar's centre (in the gutter) and points into the reply.
 * Geometry is derived from the gutter/avatar tokens so it tracks any retune of those. */
.dmsg-reply::before {
  content: "";
  position: absolute;
  /* The avatar's centre column, minus half the 2px stroke so the visible line centres on the avatar
   * rather than sitting a pixel to its right. */
  left: calc(var(--dmsg-avatar-size) / 2 - var(--dmsg-gutter-width) - 1px);
  /* Arm sits on the reply content's vertical centre (-2px trims the border thickness + line-box lead
   * so it reads as centred, not slightly low). */
  top: calc(50% - 2px);
  /* Stop a few px short of BOTH avatars (the reply avatar on the right, the big one below) so the
   * connector floats near them instead of touching. Height compensates the -2px top so the bottom
   * gap is unchanged. */
  width: calc(var(--dmsg-gutter-width) - var(--dmsg-avatar-size) / 2 - 2px);
  height: calc(var(--dmsg-reply-lift) - 12px);
  border-left: 2px solid var(--dmsg-reply-spine);
  border-top: 2px solid var(--dmsg-reply-spine);
  border-top-left-radius: 8px;
  pointer-events: none;
}
/* Shift the big avatar down so it aligns with the name, not the reply line sitting above it, and
 * keep it above the elbow connector so the line tucks behind it. */
.dmsg--has-reply .dmsg-avatar {
  margin-top: calc(2px + var(--dmsg-reply-lift)) !important;
  position: relative;
  z-index: 1;
}
.dmsg-reply-avatar {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  object-fit: cover;
  /* Above the elbow connector so it's never painted over. */
  position: relative;
  z-index: 1;
}
.dmsg-reply-name {
  flex-shrink: 0;
  font-weight: 500;
  white-space: nowrap;
}
/* Underline on hover like the primary author name — only when it's clickable (has a resolved npub,
 * which also gates the mini-profile click). */
.dmsg-reply-name[data-npub]:hover { text-decoration: underline; cursor: pointer; }
/* The quoted content: takes the remaining width and ellipsis-truncates on one line. */
.dmsg-reply-snippet {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Link preview card. */
.dmsg-preview {
  display: block;
  margin-top: 8px;
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-left-color: var(--icon-color-primary, #59fcb3);  /* uniform 1px, themed left only */
  background: rgba(0, 0, 0, 0.3);
  max-width: 480px;
}
.dmsg-preview:hover { opacity: 1; }              /* override global .btn:hover dim */
.dmsg-attachments video:hover { opacity: 1; }    /* videos shouldn't dim on hover either */
.dmsg-preview span {
  padding: 0;
  font-weight: 400;
  display: block;
}
.dmsg-preview .favicon {
  height: 15px;
  width: auto;
  margin-right: 8px;
  position: relative;
  top: 1px;
}
.dmsg-preview-img,
.dmsg-preview img:not(.favicon):not(.emoji) {
  width: 100%;
  height: auto;
  max-height: 350px;
  object-fit: cover;
  border-radius: 4px;
  margin-top: 6px;
}
.dmsg-preview-description {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-top: 4px;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.7);
  background: transparent;
  padding: 0;
  border-radius: 0;
  line-height: 1.4;
}

/* Edited indicator */
.dmsg-edited {
  font-size: 10px;
  color: var(--dmsg-time-color);
  margin-left: 6px;
  vertical-align: baseline;
  user-select: none;
}
.dmsg-edited.btn:hover { color: rgba(255, 255, 255, 0.7); cursor: pointer; }

/* Status indicator (own messages only).
 * Default: inline next to the message text (Telegram-style). For messages with
 * a block-level antecedent (attachment, link preview, npub preview, payment),
 * the status flips to its own line below them, right-aligned. */
.dmsg-status {
  display: none;
  align-items: center;
  gap: 4px;
  margin-left: 6px;
  font-size: 11px;
  color: #6a6a6a;
  user-select: none;
  -webkit-user-select: none;
  vertical-align: baseline;
}
.dmsg-status .icon {
  display: inline-block;
  width: 12px;
  height: 12px;
  background-color: #6a6a6a;
  vertical-align: middle;
  position: static;
}
.dmsg-status-failed { color: var(--danger-color); }
.dmsg-status-failed .icon { background-color: var(--danger-color); }
.dmsg-status-hidden { display: none !important; }  /* beat the block-right sibling rules above */
.dmsg-failed-action {
  cursor: pointer;
  text-decoration: underline;
  opacity: 0.85;
  pointer-events: auto;
}
.dmsg-failed-action:hover { opacity: 1; }

/* Reactions row */
.dmsg-reactions {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 4px;
  margin-bottom: 4px;
  user-select: none;
  -webkit-user-select: none;
}
.dmsg-reactions .reaction {
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 6px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.08);
  font-size: 12px;
  line-height: 1;
  opacity: 0.9;
  transition: opacity 0.12s ease, background-color 0.12s ease;
  /* Backstop for a malformed reaction glyph (the JS cap is the primary guard):
     never let one chip stretch the row or run off-screen. */
  max-width: 220px;
  overflow: hidden;
  white-space: nowrap;
}
.dmsg-reactions .reaction:hover { opacity: 1; }
/* Neutral chips (we haven't reacted) lift their fill on hover. */
.dmsg-reactions .reaction:not([data-reacted="true"]):hover {
  background: rgba(255, 255, 255, 0.1);
}
.dmsg-reactions .reaction[data-reacted="true"] {
  background: color-mix(in srgb, var(--icon-color-primary, #59fcb3) 18%, transparent);
  border-color: var(--icon-color-primary, #59fcb3);
  cursor: default;
}
.dmsg-reactions .reaction .emoji {
  width: 16px !important;
  height: 16px !important;
  margin: 0 !important;
  vertical-align: middle;
}
/* Rolling reaction counter — the count lives in a clipped box so a change can
   slide the old number out (up on +, down on −) while the new slides in.
   Tabular digits + a one-digit floor: every single-digit count occupies the
   exact same box, so 1→2 never resizes the chip or rewraps the row. */
.dmsg-reactions .reaction-count {
  position: relative;
  display: inline-block;
  overflow: hidden;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  min-width: 1ch;
  text-align: center;
}
.dmsg-reactions .rc-value {
  display: block;
  line-height: 1;
  will-change: transform;
}
.dmsg-reactions .rc-old {
  position: absolute;
  top: 0;
  left: 0;
}
/* New chip pops in on arrival (reconcile / optimistic decoy). Ends at the
   chip's resting opacity so there's no settle flash. */
@keyframes reaction-pop-in {
  from { transform: scale(0.6); opacity: 0; }
  to   { transform: scale(1); opacity: 0.9; }
}
.dmsg-reactions .reaction.reaction-enter {
  animation: reaction-pop-in 0.18s cubic-bezier(0.22, 1, 0.36, 1);
}
@media (prefers-reduced-motion: reduce) {
  .dmsg-reactions .reaction.reaction-enter { animation: none; }
}
/* Inline "add reaction" shortcut chip (Discord-style + at end of reactions row). */
.dmsg-reactions-add {
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 3px 6px;
  border-radius: 6px;
  background-color: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  transition: background-color 0.12s ease, border-color 0.12s ease;
  line-height: 1;
}
.dmsg-reactions-add:hover {
  background-color: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.15);
}
.dmsg-reactions-add .icon {
  width: 14px;
  height: 14px;
  display: inline-block;
  position: static;
  background-color: rgba(255, 255, 255, 0.55);
  transition: background-color 0.12s ease;
}
.dmsg-reactions-add:hover .icon {
  background-color: rgba(255, 255, 255, 0.9);
}

/* Mention pills inside a dmsg — preserve existing mention chip styling. */
.dmsg-text .mention { background: rgba(255, 255, 255, 0.08); }

/* Anchor for #dmsg-toolbar absolute positioning so it scrolls with content. */
.chat-messages { position: relative; }

/* ============================================================================
   Per-DM Wallpaper
   ============================================================================
   The wallpaper is a dedicated layer behind the rest of the chat surface, so
   the `filter: blur()` / `filter: brightness()` only affect the image — not
   the message bubbles, header text, or the composer.

   The wallpaper layer is absolute-inset:0 against `#chat`, sized to the
   visible chat panel rather than the scrollable content (which was the bug
   in the previous implementation: a `::before` on `.chat-messages` only
   covered the top of scrolled content).

   The image is set via the `--wp-image` CSS variable, and the blur +
   brightness `filter` is written inline by JS as the user moves the preview
   sliders or when an inbound rumor lands. (The filter is built in JS rather
   than from vars because `blur(0px)` clashes with brightness() in WebKit.)
*/
/* `isolation: isolate` makes #chat a stacking context root so the
   wallpaper layer's negative z-index is contained — it paints behind
   every other child without us having to add z-index rules to (and
   accidentally clobber the `position:` of) the header, composer, and
   the floating scroll-return button. */
#chat { position: relative; isolation: isolate; }

/* Floating "jump to unread" pill — top-center of the chat view, below the header. */
.unread-jump-pill {
  position: absolute;
  top: 75px;
  left: 50%;
  transform: translateX(-50%) translateY(-8px);
  z-index: 6;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 7px 15px;
  border: none;
  border-radius: 100px;
  background: var(--accent-color, #59fcb3);
  color: #0c0c0c;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.5), 0 2px 5px rgba(0, 0, 0, 0.4);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.unread-jump-pill.visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
  pointer-events: auto;
}
.unread-jump-pill.loading { cursor: default; pointer-events: none; }
.unread-jump-pill.loading::before {
  content: '';
  display: inline-block;
  width: 11px;
  height: 11px;
  margin-right: 7px;
  border: 2px solid rgba(12, 12, 12, 0.3);
  border-top-color: #0c0c0c;
  border-radius: 50%;
  animation: unread-pill-spin 0.7s linear infinite;
}
@keyframes unread-pill-spin { to { transform: rotate(360deg); } }

.chat-wallpaper-layer {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image: var(--wp-image, none);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  background-color: transparent;
  /* `filter` (blur + brightness) is set inline by JS — see applyChatWallpaper. */
  /* Blur leaks past the image edge by default; clip + scale slightly so the
     softened edge stays inside the visible chat area. */
  overflow: hidden;
  transform: scale(1.02);
  transform-origin: center;
}
#chat[data-wallpaper="true"] .chat-header {
  background-color: rgba(22, 22, 22, 0.9);
}
/* Override theme-defined chat-messages backgrounds so the wallpaper layer
   actually shows through. Themes (cyberpunk/monero/pivx) paint .chat-messages
   solid + add their own ::before overlay; both compete with our wallpaper. */
#chat[data-wallpaper="true"] .chat-messages {
  background-color: transparent;
  border-top: none;
}
#chat[data-wallpaper="true"] .chat-messages::before {
  display: none;
}

/* Scroll-to-bottom button collides with the wallpaper Set/Cancel buttons —
   suppress it while the preview bar is up. */
#chat[data-wallpaper-previewing="true"] #chat-scroll-return {
  display: none;
}

.wallpaper-preview-bar {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  padding: 14px 8px;
  /* Solid dark surface with a subtle grey border curved over the top,
     lifted by a soft shadow. Flush at the bottom where it meets the
     composer. */
  background: #171717;
  border: 1px solid #474747;
  border-bottom: none;
  border-radius: 12px 12px 0 0;
  box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  animation: popupFadeIn 0.18s ease-out;
}
.wallpaper-preview-sliders {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
  padding-left: 12px;
  padding-right: 12px;
}
.wallpaper-slider {
  display: flex;
  align-items: center;
  gap: 12px;
  color: #f7f4f4;
  font-size: 12px;
}
/* Blur / brightness glyph at the head of each slider row. The base `.icon`
   is absolute-fill; pin it to a fixed inline box instead. */
.wallpaper-slider-icon {
  position: relative !important;
  inset: auto !important;
  flex: 0 0 20px;
  width: 20px;
  height: 20px;
  margin: 0;
  background-color: rgba(255, 255, 255, 0.72);
}
/* Slider styled to match the voice-message preview seek bar: a thick rounded
   track with a gradient fill (driven by the JS-set --slider-pct so fill and
   thumb stay aligned) and a plain white circular handle. */
.wallpaper-slider input[type="range"] {
  flex: 1 1 auto;
  min-width: 0;
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  height: 16px;
  margin: 0;
  padding: 0;
  cursor: pointer;
  outline: none;
}
.wallpaper-slider input[type="range"]::-webkit-slider-runnable-track {
  height: 10px;
  border-radius: 5px;
  background: linear-gradient(
    to right,
    var(--primary-color) 0%,
    var(--primary-color) var(--slider-pct, 0%),
    rgba(255, 255, 255, 0.08) var(--slider-pct, 0%),
    rgba(255, 255, 255, 0.08) 100%
  );
}
.wallpaper-slider input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  width: 14px;
  height: 14px;
  margin-top: -2px; /* center on the 10px track: (14 - 10) / 2 = 2 */
  border-radius: 50%;
  background: #ffffff;
  border: none;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
  cursor: pointer;
}
.wallpaper-slider input[type="range"]::-moz-range-track {
  height: 10px;
  border-radius: 5px;
  background: linear-gradient(
    to right,
    var(--icon-color-primary, #59fcb3) 0%,
    var(--voice-progress-secondary, rgba(89, 252, 179, 0.4)) var(--slider-pct, 0%),
    rgba(255, 255, 255, 0.08) var(--slider-pct, 0%),
    rgba(255, 255, 255, 0.08) 100%
  );
  border: none;
}
.wallpaper-slider input[type="range"]::-moz-range-thumb {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #ffffff;
  border: none;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
  cursor: pointer;
}


/* Floating hover toolbar (singleton inside .chat-messages, position: absolute
 * so it scrolls naturally with the messages — no per-frame JS reposition). */
#dmsg-toolbar {
  position: absolute;
  z-index: 50;
  display: flex;
  gap: 2px;
  padding: 4px;
  background: var(--dmsg-toolbar-bg, #1a1a1a);
  border: 1px solid var(--dmsg-toolbar-border, rgba(255, 255, 255, 0.08));
  border-radius: 8px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
  user-select: none;
}
#dmsg-toolbar[hidden] { display: none; }
.dmsg-toolbar-btn {
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: 4px;
  cursor: pointer;
  padding: 0;
  transition: background-color 0.1s ease;
}
.dmsg-toolbar-btn:hover {
  background: rgba(255, 255, 255, 0.08);
}
.dmsg-toolbar-btn[hidden] { display: none; }
.dmsg-toolbar-btn .icon {
  width: 20px;
  height: 20px;
  display: inline-block;
  position: static;
  background-color: rgba(255, 255, 255, 0.7);
}
.dmsg-toolbar-btn:hover .icon {
  background-color: #fff;
}
/* Danger variant — paints the icon red so the Delete action reads
   distinctly from the additive ones (react/reply/edit) without
   tinting the whole button. */
.dmsg-toolbar-btn-danger .icon {
  background-color: var(--danger-color);
}
.dmsg-toolbar-btn-danger:hover .icon {
  background-color: var(--danger-color);
  filter: brightness(1.15);
}

/* ============================================================================
   Multi-Account: My Profile dropdown + pre-login picker
   ============================================================================
   Two surfaces share the same row template:
   - In-app: full-feature dropdown with Add Profile + edit-mode trash icons
   - Pre-login: read-only list above the PIN/password area
   Visual language stays consistent; features are layered on via classes.
   ============================================================================ */

/* The "My Profile ⌄" header trigger inside the Profile tab */
.my-profile-switcher {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  padding: 4px 8px;
  border-radius: 6px;
  transition: background-color 120ms ease;
}
.my-profile-switcher:hover {
  background-color: rgba(255, 255, 255, 0.05);
}
.my-profile-switcher h3 {
  margin: 0;
  font-size: 18px;
  color: #f7f4f4;
}
.my-profile-switcher-chevron {
  color: rgba(255, 255, 255, 0.7);
  transition: transform 160ms ease;
  flex-shrink: 0;
}
.my-profile-switcher.open .my-profile-switcher-chevron {
  transform: rotate(180deg);
}

/* Backdrop dim + blur layer. Positioned below the Profile header so the
   header stays un-darkened and reads as a single visual unit with the
   dropdown panel. Blur tucks the underlying content into a soft frosted
   plane behind the panel. */
.profile-switcher-backdrop {
  position: fixed;
  top: calc(60px + var(--chrome-h));
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 160ms ease;
  z-index: 998;
}
.profile-switcher-backdrop.visible {
  opacity: 1;
  pointer-events: auto;
}

/* The dropdown panel — flush extension of the Profile tab header. Caps at
   420px on wide windows and centers; on narrow windows it shrinks with a
   small horizontal inset so the rounded bottom corners stay visible
   instead of bleeding off the viewport edge. */
.profile-switcher-panel {
  position: fixed;
  top: calc(60px + var(--chrome-h));
  left: 0;
  right: 0;
  margin-left: auto;
  margin-right: auto;
  max-width: 420px;
  box-sizing: border-box;
  background: #161616;
  border-radius: 0 0 12px 12px;
  border-left: 1px solid rgba(255, 255, 255, 0.08);
  border-right: 1px solid rgba(255, 255, 255, 0.08);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow: 0 12px 24px rgba(0, 0, 0, 0.35);
  z-index: 999;
  display: none;
  flex-direction: column;
  overflow: hidden;
  max-height: calc(100vh - 80px);
}
.profile-switcher-panel.open {
  display: flex;
  animation: profileSwitcherIn 140ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* Account list scrolls; header + Add Profile footer stay pinned. Without
   this rule, the panel's `overflow: hidden` clips both the bottom rows
   of the list AND the Add Profile button when many accounts are listed
   on a small viewport (landscape mobile / power users with 6+ accounts).
   The footer being clipped means the user has no path to add more
   accounts. */
.profile-switcher-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.profile-switcher-header,
.profile-switcher-add {
  flex-shrink: 0;
}

@keyframes profileSwitcherIn {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Header row inside the dropdown: 👥 Profiles label centered + 🗑 trash
   pinned right via absolute positioning so the label is truly centered
   regardless of trash button width. */
.profile-switcher-header {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 14px 18px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
  background: rgba(255, 255, 255, 0.02);
}

.profile-switcher-header-label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.55);
  letter-spacing: 0.4px;
}

.profile-switcher-header-icon {
  color: rgba(255, 255, 255, 0.55);
  flex-shrink: 0;
}

.profile-switcher-trash-toggle {
  position: absolute;
  top: 50%;
  left: 10px;
  transform: translateY(-50%);
  background: transparent;
  border: none;
  cursor: pointer;
  padding: 5px;
  border-radius: 4px;
  transition: color 120ms ease;
  color: rgba(255, 255, 255, 0.45);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

#profile-switcher-trash-toggle {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  background: transparent !important;
  border: none;
}

.profile-switcher-trash-toggle:hover {
  background-color: transparent;
  color: rgba(255, 255, 255, 0.75);
}

.profile-switcher-trash-toggle svg {
  transition: color 160ms ease;
  width: 24px;
  height: 24px;
}

body.profile-switcher-editing .profile-switcher-trash-toggle {
  color: var(--danger-color);
}

/* Each account row */
.profile-switcher-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 6px 18px;
  cursor: pointer;
  transition: background-color 100ms ease;
  position: relative;
  border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}
.profile-switcher-row:last-child {
  border-bottom: none;
}
.profile-switcher-row:hover {
  background-color: rgba(255, 255, 255, 0.04);
}
.profile-switcher-row.active {
  cursor: default;
  background-color: transparent !important;
}
.profile-switcher-row.active:hover {
  background-color: transparent !important;
}

/* Active-account dot (left edge) */
.profile-switcher-active-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent-color);
  flex-shrink: 0;
  visibility: hidden;
}
.profile-switcher-row.active .profile-switcher-active-dot {
  visibility: visible;
}

/* Avatar */
.profile-switcher-avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  object-fit: cover;
  background: transparent;
  flex-shrink: 0;
}

/* Name + truncated npub */
.profile-switcher-meta {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex: 1;
  min-width: 0;
}
.profile-switcher-name {
  color: #f7f4f4;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex-shrink: 0;
}
.profile-switcher-npub {
  font-family: ui-monospace, SFMono-Regular, monospace;
  font-size: 11px;
  color: rgba(255, 255, 255, 0.4);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
  min-width: 0;
}

/* Per-row trash (visible only in edit mode) */
.profile-switcher-row-trash {
  display: none;
  background: transparent;
  border: none;
  cursor: pointer;
  padding: 6px;
  border-radius: 4px;
  flex-shrink: 0;
  color: var(--danger-color);
  align-items: center;
  justify-content: center;
}
.profile-switcher-row-trash:hover {
  transform: scale(1.25);
  opacity: 1;
}
body.profile-switcher-editing .profile-switcher-row-trash {
  display: inline-flex;
}
/* Hide chevron / row interactivity transition while editing the active row */
body.profile-switcher-editing .profile-switcher-row.active {
  opacity: 1;
}

/* Add Profile footer */
.profile-switcher-add {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 10px;
  cursor: pointer;
  color: var(--accent-color);
  font-weight: 500;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  background: rgba(255, 255, 255, 0.02);
  transition: background-color 120ms ease;
}
.profile-switcher-add:hover {
  background-color: rgba(255, 255, 255, 0.04);
}
.profile-switcher-add svg {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
}
.profile-switcher-add.disabled {
  color: rgba(255, 255, 255, 0.35);
  cursor: not-allowed;
  pointer-events: none;
  background: transparent;
}
body.profile-switcher-editing .profile-switcher-add {
  display: none;
}

/* "Get Started" pill shown where the first chat row will live: a chat-row
   shaped nudge that opens the New Chat tab. Glow is a horizontal gradient
   only; the oversized right glyph clips on the pill's rounded bounds. */
.chatlist-get-started {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 15px 15px;
  /* 60 + the 1px borders = the same 62px box as a .chatlist-contact row. */
  height: 60px;
  padding: 0;
  border-radius: 999px;
  border: 1px solid rgba(89, 252, 179, 0.5);
  background: linear-gradient(90deg, #000 0%, #000 35%, rgba(89, 252, 179, 0.3) 100%);
  text-align: left;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
}
/* Avatar-sized circle with the same soft inset a chat row's avatar gets. */
.chatlist-get-started-badge {
  position: relative;
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  margin-left: 6px;
  border-radius: 50%;
  border: 1px solid rgb(25, 25, 25);
}
.chatlist-get-started-badge .icon {
  background-color: var(--accent-color, #59fcb3);
  transform: scale(0.85);
}
.chatlist-get-started-text {
  position: relative;
  z-index: 1;
}
.chatlist-get-started-text h4 {
  margin: 0;
  font-size: 16.5px;
  font-weight: 700;
  color: #f7f4f4;
}
.chatlist-get-started-text p {
  margin: 2px 0 0;
  font-size: 13.5px;
  line-height: 1.2;
  color: rgba(255, 255, 255, 0.8);
}
/* Decorative oversized glyph, sunk past the pill's top and right bounds. */
.chatlist-get-started-watermark {
  position: absolute;
  top: -16px;
  bottom: 0;
  right: -15px;
  width: 100px;
  height: 100px;
  pointer-events: none;
}
.chatlist-get-started-watermark .icon {
  background-color: var(--accent-color, #59fcb3);
}
/* Bottom-of-list welcome: Viktor beside a short nudge toward the Hub.
   Fixed above the navbar, same anchoring as the list's fadeout. */
.chatlist-intro {
  position: fixed;
  bottom: 82px;
  left: 18px;
  right: 18px;
  display: flex;
  align-items: center;
  gap: 14px;
  text-align: left;
  user-select: none;
  -webkit-user-select: none;
}
/* No `image-rendering: pixelated`: WebKit's animated-GIF path always scales
   smooth, so the still must scale smooth too or the edges shift ~1px at
   every idle/animation swap. */
.chatlist-intro-viktor {
  width: 62px;
  height: 62px;
  flex-shrink: 0;
  cursor: pointer;
  -webkit-user-drag: none;
}
.chatlist-intro-text h4 {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  color: #f7f4f4;
}
.chatlist-intro-text p {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.2;
  color: rgba(255, 255, 255, 0.55);
}
.chatlist-intro-link {
  color: var(--accent-color, #59fcb3);
  cursor: pointer;
}
.chatlist-intro-link:hover {
  text-decoration: underline;
}
/* Login screen — block all text selection and drag-grab gestures across
   every descendant. The login surface is interactive UI, not content;
   selection and drag would only get in the way (e.g. accidentally
   highlighting a name when tapping a picker row). Inputs are explicitly
   re-enabled below so PIN/password entry still works. */
#login-form,
#login-form * {
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
}
#login-form input,
#login-form textarea {
  user-select: text;
  -webkit-user-select: text;
}

/* ── Login: the start, unlock and sign-in steps ──────────────────────────────
   Laid out on the design's 1280×720 frame: the lockup and account pill on top,
   one centred step, the credits along the bottom, and the illustration behind.
   The grid's outer rows share the spare height, so a step sits at the window's
   centre however tall the window is, and never under the header or footer. */
#login-form {
  font-family: Rubik, sans-serif;
}
#chrome-lockup.login-hidden {
  visibility: hidden;
}
/* Over the login screens the strip is only its controls, on the same art as the rest. */
#window-chrome.login {
  background: transparent;
  border-bottom-color: transparent;
}

.lg-art {
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  transition: opacity 0.3s ease;
}
.lg-art.hidden { opacity: 0; }
.lg-art img {
  position: absolute;
  left: -9.35%;
  top: -15.08%;
  width: 116.79%;
  height: 116.79%;
  object-fit: cover;
  opacity: 0.2;
}
/* Fades the camera out towards the centre, where the steps sit. */
.lg-art::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to right, #030303 57.19%, rgba(3, 3, 3, 0) 91.56%);
}

/* Spans the window from its top edge, so centring matches the design's frame; only
   its children take pointer events, leaving the title strip's controls reachable. */
.lg {
  position: fixed;
  inset: 0;
  display: grid;
  grid-template-rows: minmax(max-content, 1fr) auto minmax(max-content, 1fr);
  justify-items: center;
  color: #fff;
  text-align: center;
  pointer-events: none;
}
.lg-head,
.lg-stage,
.lg-foot,
.lg-bg-toggle,
.login-account-list { pointer-events: auto; }

.lg-head {
  align-self: start;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 23px;
  margin-top: max(51px, calc(var(--chrome-h) + 16px));
}
.lg-lockup {
  display: block;
  width: min(429.72px, calc(100vw - 48px));
  height: auto;
}
.lg-account {
  position: relative;
  z-index: 970;   /* above the account list's backdrop */
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: calc(100vw - 40px);
  padding: 0;
  border: none;
  background: none;
  backdrop-filter: none;
  cursor: pointer;
}
.lg-account-name {
  min-width: 0;
  overflow: hidden;
  color: #fff;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 2px;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.lg-account-chevron {
  flex-shrink: 0;
  margin-left: 2px;
  transition: transform 0.16s ease;
}
.lg-account.open .lg-account-chevron { transform: rotate(180deg); }

.lg-stage {
  align-self: center;
  width: 100%;
  box-sizing: border-box;
  padding: 24px 16px;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.lg-block {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.lg-title {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-bottom: 25px;
}
.lg-title h3 {
  margin: 0;
  color: #fff;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 1.6px;
}
.lg-title.error h3 { color: var(--danger-color, #ff2ea9); }

.lg-buttons {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}
#login-start .lg-buttons { gap: 13px; }
.lg-btn {
  width: 250px;
  max-width: calc(100vw - 48px);
  height: 50px;
  margin: 0;
  padding: 0 16px;
  border: 1px solid #393b3c;
  border-radius: 7px;
  background: transparent;
  backdrop-filter: none;
  color: #fff;
  font-family: inherit;
  font-size: 20px;
  font-weight: 400;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease, filter 0.15s ease;
}
.lg-btn:hover { border-color: #5b6060; }
.lg-btn.primary {
  border-color: #33db98;
  background-color: #33db98;
  color: #030303;
}
.lg-btn.primary:hover { filter: brightness(1.08); }
.lg-btn.accent { border-color: #33db98; }
.lg-btn.accent:hover { background-color: rgba(51, 219, 152, 0.08); }
.lg-recommended {
  margin: -4px 0;
  color: #59fcb3;
  font-size: 10px;
}
.lg-bio {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 18px;
  font-size: 16px;
}

.lg-pins {
  display: flex;
  gap: 11.7px;
}
.lg-pins input {
  width: 40px;
  height: 40px;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  border: 2px solid #5b6060;
  border-radius: 7px;
  background: #030303;
  color: #fff;
  caret-color: #33db98;
  font-family: inherit;
  font-size: 20px;
  text-align: center;
  outline: none;
  box-shadow: none;
  transition: border-color 0.12s ease;
}
.lg-pins input:focus { border-color: #33db98; }

.lg-field-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
/* A wrong unlock: Aggroboi, glaring, takes the title's place until the next try. */
.lg-aggro {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-bottom: 25px;
  animation: lgAggroIn 0.32s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes lgAggroIn {
  from { opacity: 0; transform: scale(0.9); }
}
.lg-aggro-art {
  position: relative;
  width: 172px;
  height: 179px;
}
.lg-aggro-face {
  position: absolute;
  left: 0;
  bottom: 0;
}
/* The sign's stroke overhangs its 71×62 slot by 4px a side, as drawn. */
.lg-aggro-warn {
  position: absolute;
  right: -4px;
  top: -4px;
}
.lg-aggro-oops {
  margin: 14px 0 0;
  color: #ff2ea9;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 1.4px;
}
.lg-aggro-title {
  margin: 8px 0 0;
  color: #fff;
  font-size: 24px;
  font-weight: 700;
  letter-spacing: 2.4px;
}
/* Its outline faces, scattered faintly behind everything while it glares. */
.lg-aggro-bg {
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s ease;
}
.lg-aggro-bg.shown { opacity: 1; }
.lg-aggro-bg img { position: absolute; }
.lg-aggro-bg-1 { left: 49.4%; top: 66.9%; }
.lg-aggro-bg-2 { left: 2%; top: 12.2%; }
.lg-aggro-bg-3 { left: 85.7%; top: 44.5%; }
.lg-aggro-bg-4 { left: 5.1%; top: 82.3%; }

/* Keeps the field itself centred, with its side button hanging off to the right. */
.lg-field-row-offset { margin-left: 48px; }
.lg-field {
  display: flex;
  align-items: center;
  width: 306px;
  max-width: calc(100vw - 96px);
  height: 40px;
  box-sizing: border-box;
  border: 2px solid #33db98;
  border-radius: 7px;
  background: #030303;
}
.lg-input {
  flex: 1;
  min-width: 0;
  height: 100%;
  margin: 0;
  padding: 0 0 0 11px;
  border: none;
  background: none;
  color: #fff;
  font-family: inherit;
  font-size: 14px;
  outline: none;
  box-shadow: none;
}
.lg-input::placeholder { color: #b2b2b2; }
.lg-input[type="password"]:not(:placeholder-shown) { font-size: 20px; }
.lg-input:disabled { opacity: 0.5; }
.lg-field-go,
.lg-field-eye {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  margin: 0;
  border: none;
  background: none;
  backdrop-filter: none;
  cursor: pointer;
}
.lg-field-go { padding: 0 6px 0 4px; }
.lg-field-eye { padding: 0 6px; }
.lg-field-go:disabled { opacity: 0.4; cursor: default; }
.lg-field-eye .icon {
  position: relative;
  inset: auto;
  width: 22px;
  height: 18px;
  margin: 0;
  background-color: #959899;
}
.lg-side-btn {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  border: 2px solid #474747;
  border-radius: 7px;
  background: #030303;
  backdrop-filter: none;
  cursor: pointer;
  transition: border-color 0.12s ease;
}
.lg-side-btn:hover:not(:disabled) { border-color: #5b6060; }
.lg-side-btn:disabled { opacity: 0.5; cursor: default; }
.lg-side-btn.bare {
  border: none;
  background: none;
}
.lg-side-btn img { display: block; }

.lg-back {
  margin: 25px 0 0;
  color: #b2b2b2;
  font-size: 11px;
  letter-spacing: 1.1px;
}
.lg-back button,
.lg-link {
  padding: 0;
  border: none;
  background: none;
  backdrop-filter: none;
  color: #33db98;
  font-family: inherit;
  font-size: inherit;
  font-weight: 400;
  letter-spacing: inherit;
  cursor: pointer;
}
.lg-back button:hover,
.lg-link:hover:not(:disabled) { text-decoration: underline; }
.lg-link {
  margin-top: 14px;
  font-size: 11px;
  letter-spacing: 1.1px;
}
.lg-link:disabled { opacity: 0.5; cursor: default; }
.lg-status {
  max-width: 340px;
  margin: 12px 0 0;
  color: #b2b2b2;
  font-size: 12px;
}
.lg-status.error { color: var(--danger-color, #ff2ea9); }
.lg-status.connecting { color: #ffb84d; }
.lg-status.online { color: #59fcb3; }
.lg-note {
  max-width: 320px;
  margin: 0 0 16px;
  color: #b2b2b2;
  font-size: 13px;
  line-height: 1.5;
}
.lg-welcome-icon {
  position: relative;
  width: 64px;
  height: 64px;
  margin: 0 0 16px;
  background-color: #33db98;
}
.lg-welcome-kicker {
  margin: 0;
  color: #33db98;
  font-size: 20px;
  font-weight: 700;
}
.lg-welcome-title {
  margin: 0 0 12px;
  font-size: 28px;
  font-weight: 700;
}

.lg-foot {
  align-self: end;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 17px;
  margin-bottom: calc(25px + env(safe-area-inset-bottom, 0px));
}
.lg-credit {
  position: relative;
  width: 173.78px;
  height: 10.47px;
}
.lg-credit-text {
  display: block;
  width: 100%;
  height: 100%;
}
.lg-credit-mark {
  position: absolute;
  left: 77.7px;
  top: -5.6px;
  width: 13.16px;
  height: 20px;
}
.lg-links {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  color: #b2b2b2;
  font-size: 8px;
  letter-spacing: 0.8px;
}
.lg-links button {
  padding: 0;
  border: none;
  background: none;
  backdrop-filter: none;
  color: #33db98;
  font-family: inherit;
  font-size: inherit;
  font-weight: 400;
  letter-spacing: inherit;
  cursor: pointer;
}
.lg-links button:hover { color: #59fcb3; }

/* A fixed width with its icon in a fixed box, so neither moves when the label changes. */
.lg-bg-toggle {
  position: fixed;
  right: 12px;
  bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  width: 114px;
  height: 24px;
  white-space: nowrap;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 4px;
  line-height: 1;
  margin: 0;
  padding: 4px;
  border: none;
  background: none;
  backdrop-filter: none;
  color: #474747;
  font-family: inherit;
  font-size: 8px;
  font-weight: 400;
  letter-spacing: 0.8px;
  cursor: pointer;
  transition: color 0.12s ease;
}
.lg-bg-toggle:hover { color: #5b6060; }
/* Narrow windows have no corner to spare: the toggle joins the footer's column. */
@media (max-width: 640px) {
  .lg-foot { margin-bottom: 6px; }
  .lg-bg-toggle {
    position: static;
    justify-self: center;
    margin-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
  }
}
.lg-bg-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 17px;
  height: 16px;
  flex-shrink: 0;
}

/* The QR popup: the pairing link, for as long as it lives. */
.lg-qr-overlay {
  position: fixed;
  inset: 0;
  z-index: 980;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.6);
  animation: lgFadeIn 0.16s ease-out;
}
@keyframes lgFadeIn { from { opacity: 0; } }
.lg-qr-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 352px;
  max-width: calc(100vw - 32px);
  box-sizing: border-box;
  padding: 20px 20px 22px;
  border: 1px solid #474747;
  border-radius: 9px;
  background: rgba(3, 3, 3, 0.95);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  animation: qr-overlay-pop 0.16s ease-out;
}
.lg-qr {
  position: relative;
  width: 100%;
  aspect-ratio: 1;
  box-sizing: border-box;
  padding: 16px;
  border-radius: 16px;
  background: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
}
.lg-qr-code { width: 100%; height: 100%; }
.lg-qr-code svg { display: block; width: 100%; height: 100%; }
.lg-qr-loading {
  position: absolute;
  margin: 0;
  padding: 0 24px;
  color: #5b6060;
  font-size: 13px;
}
.lg-qr.ready .lg-qr-loading { display: none; }
.lg-qr-status {
  min-height: 16px;
  margin: 12px 0 12px;
  color: #b2b2b2;
  font-size: 12px;
}
.lg-qr-actions {
  display: flex;
  gap: 10px;
}
.lg-qr-close,
.lg-qr-copy {
  width: 100px;
  height: 38px;
  margin: 0;
  padding: 0;
  border-radius: 6px;
  backdrop-filter: none;
  font-family: inherit;
  font-size: 16px;
  font-weight: 500;
  cursor: pointer;
}
.lg-qr-close {
  border: none;
  background: #59fcb3;
  color: #000;
}
.lg-qr-copy {
  border: 1px solid #474747;
  background: transparent;
  color: #fff;
}
.lg-qr-copy.copied { border-color: #33db98; color: #59fcb3; }
.lg-qr-copy:disabled { opacity: 0.5; cursor: default; }

/* Pre-login: fullscreen account-list takeover. Triggered by clicking the
   pill above the logo; covers the entire viewport, blurs+darkens the
   underlying login screen, and lays out account rows full-width. The
   pill itself stays visible above through z-index ordering — the design
   keeps the active account anchored at the top while the user scans
   alternates below. */
.login-account-list-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(3, 3, 3, 0.9);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 160ms ease;
  z-index: 950;
}
.login-account-list-backdrop.visible {
  opacity: 1;
  pointer-events: auto;
}

.login-account-list {
  position: fixed;
  /* Top is set imperatively by `loginPicker.open()` so the list always
     anchors immediately below the picker pill regardless of layout drift. */
  top: 72px;
  left: 0;
  right: 0;
  width: min(420px, 100%);
  margin: 0 auto;
  box-sizing: border-box;
  z-index: 960;
  display: none;
  flex-direction: column;
  overflow: hidden;
  border: 2px solid #030303;
  border-radius: 10px;
  background: rgba(3, 3, 3, 0.5);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.login-account-list.open {
  display: flex;
  animation: loginAccountListIn 160ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
@keyframes loginAccountListIn {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Account rows in the fullscreen picker. Compact density (half the height
   of the design draft) so several alternates fit without scrolling, with
   a ping-style hover highlight: green tint + green left-border, mirroring
   the chat ping/mention row treatment as a familiar selection affordance.
   Default row background is a partial-opacity black so rows stay visible
   against the blurred backdrop instead of disappearing into it. */
.login-account-list .profile-switcher-row {
  height: 40px;
  box-sizing: border-box;
  padding: 0 21px;
  border-bottom: none;
  border-left: 3px solid transparent;
  background: transparent;
  transition: background-color 120ms ease, border-left-color 120ms ease;
}
.login-account-list .profile-switcher-row:hover {
  background: rgba(89, 252, 179, 0.1);
  border-left-color: #59fcb3;
}
.login-account-list .profile-switcher-npub {
  font-family: inherit;
  font-size: 10px;
  color: #5b6060;
}
.login-account-list .profile-switcher-active-dot {
  display: none;
}
/* Avatar size is set inline by ui/Avatar.svelte so this rule no
   longer needs to override it; kept for documentation. The inline style
   wins regardless. */
.login-account-list .profile-switcher-name {
  color: #fff;
  font-size: 14px;
}










/* ── Community invite panel (settings → Invite) ───────────────────────────── */
.modal-overlay {
  position: fixed;
  inset: 0;
  background-color: rgba(0, 0, 0, 0.8);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10000;
  padding: 20px;
}
.modal-box {
  background-color: #0a0a0a;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 12px;
  padding: 22px 24px;
  max-width: 460px;
  width: 100%;
  max-height: 85vh;
  overflow-y: auto;
}
.modal-box h3 { font-size: 18px; }
.modal-box input {
  width: 100%;
  box-sizing: border-box;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  padding: 10px 12px;
  color: inherit;
  margin-bottom: 8px;
}
.modal-box .btn {
  background: var(--icon-color-primary);
  color: #000;
  border: none;
  border-radius: 8px;
  padding: 9px 14px;
  font-weight: 600;
  cursor: pointer;
}
.modal-box .btn:disabled { opacity: 0.5; cursor: default; }
/* ── Community invite modal ─────────────────────────────────────── */
.cmt-modal { max-width: 480px; }
/* Phones: slim the modal's own gutters too — with flat sections this is the only inset left */
@media (max-width: 500px) {
  .cmt-modal { padding: 18px 14px; }
}

.cmt-header { display: flex; align-items: center; gap: 13px; margin-bottom: 20px; }
.cmt-header-icon {
  width: 46px; height: 46px; border-radius: 13px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--accent-color, #59fcb3) 15%, transparent);
}
.cmt-header-icon .icon { background-color: var(--accent-color, #59fcb3); width: 23px; height: 23px; }
/* min-width:0 lets the flex child shrink so the title can ellipsize instead of wrapping */
.cmt-header-text { flex: 1; min-width: 0; }
.cmt-title {
  font-size: 18px; font-weight: 700; margin: 0; line-height: 1.1;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cmt-subtitle { font-size: 13px; opacity: 0.5; margin: 0; line-height: 1.3; }

.cmt-section {
  /* Flat, full-width sections: the old card chrome (bg + border + 16px side
     padding) stacked a third gutter onto the overlay's and the modal's, squeezing
     phone screens to ~240px of content. A hairline between sections keeps the
     structure without the nesting. */
  padding: 0;
  margin-bottom: 18px;
}
.cmt-section + .cmt-section {
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  padding-top: 16px;
}
.cmt-section-head { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 13px; }
.cmt-section-head > .icon {
  background-color: var(--accent-color, #59fcb3);
  width: 17px; height: 17px; margin-top: 2px; flex-shrink: 0;
}
.cmt-section-title { font-size: 14px; font-weight: 700; margin: 0; }
.cmt-section-desc { font-size: 12px; opacity: 0.45; margin: 3px 0 0; line-height: 1.45; }
/* Live Public/Private mode pill on the Invite Links header. Private = muted (invite-only is the
   resting state); Public = accent (links are live). */
.cmt-mode-pill {
    display: inline-block;
    vertical-align: middle;
    margin-left: 6px;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    background: rgba(255, 255, 255, 0.08);
    color: rgba(255, 255, 255, 0.55);
}
.cmt-mode-pill.is-public {
    background: color-mix(in srgb, var(--accent-color, #59fcb3) 18%, transparent);
    color: var(--accent-color, #59fcb3);
}

.cmt-link-row {
  display: flex;
  align-items: center;
  gap: 6px;
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 9px;
  padding: 7px 7px 7px 12px;
  margin-bottom: 8px;
}
.cmt-link-url {
  flex: 1;
  min-width: 0;
  font-size: 13px;
  display: flex;
  align-items: center;
  gap: 7px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  opacity: 0.9;
}
.cmt-link-glyph {
  /* Inline (not the base .icon's absolute fill) so it sits in the chip, not the panel corner. */
  position: relative;
  top: auto; left: auto; right: auto; bottom: auto;
  display: inline-block;
  margin: 0;
  flex: none;
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  background-color: var(--accent-color, #59fcb3);
  opacity: 0.85;
}
.cmt-link-count {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-right: 6px;
  font-size: 12px;
  opacity: 0.7;
}
.cmt-link-count-ico {
  position: relative;
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  display: inline-block;
}
.cmt-icon-btn {
  width: 30px; height: 30px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, 0.06);
  border: none; border-radius: 7px; cursor: pointer;
  transition: background 0.12s;
}
.cmt-icon-btn:hover { background: rgba(255, 255, 255, 0.12); }
.cmt-icon-btn .icon { width: 15px; height: 15px; background-color: #fff; opacity: 0.75; }
.cmt-icon-btn.cmt-copied .icon { background-color: var(--accent-color, #59fcb3); opacity: 1; }
.cmt-icon-btn-danger:hover { background: color-mix(in srgb, var(--danger-color) 16%, transparent); }
.cmt-icon-btn-danger:hover .icon { background-color: var(--danger-color); opacity: 1; }
.cmt-icon-btn:disabled { opacity: 0.4; cursor: default; }

.cmt-empty { font-size: 12.5px; opacity: 0.4; margin: 0 0 11px; padding: 2px; }

/* Other creators' active-link counts (§10) — context for why the community is Public when you hold no links. */
.cmt-others { margin: 0 0 10px; padding: 8px 10px; border-radius: 8px; background: rgba(255, 255, 255, 0.04); }
.cmt-other-line { font-size: 12.5px; opacity: 0.6; margin: 0; padding: 1px 0; }
/* Collapsed multi-creator state: a bare accent line, box chrome only when expanded.
   Left inset mirrors the section head's icon column (17px icon + 10px gap) so the
   line sits in the same text column as the description above it. */
.cmt-others.collapsed { background: transparent; padding: 0 0 0 27px; }
.cmt-others-toggle {
  color: var(--accent-color, #59fcb3);
  opacity: 0.9;
  cursor: pointer;
  width: fit-content;
}
.cmt-others-toggle:hover { text-decoration: underline; }

.cmt-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  border: none; border-radius: 9px; padding: 10px 15px;
  font-weight: 600; font-size: 14px; cursor: pointer;
  transition: filter 0.12s, background 0.12s;
}
.cmt-btn .icon { width: 15px; height: 15px; }
.cmt-btn:disabled { opacity: 0.5; cursor: default; }
.cmt-btn-primary { background: var(--accent-color, #59fcb3); color: #000; flex-shrink: 0; }
.cmt-btn-primary .icon { background-color: #000; }
.cmt-btn-primary:hover:not(:disabled) { filter: brightness(1.08); }
.cmt-btn-secondary {
  width: 100%;
  background: color-mix(in srgb, var(--accent-color, #59fcb3) 12%, transparent);
  color: var(--accent-color, #59fcb3);
  border: 1px solid color-mix(in srgb, var(--accent-color, #59fcb3) 28%, transparent);
}
.cmt-btn-secondary .icon { background-color: var(--accent-color, #59fcb3); }
.cmt-btn-secondary:hover:not(:disabled) { background: color-mix(in srgb, var(--accent-color, #59fcb3) 20%, transparent); }
.cmt-btn-ghost { background: rgba(255, 255, 255, 0.06); color: #fff; }
.cmt-btn-ghost:hover:not(:disabled) { background: rgba(255, 255, 255, 0.11); }
/* Compact pill for row actions (Ban / Unban). `width:auto` overrides cmt-btn-secondary's
   full-width (that variant is also used for the full-width "Create invite link" button). */
.cmt-btn-sm { padding: 5px 11px; font-size: 12px; gap: 5px; flex-shrink: 0; width: auto; }
.cmt-btn-sm .icon { width: 13px; height: 13px; }


.cmt-btn-danger:hover:not(:disabled) { background: color-mix(in srgb, var(--danger-color) 22%, transparent); }



/* Collapses to nothing when idle (no reserved gap above the CTA); expands with a transition when
   setStatus writes a message. Driven purely by :empty, so no JS toggling is needed. */
.cmt-status {
  font-size: 13px;
  opacity: 0;
  max-height: 0;
  margin: 0 2px;
  overflow: hidden;
  transition: max-height 0.25s ease, opacity 0.2s ease, margin 0.25s ease;
}
.cmt-status:not(:empty) { opacity: 0.85; max-height: 60px; margin: 4px 2px 10px; }
.cmt-status.cmt-ok:not(:empty) { color: var(--accent-color, #59fcb3); opacity: 1; }
.cmt-status.cmt-err:not(:empty) { color: var(--danger-color); opacity: 1; }
.cmt-close { width: 100%; }
/* Top-right X (shares the relay dialog's button styling) — pushed to the header's far edge. */
.cmt-close-x { margin-left: auto; align-self: flex-start; }

/* Invite panel locked during a critical op (create / revoke+rekey / direct invite) — dim + wait cursor. */
.cmt-busy { cursor: wait; }
.cmt-busy button, .cmt-busy input { opacity: 0.55; cursor: wait; }

/* Guided rekey-progress modal (privatize / private-ban) — unclosable, determinate ring + step text. */
/* Above the invite panel (.modal-overlay = 10000) + popupConfirm (10002) so the ring sits on top. */
.rekey-progress-overlay { z-index: 10010; }
.rekey-progress-box { max-width: 320px; text-align: center; padding: 28px 26px 24px; }
/* Registered so the percentage INTERPOLATES — a plain conic-gradient `background` transition snaps. The
   ring inherits the animated value and sweeps smoothly between phase jumps. */
@property --rekey-pct {
  syntax: '<percentage>';
  inherits: true;
  initial-value: 0%;
}
.rekey-ring {
  position: relative;
  width: 104px; height: 104px;
  margin: 4px auto 18px;
  display: grid; place-items: center;
  --rekey-pct: 0%;
  transition: --rekey-pct 0.55s cubic-bezier(0.4, 0, 0.2, 1);
}
.rekey-ring::before {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  background: conic-gradient(var(--accent-color, #59fcb3) 0% var(--rekey-pct), rgba(255, 255, 255, 0.13) var(--rekey-pct) 100%);
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 8px), #fff calc(100% - 7.5px));
          mask: radial-gradient(farthest-side, transparent calc(100% - 8px), #fff calc(100% - 7.5px));
}
.rekey-pct { position: relative; font-size: 21px; font-weight: 700; color: #fff; font-variant-numeric: tabular-nums; }
.rekey-title { font-size: 16px; font-weight: 700; margin: 0 0 5px; color: #fff; }
.rekey-step { font-size: 13px; color: rgba(255, 255, 255, 0.62); margin: 0; min-height: 1.2em; }
.rekey-warning {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  font-size: 12px; color: rgba(255, 200, 87, 0.85);
  margin: 14px 0 0;
}
.rekey-warning .icon {
  position: relative;
  top: auto; left: auto; right: auto; bottom: auto;
  display: inline-block;
  margin: 0;
  flex: none;
  width: 14px; height: 14px;
  background-color: rgba(255, 200, 87, 0.85);
}

/* The base .icon is position:absolute (fills a sized parent). In these inline
   contexts it must flow as a normal sized glyph instead. */
.cmt-header-icon .icon,
.cmt-section-head > .icon,
.cmt-icon-btn .icon,
.cmt-btn .icon {
  position: relative;
  top: auto; left: auto; right: auto; bottom: auto;
  display: inline-block;
  margin: 0;
  flex: none;
}

/* Direct Invites contact picker */
/* Single-scroll modal. A scrolling .cmt-modal wrapping a scrolling contact list nests two scroll
   containers, and WKWebView re-composited the inner list as the row-hover overlay animated,
   dipping avatar opacity (a flicker). So the modal never scrolls: a fixed header sits above ONE
   scroll region (.cmt-body) that holds everything, with the list flowing inline (no scroll of its
   own) and the action button as a sticky footer inside the scroll. */
.cmt-modal {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.cmt-header { flex-shrink: 0; }
.cmt-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
.cmt-contacts {
  margin: 6px 0 12px;
  padding-right: 2px;
}
/* Sticky action bar: rides inside the scroll so it shares the cards' width (same scrollbar inset)
   and stays pinned to the bottom of the viewport. Solid bg hides rows scrolling underneath. */
.cmt-footer {
  position: sticky;
  bottom: 0;
  background: #0a0a0a;
  padding-top: 10px;
  margin-top: 2px;
  /* Above scrolling content — the search icon's z-index otherwise paints over it */
  z-index: 1;
}
/* Morphing CTA: gray "Done" (no selection) cross-fades to accent "Invite N" (selection > 0). The
   two faces are stacked; one slides+fades out while the other slides+fades in, background gray->accent. */
.cmt-cta {
  position: relative;
  overflow: hidden;
  width: 100%;
  transition: background 0.25s ease, color 0.25s ease, filter 0.15s ease;
}
.cmt-cta .cmt-cta-face {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  transition: opacity 0.2s ease, transform 0.22s ease;
}
.cmt-cta .cmt-cta-face-invite {
  position: absolute;
  inset: 0;
  opacity: 0;
  transform: translateY(65%);
}
.cmt-cta .cmt-cta-face-invite .icon { background-color: #000; }
.cmt-cta.has-selection { background: var(--accent-color, #59fcb3); color: #000; }
.cmt-cta.has-selection .cmt-cta-face-done { opacity: 0; transform: translateY(-65%); }
.cmt-cta.has-selection .cmt-cta-face-invite { opacity: 1; transform: translateY(0); }
/* Hover: dim, never the ghost-gray fill (which would beat the accent on specificity). */
.cmt-cta:not(.has-selection):hover:not(:disabled) { filter: brightness(1.25); }
.cmt-cta.has-selection:hover:not(:disabled) { background: var(--accent-color, #59fcb3); filter: brightness(0.9); }
.group-placeholder { opacity: 0.5; font-style: italic; }

/* Processing overlay: dimmed + blurred backdrop that blocks input while a short
   CPU task (image decode/resize/re-encode) runs in the backend. */
.processing-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease;
}
.processing-overlay.visible {
  opacity: 1;
  pointer-events: auto;
}
.processing-overlay-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  padding: 28px 36px;
  border-radius: 16px;
  background: rgba(20, 22, 26, 0.92);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
}
.processing-overlay-spinner {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  border: 3px solid rgba(255, 255, 255, 0.15);
  border-top-color: var(--accent-color, #59fcb3);
  animation: processing-overlay-spin 0.8s linear infinite;
}
@keyframes processing-overlay-spin {
  to { transform: rotate(360deg); }
}
.processing-overlay-text {
  color: #e8e8ea;
  font-size: 0.95rem;
  letter-spacing: 0.2px;
}

/* The community overview's action row. Wraps rather than overflowing: at three
   buttons a phone-width column can no longer hold them on one line. */
.group-overview-actions {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 10px;
}

@keyframes community-raid-pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--danger-color) 70%, transparent); }
  70%  { box-shadow: 0 0 0 7px color-mix(in srgb, var(--danger-color) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--danger-color) 0%, transparent); }
}

/* Raid alarm on the narrow layout's Moderate button. */
#group-moderate-btn.raid-alert {
  border-color: var(--danger-color);
  animation: community-raid-pulse 1.9s ease-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  #group-moderate-btn.raid-alert { animation: none; }
}

/* ── Community Moderation console ─────────────────────────────────────────────
   Same shell as the QR overlay (dim + card), sized to near-fullscreen: a raid is
   read as a list, and a list needs the room. */

.mod-overlay {
  position: fixed;
  inset: 0;
  background-color: rgba(0, 0, 0, 0.88);
  z-index: 10000;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 26px;
  box-sizing: border-box;
}

.mod-overlay.active { display: flex; }

.mod-overlay.closing {
  animation: modal-overlay-fade-out 0.15s ease-out forwards;
  pointer-events: none;
}

.mod-overlay.closing .mod-card {
  animation: modal-card-pop-out 0.15s ease-out forwards;
}

.mod-card {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 1040px;
  height: 100%;
  background-color: #101010;
  border: 1px solid #2c2e2f;
  border-radius: 18px;
  overflow: hidden;
  -webkit-user-select: none;
  user-select: none;
}

/* Applied after the overlay is displayed — WebKit never starts an animation
   declared on a subtree emerging from display:none. */
.mod-card.pop-in { animation: qr-overlay-pop 0.16s ease-out; }

.mod-card.busy { pointer-events: none; }
.mod-card.busy .mod-foot { pointer-events: auto; }

/* ── Head ─────────────────────────────────────────────────────────────────── */

.mod-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 18px;
  border-bottom: 1px solid rgba(57, 57, 57, 0.5);
  flex-shrink: 0;
}

.mod-head-titles {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}

.mod-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--danger-color);
}

.mod-title {
  margin: 0;
  font-size: 17px;
  font-weight: 600;
  line-height: 22px;
}

.mod-epoch {
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: rgba(255, 255, 255, 0.45);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 999px;
  padding: 3px 10px;
  white-space: nowrap;
}

.mod-x,
.mod-x:hover,
.mod-x:focus,
.mod-x:active {
  background: none;
  border: none;
  box-shadow: none;
  outline: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  color: rgba(255, 255, 255, 0.5);
  font-size: 15px;
  cursor: pointer;
  padding: 4px 6px;
}

.mod-x:hover { color: #fff; }

/* ── Raid banner ──────────────────────────────────────────────────────────── */

.mod-alert {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 12px 18px 0 18px;
  padding: 11px 13px;
  border-radius: 10px;
  background-color: color-mix(in srgb, var(--danger-color) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--danger-color) 38%, transparent);
  font-size: 13px;
  line-height: 19px;
}

.mod-alert.working {
  background-color: rgba(255, 255, 255, 0.05);
  border-color: rgba(255, 255, 255, 0.14);
}

.mod-alert-icon {
  position: relative;
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  /* `.icon` sets `margin: auto`, which as a flex item absorbs the row's free space
     and shunts the glyph away from its text. */
  margin: 2px 0 0 0;
  background-color: var(--danger-color);
}

.mod-alert.working .mod-alert-icon { background-color: rgba(255, 255, 255, 0.6); }

.mod-alert-text strong { margin-right: 4px; }
.mod-alert-text span { color: rgba(255, 255, 255, 0.72); }

/* What a tick does, said once, so the two groups below never need guessing. */
.mod-explain {
  margin: 12px 18px 0 18px;
  font-size: 12px;
  line-height: 17px;
  color: rgba(255, 255, 255, 0.5);
}

/* ── Toolbar ──────────────────────────────────────────────────────────────── */

.mod-toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 18px;
  flex-wrap: wrap;
  flex-shrink: 0;
}

.mod-search {
  position: relative;
  flex: 1 1 220px;
  min-width: 0;
}

.mod-search-icon {
  position: absolute;
  left: 12px;
  top: 0;
  bottom: 0;
  right: auto;
  width: 15px;
  height: 15px;
  margin: auto 0;
  background-color: rgba(255, 255, 255, 0.35);
  pointer-events: none;
}

.mod-search input {
  width: 100%;
  box-sizing: border-box;
  padding: 9px 12px 9px 36px;
  font-size: 13px;
  color: var(--text-color);
  background-color: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(57, 57, 57, 0.6);
  border-radius: 9px;
  outline: none;
  -webkit-user-select: text;
  user-select: text;
}

.mod-search input:focus { border-color: rgba(255, 255, 255, 0.22); }

.mod-filters {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.mod-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 11px;
  font-size: 12px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.6);
  background-color: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(57, 57, 57, 0.6);
  border-radius: 9px;
  cursor: pointer;
  transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}

.mod-chip:hover { background-color: rgba(255, 255, 255, 0.07); color: #fff; }

.mod-chip.active {
  color: #fff;
  background-color: rgba(255, 255, 255, 0.09);
  border-color: rgba(255, 255, 255, 0.22);
}

.mod-chip-count {
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: rgba(255, 255, 255, 0.4);
}

.mod-chip.active .mod-chip-count { color: rgba(255, 255, 255, 0.75); }

/* ── Member list ──────────────────────────────────────────────────────────── */

.mod-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0 12px 8px 12px;
}

.mod-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 140px;
  position: relative;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.4);
  text-align: center;
}

.mod-empty .icon {
  position: relative;
  width: 22px;
  height: 22px;
  margin: 0;
  background-color: rgba(255, 255, 255, 0.4);
}

.mod-row {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 6px 10px;
  border-radius: 10px;
  transition: background-color 0.12s ease;
}

.mod-row:hover { background-color: rgba(255, 255, 255, 0.045); }

/* A cut row recedes rather than lighting up. In a raid the cut set IS the list, so
   tinting it turns the whole panel one colour and the tick — the thing that actually
   decides who survives — stops being what the eye lands on. The badge and the reason
   stay bright: they are the evidence you are checking. */
.mod-row.cutting .mod-avatar { opacity: 0.55; }
.mod-row.cutting .mod-name { opacity: 0.72; }

.mod-row.locked { cursor: default; }

.mod-check {
  position: relative;
  width: 19px;
  height: 19px;
  flex-shrink: 0;
  border-radius: 6px;
  border: 1.5px solid rgba(255, 255, 255, 0.25);
  box-sizing: border-box;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}

.mod-check.on {
  background-color: var(--accent-color, #59fcb3);
  border-color: var(--accent-color, #59fcb3);
}

.mod-check .icon {
  width: 72%;
  height: 72%;
  background-color: #06120d;
}

.mod-row.locked .mod-check {
  opacity: 0.4;
}

.mod-avatar {
  width: 30px;
  height: 30px;
  min-width: 30px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

.mod-body {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  flex: 1;
}

.mod-row-top {
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
  line-height: 18px;
}

.mod-name {
  font-size: 13.5px;
  line-height: 18px;
  font-weight: 600;
  min-width: 0;
}

.mod-badge {
  font-size: 9px;
  line-height: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  padding: 2px 6px;
  border-radius: 5px;
  flex-shrink: 0;
  white-space: nowrap;
}

.mod-badge-trusted {
  color: var(--accent-color, #59fcb3);
  background-color: color-mix(in srgb, var(--accent-color, #59fcb3) 14%, transparent);
}

.mod-badge-protected {
  color: #FCE459;
  background-color: rgba(252, 228, 89, 0.13);
}

/* Kept, but the evidence did not settle it — amber, not the danger colour: this is a
   prompt to look, not a verdict. */
.mod-badge-neutral {
  color: var(--status-fair);
  background-color: color-mix(in srgb, var(--status-fair) 15%, transparent);
}

.mod-meta {
  font-size: 11px;
  line-height: 15px;
  color: rgba(255, 255, 255, 0.42);
  font-variant-numeric: tabular-nums;
}

.mod-why {
  font-size: 11.5px;
  line-height: 16px;
  color: rgba(255, 255, 255, 0.5);
}

.mod-why.bad { color: color-mix(in srgb, var(--danger-color) 78%, #fff); }

/* ── Foot ─────────────────────────────────────────────────────────────────── */

.mod-foot {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 18px;
  border-top: 1px solid rgba(57, 57, 57, 0.5);
  flex-wrap: wrap;
  flex-shrink: 0;
}

.mod-tally {
  display: flex;
  align-items: center;
  gap: 14px;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.42);
  font-variant-numeric: tabular-nums;
  flex: 1;
  min-width: 0;
}

.mod-tally b { font-weight: 700; }
.mod-tally-keep b { color: var(--accent-color, #59fcb3); }
.mod-tally-cut b { color: var(--danger-color); }

.mod-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.mod-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 9px 14px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-color);
  background-color: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 10px;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 0.12s ease, border-color 0.12s ease, opacity 0.12s ease, filter 0.12s ease;
}

.mod-btn .icon {
  position: relative;
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  margin: 0;
  background-color: rgba(255, 255, 255, 0.75);
}

.mod-btn:hover:not(:disabled) {
  background-color: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.24);
}

.mod-btn:disabled {
  opacity: 0.38;
  cursor: default;
}

.mod-btn-danger {
  color: #fff;
  background-color: color-mix(in srgb, var(--danger-color) 16%, transparent);
  border-color: color-mix(in srgb, var(--danger-color) 45%, transparent);
}

.mod-btn-danger .icon { background-color: var(--danger-color); }

.mod-btn-danger:hover:not(:disabled) {
  background-color: color-mix(in srgb, var(--danger-color) 26%, transparent);
  border-color: color-mix(in srgb, var(--danger-color) 65%, transparent);
}

/* Phone: the rim goes, the actions stack full-width so nothing destructive
   ends up a thumb-slip from Close. */
@media (max-width: 620px) {
  .mod-overlay { padding: 0; }
  .mod-card { border-radius: 0; border: none; max-width: none; }
  .mod-actions { width: 100%; }
  .mod-btn { flex: 1; justify-content: center; }
  .mod-tally { justify-content: center; width: 100%; }
}

/* ── Settings layout ─────────────────────────────────────────────────────────
   Shared by Community Settings (a modal riding the moderation overlay's backdrop
   and animation) and widescreen Settings: sections on the left, each with a
   timeline of anchors into its content, and the content as headed blocks in one
   centred column. */

.cs-overlay.closing .cs-card {
  animation: modal-card-pop-out 0.15s ease-out forwards;
}

.cs-card {
  font-family: Rubik, sans-serif;
  display: flex;
  width: 100%;
  max-width: 1180px;
  height: 100%;
  max-height: 860px;
  background-color: #050505;
  border: 1px solid #2c2e2f;
  border-radius: 18px;
  overflow: hidden;
}

.cs-card.pop-in { animation: qr-overlay-pop 0.16s ease-out; }

/* Nav column */
.cs-nav {
  display: flex;
  flex-direction: column;
  text-align: left;
  width: 248px;
  flex-shrink: 0;
  background-color: #0b0b0b;
  border-right: 1px solid #171717;
  -webkit-user-select: none;
  user-select: none;
}

.cs-search {
  position: relative;
  display: flex;
  align-items: center;
  height: 50px;
  padding: 0 10px;
  border-bottom: 1px solid #171717;
  box-sizing: border-box;
  flex-shrink: 0;
}
/* The icon draws across 20 of its 24 units: a 17px box gives the design's 14px glyph. */
.cs-search .icon {
  position: absolute;
  inset: auto;
  left: 20.5px;
  top: 50%;
  width: 17px;
  height: 17px;
  margin: 0;
  transform: translateY(-50%);
  background-color: #b2b2b2;
}
.cs-search input {
  width: 100%;
  height: 34px;
  padding: 0 12px 0 34px;
  border: none;
  border-radius: 8px;
  background-color: #1f1f1f;
  color: #fff;
  font-family: inherit;
  font-size: 14px;
  font-weight: 400;
  outline: none;
  box-sizing: border-box;
}
.cs-search input::placeholder { color: #899090; }
.cs-search input:focus { box-shadow: inset 0 0 0 1px #3a3a3a; }

.cs-nav-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 18px 10px 24px;
}

/* The divider spans the column less 20px a side; the glyph sits a little outside it. */
.cs-nav-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 10px 17px;
  padding: 0 0 16px;
  border-bottom: 1px solid #262626;
}
.cs-nav-glyph {
  position: relative;
  width: 22px;
  height: 22px;
  margin-left: -3px;
  flex-shrink: 0;
}
.cs-nav-glyph .icon {
  width: 100%;
  height: 100%;
  margin: 0;
  background-color: #fff;
}
.cs-nav-head .cs-nav-avatar {
  border-radius: 8px !important;
  flex-shrink: 0;
}
.cs-nav-title {
  font-size: 14px;
  line-height: 17px;
  font-weight: 700;
  color: #fff;
}

.cs-section {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  height: 34px;
  padding: 0 8px;
  margin: 0 0 1px;
  border: none;
  border-radius: 8px;
  background: none;
  color: #b2b2b2;
  font-family: inherit;
  font-size: 16px;
  line-height: 19px;
  font-weight: 400;
  text-align: left;
  cursor: pointer;
  transition: background-color 0.12s ease, color 0.12s ease;
}
.cs-section:hover { background-color: #151515; color: #fff; }
/* No backdrop blur (the base button has one): a composited row escapes its fold's clip. */
.cs-section,
.cs-anchor { backdrop-filter: none; }
.cs-section.active { background-color: #1b1b1b; color: #fff; }
.cs-section.danger { color: var(--danger-color); }
.cs-section-icon {
  position: relative;
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}
.cs-section-icon .icon {
  width: 100%;
  height: 100%;
  margin: 0;
  background-color: #5b6060;
  transition: background-color 0.12s ease;
}
.cs-section:hover .cs-section-icon .icon,
.cs-section.active .cs-section-icon .icon { background-color: #fff; }
.cs-section.danger .cs-section-icon .icon { background-color: var(--danger-color); }

/* A section's anchors open beneath it the way a rail folder does: the row
   grows while its contents fade and settle into place. */
.cs-anchors-wrap {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}
.cs-anchors-wrap.open {
  grid-template-rows: 1fr;
}
.cs-anchors-wrap > .cs-anchors {
  min-height: 0;
  overflow: hidden;
  /* Padding counts even at 0fr, so it collapses along with the row. */
  padding-top: 0;
  padding-bottom: 0;
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 0.2s ease, transform 0.28s cubic-bezier(0.22, 1, 0.36, 1),
    padding 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}
.cs-anchors-wrap.open > .cs-anchors {
  padding-top: 1px;
  padding-bottom: 20px;
  opacity: 1;
  transform: none;
}

/* The anchors hang off a line, dots on it, the one being read lit. A button clips what
   it draws to its own box, so each row draws only its own stretch: from its dot down
   for the first, through it for the middle, down to its dot for the last. */
.cs-anchors {
  position: relative;
  /* The design's indent: each dot's centre 53.5px into the column. */
  padding: 1px 0 20px 38px;
}
.cs-anchor::after {
  content: '';
  position: absolute;
  left: 4.5px;
  top: 0;
  bottom: 0;
  width: 2px;
  background-color: #444;
}
.cs-anchor.first::after { top: 50%; }
.cs-anchor.last::after { bottom: 50%; }
.cs-anchor.shut::after { opacity: 0; }
.cs-anchor {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  height: 26px;
  /* The row clips so it can fold shut; the left inset keeps the lit dot's growth inside it. */
  padding: 0 8px 0 2px;
  border: none;
  background: none;
  color: #b2b2b2;
  font-family: inherit;
  font-size: 12px;
  line-height: 14px;
  font-weight: 400;
  text-align: left;
  cursor: pointer;
  overflow: hidden;
  /* Square: the timeline runs past the row's corners, and a rounded one notches it. */
  border-radius: 0;
  transition: color 0.15s ease, height 0.28s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.2s ease;
}
.cs-anchor.shut {
  height: 0;
  opacity: 0;
  pointer-events: none;
}
.cs-anchor:hover { color: #e6e6e6; }
.cs-anchor.active { color: #fff; }
.cs-anchor-dot {
  position: relative;
  z-index: 1;
  width: 7px;
  height: 7px;
  margin-right: 2px;
  flex-shrink: 0;
  border-radius: 50%;
  background-color: #444;
  transition: background-color 0.2s ease, transform 0.2s ease;
}
.cs-anchor.active .cs-anchor-dot {
  background-color: #e6e6e6;
  transform: scale(1.12);
}
.cs-anchor-icon {
  position: relative;
  width: 12px;
  height: 12px;
  flex-shrink: 0;
  opacity: 0.85;
}
.cs-anchor-icon .icon {
  width: 12px;
  height: 12px;
  background-color: currentColor;
}
.cs-anchor-label { min-width: 0; }

/* A search folds non-matching sections shut rather than unmounting them. */
.cs-fold {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows 0.28s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.2s ease;
}
.cs-fold.shut {
  grid-template-rows: 0fr;
  opacity: 0;
  pointer-events: none;
}
.cs-fold-inner {
  min-height: 0;
  overflow: hidden;
}
/* The empty line never shares the list with a match: it leaves at once, and
   arrives only after the sections have folded away. */
.cs-fold-empty:not(.shut) { transition-delay: 0.22s; }
.cs-fold-empty.shut { transition: none; }

.cs-nav-empty {
  margin: 12px 10px;
  color: #6f6f6f;
  font-size: 13px;
}

.cs-nav-foot {
  margin-top: 12px;
  padding: 16px 8px 0;
  border-top: 1px solid #262626;
}
.cs-nav-foot .settings-footer {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
}
.cs-nav-foot .settings-footer span { font-size: 12px; line-height: 14px; }

/* Content */
.cs-main {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}

.cs-top {
  display: flex;
  align-items: center;
  height: 50px;
  padding: 0 18px 0 28px;
  border-bottom: 1px solid #171717;
  box-sizing: border-box;
  flex-shrink: 0;
}
.cs-top-title {
  flex: 1;
  text-align: left;
  margin: 0;
  font-size: 16px;
  line-height: 19px;
  font-weight: 400;
  color: #fff;
}

.cs-close {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 0;
  border: none;
  background: none;
  color: #9a9a9a;
  cursor: pointer;
}
.cs-close-x {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border: 1.5px solid #4a4a4a;
  border-radius: 50%;
  font-size: 12px;
  transition: border-color 0.12s ease, color 0.12s ease;
}
.cs-close-key {
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.06em;
}
.cs-close:hover { color: #fff; }
.cs-close:hover .cs-close-x { border-color: #9a9a9a; }

.cs-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

.cs-content {
  max-width: 584px;
  margin: 0 auto;
  /* The save bar floats over the bottom; the last block must scroll clear of it. */
  padding: 12px 36px 140px;
  transition: opacity 0.2s ease;
}
.cs-content.loading { opacity: 0.4; pointer-events: none; }

/* A section arrives with a short rise, the same soft settle the anchors use. */
.cs-content-enter {
  animation: cs-content-in 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes cs-content-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

.cs-block {
  padding: 30px 0 34px;
}
.cs-block + .cs-block {
  border-top: 1px solid #262626;
}

.cs-heading {
  margin: 0 0 20px;
  font-size: 24px;
  line-height: 28px;
  font-weight: 700;
  color: #fff;
}

.cs-lede {
  margin: -8px 0 18px;
  color: #9a9a9a;
  font-size: 14px;
  line-height: 1.5;
}

.cs-identity {
  display: flex;
  align-items: flex-start;
  gap: 22px;
}

.cs-icon {
  position: relative;
  width: 88px;
  height: 88px;
  flex-shrink: 0;
  border-radius: 24px;
  overflow: hidden;
  background-color: #161616;
}
.cs-icon .cs-icon-img {
  width: 100% !important;
  height: 100% !important;
  border-radius: 0 !important;
}
.cs-icon.editable { cursor: pointer; }
.cs-icon-edit {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: rgba(0, 0, 0, 0.55);
  opacity: 0;
  transition: opacity 0.15s ease;
}
.cs-icon-edit .icon {
  position: relative;
  inset: auto;
  width: 22px;
  height: 22px;
  background-color: #fff;
}
.cs-icon.editable:hover .cs-icon-edit { opacity: 1; }
.cs-icon-new {
  position: absolute;
  right: 6px;
  bottom: 6px;
  padding: 1px 7px;
  border-radius: 8px;
  background-color: var(--icon-color-primary);
  color: #000;
  font-size: 10px;
  font-weight: 700;
  line-height: 16px;
}

.cs-field { display: flex; flex-direction: column; }
.cs-field-grow { flex: 1; min-width: 0; }
.cs-field-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 8px;
}
.cs-label {
  color: #cfcfcf;
  font-size: 13px;
  font-weight: 500;
}
.cs-count {
  color: #6f6f6f;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.cs-count.warn { color: var(--danger-color); }

.cs-input {
  width: 100%;
  padding: 11px 13px;
  border: 1px solid #2c2c2c;
  border-radius: 10px;
  background-color: #111;
  color: #fff;
  font-family: inherit;
  font-size: 15px;
  outline: none;
  box-sizing: border-box;
  transition: border-color 0.15s ease;
  -webkit-user-select: text;
  user-select: text;
}
.cs-input:focus { border-color: var(--icon-color-primary); }
.cs-input:disabled { opacity: 0.6; }
.cs-textarea {
  resize: vertical;
  min-height: 110px;
  line-height: 1.5;
}

.cs-hint {
  margin: 10px 0 0;
  color: #7a7a7a;
  font-size: 12.5px;
  line-height: 1.45;
}

.cs-relays {
  border: 1px solid #262626;
  border-radius: 12px;
  overflow: hidden;
}
.cs-relays .cs-hint { margin: 14px 16px; }
.cs-relay {
  display: flex;
  align-items: center;
  gap: 12px;
  height: 46px;
  padding: 0 16px;
  background-color: #0c0c0c;
}
.cs-relay + .cs-relay { border-top: 1px solid #1e1e1e; }
.cs-relay-icon {
  position: relative;
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}
.cs-relay-icon .icon { width: 100%; height: 100%; background-color: #7a7a7a; }
.cs-relay-url {
  min-width: 0;
  color: #e8e8e8;
  font-size: 14px;
  -webkit-user-select: text;
  user-select: text;
}
.cs-relay-scheme { color: #6a6a6a; }

/* Settings rows. `.st-line` is a label, its explainer and a control on one line
   (the phone layout's storage rows); `.st-row` is the same line with its explainer
   after the label. */
.st-line {
  display: flex;
  align-items: center;
  margin-top: 15px;
}
.st-line-label {
  flex: 1;
  text-align: left;
  color: rgba(255, 255, 255, 0.8);
}
.st-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 36px;
  margin-bottom: 10px;
}
.st-row-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: #fff;
  font-size: 16px;
}
.st-btn {
  height: 32px;
  padding: 0 14px;
  border: 1px solid #333;
  border-radius: 6px;
  background-color: #262626;
  color: #fff;
  font-family: inherit;
  font-size: 12px;
  font-weight: 400;
  backdrop-filter: none;
  flex-shrink: 0;
  transition: background-color 0.12s ease;
}
.st-btn:hover:not(:disabled) { background-color: #333; }
.st-btn:disabled { opacity: 0.45; cursor: default; }

/* A settings page recasts the phone layout's rows as the settings grammar: a 16px
   label with its explainer after it, the control flush right. The rows keep their
   phone markup, so the explainer moves by flex order, not by DOM. */
.st-page {
  text-align: left;
}
.st-page .form-group,
.st-page .st-line {
  margin: 0;
}
.st-page .tor-card.form-group { margin-bottom: 18px; }
.st-page .toggle-container,
.st-page .st-line,
.st-page .danger-option {
  min-height: 36px;
  margin: 0 0 10px;
  gap: 12px;
}
.st-page .toggle-container > span:first-child,
.st-page .danger-option .left-group,
.st-page #notif-privacy-group .notif-privacy-label {
  line-height: 19px;
  display: inline-flex;
  flex-direction: row-reverse;
  justify-content: flex-end;
  align-items: center;
  gap: 6px;
  margin: 0;
  color: #fff;
  font-size: 16px;
}
.st-page .st-line-label {
  flex: 0 1 auto;
  order: 1;
  color: #fff;
  font-size: 16px;
}
.st-page .st-line > .icon-info {
  order: 2;
  margin: 0 0 0 -6px !important;
}
.st-page .st-line > :last-child {
  order: 3;
  margin-left: auto !important;
}
.st-page .icon-info {
  width: 16px !important;
  height: 16px !important;
  margin: 0 !important;
}
.st-page .theme-picker {
  width: 230px;
  margin: 0 0 0 auto;
}
.st-page .vselect-btn,
.st-page .vselect-item { font-size: 12px; }
.st-page .theme-picker .vselect-btn,
.st-page .theme-picker .vselect-item { font-size: 16px; }
.st-page select {
  width: 230px;
  height: 36px;
  margin: 0 0 0 auto !important;
  padding: 0 32px 0 12px;
  border: 1px solid #333;
  border-radius: 6px;
  background-color: #0a0a0a;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23888'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 10px;
  color: #fff;
  font-size: 14px;
  text-align: left;
  text-align-last: left;
}
.st-page .select-container { margin: 0; }

/* Whisper Model: label left, the delete button and the picker right, the model's
   status beneath both. */
.st-page .form-group:has(> .model-select-container) {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  row-gap: 8px;
  margin: 0 0 10px !important;
}
.st-page .model-label {
  gap: 6px;
  color: #fff;
  font-size: 16px;
}
.st-page .model-select-container { width: auto; }
.st-page .model-status { grid-column: 1 / -1; }
.st-page .model-select-container { margin: 0; }
.st-page .model-select-container .vselect { width: 230px; }

/* A faint label over a run of related rows. */
.st-group {
  margin: 18px 0 6px;
  color: #474747;
  font-size: 14px;
  line-height: 17px;
  text-align: left;
}
.cs-heading + .st-group { margin-top: 0; }


/* Network: flat rows, a pill for where each came from, the state in words. */
.st-page .relay-item {
  min-height: 36px;
  margin: 0 -8px 2px;
  padding: 4px 8px;
  border: none;
  border-radius: 6px;
  background: none;
  transition: background-color 0.12s ease;
}
.st-page .relay-item:hover { background-color: #171717; border: none; }
.st-page .relay-item.disabled { opacity: 0.55; }
.st-page .relay-item-content { gap: 12px; }
.st-page .relay-url { font-size: 16px; color: #fff; }
.st-page .relay-default-badge {
  display: inline-flex;
  justify-content: center;
  min-width: 64px;
  padding: 3px 10px;
  border: 1px solid #474747;
  border-radius: 100px;
  background: none;
  color: #b2b2b2;
  font-size: 12px;
  text-transform: capitalize;
  box-sizing: border-box;
}
.st-page .relay-custom-badge { border-color: #333; background-color: #333; }
.st-page .relay-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0;
  border: none;
  background: none;
  font-size: 16px;
  line-height: 19px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
}
.st-page .relay-status::before {
  content: '';
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  background-color: currentColor;
  mask: url('./icons/clock.svg') no-repeat center / contain;
  -webkit-mask: url('./icons/clock.svg') no-repeat center / contain;
}
.st-page .relay-status.connected { color: var(--accent-color, #59fcb3); }
.st-page .relay-status.connected::before {
  mask-image: url('./icons/check-circle.svg');
  -webkit-mask-image: url('./icons/check-circle.svg');
}
.st-page .relay-status.disconnected,
.st-page .relay-status.terminated,
.st-page .relay-status.banned { color: var(--danger-color); }
.st-page .relay-status.disconnected::before,
.st-page .relay-status.terminated::before,
.st-page .relay-status.banned::before {
  mask-image: url('./icons/cancel.svg');
  -webkit-mask-image: url('./icons/cancel.svg');
}
.st-page .relay-status.disabled { color: #b2b2b2; }
.st-page .relay-status.disabled::before {
  mask-image: url('./icons/disable.svg');
  -webkit-mask-image: url('./icons/disable.svg');
}
.st-page .st-row + .relay-item { margin-top: 4px; }

/* Storage: the donut beside its legend, the rows under a divider. */
.st-page .storage-summary { display: none; }
.st-page .form-group:has(> #storage-donut-wrap) {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 48px;
  margin-bottom: 20px;
  padding-bottom: 28px;
  border-bottom: 1px solid #262626;
}
.st-page #storage-donut-wrap {
  width: 220px;
  margin: 0;
  flex-shrink: 0;
}
.st-page #storage-legend {
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: repeat(4, auto);
  justify-content: start;
  gap: 14px 40px;
  margin: 0;
}
.st-page .storage-legend-item { font-size: 16px; color: #fff; }

/* Updates: the version as a row, the action under the rows. */
.st-page .update-info { text-align: left; }
.st-page .update-info > p:first-child {
  display: flex;
  justify-content: space-between;
  margin-bottom: 16px;
}
.st-page .update-info > p:first-child > span:first-child { opacity: 1 !important; color: #fff; }
.st-page #check-updates-btn { margin: 6px 0 0; }

/* Blocked users: the list keeps the column's edges, names at row size. */
.st-page .blocked-row { padding: 6px 0 !important; }
.st-page .blocked-name { color: #fff !important; font-size: 16px !important; }
.st-page .unblock-btn {
  font-size: 12px;
  display: inline-flex;
  align-items: center;
  height: 32px;
  padding: 0 14px;
  box-sizing: border-box;
}
.st-page #notif-privacy-group .vselect { flex: none; width: 230px; }

.notif-sound-label { display: none; }
.st-page .notif-sound-label {
  display: block;
  margin-right: auto;
  color: #fff;
  font-size: 16px;
}
.st-page .form-group:has(> .notif-sound-label) { gap: 8px !important; }
.st-page .form-group:has(> .notif-sound-label) .notif-preview-btn { order: 1; }
.st-page .form-group:has(> .notif-sound-label) .vselect { order: 2; width: 230px; }
.st-page .notif-sound-label { white-space: nowrap; }
.st-page .cancel-btn,
.st-page .danger-option button {
  height: 32px;
  margin: 0 !important;
  padding: 0 14px;
  border-radius: 6px;
  font-size: 12px;
  font-weight: 400;
}
.st-page .cancel-btn {
  border: 1px solid #333;
  background-color: #262626;
  color: #fff;
}
.st-page .cancel-btn:hover:not(:disabled) { background-color: #333; }

/* Roles: the list, dragged like the emoji pack rail, and the editor for one role. */
.cs-heading-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 20px;
}
.cs-heading-row .cs-heading { margin: 0; }
.cs-role-create {
  height: 32px;
  padding: 0 14px;
  font-size: 13px;
}
.cs-roles {
  border: 1px solid #262626;
  border-radius: 12px;
  overflow: hidden;
  margin-bottom: 10px;
}
.cs-role {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  height: 46px;
  padding: 0 14px 0 8px;
  background-color: #0c0c0c;
  cursor: pointer;
  touch-action: pan-y;
  -webkit-user-select: none;
  user-select: none;
  transition: background-color 0.12s ease;
}
.cs-role + .cs-role { border-top: 1px solid #1e1e1e; }
.cs-role:hover { background-color: #131313; }
.cs-role.is-dragging { opacity: 0.3; }
/* Both classes: the clone carries .cs-role too, whose background and transition this beats. */
.cs-role.cs-role-ghost {
  opacity: 0.92;
  border-radius: 10px;
  background-color: #161616;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.55);
  transition: none;
}
/* The pack rail's glowing insertion line. */
.cs-role.drop-above::after,
.cs-role.drop-below::after {
  content: '';
  position: absolute;
  left: 8px;
  right: 8px;
  height: 3px;
  border-radius: 2px;
  background-color: var(--icon-color-primary);
  box-shadow: 0 0 8px var(--icon-color-primary);
  pointer-events: none;
  z-index: 2;
}
.cs-role.drop-above::after { top: -2px; }
.cs-role.drop-below::after { bottom: -2px; }
.cs-role-grip {
  position: relative;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  color: #555;
  cursor: grab;
}
.cs-role-grip .icon { width: 16px; height: 16px; background-color: currentColor; }
.cs-role.locked .cs-role-grip { cursor: default; color: #444; }
/* The owner's fixed top row: gold like the owner crown elsewhere, and inert. */
.cs-role-owner { cursor: default; }
.cs-role-owner:hover { background-color: #0c0c0c; }
.cs-role-owner .cs-role-grip { color: #444; cursor: default; }
.cs-role-crown {
  position: relative;
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}
.cs-role-crown .icon { background-color: #fce459; }
.cs-role-owner .cs-role-name { color: #fce459; font-weight: 500; }
.cs-role-owner .cs-role-count { max-width: 40%; }

.cs-role-dot {
  width: 12px;
  height: 12px;
  flex-shrink: 0;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.15);
}
.cs-role-name {
  min-width: 0;
  color: #e8e8e8;
  font-size: 14px;
}
.cs-role.locked .cs-role-name { color: #9a9a9a; }
.cs-role-scope {
  flex-shrink: 1;
  min-width: 0;
  padding: 1px 8px;
  border-radius: 9px;
  background-color: #1b1b1b;
  color: #9a9a9a;
  font-size: 12px;
}
.cs-role-count {
  margin-left: auto;
  flex-shrink: 0;
  color: #6f6f6f;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}
.cs-role-open {
  position: relative;
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}
.cs-role-open .icon { background-color: #555; }

.cs-back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 22px 0 -8px;
  padding: 6px 10px 6px 4px;
  border: none;
  border-radius: 8px;
  background: none;
  color: #9a9a9a;
  font-family: inherit;
  font-size: 13px;
  cursor: pointer;
  transition: color 0.12s ease, background-color 0.12s ease;
}
.cs-back .icon { position: relative; inset: auto; width: 16px; height: 16px; background-color: currentColor; }
.cs-back:hover { color: #fff; background-color: #151515; }

.cs-field-gap { margin-top: 22px; }
.cs-hint-tight { margin-top: 2px; }
.cs-swatches {
  display: flex;
  gap: 12px;
  margin-top: 10px;
}
/* Default and Custom stand apart from the palette, one column beside its two rows. */
.cs-swatch-specials {
  display: grid;
  grid-template-rows: repeat(2, 30px);
  gap: 8px;
  padding-right: 12px;
  border-right: 1px solid #1f1f1f;
}
.cs-swatch-grid {
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 30px));
  gap: 8px;
}
.cs-swatch-grid .cs-swatch { width: auto; aspect-ratio: 1; height: auto; }
.cs-swatch {
  position: relative;
  width: 30px;
  height: 30px;
  padding: 0;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.cs-swatch:hover:not(:disabled) { transform: scale(1.08); }
.cs-swatch.on { box-shadow: 0 0 0 2px #050505, 0 0 0 4px #fff; }
.cs-swatch:disabled { cursor: default; opacity: 0.5; }
/* Default: the theme's own, drawn as a struck-through swatch. */
.cs-swatch-default {
  background-color: #99aab5;
  background-image: linear-gradient(135deg, transparent 46%, #050505 46%, #050505 54%, transparent 54%);
}
/* Custom: the palette glyph over whatever colour was picked. */
.cs-swatch-custom {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #1b1b1b;
  box-shadow: inset 0 0 0 1px #333;
}
.cs-swatch-custom .icon { position: relative; inset: auto; width: 16px; height: 16px; background-color: #fff; }
.cs-swatch-custom input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}
.cs-role-preview {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
  padding: 6px 12px;
  border-radius: 8px;
  background-color: #111;
  color: #e8e8e8;
  font-size: 14px;
  font-weight: 500;
}
.cs-select {
  margin-top: 10px;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
}

.cs-perm-group {
  margin: 22px 0 10px;
  color: #6f6f6f;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.cs-perm-group:first-of-type { margin-top: 4px; }
.cs-perm.toggle-container {
  gap: 16px;
  margin-bottom: 0;
  padding: 12px 0;
  border-bottom: 1px solid #1a1a1a;
}
.cs-perm .cs-perm-text {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin-right: 0;
}
.cs-perm-name { color: #e8e8e8; font-size: 14.5px; }
.cs-perm-desc { color: #7a7a7a; font-size: 12.5px; line-height: 1.4; }
.cs-perm .neon-toggle { flex-shrink: 0; }

.cs-role-remove {
  position: relative;
  z-index: 1;
  width: 28px;
  height: 28px;
  margin-left: auto;
  padding: 0;
  border: none;
  border-radius: 8px;
  background: none;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease, background-color 0.12s ease;
}
.cs-role-remove .icon { width: 14px; height: 14px; background-color: #9a9a9a; }
.member-pick-row:hover .cs-role-remove { opacity: 1; }
.cs-role-remove:hover { background-color: #1e1e1e; }
.cs-role-remove:hover .icon { background-color: var(--danger-color); }
.cs-role-add-bar {
  display: flex;
  justify-content: flex-end;
  gap: 6px;
  margin-top: 10px;
}

/* Bans: the contact picker's rows and search, laid out beside the count. */
.cs-bans-bar {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 8px;
}
/* On the field itself, not the Bans toolbar: the role editor's Members search sits
   on its own, and without this it wears the emoji picker's container. */
.cs-bans-search {
  padding: 0;
  margin: 0 0 8px;
  background: transparent;
  isolation: isolate;
}
.cs-bans-bar .cs-bans-search {
  flex: 1;
  min-width: 0;
  margin: 0;
}
/* The invite picker's field, which sets these inline. */
.cs-bans-search input {
  flex: 1;
  box-sizing: border-box;
  margin: 0;
  padding: 10px 12px 10px 44px;
  background-color: transparent;
  border: 1px solid rgba(57, 57, 57, 0.5);
  border-radius: 8px;
  color: #fff;
  font-size: 16px;
}
.cs-bans { margin: 6px 0 12px; }
.cs-bans.busy { opacity: 0.55; pointer-events: none; }
.cs-bans-all { cursor: pointer; }
.cs-bans-all-label {
  flex: 1;
  position: relative;
  z-index: 1;
  color: #9a9a9a;
  font-size: 13px;
}


/* The save bar rises when the draft differs from what the community holds. */
.cs-savebar {
  position: absolute;
  left: 50%;
  bottom: 22px;
  display: flex;
  align-items: center;
  gap: 16px;
  width: min(600px, calc(100% - 44px));
  padding: 10px 10px 10px 18px;
  border: 1px solid #2c2c2c;
  border-radius: 12px;
  background-color: #151515;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.55);
  box-sizing: border-box;
  opacity: 0;
  transform: translate(-50%, 140%);
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.32s cubic-bezier(0.22, 1, 0.36, 1), border-color 0.2s ease;
}
.cs-savebar.visible {
  opacity: 1;
  transform: translate(-50%, 0);
  pointer-events: auto;
}
.cs-savebar-text {
  flex: 1;
  min-width: 0;
  color: #e6e6e6;
  font-size: 14px;
  transition: color 0.2s ease;
}
.cs-savebar.warn { border-color: var(--danger-color); animation: cs-shake 0.42s cubic-bezier(0.36, 0.07, 0.19, 0.97); }
.cs-savebar.warn .cs-savebar-text { color: var(--danger-color); }
@keyframes cs-shake {
  10%, 90% { transform: translate(calc(-50% - 1px), 0); }
  20%, 80% { transform: translate(calc(-50% + 3px), 0); }
  30%, 50%, 70% { transform: translate(calc(-50% - 5px), 0); }
  40%, 60% { transform: translate(calc(-50% + 5px), 0); }
}
.cs-savebar-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.cs-btn-text,
.cs-btn-save {
  height: 34px;
  padding: 0 14px;
  border: none;
  border-radius: 8px;
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
}
.cs-btn-text { background: none; color: #cfcfcf; }
.cs-btn-text:hover { color: #fff; text-decoration: underline; }
.cs-btn-save {
  background-color: var(--icon-color-primary);
  color: #000;
  font-weight: 600;
  transition: filter 0.12s ease, opacity 0.12s ease;
}
.cs-btn-save:hover { filter: brightness(1.08); }
.cs-btn-text:disabled,
.cs-btn-save:disabled { opacity: 0.45; cursor: default; filter: none; text-decoration: none; }

/* Narrow: the nav becomes a strip of section tabs above the content. */
@media (max-width: 760px) {
  .cs-overlay { padding: 0; }
  .cs-card { flex-direction: column; border-radius: 0; border: none; max-width: none; max-height: none; }
  .cs-nav { width: auto; border-right: none; border-bottom: 1px solid #262626; }
  .cs-search, .cs-nav-head, .cs-anchors, .cs-nav-empty { display: none; }
  .cs-nav-scroll { display: flex; gap: 6px; padding: 10px 12px; overflow-x: auto; overflow-y: hidden; }
  .cs-section { width: auto; white-space: nowrap; }
  .cs-content { padding: 4px 18px 140px; }
  .cs-identity { flex-direction: column; }
}

/* ── Policy Designer ─────────────────────────────────────────────────────── */

.mod-tabs {
    display: flex;
    gap: 4px;
    padding: 0 4px;
    margin-bottom: 10px;
    border-bottom: 1px solid var(--border-color, rgba(255, 255, 255, 0.08));
}

.mod-tab {
    background: none;
    border: none;
    color: var(--text-muted, #8b8b8b);
    font-size: 14px;
    font-weight: 600;
    padding: 8px 14px;
    cursor: pointer;
    border-bottom: 2px solid transparent;
    transition: color 0.15s ease, border-color 0.15s ease;
}

.mod-tab:hover { color: var(--text-color, #fff); }

.mod-tab.active {
    color: var(--accent-color, #59fcb3);
    border-bottom-color: var(--accent-color, #59fcb3);
}

.mod-policies { display: flex; flex-direction: column; gap: 14px; overflow-y: auto; flex: 1; min-height: 0; }

/* Live policies */
.pol-list { display: flex; flex-direction: column; gap: 8px; }

.pol-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    border-radius: 10px;
    background: var(--surface-2, rgba(255, 255, 255, 0.04));
    border: 1px solid var(--border-color, rgba(255, 255, 255, 0.07));
}

.pol-row-main { flex: 1; min-width: 0; }
.pol-row-name { font-weight: 600; font-size: 14px; }
.pol-row-sub { font-size: 12px; color: var(--text-muted, #8b8b8b); }
.pol-row-invalid { color: var(--danger-color); }

.pol-toggle {
    position: relative;
    width: 40px;
    height: 22px;
    border-radius: 11px;
    border: none;
    background: rgba(255, 255, 255, 0.16);
    cursor: pointer;
    transition: background 0.18s ease;
    flex-shrink: 0;
}

.pol-toggle::after {
    content: "";
    position: absolute;
    top: 3px;
    left: 3px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #fff;
    transition: transform 0.18s ease;
}

.pol-toggle.on { background: var(--accent-color, #59fcb3); }
.pol-toggle.on::after { transform: translateX(18px); }

.pol-delete {
    background: none;
    border: none;
    color: var(--text-muted, #8b8b8b);
    cursor: pointer;
    padding: 6px;
    border-radius: 6px;
    flex-shrink: 0;
}
.pol-delete:hover { color: var(--danger-color); background: color-mix(in srgb, var(--danger-color) 10%, transparent); }

/* Preset gallery */
.pol-gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; }

.pol-card {
    text-align: left;
    padding: 14px;
    border-radius: 12px;
    background: var(--surface-2, rgba(255, 255, 255, 0.04));
    border: 1px solid var(--border-color, rgba(255, 255, 255, 0.07));
    cursor: pointer;
    transition: border-color 0.15s ease, transform 0.1s ease;
    color: inherit;
    font: inherit;
}

.pol-card:hover { border-color: var(--accent-color, #59fcb3); transform: translateY(-1px); }
.pol-card-name { font-weight: 600; font-size: 14px; margin-bottom: 4px; }
.pol-card-desc { font-size: 12px; color: var(--text-muted, #8b8b8b); line-height: 1.45; }
.pol-card-eg { font-size: 11px; line-height: 1.2; color: var(--text-muted, #8b8b8b); font-style: italic; margin-top: 6px; opacity: 0.85; }

/* Editor */
.pol-editor { display: flex; flex-direction: column; gap: 14px; }
.pol-editor-head { display: flex; align-items: center; gap: 8px; margin-left: -4px; }
.pol-editor-title { margin: 0; font-size: 16px; }

/* The app's own back glyph rather than a literal "←": the text arrow renders
   at a different weight and baseline in every font it lands in, which is what
   made it read as pasted on. Icon-only and round, the way every other back
   control in Vector is. */
.pol-back {
    position: relative;
    flex-shrink: 0;
    width: 30px;
    height: 30px;
    padding: 0;
    background: none;
    border: none;
    border-radius: 50%;
    cursor: pointer;
    transition: background-color 0.12s ease;
}

.pol-back .icon {
    background-color: rgba(255, 255, 255, 0.5);
    transition: background-color 0.12s ease;
}

.pol-back:hover { background-color: rgba(255, 255, 255, 0.07); }
.pol-back:hover .icon { background-color: #fff; }

.pol-caveat {
    margin: 0;
    padding: 10px 12px;
    border-radius: 0 8px 8px 0;
    background: rgba(255, 193, 7, 0.08);
    border-left: 3px solid rgba(255, 193, 7, 0.6);
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--text-color, #ddd);
}

.pol-dials { display: flex; flex-direction: column; gap: 14px; }
.pol-dial-label { font-size: 13px; font-weight: 600; margin-bottom: 4px; display: block; }
.pol-dial-hint { font-size: 11.5px; color: var(--text-muted, #8b8b8b); margin-bottom: 6px; }

.pol-dial textarea,
.pol-dial input[type="text"] {
    width: 100%;
    box-sizing: border-box;
    padding: 10px 12px;
    border-radius: 8px;
    border: 1px solid var(--border-color, rgba(255, 255, 255, 0.1));
    background: var(--surface-1, rgba(0, 0, 0, 0.25));
    color: var(--text-color, #fff);
    font-size: 13px;
    font-family: inherit;
    resize: vertical;
}

.pol-dial textarea:focus,
.pol-dial input:focus { outline: none; border-color: var(--accent-color, #59fcb3); }

.pol-seg { display: flex; gap: 6px; }

.pol-seg-btn {
    flex: 1;
    padding: 8px 10px;
    border-radius: 8px;
    border: 1px solid var(--border-color, rgba(255, 255, 255, 0.1));
    background: var(--surface-1, rgba(0, 0, 0, 0.2));
    color: var(--text-muted, #8b8b8b);
    cursor: pointer;
    font-size: 12.5px;
    font-weight: 600;
}

.pol-seg-btn.active {
    border-color: var(--accent-color, #59fcb3);
    color: var(--accent-color, #59fcb3);
    background: rgba(89, 252, 179, 0.08);
}

.pol-chips { display: flex; flex-wrap: wrap; gap: 6px; }

.pol-chip {
    padding: 6px 10px;
    border-radius: 999px;
    border: 1px solid var(--border-color, rgba(255, 255, 255, 0.12));
    background: var(--surface-1, rgba(0, 0, 0, 0.2));
    color: var(--text-muted, #8b8b8b);
    cursor: pointer;
    font-size: 12px;
}
.pol-chip.on { border-color: var(--accent-color, #59fcb3); color: var(--accent-color, #59fcb3); }

/* Preview — the safety rail */

.pol-hint { font-size: 11.5px; color: var(--text-muted, #8b8b8b); }

.pol-preview-out {
    border-radius: 10px;
    padding: 14px;
    background: var(--surface-2, rgba(255, 255, 255, 0.04));
    border: 1px solid var(--border-color, rgba(255, 255, 255, 0.07));
    font-size: 13px;
    line-height: 1.55;
}

.pol-preview-head { font-weight: 600; margin-bottom: 8px; }
.pol-preview-num { color: var(--accent-color, #59fcb3); font-weight: 700; }

/* Square the left corners so the accent reads as a flat rule rather than a
   sliver of a rounded box, and take the colour from the theme — danger is red
   or pink depending on which one is selected, never a hardcoded hex. */
.pol-warn {
    margin-top: 10px;
    padding: 10px 12px;
    border-radius: 0 8px 8px 0;
    background: color-mix(in srgb, var(--danger-color) 10%, transparent);
    border-left: 3px solid var(--danger-color);
    font-size: 12.5px;
}

.pol-warn-name { font-weight: 600; }
.pol-preview-rows { margin-top: 10px; display: flex; flex-direction: column; gap: 6px; max-height: 220px; overflow-y: auto; }

.pol-preview-row {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 6px 8px;
    border-radius: 6px;
    background: rgba(0, 0, 0, 0.18);
}

.pol-preview-row-name { font-weight: 600; font-size: 12.5px; }
.pol-preview-row-why { font-size: 11.5px; color: var(--text-muted, #8b8b8b); flex: 1; min-width: 0; }
.pol-preview-score { font-size: 11px; color: var(--text-muted, #8b8b8b); font-variant-numeric: tabular-nums; }

.pol-editor-foot { display: flex; justify-content: flex-end; gap: 10px; }
.mod-btn-primary { background: var(--accent-color, #59fcb3); color: #05261a; border-color: transparent; }
/* Dim, never the ghost-gray fill: .mod-btn's generic hover paints translucent
   white over the accent and all but erases the button. Same fix .cmt-cta uses. */
.mod-btn-primary:hover:not(:disabled) {
  background: var(--accent-color, #59fcb3);
  border-color: transparent;
  filter: brightness(0.9);
}
.mod-btn-primary:disabled { opacity: 0.45; cursor: not-allowed; }

/* ── Moderation panel: instrument, not checklist ──────────────────────────────
   A healthy community means every row is ticked, which makes the tick the
   loudest thing on screen and the least informative. Signal moves to standing
   and evidence: a rail you can scan, numerals that line up, and a summary that
   answers "is anything wrong" before a single row is read. */

.mod-stats {
    display: flex;
    gap: 0;
    margin: 14px 18px 0;
    border-radius: 12px;
    overflow: hidden;
    background: rgba(255, 255, 255, 0.028);
    border: 1px solid rgba(255, 255, 255, 0.06);
}

.mod-stat {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 4px;
    padding: 12px 14px;
    position: relative;
}

.mod-stat + .mod-stat::before {
    content: "";
    position: absolute;
    left: 0;
    top: 22%;
    bottom: 22%;
    width: 1px;
    background: rgba(255, 255, 255, 0.07);
}

.mod-stat-n {
    font-size: 21px;
    font-weight: 650;
    line-height: 1.05;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
    color: rgba(255, 255, 255, 0.92);
}

.mod-stat-l {
    font-size: 10.5px;
    /* Inheriting the body's 24px leading put ~7px of empty line box under a
       10.5px all-caps label while the number above it sat tight, which read as
       the whole block being nudged upward inside its own padding. */
    line-height: 1.1;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.4);
}

/* Colour means something or it is not used. */
.mod-stat-good .mod-stat-n { color: var(--accent-color, #59fcb3); }
.mod-stat-staff .mod-stat-n { color: #FCE459; }
.mod-stat-bad .mod-stat-n { color: var(--danger-red, #FC595C); }

/* A row carries its standing on a rail — scannable down a list of 155 without
   putting a badge on every line. */
.mod-row {
    position: relative;
    padding-left: 14px;
}

.mod-row::before {
    content: "";
    position: absolute;
    left: 3px;
    top: 8px;
    bottom: 8px;
    width: 2px;
    border-radius: 2px;
    background: transparent;
    transition: background-color 0.14s ease;
}

.mod-standing-staff::before { background: #FCE459; }
.mod-standing-trusted::before { background: color-mix(in srgb, var(--accent-color, #59fcb3) 55%, transparent); }
.mod-standing-flagged::before { background: var(--danger-red, #FC595C); }

/* Numerals in tabular figures: "9 msgs" and "10 msgs" line up, so the column
   can be compared rather than merely read. */
.mod-num {
    font-variant-numeric: tabular-nums;
    color: rgba(255, 255, 255, 0.62);
}

.mod-dot {
    margin: 0 6px;
    color: rgba(255, 255, 255, 0.22);
}

.mod-unknown {
    color: rgba(255, 255, 255, 0.34);
    font-style: italic;
}

/* When everyone is kept, the ticks are wallpaper. Let them recede, and let the
   CUT rows — the rare, consequential ones — be what the eye catches. */
.mod-check.on {
    background-color: color-mix(in srgb, var(--accent-color, #59fcb3) 42%, transparent);
    border-color: transparent;
}

.mod-check.on .icon { background-color: rgba(6, 18, 13, 0.82); }

.mod-row:hover .mod-check.on {
    background-color: var(--accent-color, #59fcb3);
}

.mod-row.cutting {
    background: color-mix(in srgb, var(--danger-red, #FC595C) 7%, transparent);
}

.mod-row.cutting::before { background: var(--danger-red, #FC595C); }

.mod-row.cutting .mod-check {
    border-color: color-mix(in srgb, var(--danger-red, #FC595C) 55%, transparent);
}

/* The actions must stay reachable at any window height: a moderator should
   never have to scroll to find the thing that ends a raid. */
/* Solid, not a fade: a semi-transparent footer let member rows show through the
   tally and the action buttons, which are the two things that must never be
   misread. The fade lives above it instead, so the list still dissolves rather
   than hitting a hard edge. */
.mod-foot {
    position: sticky;
    bottom: 0;
    background: #101010;
    padding-top: 13px;
    margin-top: 4px;
}

.mod-foot::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 100%;
    height: 26px;
    background: linear-gradient(to top, #101010, transparent);
    pointer-events: none;
}

.mod-explain {
    margin: 12px 18px 0;
    font-size: 12px;
    line-height: 1.5;
    color: rgba(255, 255, 255, 0.44);
}

/* Policies pane: the gallery is a choice, so give it a line of framing rather
   than dropping four cards into empty space. */
.pol-gallery::before {
    content: "Start from a template";
    grid-column: 1 / -1;
    font-size: 10.5px;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.38);
    margin-bottom: -2px;
}

/* The last row scrolls under the sticky footer; a little slack stops it landing
   exactly on the seam. */
.mod-list {
    padding-bottom: 10px;
}

.mod-tally b {
    font-variant-numeric: tabular-nums;
    font-size: 13.5px;
    font-weight: 650;
}

.mod-tally-ban {
    margin-left: auto;
    padding-right: 6px;
    color: rgba(255, 255, 255, 0.3);
}

/* ── Console layout: the frame holds still, the list moves ───────────────
   The card used to be the scroller, so paging through 155 members took the
   search box, the filters and the standing counts off screen with it — you
   could no longer see what you were filtering by while you filtered. Now the
   card is a fixed frame and #mod-list is the only thing that scrolls. */
.mod-card {
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.mod-card > .mod-head,
.mod-card > .mod-alert,
.mod-card > .mod-tabs,
.mod-card > .mod-foot {
    flex-shrink: 0;
}

#mod-members-pane {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
}

#mod-members-pane > .mod-stats,
#mod-members-pane > .mod-explain,
#mod-members-pane > .mod-toolbar {
    flex-shrink: 0;
}

.mod-list {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
}

.mod-policies {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* No longer sticky — it is the frame's last row, so it cannot be scrolled past. */
.mod-foot {
    position: static;
    margin-top: 0;
    border-top: 1px solid rgba(57, 57, 57, 0.5);
}

.mod-foot::before {
    display: none;
}

/* The built-in rules are not editable, so the row carries a tag where a custom
   policy carries its switch — same shape, obviously not the same controls. */
.pol-row-builtin {
    background: rgba(89, 252, 179, 0.045);
    border-color: rgba(89, 252, 179, 0.16);
}

.pol-locked {
    font-size: 10px;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    font-weight: 650;
    color: rgba(89, 252, 179, 0.72);
    border: 1px solid rgba(89, 252, 179, 0.24);
    border-radius: 999px;
    padding: 3px 9px;
    white-space: nowrap;
}

/* Grid stretch was centring each card's text against its own height, so four
   titles in a row sat at four different heights. */
.pol-card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
}

.pol-gallery { padding-bottom: 4px; }

/* Members needs the full height for 155 rows; Policies is four cards and a
   status line, and stretching it to match left half the modal empty. The frame
   takes the height its tab actually needs. */
.mod-card {
    height: auto;
    max-height: 100%;
    min-height: 300px;
}

/* A dimmed accent still reads as the obvious next action. While the policy has
   not been previewed, the button has to look like what it is: not yet available. */
.mod-btn-primary:disabled {
    opacity: 1;
    background: transparent;
    color: rgba(255, 255, 255, 0.32);
    border: 1px solid rgba(255, 255, 255, 0.12);
    cursor: not-allowed;
}

/* Preview and Enable are two steps of one act, so they share a row: the gate
   and the key, side by side, with the sequence readable left to right. */
.pol-editor-foot {
    border-top: 1px solid rgba(57, 57, 57, 0.5);
    padding-top: 13px;
    padding-bottom: 13px;
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

/* Both labels reserve the width of their longest state, so neither button
   resizes as it changes what it says. */
.pol-editor-foot .mod-btn-primary {
    justify-content: center;
    min-width: 196px;
}

/* ── Tabs ─────────────────────────────────────────────────────────────────
   A flat underline and nothing else. The global button style rounds every
   corner, which turned a tab strip into two floating pills. */
.mod-tabs {
    gap: 0;
    padding: 0 18px;
    margin-bottom: 0;
}

.mod-tab {
    border-radius: 0;
    background: none;
    padding: 11px 2px;
    margin-right: 22px;
    /* Overlap the strip's own hairline so the active underline replaces it
       rather than stacking a second line beneath. */
    margin-bottom: -1px;
    border: none;
    border-bottom: 2px solid transparent;
    color: rgba(255, 255, 255, 0.42);
    backdrop-filter: none;
}

.mod-tab:last-child { margin-right: 0; }

.mod-tab:hover { color: rgba(255, 255, 255, 0.72); }

.mod-tab.active {
    color: #fff;
    border-bottom-color: var(--accent-color, #59fcb3);
}

/* Same 18px gutter the Members pane uses — the two tabs are one panel and
   should not shift their content sideways when you switch. */
/* No bottom padding: WebKit drops a scroll container's trailing padding once
   its content overflows, so the last row ends up hard against the edge. The
   space lives on the last child of each view instead, where it is honoured. */
.mod-policies {
    padding: 14px 18px 0;
}

/* ── Inspecting and building ──────────────────────────────────────────────
   The shipped defaults are not a black box: the row opens the same editor a
   template does, with every rule and every blocked domain already in it. */
.pol-inspect {
    background: none;
    border: 1px solid rgba(89, 252, 179, 0.28);
    color: rgba(89, 252, 179, 0.85);
    border-radius: 999px;
    padding: 6px 13px;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.15s ease, border-color 0.15s ease;
}

.pol-inspect:hover {
    background: rgba(89, 252, 179, 0.1);
    border-color: rgba(89, 252, 179, 0.5);
}

/* Forked: the defaults are NOT running, and the row must not imply they are. */
.pol-row-forked {
    background: rgba(255, 255, 255, 0.03);
    border-color: rgba(255, 255, 255, 0.07);
}

.pol-row-forked .pol-row-sub { color: rgba(255, 255, 255, 0.4); }

.pol-card-defaults {
    background: rgba(89, 252, 179, 0.045);
    border-color: rgba(89, 252, 179, 0.16);
}

/* Start from scratch is a different kind of answer from the templates, so it
   sits on its own line rather than posing as a fifth ready-made one. */
.pol-scratch { margin-top: 2px; margin-bottom: 18px; }

.pol-scratch-btn {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    text-align: left;
    padding: 13px 14px;
    border-radius: 12px;
    background: none;
    border: 1px dashed rgba(255, 255, 255, 0.14);
    color: inherit;
    font: inherit;
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease;
}

.pol-scratch-btn:hover {
    border-color: rgba(89, 252, 179, 0.45);
    background: rgba(255, 255, 255, 0.02);
}

.pol-scratch-plus {
    font-size: 19px;
    line-height: 1;
    color: rgba(89, 252, 179, 0.8);
    width: 22px;
    text-align: center;
}

.pol-scratch-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pol-scratch-name { font-weight: 600; font-size: 14px; }
.pol-scratch-desc { font-size: 12px; color: var(--text-muted, #8b8b8b); }

.pol-text {
    width: 100%;
    box-sizing: border-box;
    padding: 9px 12px;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.1);
    color: #fff;
    font: inherit;
    font-size: 13px;
}

.pol-text:focus { outline: none; border-color: rgba(255, 255, 255, 0.22); }

.pol-rules { display: flex; flex-direction: column; gap: 10px; }

.pol-rule {
    padding: 12px 13px;
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.035);
    border: 1px solid rgba(255, 255, 255, 0.08);
}

.pol-rule-head { display: flex; align-items: center; gap: 10px; }
.pol-rule-name { font-weight: 600; font-size: 13.5px; flex: 1; min-width: 0; }

.pol-rule-x {
    background: none;
    border: none;
    color: rgba(255, 255, 255, 0.35);
    font-size: 13px;
    padding: 2px 4px;
    border-radius: 6px;
    cursor: pointer;
}

.pol-rule-x:hover { color: var(--danger-red, #FC595C); }

.pol-rule-desc {
    font-size: 12px;
    line-height: 1.45;
    color: rgba(255, 255, 255, 0.44);
    margin-top: 3px;
}

.pol-rule-hint {
    font-size: 11.5px;
    color: rgba(255, 255, 255, 0.36);
    margin: 9px 0 5px;
}

.pol-rule textarea {
    width: 100%;
    box-sizing: border-box;
    resize: vertical;
}

.pol-rule-add { display: flex; flex-wrap: wrap; gap: 7px; }

.pol-rule-add-btn {
    background: none;
    border: 1px dashed rgba(255, 255, 255, 0.16);
    border-radius: 8px;
    color: rgba(255, 255, 255, 0.6);
    font-size: 12px;
    font-weight: 600;
    padding: 7px 11px;
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease;
}

.pol-rule-add-btn:hover {
    border-color: rgba(89, 252, 179, 0.45);
    color: #fff;
}

/* The rule read-out: what a policy actually does, before you change anything. */
.pol-summary { display: flex; flex-direction: column; gap: 1px; }

.pol-sum {
    padding: 9px 12px;
    background: rgba(255, 255, 255, 0.03);
    border-left: 2px solid rgba(89, 252, 179, 0.45);
}

.pol-sum:first-child { border-radius: 0 8px 0 0; }
.pol-sum:last-child { border-radius: 0 0 8px 0; }

/* An aggravator is not cover on its own, and the rail says so before the words do. */
.pol-sum-armed { border-left-color: rgba(255, 255, 255, 0.14); }

.pol-sum-name {
    font-size: 13px;
    font-weight: 600;
    display: flex;
    align-items: baseline;
    gap: 9px;
    flex-wrap: wrap;
}

.pol-sum-tag {
    font-size: 10.5px;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.34);
    letter-spacing: 0.01em;
}

.pol-sum-detail {
    font-size: 12px;
    line-height: 1.45;
    color: rgba(255, 255, 255, 0.46);
}

/* ── Preview button: spinner, result, then back to the offer ──────────────
   Fixed width across both labels so the button does not resize under the
   cursor as it changes what it says. */
.pol-preview-btn {
    justify-content: center;
    min-width: 232px;
}

.pol-spin {
    display: none;
    width: 12px;
    height: 12px;
    flex-shrink: 0;
    border: 2px solid rgba(255, 255, 255, 0.14);
    border-top-color: rgba(255, 255, 255, 0.75);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}

.pol-preview-btn.is-busy .pol-spin { display: inline-block; }

.pol-preview-btn.is-busy {
    opacity: 1;
    color: rgba(255, 255, 255, 0.62);
    cursor: default;
}

@media (prefers-reduced-motion: reduce) {
    .pol-spin { animation: none; }
}

/* Label and its hint are one unit; the label's own line box was holding them
   two lines apart. */
.pol-dial-label { margin-bottom: -5px; }

.pol-info {
    position: relative;
    display: inline-block;
    width: 15px;
    height: 15px;
    margin-left: 6px;
    vertical-align: -2px;
    background-color: rgba(255, 255, 255, 0.4);
    cursor: pointer;
    transition: background-color 0.12s ease;
}

.pol-info:hover { background-color: #fff; }

/* Names what the CURRENT choice does, so the three words above it stop being
   a guess. */
.pol-seg-says {
    margin-top: 8px;
    font-size: 12px;
    line-height: 1.5;
    color: rgba(255, 255, 255, 0.42);
}

/* ── Names in Rubik ───────────────────────────────────────────────────────
   People and communities are named in Rubik; everything around them stays on
   the body face. Rubik ships here as a variable font (100–900), so this costs
   no extra asset — only a face the name renders in. */
.ws-rail-item-name,
.chatlist-contact-header h4,
.chatlist-community-head-name,
.dmsg-author,
#chat-contact,
#profile-name,
#group-overview-name,
.chat-contact {
  font-family: 'Rubik', Inter, Avenir, Helvetica, Arial, sans-serif;
}

/* A span is a number with a unit, not a free-text field. */
.pol-seconds {
    display: flex;
    align-items: center;
    gap: 8px;
}

.pol-seconds-input {
    width: 96px;
    flex: 0 0 auto;
}

.pol-seconds-unit {
    font-size: 12.5px;
    color: rgba(255, 255, 255, 0.42);
}

/* ── Calls ────────────────────────────────────────────────────────────────── */

/* The header's right-hand actions: call, pins, menu, as one row of 40px boxes. */
.chat-header .chat-header-actions {
  position: absolute;
  top: 10px;
  right: 10px;
  display: flex;
  align-items: center;
}

.chat-header .chat-header-actions .call-header-btn,
.chat-header .chat-header-actions .pins-header-btn,
.chat-header .chat-header-actions .nav-menu-btn {
  position: static;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin: 0;
  padding: 0;
  color: #b2b2b2;
}

.call-header-btn svg {
  width: 22px;
  height: 22px;
}

/* The hung-up phone is the phone turned about its own box, so it stays centred. */
.call-glyph-down path {
  transform-box: fill-box;
  transform-origin: center;
  transform: rotate(135deg);
}

/* ── The pill: above every pane, draggable, remembered ── */
.call-pill {
  position: fixed;
  top: calc(var(--chrome-h, 0px) + 10px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 9000;
  display: flex;
  flex-direction: column;
  width: max-content;
  max-width: min(320px, calc(100% - 24px));
  border-radius: 16px;
  /* The relay dialog's surface, so it reads as one of the app's own panels. */
  background: rgba(20, 20, 20, 0.95);
  border: 1px solid #262626;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(10px);
  color: var(--text-color, #f6f6f6);
  overflow: hidden;
  user-select: none;
  -webkit-user-select: none;
  animation: qr-overlay-pop 0.16s ease-out;
}

/* With pictures showing, the pill is a window: wider, and never taller than the screen.
   The panel below the pictures scrolls, without a scrollbar, so a short screen still
   reaches every setting. */
.call-pill-video {
  width: min(480px, calc(100vw - 24px));
  max-width: none;
}

.call-pill {
  max-height: calc(100vh - var(--chrome-h, 0px) - 20px);
}

.call-pill-under {
  visibility: hidden;
}

.call-pictures {
  position: relative;
  flex: none;
  width: 100%;
  max-height: min(50vh, 420px);
  background: #000;
  overflow: hidden;
}

/* Their leading picture is painted by the worker at its own size; CSS fits it. */
.call-picture-lead {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.call-pictures-idle {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-size: 13px;
  color: #b2b2b2;
  pointer-events: none;
}

/* Their camera beside their screen, and our own pictures, sit in the corners. */
.call-picture-inset,
.call-pictures-mine {
  position: absolute;
  bottom: 8px;
  display: flex;
  gap: 6px;
}

.call-picture-inset {
  left: 8px;
  width: 26%;
  max-width: 150px;
  padding: 0;
  border: 1px solid #444;
  border-radius: 8px;
  background: #111;
  overflow: hidden;
  cursor: pointer;
}

.call-picture-inset canvas {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.call-pictures-mine {
  right: 8px;
  flex-direction: row-reverse;
}

.call-picture-self-wrap {
  position: relative;
  display: block;
  width: min(110px, 24vw);
  aspect-ratio: 4 / 3;
  border-radius: 8px;
  border: 1px solid #444;
  background: #111;
  overflow: hidden;
}

.call-picture-self {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: filter 0.2s ease, opacity 0.2s ease;
}

.call-picture-self-screen {
  aspect-ratio: 16 / 10;
}

.call-picture-self-screen .call-picture-self {
  object-fit: contain;
}

/* While the peer is not looking, our pictures fade and wear an eye-off. */
.call-pictures-mine-unseen .call-picture-self {
  filter: grayscale(1);
  opacity: 0.45;
}

/* In the thumbnail's top-right corner; the icon base pins every edge, so all four are set. */
.call-picture-self-wrap .call-picture-unseen {
  position: absolute;
  top: 6px;
  right: 6px;
  bottom: auto;
  left: auto;
  width: 18px;
  height: 18px;
  background-color: #fff;
  opacity: 0;
  transition: opacity 0.2s ease;
  pointer-events: none;
}

.call-pictures-mine-unseen .call-picture-unseen {
  opacity: 0.9;
}

.call-picture-tag {
  position: absolute;
  top: 8px;
  left: 8px;
  padding: 3px 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.55);
  font-size: 11px;
  color: #ddd;
  pointer-events: none;
}

/* Parent-qualified: the button base declares its own position further down. */
.call-pictures .call-picture-max {
  position: absolute;
  top: 8px;
  right: 8px;
  background: rgba(0, 0, 0, 0.55);
}

/* The panel scrolls when the screen is short, with its scrollbar hidden. Its rows
   never give height to make room: a row mid-curtain would be the one squeezed. */
.call-panel {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  scrollbar-width: none;
}

.call-panel > * {
  flex-shrink: 0;
}

.call-panel::-webkit-scrollbar {
  display: none;
}

/* A quality section as one block, so it can slide in and out as a whole. */
.call-panel-block {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* A standing refusal from the system, beside the switch it blocks. */
.call-switch-text {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.call-warn {
  padding: 1px 7px;
  border-radius: 999px;
  border: 1px solid rgba(255, 196, 0, 0.45);
  background: rgba(255, 196, 0, 0.12);
  color: #ffc400;
  font-size: 10.5px;
  font-weight: 600;
  white-space: nowrap;
  cursor: default;
}

/* A switch sitting in the value column of the grid. */
.call-panel .call-switch-inline {
  justify-self: stretch;
  margin: 0;
}

/* What a picture is being sent at, on its section's own line. */
.call-panel-section-note {
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: #8a8a8a;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.call-panel-select {
  justify-self: start;
  align-self: center;
  margin: 0;
  min-width: 96px;
  line-height: 1.2;
  background: #262626;
  color: #fff;
  border: 1px solid #333;
  border-radius: 6px;
  padding: 3px 6px;
  font-size: 12px;
}

.call-panel-button {
  justify-self: start;
  align-self: center;
  margin: 0;
  border: 1px solid #333;
  border-radius: 6px;
  background: #262626;
  color: #fff;
  font-size: 12px;
  padding: 3px 10px;
  cursor: pointer;
}

.call-panel-button:hover {
  background: #333;
}

.call-panel-value-quiet {
  font-size: 12px;
  font-weight: 400;
  color: #b2b2b2;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: block;
}

/* One of their pictures filling the window below the chrome, above the pill. */
.call-max {
  position: fixed;
  top: var(--chrome-h, 0px);
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 9500;
  background: #000;
  user-select: none;
  -webkit-user-select: none;
}

.call-max-canvas {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* The word for the connection, only while it is worth a word. */
.call-max-quality {
  position: absolute;
  top: 12px;
  left: 12px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.6);
  font-size: 12px;
  color: #ddd;
}

/* The way out sits top-right on a solid dark pad, so it reads over any picture, and
   shows itself with the mouse, like the strip below. */
.call-max .call-max-restore {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 32px;
  height: 32px;
  padding: 6px;
  border: 0;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.85);
  color: #fff;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.35s ease;
}

.call-max .call-max-restore svg {
  width: 100%;
  height: 100%;
}

.call-max-controls .call-max-restore {
  opacity: 1;
}

/* Their other picture rides along the bottom and glides down into the strip's place
   as the strip fades; the strip of controls fades in on movement. */
.call-max .call-max-other {
  position: absolute;
  bottom: 16px;
  transition: bottom 0.35s ease;
  left: 50%;
  transform: translateX(-50%);
  width: min(28vw, 320px);
  padding: 0;
  border: 1px solid #555;
  border-radius: 10px;
  background: #111;
  overflow: hidden;
  cursor: pointer;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
}

.call-max-other canvas {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: contain;
}

.call-max-strip {
  position: absolute;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 10px;
  padding: 8px 12px;
  border-radius: 999px;
  background: rgba(20, 20, 20, 0.85);
  border: 1px solid #333;
  opacity: 0;
  transition: opacity 0.35s ease;
}

.call-max-controls .call-max-strip {
  opacity: 1;
}

.call-max-controls .call-max-other {
  bottom: 72px;
}

.call-max-strip svg,
.call-btn-small svg,
.call-pill-row .call-btn svg {
  width: 20px;
  height: 20px;
}

.call-btn:disabled {
  opacity: 0.35;
  cursor: default;
}

.call-btn-small {
  width: 26px;
  height: 26px;
}

/* The header row is the dialog's grey band, open or closed. */
.call-pill-row {
  background: #262626;
  cursor: grab;
  touch-action: none;
}

.call-pill-row:active {
  cursor: grabbing;
}

.call-pill-open .call-pill-row {
  border-bottom: 1px solid #333333;
}

/* The peer's ring: a green that brightens with their voice. */
.call-pill-avatar {
  display: flex;
  flex: none;
  border-radius: 50%;
  box-shadow:
    0 0 0 2px rgba(89, 252, 179, calc(0.12 + var(--talk, 0) * 0.88)),
    0 0 calc(var(--talk, 0) * 12px) rgba(89, 252, 179, calc(var(--talk, 0) * 0.7));
  transition: box-shadow 0.1s linear;
}

.call-pill-open {
  width: 300px;
}

.call-pill {
  transition: width 0.18s ease;
}

/* Only while opening or closing: a drag must follow the pointer exactly. */
.call-pill-sliding {
  transition: width 0.18s ease, left 0.18s ease;
}

.call-pill,
.call-pill-live {
  border-color: #333333;
}

.call-pill-ended {
  opacity: 0.7;
}

.call-pill-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px 6px 6px;
}

.call-pill-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1 1 auto;
  line-height: 1.15;
}

.call-pill-name {
  font-size: 13px;
  font-weight: 600;
}

.call-pill-status {
  font-size: 11.5px;
  color: #b2b2b2;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: center;
  gap: 5px;
}

/* One dot says how the call is going; the words are in the panel. */
.call-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex: none;
}

.call-dot-excellent { background: var(--status-excellent); }
.call-dot-good { background: var(--status-good); }
.call-dot-fair { background: var(--status-fair); }
.call-dot-poor { background: var(--status-poor); }

.call-btn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 34px;
  height: 34px;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.88);
  cursor: pointer;
  padding: 0;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.call-btn:hover {
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
}

.call-btn .icon {
  width: 55%;
  height: 55%;
  background-color: currentColor;
  transition: background-color 0.15s ease;
}

/* On: a wash of the accent under an accent glyph, for masked icons and strokes alike. */
.call-btn-on,
.call-btn-on:hover {
  background: color-mix(in srgb, var(--accent-color, #59fcb3) 22%, rgba(255, 255, 255, 0.04));
  color: var(--accent-color, #59fcb3);
}

.call-btn-on:hover {
  background: color-mix(in srgb, var(--accent-color, #59fcb3) 30%, rgba(255, 255, 255, 0.04));
}

.call-btn-hangup {
  background: var(--danger-color, #FC595C);
}

.call-btn-hangup:hover {
  background: color-mix(in srgb, var(--danger-color, #FC595C) 85%, white);
}

.call-btn-hangup svg {
  width: 20px;
  height: 20px;
}

/* ── The panel under the row ── */
.call-panel {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 6px 14px 14px;
  cursor: default;
}

.call-panel-label {
  font-size: 12.5px;
  color: rgba(255, 255, 255, 0.8);
}

.call-panel-value {
  font-size: 13px;
  color: #fff;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

.call-panel-hint {
  font-size: 11px;
  color: #8a8a8a;
  font-weight: 400;
}

.call-panel-slider {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-top: 8px;
}

.call-panel-slider .call-panel-label {
  flex: none;
  width: 84px;
}

.call-panel-slider .call-panel-value {
  flex: none;
  width: 40px;
  justify-content: flex-end;
}

.call-panel-slider input[type="range"] {
  flex: 1 1 auto;
  min-width: 0;
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  height: 16px;
  margin: 0;
  padding: 0;
  cursor: pointer;
  outline: none;
}

.call-panel-slider input[type="range"]::-webkit-slider-runnable-track {
  height: 8px;
  border-radius: 4px;
  background: linear-gradient(
    to right,
    var(--accent-color, #59fcb3) 0%,
    var(--accent-color, #59fcb3) var(--slider-pct, 50%),
    rgba(255, 255, 255, 0.1) var(--slider-pct, 50%),
    rgba(255, 255, 255, 0.1) 100%
  );
}

.call-panel-slider input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  width: 14px;
  height: 14px;
  margin-top: -3px;
  border-radius: 50%;
  background: #fff;
  border: none;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}

/* Each section starts with its own rule and title, the dialog's way of separating things. */
.call-panel-section {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-color, #59fcb3);
  margin-top: 6px;
  padding-top: 10px;
  border-top: 1px solid #262626;
}

.call-panel-grid {
  display: grid;
  grid-template-columns: 84px 1fr;
  row-gap: 6px;
  align-items: center;
}

.call-panel-grid > .call-panel-label {
  align-self: center;
  line-height: 1.2;
}

.call-graph {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* The tip is placed in the plot's own box, so its percentages match the samples. */
.call-graph-plot {
  position: relative;
}

.call-graph-dot {
  stroke: #fff;
  stroke-width: 6px;
}

/* The readout: a tip inside the chart's top edge, never past its sides, and the
   same words on the legend line beneath. */
.call-graph-tip {
  position: absolute;
  transform: translate(-50%, calc(-100% - 6px));
  padding: 2px 7px;
  border-radius: 6px;
  background: rgba(20, 20, 20, 0.96);
  border: 1px solid rgba(255, 255, 255, 0.14);
  font-size: 11px;
  color: var(--text-color, #f6f6f6);
  white-space: nowrap;
  pointer-events: none;
  font-variant-numeric: tabular-nums;
  z-index: 1;
}

.call-graph-tip b {
  color: var(--accent-color, #59fcb3);
  font-weight: 600;
}

.call-graph-tip-left { transform: translate(0, calc(-100% - 6px)); }
.call-graph-tip-right { transform: translate(-100%, calc(-100% - 6px)); }

.call-graph-readout {
  font-size: 11.5px;
  color: var(--text-color, #f6f6f6);
  font-variant-numeric: tabular-nums;
}

.call-graph-readout b {
  color: var(--accent-color, #59fcb3);
  font-weight: 600;
}

.call-graph svg {
  display: block;
  width: 100%;
  height: 72px;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.04);
}

.call-graph-delay {
  fill: none;
  stroke: var(--accent-color, #59fcb3);
  stroke-width: 1.5;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

.call-graph-lost {
  fill: var(--status-fair);
  opacity: 0.85;
}

.call-graph-legend {
  display: flex;
  justify-content: space-between;
  min-height: 14px;
  font-size: 10.5px;
  color: #8a8a8a;
}

.call-graph-key {
  display: inline-block;
  width: 10px;
  height: 3px;
  margin-right: 5px;
  vertical-align: middle;
  border-radius: 2px;
}

.call-graph-key-delay { background: var(--accent-color, #59fcb3); }
.call-graph-key-lost { background: var(--status-fair); height: 7px; }

.call-graph-empty {
  height: 72px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11.5px;
  color: #8a8a8a;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.04);
}

/* ── The incoming ring: the pop overlay recipe, one card, two round answers ── */
.call-ring-overlay {
  position: fixed;
  inset: 0;
  z-index: 10001;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.7);
  backdrop-filter: blur(8px);
}

.call-ring-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  min-width: 240px;
  padding: 28px 32px 24px;
  border-radius: 20px;
  background: #1b1b1b;
  border: 1px solid rgba(255, 255, 255, 0.08);
  animation: qr-overlay-pop 0.16s ease-out;
}

.call-ring-name {
  margin-top: 8px;
  font-size: 18px;
  font-weight: 600;
  color: #fff;
}

.call-ring-sub {
  font-size: 13px;
  color: #b2b2b2;
}

.call-ring-actions {
  display: flex;
  gap: 40px;
  margin-top: 18px;
}

.call-round {
  width: 60px;
  height: 60px;
  border: 0;
  border-radius: 50%;
  color: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}

.call-round svg {
  width: 28px;
  height: 28px;
}

.call-round-decline {
  background: var(--danger-color, #FC595C);
}

.call-round-accept {
  background: var(--accent-color, #59fcb3);
  color: #111;
  animation: call-ring-pulse 1.2s ease-in-out infinite;
}

@keyframes call-ring-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent-color, #59fcb3) 45%, transparent); }
  50% { box-shadow: 0 0 0 12px transparent; }
}

/* ── Meters and switches ── */
.voice-meter {
  position: relative;
  height: 8px;
  border-radius: 4px;
  background: #1a1a1a;
  overflow: hidden;
}

.voice-meter-fill {
  position: absolute;
  inset: 0;
  border-radius: 4px;
  background: linear-gradient(90deg, #6ed99e, #f7e470 80%, #ea6662);
  transition: clip-path 60ms linear;
}

.voice-meter:not(.voice-meter-active) .voice-meter-fill {
  opacity: 0.35;
}

.call-panel-meters {
  display: grid;
  grid-template-columns: 84px 1fr;
  row-gap: 8px;
  align-items: center;
}

.call-panel-switches {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* The app's neon toggle, at the panel's size. */
.call-panel .call-switch {
  font-size: 12.5px;
  padding: 2px 0;
  margin: 0;
}

.call-panel .call-switch .neon-toggle {
  transform: scale(0.8);
  transform-origin: right center;
}

/* Settings: the Calls section. Its pairs sit side by side while there's room. */
.calls-pair {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 20px;
  margin-bottom: 28px;
}
@media (max-width: 600px) {
  .calls-pair { grid-template-columns: minmax(0, 1fr); }
}
.calls-field {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
  text-align: left;
}
.calls-field-label {
  color: #b2b2b2;
  font-size: 14px;
  line-height: 17px;
}

/* The test button, then the meter across the rest of the row. */
.calls-mic-bench {
  display: flex;
  align-items: center;
  gap: 22px;
  margin-bottom: 16px;
}
.calls-mic-bench .voice-meter {
  flex: 1;
  height: 6px;
  border-radius: 3px;
}
/* One width for both labels, so starting a test moves nothing. */
.calls-mic-test-btn {
  flex: none;
  width: 86px;
  height: 32px;
  padding: 0 16px;
  border: none;
  border-radius: 6px;
  background-color: #333;
  backdrop-filter: none;
  color: #fff;
  font-family: inherit;
  font-size: 12px;
  font-weight: 400;
  cursor: pointer;
  transition: background-color 0.12s ease;
}
.calls-mic-test-btn:hover { background-color: #3d3d3d; }
.calls-mic-live {
  flex: none;
  color: #b2b2b2;
  font-size: 14px;
}

/* A settings slider: a thin track filled to its value, a round white handle. The page
   sets --slider-pct from the value, so the fill needs no script of its own. */
.st-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 20px;
  margin: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
  outline: none;
}
.st-slider::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: 3px;
  background: linear-gradient(to right,
    var(--slider-fill, var(--icon-color-primary)) var(--slider-pct, 0%),
    #1a1a1a var(--slider-pct, 0%));
}
.st-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 20px;
  height: 20px;
  margin-top: -7px;
  border: none;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
}
.st-slider::-moz-range-track {
  height: 6px;
  border-radius: 3px;
  background: linear-gradient(to right,
    var(--slider-fill, var(--icon-color-primary)) var(--slider-pct, 0%),
    #1a1a1a var(--slider-pct, 0%));
}
.st-slider::-moz-range-thumb {
  width: 20px;
  height: 20px;
  border: none;
  border-radius: 50%;
  background: #fff;
}
