/* ==========================================================================
   Map Visual Redesign — CSS Foundation & Custom Properties
   Pure CSS visual enhancement layer for the Nivelia adventure map.
   No external frameworks. No DOM changes. GPU-composited animations only.
   ========================================================================== */

/* --- Custom Properties (Design Tokens) --- */
:root {
  /* Glow colors by state */
  --map-glow-active: rgba(124, 58, 237, 0.6);
  --map-glow-mastered: rgba(16, 185, 129, 0.4);
  --map-glow-locked: transparent;

  /* Shadow / depth values */
  --map-depth-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);
  --map-depth-shadow-locked: 0 3px 8px rgba(0, 0, 0, 0.15);
  --map-inset-highlight: inset 0 -2px 4px rgba(255, 255, 255, 0.2);

  /* Animation durations */
  --map-pulse-duration: 2s;
  --map-particle-duration: 3s;
  --map-dragon-float-duration: 3s;
  --map-shimmer-duration: 2.5s;

  /* Sizing variables */
  --map-dragon-size: 48px;
  --map-dragon-size-mobile: 32px;
  --map-card-max-width: 110px;
  --map-card-border-radius: 10px;

  /* Fog / locked overlay */
  --map-fog-opacity: 0.55;
  --map-locked-saturation: 0.45;
  --map-locked-brightness: 0.8;

  /* Background de-emphasis */
  --map-bg-saturation: 0.8;
  --map-bg-focal-gradient: radial-gradient(ellipse at 50% 50%, transparent 30%, rgba(0, 0, 20, 0.25) 100%);
  --map-deco-blur: 1px;

  /* Path styling */
  --map-path-opacity-locked: 0.4;
  --map-path-opacity-active: 1;

  /* Borders */
  --map-border-luminous: 3px solid rgba(255, 255, 255, 0.8);
  --map-border-locked: 3px solid rgba(255, 255, 255, 0.3);
}

/* ==========================================================================
   2.1 — World Node Glow System
   Applies a soft outer glow to .mnode-circle based on node state.
   Uses --glow-color scoped custom property for per-state color control.
   Requirements: 1.1, 1.2, 1.3, 1.4
   ========================================================================== */

/* Base glow — defaults to transparent (no glow unless state overrides) */
.mnode .mnode-circle {
  --glow-color: transparent;
  box-shadow:
    0 0 12px 4px var(--glow-color),
    0 4px 16px rgba(0, 0, 0, 0.3);
  transition: box-shadow 0.3s ease;
}

/* Active state — bright purple glow matching active gradient */
.mnode .mnode-circle.active {
  --glow-color: var(--map-glow-active);
}

/* Mastered state — softer green glow matching mastered gradient */
.mnode .mnode-circle.mastered {
  --glow-color: var(--map-glow-mastered);
}

/* Locked state — glow suppressed entirely */
.mnode .mnode-circle.locked {
  --glow-color: var(--map-glow-locked);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

/* ==========================================================================
   2.2 — Depth and Elevation Effects
   Floating island appearance with drop shadows, inset highlights, and
   luminous borders. Combined with glow from 2.1 in box-shadow shorthand.
   Requirements: 2.1, 2.2, 2.3, 2.4
   ========================================================================== */

/* Prevent shadow/glow clipping on node containers (Req 2.4) */
.mnode {
  overflow: visible;
}

/* Unlocked nodes: full depth + glow + inset highlight + luminous border */
.mnode .mnode-circle:not(.locked) {
  box-shadow:
    0 0 12px 4px var(--glow-color, transparent),   /* outer glow (from 2.1) */
    var(--map-depth-shadow),                         /* drop shadow: 0 6px 16px */
    var(--map-inset-highlight);                      /* inset bottom highlight */
  border: var(--map-border-luminous);                /* 3px solid rgba(255,255,255,0.8) */
}

/* Locked nodes: reduced depth, muted border (Req 2.3) */
.mnode .mnode-circle.locked {
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.4);
  border: 3px solid rgba(255, 255, 255, 0.6);
}

/* ==========================================================================
   3.1 — Active World Pulsing Glow Animation
   Pulsing box-shadow animation on .mnode-circle.active (2s cycle, 12px→20px
   spread). Scale prominence on the active .mnode container. GPU-composited.
   Requirements: 3.1, 3.3
   ========================================================================== */

