/* ============================================================
   GAME LAYER  (css/game.css + js/game.js)
   Loaded by the homepage and 404.html only; css/style.css is
   the site's own design system and never depends on this file.

   Every piece is switched on by a word in <body data-fx="...">.
   Delete a word to veto that piece: anything tagged with the
   matching .fx-<word> class disappears, word-gated CSS stops
   matching, and js/game.js skips that feature. Remove this
   stylesheet + js/game.js to drop the whole layer.

   Art: 0x72, pixel-boy & AAA (Ninja Adventure), LuizMelo,
   Smithy Games, Foozle, greatdocbrown, Crusenho — all listed on
   credits.html. Sprites live in img/sprites/, one strip per
   animation.

   Performance: sections scrolled off screen get .fx-offscreen
   (from js/game.js), which pauses every animation inside them.
   Motion uses transform/opacity only, particle counts drop on
   phones, and prefers-reduced-motion stops all of it.
   ============================================================ */

/* --- Switchboard --------------------------------------------- */
body:not([data-fx~="player-one"]) .fx-player-one,
body:not([data-fx~="critters"]) .fx-critters,
body:not([data-fx~="patrol"]) .fx-patrol,
body:not([data-fx~="coin-hunt"]) .fx-coin-hunt,
body:not([data-fx~="sparkles"]) .fx-sparkles,
body:not([data-fx~="section-flags"]) .fx-section-flags,
body:not([data-fx~="fountains"]) .fx-fountains,
body:not([data-fx~="crow"]) .fx-crow,
body:not([data-fx~="nap-cat"]) .fx-nap-cat,
body:not([data-fx~="butterflies"]) .fx-butterflies,
body:not([data-fx~="pet-snow"]) .fx-pet-snow,
body:not([data-fx~="quest-stamps"]) .fx-quest-stamps,
body:not([data-fx~="d20"]) .fx-d20,
body:not([data-fx~="campfire"]) .fx-campfire,
body:not([data-fx~="lever"]) .fx-lever,
body:not([data-fx~="sfx"]) .fx-sfx,
body:not([data-fx~="achievements"]) .fx-achievements,
body:not([data-fx~="boot-screen"]) .fx-boot-screen {
  display: none !important;
}

:root {
  --font-pixel: 'Silkscreen', 'JetBrains Mono', monospace;
}

/* Off-screen sections: freeze everything inside them. */
.fx-offscreen *,
.fx-offscreen *::before,
.fx-offscreen *::after {
  animation-play-state: paused !important;
}

/* --- Sprite strips -------------------------------------------
   --fw/--fh: frame size in source pixels. --frames: frames in
   the strip. --px: on-screen scale (3 by default so sprites
   share one pixel size). --dur: one pass through the strip. */
.sprite {
  --px: 3;
  --dur: 0.6s;
  display: inline-block;
  flex-shrink: 0;
  width: calc(var(--fw) * var(--px) * 1px);
  height: calc(var(--fh) * var(--px) * 1px);
  background-repeat: no-repeat;
  background-position: 0 0;
  background-size: calc(var(--fw) * var(--frames) * var(--px) * 1px) 100%;
  image-rendering: pixelated;
  animation: sprite-play var(--dur) steps(var(--frames)) infinite;
}

@keyframes sprite-play {
  to {
    background-position-x: calc(var(--fw) * var(--frames) * var(--px) * -1px);
  }
}

/* Play a strip once and end on the empty frame past the last. */
.sprite.play-once {
  animation: sprite-play var(--dur) steps(var(--frames)) 1 forwards;
}

/* Hold a single frame: set --frame on the element. */
.sprite.hold {
  animation: none;
  background-position-x: calc(var(--frame, 0) * var(--fw) * var(--px) * -1px);
}

