/* ============================================
   PRINCESS PUMPKIN'S RESCUE — Main Styles
   ============================================ */

/* Self-hosted pixel font (was Google Fonts CDN — the game must work offline
   once loaded, so no external requests). Latin subset; SIL OFL licensed. */
@font-face {
  font-family: 'Press Start 2P';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../assets/fonts/press-start-2p-latin.woff2') format('woff2');
}

/* Reset */
*, *::before, *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

:root {
  /* Core colours */
  --gold: #daba6a;
  --gold-bright: #ffe888;
  --gold-dark: #8a7a3a;
  --dark-bg: #0c1016;
  --text-light: #eae4d4;
  --text-highlight: #ffd868;
  --purple-witch: #6a3a8a;
  --green-witch: #4aaa5a;

  /* UI colours */
  --btn-bg: #3a5a2a;
  --btn-hover: #4a7a3a;
  --btn-text: #dae0ca;
  --error-red: #ca5a48;
  --success-green: #58a248;

  /* Fern's colours */
  --fern-name-bg: #3a5a2a;
  --fern-name-text: #dae0ca;

  /* Font */
  --font-pixel: 'Press Start 2P', monospace;
}

html, body {
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #000;
  font-family: var(--font-pixel);
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

/* ============================================
   Game Viewport — 16:9, centred, pixel-scaled
   ============================================ */
#game-viewport {
  position: relative;
  width: 100vw;
  height: 100vh;
  max-width: calc(100vh * 16 / 9);
  max-height: calc(100vw * 9 / 16);
  margin: auto;
  overflow: hidden;
  background: var(--dark-bg);
  touch-action: manipulation;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  /* Note: image-rendering: pixelated is applied per-element (sprites, UI)
     NOT here — it prevents background-size: cover from scaling up backgrounds */
}

/* ============================================
   Screens — full-viewport layers
   ============================================ */
.screen {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease;
  z-index: 1;
}

.screen.active {
  opacity: 1;
  pointer-events: auto;
  z-index: 10;
}

/* ============================================
   Title Screen
   ============================================ */
#screen-title {
  background: radial-gradient(ellipse at 50% 60%, #1a2a18 0%, #0a1008 70%, #000 100%);
}

.title-content {
  text-align: center;
  padding: 2rem;
}

.title-text {
  font-size: clamp(1.2rem, 4vw, 2.4rem);
  color: var(--gold-bright);
  text-shadow:
    0 0 20px rgba(255, 232, 136, 0.5),
    0 4px 0 var(--gold-dark);
  line-height: 1.6;
  margin-bottom: 1.5rem;
  animation: title-glow 3s ease-in-out infinite alternate;
}

.title-subtitle {
  font-size: clamp(0.5rem, 1.5vw, 0.8rem);
  color: var(--text-light);
  opacity: 0.7;
  margin-bottom: 3rem;
  letter-spacing: 0.2em;
}

.precache-status {
  min-height: 1.4em;
  margin: 1.25rem 0 0;
  color: var(--gold-bright);
  font-size: clamp(0.32rem, 1vw, 0.48rem);
  opacity: 0.85;
  text-shadow: 0 0 8px rgba(255, 232, 136, 0.6);
}

@keyframes title-glow {
  from { text-shadow: 0 0 20px rgba(255, 232, 136, 0.3), 0 4px 0 var(--gold-dark); }
  to { text-shadow: 0 0 40px rgba(255, 232, 136, 0.7), 0 4px 0 var(--gold-dark); }
}

/* ============================================
   Pixel Button
   ============================================ */
.pixel-btn {
  font-family: var(--font-pixel);
  font-size: clamp(0.6rem, 1.8vw, 0.9rem);
  color: var(--btn-text);
  background: var(--btn-bg);
  border: 3px solid var(--gold);
  padding: 0.8em 1.6em;
  cursor: pointer;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  transition: all 0.15s ease;
  animation: btn-pulse 2s ease-in-out infinite;
  position: relative;
}

.pixel-btn:hover, .pixel-btn:active {
  background: var(--btn-hover);
  transform: scale(1.05);
  box-shadow: 0 0 15px rgba(218, 186, 106, 0.4);
}

.pixel-btn:disabled {
  opacity: 0.4;
  cursor: default;
  animation: none;
  transform: none;
}

.pixel-btn:disabled:hover {
  background: var(--btn-bg);
  box-shadow: none;
  transform: none;
}

@keyframes btn-pulse {
  0%, 100% { box-shadow: 0 0 5px rgba(218, 186, 106, 0.2); }
  50% { box-shadow: 0 0 15px rgba(218, 186, 106, 0.5); }
}

/* ============================================
   Name Entry Screen
   ============================================ */
#screen-name-entry {
  background: radial-gradient(ellipse at 50% 40%, #1a1a3a 0%, #0a0818 70%, #000 100%);
}

.name-entry-content {
  text-align: center;
  padding: 2rem;
}

.name-prompt {
  font-size: clamp(0.7rem, 2vw, 1rem);
  color: var(--gold-bright);
  margin-bottom: 2rem;
  line-height: 1.8;
}

.name-input-wrapper {
  margin-bottom: 2rem;
}

.pixel-input {
  font-family: var(--font-pixel);
  font-size: clamp(0.8rem, 2.5vw, 1.2rem);
  color: var(--text-light);
  background: rgba(12, 16, 22, 0.9);
  border: 3px solid var(--gold);
  padding: 0.6em 1em;
  text-align: center;
  width: min(80%, 400px);
  outline: none;
  caret-color: var(--gold-bright);
}

