/* ==========================================================================
   Premium UI — cards, FAB, skeleton loaders, page transitions, particle
   backdrop, and a few accessibility primitives. Loaded globally.

   Performance notes (see README Phase 12 for the full writeup):
   - Every animation here drives only `transform` and `opacity` — the two
     properties a browser can animate on the compositor without triggering
     layout or paint. Nothing here animates width/height/top/left/box-shadow.
   - `will-change` is applied narrowly (via a class added only while an
     element is actually animating, e.g. .fab:active) rather than globally,
     since an always-on will-change wastes GPU memory on layers that never
     move.
   - `contain: content` is used on repeating, independent UI (skeleton rows,
     leaderboard rows) so the browser can skip recalculating layout for
     siblings when one row's content changes size.
   ========================================================================== */

/* ------------------------------ Accessibility ------------------------------ */

.skip-link {
  position: fixed;
  top: -60px; left: var(--space-4);
  z-index: var(--z-page-transition);
  padding: 10px 18px;
  border-radius: var(--radius-sm);
  background: var(--color-gold);
  color: #241300;
  font-weight: 700;
  font-size: var(--fs-sm);
  text-decoration: none;
  transition: top var(--dur-fast) var(--ease-out);
}
.skip-link:focus { top: var(--space-4); }

/* ------------------------------- Better cards ------------------------------ */

/* Applied automatically to every existing .glass-card — no markup changes
   needed anywhere else in the app. Adds real depth (layered shadow instead
   of one flat blur) and a hover/press response only where a card is
   actually clickable (.glass-card--interactive, or any <a>/<button> that
   wraps one). */
.glass-card {
  box-shadow: var(--shadow-md);
  transition: transform var(--dur-base) var(--ease-spring), box-shadow var(--dur-base) var(--ease-out), border-color var(--dur-base);
}

.glass-card--interactive,
a.glass-card, button.glass-card {
  cursor: pointer;
}
.glass-card--interactive:hover,
a.glass-card:hover, button.glass-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-lg);
  border-color: rgba(242, 183, 5, 0.35);
}
.glass-card--interactive:active,
a.glass-card:active, button.glass-card:active {
  transform: translateY(-1px) scale(0.99);
}

/* A featured/hero card gets the layered mesh glow instead of a flat fill —
   reserved for the one or two highest-emphasis cards per page (progression
   hero, a game's "you won" card) rather than applied everywhere, so it
   still reads as special. */
.glass-card--featured {
  position: relative;
  overflow: hidden;
  border-color: rgba(242, 183, 5, 0.3);
  box-shadow: var(--shadow-lg), var(--shadow-glow-gold);
}
.glass-card--featured::before {
  content: "";
  position: absolute; inset: 0;
  background: var(--gradient-mesh);
  pointer-events: none;
}
.glass-card--featured > * { position: relative; z-index: 1; }

/* A thin animated gold edge for a single "this one matters most" card
   (e.g. the daily reward card when unclaimed). Opt-in via a class so it
   never fires on cards that don't want the attention. */
.glass-card--pulse-border {
  animation: card-border-pulse 2.4s ease-in-out infinite;
}
@keyframes card-border-pulse {
  0%, 100% { box-shadow: var(--shadow-md), 0 0 0 0 rgba(242,183,5,0); }
  50%      { box-shadow: var(--shadow-md), 0 0 0 4px rgba(242,183,5,0.18); }
}

/* --------------------------- Floating action button ------------------------ */

.fab {
  position: fixed;
  right: calc(var(--space-5) + var(--safe-right, 0px));
  bottom: calc(var(--nav-height) + var(--space-5) + var(--safe-bottom));
  z-index: var(--z-fab);
  width: 56px; height: 56px;
  border-radius: 50%;
  display: grid; place-items: center;
  background: var(--gradient-gold);
  color: #241300;
  border: none;
  box-shadow: var(--shadow-lg), var(--shadow-glow-gold);
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-spring), opacity var(--dur-base), box-shadow var(--dur-base);
}
.fab .material-symbols-rounded { font-size: 26px; }
.fab:hover { box-shadow: var(--shadow-lg), 0 8px 36px rgba(242,183,5,0.42); }
.fab:active { transform: scale(0.9); }

