/* =========================================================
   goofball.sajmi.com — a birthday world

   Font: drop your Minecraft font file into public/fonts/ named
   "Minecraft" with whatever extension it came in — .woff2, .woff, .ttf,
   or .otf all work, no renaming needed beyond the base name below. Until
   that file exists, Press Start 2P is the fallback so text still renders in
   something blocky rather than a default system font.

   Block textures are generated at runtime by app.js and injected as
   --tex-* custom properties on :root.
   ========================================================= */

@font-face {
  font-family: "Minecraft";
  src:
    url("/fonts/Minecraft.woff2") format("woff2"),
    url("/fonts/Minecraft.woff") format("woff"),
    url("/fonts/Minecraft.ttf") format("truetype"),
    url("/fonts/Minecraft.otf") format("opentype");
  font-display: swap;
}
@font-face {
  font-family: "PressStart2P";
  src: url("/fonts/PressStart2P.woff2") format("woff2");
  font-display: swap;
}

:root {
  --head: "Minecraft", "PressStart2P", "Courier New", monospace;
  --body: "Minecraft", "PressStart2P", "Courier New", monospace;

  --grass: #5ea832;
  --grass-lit: #7cbd4c;
  --gold: #ffd54a;
  --gold-deep: #c8931f;
  --ench: #b57cff;
  --ench-deep: #7b3fd4;
  --emerald: #17dd62;
  --danger: #ff5c5c;

  --ink: #f2f4ef;
  --ink-dim: #b8bfc9;

  /* Overwritten by app.js with generated pixel textures. */
  --tex-dirt: #6b4c33;
  --tex-stone: #6f6f6f;
  --tex-plank: #8a5a2b;
  --tex-deepslate: #262a33;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: #05070f;
  color: var(--ink);
  font-family: var(--body);
  font-size: 18px;
  line-height: 1.5;
  overflow-x: hidden;
}

img { display: block; max-width: 100%; }

.hidden { display: none !important; }

/* ---------------------------------------------------------
   Minecraft UI primitives
   --------------------------------------------------------- */

/* Vanilla button: flat grey fill, hard 2px bevel (light top-left, dark
   bottom-right), black outline. No gradients, no glow. */
.mc-btn {
  font-family: var(--head);
  font-size: 18px;
  line-height: 1.4;
  color: #dedede;
  text-shadow: 2px 2px 0 #3f3f3f;
  background: #6c6c6c;
  border: 2px solid #000;
  box-shadow:
    inset -3px -3px 0 #2e2e2e,
    inset 3px 3px 0 #8f8f8f;
  padding: 15px 26px;
  cursor: pointer;
  transition: background 0.08s steps(1), color 0.08s steps(1);
  image-rendering: pixelated;
}
.mc-btn:hover { background: #7f7f8e; color: #ffffa0; }
.mc-btn:active {
  box-shadow: inset 3px 3px 0 #2e2e2e, inset -3px -3px 0 #8f8f8f;
}
.mc-btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.mc-btn[disabled] { color: #a0a0a0; background: #545454; cursor: default; }

.mc-btn--wide { width: 100%; }

.mc-btn--gold {
  background: #9c7418;
  color: #fff6d5;
  box-shadow:
    inset -3px -3px 0 #5e4409,
    inset 3px 3px 0 #d3a032;
  padding: 19px 34px;
}
.mc-btn--gold:hover { background: #b0851c; color: #fff; }
.mc-btn--gold:active {
  box-shadow: inset 3px 3px 0 #5e4409, inset -3px -3px 0 #d3a032;
}

.mc-btn--small { padding: 12px 20px; }

.mc-btn--icon {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 60;
  padding: 11px;
}
/* pixel speaker drawn as SVG so it doesn't depend on font glyph coverage */
.mc-btn--icon svg { width: 20px; height: 20px; image-rendering: pixelated; }
.mc-btn--icon .icon-muted { display: none; }
.mc-btn--icon.is-muted .icon-muted { display: block; }
.mc-btn--icon.is-muted .icon-on { display: none; }

/* Vanilla text field: black fill, flat 2px grey outline that turns white when
   focused. */
.mc-field {
  background: #000;
  border: 2px solid #a0a0a0;
  padding: 10px 10px;
  width: 100%;
  transition: border-color 0.08s steps(1);
}
.mc-field:focus-within { border-color: #fff; }

.mc-field input {
  font-family: var(--body);
  font-size: 18px;
  color: #e0e0e0;
  background: transparent;
  border: none;
  outline: none;
  width: 100%;
  padding: 0;
  letter-spacing: 1px;
  caret-color: #e0e0e0;
}

/* ---------------------------------------------------------
   Lock screen
   --------------------------------------------------------- */

#gate {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: 24px;
  background: #4a3524; /* dirt-brown, not black, in case this shows for a moment before layers below paint */
  transition: opacity 0.6s ease, filter 0.6s ease;
}
#gate.is-leaving { opacity: 0; filter: brightness(2.2); pointer-events: none; }

/* Base layer: the dirt tile, always present, always pixelated. */
#gate-bg-fallback {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-color: #4a3524;
  background-image: var(--tex-dirt);
  background-size: 52px 52px;
  image-rendering: pixelated;
}

/* Optional photo layer on top. Drop a file at public/gate-bg.jpg and it
   appears here automatically — no config, no code change. If the file is
   missing this layer paints nothing and the dirt tile above shows straight
   through. Lightly darkened so the panel text stays readable, but kept
   bright enough that it actually reads as a photo, not a black screen. */
#gate-bg-photo {
  position: absolute;
  inset: 0;
  z-index: 1;
  background-image: url("/gate-bg.jpg");
  background-size: cover;
  background-position: center;
  filter: blur(3px) brightness(0.85) saturate(1.05);
  transform: scale(1.04); /* hides the blur's soft edge past the viewport */
}

#gate::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  background: rgba(10, 8, 6, 0.28);
  pointer-events: none;
}

