/* ===========================================================================
 * graph-theme.css — "Verse Island at dusk" atmospheric backdrop for the
 * Knowledge Graph Explorer. Scoped to the explorer page (markup lives in
 * graph.html); the force-graph canvas sits transparent on top of this scene.
 *
 * Layers (back -> front):
 *   1. dusk sky gradient (.vc-scene::before)
 *   2. parallax hill / floating-island silhouettes (inline SVG)
 *   3. drifting battle-bus easter egg (inline SVG + keyframes)
 *   4. low mist band
 *   5. ember/particle <canvas> (rAF, capped)  -> driven from graph.js
 *   6. center vignette to keep nodes legible
 *   7. the force-graph canvas (#graph-canvas) — transparent
 *
 * Light + dark via the .dark class on <html>.
 * ========================================================================= */

/* The graph panel becomes the stacking + clipping context for the scene. */
.vc-graph-panel {
  position: relative;
  isolation: isolate;
  background: transparent !important;
}

/* ---- the layered scene container (absolutely fills the panel) ---- */
.vc-scene {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;
}

/* 1 — sky gradient.
 *   LIGHT  = bright DAY island: clear blue sky, high white haze.
 *   DARK   = dusk / sunset island (the original mood). */
.vc-scene::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 80% at 80% 8%, rgba(255, 255, 245, 0.85) 0%, rgba(255, 255, 245, 0) 46%),
    linear-gradient(180deg,
      #5cb8f5 0%,
      #7cc8f8 26%,
      #a6dcf9 52%,
      #cdeefb 76%,
      #e8f7fd 100%);
  transition: opacity 0.4s ease;
}
.dark .vc-scene::before {
  /* Verse Island ocean-night sky (UEFN-282): teal/cyan dusk with a warm
     amber sunrise glow on the horizon — matches the site's ink palette. */
  background:
    radial-gradient(120% 80% at 78% 10%, rgba(245, 158, 11, 0.22) 0%, rgba(245, 158, 11, 0) 52%),
    radial-gradient(90% 60% at 18% 6%, rgba(34, 211, 238, 0.18) 0%, rgba(34, 211, 238, 0) 55%),
    linear-gradient(180deg,
      #102338 0%,
      #0e1d31 24%,
      #0d1a2c 48%,
      #0b1626 74%,
      #0a1422 100%);
}

/* the sun / glow orb. LIGHT = bright high sun; DARK = low setting sun. */
.vc-sun {
  position: absolute;
  top: 7%;
  right: 14%;
  width: 96px;
  height: 96px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
    rgba(255, 255, 255, 1) 0%,
    rgba(255, 250, 220, 0.98) 30%,
    rgba(255, 240, 170, 0.7) 55%,
    rgba(255, 236, 150, 0) 76%);
  filter: blur(0.3px);
  z-index: 0;
}
.dark .vc-sun {
  top: 9%;
  right: 16%;
  width: 78px;
  height: 78px;
  background: radial-gradient(circle at 50% 50%,
    rgba(255, 226, 188, 0.9) 0%,
    rgba(255, 158, 110, 0.7) 40%,
    rgba(214, 110, 150, 0.25) 66%,
    rgba(214, 110, 150, 0) 80%);
}

/* 2 — parallax silhouette layers */
.vc-layer {
  position: absolute;
  left: 0;
  width: 100%;
  bottom: 0;
  z-index: 0;
}
.vc-layer svg { display: block; width: 100%; height: auto; }

/* far floating islands (highest, palest, slow horizontal drift) */
.vc-islands {
  top: 30%;
  bottom: auto;
  opacity: 0.55;
  animation: vc-drift-x 90s ease-in-out infinite alternate;
}
.dark .vc-islands { opacity: 0.45; }

/* mid hills */
.vc-hills-far  { bottom: 0; opacity: 0.7;  }
.vc-hills-mid  { bottom: 0; opacity: 0.85; }
.vc-hills-near { bottom: 0; opacity: 1;    }

/* ---- DAY palette (light mode): vivid green hills + soft sky-tinted islands.
 * CSS `fill` overrides the inline presentation `fill` attributes in the SVGs,
 * so we recolor the scene without touching markup. Dark mode keeps the
 * original purple dusk fills baked into graph.html. ---- */
