/* =============================================================
   widget.css — standalone OBS / TikTok overlay (Shell C)
   Always dark, transparent background. Depends on tokens.css.
   ============================================================= */
html, body { margin: 0; padding: 0; height: 100%; background: transparent !important; overflow: hidden; }
body { font-family: var(--font-body); -webkit-font-smoothing: antialiased; }

.npw { display: flex; flex-direction: column; gap: 8px; padding: 12px; box-sizing: border-box; width: 100%;
       transition: opacity 200ms var(--ease-default); }
.npw[hidden] { display: none; }
/* Song-change animation wrapper: holds the full widget content (main + progress)
   and carries the in/out animation, so the whole widget animates while the root
   keeps padding/background/overflow and the art pulse stays independent. */
.npw__anim { display: flex; flex-direction: column; gap: 8px; }
.npw__main { display: flex; align-items: center; gap: 12px; }
.npw__art { width: 56px; height: 56px; flex-shrink: 0; border-radius: 8px; background: #2a2a2a center/cover no-repeat;
            box-shadow: 0 0 0 0 var(--np-accent, var(--color-brand-primary));
            animation: np-art-pulse 2000ms var(--ease-default) infinite; position: relative; overflow: hidden; }
.npw__art[data-shape="circle"] { border-radius: 50%; }
.npw__art[data-shape="square"] { border-radius: 0; }
.npw__meta { flex: 1; min-width: 0; overflow: hidden; }
.npw__title { color: #fff; font-weight: 700; font-size: 16px; white-space: nowrap; overflow: hidden;
              text-overflow: ellipsis; text-shadow: 0 1px 3px rgba(0,0,0,0.6); }
.npw__artist { color: #C9CDD3; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
               text-shadow: 0 1px 3px rgba(0,0,0,0.6); }
.npw__badge { flex-shrink: 0; display: inline-flex; align-self: flex-start; margin-top: 2px; }
.npw__progress { height: 4px; border-radius: 99px; background: rgba(255,255,255,0.18); overflow: hidden; }
.npw__bar { height: 100%; width: 0%; background: var(--np-accent, var(--color-brand-primary)); border-radius: 99px;
            transition: width 1s linear; }
.npw__anim { animation: np-song-fade 400ms var(--ease-out); }

/* -------------------------------------------------------------
   Song-change animations (Shell C). The live widget toggles
   .npw--anim-out then .npw--anim-in on [data-npw-anim] when the
   track changes. Durations come from --np-anim-in / --np-anim-out
   (default 500/350ms) and the chosen style from a per-style class
   (.npw-a-<style>) set by widget.js, defaulting to fade. Shipped
   here so animations work out of the box without re-saving a
   style; the Style Builder still emits a self-contained copy for
   pasting into OBS Custom CSS. */
[data-npw-anim] {
  --np-anim-in: 500ms;
  --np-anim-out: 350ms;
}
[data-npw-anim].npw--anim-in { animation: np-in-fade var(--np-anim-in) var(--ease-out) both; }
[data-npw-anim].npw--anim-out { animation: np-out-fade var(--np-anim-out) var(--ease-in) both; }

.npw-a-fade.npw--anim-in { animation-name: np-in-fade; }
.npw-a-fade.npw--anim-out { animation-name: np-out-fade; }
.npw-a-slide-left.npw--anim-in { animation-name: np-in-slide-left; }
.npw-a-slide-left.npw--anim-out { animation-name: np-out-slide-left; }
.npw-a-slide-right.npw--anim-in { animation-name: np-in-slide-right; }
.npw-a-slide-right.npw--anim-out { animation-name: np-out-slide-right; }
.npw-a-slide-up.npw--anim-in { animation-name: np-in-slide-up; }
.npw-a-slide-up.npw--anim-out { animation-name: np-out-slide-up; }
.npw-a-slide-down.npw--anim-in { animation-name: np-in-slide-down; }
.npw-a-slide-down.npw--anim-out { animation-name: np-out-slide-down; }
.npw-a-pop.npw--anim-in { animation-name: np-in-pop; }
.npw-a-pop.npw--anim-out { animation-name: np-out-pop; }
.npw-a-zoom.npw--anim-in { animation-name: np-in-zoom; }
.npw-a-zoom.npw--anim-out { animation-name: np-out-zoom; }
.npw-a-flip.npw--anim-in { animation-name: np-in-flip; }
.npw-a-flip.npw--anim-out { animation-name: np-out-flip; }
.npw-a-blur.npw--anim-in { animation-name: np-in-blur; }
.npw-a-blur.npw--anim-out { animation-name: np-out-blur; }

@keyframes np-in-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes np-out-fade { from { opacity: 1; } to { opacity: 0; } }
/* Slide names follow the common convention (Animate.css): the NEW track enters
   from the named edge — left/right = that side, up = from the bottom moving up,
   down = from the top moving down. The old track exits toward the opposite edge. */