/* The viewport is non-selectable, but Eva must still be able to type her name. */
#name-input {
  -webkit-user-select: text;
  user-select: text;
}

.pixel-input:focus {
  box-shadow: 0 0 15px rgba(218, 186, 106, 0.4);
  border-color: var(--gold-bright);
}

.pixel-input::placeholder {
  color: rgba(234, 228, 212, 0.3);
  font-size: 0.7em;
}

/* ============================================
   Portrait Orientation Prompt
   ============================================ */
#rotate-overlay {
  display: none;
  position: absolute;
  inset: 0;
  z-index: 1000;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 1.25rem;
  padding: 2rem;
  color: var(--gold-bright);
  background: rgba(4, 4, 12, 0.96);
  text-align: center;
  line-height: 1.8;
  pointer-events: all;
}

#rotate-overlay.visible {
  display: flex;
}

.rotate-tablet-glyph {
  width: 3.4rem;
  height: 2.2rem;
  border: 4px solid var(--gold-bright);
  border-radius: 0.25rem;
  color: transparent;
  box-shadow: 0 0 16px rgba(255, 232, 136, 0.55);
  animation: rotate-tablet 1.6s ease-in-out infinite alternate;
}

#rotate-overlay p {
  margin: 0;
  font-size: clamp(0.6rem, 2.2vw, 1rem);
}

@keyframes rotate-tablet {
  from { transform: rotate(0deg); }
  to { transform: rotate(90deg); }
}

/* ============================================
   Scene Screen
   ============================================ */
.scene-bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

.scene-character {
  position: absolute;
  bottom: 22%;
  left: 42%;
  transform: translateX(-50%);
  height: 28%;
  image-rendering: pixelated;
  z-index: 5;
  transition: opacity 0.5s ease, transform 0.5s ease;
}

.scene-character img {
  height: 100%;
  width: auto;
  image-rendering: pixelated;
  filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3));
}

/* The fx wrapper exists so materialize can split effects across two
   elements: the clip-path reveal wipe lives on the <img>, the glow
   drop-shadow on the wrapper. On one element the clip rectangle would
   crop the glow into a visible box; on the wrapper the glow is computed
   from the already-clipped silhouette. */
.scene-character .char-fx {
  height: 100%;
  width: fit-content;
}

/* ── Character Idle Animations ──
   Procedural "alive" motion on the single static sprite. The engine sets
   --face (1 or -1, for horizontal flip) on the container; every transform
   that touches the image must include scaleX(var(--face, 1)) so flipping
   composes with idle/walk motion instead of fighting it. */
.scene-character .char-img {
  transform-origin: 50% 100%;
  transform: scaleX(var(--face, 1));
}

.scene-character.idle-breathe .char-img {
  animation: char-idle-breathe 3.6s ease-in-out infinite;
}

.scene-character.idle-float .char-img {
  animation: char-idle-float 4.2s ease-in-out infinite;
}

@keyframes char-idle-breathe {
  0%, 100% { transform: scaleX(var(--face, 1)) scaleY(1); }
  50%      { transform: scaleX(calc(var(--face, 1) * 1.008)) scaleY(1.016) translateY(-0.4%); }
}

@keyframes char-idle-float {
  0%, 100% { transform: scaleX(var(--face, 1)) translateY(0); }
  50%      { transform: scaleX(var(--face, 1)) translateY(-3.5%); }
}

/* ── Walking ──
   The engine adds .walking + inline left/bottom transitions while a
   character relocates; the gait gives the static sprite a gentle
   step-bounce so the slide reads as walking. */
.scene-character.walking .char-img {
  animation: char-walk-gait 0.42s ease-in-out infinite;
}

@keyframes char-walk-gait {
  0%, 100% { transform: scaleX(var(--face, 1)) translateY(0) rotate(-1.2deg); }
  50%      { transform: scaleX(var(--face, 1)) translateY(-2.5%) rotate(1.2deg); }
}

/* ── Sprite-sheet playback (future frame-animated idles/walks) ──
   A .char-sheet div replaces the <img>: horizontal strip as background,
   stepped through by background-position. The engine sets background
   image/size and the animation (frame count isn't known to CSS). */
.scene-character .char-sheet {
  height: 100%;
  image-rendering: pixelated;
  background-repeat: no-repeat;
  filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3));
}

@keyframes char-sheet-play {
  to { background-position-x: 100%; }
}

/* 2-frame sheets: steps(1, jump-none) is invalid CSS, so the hold is
   baked into the keyframes instead (used with linear timing) */
