/* ---------- pixel font ---------- */
/* only ships one weight — the browser synthesizes bold when needed */
/* Subset of x12y12pxMaruMinyaHangul holding only the glyphs this page
   renders (~7KB instead of ~486KB). Regenerate with tools/subset-font.py
   whenever user-visible text changes, or new characters will render as
   tofu boxes. Only ships one weight — the browser synthesizes bold. */
@font-face {
  font-family: "MaruMinyaHangul";
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url("fonts/MaruMinyaHangul-subset.woff2") format("woff2");
}

/* ---------- reset / viewport lock ---------- */
* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  height: 100%;
  overflow: hidden; /* no scroll, ever */
  background: #0e0e12;
}

body {
  font-family: "MaruMinyaHangul", "Courier New", monospace;
  color: #eae6da;
}

/* ---------- full-viewport game screen ---------- */
/* Mobile portrait is the primary target, and the 20/60/20 bands are
   percentage-based already, so the screen just fills the real viewport
   instead of being locked to a landscape aspect ratio — a fixed ratio
   like 16:9 would letterbox tall/portrait screens and waste most of the
   vertical space. */
.viewport {
  width: 100vw;
  height: 100dvh;
  background: #0e0e12;
}

/* the grass texture backs the whole screen, not just the stage, so the
   inset dialogue box in the top band floats over the scene the way an
   RPG textbox does — otherwise its margin would just expose page black */
.game-screen {
  position: relative;
  width: 100%;
  height: 100%;
  display: grid;
  grid-template-rows: 20% 60% 20%;
  overflow: hidden;
  background-color: #4a934a;
  background-image: url("Grass_Sample.webp");
  background-repeat: repeat;
  background-size: auto;
  background-position: top center;
  image-rendering: pixelated;
}

/* ---------- reserved bands ---------- */
/* Both bands are classic RPG dialogue boxes: cream panel with a double
   pixel border (outer line / cream gutter / inner line) drawn as square
   inset rings so the corners stay hard-edged rather than anti-aliased.
   Same cream/dark palette as the modals. */
.band {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 3px solid #2b2118;
  box-shadow:
    inset 0 0 0 2px #eae6da,
    inset 0 0 0 5px #2b2118;
  background: #eae6da;
  color: #2b2118;
  text-align: center;
  overflow: hidden;
}

.band-top {
  flex-direction: column;
  gap: 0.25em;
  margin: 0.7em;
  padding: 0.9em 1.1em;
}

.invite-date {
  margin: 0;
  font-size: clamp(15px, 4.2vw, 22px);
  line-height: 1.3;
  letter-spacing: 0.04em;
  color: #2b2118;
}

.invite-couple {
  margin: 0;
  font-size: clamp(18px, 5vw, 26px);
  line-height: 1.3;
  letter-spacing: 0.03em;
  color: #2b2118;
}

.invite-spacer {
  height: 0.5em; /* breathing room between the names and the details below */
}

.invite-venue {
  margin: 0;
  font-size: clamp(15px, 4.2vw, 22px);
  line-height: 1.3;
  letter-spacing: 0.02em;
  color: #6b6355;
}

/* bottom band is now the button itself — the whole area is tappable */
/* no margin here, unlike .band-top — the whole band is the RSVP tap
   target, so keeping it flush to the screen edges avoids dead zones */
.band-bottom {
  outline: none;
  width: 100%;
  padding: 0 1.2em;
  font-family: inherit;
  font-size: clamp(23px, 5.2vw, 31px);
  font-weight: 700;
  letter-spacing: 0.1em;
  cursor: pointer;
}

/* ---------- account info panel ---------- */
.account-panel {
  width: 80%;
  max-height: 84%;
  overflow-y: auto;
  background: #eae6da;
  border: 2px solid #2b2118;
  text-align: center;
}

.account-list {
  display: flex;
  flex-direction: column;
}

.account-row {
  padding: 1.2em 0;
  border-top: 1px dashed #b9b6ab;
}

.account-row:first-child {
  border-top: none;
}

.account-row p {
  margin: 0 0 0.6em;
}

.account-role {
  font-size: clamp(11px, 2.7vw, 13px);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #6b6355;
}

.account-name {
  font-size: clamp(16px, 4.1vw, 21px);
  font-weight: 700;
  color: #2b2118;
}

.account-bank {
  font-size: clamp(12px, 2.9vw, 14px);
  color: #6b6355;
}

.account-number {
  font-size: clamp(14px, 3.5vw, 18px);
  letter-spacing: 0.05em;
  color: #2b2118;
}

.account-actions {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.6em;
  margin-top: 0.6em;
}

.account-copy {
  padding: 0.6em 1.2em; /* em-based so height tracks font-size, not panel width */
  border: 2px solid #2b2118;
  background: #2b2118;
  color: #eae6da;
  font-family: inherit;
  font-size: clamp(11px, 2.7vw, 15px);
  letter-spacing: 0.1em;
  cursor: pointer;
}

/* ---------- Toss transfer link ----------
   supertoss://send?bank=&accountNo= — official deep link, opens the Toss
   app straight to the transfer screen with bank/account pre-filled.
   No web fallback exists for this scheme, so if Toss isn't installed the
   tap is a no-op and "계좌번호 복사" next to it is the practical fallback. */