.sprite-knight-idle { --fw: 16; --fh: 28; --frames: 4; --dur: 0.7s; background-image: url(../img/sprites/knight-idle.png); }
.sprite-knight-run { --fw: 16; --fh: 28; --frames: 4; --dur: 0.42s; background-image: url(../img/sprites/knight-run.png); }
.sprite-chort { --fw: 16; --fh: 23; --frames: 4; --dur: 0.7s; background-image: url(../img/sprites/chort-idle.png); }
.sprite-necromancer { --fw: 16; --fh: 23; --frames: 4; --dur: 0.9s; background-image: url(../img/sprites/necromancer.png); }
.sprite-slug { --fw: 16; --fh: 16; --frames: 4; --dur: 0.8s; background-image: url(../img/sprites/tiny-slug.png); }
.sprite-coin { --fw: 6; --fh: 7; --frames: 4; --dur: 0.6s; background-image: url(../img/sprites/coin.png); }
.sprite-coin-spin { --fw: 16; --fh: 16; --frames: 6; --dur: 0.6s; background-image: url(../img/sprites/coin-spin.png); }
.sprite-chest { --fw: 16; --fh: 16; --frames: 3; background-image: url(../img/sprites/chest.png); animation: none; }
.sprite-bat { --fw: 16; --fh: 16; --frames: 4; --dur: 0.4s; background-image: url(../img/sprites/bat.png); }
.sprite-butterfly { --fw: 16; --fh: 16; --frames: 4; --dur: 0.5s; background-image: url(../img/sprites/butterfly.png); }
.sprite-mouse { --fw: 16; --fh: 16; --frames: 4; --dur: 0.32s; background-image: url(../img/sprites/mouse-run.png); }
.sprite-emote { --fw: 14; --fh: 13; --frames: 7; background-image: url(../img/sprites/emotes.png); animation: none; }
.sprite-flag-red { --fw: 16; --fh: 16; --frames: 4; --dur: 0.7s; background-image: url(../img/sprites/flag-red.png); }
.sprite-flag-blue { --fw: 16; --fh: 16; --frames: 4; --dur: 0.7s; background-image: url(../img/sprites/flag-blue.png); }
.sprite-flag-green { --fw: 16; --fh: 16; --frames: 4; --dur: 0.7s; background-image: url(../img/sprites/flag-green.png); }
.sprite-flag-yellow { --fw: 16; --fh: 16; --frames: 4; --dur: 0.7s; background-image: url(../img/sprites/flag-yellow.png); }
.sprite-fire { --fw: 8; --fh: 12; --frames: 8; --dur: 0.8s; background-image: url(../img/sprites/fire.png); }
.sprite-twinkle { --fw: 10; --fh: 8; --frames: 7; --dur: 0.7s; background-image: url(../img/sprites/twinkle.png); }
.sprite-burst { --fw: 32; --fh: 32; --frames: 6; --dur: 0.36s; background-image: url(../img/sprites/burst.png); }
.sprite-sparkle-burst { --fw: 30; --fh: 35; --frames: 9; --dur: 0.8s; background-image: url(../img/sprites/sparkle-burst.png); }
.sprite-smoke { --fw: 32; --fh: 32; --frames: 6; --dur: 0.5s; background-image: url(../img/sprites/smoke.png); }
.sprite-slash { --fw: 32; --fh: 32; --frames: 4; --dur: 0.24s; background-image: url(../img/sprites/slash.png); }
.sprite-snow { --fw: 8; --fh: 8; --frames: 7; background-image: url(../img/sprites/snow.png); }
.sprite-lever { --fw: 16; --fh: 16; --frames: 2; background-image: url(../img/sprites/lever.png); animation: none; }
.sprite-fountain { --fw: 16; --fh: 48; --frames: 3; --dur: 0.45s; background-image: url(../img/sprites/fountain.png); }
.sprite-cat-sleep { --fw: 24; --fh: 14; --frames: 2; --dur: 2.4s; background-image: url(../img/sprites/cat-sleep.png); }
.sprite-cat-meow { --fw: 24; --fh: 14; --frames: 4; --dur: 0.5s; background-image: url(../img/sprites/cat-meow.png); }
.sprite-meow { --fw: 16; --fh: 16; --frames: 3; --dur: 0.45s; background-image: url(../img/sprites/meow.png); }
.sprite-crow-idle { --fw: 35; --fh: 27; --frames: 7; --dur: 1.2s; background-image: url(../img/sprites/crow-idle.png); }
.sprite-crow-fly { --fw: 35; --fh: 27; --frames: 5; --dur: 0.45s; background-image: url(../img/sprites/crow-fly.png); }
.sprite-portal { --fw: 41; --fh: 41; --frames: 10; --dur: 0.9s; background-image: url(../img/sprites/portal.png); }

/* --- Skills: inventory slots (always on) --------------------- */
.skill-group h3 {
  display: flex;
  align-items: center;
  gap: 12px;
}

.inv-slot {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  overflow: hidden;
  background: var(--ink);
  border: 2px solid var(--shadow-ink);
  border-radius: 7px;
  box-shadow: inset 0 0 0 2px var(--border), 2px 2px 0 var(--shadow-ink);
}

.inv-slot img {
  width: 32px;
  height: 32px;
  image-rendering: pixelated;
}

/* --- inv-shine ----------------------------------------------- */
body[data-fx~="inv-shine"] .inv-slot::after {
  content: '';
  position: absolute;
  inset: -20px auto -20px -30px;
  width: 14px;
  background: rgba(255, 248, 231, 0.55);
  transform: rotate(25deg) translateX(0);
  animation: inv-shine 4.5s steps(12) infinite;
}

body[data-fx~="inv-shine"] .skill-group:nth-child(2) .inv-slot::after { animation-delay: 1.1s; }
body[data-fx~="inv-shine"] .skill-group:nth-child(3) .inv-slot::after { animation-delay: 2.2s; }
body[data-fx~="inv-shine"] .skill-group:nth-child(4) .inv-slot::after { animation-delay: 3.3s; }

@keyframes inv-shine {
  0%, 70% { transform: rotate(25deg) translateX(0); }
  100% { transform: rotate(25deg) translateX(110px); }
}

body[data-fx~="inv-shine"] .skill-group:hover .inv-slot img {
  animation: item-bob 0.5s steps(4) infinite alternate;
}

@keyframes item-bob {
  to { transform: translateY(-4px); }
}

/* --- player-one ---------------------------------------------- */
.photo-card {
  position: relative;
}

.player-one {
  position: absolute;
  bottom: 100%;
  right: 26px;
  display: flex;
  flex-direction: column;
  align-items: center;
  pointer-events: none;
}

.player-one > .sprite {
  display: block;
}

.p1-tag {
  position: relative;
  /* the knight strip has 8 empty rows (24px) above the helmet */
  margin-bottom: -14px;
  padding: 3px 6px 2px;
  font-family: var(--font-pixel);
  font-size: 11px;
  line-height: 1;
  color: var(--ink);
  background: var(--accent);
  border: 2px solid var(--ink);
  border-radius: 4px;
  animation: p1-bob 1.1s ease-in-out infinite;
}

.p1-tag::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 100%;
  margin-left: -5px;
  border: 5px solid transparent;
  border-top-color: var(--ink);
}

@keyframes p1-bob {
  50% { transform: translateY(-4px); }
}

/* p1-emotes: a bubble beside the knight's head (.sprite.hold would
   otherwise cancel the pop-in animation) */