.vc-islands g:nth-of-type(1) path { fill: #b8e0a8; }
.vc-islands g:nth-of-type(2) path { fill: #cdeab8; }
.vc-islands g:nth-of-type(3) path { fill: #def2cc; }
.vc-hills-far  path { fill: #8fd07a; }
.vc-hills-mid  path { fill: #5fb35a; }
.vc-hills-near path { fill: #3f9444; }

/* Dark mode: restore the dusk silhouettes. */
.dark .vc-islands g:nth-of-type(1) path { fill: #2f7d8c; }
.dark .vc-islands g:nth-of-type(2) path { fill: #3a93a0; }
.dark .vc-islands g:nth-of-type(3) path { fill: #4fb0b8; }
.dark .vc-hills-far  path { fill: #245f73; }
.dark .vc-hills-mid  path { fill: #1c4d61; }
.dark .vc-hills-near path { fill: #143a4d; }

@keyframes vc-drift-x {
  from { transform: translateX(-2%); }
  to   { transform: translateX(3%); }
}

/* day clouds — light mode only, slow drift; hidden at dusk */
.vc-clouds {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 0;
  opacity: 0.95;
  filter: drop-shadow(0 6px 8px rgba(120, 170, 210, 0.18));
  animation: vc-drift-x 120s ease-in-out infinite alternate;
}
.vc-clouds svg { display: block; width: 100%; height: auto; }
.dark .vc-clouds { display: none; }

/* 4 — low mist band hovering over the hills */
.vc-mist {
  position: absolute;
  left: -10%;
  right: -10%;
  bottom: 12%;
  height: 34%;
  z-index: 0;
  background: linear-gradient(180deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.35) 55%,
    rgba(255, 255, 255, 0.55) 100%);
  filter: blur(14px);
  opacity: 0.6;
  animation: vc-mist-drift 40s ease-in-out infinite alternate;
}
.dark .vc-mist {
  background: linear-gradient(180deg,
    rgba(120, 110, 170, 0) 0%,
    rgba(120, 110, 170, 0.25) 55%,
    rgba(90, 80, 140, 0.4) 100%);
  opacity: 0.45;
}
@keyframes vc-mist-drift {
  from { transform: translateX(-4%) translateY(0); }
  to   { transform: translateX(4%) translateY(-6px); }
}

/* 5 — ember / particle canvas (filled by graph.js) */
.vc-particles {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

/* 6 — center vignette: darken the edges so the graph nodes pop */
.vc-vignette {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(120% 100% at 50% 45%,
    rgba(255, 255, 255, 0.30) 0%,
    rgba(255, 255, 255, 0) 38%,
    rgba(40, 90, 140, 0.06) 82%,
    rgba(30, 70, 120, 0.14) 100%);
}
.dark .vc-vignette {
  background: radial-gradient(120% 100% at 50% 45%,
    rgba(40, 30, 70, 0.0) 0%,
    rgba(20, 14, 40, 0.18) 55%,
    rgba(8, 5, 20, 0.45) 100%);
}

/* 7 — keep the force-graph canvas above the scene */
#graph-canvas { position: absolute; inset: 0; z-index: 1; }

/* ---- 3 — drifting Fortnite battle bus easter egg ---- */
.vc-bus {
  position: absolute;
  top: 14%;
  left: -180px;
  width: 132px;
  z-index: 0;
  opacity: 0.92;
  will-change: transform;
  /* long pause off-screen, then a slow cross every ~48s */
  animation: vc-bus-cross 48s linear infinite;
}
.vc-bus svg { display: block; width: 100%; height: auto; }
@keyframes vc-bus-cross {
  0%       { transform: translate(0, 0) rotate(-2deg); }
  6%       { transform: translate(8vw, -6px) rotate(-1deg); }
  44%      { transform: translate(64vw, 10px) rotate(1deg); }
  50%      { transform: translate(calc(100vw + 200px), 0) rotate(-1deg); }
  100%     { transform: translate(calc(100vw + 200px), 0) rotate(-1deg); }
}

/* ---- floating UI controls on the graph panel ---- */
.vc-graph-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  border-radius: 0.55rem;
  background: rgba(255, 255, 255, 0.72);
  color: #3f3f46;
  border: 1px solid rgba(255, 255, 255, 0.6);
  box-shadow: 0 2px 10px rgba(40, 25, 60, 0.16);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: background 0.15s ease, transform 0.1s ease, color 0.15s ease;
  cursor: pointer;
}
.vc-graph-btn:hover { background: rgba(255, 255, 255, 0.92); transform: translateY(-1px); }
.vc-graph-btn:active { transform: translateY(0); }
.dark .vc-graph-btn {
  background: rgba(30, 22, 48, 0.6);
  color: #e4e4e7;
  border-color: rgba(140, 120, 190, 0.35);
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);
}
.dark .vc-graph-btn:hover { background: rgba(48, 36, 72, 0.85); }

/* hover preview card — richer, glassy */
#graph-hover {
  width: 19rem;
  background: rgba(255, 255, 255, 0.94);
  border: 1px solid rgba(255, 255, 255, 0.7);
  box-shadow: 0 14px 40px rgba(40, 25, 60, 0.28);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition: opacity 0.12s ease;
}
.dark #graph-hover {
  background: rgba(26, 19, 42, 0.94);
  border-color: rgba(140, 120, 190, 0.4);
  box-shadow: 0 14px 46px rgba(0, 0, 0, 0.55);
}

/* ===========================================================================
 * Detail pop-out overlay
 * ---------------------------------------------------------------------------
 * The detail panel is a slide-in overlay rather than a fixed grid column. The
 * graph canvas always uses the full container width; this panel slides over the
 * right edge only when a node is selected, and the × button closes it (which
 * returns the graph to full width — graph.js resizes + re-fits on each toggle).
 *
 *   Desktop (>=1024px): absolute, right edge of the graph wrapper, slides in
 *                       from the right.
 *   Mobile  (<1024px) : fixed bottom-sheet, slides up from the bottom.
 *   Large window (.vc-maximized active): the overlay becomes fixed so it floats
 *                       over the maximized graph (which is itself fixed/viewport).
 * ========================================================================= */
.vc-detail-overlay {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(420px, 92%);
  z-index: 30;
  border-radius: 0.75rem;
  border-top-right-radius: 0.75rem;
  border-bottom-right-radius: 0.75rem;
  box-shadow: -18px 0 48px rgba(40, 25, 60, 0.22);
  transition: transform 0.28s cubic-bezier(0.22, 1, 0.36, 1),
              opacity 0.2s ease;
  transform: translateX(0);
  opacity: 1;
}
.dark .vc-detail-overlay {
  box-shadow: -18px 0 52px rgba(0, 0, 0, 0.5);
}
/* Closed: slid fully off the right edge and inert. */
.vc-detail-overlay.closed {
  transform: translateX(calc(100% + 24px));
  opacity: 0;
  pointer-events: none;
}

/* In large-window mode the panel itself is fixed/full-viewport, so the overlay
 * (absolute within it) already fills the panel's right edge — no extra rules
 * needed. Square off the right corners so it meets the viewport edge cleanly. */
body.vc-maximized-lock .vc-detail-overlay {
  border-radius: 0;
}

/* Mobile: bottom-sheet that slides up from the bottom of the viewport.
 * `position: fixed` escapes the panel's overflow:hidden (no transformed
 * ancestor), pinning the sheet to the viewport bottom in both normal and
 * large-window modes. */
@media (max-width: 1023px) {
  .vc-detail-overlay {
    position: fixed;
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    height: auto;
    max-height: 78vh;
    border-radius: 1rem 1rem 0 0;
    box-shadow: 0 -14px 44px rgba(40, 25, 60, 0.28);
    transform: translateY(0);
  }
  .dark .vc-detail-overlay { box-shadow: 0 -14px 48px rgba(0, 0, 0, 0.55); }
  .vc-detail-overlay.closed {
    transform: translateY(calc(100% + 24px));
  }
  body.vc-maximized-lock .vc-detail-overlay {
    border-radius: 1rem 1rem 0 0;
  }
}

/* ---- native fullscreen mode ---- */
.vc-graph-panel:fullscreen,
.vc-graph-panel:-webkit-full-screen {
  width: 100vw;
  height: 100vh;
  border-radius: 0;
  border: none;
}
.vc-graph-panel:fullscreen #graph-canvas,
.vc-graph-panel:-webkit-full-screen #graph-canvas { inset: 0; }

/* ---- CSS "maximize" mode — fills the viewport over the page chrome without
 * relying on the Fullscreen API (which needs a user gesture and silently fails
 * in some contexts). Toggled by graph.js via the .vc-maximized class. ---- */
.vc-graph-panel.vc-maximized {
  position: fixed !important;
  inset: 0 !important;
  z-index: 9999 !important;
  width: 100vw !important;
  height: 100vh !important;
  min-height: 0 !important;
  max-height: none !important;
  border-radius: 0 !important;
  border: none !important;
  margin: 0 !important;
}
.vc-graph-panel.vc-maximized #graph-canvas { inset: 0; }
/* Lock background scroll while maximized. */
body.vc-maximized-lock { overflow: hidden; }

/* ---- Battle Bus daily credit drop (UEFN-301) ---- */
.vc-battlebus {
  position: absolute;
  z-index: 25;
  left: 0.75rem;
  bottom: 0.75rem;
  width: 84px;
  height: 84px;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  line-height: 0;
  /* gentle idle bob so it always feels alive */
  animation: vc-bus-bob 3.2s ease-in-out infinite;
  transition: transform 0.15s ease, filter 0.2s ease;
  filter: drop-shadow(0 6px 10px rgba(0,0,0,0.35));
}
.vc-battlebus:hover { transform: scale(1.08) translateY(-2px); }
.vc-battlebus:active { transform: scale(0.94); }
.vc-battlebus.is-claiming { animation: vc-bus-drop 0.9s cubic-bezier(.5,-0.4,.4,1.4) both; }
.vc-battlebus img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  position: relative;
  z-index: 2;
  -webkit-user-select: none;
  user-select: none;
  pointer-events: none;
}
.vc-battlebus:disabled { cursor: default; }
.vc-battlebus.is-claimed { opacity: 0.7; filter: grayscale(0.35) drop-shadow(0 4px 8px rgba(0,0,0,0.3)); }

/* pulsing "available today" glow ring behind the bus */
.vc-battlebus-glow {
  position: absolute;
  inset: -10%;
  z-index: 1;
  border-radius: 9999px;
  background: radial-gradient(circle, rgba(255,210,74,0.55) 0%, rgba(255,210,74,0.18) 45%, transparent 70%);
  opacity: 0;
  pointer-events: none;
}
.vc-battlebus.is-available .vc-battlebus-glow {
  opacity: 1;
  animation: vc-bus-glow 1.8s ease-in-out infinite;
}
/* little "!" badge — only when claimable */
.vc-battlebus-badge {
  position: absolute;
  z-index: 3;
  top: -2px;
  right: -2px;
  width: 20px;
  height: 20px;
  display: none;
  align-items: center;
  justify-content: center;
  border-radius: 9999px;
  background: #ef4444;
  color: #fff;
  font-size: 13px;
  font-weight: 800;
  box-shadow: 0 1px 4px rgba(0,0,0,0.4);
}
.vc-battlebus.is-available .vc-battlebus-badge {
  display: flex;
  animation: vc-bus-bob 1.2s ease-in-out infinite;
}

/* toast */
.vc-battlebus-toast {
  position: absolute;
  z-index: 40;
  left: 50%;
  bottom: 1.25rem;
  transform: translate(-50%, 16px);
  max-width: 88%;
  padding: 0.6rem 1rem;
  border-radius: 0.75rem;
  background: rgba(17, 24, 39, 0.94);
  color: #fff;
  font-size: 0.85rem;
  font-weight: 600;
  text-align: center;
  box-shadow: 0 10px 30px rgba(0,0,0,0.4);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease, transform 0.25s ease;
}
.vc-battlebus-toast.show {
  opacity: 1;
  transform: translate(-50%, 0);
}
.vc-battlebus-toast .vc-toast-amount { color: #ffd24a; }

@keyframes vc-bus-bob {
  0%,100% { transform: translateY(0); }
  50%     { transform: translateY(-5px); }
}
@keyframes vc-bus-glow {
  0%,100% { opacity: 0.55; transform: scale(0.96); }
  50%     { opacity: 1;    transform: scale(1.06); }
}
@keyframes vc-bus-drop {
  0%   { transform: translateY(0) rotate(0deg); }
  35%  { transform: translateY(-26px) rotate(-6deg); }
  100% { transform: translateY(0) rotate(0deg); }
}

/* respect reduced motion */
@media (prefers-reduced-motion: reduce) {
  .vc-islands, .vc-mist, .vc-bus { animation: none; }
  .vc-battlebus, .vc-battlebus-glow, .vc-battlebus-badge { animation: none; }
}