/* Keyframes: alternate glow spread from 12px to 20px using --glow-color */
@keyframes activeWorldPulse {
  0%, 100% {
    box-shadow:
      0 0 12px 4px var(--glow-color),
      var(--map-depth-shadow),
      var(--map-inset-highlight);
  }
  50% {
    box-shadow:
      0 0 20px 8px var(--glow-color),
      var(--map-depth-shadow),
      var(--map-inset-highlight);
  }
}

/* Apply pulse animation to active world circle */
.mnode .mnode-circle.active {
  animation: activeWorldPulse var(--map-pulse-duration) ease-in-out infinite;
  will-change: box-shadow;
}

/* Scale the active world's container node for size prominence (Req 3.3) */
.mnode:has(.mnode-circle.active) {
  transform: scale(1.08);
  will-change: transform;
}

/* ==========================================================================
   4.1 — Locked World Fog Overlay
   Applies a radial-gradient fog pseudo-element over locked nodes and
   desaturates/dims the circle to create a mysterious, partially-hidden look.
   The world icon shape remains visible through the overlay for curiosity.
   Requirements: 4.1, 4.2, 4.3, 4.4
   ========================================================================== */

/* Fog overlay via ::after pseudo-element on the locked node container */
.mnode-locked {
  position: relative;
}

.mnode-locked::after {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  background: radial-gradient(
    circle,
    rgba(100, 100, 140, 0.3) 0%,
    rgba(60, 60, 100, 0.15) 100%
  );
  opacity: 0.4;
  pointer-events: none;
  z-index: 2;
}

/* Desaturate and dim the locked circle — icon shape stays visible (Req 4.2, 4.3) */
.mnode-locked .mnode-circle {
  filter: saturate(0.6) brightness(0.9);
  background: rgba(80, 80, 100, 0.7);
}

/* ==========================================================================
   3.2 — Particle Animations on Active World
   Orbiting sparkle (::before) and secondary particle (::after) on the active
   world node. Only applied to non-locked, non-mastered (current active) nodes.
   GPU-composited using transform + opacity. pointer-events: none on both.
   Requirements: 3.2, 3.4, 10.1
   ========================================================================== */

/* Keyframes: sparkle orbiting the node in a full circle */
@keyframes particleOrbit {
  0% {
    transform: rotate(0deg) translateX(38px) rotate(0deg);
    opacity: 0.9;
  }
  50% {
    opacity: 0.4;
  }
  100% {
    transform: rotate(360deg) translateX(38px) rotate(-360deg);
    opacity: 0.9;
  }
}

/* Keyframes: secondary particle with different orbit radius and fade pattern */
@keyframes particleOrbitSecondary {
  0% {
    transform: rotate(0deg) translateX(30px) rotate(0deg);
    opacity: 0.6;
  }
  25% {
    opacity: 1;
  }
  75% {
    opacity: 0.3;
  }
  100% {
    transform: rotate(-360deg) translateX(30px) rotate(360deg);
    opacity: 0.6;
  }
}

/* --- Shared base styles for both pseudo-element particles --- */
.mnode:not(.mnode-locked):has(.mnode-circle.active)::before,
.mnode:not(.mnode-locked):has(.mnode-circle.active)::after {
  content: '';
  position: absolute;
  pointer-events: none;
  border-radius: 50%;
  top: 50%;
  left: 50%;
  z-index: 12;
}

/* --- Primary sparkle particle (::before) — orbiting clockwise --- */
.mnode:not(.mnode-locked):has(.mnode-circle.active)::before {
  width: 8px;
  height: 8px;
  margin-top: -4px;
  margin-left: -4px;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.95) 0%, rgba(168, 130, 255, 0.6) 100%);
  box-shadow: 0 0 6px 2px rgba(168, 130, 255, 0.5);
  animation: particleOrbit var(--map-particle-duration) linear infinite;
  will-change: transform, opacity;
}