.sprite.p1-emote {
  position: absolute;
  right: calc(100% - 6px);
  top: 18px;
  --px: 2;
  animation: emote-pop 0.2s steps(3) both;
}

@keyframes emote-pop {
  from { transform: scale(0); }
  to { transform: scale(1); }
}

/* idle-zzz */
.player-one.is-sleeping > .sprite-knight-idle {
  --dur: 2s;
}

.player-one.is-sleeping .p1-tag {
  opacity: 0.35;
}

.zzz {
  position: absolute;
  right: -4px;
  top: 12px;
  font-family: var(--font-pixel);
  font-size: 14px;
  color: var(--cream);
  text-shadow: 2px 2px 0 var(--shadow-ink);
  opacity: 0;
  animation: zzz-float 2.4s ease-out infinite;
}

.zzz:nth-child(2) { animation-delay: 0.8s; font-size: 12px; }
.zzz:nth-child(3) { animation-delay: 1.6s; font-size: 10px; }

@keyframes zzz-float {
  0% { opacity: 0; transform: translate(0, 0); }
  20% { opacity: 1; }
  100% { opacity: 0; transform: translate(18px, -34px); }
}

/* --- sparkles: glints on the hero ----------------------------- */
.glint {
  position: absolute;
  z-index: 2;
  pointer-events: none;
  animation: glint var(--glint-dur, 3.4s) steps(7) infinite var(--glint-delay, 0s);
}

@keyframes glint {
  0% { background-position-x: 0; }
  22%, 100% { background-position-x: calc(var(--fw) * var(--frames) * var(--px) * -1px); }
}

.glint-anchor {
  position: relative;
  display: inline-block;
  width: 0;
  height: 0;
  vertical-align: top;
}

.glint-anchor .glint { left: -4px; top: -6px; }
.photo-card .glint-a { top: -16px; left: -16px; --glint-delay: 1.2s; }
.photo-card .glint-b { bottom: 54px; right: -18px; --glint-delay: 2.3s; --glint-dur: 4.1s; }
.hero-actions .btn-primary { position: relative; }
.hero-actions .glint { top: -14px; right: -14px; --glint-delay: 0.6s; --glint-dur: 3.9s; }

/* --- typewriter ---------------------------------------------- */
/* Hidden until js/game.js takes over (it can start late while the
   font stylesheet loads); shows on its own after 2.5s if it never does. */
body[data-fx~="typewriter"] .hero .t-type {
  opacity: 0;
  animation: tw-fallback 0s 2.5s forwards;
}

body[data-fx~="typewriter"] .hero .tw-ready .t-type {
  opacity: 1;
  animation: none;
}

@keyframes tw-fallback {
  to { opacity: 1; }
}

.is-typing .terminal-cursor {
  animation: none;
}

/* --- flyby: a bat crosses the hero ----------------------------- */
.hero {
  isolation: isolate;
}

.flyby-lane {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}

.flyby {
  position: absolute;
  left: -60px;
  animation: flyby-x var(--fly-time, 7s) linear forwards;
}

.flyby .sprite {
  display: block;
  animation: sprite-play var(--dur) steps(var(--frames)) infinite, flyby-bob 1.1s ease-in-out infinite;
}

@keyframes flyby-x {
  to { transform: translateX(calc(var(--fly-w, 1200px) + 120px)); }
}

@keyframes flyby-bob {
  50% { translate: 0 -22px; }
}

/* --- menu-cursor: JRPG pointer on the active nav link ------- */
body[data-fx~="menu-cursor"] .site-nav a:not(.btn) {
  position: relative;
}

body[data-fx~="menu-cursor"] .site-nav a.is-active::before {
  content: '';
  position: absolute;
  top: 50%;
  left: -14px;
  width: 8px;
  height: 10px;
  margin-top: -5px;
  background: var(--accent);
  /* stepped triangle so it reads as a pixel cursor */
  clip-path: polygon(0 0, 25% 0, 25% 10%, 50% 10%, 50% 30%, 75% 30%, 75% 40%, 100% 40%, 100% 60%, 75% 60%, 75% 70%, 50% 70%, 50% 90%, 25% 90%, 25% 100%, 0 100%);
  animation: menu-cursor 0.6s steps(2) infinite;
}

@keyframes menu-cursor {
  50% { transform: translateX(-3px); }
}

/* --- xp-bar -------------------------------------------------- */
.xp-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -3px;
  height: 4px;
  pointer-events: none;
}

.xp-fill {
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(90deg, var(--accent) 0 6px, var(--accent-strong) 6px 8px);
  transform-origin: left center;
  transform: scaleX(0);
}

/* HUD tabs: LV, sound, and achievements hang under the header */
.hud-tabs {
  position: absolute;
  top: 100%;
  right: max(24px, calc((100% - var(--maxw)) / 2 + 24px));
  display: flex;
  gap: 6px;
}

.hud-tab {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 22px;
  padding: 1px 8px 0;
  font-family: var(--font-pixel);
  font-size: 10px;
  line-height: 1;
  color: var(--text-dim);
  background: #150f0a;
  border: 2px solid var(--border);
  border-top: 0;
  border-radius: 0 0 6px 6px;
  cursor: pointer;
}

.hud-tab img {
  width: 14px;
  height: 14px;
  image-rendering: pixelated;
}

.hud-tab:hover,
.hud-tab[aria-pressed="true"] {
  color: var(--accent);
  border-color: var(--accent);
}

.hud-tab.is-bump {
  animation: hud-bump 0.35s ease;
}

.hud-tab.xp-lv {
  color: var(--ink);
  background: var(--accent);
  border-color: var(--ink);
  cursor: default;
}