@keyframes char-sheet-play-2f {
  0%, 49.99%  { background-position-x: 0%; }
  50%, 100%   { background-position-x: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .scene-character .char-img,
  .scene-character .char-sheet {
    animation: none !important;
  }
}

/* ── Digital Materialization Effect ──
   Every visual here is built from the character's own pixels (the image,
   clones of it, or its drop-shadow), so nothing reveals the sprite's
   rectangular bounding box. Stages:
     1. bottom-up data-stream reveal (clip wipe + cyan silhouette glow)
     2. RGB-split glitch slices (clipped clones flicking off-axis)
     3. scanline shimmer masked to the character's alpha
     4. settle: glow and colour snap to normal */
.scene-character.materializing {
  animation: materialize-pop 1.3s cubic-bezier(0.25, 0, 0.3, 1) forwards;
  transition: none; /* Override base transition during animation */
}

.scene-character.materializing .char-img {
  animation: char-reveal 1.3s cubic-bezier(0.25, 0, 0.3, 1) forwards;
}

.scene-character.materializing .char-fx {
  animation: char-reveal-glow 1.3s cubic-bezier(0.25, 0, 0.3, 1) forwards;
}

/* Subtle whole-body settle: a breath of overshoot as the data "locks in" */
@keyframes materialize-pop {
  0%   { opacity: 0; transform: translateX(-50%) scaleY(1.02); }
  6%   { opacity: 1; }
  62%  { transform: translateX(-50%) scaleY(1.02); }
  78%  { transform: translateX(-50%) scaleY(0.985); }
  100% { opacity: 1; transform: translateX(-50%) scaleY(1); }
}

/* Bottom-up reveal wipe + white-hot → normal colour ramp (on the image) */
@keyframes char-reveal {
  0%   { clip-path: inset(96% 0 0 0); filter: brightness(2.8) saturate(0); }
  12%  { clip-path: inset(72% 0 0 0); filter: brightness(2.4) saturate(0.1); }
  22%  { clip-path: inset(55% 0 0 0); filter: brightness(2.5) saturate(0.15); }
  34%  { clip-path: inset(28% 0 0 0); filter: brightness(2) saturate(0.3); }
  48%  { clip-path: inset(0 0 0 0); filter: brightness(1.7) saturate(0.45); }
  60%  { clip-path: inset(0 0 0 0); filter: brightness(1.25) saturate(0.8); }
  68%  { filter: brightness(1.5) saturate(0.65); }
  76%  { filter: brightness(1.05) saturate(0.95); }
  100% { clip-path: inset(0 0 0 0); filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3)) brightness(1) saturate(1); }
}

/* Cyan silhouette glow (on the wrapper, so the wipe never crops it) */
@keyframes char-reveal-glow {
  0%   { filter: drop-shadow(0 0 14px rgba(100, 220, 255, 0.9)); }
  34%  { filter: drop-shadow(0 0 18px rgba(100, 220, 255, 0.8)); }
  48%  { filter: drop-shadow(0 0 20px rgba(100, 220, 255, 0.7)); }
  60%  { filter: drop-shadow(0 0 14px rgba(100, 220, 255, 0.5)); }
  68%  { filter: drop-shadow(0 0 16px rgba(100, 220, 255, 0.55)); }
  76%  { filter: drop-shadow(0 0 8px rgba(100, 220, 255, 0.3)); }
  100% { filter: drop-shadow(0 0 0 rgba(100, 220, 255, 0)); }
}

/* Glitch slices: clones of the character image, each clipped to a thin
   horizontal band and flicked sideways with a colour shift. Because the
   slices are cut from the transparent sprite itself, the displaced bands
   follow the character's outline. */
.scene-character .glitch-slice {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: auto;
  opacity: 0;
  pointer-events: none;
  mix-blend-mode: screen;
  z-index: 6;
}

.scene-character.materializing .glitch-slice.slice-a {
  animation: glitch-slice-a 1.3s steps(1, jump-none) forwards;
  filter: hue-rotate(140deg) brightness(1.8) saturate(2);
}

.scene-character.materializing .glitch-slice.slice-b {
  animation: glitch-slice-b 1.3s steps(1, jump-none) forwards;
  filter: hue-rotate(-60deg) brightness(1.6) saturate(2.2);
}

@keyframes glitch-slice-a {
  0%   { opacity: 0; }
  10%  { opacity: 0.85; clip-path: inset(14% 0 72% 0); transform: scaleX(var(--face, 1)) translateX(-2.5%); }
  17%  { opacity: 0; }
  27%  { opacity: 0.75; clip-path: inset(48% 0 38% 0); transform: scaleX(var(--face, 1)) translateX(2%); }
  33%  { opacity: 0; }
  46%  { opacity: 0.65; clip-path: inset(68% 0 18% 0); transform: scaleX(var(--face, 1)) translateX(-1.8%); }
  52%  { opacity: 0; }
  64%  { opacity: 0.5; clip-path: inset(30% 0 58% 0); transform: scaleX(var(--face, 1)) translateX(1.2%); }
  68%  { opacity: 0; }
  100% { opacity: 0; }
}

@keyframes glitch-slice-b {
  0%   { opacity: 0; }
  13%  { opacity: 0.8; clip-path: inset(34% 0 52% 0); transform: scaleX(var(--face, 1)) translateX(2.2%); }
  20%  { opacity: 0; }
  31%  { opacity: 0.7; clip-path: inset(8% 0 80% 0); transform: scaleX(var(--face, 1)) translateX(-2%); }
  37%  { opacity: 0; }
  50%  { opacity: 0.6; clip-path: inset(56% 0 28% 0); transform: scaleX(var(--face, 1)) translateX(1.6%); }
  56%  { opacity: 0; }
  70%  { opacity: 0.4; clip-path: inset(20% 0 70% 0); transform: scaleX(var(--face, 1)) translateX(-1%); }
  74%  { opacity: 0; }
  100% { opacity: 0; }
}

/* Scanline shimmer: another clone, masked to thin horizontal lines so the
   CRT texture only appears on the character's own pixels. */