/* --- Secondary particle (::after) — counter-orbit with offset timing --- */
.mnode:not(.mnode-locked):has(.mnode-circle.active)::after {
  width: 5px;
  height: 5px;
  margin-top: -2.5px;
  margin-left: -2.5px;
  background: radial-gradient(circle, rgba(255, 255, 220, 0.9) 0%, rgba(255, 200, 100, 0.5) 100%);
  box-shadow: 0 0 4px 1px rgba(255, 200, 100, 0.4);
  animation: particleOrbitSecondary var(--map-particle-duration) linear infinite;
  animation-delay: calc(var(--map-particle-duration) / -2);
  will-change: transform, opacity;
}

/* ==========================================================================
   4.2 — World Card Redesign
   Transforms .mnode-label into compact game-style cards with gradient
   backgrounds, colored state borders, and mobile-legible typography.
   Requirements: 5.1, 5.2, 5.3, 5.4, 5.5
   ========================================================================== */

/* Base world card styling — game-style compact card appearance (Req 5.1, 5.2) */
.mnode .mnode-label {
  border-radius: var(--map-card-border-radius);           /* 10px */
  max-width: var(--map-card-max-width);                   /* 110px */
  background: linear-gradient(
    135deg,
    rgba(20, 10, 40, 0.75),
    rgba(30, 20, 60, 0.65)
  );
  border: 1.5px solid rgba(255, 255, 255, 0.2);
  box-shadow:
    0 3px 10px rgba(0, 0, 0, 0.3),
    inset 0 1px 0 rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  color: rgba(255, 255, 255, 0.95);
  transition: border-color 0.3s ease, opacity 0.3s ease;
}

/* World name legibility on mobile (Req 5.5) */
.mnode-label-name {
  font-size: max(0.45rem, 0.5rem);
  color: rgba(255, 255, 255, 0.95);
}

/* Label info text color for dark card background */
.mnode-label-info {
  color: rgba(255, 255, 255, 0.7);
}

/* Active world card — purple accent border matching active glow (Req 5.3) */
.mnode:has(.mnode-circle.active) .mnode-label {
  border-color: var(--map-glow-active);                   /* purple */
  box-shadow:
    0 3px 10px rgba(0, 0, 0, 0.3),
    0 0 6px 1px rgba(124, 58, 237, 0.25),
    inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

/* Mastered world card — green accent border (Req 5.1) */
.mnode:has(.mnode-circle.mastered) .mnode-label {
  border-color: var(--map-glow-mastered);                 /* green */
  box-shadow:
    0 3px 10px rgba(0, 0, 0, 0.3),
    0 0 6px 1px rgba(16, 185, 129, 0.2),
    inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

/* Locked world card — gray border, reduced opacity, muted colors (Req 5.4) */
.mnode-locked .mnode-label {
  border-color: rgba(107, 114, 128, 0.5);                /* gray */
  opacity: 0.85;
  background: linear-gradient(
    135deg,
    rgba(40, 40, 50, 0.6),
    rgba(50, 50, 65, 0.5)
  );
  box-shadow:
    0 2px 6px rgba(0, 0, 0, 0.2),
    inset 0 1px 0 rgba(255, 255, 255, 0.05);
}

.mnode-locked .mnode-label-name {
  color: rgba(255, 255, 255, 0.6);
}

.mnode-locked .mnode-label-info {
  color: rgba(255, 255, 255, 0.4);
}

/* Progress bar fill adjustment for dark card */
.mnode-label-bar {
  background: rgba(255, 255, 255, 0.15);
}


/* ==========================================================================
   7.2 — Background De-emphasis
   Reduces visual prominence of the map background and decorations so that
   World_Nodes and paths remain the center of attention. Uses subtle
   saturation reduction, focal lighting via radial gradient, and decoration
   blur — all changes are mild to preserve the original map artwork.
   Requirements: 8.1, 8.2, 8.3, 8.4
   ========================================================================== */

/* Saturation reduction on map landscape — 20% desaturation (Req 8.2) */
.map-landscape {
  filter: saturate(var(--map-bg-saturation, 0.8));
  position: relative;
}

/* Focal lighting overlay — subtle radial gradient darkening edges (Req 8.1) */
.map-landscape::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--map-bg-focal-gradient, radial-gradient(ellipse at 50% 50%, transparent 30%, rgba(0, 0, 20, 0.25) 100%));
  pointer-events: none;
  z-index: 1;
}

/* Background decoration blur — pushes .map-deco to visual background (Req 8.3) */
.map-deco {
  filter: blur(var(--map-deco-blur, 1px));
}