body.nav-open .hud-tabs {
  display: none;
}

.level-up {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  font-family: var(--font-pixel);
  font-size: 13px;
  color: var(--accent);
  text-shadow: 2px 2px 0 var(--shadow-ink);
  white-space: nowrap;
  animation: level-up 1.6s ease-out forwards;
}

@keyframes level-up {
  0% { opacity: 0; transform: translateY(8px) scale(0.6); }
  15% { opacity: 1; transform: translateY(0) scale(1.1); }
  25% { transform: scale(1); }
  80% { opacity: 1; }
  100% { opacity: 0; transform: translateY(-10px); }
}

/* --- critters ------------------------------------------------ */
.critter {
  position: absolute;
  bottom: 100%;
  margin-bottom: -3px;
  z-index: 2;
  pointer-events: none;
}

.critter-left { left: 30px; }
.critter-right { right: 30px; }

body[data-fx~="critters"] #projects .projects-grid {
  row-gap: 96px;
  padding-top: 24px;
}

/* critter-react: hop and "!" when their card is hovered */
body[data-fx~="critter-react"] .critter::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: calc(100% - 4px);
  width: 42px;
  height: 39px;
  margin-left: -21px;
  background: url(../img/sprites/emotes.png) 0 0 / 294px 39px no-repeat;
  image-rendering: pixelated;
  transform: scale(0);
  transform-origin: bottom center;
  transition: transform 0.15s steps(3);
}

body[data-fx~="critter-react"] .project-card:hover .critter::after,
body[data-fx~="critter-react"] .project-card:focus-within .critter::after {
  transform: scale(1);
}

body[data-fx~="critter-react"] .project-card:hover .critter {
  animation: sprite-play var(--dur) steps(var(--frames)) infinite, critter-hop 0.5s steps(5) 1;
}

@keyframes critter-hop {
  40% { translate: 0 -14px; }
}

/* combat: poke the critters */
body[data-fx~="combat"] .critter {
  pointer-events: auto;
  cursor: crosshair;
}

.critter:focus-visible {
  outline: 2px dashed var(--accent);
  outline-offset: 2px;
}

/* same weight as the critter-react hover rule, and later, so a hit
   still shakes while the card is hovered */
body[data-fx] .project-card .critter.is-hit {
  filter: brightness(2.6) saturate(0);
  animation: sprite-play var(--dur) steps(var(--frames)) infinite, hit-shake 0.2s steps(4);
}

@keyframes hit-shake {
  25% { translate: -4px 0; }
  75% { translate: 4px 0; }
}

.critter.is-defeated {
  visibility: hidden;
}

body[data-fx] .project-card .critter.is-respawning {
  animation: sprite-play var(--dur) steps(var(--frames)) infinite, respawn-blink 0.9s steps(6);
}

@keyframes respawn-blink {
  0%, 33%, 66% { opacity: 0; }
  16%, 50%, 83%, 100% { opacity: 1; }
}

.critter-hp {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 4px);
  width: 54px;
  height: 12px;
  margin-left: -27px;
  background: url(../img/sprites/hp-under.png) 0 0 / 100% 100%;
  image-rendering: pixelated;
}

.critter-hp-fill {
  position: absolute;
  inset: 0;
  background: url(../img/sprites/hp-fill.png) 0 0 / 54px 12px;
  image-rendering: pixelated;
  transform-origin: left center;
  transition: transform 0.2s steps(3);
}

.fx-oneshot {
  position: absolute;
  z-index: 4;
  pointer-events: none;
}

.dmg-pop {
  position: fixed;
  z-index: 700;
  font-family: var(--font-pixel);
  font-size: 16px;
  color: var(--cream);
  text-shadow: 2px 2px 0 var(--rose-strong), -1px -1px 0 var(--shadow-ink);
  pointer-events: none;
  animation: dmg-pop 0.8s ease-out forwards;
}

.dmg-pop.is-crit {
  font-size: 20px;
  color: var(--accent);
}

@keyframes dmg-pop {
  0% { transform: translateY(0) scale(0.6); opacity: 1; }
  20% { transform: translateY(-14px) scale(1.2); }
  100% { transform: translateY(-40px) scale(1); opacity: 0; }
}

/* rat-run: mice scurry along the top of the Tails card */
.mouse-run {
  position: absolute;
  bottom: 100%;
  left: 10px;
  margin-bottom: -4px;
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  animation: mouse-run 2.8s linear forwards;
}

.mouse-run .sprite {
  --px: 2;
  display: block;
}

@keyframes mouse-run {
  0% { transform: translateX(0); opacity: 0; }
  8% { opacity: 1; }
  90% { opacity: 1; }
  100% { transform: translateX(var(--run, 280px)); opacity: 0; }
}

/* --- press-start --------------------------------------------- */
body[data-fx~="press-start"] .project-card > .media-frame::before {
  content: '\25B6  PRESS START';
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-pixel);
  font-size: 16px;
  letter-spacing: 0.08em;
  white-space: pre;
  color: var(--cream);
  text-shadow: 3px 3px 0 var(--shadow-ink);
  background: rgba(20, 13, 8, 0.62);
  opacity: 0;
  transition: opacity 0.15s ease;
}

body[data-fx~="press-start"] .project-card > .media-frame:hover::before,
body[data-fx~="press-start"] .project-card > .media-frame:focus-visible::before {
  opacity: 1;
  animation: press-blink 1s steps(1) infinite;
}

@keyframes press-blink {
  50% { color: transparent; text-shadow: none; }
}

/* --- pet-snow: snowfall over the Project P.E.T. key art ------ */
.snowfall {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  pointer-events: none;
  container-type: size;
}

