/* Spellsanity: Wizard Survival
   Palette and type are the game's own. Colours mirror autoload/UITheme.gd:32-39 so the
   site and the game look like one thing. Everything here is local: no CDN, no webfont
   service, no analytics, which is what lets the privacy policy stay this short. */

/* The game's own display face (assets/ui/FusionPixel12.ttf, the master UITheme hands to
   anything 12px and up). Jersey 10 was the game font until 2026-07-18 and is now only
   the wordmark's — and the wordmark arrives here as logo.png, so no Jersey webfont.
   Display sizes below sit on the font's 12px grid (24 and 36px), because a pixel face
   at 39px lands between rows and the browser blurs it. The one exception is .lnk at
   18px, a half-step: a footer toggle at 24px shouts, and at 12px nobody finds it. */
@font-face {
  font-family: "Fusion Pixel";
  src: url("fonts/FusionPixel12.woff2") format("woff2");
  font-display: swap;
}

:root {
  --bg: #16100d;          /* boot splash background, project.godot */
  --surface: #1a1410;     /* UI_SURFACE */
  --card: #1f1812;
  --well: #120e0b;        /* UI_WELL */
  --edge: #4a3a24;
  --gold: #b3915c;        /* UI_ACCENT */
  --gold-bright: #f5db99; /* UI_ACCENT_BRIGHT */
  --text: #ede6d1;        /* UI_TEXT */
  --dim: #b3a891;         /* UI_TEXT_DIM */
  --violet: #b9a8ff;      /* the story's magic accent */
  --shadow: #0d0906;
  --nav-h: 60px;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* display on .btn would otherwise win over the hidden attribute, and the demo and
   wishlist buttons are meant to stay invisible until those pages exist. */
[hidden] { display: none !important; }

html { scroll-behavior: smooth; }

body {
  background: var(--bg);
  color: var(--text);
  font-family: Georgia, "Times New Roman", serif;
  font-size: 17px;
  line-height: 1.65;
  overflow-x: hidden;
}

img, video { display: block; max-width: 100%; height: auto; }
img { image-rendering: pixelated; }

h1, h2, h3, .btn, nav a, .lnk {
  font-family: "Fusion Pixel", "Courier New", monospace;
  font-weight: normal;
  letter-spacing: 0.5px;
}

a { color: var(--gold-bright); }
a:hover { color: #fff; }

:focus-visible {
  outline: 2px solid var(--gold-bright);
  outline-offset: 3px;
}

.sr-only, .skip:not(:focus) {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.skip {
  position: fixed; top: 8px; left: 8px; z-index: 60;
  background: var(--card); color: var(--gold-bright);
  border: 2px solid var(--gold); padding: 8px 14px; text-decoration: none;
}

/* ------------------------------------------------------------------ cursor --
   The game's own arrow, hotspot at 0,0 exactly like UITheme.gd:255. Firefox drops
   cursor images larger than 32px in either axis, so the 24x38 copy is listed next
   and the keyword last. Coarse pointers never get it. */
@media (hover: hover) and (pointer: fine) {
  html:not(.sys-cursor) body,
  html:not(.sys-cursor) a,
  html:not(.sys-cursor) button,
  html:not(.sys-cursor) label {
    cursor: url("img/cursor.png") 0 0, url("img/cursor_24.png") 0 0, auto;
  }
  html:not(.sys-cursor) input,
  html:not(.sys-cursor) textarea { cursor: text; }
}

/* --------------------------------------------------------------------- nav -- */
#nav {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
  min-height: var(--nav-h);
  padding: 6px 18px;
  background: rgba(22, 16, 13, 0.92);
  backdrop-filter: blur(6px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.25s ease;
}
#nav.stuck { border-bottom-color: var(--gold); }

.nav-mark img { width: 168px; height: auto; }

#nav ul { display: flex; align-items: center; gap: 4px; list-style: none; }
#nav a {
  display: block;
  padding: 8px 6px;
  font-size: 1.125rem; /* 18px, the same half-step as .lnk. Fusion Pixel runs ~1.4x
                          wider than Jersey did, and there are six labels now, so 24px
                          no longer fits next to a wordmark big enough to read: at 18px
                          the row measures ~695px and still clears the 720px nav
                          breakpoint. */
  color: var(--dim);
  text-decoration: none;
  border-bottom: 2px solid transparent;
}
#nav a:hover { color: var(--gold-bright); border-bottom-color: var(--gold); }
#nav a.out { color: var(--violet); }

/* The burger button. Three bars drawn as plain blocks, because a pixel page has no
   business loading an icon font for them. Hidden until the mobile breakpoint. */
.burger {
  display: none;
  flex-direction: column; justify-content: center; gap: 5px;
  width: 42px; height: 38px;
  padding: 0 8px;
  background: none;
  border: 2px solid var(--edge);
  border-radius: 4px;
}
.burger span {
  display: block;
  height: 3px;
  background: var(--gold);
  transition: transform 0.18s ease, opacity 0.18s ease;
}
.burger:hover { border-color: var(--gold); }
.burger:hover span { background: var(--gold-bright); }
/* Open: the outer bars cross, the middle one goes. */
#nav.open .burger span:nth-child(1) { transform: translateY(8px) rotate(45deg); }
#nav.open .burger span:nth-child(2) { opacity: 0; }
#nav.open .burger span:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }

/* -------------------------------------------------------------------- hero -- */
.hero {
  position: relative;
  min-height: min(78vh, 660px);
  display: flex; align-items: center; justify-content: center;
  padding: 64px 18px 72px;
  overflow: hidden;
  border-bottom: 1px solid var(--edge);
}
.hero-vid {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  image-rendering: pixelated;
  filter: brightness(0.68) saturate(0.95);
}
.hero-veil {
  position: absolute; inset: 0;
  background:
    radial-gradient(ellipse at 50% 45%, rgba(22,16,13,0.05), rgba(22,16,13,0.72) 80%),
    linear-gradient(to bottom, rgba(22,16,13,0.55), rgba(22,16,13,0.1) 35%, var(--bg));
}
.hero-inner { position: relative; text-align: center; max-width: 660px; }

.hero-logo {
  width: min(520px, 84vw);
  margin: 0 auto 18px;
  filter: drop-shadow(0 4px 0 var(--shadow));
}
.genre {
  color: var(--gold-bright);
  font-size: 0.92rem;
  letter-spacing: 0.06em;
  text-shadow: 0 2px 8px rgba(0,0,0,0.9);
  margin: 0 auto 14px;
}
.logline {
  color: var(--text);
  text-shadow: 0 2px 8px rgba(0,0,0,0.9);
  margin: 0 auto 26px;
  max-width: 600px;
}
.hero-cta { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
.kicker {
  margin-top: 22px;
  font-size: 0.92rem;
  color: var(--dim);
  text-shadow: 0 2px 6px rgba(0,0,0,0.9);
}

/* ------------------------------------------------------------------ buttons -- */
.btn {
  display: inline-block;
  padding: 9px 22px 11px;
  font-size: 1.5rem;   /* 24px */
  color: var(--gold-bright);
  background: var(--card);
  border: 2px solid var(--gold);
  border-radius: 4px;
  box-shadow: 0 3px 0 var(--shadow);
  text-decoration: none;
  transition: transform 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.btn:hover {
  color: #fff;
  border-color: var(--gold-bright);
  transform: translateY(-1px);
}
.btn:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--shadow); }
.btn.ghost { color: var(--violet); background: rgba(31, 24, 18, 0.7); }
.btn.wish { color: #ffd640; }

.lnk {
  background: none; border: none;
  font-size: 1.125rem; color: var(--dim);   /* 18px = 1.5x, the only half-step */
  text-decoration: underline; text-underline-offset: 3px;
}
.lnk:hover { color: var(--gold-bright); }
.lnk[aria-pressed="true"] { color: var(--gold-bright); }

/* ---------------------------------------------------------------- sections -- */
.wrap { max-width: 1100px; margin: 0 auto; padding: 58px 20px; }
.wrap.wide { max-width: 1020px; }   /* room for a 960px screenshot plus the arrows */
section { scroll-margin-top: calc(var(--nav-h) + 8px); }

h2 {
  font-size: 2.25rem;   /* 36px = 3x the font grid */
  color: var(--gold-bright);
  text-align: center;
}
h3 { font-size: 1.5rem; color: var(--gold-bright); margin-bottom: 6px; }   /* 24px = 2x */

.rule {
  width: 130px; height: 1px;
  margin: 10px auto 34px;
  background: var(--gold);
  opacity: 0.65;
}

.cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: 26px; }
.cols p { color: var(--dim); }
.cols strong { display: block; color: var(--text); font-size: 1.05rem; }

/* ----------------------------------------------------------------- roadmap -- */
.road { position: relative; max-width: 720px; margin: 0 auto; padding-left: 42px; }
.road::before {
  content: "";
  position: absolute; left: 13px; top: 6px; bottom: 6px;
  width: 2px;
  background: linear-gradient(to bottom, var(--gold), var(--edge));
  opacity: 0.75;
}
.road ol { list-style: none; }
.road li { position: relative; margin-bottom: 34px; }
.road li::before {
  content: "";
  position: absolute; left: -35px; top: 10px;
  width: 11px; height: 11px;
  background: var(--gold);
  border: 2px solid var(--bg);
  transform: rotate(45deg);
}
.road li.last::before { background: var(--violet); }
.road p { color: var(--dim); }

/* --------------------------------------------------------------- about the dev
   A portrait phone clip, so it gets a fixed column and the text takes the rest.
   It plays muted and silent (the audio track is stripped from the file itself). */
.dev {
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: 28px;
  align-items: start;
  max-width: 720px;
  margin: 0 auto;
}
.dev-vid {
  width: 100%; height: auto;
  background: var(--well);
  border: 1px solid var(--edge);
  border-radius: 6px;
  box-shadow: 0 3px 0 var(--shadow);
}
.dev-text p { color: var(--dim); margin-bottom: 14px; }
.dev-text p:last-child { margin-bottom: 0; }
.dev-sign { color: var(--gold); }

/* ---------------------------------------------------------------- newsletter */
.signup {
  background: var(--surface);
  border: 1px solid var(--edge);
  border-radius: 6px;
  padding: 34px 26px 30px;
  text-align: center;
}
.signup .lede { color: var(--dim); max-width: 520px; margin: 0 auto 22px; }
#signup {
  position: relative;
  display: flex; flex-wrap: wrap; gap: 10px; justify-content: center;
}
#signup input[type="email"] {
  flex: 1 1 260px; max-width: 340px;
  padding: 11px 14px;
  font-family: inherit; font-size: 1rem;
  color: var(--text);
  background: var(--well);
  border: 2px solid var(--edge);
  border-radius: 4px;
}
#signup input[type="email"]:focus { outline: none; border-color: var(--gold); }
#signup input[aria-invalid="true"] { border-color: #d4674a; }
#signup button[disabled] { opacity: 0.6; }