/* Entrance/exit for a FAB that appears conditionally (e.g. scroll-to-top) */
.fab[hidden] { display: grid; opacity: 0; pointer-events: none; transform: scale(0.6) translateY(12px); }
.fab {
  animation: fab-in var(--dur-slow) var(--ease-spring) both;
}
@keyframes fab-in {
  0%   { opacity: 0; transform: scale(0.5) translateY(16px) rotate(-20deg); }
  100% { opacity: 1; transform: scale(1) translateY(0) rotate(0); }
}

/* A second, smaller FAB stacks above the first with a fixed gap, for pages
   that want two quick actions (rare — used sparingly). */
.fab-mini {
  width: 44px; height: 44px;
  bottom: calc(var(--nav-height) + var(--space-5) + 68px + var(--safe-bottom));
}

/* -------------------------------- Skeletons -------------------------------- */

.skeleton-block {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--glass-fill);
  contain: content;
}
.skeleton-block::after {
  content: "";
  position: absolute; inset: 0;
  background: var(--gradient-shimmer);
  transform: translateX(-100%);
  animation: skeleton-shimmer 1.5s ease-in-out infinite;
}
@keyframes skeleton-shimmer {
  100% { transform: translateX(100%); }
}

.skeleton-text { height: 14px; margin-bottom: 8px; border-radius: 6px; }
.skeleton-text.w-60 { width: 60%; }
.skeleton-text.w-40 { width: 40%; }
.skeleton-text.w-80 { width: 80%; }
.skeleton-avatar { width: 40px; height: 40px; border-radius: 50%; flex-shrink: 0; }

.skeleton-row {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) 0;
}
.skeleton-row .skeleton-lines { flex: 1; min-width: 0; }

.skeleton-card { height: 96px; border-radius: var(--radius-md); margin-bottom: var(--space-3); }

/* Fade the real content in and let the skeleton fade out, rather than an
   abrupt swap — the handoff itself should feel considered. */
.skeleton-fade-out { animation: skeleton-fade-out var(--dur-base) var(--ease-out) forwards; }
@keyframes skeleton-fade-out { to { opacity: 0; } }
.content-fade-in { animation: fade-in var(--dur-slow) var(--ease-out) both; }

/* ----------------------------- Page transitions ----------------------------- */

/* Progressive enhancement for browsers that support native cross-document
   view transitions (Chrome 126+). Inert (silently ignored) everywhere else,
   so it costs nothing to always include — the JS-driven veil below is what
   covers every other browser. */
@view-transition {
  navigation: auto;
}

/* The JS fallback veil (see page-transitions.js): a full-screen fade that
   plays for ~140ms before an internal link navigates away, so the old page
   doesn't just vanish. Browsers with native view-transition support paint
   over this so fast it's imperceptible — no double-animation in practice. */
.page-transition-veil {
  position: fixed; inset: 0;
  z-index: var(--z-page-transition);
  background: var(--color-bg-deep);
  opacity: 0;
  pointer-events: none;
  transition: opacity 140ms var(--ease-out);
}
.page-transition-veil.active { opacity: 1; pointer-events: all; }

/* A slightly richer entrance than the existing plain fade — content settles
   in from a few pixels down with a soft scale, still cheap (transform+opacity). */
.page-enter-rich {
  animation: page-enter-rich var(--dur-slow) var(--ease-out) both;
}
@keyframes page-enter-rich {
  0%   { opacity: 0; transform: translateY(10px) scale(0.99); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}

/* ------------------------------ Particle canvas ----------------------------- */

.particle-canvas {
  position: fixed; inset: 0;
  z-index: var(--z-particles);
  pointer-events: none;
  /* The canvas element itself never resizes via CSS — particles.js sets
     width/height to match the viewport in device pixels directly, which is
     cheaper than letting the browser rescale a CSS-sized canvas. */
}

/* Respect reduced motion: base.css already freezes CSS animations globally,
   but particles.js also checks this media query in JS before ever starting
   its rAF loop, so no canvas work happens at all rather than just being
   invisible. This selector exists as a CSS-only backstop in case the canvas
   was already injected before JS could check. */
@media (prefers-reduced-motion: reduce) {
  .particle-canvas { display: none; }
}