.snowfall i {
  position: absolute;
  top: -16px;
  left: var(--x);
  width: 16px;
  height: 16px;
  background: url(../img/sprites/snow.png) calc(var(--f) * -16px) 0 / 112px 16px no-repeat;
  image-rendering: pixelated;
  animation: snow-fall var(--t) linear infinite var(--d);
}

.snowfall i:nth-child(1) { --x: 8%; --t: 7s; --d: -1s; --f: 3; }
.snowfall i:nth-child(2) { --x: 22%; --t: 9s; --d: -5s; --f: 1; }
.snowfall i:nth-child(3) { --x: 37%; --t: 6.5s; --d: -3s; --f: 2; }
.snowfall i:nth-child(4) { --x: 51%; --t: 8s; --d: -6.5s; --f: 4; }
.snowfall i:nth-child(5) { --x: 64%; --t: 7.5s; --d: -2s; --f: 1; }
.snowfall i:nth-child(6) { --x: 76%; --t: 9.5s; --d: -7s; --f: 3; }
.snowfall i:nth-child(7) { --x: 88%; --t: 6.8s; --d: -4s; --f: 2; }
.snowfall i:nth-child(8) { --x: 45%; --t: 10s; --d: -8.5s; --f: 5; }

@keyframes snow-fall {
  0% { transform: translate(0, 0); }
  50% { transform: translate(10px, calc(50cqh + 8px)); }
  100% { transform: translate(-4px, calc(100cqh + 20px)); }
}

/* --- section-flags ------------------------------------------- */
.section-flag {
  --px: 2;
  margin-left: 4px;
  transform: rotate(1deg);
}

/* --- fountains + crow on the devlog book -------------------- */
.devlog-book .fountain {
  position: absolute;
  bottom: 0;
}

.devlog-book .fountain-l { left: -78px; }
.devlog-book .fountain-r { right: -78px; }
.devlog-book .fountain-r .sprite { animation-delay: -0.2s; }

.crow {
  position: absolute;
  z-index: 2;
  top: -48px;
  right: 16px;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
}

.crow .sprite {
  --px: 2;
  display: block;
}

.crow:focus-visible {
  outline: 2px dashed var(--accent);
  outline-offset: 2px;
}

.crow.is-flying {
  pointer-events: none;
  animation: crow-away 2.8s ease-in-out forwards;
}

@keyframes crow-away {
  0% { transform: translate(0, 0); }
  35% { transform: translate(140px, -120px); opacity: 1; }
  40% { opacity: 0; }
  60% { transform: translate(-160px, -140px); opacity: 0; }
  65% { opacity: 1; }
  100% { transform: translate(0, 0); }
}

.caw {
  position: absolute;
  z-index: 3;
  top: -80px;
  right: 40px;
  width: 62px;
  height: 34px;
  background: url(../img/sprites/caw.png) 0 0 / 100% 100%;
  image-rendering: pixelated;
  pointer-events: none;
  animation: caw-pop 0.9s ease-out forwards;
}

@keyframes caw-pop {
  0% { transform: scale(0) rotate(-10deg); opacity: 1; }
  20% { transform: scale(1.2) rotate(4deg); }
  30% { transform: scale(1) rotate(0); }
  80% { opacity: 1; }
  100% { transform: translateY(-14px); opacity: 0; }
}

/* --- nap-cat: asleep on the About fact card ------------------ */
.nap-cat {
  position: absolute;
  z-index: 2;
  bottom: 100%;
  right: 34px;
  margin-bottom: -3px;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
}

.nap-cat .sprite {
  display: block;
}

.nap-cat:focus-visible {
  outline: 2px dashed var(--accent);
  outline-offset: 2px;
}

.nap-cat .meow {
  position: absolute;
  bottom: calc(100% - 6px);
  left: -10px;
  --px: 2;
}

/* --- butterflies around Tools of the Trade ------------------- */
#skills {
  position: relative;
}

.butterflies {
  position: absolute;
  inset: -40px 0 -20px;
  z-index: 3;
  pointer-events: none;
  container-type: size;
}

.bfly {
  position: absolute;
  top: 0;
  left: 0;
}

.bfly .sprite {
  --px: 2;
  display: block;
}

.bfly-a { animation: bfly-a 22s ease-in-out infinite; }
.bfly-b { animation: bfly-b 27s ease-in-out infinite -9s; }
.bfly-b .sprite { animation-delay: -0.2s; }

@keyframes bfly-a {
  0%, 100% { transform: translate(6cqw, 4cqh); }
  20% { transform: translate(28cqw, 0); }
  40% { transform: translate(46cqw, 12cqh); }
  60% { transform: translate(70cqw, 2cqh); }
  80% { transform: translate(36cqw, 20cqh); }
}

@keyframes bfly-b {
  0%, 100% { transform: translate(92cqw, 6cqh); }
  25% { transform: translate(74cqw, 30cqh); }
  50% { transform: translate(55cqw, 2cqh); }
  75% { transform: translate(84cqw, 18cqh); }
}

/* --- crt: scanlines + flicker on the terminal cards ---------- */
body[data-fx~="crt"] .terminal-card {
  position: relative;
}

body[data-fx~="crt"] .terminal-card::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(0deg, rgba(0, 0, 0, 0.22) 0 1px, transparent 1px 3px);
  animation: crt-flicker 5s steps(1) infinite;
}

body[data-fx~="crt"] .terminal-body {
  text-shadow: 0 0 6px rgba(215, 230, 201, 0.35);
}