.scene-character .glitch-scan {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: auto;
  opacity: 0;
  pointer-events: none;
  z-index: 7;
  filter: brightness(2.2) saturate(0.3);
  -webkit-mask-image: repeating-linear-gradient(
    0deg, transparent 0px, transparent 3px, #000 3px, #000 4px);
  mask-image: repeating-linear-gradient(
    0deg, transparent 0px, transparent 3px, #000 3px, #000 4px);
}

.scene-character.materializing .glitch-scan {
  animation: glitch-scan-fade 1.3s ease-out forwards;
}

@keyframes glitch-scan-fade {
  0%   { opacity: 0.9; }
  45%  { opacity: 0.55; }
  80%  { opacity: 0.15; }
  100% { opacity: 0; }
}

.scene-overlay {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}

.tap-advance {
  position: absolute;
  inset: 0;
  z-index: 15;
  cursor: pointer;
}

/* ============================================
   Cipher Screen (Level 1)
   ============================================ */
.cipher-content {
  position: relative;
  z-index: 10;
  text-align: center;
  padding: 2rem;
  background: rgba(12, 16, 22, 0.85);
  border: 3px solid var(--gold);
  border-radius: 4px;
  max-width: 90%;
  width: 600px;
}

.cipher-title {
  font-size: clamp(0.7rem, 2vw, 1rem);
  color: var(--gold-bright);
  margin-bottom: 1.5rem;
}

.cipher-symbols {
  display: flex;
  justify-content: center;
  gap: 1rem;
  margin-bottom: 1.5rem;
  flex-wrap: wrap;
}

.cipher-symbol {
  width: clamp(40px, 8vw, 64px);
  height: clamp(40px, 8vw, 64px);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: clamp(1.2rem, 4vw, 2rem);
  background: rgba(42, 26, 58, 0.8);
  border: 2px solid var(--purple-witch);
  border-radius: 4px;
  color: var(--gold-bright);
  animation: symbol-glow 2s ease-in-out infinite alternate;
}

@keyframes symbol-glow {
  from { box-shadow: 0 0 5px rgba(106, 58, 138, 0.3); }
  to { box-shadow: 0 0 15px rgba(106, 58, 138, 0.6); }
}

.cipher-hint {
  font-size: clamp(0.45rem, 1.3vw, 0.65rem);
  color: var(--text-light);
  line-height: 1.8;
  opacity: 0.8;
}

/* ============================================
   Level Complete Screen
   ============================================ */
#screen-level-complete {
  background: radial-gradient(ellipse at 50% 50%, #1a2a18 0%, #0a1008 60%, #000 100%);
}

.level-complete-content {
  text-align: center;
  padding: 2rem;
}

/* ---- Large Seal Reveal (two-sided medallion flip) ---- */
.seal-break-animation {
  width: clamp(160px, 27vw, 220px);
  height: clamp(160px, 27vw, 220px);
  margin: 0 auto 1.25rem;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Golden glow burst behind the coin. Lives on its own layer so the coin's
   3D backface-hiding never clips it, and it can outlast the spin. */
.seal-break-animation::before {
  content: "";
  position: absolute;
  inset: -22%;
  border-radius: 50%;
  background: radial-gradient(circle,
    rgba(255, 232, 136, 0.55) 0%,
    rgba(255, 232, 136, 0.20) 42%,
    rgba(255, 232, 136, 0) 70%);
  opacity: 0;
  pointer-events: none;
  z-index: 0;
}
.seal-break-animation.playing::before {
  animation: seal-glow-burst 1.5s ease-out forwards;
}
.seal-break-animation.revealed::before {
  animation: seal-glow-idle 3s ease-in-out infinite alternate;
}

@keyframes seal-glow-burst {
  0%   { opacity: 0;   transform: scale(0.35); }
  35%  { opacity: 1;   transform: scale(1.18); }
  100% { opacity: 0.6; transform: scale(1); }
}
@keyframes seal-glow-idle {
  0%   { opacity: 0.45; transform: scale(0.97); }
  100% { opacity: 0.72; transform: scale(1.06); }
}

/* The spinning two-sided medallion. Perspective lives inside the transform
   (not on an ancestor) and we only ever animate transform here — never
   opacity/filter — so the preserve-3d context stays intact and the flip
   shows real depth. The scale(0) start is what makes it "appear". */
.seal-coin {
  position: relative;
  width: 100%;
  height: 100%;
  z-index: 1;
  transform-style: preserve-3d;
  transform: perspective(1100px) rotateY(-900deg) scale(0);
}
.seal-break-animation.playing .seal-coin {
  animation: seal-coin-flip 1.2s cubic-bezier(0.2, 0.72, 0.24, 1) forwards;
}
.seal-break-animation.revealed .seal-coin {
  transform: perspective(1100px) rotateY(0deg) scale(1);
  animation: seal-coin-float 3s ease-in-out infinite alternate;
}

.seal-coin .seal-face {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
.seal-coin .seal-face img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  image-rendering: auto;             /* HD medallion art — smooth, not pixelated */
  filter: drop-shadow(0 6px 12px rgba(0, 0, 0, 0.5));
}
.seal-coin .seal-face--back {
  transform: rotateY(180deg);
}

@keyframes seal-coin-flip {
  0%   { transform: perspective(1100px) rotateY(-900deg) scale(0.2); }
  100% { transform: perspective(1100px) rotateY(0deg) scale(1); }
}
@keyframes seal-coin-float {
  0%   { transform: perspective(1100px) translateY(0) rotateY(0deg) scale(1); }
  100% { transform: perspective(1100px) translateY(-7px) rotateY(0deg) scale(1); }
}

/* Text fade-up after seal reveals */
.level-complete-text {
  font-size: clamp(0.8rem, 2.5vw, 1.2rem);
  color: var(--gold-bright);
  margin-bottom: 1.5rem;
  line-height: 1.8;
  opacity: 0;
  transform: translateY(10px);
  animation: none;
}

.level-complete-text.visible {
  animation: text-fade-up 0.6s ease-out forwards, title-glow 2s 0.6s ease-in-out infinite alternate;
}

@keyframes text-fade-up {
  0% {
    opacity: 0;
    transform: translateY(10px);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

.level-complete-character {
  font-size: clamp(0.5rem, 1.4vw, 0.7rem);
  color: var(--text-light);
  line-height: 2;
  margin-bottom: 2rem;
  max-width: 500px;
  margin-left: auto;
  margin-right: auto;
  opacity: 0;
  transform: translateY(10px);
}

.level-complete-character.visible {
  animation: text-fade-up 0.6s ease-out forwards;
}

/* ============================================
   Adventure Complete Screen (final "The End")
   ============================================ */
#screen-adventure-complete {
  background:
    radial-gradient(ellipse at 50% 35%, rgba(255, 232, 136, 0.18) 0%, transparent 55%),
    radial-gradient(ellipse at 50% 75%, #3a2a58 0%, #1a1030 60%, #0a0620 100%);
  overflow: hidden;
}

/* Slowly drifting starfield / sparkle background (pure CSS, decorative). */
.adventure-complete-bg {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    radial-gradient(2px 2px at 12% 18%, rgba(255, 232, 136, 0.9), transparent 60%),
    radial-gradient(1px 1px at 28% 72%, rgba(255, 220, 180, 0.8), transparent 60%),
    radial-gradient(1.5px 1.5px at 48% 32%, rgba(255, 240, 200, 0.9), transparent 60%),
    radial-gradient(1px 1px at 66% 82%, rgba(255, 220, 180, 0.7), transparent 60%),
    radial-gradient(2px 2px at 82% 24%, rgba(255, 232, 136, 0.8), transparent 60%),
    radial-gradient(1px 1px at 92% 58%, rgba(255, 220, 200, 0.7), transparent 60%),
    radial-gradient(1.5px 1.5px at 18% 88%, rgba(255, 240, 200, 0.8), transparent 60%),
    radial-gradient(1px 1px at 54% 12%, rgba(255, 220, 180, 0.7), transparent 60%);
  animation: starfield-twinkle 4s ease-in-out infinite alternate;
}

@keyframes starfield-twinkle {
  0%   { opacity: 0.55; }
  100% { opacity: 1; }
}

.adventure-complete-content {
  text-align: center;
  padding: 2rem;
  position: relative;
  z-index: 2;
  max-width: 720px;
  margin: 0 auto;
}

.adventure-complete-title {
  font-size: clamp(1.6rem, 5vw, 3rem);
  color: var(--gold-bright);
  text-shadow:
    0 0 30px rgba(255, 232, 136, 0.7),
    0 4px 0 var(--gold-dark);
  line-height: 1.5;
  margin-bottom: 1.5rem;
  animation: title-glow 3s ease-in-out infinite alternate, adventure-title-in 1.2s ease-out both;
}

@keyframes adventure-title-in {
  0% {
    opacity: 0;
    transform: scale(0.6);
    filter: brightness(3) blur(6px);
  }
  60% {
    opacity: 1;
    transform: scale(1.08);
    filter: brightness(1.6) blur(0);
  }
  100% {
    opacity: 1;
    transform: scale(1);
    filter: brightness(1);
  }
}

.adventure-complete-subtitle {
  font-size: clamp(0.6rem, 1.7vw, 0.95rem);
  color: var(--text-light);
  letter-spacing: 0.15em;
  margin-bottom: 2rem;
  opacity: 0;
  animation: text-fade-up 0.8s 0.9s ease-out forwards;
}

.adventure-complete-quote {
  font-size: clamp(0.55rem, 1.5vw, 0.8rem);
  color: var(--text-light);
  line-height: 2;
  max-width: 520px;
  margin: 0 auto 2.5rem;
  opacity: 0;
  font-style: italic;
  animation: text-fade-up 0.8s 1.6s ease-out forwards;
}

/* ============================================
   Spell Seals Progress
   ============================================ */
.spell-seals {
  position: absolute;
  top: 1rem;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 0.8rem;
  z-index: 50;
}

.spell-seals.hidden {
  pointer-events: none;
}

.seal {
  width: clamp(50px, 8vw, 72px);
  height: clamp(50px, 8vw, 72px);
  border: none;
  border-radius: 0; /* No clipping — seal images are already round */
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  opacity: 1;
  /* Springy transform for the surface/remove pop; quick opacity fade;
     smooth filter for the dim→bright settle when a seal is earned. */
  transition:
    opacity 0.4s ease,
    transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1),
    filter 0.5s ease;
  /* Dimmed but clearly visible — muted, slightly desaturated until earned */
  filter: brightness(0.72) saturate(0.6);
  overflow: visible;
}

/* Staggered surface / removal — seals scale + drop in sequence rather than
   a flat fade, so they feel like they're being placed onto / lifted off the HUD. */
.spell-seals.hidden .seal {
  opacity: 0;
  transform: translateY(-16px) scale(0.35);
}
.spell-seals .seal[data-level="1"] { transition-delay: 0.00s; }
.spell-seals .seal[data-level="2"] { transition-delay: 0.05s; }
.spell-seals .seal[data-level="3"] { transition-delay: 0.10s; }
.spell-seals .seal[data-level="4"] { transition-delay: 0.15s; }
.spell-seals .seal[data-level="5"] { transition-delay: 0.20s; }
.spell-seals .seal[data-level="6"] { transition-delay: 0.25s; }
.spell-seals .seal[data-level="7"] { transition-delay: 0.30s; }

.seal-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  image-rendering: auto;       /* HD medallion art — smooth, not pixelated */
}

.seal.broken {
  filter: brightness(1.12) saturate(1.25) drop-shadow(0 0 7px rgba(255, 232, 136, 0.6));
  animation: seal-glow 1s ease-out;
}

@keyframes seal-glow {
  0% { transform: scale(1); filter: brightness(1.12) saturate(1.25); }
  50% { transform: scale(1.3); filter: brightness(1.6) saturate(1.55) drop-shadow(0 0 12px rgba(255, 232, 136, 0.9)); }
  100% { transform: scale(1); filter: brightness(1.12) saturate(1.25) drop-shadow(0 0 6px rgba(255, 232, 136, 0.6)); }
}

/* ============================================
   Global Dialogue Overlay
   ============================================ */
#dialogue-overlay {
  position: absolute;
  inset: 0;
  z-index: 80;
  pointer-events: none;
}

#dialogue-overlay .tap-advance {
  pointer-events: none;
  z-index: 90;   /* Must sit above #dialogue-container (z-index: 82) so taps on the dialogue box register */
}

#dialogue-overlay #dialogue-container.visible ~ .tap-advance {
  pointer-events: auto;
}