.gate-panel {
  position: relative;
  z-index: 3;
  width: min(400px, 100%);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
}

/* --- title + splash --- */

.logo {
  position: relative;
  text-align: center;
  margin-bottom: 30px;
}

.logo-text {
  font-family: var(--head);
  font-size: clamp(34px, 10vw, 60px);
  letter-spacing: 2px;
  margin: 0;
  color: #d8d8d8;
  /* stacked hard shadows fake the chunky 3D of the in-game logo */
  text-shadow:
    3px 3px 0 #6f6f6f,
    6px 6px 0 #3a3a3a,
    9px 9px 0 rgba(0, 0, 0, 0.6);
}

/* the yellow tilted line that bounces on the title screen */
.splash {
  position: absolute;
  right: -6px;
  bottom: -26px;
  margin: 0;
  font-family: var(--head);
  font-size: clamp(15px, 3.6vw, 20px);
  color: #ffff55;
  text-shadow: 2px 2px 0 #3f3f00;
  transform: rotate(-17deg);
  transform-origin: 50% 50%;
  animation: splash-bob 0.62s ease-in-out infinite alternate;
  white-space: nowrap;
  pointer-events: none;
}
@keyframes splash-bob {
  from { transform: rotate(-17deg) scale(1); }
  to   { transform: rotate(-17deg) scale(1.09); }
}

/* --- form --- */

#gate-form { display: flex; flex-direction: column; gap: 10px; }

.field-label {
  font-family: var(--head);
  font-size: 16px;
  color: #c0c0c0;
  text-shadow: 2px 2px 0 #101010;
}

.gate-error {
  min-height: 26px;
  margin: 0;
  text-align: center;
  font-family: var(--head);
  font-size: 16px;
  line-height: 1.7;
  color: #fc5454;
  text-shadow: 2px 2px 0 #3f1515;
}

/* version / author text pinned in the corners, like the title screen */
.gate-corner {
  position: absolute;
  bottom: 6px;
  z-index: 3;
  margin: 0;
  font-family: var(--head);
  font-size: 13px;
  color: #d8d8d8;
  text-shadow: 2px 2px 0 #101010;
}
.gate-corner--left { left: 8px; }
.gate-corner--right { right: 8px; }

.is-shaking { animation: shake 0.42s steps(3); }
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-11px); }
  40% { transform: translateX(11px); }
  60% { transform: translateX(-7px); }
  80% { transform: translateX(7px); }
}

/* ---------------------------------------------------------
   World scaffolding
   --------------------------------------------------------- */

#sky {
  position: fixed;
  inset: 0;
  z-index: 0;
  background: linear-gradient(180deg, #6ec1f5 0%, #b9e3ff 100%);
}