@keyframes crt-flicker {
  0%, 100% { opacity: 1; }
  47% { opacity: 0.7; }
  48% { opacity: 1; }
  82% { opacity: 0.85; }
  83% { opacity: 1; }
}

/* --- shipped-flags: waving flags in the Games Made list ------ */
body[data-fx~="shipped-flags"] .game-status.shipped {
  --fw: 16; --fh: 16; --frames: 4; --px: 2;
  width: 32px;
  height: 32px;
  margin: -10px -6px -10px -8px;
  font-size: 0;
  background: url(../img/sprites/flag-red.png) 0 0 / 128px 32px no-repeat;
  image-rendering: pixelated;
  animation: sprite-play 0.7s steps(4) infinite;
}

body[data-fx~="shipped-flags"] .game-row:nth-child(2) .game-status { animation-delay: -0.25s; }
body[data-fx~="shipped-flags"] .game-row:nth-child(3) .game-status { animation-delay: -0.5s; }
body[data-fx~="shipped-flags"] .game-row:nth-child(4) .game-status { animation-delay: -0.35s; }

/* --- quest-stamps on Experience cards ------------------------ */
.quest-stamp {
  position: absolute;
  z-index: 3;
  left: 22px;
  bottom: -16px;
  padding: 4px 8px 3px;
  font-family: var(--font-pixel);
  font-size: 10px;
  line-height: 1.15;
  text-align: center;
  color: var(--rose-strong);
  background: var(--cream);
  border: 2px dashed var(--rose-strong);
  border-radius: 4px;
  transform: rotate(-7deg);
  pointer-events: none;
}

.quest-stamp.is-active {
  color: #274a6b;
  border-color: #274a6b;
}

.quest-stamp.is-armed {
  opacity: 0;
}

.quest-stamp.is-stamped {
  animation: stamp 0.35s cubic-bezier(0.3, 1.6, 0.6, 1) both;
}

@keyframes stamp {
  from { opacity: 0; transform: scale(2.4) rotate(-18deg); }
  to { opacity: 1; transform: scale(1) rotate(-7deg); }
}

/* --- d20 on the Dungeon Master card -------------------------- */
.d20 {
  position: relative;
  margin-left: auto;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
}

.d20 img {
  width: 40px;
  height: 40px;
  image-rendering: pixelated;
}

.d20:hover img {
  transform: rotate(-12deg);
}

.d20:focus-visible {
  outline: 2px dashed var(--accent);
  outline-offset: 2px;
}

.d20.is-rolling img {
  animation: d20-roll 0.6s steps(8);
}

@keyframes d20-roll {
  0% { transform: rotate(0) translateY(0); }
  50% { transform: rotate(360deg) translateY(-18px); }
  100% { transform: rotate(720deg) translateY(0); }
}

.d20-result {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 4px);
  min-width: 34px;
  padding: 3px 6px 2px;
  font-family: var(--font-pixel);
  font-size: 13px;
  line-height: 1;
  color: var(--ink);
  background: var(--accent);
  border: 2px solid var(--ink);
  border-radius: 5px;
  transform: translateX(-50%);
  white-space: nowrap;
  animation: emote-pop 0.2s steps(3) both;
}

.d20-result:empty {
  display: none;
}

.d20-result.is-crit { background: #ffe066; }
.d20-result.is-fail { background: var(--rose); color: var(--cream); }

/* --- campfire: save point by the Contact heading ------------- */
.campfire {
  position: relative;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  margin-left: 14px;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
  transform: rotate(1deg);
}

.campfire-label {
  font-family: var(--font-pixel);
  font-size: 9px;
  line-height: 1;
  color: var(--accent);
  letter-spacing: 0.06em;
  margin-bottom: 2px;
}

.campfire-fire {
  position: relative;
  display: flex;
  align-items: flex-end;
  height: 24px;
  margin-bottom: -10px;
  z-index: 1;
}

.campfire-fire .sprite { --px: 2; }
.campfire-fire .sprite + .sprite { margin-left: -6px; animation-delay: -0.35s; }

.campfire-logs {
  width: 32px;
  height: 32px;
  image-rendering: pixelated;
}

.campfire::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 30px;
  width: 3px;
  height: 3px;
  background: var(--accent);
  box-shadow: 6px 4px 0 #ffd166, -5px 8px 0 var(--accent);
  animation: embers 1.6s steps(8) infinite;
}

@keyframes embers {
  0% { transform: translateY(0); opacity: 1; }
  100% { transform: translateY(-22px); opacity: 0; }
}

.campfire:focus-visible {
  outline: 2px dashed var(--accent);
  outline-offset: 3px;
}

/* --- lever in the footer ------------------------------------- */
.footer-links {
  align-items: center;
}

.lever {
  padding: 0;
  margin-right: -10px;
  background: none;
  border: 0;
  cursor: pointer;
}

.lever .sprite { --px: 2; display: block; }
.lever.is-on .sprite { background-position-x: -32px; }

.lever:focus-visible {
  outline: 2px dashed var(--accent);
  outline-offset: 2px;
}

/* --- patrol -------------------------------------------------- */
.site-footer {
  position: relative;
}

.patrol {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  max-width: var(--maxw);
  height: 84px;
  margin: 0 auto;
  pointer-events: none;
  container-type: inline-size;
}

.patrol-walker {
  position: absolute;
  bottom: 0;
  left: 24px;
  animation: patrol-move 14s linear infinite alternate;
}

.patrol-facing {
  display: block;
  animation: patrol-face 28s steps(1) infinite;
}

.patrol-facing .sprite {
  display: block;
}

@keyframes patrol-move {
  to { transform: translateX(calc(100cqw - 96px)); }
}