@keyframes np-in-slide-left { from { opacity: 0; transform: translateX(-40px); } to { opacity: 1; transform: none; } }
@keyframes np-out-slide-left { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateX(40px); } }
@keyframes np-in-slide-right { from { opacity: 0; transform: translateX(40px); } to { opacity: 1; transform: none; } }
@keyframes np-out-slide-right { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateX(-40px); } }
@keyframes np-in-slide-up { from { opacity: 0; transform: translateY(40px); } to { opacity: 1; transform: none; } }
@keyframes np-out-slide-up { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(-40px); } }
@keyframes np-in-slide-down { from { opacity: 0; transform: translateY(-40px); } to { opacity: 1; transform: none; } }
@keyframes np-out-slide-down { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(40px); } }
@keyframes np-in-pop { from { opacity: 0; transform: scale(.7); } to { opacity: 1; transform: scale(1); } }
@keyframes np-out-pop { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(.7); } }
@keyframes np-in-zoom { from { opacity: 0; transform: scale(1.3); } to { opacity: 1; transform: scale(1); } }
@keyframes np-out-zoom { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(1.3); } }
@keyframes np-in-flip { from { opacity: 0; transform: perspective(400px) rotateX(90deg); } to { opacity: 1; transform: perspective(400px) rotateX(0); } }
@keyframes np-out-flip { from { opacity: 1; transform: perspective(400px) rotateX(0); } to { opacity: 0; transform: perspective(400px) rotateX(-90deg); } }
@keyframes np-in-blur { from { opacity: 0; filter: blur(8px); } to { opacity: 1; filter: blur(0); } }
@keyframes np-out-blur { from { opacity: 1; filter: blur(0); } to { opacity: 0; filter: blur(8px); } }

/* idle / error states */
.npw-state { display: flex; align-items: center; gap: 10px; padding: 14px 16px; font-size: 14px;
             color: #9CA3AF; background: rgba(15,15,15,0.6); border-radius: 8px; }
.npw-state--error { color: var(--color-error-500); }
.npw-state[hidden] { display: none; }

/* Listening-history widget (3rd type). Style Builder overrides these. */
.npw-hist { width: 100%; box-sizing: border-box; padding: 12px; }
.npw-hist[hidden] { display: none; }
.npw-hist__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.npw-hist__item { display: flex; align-items: center; gap: 10px; }
.npw-hist__art { width: 40px; height: 40px; flex-shrink: 0; border-radius: 8px; background: #2a2a2a center/cover no-repeat; }
.npw-hist__meta { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.npw-hist__badge { flex-shrink: 0; display: inline-flex; align-items: center; opacity: .9; }
.npw-hist__badge svg { display: block; }
.npw-hist__title { color: #fff; font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.npw-hist__artist { color: #C9CDD3; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* TikTok ticker variant */
.npw-tt { display: flex; align-items: center; gap: 10px; height: 60px; padding: 0 12px; box-sizing: border-box;
          background: rgba(1,1,1,0.55); border-radius: var(--radius-lg);
          border: 1.5px solid var(--color-platform-tiktok-accent); }
.npw-tt__art { width: 40px; height: 40px; border-radius: 8px; flex-shrink: 0; background: #2a2a2a center/cover no-repeat; }
.npw-tt__scroll { flex: 1; overflow: hidden; white-space: nowrap; }
.npw-tt__text { display: inline-block; color: #fff; font-weight: 700; font-size: 15px;
                text-shadow: 0 1px 3px rgba(0,0,0,0.6); }
.npw-tt__badge { flex-shrink: 0; display: inline-flex; }

/* RGB / rainbow mode — cycles the accent hue on the progress bar and the art
   glow ring (not the album image/text). Added by a Style Builder preset via
   .npw--rgb / .npw-tt--rgb. The bar starts red so the hue sweep covers the
   full spectrum; the art keeps its image and only its glow ring animates. */
@keyframes np-rgb-bar { to { filter: hue-rotate(360deg); } }
@keyframes np-rgb-glow {
  0%   { box-shadow: 0 0 10px 1px hsl(0 100% 55%); }
  25%  { box-shadow: 0 0 10px 1px hsl(90 100% 55%); }
  50%  { box-shadow: 0 0 10px 1px hsl(180 100% 55%); }
  75%  { box-shadow: 0 0 10px 1px hsl(270 100% 55%); }
  100% { box-shadow: 0 0 10px 1px hsl(360 100% 55%); }
}
.npw--rgb .npw__bar, .npw-tt--rgb .npw-tt__bar { background: #ff0000; animation: np-rgb-bar 6s linear infinite; }
.npw--rgb .npw__art, .npw-tt--rgb .npw-tt__art { animation: np-rgb-glow 6s linear infinite; }
.npw--rgb { border-image: linear-gradient(90deg,#ff0000,#ffea00,#00ff66,#00e5ff,#7c4dff,#ff00d4,#ff0000) 1; }

/* NOTE: the widget is an opt-in decorative streaming overlay shown to a stream's
   audience — the album-art pulse and the song-change animation are explicitly
   configured by the streamer in the Style Builder. They must therefore run
   regardless of the machine's "reduce motion" OS setting (which would otherwise
   silently kill the pulse + animation in OBS/CEF). So we do NOT gate them behind
   prefers-reduced-motion. */