/* ============================================
   Transition Overlay
   ============================================ */
.transition-overlay {
  position: absolute;
  inset: 0;
  background: #000;
  opacity: 0;
  pointer-events: none;
  z-index: 100;
  transition: opacity 0.6s ease;
}

.transition-overlay.fade-in {
  opacity: 1;
  pointer-events: auto;
}

/* ============================================
   FX Layer — celebrations, slot pops, shake
   (driven by GameEngine.celebrate / popInSlot / shakeScreen)
   ============================================ */
#fx-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 95;        /* above screens + dialogue, below transitions (100) */
  overflow: hidden;
}

.fx-p {
  position: absolute;
  transform: translate(-50%, -50%);
  will-change: transform, opacity;
}

.fx-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  box-shadow: 0 0 8px 2px rgba(255, 232, 136, 0.7);
}

/* Four-point star drawn with clip-path; colour via `color` + currentColor */
.fx-star {
  width: 14px;
  height: 14px;
  background: currentColor;
  clip-path: polygon(50% 0%, 62% 38%, 100% 50%, 62% 62%, 50% 100%, 38% 62%, 0% 50%, 38% 38%);
  filter: drop-shadow(0 0 6px currentColor);
}

.fx-confetti {
  width: 9px;
  height: 9px;
  image-rendering: pixelated;
}

