/* === Galactic Customs — environment (v4) ===
 *
 * Real Milky Way photo backdrop, transparent-bg Death Star + Falcon.
 * Ship choreography is JS-driven (assets/game.js → spawnShipWave()) so
 * CSS only defines the base ship class and the two keyframe paths.
 */

:root {
  --sw-bg-deep: #02030a;
  --sw-bg-mid:  #060a1d;
  --sw-bg-near: #0b1230;
  --sw-ink:     #e8edff;
  --sw-ink-dim: #9aa3c2;
  --sw-accent:  #7be3a3;
  --sw-accent-2:#5dd4ff;
  --sw-warn:    #ff5d6c;
  --sw-gold:    #ffd166;
  --sw-imperial:#c0392b;
  --sw-rebel:   #ffb000;
  --sw-mando:   #b9c7d6;
  --sw-jedi:    #7fd6ff;
  --sw-sith:    #b71c1c;
  --sw-hutt:    #6e5318;

  /* Local hosted assets (no external CDN dependence). */
  --sw-img-galaxy:    url('/starwars/assets/galaxy-bg.jpg');
  --sw-img-deathstar: url('/starwars/assets/death-star.png');
  --sw-img-deathstar-destroyed: url('/starwars/assets/death-star-destroyed.png');
  --sw-img-falcon:    url('/starwars/assets/millennium-falcon.png');
  --sw-img-xwing:     url('https://static.wikia.nocookie.net/starwars/images/7/77/Xwing-SWB.png');
  --sw-img-tie:       url('https://static.wikia.nocookie.net/starwars/images/d/d1/TIE_Fighter_DICE.png');
}

@media (prefers-color-scheme: dark) { :root { color-scheme: dark; } }

/* Always honor [hidden] inside the game UI — several rules later set
   display:flex/grid/inline-flex on classes that are independently
   marked [hidden] (.sw-btn, .sw-avatar__controls children, .sw-feedback,
   etc.), and the browser's default [hidden]{display:none} loses that
   specificity battle silently. */
.sw-stage [hidden] { display: none !important; }