/* ==========================================================================
   7.1 — Current World Dragon Indicator
   Displays the player's dragon mascot at the current world using pseudo-
   elements on .mnode-label to avoid collision with particle ::before/::after
   on .mnode (Task 3.2). Includes floating animation and glowing platform.
   Requirements: 7.1, 7.2, 7.3, 7.4
   ========================================================================== */

/* Keyframes: gentle vertical float oscillation (-3px to 3px) */
@keyframes dragonFloat {
  0%, 100% {
    transform: translateX(-50%) translateY(-3px);
  }
  50% {
    transform: translateX(-50%) translateY(3px);
  }
}

/* --- Dragon image indicator via .mnode-label::before --- */
.mnode-current-dragon .mnode-label::before {
  content: '';
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  width: var(--map-dragon-size);
  height: var(--map-dragon-size);
  background-image: var(--dragon-img);
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  pointer-events: none;
  z-index: 15;
  animation: dragonFloat var(--map-dragon-float-duration) ease-in-out infinite;
  /* Ensure image renders crisply */
  image-rendering: -webkit-optimize-contrast;
  image-rendering: crisp-edges;
}

/* --- Glowing platform beneath dragon via .mnode-label::after --- */
.mnode-current-dragon .mnode-label::after {
  content: '';
  position: absolute;
  bottom: calc(100% - 4px);
  left: 50%;
  transform: translateX(-50%);
  width: calc(var(--map-dragon-size) * 0.8);
  height: calc(var(--map-dragon-size) * 0.25);
  background: radial-gradient(
    ellipse at center,
    rgba(124, 58, 237, 0.5) 0%,
    rgba(124, 58, 237, 0.2) 50%,
    transparent 100%
  );
  border-radius: 50%;
  pointer-events: none;
  z-index: 14;
  filter: blur(2px);
}

/* --- Mobile responsive sizing (≤768px) --- */
@media (max-width: 768px) {
  .mnode-current-dragon .mnode-label::before {
    width: var(--map-dragon-size-mobile);
    height: var(--map-dragon-size-mobile);
  }

  .mnode-current-dragon .mnode-label::after {
    width: calc(var(--map-dragon-size-mobile) * 0.8);
    height: calc(var(--map-dragon-size-mobile) * 0.25);
  }
}

/* --- Reduced motion: disable floating animation --- */
@media (prefers-reduced-motion: reduce) {
  .mnode-current-dragon .mnode-label::before {
    animation: none;
    transform: translateX(-50%) translateY(0);
  }
}

/* ==========================================================================
   6.1 — Map Path Connections Between World Nodes
   Creates visual path connections using .mnode-label pseudo-elements.
   Paths glow for unlocked nodes, dim for locked ones, and include
   decorative dots and shimmer animations.
   Requirements: 6.1, 6.2, 6.3, 6.4, 6.5
   ========================================================================== */

/* --- Shimmer animation for active/unlocked path segments --- */
@keyframes pathShimmer {
  0% {
    background-position: 0% 0%;
    opacity: var(--map-path-opacity-active);
  }
  50% {
    background-position: 0% 100%;
    opacity: 0.7;
  }
  100% {
    background-position: 0% 0%;
    opacity: var(--map-path-opacity-active);
  }
}

/* --- Decorative dot pulse along paths --- */
@keyframes pathDotPulse {
  0%, 100% {
    opacity: 0.6;
    transform: translateX(-50%) scale(1);
  }
  50% {
    opacity: 1;
    transform: translateX(-50%) scale(1.3);
  }
}

/* ==========================================================================
   Path connector line — .mnode-label::after
   Draws a luminous vertical/diagonal line extending downward from the label,
   simulating the path/connection to the next world node.
   Skipped on the first node (no preceding path).
   ========================================================================== */