.fx-ring {
  width: 90px;
  height: 90px;
  border-radius: 50%;
  border: 4px solid rgba(255, 232, 136, 0.9);
  box-shadow:
    0 0 24px 6px rgba(255, 232, 136, 0.5),
    inset 0 0 18px 4px rgba(255, 232, 136, 0.4);
}

/* ── Slot pop — value lands with an overshoot bounce + glow flash ── */
.slot-pop {
  animation: slot-pop 0.45s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes slot-pop {
  0%   { transform: scale(0.55); filter: brightness(2.2) drop-shadow(0 0 10px rgba(255, 232, 136, 0.9)); }
  60%  { transform: scale(1.18); filter: brightness(1.5) drop-shadow(0 0 8px rgba(255, 232, 136, 0.6)); }
  100% { transform: scale(1); filter: brightness(1) drop-shadow(0 0 0 rgba(255, 232, 136, 0)); }
}

/* ── Screen shake (gentle for wrong answers, heavy for cinematic impact) ── */
.screen-shake-soft {
  animation: vp-shake-soft 0.35s ease-out;
}
.screen-shake-heavy {
  animation: vp-shake-heavy 0.6s ease-out;
}

@keyframes vp-shake-soft {
  0%, 100% { transform: translate(0, 0); }
  25% { transform: translate(-5px, 2px); }
  50% { transform: translate(5px, -2px); }
  75% { transform: translate(-3px, -1px); }
}

@keyframes vp-shake-heavy {
  0%, 100% { transform: translate(0, 0); }
  10% { transform: translate(-12px, 6px); }
  25% { transform: translate(11px, -8px); }
  40% { transform: translate(-9px, 4px); }
  55% { transform: translate(8px, 6px); }
  70% { transform: translate(-6px, -4px); }
  85% { transform: translate(4px, 2px); }
}

@media (prefers-reduced-motion: reduce) {
  .screen-shake-soft, .screen-shake-heavy { animation: none !important; }
}

/* ============================================
   Ambient FX — scene atmosphere particles (see js/ambient.js)
   ============================================ */
.ambient-host {
  overflow: hidden;
}

.amb-p {
  position: absolute;
  pointer-events: none;
  will-change: transform, opacity;
  opacity: 0;
}

.amb-firefly {
  border-radius: 50%;
  background: #ffe9a0;
  box-shadow: 0 0 8px 2px rgba(255, 233, 160, 0.65);
}

.amb-dust {
  border-radius: 50%;
  background: rgba(255, 244, 214, 0.85);
}

.amb-sparkle, .amb-glint {
  background: currentColor;
  color: #ffe888;
  clip-path: polygon(50% 0%, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0% 50%, 40% 40%);
  filter: drop-shadow(0 0 6px currentColor);
}

.amb-glint {
  color: #cdf3ff;
}

.amb-mist {
  border-radius: 50%;
  background: radial-gradient(ellipse at center, rgba(190, 220, 215, 0.9) 0%, transparent 70%);
  filter: blur(14px);
}

.amb-wisp {
  border-radius: 50%;
  background: radial-gradient(ellipse at center, rgba(255, 255, 255, 0.9) 0%, transparent 70%);
  filter: blur(10px);
}

.amb-ember {
  border-radius: 50%;
  background: #ffb050;
  box-shadow: 0 0 6px 2px rgba(255, 150, 60, 0.6);
}

.amb-bubble {
  border-radius: 50%;
  background: transparent;
  border: 1.5px solid rgba(255, 255, 255, 0.55);
  box-shadow: inset -1px -1px 2px rgba(255, 255, 255, 0.35);
}

.amb-leaf {
  border-radius: 60% 5% 60% 5%;
  background: #7a9a4a;
  opacity: 0.85;
}

/* ============================================
   Themed Transitions — pixel dissolve + iris
   ============================================ */
#pixel-overlay {
  position: absolute;
  inset: 0;
  display: none;
  z-index: 100;
  pointer-events: none;
}
#pixel-overlay.on {
  display: grid;
  pointer-events: auto;
}
#pixel-overlay .px-cell {
  background: #000;
  opacity: 0;
  transition-property: opacity;
  transition-timing-function: steps(3, end);   /* chunky, not smooth — pixel feel */
}
#pixel-overlay.covering .px-cell {
  opacity: 1;
}