#celestial {
  /* sun early on, moon by the end — arcs across as she scrolls */
  position: absolute;
  width: 74px;
  height: 74px;
  left: 0;
  top: 0;
  background: #ffe98a;
  box-shadow: 0 0 60px 18px rgba(255, 233, 138, 0.5);
  image-rendering: pixelated;
  will-change: transform;
}

#fx {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}

#vignette {
  position: fixed;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: radial-gradient(ellipse at center, transparent 38%, rgba(0, 0, 0, 0.72) 100%);
  opacity: 0.45;
}

main { position: relative; z-index: 3; }

/* ---------------------------------------------------------
   Intro
   --------------------------------------------------------- */

#intro {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  text-align: center;
  padding: 24px;
}

.intro-title {
  font-family: var(--head);
  font-size: clamp(30px, 9vw, 62px);
  margin: 0;
  color: #fff;
  text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.6);
  animation: drop-in 0.9s cubic-bezier(0.2, 1.4, 0.4, 1) both;
}
.intro-sub {
  margin: 0;
  font-size: clamp(16px, 3vw, 20px);
  color: #fff;
  text-shadow: 3px 3px 0 rgba(0, 0, 0, 0.55);
  animation: drop-in 0.9s 0.25s cubic-bezier(0.2, 1.4, 0.4, 1) both;
}

@keyframes drop-in {
  from { opacity: 0; transform: translateY(-38px) scale(0.9); }
  to   { opacity: 1; transform: none; }
}

/* pixel chevron, not a font glyph */
.scroll-cue {
  width: 22px;
  height: 22px;
  image-rendering: pixelated;
  filter: drop-shadow(3px 3px 0 rgba(0, 0, 0, 0.5));
  animation: bob 1.7s ease-in-out infinite;
}
@keyframes bob {
  0%, 100% { transform: translateY(0); opacity: 0.55; }
  50% { transform: translateY(14px); opacity: 1; }
}

/* ---------------------------------------------------------
   Photo gallery — scrapbook feed
   --------------------------------------------------------- */

#gallery {
  display: flex;
  flex-direction: column;
  max-width: 1080px;
  margin: 0 auto;
  padding: 4vh 24px 12vh;
  gap: clamp(60px, 11vh, 130px);
}

.card {
  width: var(--w, 62%);
  transform: rotate(var(--rot, 0deg));
}
.card--left   { align-self: flex-start; }
.card--right  { align-self: flex-end; }
.card--center { align-self: center; }

.frame {
  position: relative;
  background-color: #8a5a2b;
  background-image: var(--tex-plank);
  background-size: 48px 48px;
  padding: clamp(9px, 1.7vw, 16px);
  border: 4px solid #1a0f06;
  box-shadow:
    inset -5px -5px 0 rgba(0, 0, 0, 0.4),
    inset 5px 5px 0 rgba(255, 255, 255, 0.16),
    0 20px 44px rgba(0, 0, 0, 0.55);
  image-rendering: pixelated;

  opacity: 0;
  transform: translateY(52px) scale(0.93);
  transition: opacity 0.65s ease, transform 0.75s cubic-bezier(0.2, 1.15, 0.35, 1);
}
.card.is-in .frame { opacity: 1; transform: none; }

.frame img {
  width: 100%;
  height: auto;
  image-rendering: auto;
  border: 2px solid rgba(0, 0, 0, 0.55);
  background: #14171d;
}

/* the photo that failed to load shouldn't leave an empty frame */
.card.is-broken { display: none; }

/* ---------------------------------------------------------
   Descent into night — breathing room before the finale
   --------------------------------------------------------- */

#descent { height: 70svh; }

/* ---------------------------------------------------------
   Finale
   --------------------------------------------------------- */

#finale {
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: 10vh 24px 16vh;
  text-align: center;
}

.finale-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 26px;
  max-width: 900px;
}

.finale-title {
  font-family: var(--head);
  font-size: clamp(26px, 7.5vw, 56px);
  line-height: 1.7;
  margin: 0;
  color: #fff;
  word-spacing: 0.3em;
  text-shadow: 0 0 18px rgba(181, 124, 255, 0.75), 4px 4px 0 rgba(0, 0, 0, 0.6);
}

/* The love line is the payoff, so it lands at nearly title size with a warm
   red bloom instead of the cold enchantment purple above it. */