/* Base path connector — all nodes except the first and dragon node get a downward path line */
.mnode:not(:first-child):not(.mnode-current-dragon) .mnode-label::after {
  content: '';
  position: absolute;
  bottom: -28px;
  left: 50%;
  transform: translateX(-50%);
  width: 3px;
  height: 22px;
  border-radius: 2px;
  pointer-events: none;
  z-index: 7;

  /* Luminous gradient line (Req 6.1, 6.2) */
  background: linear-gradient(
    to bottom,
    rgba(168, 130, 255, 0.8),
    rgba(45, 212, 191, 0.6),
    rgba(168, 130, 255, 0.3)
  );
  background-size: 100% 200%;

  /* Glow effect via box-shadow (Req 6.2) */
  box-shadow:
    0 0 6px 2px rgba(168, 130, 255, 0.4),
    0 0 12px 4px rgba(45, 212, 191, 0.2);

  /* Default: full brightness + shimmer animation for unlocked paths (Req 6.3) */
  opacity: var(--map-path-opacity-active);
  animation: pathShimmer var(--map-shimmer-duration) ease-in-out infinite;
  will-change: opacity, background-position;
}

/* Ensure .mnode-label has relative positioning for pseudo-element placement */
.mnode-label {
  position: relative;
}

/* --- Locked path segments — reduced opacity, no animation (Req 6.4) --- */
.mnode-locked:not(:first-child):not(.mnode-current-dragon) .mnode-label::after {
  opacity: var(--map-path-opacity-locked);
  animation: none;

  /* Muted path color for locked segments */
  background: linear-gradient(
    to bottom,
    rgba(120, 120, 150, 0.5),
    rgba(100, 100, 130, 0.3),
    rgba(80, 80, 110, 0.2)
  );

  /* Suppressed glow on locked paths */
  box-shadow:
    0 0 3px 1px rgba(100, 100, 130, 0.2);
}

/* ==========================================================================
   Decorative dots along path — .mnode-label::before
   Simulates luminous stepping stones or footprints spaced along the path.
   Uses radial-gradient background-image to create dot pattern (Req 6.5).
   ========================================================================== */

/* Decorative dots on path — all nodes except the first and dragon node */
.mnode:not(:first-child):not(.mnode-current-dragon) .mnode-label::before {
  content: '';
  position: absolute;
  bottom: -30px;
  left: 50%;
  transform: translateX(-50%);
  width: 7px;
  height: 26px;
  pointer-events: none;
  z-index: 6;

  /* Dot pattern using repeating radial gradient (Req 6.5) */
  background-image:
    radial-gradient(circle, rgba(168, 130, 255, 0.7) 1.5px, transparent 1.5px),
    radial-gradient(circle, rgba(45, 212, 191, 0.5) 1px, transparent 1px);
  background-size: 7px 8px, 7px 8px;
  background-position: 0 0, 3.5px 4px;
  background-repeat: repeat-y;

  /* Gentle pulsing animation on active paths */
  animation: pathDotPulse 2.5s ease-in-out infinite;
  will-change: opacity, transform;
}

/* Locked path dots — dimmed and static (Req 6.4) */
.mnode-locked:not(:first-child):not(.mnode-current-dragon) .mnode-label::before {
  background-image:
    radial-gradient(circle, rgba(100, 100, 130, 0.4) 1.5px, transparent 1.5px),
    radial-gradient(circle, rgba(80, 80, 110, 0.3) 1px, transparent 1px);
  background-size: 7px 8px, 7px 8px;
  background-position: 0 0, 3.5px 4px;
  opacity: var(--map-path-opacity-locked);
  animation: none;
}

/* ==========================================================================
   Mastered world paths — green-tinted glow to match mastered node state
   Distinguishes completed path segments from active ones.
   ========================================================================== */

.mnode:not(:first-child):not(.mnode-current-dragon):has(.mnode-circle.mastered) .mnode-label::after {
  background: linear-gradient(
    to bottom,
    rgba(16, 185, 129, 0.7),
    rgba(5, 150, 105, 0.5),
    rgba(16, 185, 129, 0.3)
  );
  background-size: 100% 200%;
  box-shadow:
    0 0 6px 2px rgba(16, 185, 129, 0.3),
    0 0 10px 3px rgba(5, 150, 105, 0.15);
  /* Mastered paths shimmer subtly (completed, not attention-grabbing) */
  animation: pathShimmer calc(var(--map-shimmer-duration) * 1.5) ease-in-out infinite;
}

.mnode:not(:first-child):not(.mnode-current-dragon):has(.mnode-circle.mastered) .mnode-label::before {
  background-image:
    radial-gradient(circle, rgba(16, 185, 129, 0.6) 1.5px, transparent 1.5px),
    radial-gradient(circle, rgba(5, 150, 105, 0.4) 1px, transparent 1px);
  background-size: 7px 8px, 7px 8px;
  background-position: 0 0, 3.5px 4px;
}