.toss-transfer {
  display: inline-block;
  padding: 0.6em 1.2em;
  border: 2px solid #0064ff;
  background: #0064ff;
  color: #ffffff;
  font-family: inherit;
  font-size: clamp(11px, 2.7vw, 15px);
  letter-spacing: 0.1em;
  text-decoration: none;
  cursor: pointer;
}

/* ---------- directions panel ---------- */
.directions-panel {
  width: 80%;
  max-height: 84%;
  overflow-y: auto;
  background: #eae6da;
  border: 2px solid #2b2118;
  /* em-based, same reasoning as .account-panel above */
  padding: 1.5em;
  text-align: center;
}

.directions-body {
  margin: 0;
  font-size: clamp(13px, 3.3vw, 17px);
  color: #4a4438;
  line-height: 1.5;
  word-break: keep-all; /* wrap at word gaps, not mid-word, so long lines break at natural points */
}

/* keeps each phrase intact — the only allowed break point is the plain
   space between two segments, so it's either one line or splits exactly
   there, never mid-phrase */
.nowrap-segment {
  white-space: nowrap;
}

.directions-section {
  margin-top: 1.2em;
  padding-top: 1.2em;
  border-top: 1px dashed #b9b6ab;
}

.directions-section:first-of-type {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}

.directions-section-title {
  margin: 0 0 0.6em;
  font-size: clamp(13px, 3.3vw, 17px);
  font-weight: 700;
  letter-spacing: 0.08em;
  color: #2b2118;
}

.map-links {
  display: flex;
  justify-content: center;
  gap: 0.6em;
  margin-top: 1em;
}

.map-link {
  /* em-based padding scales with the button's own font-size, not the
     panel's width — percentage padding here would make the button
     unreasonably tall once the panel gets wide (e.g. on desktop) */
  padding: 0.6em 1.4em;
  border: none;
  font-family: inherit;
  font-size: clamp(12px, 2.9vw, 14px);
  font-weight: 700;
  letter-spacing: 0.02em;
  text-decoration: none;
  cursor: pointer;
}

/* brand colors */
.map-link-naver {
  background: #03c75a;
  color: #ffffff;
}

.map-link-kakao {
  background: #fee500;
  color: #191919;
}

/* ---------- stage ---------- */
/* split into left/right halves, each character centered (both axes)
   within its own half */
/* grass now comes from .game-screen behind it */
.stage {
  position: relative;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  place-items: center;
}

/* ---------- characters ---------- */
.character {
  position: relative;
  z-index: 2;
  width: clamp(140px, 28vw, 302px);
}

.character-sprite {
  width: 100%;
  height: auto;
  display: block;
  image-rendering: pixelated;
}

.character-a {
  width: clamp(145px, 28vw, 307px);
}

.character-a .character-sprite {
  transform: scaleX(-1);
}

/* ---------- items clustered around each character ---------- */
/* the group wraps tightly around .character (items are position:absolute
   and don't affect its size), so item positions below are percentages of
   the character's own box, not the whole stage — they stay glued to the
   character no matter where the group ends up. */
.character-group {
  position: relative;
  transform: translateY(18%) translateY(30px); /* nudge down to balance the triggers now sitting at the top */
  cursor: pointer;
}

.character-group:focus-visible {
  outline: 2px dashed #eae6da;
  outline-offset: 4px;
}

.item {
  position: absolute;
  z-index: 1; /* stays below .character so it's covered on overlap */
  width: clamp(62px, 13vw, 110px);
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}

.groom-group .item-3 {
  width: clamp(87px, 18.2vw, 154px);
}

.item-sprite {
  width: 100%;
  height: auto;
  display: block;
  image-rendering: pixelated;
}

.item-1 {
  left: -12%;
  top: 0%;
}
.item-2 {
  left: 78%;
  top: -6%;
}
.item-3 {
  left: -14%;
  top: 68%;
}
.item-4 {
  left: 80%;
  top: 72%;
}

/* ---------- gallery trigger ---------- */
.gallery-trigger {
  position: absolute;
  right: calc(20% - 10px);
  top: 8%;
  width: clamp(98px, 19.5vw, 170px);
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: trigger-bob 1s steps(2) infinite;
}

.gallery-trigger-sprite {
  width: 100%;
  height: auto;
  display: block;
  image-rendering: pixelated;
}

/* ---------- directions (bus) trigger ---------- */
.directions-trigger {
  position: absolute;
  left: calc(20% - 10px);
  top: 8%;
  width: clamp(104px, 19.5vw, 176px);
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: trigger-bob 1s steps(2) infinite;
  animation-delay: 0.5s; /* offset so the two triggers don't bob in lockstep */
}

@keyframes trigger-bob {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-4px);
  }
}

.directions-trigger-sprite {
  width: 100%;
  height: auto;
  display: block;
  image-rendering: pixelated;
}

/* ---------- gallery modal ---------- */
.modal-overlay {
  position: absolute;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  background: rgba(10, 10, 14, 0.82);
  z-index: 10;
}