#iris-overlay {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  display: none;
  width: 240vmax;
  height: 240vmax;
  box-sizing: content-box;
  border: 220vmax solid #000;
  border-radius: 50%;
  z-index: 100;
  pointer-events: auto;
}

/* ============================================
   Unified tactile press behaviour (.game-btn)
   Additive class: keeps each button's visual identity, standardises the
   physical press response. Apply to every tappable control.
   ============================================ */
.game-btn {
  transition: transform 0.08s ease-out, filter 0.08s ease-out, box-shadow 0.08s ease-out;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
.game-btn:active {
  transform: translateY(2px) scale(0.96);
  filter: brightness(1.3);
}
.game-btn:disabled:active {
  transform: none;
  filter: none;
}

/* .pixel-btn presses DOWN, not up — overrides the legacy scale-up :active.
   (Hover keeps the gentle lift; the press itself should feel physical.) */
.pixel-btn:active {
  transform: translateY(2px) scale(0.97);
  filter: brightness(1.25);
  box-shadow: 0 0 8px rgba(218, 186, 106, 0.3);
}
.pixel-btn:disabled:active {
  transform: none;
  filter: none;
}

/* ============================================
   Utility
   ============================================ */
.hidden { display: none !important; }

/* Screen shake for wrong code */
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-8px); }
  40% { transform: translateX(8px); }
  60% { transform: translateX(-6px); }
  80% { transform: translateX(6px); }
}

.shake {
  animation: shake 0.4s ease-out;
}

/* Fade-in for elements */
@keyframes fade-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

.fade-in {
  animation: fade-in 0.5s ease-out forwards;
}

/* Princess Pumpkin glitch effect — opacity + colour shift only, no position shift */
.glitch-text {
  position: relative;
  animation: glitch-flicker 4s infinite;
}

@keyframes glitch-flicker {
  0%, 90%, 100% { opacity: 1; filter: none; }
  91% { opacity: 0.7; filter: hue-rotate(40deg) saturate(1.5); }
  92% { opacity: 0.5; filter: hue-rotate(-30deg) brightness(1.3); }
  93% { opacity: 0.8; filter: hue-rotate(20deg); }
  94% { opacity: 1; filter: none; }
}

/* Digital scan lines overlay for PP scenes */
.scanlines::after {
  content: '';
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    to bottom,
    transparent 0px,
    transparent 2px,
    rgba(0, 0, 0, 0.05) 2px,
    rgba(0, 0, 0, 0.05) 4px
  );
  pointer-events: none;
  z-index: 20;
}

/* ============================================
   Opening Scene — Portal + PP Cinematic
   ============================================ */

/* Portal wrapper: centred in scene, handles scale animation */
.portal-wrapper {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 3;
}