/* ==========================================================================
   Responsive adjustments for path connections on mobile
   ========================================================================== */

@media (max-width: 768px) {
  .mnode:not(:first-child):not(.mnode-current-dragon) .mnode-label::after {
    height: 16px;
    bottom: -22px;
    width: 2px;
  }
  .mnode:not(:first-child):not(.mnode-current-dragon) .mnode-label::before {
    height: 18px;
    bottom: -24px;
    width: 5px;
    background-size: 5px 6px, 5px 6px;
    background-position: 0 0, 2.5px 3px;
  }
}

/* ==========================================================================
   Reduced motion — disable path shimmer and dot animations
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .mnode:not(:first-child):not(.mnode-current-dragon) .mnode-label::after {
    animation: none;
  }
  .mnode:not(:first-child):not(.mnode-current-dragon) .mnode-label::before {
    animation: none;
  }
}

/* ==========================================================================
   8.1 — Responsive Breakpoint Styles (Consolidated)
   Scales glow, depth, particles, and active effects proportionally on mobile.
   Reduces particle complexity and ensures no overlap between adjacent nodes.
   Requirements: 9.1, 9.2, 9.4
   ========================================================================== */

@media (max-width: 768px) {

  /* --- Glow & Depth: reduce spread and blur for smaller screens (Req 9.1) --- */
  .mnode-circle:not(.locked) {
    box-shadow:
      0 0 8px 2px var(--glow-color, transparent),
      0 4px 10px rgba(0, 0, 0, 0.3),
      var(--map-inset-highlight);
  }

  .mnode-circle.locked {
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
  }

  /* --- Active pulse: smaller glow spread on mobile (Req 9.1) --- */
  @keyframes activeWorldPulseMobile {
    0%, 100% {
      box-shadow:
        0 0 8px 2px var(--glow-color),
        0 4px 10px rgba(0, 0, 0, 0.3),
        var(--map-inset-highlight);
    }
    50% {
      box-shadow:
        0 0 14px 5px var(--glow-color),
        0 4px 10px rgba(0, 0, 0, 0.3),
        var(--map-inset-highlight);
    }
  }

  .mnode-circle.active {
    animation: activeWorldPulseMobile var(--map-pulse-duration) ease-in-out infinite;
  }

  /* --- Active node scale: reduced from 1.08 to 1.04 to prevent overlap (Req 9.4) --- */
  .mnode:has(.mnode-circle.active) {
    transform: scale(1.04);
  }

  /* --- Particles: hide secondary (::after) to reduce complexity (Req 9.2) --- */
  .mnode:not(.mnode-locked):has(.mnode-circle.active)::after {
    display: none;
  }

  /* --- Primary particle: smaller orbit radius on mobile (Req 9.2) --- */
  @keyframes particleOrbitMobile {
    0% {
      transform: rotate(0deg) translateX(26px) rotate(0deg);
      opacity: 0.85;
    }
    50% {
      opacity: 0.4;
    }
    100% {
      transform: rotate(360deg) translateX(26px) rotate(-360deg);
      opacity: 0.85;
    }
  }

  .mnode:not(.mnode-locked):has(.mnode-circle.active)::before {
    width: 6px;
    height: 6px;
    margin-top: -3px;
    margin-left: -3px;
    animation: particleOrbitMobile var(--map-particle-duration) linear infinite;
  }

  /* --- Fog overlay: slightly smaller inset on mobile --- */
  .mnode-locked::after {
    inset: -2px;
  }

  /* --- World card: tighter max-width on mobile (Req 9.4) --- */
  .mnode-label {
    max-width: 95px;
  }

  /* --- Background de-emphasis: lighter focal gradient on mobile --- */
  .map-landscape::after {
    background: radial-gradient(ellipse at 50% 50%, transparent 35%, rgba(0, 0, 20, 0.18) 100%);
  }
}

/* ==========================================================================
   8.2 — Reduced Motion & Accessibility Support
   Comprehensive accessibility layer covering all animations, WCAG AA color
   contrast compliance, @supports fallbacks for backdrop-filter, and
   verification that all animations use GPU-composited properties.
   Requirements: 9.3, 10.1
   ========================================================================== */