.modal-overlay.is-open {
  display: flex;
  animation: backdrop-fade 0.25s ease both;
}

@keyframes backdrop-fade {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/* Capping the panel rather than each photo is what keeps the scatter
   centred: the layout positions photos as a percentage of this box, so
   a cap on the photos themselves would shrink them without shrinking
   their spread, leaving dead space on the right of a wide screen. */
.modal-photos {
  position: relative;
  /* the 55vh term matters on short, wide windows: photo height follows the
     panel's width, so without it the bottom row runs past the panel when
     there is little vertical room */
  width: min(90%, 520px, 55vh);
  height: 84%;
  cursor: pointer; /* empty space here sweeps the photos away */
}

/* Every scatter value (landing spot, entry/exit vectors, width) is set by
   script.js as a custom property, derived from how many photos there are —
   see buildGalleryPhotos(). Width stays a plain percentage of the panel so
   photos and their spread scale together; .modal-photos carries the cap. */
.photo {
  position: absolute;
  left: var(--photo-x, 0);
  top: var(--photo-y, 0);
  margin: 0;
  width: var(--photo-w, 38%);
  background: #2b2118;
  border: 1px solid #2b2118;
  padding: 0;
  animation: throw-in 0.7s ease-out both;
  cursor: zoom-in;
}

/* tap-to-zoom target state — JS drives the actual transform (FLIP
   technique) with !important so it can out-rank the held throw-in/
   sweep-out animation without touching animation-name (which would
   restart those animations from the beginning) */
.photo.is-enlarged {
  position: fixed;
  top: 50%;
  left: 50%;
  width: min(86vw, 480px);
  transform: translate(-50%, -50%) !important;
  z-index: 20;
  cursor: zoom-out;
}

/* Height comes from each image's own width/height attributes rather than a
   forced ratio, so the landscape shots aren't centre-cropped down to a
   portrait sliver — and the browser still reserves the right box before
   the file loads, so nothing shifts. */
.photo-frame {
  width: 100%;
  height: auto;
  background: #7a8fb0; /* shows while the photo loads */
  display: block;
}


@keyframes throw-in {
  from {
    transform: translate(var(--start-x), var(--start-y)) rotate(var(--start-rot));
  }
  to {
    transform: translate(0, 0) rotate(var(--rest-rot));
  }
}


/* ---------- sweep-away close: swipe everything off to the right ---------- */
.modal-overlay.is-closing .photo {
  animation: sweep-out 0.5s cubic-bezier(0.55, 0.06, 0.68, 0.19) both;
  animation-delay: var(--exit-delay, 0s);
}

@keyframes sweep-out {
  from {
    transform: translate(0, 0) rotate(var(--rest-rot));
  }
  to {
    transform: translate(140vw, var(--exit-y)) rotate(var(--exit-rot));
  }
}

/* ---------- first-visit onboarding: dims the screen and punches a
   circular hole over each interactive item (camera, bus, groom, bride) ---------- */
.onboarding-overlay {
  position: absolute;
  inset: 0;
  z-index: 50;
  cursor: pointer;
}

.onboarding-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.onboarding-hint {
  position: absolute;
  top: 10%;
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  padding: 0 8%;
  text-align: center;
  color: #eae6da;
  font-size: clamp(13px, 3.5vw, 18px);
  letter-spacing: 0.05em;
  white-space: nowrap;
}

/* secondary line under the main hint — dimmer so the "tap these" message
   still leads */
.onboarding-subhint {
  position: absolute;
  top: calc(10% + 2em);
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  padding: 0 8%;
  text-align: center;
  color: #b9b6ab;
  font-size: clamp(11px, 3vw, 15px);
  letter-spacing: 0.05em;
  white-space: nowrap;
}

/* positioned in JS (left/top/transform), right next to each spotlight hole */
.onboarding-label {
  position: absolute;
  margin: 0;
  white-space: nowrap;
  text-align: center;
  color: #eae6da;
  font-size: clamp(16px, 4.1vw, 21px);
  letter-spacing: 0.03em;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
}

/* ---------- attention arrows: point from the stage down into RSVP ----------
   ~20 arrows are injected in JS with randomized horizontal position and
   bounce timing so the gaps between them read as irregular, not a grid. */
.attention-arrows {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 15px; /* sits just above the stage/RSVP-band boundary */
  height: 0;
  pointer-events: none;
}

.attention-arrow {
  position: absolute;
  top: 0;
  transform: translate(-50%, -50%); /* centers the arrow ON the boundary, so it straddles both sections */
  font-size: clamp(54px, 10.9vw, 78px);
  font-weight: 900;
  color: #14141a;
  -webkit-text-stroke: 1.5px #14141a;
  text-shadow: 0 0 4px rgba(255, 255, 255, 0.8);
  animation: attention-arrow-bounce 1s ease-in-out infinite;
}

@keyframes attention-arrow-bounce {
  0%,
  100% {
    transform: translate(-50%, -50%);
    opacity: 0.4;
  }
  50% {
    transform: translate(-50%, calc(-50% + 10px));
    opacity: 1;
  }
}
