/* ==========================================================================
   css/core/loading-animations-tokens.css
   Split from app.html (build 2026.10.08.9-beta), original lines 194-334.
   Rules are in their ORIGINAL order; load order is defined in index.html.
   ========================================================================== */
/* ═══════════════════════════════════════
   LOADING STATES
═══════════════════════════════════════ */
.skeleton {
  background: linear-gradient(100deg, var(--bg-hover) 35%, var(--bg-active) 50%, var(--bg-hover) 65%);
  background-size: 220% 100%;
  border-radius: 4px;
  animation: shimmer 1.4s linear infinite;
}

@keyframes shimmer {
  from { background-position: 120% 0; }
  to   { background-position: -120% 0; }
}
@keyframes hrbeat { 0%,100%{transform:scale(1)} 15%{transform:scale(1.2)} 30%{transform:scale(1)} 45%{transform:scale(1.1)} }

/* -- Shared entrance animations ------------------------------- */
@keyframes menu-in {
  from { opacity: 0; transform: translateY(10px) scale(0.95); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes overlay-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes modal-pop {
  from { opacity: 0; transform: translateY(24px) scale(0.94); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes badge-pop {
  0%   { transform: scale(0.4); opacity: 0; }
  70%  { transform: scale(1.12); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}
/* View-switch motion: channel list eases up on a server change; the message list
   fades in on a channel change. Opacity/short-travel only, so neither fights scrolling. */
@keyframes view-rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes view-fade { from { opacity: 0; } to { opacity: 1; } }
.view-rise { animation: view-rise 0.34s var(--ease-out, ease); }
.view-fade { animation: view-fade 0.3s ease; }
/* --ease-out / --ease-spring are referenced all over this file (modals, menus, badge
   pops, button hovers, the send button, channel-item hover, etc) but were never
   actually defined, so every one of those transitions/animations was silently
   falling back to the flat default `ease` curve. Defining them here retroactively
   upgrades every existing usage at once - no per-element edits needed.
   ease-out: smooth, no-overshoot deceleration - for things settling into place
   (modals opening, menus dropping in, panels sliding).
   ease-spring: slight overshoot then settle - for things that should feel alive
   (button presses, badge pops, hover lifts). */
:root {
  /* Settings (and any full-screen modal) must be OPAQUE.
     --bg-deep / --bg-void are translucent SCRIMS under the custom theme - panels are
     meant to be see-through over the gradient wallpaper - so Settings ended up showing
     the whole app through itself. Transparency there is only wanted for a real custom
     BACKGROUND (html[data-bg]) or the glass theme, never for a custom gradient theme,
     where the reference app uses a solid tinted surface.
     Default to the existing tokens so nothing changes on the stock themes; the custom
     theme painter overrides these with opaque mixes, and the two cases that SHOULD stay
     see-through put them back below. */
  --bg-modal:      var(--bg-deep);
  --bg-modal-side: var(--bg-void);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  /* App-wide default typeface. Rift Sans (brand font, loaded via @font-face above)
     for everything; JetBrains Mono for code. Defined here (after GlobalRiftCss.css)
     so it wins, and so the Appearance font picker can override --font-body /
     --font-display at runtime. Outfit stays as a fallback in the stack in case the
     font file fails to load, then system-ui after that. "Classic" restores
     DM Sans / Space Grotesk. */
  --font-body: 'Rift Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-display: 'Rift Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', 'Fira Code', ui-monospace, monospace;
  /* Popup redesign: the crisp separator that rings every popup INSTEAD of a drop shadow. */
  --popup-edge: rgba(255,255,255,.30);
}
html[data-theme="light"] { --popup-edge: rgba(0,0,0,.28); }
/* ── Flat popups ──────────────────────────────────────────────────────────────
   Gavin: popups shouldn't cast a shadow. Kill the drop shadow on every shared
   modal / menu / picker / popover and give it a clear separator border on all four
   edges so it stands out from the page on its own. !important so it also beats the
   inline box-shadows on the JS-built popups. */
.ctx-menu, .stm-menu, .stm-sub, .ch-modal, .poll-modal, .vmm, .rxm, .fp-modal,
.group-dm-modal, .wall-composer, .emoji-picker, .mention-dropdown, .profile-popup,
.dmp, .toast, #agPickerPopover, #profileMoreMenu, #reactionTooltip {
  box-shadow: none !important;
  /* A normal separator (matches the app's other dividers) - not a bright ring. */
  border: 1px solid var(--border-strong) !important;
}
/* Streamer Mode: blur private things until the streamer hovers them, so they never leak
   on stream. Only active when body.streamer-mode is set (toggle in Gaming settings). */
body.streamer-mode .priv-blur,
body.streamer-mode .webhook-url,
body.streamer-mode #sessionsList { filter: blur(7px); transition: filter .12s; cursor: default; -webkit-user-select: none; user-select: none; }
body.streamer-mode .priv-blur:hover,
body.streamer-mode .webhook-url:hover,
body.streamer-mode #sessionsList:hover { filter: none; -webkit-user-select: auto; user-select: auto; }
/* DMs are private - blur message text + attachments (hover reveals for the streamer).
   Two DM surfaces: the main area when a DM is open, and the floating DM popout. */
body.streamer-mode.dm-open #messagesArea .msg-content,
body.streamer-mode.dm-open #messagesArea .msg-attachment,
body.streamer-mode #dmPopout .dmp-bubble,
body.streamer-mode #dmPopout .dmp-li-name { filter: blur(6px); transition: filter .12s; cursor: default; }
body.streamer-mode.dm-open #messagesArea .msg-content:hover,
body.streamer-mode.dm-open #messagesArea .msg-attachment:hover,
body.streamer-mode #dmPopout .dmp-bubble:hover,
body.streamer-mode #dmPopout .dmp-li-name:hover { filter: none; }
/* Thin, low-profile scrollbars for the main scroll panes - was the browser default
   (thick, always-on-grey) since none of these had custom scrollbar styling. */
.channel-list, .messages-area, .members-list {
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
.channel-list::-webkit-scrollbar, .messages-area::-webkit-scrollbar, .members-list::-webkit-scrollbar {
  width: 8px;
}
.channel-list::-webkit-scrollbar-track, .messages-area::-webkit-scrollbar-track, .members-list::-webkit-scrollbar-track {
  background: transparent;
}
.channel-list::-webkit-scrollbar-thumb, .messages-area::-webkit-scrollbar-thumb, .members-list::-webkit-scrollbar-thumb {
  background-color: var(--border-strong);
  background-clip: padding-box;
  border: 2px solid transparent;
  border-radius: 8px;
}
.channel-list::-webkit-scrollbar-thumb:hover, .messages-area::-webkit-scrollbar-thumb:hover, .members-list::-webkit-scrollbar-thumb:hover {
  background-color: var(--text-muted);
  background-clip: padding-box;
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

.loading-bar {
  height: 12px;
  margin-bottom: 8px;
}