.finale-sub {
  font-family: var(--head);
  font-size: clamp(24px, 6.8vw, 50px);
  line-height: 1.75;
  margin: 14px 0 0;
  color: #fff;
  word-spacing: 0.3em;
  text-shadow: 0 0 20px rgba(255, 64, 108, 0.85), 4px 4px 0 rgba(0, 0, 0, 0.6);
}

/* A full paragraph reads badly at the display sizes above and would be
   unreadable chaos if it slammed/decoded in word by word — this is quieter
   on purpose: normal-weight body text, generous line-height, and a soft
   fade+rise once the love line above it has landed. */
.finale-message {
  font-family: var(--body);
  font-size: clamp(15px, 2.6vw, 20px);
  line-height: 1.9;
  max-width: 640px;
  margin: 30px 0 0;
  color: #fdf3ff;
  text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.6);
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.9s ease, transform 0.9s ease;
}
.finale-message.is-in { opacity: 1; transform: none; }

/* each character swaps between scrambled glyphs before locking in */
.glyph { display: inline-block; }
.glyph.is-scrambling { color: var(--ench); }
.word { display: inline-block; white-space: nowrap; }

/* --- word-by-word slam for the love line --- */

.slam {
  display: inline-block;
  white-space: nowrap;
  opacity: 0;
  transform: scale(2.9);
}
.slam.is-landed { animation: slam-in 0.44s cubic-bezier(0.18, 1.5, 0.32, 1) forwards; }

@keyframes slam-in {
  0%   { opacity: 0; transform: scale(2.9); }
  1%   { opacity: 1; transform: scale(2.9); }
  100% { opacity: 1; transform: scale(1); }
}

/* impact shake — only `main` moves, so the fixed sky and canvas stay put */
main.is-shaken { animation: screen-shake 0.3s steps(2); }
@keyframes screen-shake {
  0%, 100% { transform: translate(0, 0); }
  25% { transform: translate(calc(var(--shake, 6px) * -1), var(--shake, 6px)); }
  50% { transform: translate(var(--shake, 6px), calc(var(--shake, 6px) * -1)); }
  75% { transform: translate(calc(var(--shake, 6px) * -0.6), 0); }
}

/* ---------------------------------------------------------
   Gift
   --------------------------------------------------------- */

.gift-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 26px;
  margin-top: 22px;
  min-height: 190px;
}

#gift-btn {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.8s ease, transform 0.8s ease;
  pointer-events: none;
}
#gift-btn.is-ready { opacity: 1; transform: none; pointer-events: auto; }
#gift-btn.is-gone { display: none; }

/* --- pixel chest --- */

.chest {
  position: relative;
  width: 112px;
  height: 104px;
  perspective: 460px;
  image-rendering: pixelated;
}
.chest--gift { display: none; }
.chest--gift.is-shown { display: block; }

.chest-lid,
.chest-body {
  position: absolute;
  left: 0;
  right: 0;
  background-color: #8a5a2b;
  background-image: var(--tex-plank);
  background-size: 42px 42px;
  border: 4px solid #2b1a0c;
  box-shadow:
    inset -5px -5px 0 rgba(0, 0, 0, 0.42),
    inset 5px 5px 0 rgba(255, 255, 255, 0.2);
}
.chest-lid {
  top: 0;
  height: 42px;
  transform-origin: 50% 100%;
  transition: transform 0.75s cubic-bezier(0.3, 1.5, 0.5, 1);
  z-index: 2;
}
.chest-body { top: 38px; bottom: 0; }

.chest-latch {
  position: absolute;
  left: 50%;
  bottom: -13px;
  width: 20px;
  height: 26px;
  margin-left: -10px;
  background: #d8b25a;
  border: 3px solid #2b1a0c;
  box-shadow: inset -3px -3px 0 rgba(0, 0, 0, 0.35);
  z-index: 3;
}

.chest.is-open .chest-lid { transform: rotateX(-112deg); }
.chest.is-open .chest-latch { opacity: 0; transition: opacity 0.2s; }

