/* ──────────────────────────────────────────────────────────────
   PvP FOREVER — HYJAL NIGHT

   Background is bg.jpg — 2560x1440, 155 KB, optimized externally.
   Served as-is, single source, no image-set.

   The lockup runs a 34s palette cycle: 15s Warcraft Forever, a 2s
   blend to crimson, 15s crimson, a 2s blend back. Halo pulse and
   letterform flicker continue throughout; nothing sways.

   The screenshot runs ungraded: no scrim, no vignette, no filter,
   full vibrance. The only separation between the scene and the type
   is the wordmark's own crimson bloom.
   ────────────────────────────────────────────────────────────── */

* { box-sizing: border-box; }

html { background: #070810; }

body {
  margin: 0;
  color: #fff;
  font-family: 'Nunito Sans', sans-serif;
  overflow-x: hidden;
}

.panel {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  isolation: isolate;
  padding: 80px 24px;
}

.layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* ── THE PHOTO ──
   Slightly oversized and drifting very slowly, so a still screenshot
   doesn't read as a dead JPEG.

   Nothing sits on top of it: no scrim, no vignette, no filter. The
   shot plays at full vibrance exactly as captured. */
.hyjal-photo {
  inset: -3%;
  background-image: url('bg.jpg');
  background-size: cover;
  background-position: center 42%;
  animation: hyjal-drift 64s ease-in-out infinite;
}
@keyframes hyjal-drift {
  0%, 100% { transform: scale(1.03) translate3d(0, 0, 0);      }
  50%      { transform: scale(1.07) translate3d(-0.8%, -0.6%, 0); }
}

/* ── VIGNETTE ──
   Tight and heavy. The clear zone ends at 26% — just outside the
   lockup — and the falloff is steep from there, so the corners go
   properly dark and the eye is pushed to the centre. The ellipse is
   also pulled in (100% x 82%) so the sides darken, not just the
   extreme corners.

   To loosen: raise the 26% stop and/or drop the opacities.
   To tighten further: lower it toward 18%. */
.hyjal-vignette {
  background: radial-gradient(100% 82% at 50% 44%,
    transparent 0%,
    transparent 26%,
    rgba(0, 0, 0, 0.34) 48%,
    rgba(0, 0, 0, 0.66) 68%,
    rgba(0, 0, 0, 0.86) 85%,
    rgba(0, 0, 0, 0.94) 100%);
}

/* ══════════════════════════════════════════════════════════════
   FEL DUST
   Glowing green motes drifting up off the floor, picking up the fel
   pools in the plate. Confined to the lower half: the container is
   only 52% tall and clips, and each mote fades out before it gets
   there, so nothing reaches the top half of the frame.
   ══════════════════════════════════════════════════════════════ */

.fel-dust {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 52%;
  z-index: 2;
  overflow: hidden;
  pointer-events: none;
}

.fel-dust i {
  position: absolute;
  bottom: -8px;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: #b6ff7a;
  box-shadow:
    0 0 6px rgba(140, 255, 90, 0.9),
    0 0 14px rgba(90, 220, 60, 0.55);
  opacity: 0;
  animation: fel-rise linear infinite;
}

/* Brighter, larger motes — a handful, for depth. */
.fel-dust i.bright {
  width: 4px;
  height: 4px;
  background: #d9ffb0;
  box-shadow:
    0 0 9px rgba(170, 255, 120, 1),
    0 0 22px rgba(100, 230, 70, 0.6);
}

/* Rises, drifts sideways, and is fully faded by 80% of the travel —
   well inside the container, so they dissolve rather than clip. */
@keyframes fel-rise {
  0%   { transform: translate3d(0, 0, 0) scale(0.5);                 opacity: 0;    }
  10%  {                                                              opacity: 0.85; }
  45%  { transform: translate3d(calc(var(--dx, 20px) * 0.5), -22vh, 0) scale(1); opacity: 0.7; }
  80%  {                                                              opacity: 0.15; }
  100% { transform: translate3d(var(--dx, 20px), -44vh, 0) scale(0.4); opacity: 0;   }
}

/* ══════════════════════════════════════════════════════════════
   QUEST MARKER
   The classic turn-in "?". Idles with a slow bob so it reads as
   interactive at a glance; on hover it stops dead and lights up.

   The bob and the hover scale live on SEPARATE elements — they'd
   overwrite each other on one, since both animate transform.
   ══════════════════════════════════════════════════════════════ */

/* Docked near the bottom of the panel rather than riding along with
   the centred lockup. The dock owns the centring transform so the
   button's hover scale doesn't fight it — two transforms on one
   element would overwrite each other. */
.quest-dock {
  position: absolute;
  left: 50%;
  bottom: 52px;
  transform: translateX(-50%);
  z-index: 4;
}

.quest-marker {
  /* button reset */
  appearance: none;
  border: 0;
  padding: 0;
  background: none;
  display: block;
  margin: 0;
  cursor: pointer;
  transition: transform 0.22s ease;
}
.quest-marker:hover,
.quest-marker:focus-visible {
  transform: scale(1.09);
}
.quest-marker:focus-visible {
  outline: 2px solid rgba(255, 200, 80, 0.85);
  outline-offset: 10px;
  border-radius: 8px;
}

/* Inner element carries the idle bob. */
.qm-float {
  display: block;
  animation: qm-bob 3.6s ease-in-out infinite;
}

.quest-marker svg {
  display: block;
  width: 46px;
  height: auto;
  /* Base state: a soft warm drop shadow so it lifts off the fog. */
  filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.75))
          drop-shadow(0 0 10px rgba(255, 150, 20, 0.35));
  transition: filter 0.22s ease;
}