@keyframes patrol-face {
  0% { transform: scaleX(1); }
  50% { transform: scaleX(-1); }
}

/* --- coin-hunt ----------------------------------------------- */
.header-brand {
  display: flex;
  align-items: center;
  gap: 14px;
}

.coin-hud {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 9px 4px 8px;
  font-family: var(--font-pixel);
  font-size: 12px;
  line-height: 1;
  color: var(--text-dim);
  background: #150f0a;
  border: 2px solid var(--border);
  border-radius: 7px;
  box-shadow: 2px 2px 0 var(--shadow-ink);
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}

.coin-hud .sprite-coin {
  --px: 2;
}

.coin-hud:hover,
.coin-hud.is-complete {
  border-color: var(--accent);
  color: var(--accent);
}

.coin-hud.is-bump {
  animation: hud-bump 0.35s ease;
}

@keyframes hud-bump {
  40% { transform: scale(1.18) rotate(-3deg); }
}

.fact-list,
.skill-group {
  position: relative;
}

.coin {
  position: absolute;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  filter: drop-shadow(2px 2px 0 var(--shadow-ink));
  transition: transform 0.12s ease;
}

.coin[hidden] {
  display: none;
}

.coin:hover {
  transform: scale(1.2);
}

.coin:focus-visible {
  outline: 2px dashed var(--accent);
  outline-offset: 2px;
}

/* stuck to a card's corner like a sticker */
.coin-corner {
  right: -14px;
  bottom: -14px;
}

/* lives in a terminal title bar, pushed to the right end */
.coin-inline {
  position: static;
  width: 22px;
  height: 22px;
  margin: -6px -4px -6px auto;
  filter: none;
}

.coin-inline .sprite-coin {
  --px: 2;
}

.coin.is-collected {
  pointer-events: none;
  animation: coin-grab 0.45s ease-out forwards;
}

@keyframes coin-grab {
  40% { transform: translateY(-18px) scale(1.3); opacity: 1; }
  100% { transform: translateY(-30px) scale(0.6); opacity: 0; }
}

.coin-pop {
  position: fixed;
  z-index: 700;
  font-family: var(--font-pixel);
  font-size: 14px;
  color: var(--accent);
  text-shadow: 2px 2px 0 var(--shadow-ink);
  pointer-events: none;
  animation: coin-pop 0.8s ease-out forwards;
}

@keyframes coin-pop {
  to { transform: translateY(-36px); opacity: 0; }
}

.loot-chest {
  position: relative;
  margin-left: auto;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
  filter: drop-shadow(3px 3px 0 var(--shadow-ink));
}

.loot-chest .sprite {
  display: block;
}

.loot-chest:focus-visible {
  outline: 2px dashed var(--accent);
  outline-offset: 4px;
}

.loot-chest.is-shaking {
  animation: chest-shake 0.4s ease;
}

.loot-chest.is-unlocked:not(.is-open) {
  animation: chest-ready 1.2s ease-in-out infinite;
}

.loot-chest.is-open .sprite-chest {
  background-position-x: calc(var(--fw) * -2 * var(--px) * 1px);
}

.loot-chest.is-opening .sprite-chest {
  animation: chest-open 0.3s steps(2) forwards;
}

@keyframes chest-open {
  from { background-position-x: 0; }
  to { background-position-x: calc(var(--fw) * -2 * var(--px) * 1px); }
}

@keyframes chest-shake {
  20%, 60% { transform: translateX(-4px) rotate(-4deg); }
  40%, 80% { transform: translateX(4px) rotate(4deg); }
}

@keyframes chest-ready {
  50% { transform: translateY(-5px); }
}

/* --- Toast (shared by coin-hunt, achievements, d20, ...) ----- */
.game-toast {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 600;
  display: flex;
  align-items: center;
  gap: 14px;
  max-width: min(360px, calc(100vw - 40px));
  padding: 14px 18px 14px 14px;
  color: var(--ink);
  background: var(--cream);
  border: 3px solid var(--ink);
  border-radius: var(--radius);
  box-shadow: 6px 6px 0 var(--shadow-ink);
  opacity: 0;
  visibility: hidden;
  transform: translateY(14px) rotate(-0.8deg);
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
}

.game-toast.is-visible {
  opacity: 1;
  visibility: visible;
  transform: rotate(-0.8deg);
}

.game-toast-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 58px;
  height: 58px;
  flex-shrink: 0;
  background: var(--ink);
  border-radius: 9px;
}

.game-toast-icon img {
  width: 32px;
  height: 32px;
  image-rendering: pixelated;
}

/* the toast's chest is always shown open */
.game-toast .sprite-chest {
  background-position-x: calc(var(--fw) * -2 * var(--px) * 1px);
}

.game-toast-title {
  display: block;
  margin-bottom: 3px;
  font-family: var(--font-pixel);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--rose-strong);
}

.game-toast-body {
  display: block;
  font-size: 14px;
  line-height: 1.45;
  white-space: pre-line;
  color: var(--ink-soft);
}

.game-toast-action {
  display: inline-block;
  margin-top: 6px;
  padding: 0 0 1px;
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  color: var(--ink);
  background: none;
  border: 0;
  border-bottom: 2px dashed var(--rose-strong);
  cursor: pointer;
}

.game-toast-action:hover {
  color: var(--rose-strong);
}

/* --- click-fx ------------------------------------------------ */
.click-burst {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 800;
  pointer-events: none;
  --px: 2;
}

/* --- konami party mode --------------------------------------- */
.is-party .sprite {
  --dur: 0.22s;
}

.party-portal {
  position: fixed;
  top: 64px;
  left: 50%;
  z-index: 650;
  margin-left: -82px;
  --px: 4;
  pointer-events: none;
}