.sw-body { background: #000; color: var(--sw-ink); overflow-x: hidden; }
.sw-main { background: #000; }

/* The stage — full-bleed dark mode region between header and footer. */
.sw-stage {
  position: relative;
  isolation: isolate;
  min-height: calc(100vh - 96px);
  padding: 20px clamp(16px, 4vw, 56px) 36px;
  background:
    radial-gradient(ellipse at 50% 50%, transparent 0%, rgba(0,0,0,0.45) 70%, rgba(0,0,0,0.78) 100%),
    var(--sw-img-galaxy) center / cover no-repeat,
    linear-gradient(180deg, #02030a 0%, #060a1d 65%, #010204 100%);
  background-attachment: scroll, scroll, scroll;
  overflow: hidden;
  color: var(--sw-ink);
}

/* ---- Subtle nebula accents on top of the photo backdrop ---- */
.sw-galaxy { position: absolute; inset: 0; pointer-events: none; z-index: 0; }
.sw-galaxy::after {
  content: "";
  position: absolute; inset: 0;
  background:
    radial-gradient(700px 500px at 22% 18%, rgba(50, 110, 220, 0.18), transparent 60%),
    radial-gradient(700px 500px at 80% 78%, rgba(180, 60, 220, 0.12), transparent 65%);
  mix-blend-mode: screen;
}

/* Procedural starfield additive layer — gives the galaxy photo motion
   without compromising legibility. */
.sw-galaxy__layer {
  position: absolute; inset: -10%;
  background-repeat: repeat;
  will-change: opacity;
  animation: sw-twinkle 8s ease-in-out infinite alternate;
  z-index: 1;
}
.sw-galaxy__layer--far {
  background-image:
    radial-gradient(1px 1px at 12% 22%, rgba(207, 216, 255, .8) 50%, transparent 51%),
    radial-gradient(1px 1px at 78% 8%,  rgba(233, 239, 255, .85) 50%, transparent 51%),
    radial-gradient(1px 1px at 33% 67%, rgba(255, 231, 199, .8) 50%, transparent 51%),
    radial-gradient(1.2px 1.2px at 88% 64%, #ffffff 50%, transparent 51%),
    radial-gradient(1px 1px at 8% 80%,  rgba(216, 230, 255, .85) 50%, transparent 51%),
    radial-gradient(1px 1px at 56% 31%, #ffffff 50%, transparent 51%),
    radial-gradient(1px 1px at 44% 50%, rgba(200, 212, 255, .85) 50%, transparent 51%);
  background-size: 600px 480px;
  opacity: .35;
}
.sw-galaxy__layer--mid {
  background-image:
    radial-gradient(1.4px 1.4px at 14% 12%, #ffffff 50%, transparent 51%),
    radial-gradient(1.2px 1.2px at 62% 18%, rgba(174, 226, 255, .9) 50%, transparent 51%),
    radial-gradient(1.6px 1.6px at 80% 70%, #ffffff 50%, transparent 51%),
    radial-gradient(1.2px 1.2px at 26% 78%, rgba(255, 251, 230, .9) 50%, transparent 51%);
  background-size: 480px 360px;
  opacity: .55;
  animation-duration: 6s;
}
.sw-galaxy__layer--near {
  background-image:
    radial-gradient(1.8px 1.8px at 22% 30%, #ffffff 50%, transparent 51%),
    radial-gradient(1.8px 1.8px at 70% 80%, rgba(181, 232, 255, .95) 50%, transparent 51%),
    radial-gradient(1.6px 1.6px at 90% 14%, rgba(255, 247, 214, .9) 50%, transparent 51%);
  background-size: 360px 280px;
  opacity: .65;
  animation-duration: 4s;
}
@keyframes sw-twinkle {
  0%, 100% { opacity: .35; }
  50%      { opacity: .85; }
}

/* Soft accent glows so the photo doesn't feel flat. Bat-wing easter
   egg removed per design direction. */
.sw-galaxy__nebula {
  position: absolute; pointer-events: none; mix-blend-mode: screen;
  filter: blur(80px); opacity: .25; z-index: 1;
}
.sw-galaxy__nebula--violet {
  width: 70vw; height: 60vh; top: -10vh; right: -10vw;
  background: radial-gradient(closest-side, rgba(160,90,255,.4), rgba(160,90,255,0));
}
.sw-galaxy__nebula--cyan {
  width: 60vw; height: 50vh; bottom: -10vh; left: -12vw;
  background: radial-gradient(closest-side, rgba(60,200,240,.35), rgba(60,200,240,0));
}

/* ---- Death Star — real photo, transparent background, no mask needed. ---- */
.sw-deathstar {
  position: absolute;
  width: clamp(220px, 30vw, 520px);
  height: clamp(220px, 30vw, 520px);
  right: clamp(-80px, -3vw, -20px);
  top: clamp(40px, 8vh, 120px);
  z-index: 2;
  pointer-events: none;
  filter: drop-shadow(0 30px 60px rgba(0,0,0,.7))
          drop-shadow(0 0 80px rgba(95,200,255,.18));
  animation: sw-deathstar-drift 90s ease-in-out infinite alternate;
  background: var(--sw-img-deathstar) center / contain no-repeat;
}
.sw-deathstar svg { display: none; }
@keyframes sw-deathstar-drift {
  0%   { transform: translate3d(0, 0, 0) rotate(-3deg); }
  100% { transform: translate3d(-12px, 14px, 0) rotate(-1.6deg); }
}
@media (max-width: 720px) {
  .sw-deathstar { right: -28%; top: 4%; opacity: 0.7; }
}

/* ---- Battle fleet — JS-driven choreography ---- */
.sw-fleet {
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 3;
}

/* Sniper-scope cursor activates only on the play stage so the marketing
 * landing / sign-in still feels normal. Only TIEs accept clicks —
 * X-wings and the Falcon stay pointer-events:none so accidental
 * clicks don't blow up Rebel ships.
 *
 * Reliability: child elements (.sw-decision, .sw-btn, etc.) all set
 * their own cursor, so a bare rule on .sw-stage only applies over the
 * background. We force the scope cursor on every descendant with
 * !important, then explicitly restore grab on the Death Star (which
 * is meant to be draggable) and pointer on lightbox/feedback close
 * controls when those overlays surface during play. The scope SVG is
 * 32×32 with the hotspot at center (16,16); the trailing `crosshair`
 * is the universal fallback if a browser refuses the URL cursor. */
.sw-stage[data-stage-mode="targeting"],
.sw-stage[data-stage-mode="targeting"] * {
  cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='37' height='37' viewBox='0 0 32 32'><circle cx='16' cy='16' r='14' fill='none' stroke='black' stroke-width='2.5' opacity='0.55'/><circle cx='16' cy='16' r='14' fill='none' stroke='white' stroke-width='1' opacity='0.95'/><circle cx='16' cy='16' r='6' fill='none' stroke='white' stroke-width='0.5' opacity='0.7'/><line x1='16' y1='0' x2='16' y2='9' stroke='black' stroke-width='2.5' opacity='0.55'/><line x1='16' y1='0' x2='16' y2='9' stroke='white' stroke-width='1'/><line x1='16' y1='23' x2='16' y2='32' stroke='black' stroke-width='2.5' opacity='0.55'/><line x1='16' y1='23' x2='16' y2='32' stroke='white' stroke-width='1'/><line x1='0' y1='16' x2='9' y2='16' stroke='black' stroke-width='2.5' opacity='0.55'/><line x1='0' y1='16' x2='9' y2='16' stroke='white' stroke-width='1'/><line x1='23' y1='16' x2='32' y2='16' stroke='black' stroke-width='2.5' opacity='0.55'/><line x1='23' y1='16' x2='32' y2='16' stroke='white' stroke-width='1'/><circle cx='16' cy='6' r='0.9' fill='white'/><circle cx='16' cy='26' r='0.9' fill='white'/><circle cx='6' cy='16' r='0.9' fill='white'/><circle cx='26' cy='16' r='0.9' fill='white'/><circle cx='16' cy='16' r='1.6' fill='%23ff2d2d' stroke='black' stroke-width='0.6'/></svg>") 18 18, crosshair !important;
}
.sw-stage[data-stage-mode="targeting"] .sw-ship--tie {
  pointer-events: auto;
}
/* Death Star is grab/grabbing — it's a drag target, not a TIE. */
.sw-stage[data-stage-mode="targeting"] .sw-deathstar         { cursor: grab !important; }
.sw-stage[data-stage-mode="targeting"] .sw-deathstar.is-grabbing { cursor: grabbing !important; }
/* Lightbox / leaderboard overlays bypass scope cursor — those are
 * info surfaces that only render on the results view, but the play
 * view's TIE explosion uses regular pointer events. */
.sw-stage[data-stage-mode="targeting"] .sw-lightbox,
.sw-stage[data-stage-mode="targeting"] .sw-lightbox *,
.sw-stage[data-stage-mode="targeting"] .sw-lightbox__close { cursor: zoom-out !important; }

/* TIE explosion: the ship is detached from the DOM the moment a hit
 * is detected (see explodeTie in game.js) — no fade-out keyframe on
 * the sprite, because once its fly animation stops, no transform
 * applies and the element would snap to left = 0 mid-fade. All
 * residual motion lives on the burst + fragments below. */

/* Big radial burst at the TIE's position when it's hit. */
.sw-tie-burst {
  position: absolute;
  width: 80px; height: 80px;
  margin-left: -40px; margin-top: -40px;
  border-radius: 50%;
  pointer-events: none;
  z-index: 5;
  background: radial-gradient(
    circle,
    rgba(255, 250, 220, 1)   0%,
    rgba(255, 180, 60, 0.95) 22%,
    rgba(255, 80, 30, 0.7)   48%,
    rgba(255, 30, 0, 0)      75%
  );
  filter: blur(1.5px);
  animation: sw-tie-burst 600ms ease-out forwards;
}
@keyframes sw-tie-burst {
  0%   { transform: scale(0.3); opacity: 0; }
  18%  { transform: scale(1.5); opacity: 1; }
  60%  { transform: scale(2.0); opacity: 0.7; }
  100% { transform: scale(2.6); opacity: 0; }
}

/* Hull-fragment shards flying outward — JS sets --fx, --fy, --rot
 * per shard so each one drifts in a different direction. */
.sw-tie-fragment {
  position: absolute;
  width: 8px; height: 8px;
  margin-left: -4px; margin-top: -4px;
  background: linear-gradient(135deg, #c8c8d6 0%, #5a5a68 60%, #1c1c24 100%);
  border: 1px solid #0d0d12;
  pointer-events: none;
  z-index: 5;
  box-shadow: 0 0 6px rgba(255, 180, 60, 0.6);
  animation: sw-tie-fragment 720ms cubic-bezier(0.18, 0.82, 0.45, 1) forwards;
}
@keyframes sw-tie-fragment {
  0%   { transform: translate(0, 0) rotate(0); opacity: 1; }
  60%  { opacity: 1; }
  100% { transform: translate(var(--fx, 60px), var(--fy, 60px)) rotate(var(--rot, 360deg)); opacity: 0; }
}

/* Red sniper-laser shot — perspective view, tapered from user → target.
 * Length is JS-set on .style.width; --sw-laser-angle carries the
 * rotation so the keyframe can compose rotate() + scaleX() without
 * fighting the inline transform.
 *
 * The clip-path makes a trapezoid: full height at the left edge (the
 * user-end, full-strength flash) tapering to ~12% at the right edge
 * (the target / crosshair point). Reads as a beam shooting away from
 * the camera in perspective. */
.sw-laser {
  position: absolute;
  height: 22px;
  margin-top: -11px;          /* centre the beam vertically on origin */
  pointer-events: none;
  z-index: 6;
  background:
    linear-gradient(
      90deg,
      rgba(255, 250, 220, 1)   0%,    /* hot white at muzzle */
      #ff6a3a                  10%,
      #ff2828                  35%,
      #ff2828                  65%,
      #ff5a3a                  88%,
      rgba(255, 60, 30, 0.4)   100%   /* fading at target */
    );
  clip-path: polygon(0 0%, 100% 44%, 100% 56%, 0 100%);
  filter:
    drop-shadow(0 0 6px #ff2828)
    drop-shadow(0 0 14px rgba(255, 40, 30, 0.65))
    drop-shadow(0 0 22px rgba(255, 40, 30, 0.35));
  transform-origin: 0 50%;
  transform: rotate(var(--sw-laser-angle, 0rad)) scaleX(0);
  animation: sw-laser-shoot 340ms cubic-bezier(0.22, 0.85, 0.4, 1) forwards;
}
@keyframes sw-laser-shoot {
  0%   { transform: rotate(var(--sw-laser-angle, 0rad)) scaleX(0.05); opacity: 0; }
  12%  { transform: rotate(var(--sw-laser-angle, 0rad)) scaleX(0.35); opacity: 1; }
  55%  { transform: rotate(var(--sw-laser-angle, 0rad)) scaleX(1);    opacity: 1; }
  100% { transform: rotate(var(--sw-laser-angle, 0rad)) scaleX(1);    opacity: 0; }
}

/* Muzzle flash — big radial bloom at the user end (bottom-center of
 * stage) on every shot. Pops outward then fades. */
.sw-laser-flash {
  position: absolute;
  width: 90px; height: 90px;
  border-radius: 50%;
  pointer-events: none;
  z-index: 6;
  background: radial-gradient(
    circle,
    rgba(255, 250, 220, 1)   0%,
    rgba(255, 130, 60, 0.95) 22%,
    rgba(255, 50, 20, 0.7)   45%,
    rgba(255, 30, 20, 0)     72%
  );
  filter: blur(2px);
  transform: translate(-50%, -50%) scale(0.2);
  animation: sw-laser-flash 320ms ease-out forwards;
}
@keyframes sw-laser-flash {
  0%   { transform: translate(-50%, -50%) scale(0.2); opacity: 0; }
  18%  { transform: translate(-50%, -50%) scale(1.4); opacity: 1; }
  60%  { transform: translate(-50%, -50%) scale(1.1); opacity: 0.7; }
  100% { transform: translate(-50%, -50%) scale(0.7); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .sw-laser, .sw-laser-flash { animation-duration: 220ms; }
}

/* Tiny "blaster spark" that pops at the click point. */
.sw-ship-spark {
  position: absolute;
  width: 90px; height: 90px;
  border-radius: 50%;
  pointer-events: none;
  background:
    radial-gradient(circle at 50% 50%, rgba(255, 240, 180, 0.95) 0%,
                                       rgba(255, 200, 80, 0.75) 25%,
                                       rgba(255, 100, 30, 0.5) 55%,
                                       rgba(255, 60, 0, 0) 75%);
  filter: blur(2px);
  z-index: 4;
  animation: sw-spark 520ms ease-out forwards;
}
@keyframes sw-spark {
  0%   { transform: translate(-50%, -50%) scale(0.4); opacity: 1; }
  60%  { transform: translate(-50%, -50%) scale(1.4); opacity: 0.9; }
  100% { transform: translate(-50%, -50%) scale(2.2); opacity: 0; }
}

/* Death Star is grabbable; ambient drift pauses while a drag is in
 * progress. Releasing pins the dropped position. */
.sw-deathstar { pointer-events: auto; cursor: grab; }
.sw-deathstar.is-grabbing { cursor: grabbing; animation-play-state: paused; }

.sw-ship {
  --sw-ship-w: 160px;
  position: absolute;
  width: var(--sw-ship-w);
  aspect-ratio: 4 / 1.6;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  filter: drop-shadow(0 8px 18px rgba(0, 0, 0, 0.6));
  opacity: 0;
  will-change: transform, opacity;
}
.sw-ship--xwing  { background-image: var(--sw-img-xwing);  }
.sw-ship--tie    { background-image: var(--sw-img-tie);    }
.sw-ship--falcon { background-image: var(--sw-img-falcon); --sw-ship-w: clamp(67px, 7.2vw, 110px); aspect-ratio: 800 / 363; }

/* Source images don't share a natural orientation:
 *   X-wing (Battlefront render) → nose LEFT
 *   Falcon (user-provided photo) → mandibles RIGHT
 *   TIE (DICE render) → symmetric
 *
 * To fly nose-first we need the CSS flip to depend on BOTH the motion
 * direction and the source's natural facing. Four keyframes cover the
 * four combos; spawnShip() picks the right one per ship type. */

/* Ship moves right-to-left (RTL = leftward), no horizontal mirror.
 * Use for X-wings going RTL (natural already faces left = forward). */
@keyframes sw-fly-rtl-noflip {
  0%   { transform: translate3d(115vw, 0, 0) rotate(-1deg); opacity: 0; }
  6%   { opacity: 1; }
  94%  { opacity: 1; }
  100% { transform: translate3d(-32vw, 0, 0) rotate(1deg); opacity: 0; }
}
/* Ship moves RTL with a horizontal mirror.
 * Use for Falcon going RTL (natural faces right; flip to face left). */
@keyframes sw-fly-rtl-flip {
  0%   { transform: translate3d(115vw, 0, 0) rotate(-1deg) scaleX(-1); opacity: 0; }
  6%   { opacity: 1; }
  94%  { opacity: 1; }
  100% { transform: translate3d(-32vw, 0, 0) rotate(1deg) scaleX(-1); opacity: 0; }
}
/* Ship moves left-to-right (LTR = rightward), no horizontal mirror.
 * Use for Falcon going LTR (natural already faces right = forward). */
@keyframes sw-fly-ltr-noflip {
  0%   { transform: translate3d(-32vw, 0, 0) rotate(1deg); opacity: 0; }
  6%   { opacity: 1; }
  94%  { opacity: 1; }
  100% { transform: translate3d(115vw, 0, 0) rotate(-1deg); opacity: 0; }
}
/* Ship moves LTR with a horizontal mirror.
 * Use for X-wings going LTR (natural faces left; flip to face right). */
@keyframes sw-fly-ltr-flip {
  0%   { transform: translate3d(-32vw, 0, 0) rotate(1deg) scaleX(-1); opacity: 0; }
  6%   { opacity: 1; }
  94%  { opacity: 1; }
  100% { transform: translate3d(115vw, 0, 0) rotate(-1deg) scaleX(-1); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .sw-galaxy__layer { animation: none; opacity: .55; }
  .sw-deathstar    { animation: none; }
  .sw-ship         { animation: none !important; opacity: .85; }
}

/* ===== R2-D2 easter egg =====
 * Hover-trigger zone sits in the lower-left corner of the stage.
 * R2 himself lives offscreen below the stage edge; .is-peeking pops
 * him up, kicks off a swivel "look around" animation, and after a
 * short dwell he retreats back below. */
.sw-r2-zone {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 140px;
  height: 140px;
  z-index: 8;
  pointer-events: auto;
  /* invisible — only its bounding box matters */
}
.sw-r2 {
  position: absolute;
  left: 18px;
  /* Fully hidden by default. The 150-px width grew the natural image
   * height past the previous -180 hide value, leaving R2's antenna
   * permanently visible. -260 gives enough margin for any reasonable
   * source-image aspect ratio. */
  bottom: -260px;
  width: 150px;         /* +15 % from 130 */
  height: auto;
  z-index: 9;
  pointer-events: none;
  filter:
    drop-shadow(0 8px 18px rgba(0, 0, 0, 0.65))
    drop-shadow(0 0 12px rgba(80, 200, 255, 0.18));
  transition: bottom 480ms cubic-bezier(0.2, 0.9, 0.4, 1);
  transform-origin: 50% 90%;
}
.sw-r2.is-peeking {
  bottom: -42px;        /* peeks his dome above the edge */
  animation: sw-r2-look 1800ms ease-in-out 480ms 1 forwards;
}
.sw-r2.is-peeking--small {
  /* ~15 % of the full peek motion. With default -260 → full -42 = 218
   * px arc, 15 % ≈ 33 px. Just the very top of the dome shows. No
   * look animation — silent and brief. */
  bottom: -227px;
}
@keyframes sw-r2-look {
  0%   { transform: rotate(0deg); }
  20%  { transform: rotate(-8deg); }
  45%  { transform: rotate(8deg); }
  70%  { transform: rotate(-4deg); }
  100% { transform: rotate(0deg); }
}
@media (prefers-reduced-motion: reduce) {
  .sw-r2 { transition: none; }
  .sw-r2.is-peeking { animation: none; }
}

/* ===== Death Star vulnerable point + destroyed state =====
 * A tiny pulsing red dot sits at the center of the equatorial trench.
 * Hit it with a precise shot (10 px click target) → massive explosion
 * + the Death Star transitions to its half-destroyed visual: heavy
 * red-tinted filter + a dark crater overlay (::before) suggesting the
 * blown-out chunk. Hover-cursor reads as crosshair to invite the shot. */
.sw-deathstar__weakpoint {
  position: absolute;
  /* On the equatorial trench, slightly left of horizontal centre —
   * iterated against the user's reference arrow. -45 px from the
   * (44%, 48%) anchor lands the dot on the trench. */
  left: 44%;
  top: 48%;
  transform: translate(-50%, calc(-50% - 45px));
  width: 10px;
  height: 10px;
  border: 0;
  padding: 0;
  background: transparent;
  border-radius: 50%;
  pointer-events: auto;
  cursor: crosshair;
  z-index: 3;
}
.sw-deathstar__weakpoint::after {
  content: '';
  position: absolute;
  inset: 3px;     /* visible dot is 4 px on a 10 px halo */
  border-radius: 50%;
  background: #ff1818;
  box-shadow:
    0 0 4px rgba(255, 30, 30, 0.95),
    0 0 10px rgba(255, 30, 30, 0.55),
    0 0 18px rgba(255, 30, 30, 0.25);
  animation: sw-weakpoint-pulse 2.4s ease-in-out infinite alternate;
}
.sw-deathstar__weakpoint:focus-visible {
  outline: 2px solid rgba(255, 240, 200, 0.95);
  outline-offset: 4px;
  border-radius: 50%;
}
@keyframes sw-weakpoint-pulse {
  0%   { opacity: 0.35; transform: scale(0.7); }
  100% { opacity: 1.0;  transform: scale(1.05); }
}
@media (prefers-reduced-motion: reduce) {
  .sw-deathstar__weakpoint::after { animation: none; opacity: 0.85; }
}
.sw-deathstar.is-destroyed .sw-deathstar__weakpoint { display: none; }
/* Destroyed state: swap the background image to the post-blast hulk
 * artwork. Width, height, position, drift animation, drop-shadow,
 * AND drag interactivity are all inherited from the base
 * .sw-deathstar rule — the player can still grab and move the wreck
 * around the stage. Only the weakpoint button is gone. */
.sw-deathstar.is-destroyed {
  background-image: var(--sw-img-deathstar-destroyed);
}

/* Massive Death-Star detonation — multi-layer for cinematic scale.
 * Big central fireball (white core → yellow → orange → red haze),
 * an expanding shockwave ring, lingering smoke pall, and a flurry
 * of secondary mini-bursts around the orb staggered just before the
 * main detonation. */
.sw-deathstar-burst {
  position: absolute;
  width: 220px; height: 220px;
  margin-left: -110px; margin-top: -110px;
  border-radius: 50%;
  pointer-events: none;
  z-index: 5;
  background: radial-gradient(
    circle,
    rgba(255, 255, 240, 1)   0%,
    rgba(255, 230, 150, 0.98) 12%,
    rgba(255, 170, 60, 0.92) 28%,
    rgba(255, 90, 30, 0.78)  48%,
    rgba(170, 30, 10, 0.45)  68%,
    rgba(60, 20, 10, 0.18)   85%,
    rgba(20, 10, 5, 0)       100%
  );
  filter: blur(2px);
  animation: sw-deathstar-burst 2200ms cubic-bezier(0.18, 0.85, 0.4, 1) forwards;
}
@keyframes sw-deathstar-burst {
  0%   { transform: scale(0.12); opacity: 0; }
  6%   { transform: scale(0.6);  opacity: 1; }
  18%  { transform: scale(2.6);  opacity: 1; }
  40%  { transform: scale(7);    opacity: 1; }
  70%  { transform: scale(13);   opacity: 0.7; }
  100% { transform: scale(20);   opacity: 0; }
}

/* Outward shockwave ring — bright, expanding, fading. */
.sw-deathstar-shockwave {
  position: absolute;
  width: 120px; height: 120px;
  margin-left: -60px; margin-top: -60px;
  border-radius: 50%;
  border: 5px solid rgba(255, 240, 200, 0.9);
  pointer-events: none;
  z-index: 5;
  box-shadow:
    0 0 24px rgba(255, 220, 130, 0.8),
    inset 0 0 18px rgba(255, 220, 130, 0.6);
  animation: sw-deathstar-shockwave 1500ms cubic-bezier(0.2, 0.7, 0.45, 1) forwards;
}
@keyframes sw-deathstar-shockwave {
  0%   { transform: scale(0.4);  opacity: 0;   border-width: 6px; }
  10%  { transform: scale(1.6);  opacity: 1;   border-width: 5px; }
  60%  { transform: scale(11);   opacity: 0.55; border-width: 2px; }
  100% { transform: scale(20);   opacity: 0;   border-width: 0.5px; }
}

/* Lingering smoke pall — sticks around well past the fireball. */
.sw-deathstar-smoke {
  position: absolute;
  width: 280px; height: 280px;
  margin-left: -140px; margin-top: -140px;
  border-radius: 50%;
  pointer-events: none;
  z-index: 4;
  background: radial-gradient(circle,
    rgba(70, 45, 32, 0.65) 0%,
    rgba(45, 28, 22, 0.45) 35%,
    rgba(25, 16, 12, 0.18) 65%,
    transparent 100%);
  filter: blur(8px);
  animation: sw-deathstar-smoke 2800ms ease-out forwards;
}
@keyframes sw-deathstar-smoke {
  0%   { transform: scale(0.4); opacity: 0; }
  18%  { transform: scale(2.6); opacity: 0.95; }
  60%  { transform: scale(4.5); opacity: 0.6; }
  100% { transform: scale(6.5); opacity: 0; }
}

/* Secondary mini-bursts — pop around the orb in the moments before
 * (and during) the main detonation. */
.sw-deathstar-spark-burst {
  position: absolute;
  width: 60px; height: 60px;
  margin-left: -30px; margin-top: -30px;
  border-radius: 50%;
  pointer-events: none;
  z-index: 5;
  background: radial-gradient(circle,
    rgba(255, 255, 220, 0.98) 0%,
    rgba(255, 180, 60, 0.88) 28%,
    rgba(255, 80, 30, 0.55)  58%,
    transparent 100%);
  filter: blur(1.5px);
  animation: sw-deathstar-mini 700ms ease-out forwards;
}
@keyframes sw-deathstar-mini {
  0%   { transform: scale(0.2); opacity: 0; }
  20%  { transform: scale(1.4); opacity: 1; }
  100% { transform: scale(2.4); opacity: 0; }
}

/* Brief whole-stage white flash on Death-Star detonation — taller
 * and warmer than the prior version so the screen reads as bleached
 * by the blast for a beat. */
.sw-stage-flash {
  position: absolute;
  inset: 0;
  background: rgba(255, 245, 215, 1);
  pointer-events: none;
  z-index: 7;
  animation: sw-stage-flash 900ms ease-out forwards;
}
@keyframes sw-stage-flash {
  0%   { opacity: 0; }
  6%   { opacity: 1; }
  35%  { opacity: 0.7; }
  100% { opacity: 0; }
}

/* ===== BB-8 easter egg =====
 * Two-image rig: head (.sw-bb8__head) sits z=2 over body (.sw-bb8__body)
 * z=1, with the head's bottom shadow overlapping the body so it always
 * lands on the dome. Behaviours:
 *   - Auto: at random 30-120 s intervals the container peeks up ~20 %
 *     of full reveal (silent, no roll) and drops back.
 *   - Hover (.sw-bb8-zone, bottom-right corner): full roll into the
 *     corner (body spins, head upright), brief pause, speed away to
 *     the left (body spins fast, head tilts back + shifts right with
 *     momentum). Plays sfxBB8 once at the start. */
.sw-bb8-zone {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 160px;
  height: 160px;
  z-index: 8;
  pointer-events: auto;
}
.sw-bb8 {
  position: absolute;
  bottom: 0;               /* always on the ground — rolls horizontally */
  /* Match the auto-pop keyframe's rest value so the rig isn't
   * partially visible between peeks. */
  right: -200px;
  width: 118px;            /* +20 % from 98 px */
  z-index: 9;
  pointer-events: none;
  filter: drop-shadow(0 10px 16px rgba(0, 0, 0, 0.6));
  will-change: right;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.sw-bb8__head {
  width: 70%;              /* ~69 px on a 98 px body */
  height: auto;
  display: block;
  z-index: 2;
  margin-bottom: -8px;     /* shadow on bottom of head lands on body */
  transform-origin: 50% 100%;  /* pivot at base where head meets body */
}
.sw-bb8__body {
  width: 100%;
  height: auto;
  display: block;
  z-index: 1;
  transform-origin: 50% 50%;
}

/* --- Auto-pop: silent ~20 % peek rolling in from the right edge. --- */
.sw-bb8.is-popping-small {
  animation: sw-bb8-pop-small 1500ms ease-in-out forwards;
}
.sw-bb8.is-popping-small .sw-bb8__body {
  animation: sw-bb8-spin-small 1500ms ease-in-out forwards;
}
@keyframes sw-bb8-pop-small {
  /* BB-8's natural width is ~118 px; the keyframe used to peek to
   * `right: -140` which leaves the rig fully offscreen
   * (118 − 140 = −22 px visible). Now peeks to `right: -65` so the
   * body curves out ~53 px of BB-8's chassis and the head dome rotates
   * into view briefly. Default rest position stays at -200 so the
   * larger-than-original 118-px rig is fully hidden between peeks. */
  0%   { right: -200px; }
  25%  { right: -65px; }
  75%  { right: -65px; }
  100% { right: -200px; }
}
@keyframes sw-bb8-spin-small {
  0%, 100% { transform: rotate(0deg); }
  25%, 75% { transform: rotate(-90deg); }
}

/* --- Roll-in: container slides from right edge into corner; body
 *      spins -360 deg. No vertical motion — pure rolling. --- */
.sw-bb8.is-rolling-in {
  animation: sw-bb8-rollin 800ms ease-out forwards;
}
.sw-bb8.is-rolling-in .sw-bb8__body {
  animation: sw-bb8-spin-rollin 800ms linear forwards;
}
@keyframes sw-bb8-rollin {
  0%   { right: -180px; }
  100% { right: 50px; }
}
@keyframes sw-bb8-spin-rollin {
  0%   { transform: rotate(0deg); }
  100% { transform: rotate(-360deg); }
}

/* --- Pause at corner: hold the rolled-in state. --- */
.sw-bb8.is-paused             { right: 50px; }
.sw-bb8.is-paused .sw-bb8__body { transform: rotate(-360deg); }

/* --- Speed away: slide far left; body spins fast; head tilts +24deg
 *      AND shifts right ~10 px so it reads as lagging behind from
 *      momentum. Bottom edge of head still touches body so the head
 *      shadow keeps landing on the dome. --- */
.sw-bb8.is-speeding-away {
  animation: sw-bb8-speedaway 850ms cubic-bezier(0.32, 0, 0.52, 1) forwards;
}
.sw-bb8.is-speeding-away .sw-bb8__body {
  animation: sw-bb8-spin-fast 850ms linear forwards;
}
.sw-bb8.is-speeding-away .sw-bb8__head {
  animation: sw-bb8-head-momentum 850ms cubic-bezier(0.32, 0, 0.52, 1) forwards;
}
@keyframes sw-bb8-speedaway {
  0%   { right: 50px; }
  100% { right: calc(100% + 220px); }
}
@keyframes sw-bb8-spin-fast {
  0%   { transform: rotate(-360deg); }
  100% { transform: rotate(-1980deg); }
}
@keyframes sw-bb8-head-momentum {
  0%   { transform: translateX(0)    rotate(0deg); }
  18%  { transform: translateX(8px)  rotate(20deg); }
  85%  { transform: translateX(10px) rotate(24deg); }
  100% { transform: translateX(8px)  rotate(20deg); }
}

@media (prefers-reduced-motion: reduce) {
  .sw-bb8.is-rolling-in,
  .sw-bb8.is-speeding-away { animation-duration: 600ms; }
  .sw-bb8.is-speeding-away .sw-bb8__head { animation: none !important; }
}

/* ===== Princess Leia hologram easter egg =====
 * Click the ID.me logo in the brand strip → Leia projects below it.
 * Total visible duration is ~2 s with a subtle flicker the whole
 * time. No light-beam, no audio. */
.sw-leia {
  position: absolute;
  top: 92px;          /* just under the brand strip */
  left: 60px;         /* under the ID.me logo */
  width: 220px;
  z-index: 7;
  pointer-events: none;
  opacity: 0;
}
.sw-leia.is-summoned {
  animation: sw-leia-fade 3000ms ease-in-out forwards;
}
@keyframes sw-leia-fade {
  0%   { opacity: 0; transform: translateY(-4px); }
  8%   { opacity: 1; transform: translateY(0); }
  87%  { opacity: 1; }
  100% { opacity: 0; transform: translateY(-2px); }
}
.sw-leia__projection {
  position: relative;
  text-align: center;
}
.sw-leia__img {
  width: 200px;
  height: auto;
  display: block;
  margin: 0 auto;
  /* Subtle flicker while summoned — opacity micro-pulses to read as
   * an unstable holographic signal. */
  animation: sw-leia-flicker 140ms infinite alternate;
}
@keyframes sw-leia-flicker {
  0%   { opacity: 0.86; filter: brightness(1.0); }
  100% { opacity: 1;    filter: brightness(1.08); }
}
.sw-leia__caption {
  font-family: "Inter", system-ui, sans-serif;
  font-size: 12px;
  font-style: italic;
  letter-spacing: 0.3px;
  color: rgba(180, 230, 255, 0.95);
  text-shadow: 0 0 8px rgba(0, 0, 0, 0.95), 0 0 12px rgba(80, 180, 240, 0.6);
  margin: 8px 0 0;
  /* Solid backdrop so the caption is legible against any page
   * content that happens to be behind the easter-egg projection
   * (e.g., results-screen headlines on mobile). */
  display: inline-block;
  background: rgba(8, 12, 22, 0.85);
  border: 1px solid rgba(180, 230, 255, 0.25);
  border-radius: 6px;
  padding: 6px 10px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

/* Mobile: project Leia centred at the top of the viewport with a
 * smaller footprint so she doesn't overlap result-screen content
 * and the caption stays in-frame. */
@media (max-width: 720px) {
  .sw-leia {
    left: 50%;
    top: 80px;
    width: 60vw;
    max-width: 220px;
    transform: translateX(-50%);
  }
  .sw-leia.is-summoned {
    animation: sw-leia-fade-mobile 3000ms ease-in-out forwards;
  }
  @keyframes sw-leia-fade-mobile {
    0%   { opacity: 0; transform: translate(-50%, -4px); }
    8%   { opacity: 1; transform: translate(-50%, 0); }
    87%  { opacity: 1; transform: translate(-50%, 0); }
    100% { opacity: 0; transform: translate(-50%, -2px); }
  }
  .sw-leia__img {
    width: 100%;
    max-width: 200px;
  }
  .sw-leia__caption {
    font-size: 13px;
    max-width: 90vw;
  }
}

/* ===== Lightsaber prop (Konami-code unlock) =====
 * Hidden by default. While body.lightsaber-active, the native cursor
 * is hidden and the prop tracks the pointer (JS pointermove + a
 * transform that anchors the hilt at the cursor). Each click swings
 * the blade from -30deg → +30deg and back. */
.sw-lightsaber {
  position: fixed;
  left: 0;
  top: 0;
  width: 52px;          /* 30%-bigger feel; image natural is 202×832 */
  height: auto;
  z-index: 9999;
  pointer-events: none;
  transform: translate(-9999px, -9999px);
  /* Pivot at vertical center so swings rotate around the middle of
   * the blade (the cursor is anchored there, not at the hilt). */
  transform-origin: 50% 50%;
  filter:
    drop-shadow(0 0 10px rgba(140, 255, 140, 0.55))
    drop-shadow(0 0 22px rgba(120, 240, 120, 0.35));
  display: none;
  will-change: transform;
}
body.lightsaber-active .sw-lightsaber { display: block; }
/* Hide every cursor flavour while the saber is active — including
 * the targeting-mode sniper-scope, which has its own !important +
 * descendant-of-.sw-stage[data-stage-mode] specificity. The
 * combinators below match (or exceed) that specificity so the
 * none-cursor wins. */
body.lightsaber-active,
body.lightsaber-active *,
body.lightsaber-active .sw-stage[data-stage-mode="targeting"],
body.lightsaber-active .sw-stage[data-stage-mode="targeting"] * {
  cursor: none !important;
}

/* The shell itself — the centered content column above the galaxy. */
.sw-shell {
  position: relative;
  z-index: 4;
  max-width: min(960px, 100%);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
@media (min-width: 1100px) {
  .sw-shell { gap: 18px; }
}