/* HOVER — the bob freezes exactly where it is (paused, not removed,
   so there's no snap back to the start position) and the glow comes
   up hot. */
.quest-marker:hover .qm-float,
.quest-marker:focus-visible .qm-float {
  animation-play-state: paused;
}
.quest-marker:hover svg,
.quest-marker:focus-visible svg {
  filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.7))
          drop-shadow(0 0 14px rgba(255, 190, 60, 0.95))
          drop-shadow(0 0 34px rgba(255, 140, 20, 0.7));
}

@keyframes qm-bob {
  0%, 100% { transform: translateY(0)     rotate(0deg);    }
  35%      { transform: translateY(-7px)  rotate(-2.5deg); }
  70%      { transform: translateY(-2px)  rotate(2deg);    }
}

/* ══════════════════════════════════════════════════════════════
   MODAL
   ══════════════════════════════════════════════════════════════ */

.quest-modal {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(3, 4, 10, 0.72);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.26s ease, visibility 0.26s;
}
.quest-modal.is-open {
  opacity: 1;
  visibility: visible;
}

.quest-dialog {
  position: relative;
  width: min(520px, 100%);
  padding: 34px 34px 30px;
  border-radius: 14px;
  /* Parchment-dark panel with a warm quest-gold edge. */
  background:
    radial-gradient(120% 100% at 50% 0%, rgba(90, 60, 20, 0.5) 0%, transparent 60%),
    linear-gradient(180deg, #16120d 0%, #0d0b09 100%);
  border: 1px solid rgba(220, 165, 60, 0.4);
  box-shadow:
    0 30px 80px rgba(0, 0, 0, 0.7),
    0 0 0 1px rgba(0, 0, 0, 0.6),
    inset 0 1px 0 rgba(255, 210, 120, 0.14);
  transform: translateY(14px) scale(0.97);
  transition: transform 0.26s cubic-bezier(0.2, 0.8, 0.3, 1);
}
.quest-modal.is-open .quest-dialog {
  transform: translateY(0) scale(1);
}

.quest-dialog h2 {
  margin: 0 0 16px;
  font-size: 19px;
  font-weight: 800;
  letter-spacing: 0.08em;
  color: #ffc94a;
  text-transform: uppercase;
}
.quest-dialog p {
  margin: 0 0 14px;
  font-size: 14.5px;
  line-height: 1.65;
  color: rgba(255, 255, 255, 0.74);
}
.quest-dialog p:last-of-type { margin-bottom: 0; }

.quest-close {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  appearance: none;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: rgba(255, 255, 255, 0.5);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  transition: color 0.18s ease, background 0.18s ease;
}
.quest-close:hover {
  color: #ffc94a;
  background: rgba(255, 200, 80, 0.1);
}

/* ══════════════════════════════════════════════════════════════
   THE LOCKUP — unchanged
   ══════════════════════════════════════════════════════════════ */

.lockup {
  position: relative;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* The bloom is a STATIC declaration that reads the animated custom
   properties — it is deliberately not an @keyframes animation.
   Firefox resolves var() inside keyframes once, when the animation
   starts, and then reuses that snapshot forever; Chrome re-resolves
   every frame. A var() in a normal declaration updates in both, so
   the glow follows the palette cycle everywhere.

   The breathing that the old keyframes provided now comes entirely
   from the halo, which pulses on scale and opacity — neither of
   which needs a colour. */
.pvp-flame {
  display: block;
  width: 560px;
  max-width: 100%;
  overflow: visible;
  filter: drop-shadow(0 0 12px var(--glow-near))
          drop-shadow(0 0 32px var(--glow-far));
}

.pvp-text {
  font-family: 'Nunito Sans', sans-serif;
  font-weight: 900;
  font-size: 92px;
  letter-spacing: 0;
}

.pvp-halo {
  fill: var(--halo);
  filter: blur(16px);
  transform-origin: 50% 55%;
  animation: halo-pulse 6.75s ease-in-out infinite;
}
.pvp-body {
  transform-origin: 50% 100%;
  animation: flame-body-flicker 3.25s ease-in-out infinite;
}
.pvp-core {
  transform-origin: 50% 90%;
  animation: flame-core-flicker 2.25s ease-in-out infinite;
}

/* ══════════════════════════════════════════════════════════════
   PALETTE CYCLE — 34s loop

     0-15s   Warcraft Forever, held
     15-17s  2s transition to crimson
     17-32s  crimson, held
     32-34s  2s transition back

   Two mechanisms, because SVG gradients and colours behave
   differently:

   - The LETTERS carry url() gradient fills, and a url() can't
     interpolate into another url(). So both palettes are drawn as
     stacked copies and cross-faded on opacity.

   - The HALO and the GLOWS are plain colours, so they're registered
     with @property and animated directly. Registering them is what
     makes it work: an unregistered custom property is just a string
     to the engine and snaps between values instead of interpolating.
   ══════════════════════════════════════════════════════════════ */

@property --halo          { syntax: '<color>'; inherits: true; initial-value: #7a5620; }
@property --glow-near     { syntax: '<color>'; inherits: true; initial-value: rgba(214, 176, 106, 0.48); }
@property --glow-far      { syntax: '<color>'; inherits: true; initial-value: rgba(120, 82, 32, 0.38); }

body {
  animation: palette-cycle 34s linear infinite;
}

@keyframes palette-cycle {
  /* ── Warcraft Forever: cream crown on bronze, warm bronze halo ── */
  0%, 44.118% {
    --halo: #7a5620;
    --glow-near: rgba(214, 176, 106, 0.48);
    --glow-far:  rgba(120, 82, 32, 0.38);
  }
  /* ── Crimson. The glow runs much darker than the letters — deep
     blood rather than bright red — so the bright crown of the
     gradient stays the brightest thing in the lockup. ── */
  50%, 94.118% {
    --halo: #6b0512;
    --glow-near: rgba(118, 6, 22, 0.58);
    --glow-far:  rgba(58, 2, 11, 0.48);
  }
  100% {
    --halo: #7a5620;
    --glow-near: rgba(214, 176, 106, 0.48);
    --glow-far:  rgba(120, 82, 32, 0.38);
  }
}

/* The stacked palette copies. Same timing as the colour cycle above,
   so the letters and their glow arrive together. */
.tint-wcforever { animation: tint-wcforever 34s linear infinite; }
.tint-crimson   { animation: tint-crimson   34s linear infinite; }

.tint-wcforever .pvp-body { fill: url(#body-wcforever); }
.tint-wcforever .pvp-core { fill: url(#core-wcforever); }
.tint-crimson   .pvp-body { fill: url(#body-crimson);   }
.tint-crimson   .pvp-core { fill: url(#core-crimson);   }

@keyframes tint-wcforever {
  0%, 44.118%  { opacity: 1; }
  50%, 94.118% { opacity: 0; }
  100%       { opacity: 1; }
}
@keyframes tint-crimson {
  0%, 44.118%  { opacity: 0; }
  50%, 94.118% { opacity: 1; }
  100%       { opacity: 0; }
}

/* ── FOREVER MARK ──
   Two pre-coloured copies stacked and cross-faded on the same 40s
   clock as the letters. The gradients are baked into the pixels
   (generated from the white original's alpha channel) rather than
   applied with a CSS mask — a mask-image on a file:// page resolves
   to an opaque origin in Chrome and masks the element to nothing.

   No pulse, no bob. The only thing that changes is the palette.

   The WRAPPER is what JS sizes to the measured ink width of the PvP
   letters; the images just fill it. */
.forever-wrap {
  position: relative;
  width: 300px;      /* pre-measurement fallback, replaced by JS */
  max-width: 88%;
  margin-top: -38px;
  line-height: 0;
  filter: drop-shadow(0 0 9px var(--glow-near))
          drop-shadow(0 0 22px var(--glow-far));
}
.forever-wrap img {
  display: block;
  width: 100%;
  height: auto;
}
/* The second copy sits exactly on top of the first. */
.forever-wrap img + img {
  position: absolute;
  inset: 0;
}
.forever-wcforever { animation: tint-wcforever 34s linear infinite; }
.forever-crimson   { animation: tint-crimson   34s linear infinite; }

/* ── COMING SOON ──
   Small, wide-tracked, and quiet. The right padding cancels the
   trailing letter-space so the line stays optically centred under
   the lockup rather than sitting a few pixels left. */
.coming-soon {
  margin: 88px 0 0;
  padding-left: 0.34em;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.34em;
  color: rgba(255, 255, 255, 0.62);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.8);
}

@keyframes flame-body-flicker {
  0%, 100% { transform: scale(1, 1);        }
  30%      { transform: scale(0.99, 1.04);  }
  60%      { transform: scale(1.01, 0.98);  }
  80%      { transform: scale(0.995, 1.02); }
}
@keyframes flame-core-flicker {
  0%, 100% { transform: scale(1, 1)        translateY(0);    opacity: 1;    }
  25%      { transform: scale(0.98, 1.06)  translateY(-2px); opacity: 0.9;  }
  50%      { transform: scale(1.02, 0.95)  translateY(1px);  opacity: 1;    }
  75%      { transform: scale(0.99, 1.03)  translateY(-1px); opacity: 0.95; }
}
@keyframes halo-pulse {
  0%, 100% { transform: scale(0.76); opacity: 0.36; }
  50%      { transform: scale(0.95); opacity: 0.64; }
}
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; }
}