.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.status { min-height: 1.5em; margin-top: 14px; color: var(--gold-bright); }
.status.bad { color: #e88a6a; }
.fineprint { font-size: 0.82rem; color: var(--dim); max-width: 520px; margin: 8px auto 0; }
.fineprint a { color: var(--dim); }

/* ----------------------------------------------------------------- divider -- */
.divider {
  height: 1px;
  max-width: 1040px;
  margin: 0 auto;
  background: linear-gradient(to right, transparent, var(--gold), transparent);
  opacity: 0.6;
}

/* ------------------------------------------------------------------ footer -- */
footer { border-top: 1px solid var(--edge); margin-top: 40px; background: var(--well); }
.foot { padding: 30px 20px 44px; text-align: center; font-size: 0.85rem; color: var(--dim); }
.foot-links a { margin: 0 10px; color: var(--gold-bright); }
.foot-toggles { margin: 12px 0 18px; display: flex; gap: 18px; justify-content: center; }
.credits { max-width: 620px; margin: 0 auto 10px; font-size: 0.78rem; line-height: 1.5; }
.credits a, .copy { color: var(--dim); }

/* ------------------------------------------------------------------ slider --
   A scroll-snap strip, not a script-driven carousel: the browser does the sliding,
   the swiping and the keyboard, and the buttons below only call scrollBy. Without
   JavaScript it stays a scrollable row of screenshots, which is still the content.

   Screenshots are drawn at 960, which is 1.5x their stored 640x360. A nearest-neighbour
   picture is only sharp at whole multiples, so this size splits by device pixel ratio:
   on a HiDPI screen 960 CSS px are 1920 device px, an exact 3x, and the shots stay
   razor sharp; on a 1x screen 1.5x would grow uneven pixel blocks, so those get smooth
   scaling instead. Both rules live at .slide img below. */
.slider { position: relative; margin-bottom: 30px; }

.track {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  gap: 0;
}
.track::-webkit-scrollbar { display: none; }
.track:focus-visible { outline: 2px solid var(--gold-bright); outline-offset: 4px; }

.slide {
  flex: 0 0 100%;
  scroll-snap-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0 6px;
}
.slide img {
  width: 960px; height: auto; max-width: 100%;
  image-rendering: auto;          /* 1x screens: 1.5x, smooth beats blocky */
  border: 1px solid var(--edge);
  border-radius: 3px;
  box-shadow: 0 6px 0 rgba(0,0,0,0.35);
}
/* HiDPI: 960 CSS px land on 1920 device px, a whole 3x — take the hard pixels back. */
@media (min-resolution: 2dppx) {
  .slide img { image-rendering: pixelated; }
}
.slide figcaption {
  max-width: 760px;
  margin: 16px auto 0;
  text-align: center;
  color: var(--dim);
}
.slide figcaption h3 { margin-bottom: 6px; }

.s-nav {
  position: absolute; top: 32%;
  width: 40px; height: 54px;
  font-family: "Fusion Pixel", monospace; font-size: 1.5rem; line-height: 1;   /* 24px */
  color: var(--gold-bright);
  background: rgba(22, 16, 13, 0.82);
  border: 2px solid var(--gold);
  border-radius: 4px;
}
.s-nav:hover { color: #fff; border-color: var(--gold-bright); }
.s-nav.prev { left: -6px; }
.s-nav.next { right: -6px; }
.s-nav[disabled] { opacity: 0.25; }

.dots { display: flex; gap: 7px; justify-content: center; margin-top: 18px; }
.dots button {
  width: 9px; height: 9px;
  padding: 0;
  background: var(--edge);
  border: 1px solid var(--edge);
  transform: rotate(45deg);          /* the game's diamond, not a bullet */
}
.dots button[aria-current="true"] { background: var(--gold-bright); border-color: var(--gold-bright); }

/* ------------------------------------------------- small pages and legal ---
   subscribed, confirmed, 404, imprint, privacy: one column, same furniture. */
.page { max-width: 760px; margin: 0 auto; padding: 70px 20px 90px; }
.page.narrow { max-width: 620px; text-align: center; }
.page .mark { width: 200px; margin: 0 auto 26px; }
.page h1 { font-size: 2.25rem; color: var(--gold-bright); margin-bottom: 6px; }
.page h2 { font-size: 1.5rem; text-align: left; margin: 30px 0 6px; }
.page p { margin-bottom: 12px; color: var(--dim); }
.page.narrow p { color: var(--text); }
.page ul { margin: 0 0 12px 22px; color: var(--dim); }
.page li { margin-bottom: 4px; }
.page address { font-style: normal; color: var(--text); margin-bottom: 12px; }
.page .en {
  font-size: 0.86rem;
  font-style: italic;
  color: var(--dim);
  border-left: 2px solid var(--edge);
  padding-left: 12px;
  margin: 8px 0 18px;
}
.page .back { display: inline-block; margin-top: 26px; }
.page .stamp { font-size: 0.8rem; margin-top: 30px; }
.orb-404 {
  width: 64px; height: 64px;
  margin: 0 auto 20px;
  transform: scale(1.6);
}

/* ------------------------------------------------------------------ sprites --
   Spell effects were built and dropped again, and the walking Keeper went with the
   roadmap rework: on a page you read, moving art pulls the eye off the text. One is
   one left: the ending orb on the 404 and confirmation pages. The sheet is built by
   tools/make_web_assets.py, which prints the background-size and step count it used,
   so the CSS below can never drift from the file. */
.sprite {
  display: block;
  background-repeat: no-repeat;
  background-position: 0 0;
  image-rendering: pixelated;
  animation-play-state: paused;
}
.sprite.on { animation-play-state: running; }

[data-sprite="orb"] {
  background-image: url("img/sheet_orb.png");
  background-size: 900% 100%;
  animation: play 0.9s steps(9) infinite;
}

/* One keyframe drives every sheet. Percentages in background-position are measured
   against (element width minus image width), so 0% to 100% walks the strip exactly
   once no matter how many frames it holds. steps(n) picks the frame count. */
@keyframes play { from { background-position-x: 0%; } to { background-position-x: 100%; } }

/* ----------------------------------------------------------------- reveals -- */
.js .reveal { opacity: 0; transform: translateY(14px); }
.js .reveal.is-in {
  opacity: 1; transform: none;
  transition: opacity 0.42s cubic-bezier(.2,.7,.3,1), transform 0.42s cubic-bezier(.2,.7,.3,1);
}
.js .cols .reveal:nth-child(2).is-in { transition-delay: 0.08s; }
.js .cols .reveal:nth-child(3).is-in { transition-delay: 0.16s; }
.js .road li:nth-child(2).is-in { transition-delay: 0.06s; }
.js .road li:nth-child(3).is-in { transition-delay: 0.12s; }
.js .road li:nth-child(4).is-in { transition-delay: 0.18s; }

/* -------------------------------------------------------------- responsive -- */
@media (max-width: 1020px) {
  /* Below the wide wrap the 960px picture no longer fits, so let it take the column
     and scale smoothly at whatever fraction that is — nearest-neighbour there would
     grow uneven pixel blocks, on any device pixel ratio. */
  .slide img { width: 100%; image-rendering: auto; }
  .s-nav.prev { left: 2px; }
  .s-nav.next { right: 2px; }
}

@media (max-width: 1040px) {
  .cols { grid-template-columns: 1fr; gap: 18px; }
}

@media (max-width: 620px) {
  /* the portrait clip over the text, centred, before the two columns get too thin */
  .dev { grid-template-columns: 1fr; gap: 20px; justify-items: center; }
  .dev-vid { width: 200px; }
}

@media (max-width: 720px) {
  body { font-size: 16px; }
  #nav { flex-wrap: wrap; padding: 6px 12px; }
  .nav-mark img { width: 140px; }

  /* Without JS the burger cannot open anything, so the list stays a scroll strip
     and the button never shows. With JS it becomes a drop-down panel. */
  html:not(.js) #nav ul {
    width: 100%;
    overflow-x: auto;
    justify-content: flex-start;
    padding-bottom: 4px;
    scrollbar-width: none;
  }
  html:not(.js) #nav ul::-webkit-scrollbar { display: none; }

  .js .burger { display: flex; }
  .js #nav ul {
    display: none;
    position: absolute; top: 100%; right: 0;
    min-width: 220px;
    flex-direction: column; align-items: stretch; gap: 0;
    padding: 6px;
    background: var(--surface);   /* opaque: the hero must not read through the menu */
    border: 1px solid var(--gold);
    border-top: none;
    border-radius: 0 0 6px 6px;
    box-shadow: 0 6px 0 var(--shadow);
  }
  .js #nav.open ul { display: flex; }
  .js #nav.open ul a {
    padding: 10px 12px;
    border-bottom: 1px solid var(--edge);
  }
  .js #nav.open ul li:last-child a { border-bottom: none; }
  .js #nav.open ul a:hover { color: var(--gold-bright); background: var(--card); }

  #nav a { padding: 6px 8px; font-size: 1.125rem; }
  .wrap { padding: 46px 16px; }
  h2 { font-size: 1.5rem; }
  h3 { font-size: 1.125rem; }
  .hero { min-height: 62vh; padding: 40px 16px 52px; }
}

@media (max-width: 430px) {
  /* six nav labels on a phone: the strip scrolls, so only the wordmark comes down */
  #nav a { padding: 5px 5px; }
  .nav-mark img { width: 120px; }
}

/* ------------------------------------------------------- motion preferences -- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation: none !important; transition: none !important; }
  .sprite { background-position: 0 0 !important; }
  .js .reveal { opacity: 1 !important; transform: none !important; }
  .hero-vid { display: none; }
  .hero { background: url("img/keyart.webp") center / cover no-repeat; }
}
html.reduce-motion, html.reduce-motion * { animation: none !important; transition: none !important; }
html.reduce-motion .sprite { background-position: 0 0 !important; }
html.reduce-motion .reveal { opacity: 1 !important; transform: none !important; }
html.reduce-motion .hero-vid { display: none; }
html.reduce-motion .hero { background: url("img/keyart.webp") center / cover no-repeat; }