/* --------------------------------------------------------------------------
   Comprehensive Reduced Motion — disables ALL animations in one block.
   Overrides partial rules from Tasks 6.1 and 7.1 to ensure full coverage.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  /* 3.1 — Disable active world pulsing glow */
  .mnode .mnode-circle.active {
    animation: none;
  }

  /* 3.2 — Disable particle orbit animations and hide particles */
  .mnode:not(.mnode-locked):has(.mnode-circle.active)::before,
  .mnode:not(.mnode-locked):has(.mnode-circle.active)::after {
    animation: none;
    display: none;
  }

  /* 7.1 — Disable dragon floating animation */
  .mnode-current-dragon .mnode-label::before {
    animation: none;
    transform: translateX(-50%) translateY(0);
  }

  /* 6.1 — Disable path shimmer animation */
  .mnode:not(:first-child):not(.mnode-current-dragon) .mnode-label::after {
    animation: none;
  }

  /* 6.1 — Disable path dot pulse animation */
  .mnode:not(:first-child):not(.mnode-current-dragon) .mnode-label::before {
    animation: none;
  }

  /* Remove will-change hints when animations are disabled (saves GPU memory) */
  .mnode-circle.active,
  .mnode:has(.mnode-circle.active) {
    will-change: auto;
  }

  /* Keep the active world visually distinct via static scale (no animation) */
  .mnode:has(.mnode-circle.active) {
    transform: scale(1.08);
  }
}

/* --------------------------------------------------------------------------
   WCAG AA Color Contrast — World Label Text
   Ensures .mnode-label-name has at least 4.5:1 contrast ratio against the
   dark gradient card background (rgba(20,10,40,0.75) to rgba(30,20,60,0.65)).
   White text (#ffffff) on darkest bg (#140a28) = 16.5:1 — well above AA.
   Explicit declarations prevent inheritance issues.
   -------------------------------------------------------------------------- */
.mnode-label-name {
  color: #ffffff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}

.mnode-label-info {
  color: rgba(255, 255, 255, 0.85);
}

/* Locked state labels — lighter but still AA compliant on muted dark bg.
   rgba(255,255,255,0.75) on rgba(40,40,50,0.6) bg ≈ 5.2:1 — passes AA. */
.mnode-locked .mnode-label-name {
  color: rgba(255, 255, 255, 0.75);
}

.mnode-locked .mnode-label-info {
  color: rgba(255, 255, 255, 0.6);
}

/* --------------------------------------------------------------------------
   @supports Queries — backdrop-filter Progressive Enhancement
   Wraps backdrop-filter usage in @supports with solid rgba fallbacks for
   browsers that don't support it (older Firefox, pre-Chromium Edge).
   -------------------------------------------------------------------------- */
@supports (backdrop-filter: blur(4px)) {
  .mnode-label {
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
  }
}

/* Fallback for browsers WITHOUT backdrop-filter support — use opaque bg */
@supports not (backdrop-filter: blur(4px)) {
  .mnode-label {
    background: linear-gradient(
      135deg,
      rgba(20, 10, 40, 0.92),
      rgba(30, 20, 60, 0.88)
    );
  }

  .mnode-locked .mnode-label {
    background: linear-gradient(
      135deg,
      rgba(40, 40, 50, 0.88),
      rgba(50, 50, 65, 0.82)
    );
  }
}

/* --------------------------------------------------------------------------
   GPU Compositing Verification Notes
   All animations in this stylesheet use GPU-composited properties:
   
   - activeWorldPulse: box-shadow (composited on paint, acceptable for pulse)
   - particleOrbit / particleOrbitSecondary: transform + opacity ✓
   - dragonFloat: transform ✓
   - pathShimmer: opacity + background-position (lightweight)
   - pathDotPulse: opacity + transform ✓
   - scale(1.08) on .mnode: transform ✓
   
   will-change declarations are applied where beneficial:
   - .mnode-circle.active: will-change: box-shadow
   - .mnode:has(.active): will-change: transform
   - Particle pseudo-elements: will-change: transform, opacity
   - Path connectors: will-change: opacity, background-position
   
   These ensure layer promotion for 30fps+ on mid-range mobile (Req 9.3).
   -------------------------------------------------------------------------- */