.party-coin {
  position: fixed;
  top: -40px;
  z-index: 650;
  pointer-events: none;
  animation: party-fall var(--t, 2.4s) linear forwards;
}

.party-coin .sprite {
  --px: 2;
  display: block;
}

@keyframes party-fall {
  to { transform: translate(var(--drift, 0), 115vh); }
}

/* --- boot-screen --------------------------------------------- */
.boot-screen {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  animation: boot-out 0.3s ease 1.15s forwards;
}

html.boot-seen .boot-screen {
  display: none;
}

.boot-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  width: min(320px, 80vw);
}

.boot-title {
  font-family: var(--font-pixel);
  font-size: clamp(18px, 4vw, 26px);
  color: var(--cream);
  letter-spacing: 0.06em;
}

.boot-title span {
  color: var(--accent);
}

.boot-bar {
  position: relative;
  width: 100%;
  height: 14px;
  margin-top: 70px;
  border: 3px solid var(--border);
  border-radius: 3px;
  container-type: inline-size;
}

.boot-fill {
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(90deg, var(--accent) 0 10px, var(--accent-strong) 10px 12px);
  transform-origin: left center;
  animation: boot-fill 1s steps(10) forwards;
}

.boot-runner {
  position: absolute;
  bottom: calc(100% + 3px);
  left: -6px;
  animation: sprite-play var(--dur) steps(var(--frames)) infinite, boot-run 1s linear forwards;
}

.boot-hint {
  font-family: var(--font-pixel);
  font-size: 11px;
  color: var(--text-faint);
  animation: press-blink 0.8s steps(1) infinite;
}

@keyframes boot-fill {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

@keyframes boot-run {
  to { transform: translateX(calc(100cqw - 36px)); }
}

@keyframes boot-out {
  to { opacity: 0; visibility: hidden; }
}

/* --- 404.html: Game Over ------------------------------------- */
.sprite-knight-hit { --fw: 16; --fh: 28; --frames: 1; background-image: url(../img/sprites/knight-hit.png); }

.game-over {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  padding: 48px 24px;
  text-align: center;
}

.game-over-scene {
  display: flex;
  align-items: flex-end;
  gap: 28px;
  padding-bottom: 6px;
  border-bottom: 3px dashed var(--border);
}

.game-over-knight {
  --px: 4;
  animation: respawn-blink 1.2s steps(6) infinite;
}

.game-over-chort {
  --px: 4;
  transform: scaleX(-1);
}

.game-over-title {
  font-family: var(--font-pixel);
  font-size: clamp(40px, 10vw, 88px);
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--rose);
  text-shadow: 5px 5px 0 var(--shadow-ink);
}

.game-over-copy {
  max-width: 440px;
  margin: 0;
  font-size: 17px;
}

.game-over-continue {
  margin: 0;
  font-family: var(--font-pixel);
  font-size: 18px;
  color: var(--accent);
  animation: press-blink 1s steps(1) infinite;
}

.game-over-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 14px;
  margin-top: 8px;
}

/* --- cursor: pixel swords (mouse users only) ----------------- */
@media (pointer: fine) {
  body[data-fx~="cursor"],
  body[data-fx~="cursor"] * {
    cursor: url(../img/sprites/cursor-sword.png) 1 1, auto;
  }

  body[data-fx~="cursor"] a,
  body[data-fx~="cursor"] a *,
  body[data-fx~="cursor"] button,
  body[data-fx~="cursor"] button *,
  body[data-fx~="cursor"] .critter {
    cursor: url(../img/sprites/cursor-sword-gold.png) 1 1, pointer;
  }

  body[data-fx~="cursor"] input,
  body[data-fx~="cursor"] textarea {
    cursor: text;
  }
}

/* --- Small screens ------------------------------------------- */
@media (max-width: 900px) {
  .player-one {
    display: none;
  }
}

@media (max-width: 720px) {
  .header-brand {
    gap: 10px;
  }

  .patrol {
    height: 56px;
  }

  .patrol .sprite {
    --px: 2;
  }

  .patrol-walker {
    animation-name: patrol-move-sm;
  }

  /* fewer particles on phones */
  .snowfall i:nth-child(n+5),
  .bfly-b {
    display: none;
  }
}

@keyframes patrol-move-sm {
  to { transform: translateX(calc(100cqw - 80px)); }
}

@media (max-width: 480px) {
  .devlog-book .fountain .sprite { --px: 2; }
  .devlog-book .fountain-l { left: -46px; }
  .devlog-book .fountain-r { right: -46px; }
}

/* --- Reduced motion: everything holds still ----------------- */
@media (prefers-reduced-motion: reduce) {
  .sprite,
  .p1-tag,
  .patrol-walker,
  .patrol-facing,
  .glint,
  .snowfall,
  .butterflies,
  .zzz,
  .campfire::after,
  .coin-hud.is-bump,
  .loot-chest.is-unlocked:not(.is-open),
  body[data-fx~="crt"] .terminal-card::after,
  body[data-fx~="inv-shine"] .inv-slot::after,
  body[data-fx~="menu-cursor"] .site-nav a.is-active::before,
  body[data-fx~="press-start"] .project-card > .media-frame:hover::before {
    animation: none !important;
  }

  .snowfall,
  .butterflies,
  .boot-screen,
  body[data-fx~="inv-shine"] .inv-slot::after {
    display: none;
  }

  .coin.is-collected {
    animation: none;
    opacity: 0;
  }

  body[data-fx~="typewriter"] .hero .t-type {
    opacity: 1;
    animation: none;
  }
}