/* The portal image itself */
.portal-image {
  width: 65%;
  max-width: 620px;
  aspect-ratio: 3 / 2;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  image-rendering: pixelated;
  transform: scale(0);
  opacity: 0;
  transition: none;
  /* Fade edges into the void so the rectangular border isn't visible */
  -webkit-mask-image: radial-gradient(ellipse 48% 48% at 50% 50%, #000 55%, transparent 100%);
  mask-image: radial-gradient(ellipse 48% 48% at 50% 50%, #000 55%, transparent 100%);
}

/* Portal entrance animation — expand from a dot */
.portal-image.portal-enter {
  animation: portal-expand 1.8s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes portal-expand {
  0% { transform: scale(0); opacity: 0; }
  20% { transform: scale(0.05); opacity: 0.6; }
  60% { transform: scale(0.9); opacity: 1; }
  80% { transform: scale(1.03); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

/* Ongoing portal idle animation — slow rotate, gentle pulse */
.portal-image.portal-idle {
  transform: scale(1);
  opacity: 1;
  animation: portal-pulse 6s ease-in-out infinite, portal-hue 12s linear infinite;
}

@keyframes portal-pulse {
  0%, 100% { transform: scale(1) rotate(0deg); filter: brightness(1); }
  50% { transform: scale(1.03) rotate(3deg); filter: brightness(1.1); }
}

@keyframes portal-hue {
  0% { filter: hue-rotate(0deg); }
  100% { filter: hue-rotate(25deg); }
}

/* Portal collapse — reverse of expand */
.portal-image.portal-collapse {
  animation: portal-collapse 1s cubic-bezier(0.55, 0, 1, 0.45) forwards;
}

@keyframes portal-collapse {
  0% { transform: scale(1); opacity: 1; }
  40% { transform: scale(1.1); opacity: 1; filter: brightness(1.8); }
  100% { transform: scale(0); opacity: 0; filter: brightness(3); }
}

/* PP silhouette inside the portal */
.portal-silhouette {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 22%;
  max-width: 200px;
  opacity: 0;
  z-index: 4;
  image-rendering: pixelated;
  pointer-events: none;
  animation: none;
}

.portal-silhouette.visible {
  opacity: 0.35;
  animation: silhouette-float 4s ease-in-out infinite;
}

@keyframes silhouette-float {
  0%, 100% { transform: translate(-50%, -50%) translateY(0); }
  50% { transform: translate(-50%, -50%) translateY(-6px); }
}

/* Full-screen glitch effect — applied to the scene screen */
.screen-glitch {
  animation: screen-glitch-burst 0.15s steps(1) forwards;
}

@keyframes screen-glitch-burst {
  0% { transform: translate(3px, 0); filter: hue-rotate(60deg) saturate(2); }
  33% { transform: translate(-4px, 1px); filter: hue-rotate(-40deg) brightness(1.3); }
  66% { transform: translate(2px, -1px); filter: hue-rotate(30deg) saturate(1.5); }
  100% { transform: translate(0); filter: none; }
}

/* Spell seal preview flash */
.spell-seals.preview-flash {
  opacity: 1 !important;
  pointer-events: none;
  animation: seal-preview 2.5s ease-in-out forwards;
}

@keyframes seal-preview {
  0% { opacity: 0; }
  20% { opacity: 0.8; }
  70% { opacity: 0.8; }
  100% { opacity: 0; }
}

/* ---- Finale seal dissolve (all seals rise + fade into golden light) ---- */
/* Applied to the container; each seal reads its own data-level for stagger. */
.spell-seals.dissolving {
  opacity: 1 !important;
  pointer-events: none;
}
.spell-seals.dissolving .seal {
  /* Each seal starts from its normal broken-glow state and rises away. */
  animation: seal-dissolve 2.4s ease-in forwards;
  /* Stagger via the data-level attribute (1..7) so seals lift in a wave. */
}
.spell-seals.dissolving .seal[data-level="1"] { animation-delay: 0.00s; }
.spell-seals.dissolving .seal[data-level="2"] { animation-delay: 0.12s; }
.spell-seals.dissolving .seal[data-level="3"] { animation-delay: 0.24s; }
.spell-seals.dissolving .seal[data-level="4"] { animation-delay: 0.36s; }
.spell-seals.dissolving .seal[data-level="5"] { animation-delay: 0.48s; }
.spell-seals.dissolving .seal[data-level="6"] { animation-delay: 0.60s; }
.spell-seals.dissolving .seal[data-level="7"] { animation-delay: 0.72s; }

@keyframes seal-dissolve {
  0% {
    opacity: 1;
    transform: translateY(0) scale(1);
    filter: brightness(1.1) saturate(1.2) drop-shadow(0 0 6px rgba(255, 232, 136, 0.6));
  }
  20% {
    /* Flare brighter before lift — like it's charging up */
    opacity: 1;
    transform: translateY(0) scale(1.25);
    filter: brightness(2.2) saturate(1.6) drop-shadow(0 0 24px rgba(255, 232, 136, 1));
  }
  60% {
    opacity: 0.85;
    transform: translateY(-60px) scale(1.1);
    filter: brightness(2.4) saturate(1.2) blur(3px) drop-shadow(0 0 30px rgba(255, 220, 120, 0.9));
  }
  100% {
    opacity: 0;
    transform: translateY(-180px) scale(0.6);
    filter: brightness(3) saturate(0.4) blur(14px) drop-shadow(0 0 40px rgba(255, 220, 120, 0.4));
  }
}

/* Rising golden-light motes spawned during the seal dissolve — pure CSS, JS
   just appends the elements; each gets a random horizontal position + delay. */
.golden-motes {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 55;
  overflow: hidden;
}
.golden-mote {
  position: absolute;
  bottom: 0;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgba(255, 232, 136, 0.9);
  box-shadow:
    0 0 10px 2px rgba(255, 232, 136, 0.8),
    0 0 20px 6px rgba(255, 210, 120, 0.5);
  animation: mote-rise 3.8s ease-out forwards;
  opacity: 0;
}
@keyframes mote-rise {
  0%   { opacity: 0; transform: translateY(20px) scale(0.6); }
  12%  { opacity: 1; }
  80%  { opacity: 0.8; }
  100% { opacity: 0; transform: translateY(-110vh) scale(0.3); }
}

/* Name plate flicker for unstable connection */
@keyframes nameplate-flicker {
  0%, 85%, 100% { opacity: 1; }
  87% { opacity: 0.3; }
  89% { opacity: 0.7; }
  91% { opacity: 0.2; }
  93% { opacity: 0.9; }
}