.chest-beam {
  position: absolute;
  left: 50%;
  bottom: 40px;
  width: 56px;
  margin-left: -28px;
  height: 420px;
  background: linear-gradient(to top, rgba(255, 213, 74, 0.85), rgba(255, 213, 74, 0));
  transform-origin: 50% 100%;
  transform: scaleY(0);
  opacity: 0;
  filter: blur(1px);
  z-index: 1;
  pointer-events: none;
}
.chest.is-open .chest-beam {
  animation: beam-up 1.5s cubic-bezier(0.2, 0.9, 0.3, 1) forwards;
}
@keyframes beam-up {
  0%   { transform: scaleY(0); opacity: 0; }
  35%  { transform: scaleY(1); opacity: 1; }
  100% { transform: scaleY(1); opacity: 0.32; }
}

/* --- the code --- */

.code-reveal {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  opacity: 0;
  transition: opacity 0.5s ease;
}
.code-reveal.is-in { opacity: 1; }

/* --- inventory slot: bevel runs dark top-left, light bottom-right (sunken,
       the opposite of a button) --- */

.item-slot {
  position: relative;
  width: 76px;
  height: 76px;
  display: grid;
  place-items: center;
  background: #8b8b8b;
  border: 2px solid #373737;
  box-shadow:
    inset 3px 3px 0 #373737,
    inset -3px -3px 0 #ffffff;
  overflow: hidden;
}
.code-reveal.is-in .item-slot {
  animation: item-pop 0.62s cubic-bezier(0.2, 1.6, 0.4, 1) both;
}
@keyframes item-pop {
  from { transform: translateY(26px) scale(0.35); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

.item-icon {
  width: 46px;
  height: 46px;
  image-rendering: pixelated;
  filter: drop-shadow(3px 3px 0 rgba(0, 0, 0, 0.32));
}

/* A dropped-in custom image is a normal photo, not a pixel grid — render it
   smoothly and let it fill more of the slot so it's actually recognizable. */
.item-icon--custom {
  width: 62px;
  height: 62px;
  image-rendering: auto;
  object-fit: cover;
}

/* enchantment glint sweeping across the item */
.item-slot::after {
  content: "";
  position: absolute;
  inset: 3px;
  background: linear-gradient(115deg,
    transparent 34%,
    rgba(178, 108, 255, 0.55) 46%,
    rgba(228, 200, 255, 0.35) 52%,
    transparent 64%);
  background-size: 280% 280%;
  mix-blend-mode: screen;
  animation: glint 2.8s linear infinite;
  pointer-events: none;
}
@keyframes glint {
  from { background-position: 180% 0%; }
  to   { background-position: -80% 0%; }
}

/* --- item tooltip ---
   One continuous gradient background showing through a padding-width ring
   around a solid inner box, rather than a border-image (which slices a
   gradient into 4 edge strips and commonly leaves a visible seam at each
   corner). This way the gradient wraps unbroken around all four corners. */

.tooltip {
  background: linear-gradient(180deg, #6a1fd6 0%, #b23fe0 50%, #6a1fd6 100%);
  padding: 3px;
  max-width: min(560px, 90vw);
}
.tooltip-inner {
  background: #100010;
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.tooltip p {
  margin: 0;
  font-family: var(--head);
  line-height: 1.6;
  text-shadow: 2px 2px 0 #1a0a1a;
}

.tooltip-name { font-size: 17px; color: #ffffff; }

.tooltip-code {
  font-size: 19px;
  color: #ffaa00;
  letter-spacing: 1px;
  word-break: break-all;
  user-select: all; /* one tap selects the whole code */
}

.tooltip-lore { font-size: 14px; color: #a0a0a0; }

.code-hint {
  margin: 0;
  min-height: 24px;
  font-size: 16px;
  color: var(--emerald);
  text-shadow: 2px 2px 0 #0a1a0f;
}

/* ---------------------------------------------------------
   Responsive
   --------------------------------------------------------- */

@media (max-width: 720px) {
  body { font-size: 16px; }
  #gallery { padding: 3vh 16px 10vh; gap: clamp(44px, 8vh, 80px); }
  /* narrow screens: let every photo run near full width, drop the zig-zag */
  .card { width: min(100%, 94vw) !important; align-self: center !important; }
  .chest { width: 92px; height: 86px; }
  .mc-btn--icon { right: 10px; bottom: 10px; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .frame { opacity: 1; transform: none; }
  .finale-message { opacity: 1; transform: none; }
  #gift-btn { opacity: 1; transform: none; pointer-events: auto; }
}
