/* Wishtrap, the public site.
   -------------------------------------------------------------------------
   Two pages share this file: index.html, which leads with the download, and
   manual.html, which explains the game. It used to be one inline stylesheet
   inside index.html, which was correct while there was one page and became a
   second copy the moment there were two.

   The palette is the interface half of ApplicationTheme, not a set of web
   colours. The world's palette and the interface's palette are deliberately
   separate in this project and this page belongs to the interface one.

   Two devices carry the game's look and both are cheap: a hairline in warm
   brown around every panel, and a single lit pixel along a panel's top edge,
   which is the moonlit coping the whole game is drawn around. */

:root {
  color-scheme: dark;

  --lacquer: #161C28;
  --lacquer-raised: #242E40;
  --ink: #0F131C;
  --primary-text: #EDE2C9;
  --secondary-text: #B0A68F;
  --hairline: #5D5342;
  --achievement: #8ABA96;
  --alarm: #D6684F;

  /* The lit edge. Alpha rather than a colour, so it works over both the flat
     lacquer and over a photograph. */
  --moonlit-edge: rgba(237, 226, 201, 0.14);

  --page: 74rem;
  --prose: 44rem;
}

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  /* Every anchor on the manual lands under the sticky header without this. */
  scroll-padding-top: 5.5rem;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background-color: var(--lacquer);
  color: var(--primary-text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 1.0625rem;
  line-height: 1.65;
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
}

@font-face {
  font-family: 'Frijole';
  src: url('/font/Frijole-Regular.ttf') format('truetype');
  font-weight: 400;
  font-display: swap;
}

/* ------------------------------------------------------------------ language

   Both languages sit in the document, next to each other, and the one not in
   use is hidden. Separate files per language was the other option and it was
   refused for one reason: the mechanics on this site change every few days, and
   with two files a change lands in one of them and the other silently becomes a
   lie. Adjacent translations make a missing one visible while you are editing.

   Only ever hidden, never forced to a display value, so a flex or grid element
   keeps its own layout when it is the one being shown. */

html[data-language="sr"] [lang="en"],
html[data-language="en"] [lang="sr"] {
  display: none !important;
}

/* -------------------------------------------------------------------- layout */

.page {
  max-width: var(--page);
  margin: 0 auto;
  padding: 0 1.5rem;
}

.prose {
  max-width: var(--prose);
}

section {
  padding: 4.5rem 0;
  border-top: 1px solid rgba(93, 83, 66, 0.45);
}

section:first-of-type { border-top: none; }

/* ---------------------------------------------------------------- typography */

h1, h2, h3, h4 {
  line-height: 1.15;
  margin: 0;
}

.wordmark {
  font-family: 'Frijole', Georgia, serif;
  text-transform: uppercase;
  letter-spacing: 0.01em;
}

h1 {
  font-size: clamp(2.6rem, 11vw, 4.75rem);
  margin-bottom: 1rem;
}

h2 {
  font-size: clamp(1.5rem, 4vw, 2rem);
  font-weight: 600;
  letter-spacing: 0.01em;
  margin-bottom: 1.25rem;
}

h3 {
  font-size: 1.15rem;
  font-weight: 600;
  margin-bottom: 0.5rem;
}

h4 {
  font-size: 0.95rem;
  font-weight: 600;
  margin-bottom: 0.35rem;
}

p { margin: 0 0 1.15rem 0; }
p:last-child { margin-bottom: 0; }

.lead {
  font-size: clamp(1.15rem, 2.6vw, 1.4rem);
  line-height: 1.5;
  color: var(--primary-text);
}

/* Every caption in the game is drawn uppercase by QuietLabel, so every caption
   here is too. It is the cheapest piece of the interface to bring across. */
.caption {
  display: block;
  color: var(--secondary-text);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.72rem;
  font-weight: 600;
  margin-bottom: 0.75rem;
}

.quiet { color: var(--secondary-text); }
.alarm-text { color: var(--alarm); }

a {
  color: var(--primary-text);
  text-decoration-color: var(--hairline);
  text-underline-offset: 0.2em;
}

a:hover { text-decoration-color: var(--primary-text); }

:focus-visible {
  outline: 2px solid var(--alarm);
  outline-offset: 3px;
}

/* -------------------------------------------------------------------- header */

.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background-color: rgba(22, 28, 40, 0.92);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid rgba(93, 83, 66, 0.5);
}

.site-header .page {
  display: flex;
  align-items: center;
  gap: 1rem;
  min-height: 3.75rem;
}

.site-header .home {
  font-family: 'Frijole', Georgia, serif;
  font-size: 1.05rem;
  text-transform: uppercase;
  text-decoration: none;
  letter-spacing: 0.02em;
  margin-right: auto;
}

.site-header nav {
  display: flex;
  align-items: center;
  gap: 1.25rem;
}

.site-header nav a {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  text-decoration: none;
  color: var(--secondary-text);
}

.site-header nav a:hover,
.site-header nav a[aria-current="page"] { color: var(--primary-text); }

/* On a phone the wordmark, two links and the switch do not fit on one line: they add up
   to about 430 points inside 342 of usable width, and what fell off the right edge was the
   language switch, which also gave the page a horizontal scrollbar. So the header wraps
   instead. `.home` carries `margin-right: auto`, which pushes the whole nav onto a second
   line rather than breaking it in the middle, and nothing is hidden to make room: a link
   removed on small screens is a link the phone reader cannot reach at all. */
@media (max-width: 40rem) {
  .site-header .page {
    flex-wrap: wrap;
    padding-top: 0.6rem;
    padding-bottom: 0.6rem;
    row-gap: 0.5rem;
  }

  .site-header nav { gap: 1rem; }
}

.language-switch {
  display: flex;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  overflow: hidden;
}

.language-switch button {
  appearance: none;
  border: none;
  background: transparent;
  color: var(--secondary-text);
  font: inherit;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  padding: 0.3rem 0.7rem;
  cursor: pointer;
}

.language-switch button[aria-pressed="true"] {
  background-color: var(--primary-text);
  color: var(--lacquer);
}

/* ---------------------------------------------------------------------- hero

   The backdrop is the actual painting the game is played on, dimmed by two
   gradients: one across the whole picture so cream text holds its contrast
   wherever it falls, and one at the foot so the section below does not start
   with a hard horizontal cut. */

.hero {
  position: relative;
  border-top: none;
  padding: 0;
  overflow: hidden;
  border-bottom: 1px solid rgba(93, 83, 66, 0.5);
}

.hero-backdrop {
  position: absolute;
  inset: 0;
  background-image: url('/image/castle-night.webp');
  background-size: cover;
  background-position: center 30%;
}

.hero-backdrop::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to bottom, rgba(22, 28, 40, 0.55) 0%, rgba(22, 28, 40, 0.78) 55%, rgba(22, 28, 40, 0.97) 100%),
    linear-gradient(to right, rgba(22, 28, 40, 0.85) 0%, rgba(22, 28, 40, 0.35) 70%);
}

.hero .page {
  position: relative;
  padding-top: 4.5rem;
  padding-bottom: 4rem;
}

.hero-body { max-width: 40rem; }

.hero h1 { margin-bottom: 1.25rem; }

.invitation {
  margin: 2rem 0;
  padding: 1.25rem 1.35rem;
  background-color: rgba(15, 19, 28, 0.72);
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--alarm);
  border-radius: 4px;
}

.invitation p { font-size: 1rem; }

/* -------------------------------------------------------------------- badges

   These are shaped like the store badges and they are deliberately not the
   store badges. Neither link goes to a store: the iPhone one goes to TestFlight
   and the Android one to Firebase App Distribution. Apple's and Google's badge
   guidelines both forbid their artwork for anything but a real store listing,
   and a tester who taps "Download on the App Store" and lands in TestFlight
   reasonably thinks something is broken. So the shape and the two-line format
   are kept, because that is what makes a badge read as a download, and the
   words say what actually happens. */

.badges {
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem;
  margin: 2rem 0 1.25rem 0;
  padding: 0;
  list-style: none;
}

.badge {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  min-width: 15rem;
  padding: 0.7rem 1.2rem 0.7rem 1rem;
  background-color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  box-shadow: inset 0 1px 0 var(--moonlit-edge);
  text-decoration: none;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}

.badge:hover {
  border-color: var(--primary-text);
  background-color: var(--lacquer-raised);
}

.badge svg {
  flex: none;
  width: 1.85rem;
  height: 1.85rem;
  fill: var(--primary-text);
}

.badge-words {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
}

.badge-words .over {
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--secondary-text);
}

.badge-words .under {
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: 0.01em;
}

.requirements {
  font-size: 0.85rem;
  color: var(--secondary-text);
}

/* Inside a panel it is a footnote under the steps, and the last step's own margin is
   removed, so without this it sits against the sentence above it. */
.panel .requirements { margin-top: 1.25rem; }

/* --------------------------------------------------------------------- panels */

.panel {
  background-color: var(--lacquer-raised);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  box-shadow: inset 0 1px 0 var(--moonlit-edge);
  padding: 1.5rem;
}

.grid {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: 1fr;
}

@media (min-width: 48rem) {
  .grid.two { grid-template-columns: repeat(2, 1fr); }
  .grid.three { grid-template-columns: repeat(3, 1fr); }
}

/* --------------------------------------------------------------------- steps */

.steps {
  margin: 1.25rem 0 0 0;
  padding-left: 1.4rem;
  counter-reset: step;
}

.steps li {
  margin-bottom: 0.9rem;
  padding-left: 0.25rem;
}

.steps li:last-child { margin-bottom: 0; }

.steps strong { font-weight: 600; }

.platform-head {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid rgba(93, 83, 66, 0.5);
}

.platform-head svg {
  width: 1.5rem;
  height: 1.5rem;
  fill: var(--primary-text);
  flex: none;
}

.platform-head h3 { margin: 0; }

.caveat {
  margin-top: 1.5rem;
  padding: 1.1rem 1.25rem;
  border: 1px solid var(--alarm);
  border-radius: 6px;
  background-color: rgba(214, 104, 79, 0.09);
}

.caveat p { font-size: 0.95rem; }

.good {
  border-color: var(--achievement);
  background-color: rgba(138, 186, 150, 0.09);
}

/* -------------------------------------------------------------------- figures */

figure {
  margin: 0;
}

figure img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--hairline);
  border-radius: 6px;
}

figcaption {
  margin-top: 0.65rem;
  font-size: 0.85rem;
  color: var(--secondary-text);
}

/* ---------------------------------------------------------------------- traps */

.trap-card { text-align: center; }

/* 11rem against the picture's own 341px, so the trap is as large as it can be drawn
   without being upscaled past what the generator produced. It was 8.5rem, at which the
   0.7m SPIKE is about thirty pixels tall and the shared scale these three pictures exist
   for stops being readable. */
.trap-card img {
  display: block;
  width: 11rem;
  height: auto;
  margin: 0 auto 0.75rem auto;
}

.trap-card .dimension {
  display: block;
  font-family: 'Frijole', Georgia, serif;
  font-size: 1.05rem;
  color: var(--alarm);
  margin-bottom: 0.5rem;
}

.trap-card h3 {
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* ------------------------------------------------------------------ next step */

.onward {
  display: block;
  padding: 1.75rem;
  text-decoration: none;
  background-color: var(--lacquer-raised);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  box-shadow: inset 0 1px 0 var(--moonlit-edge);
  transition: border-color 0.15s ease;
}

.onward:hover { border-color: var(--primary-text); }

.onward .chapters {
  margin: 1rem 0 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.5rem;
}

.onward .chapters li {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--secondary-text);
  border: 1px solid rgba(93, 83, 66, 0.7);
  border-radius: 999px;
  padding: 0.2rem 0.65rem;
}

.onward .go {
  display: inline-block;
  margin-top: 1.25rem;
  font-weight: 600;
  color: var(--alarm);
}

/* --------------------------------------------------------------------- manual */

.manual-layout {
  display: grid;
  gap: 3rem;
  grid-template-columns: 1fr;
  padding-top: 3rem;
  padding-bottom: 5rem;
}

/* A grid item's automatic minimum size is its min-content width, and this page holds two
   things whose min-content width is enormous: a formula set in `white-space: pre` and a
   six column table. Without this the column grows to fit them and the whole page scrolls
   sideways on a phone, 700 points of it inside 375. The overflow belongs to the formula
   and the table, which each scroll inside their own box, and `min-width: 0` is what keeps
   it there. */
.manual-layout > * { min-width: 0; }

@media (min-width: 62rem) {
  .manual-layout { grid-template-columns: 16rem 1fr; }

  .contents {
    position: sticky;
    top: 5rem;
    align-self: start;
    max-height: calc(100vh - 7rem);
    overflow-y: auto;
  }
}

.contents ol {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: chapter;
  border-left: 1px solid rgba(93, 83, 66, 0.6);
}

.contents li { counter-increment: chapter; }

.contents a {
  display: block;
  padding: 0.4rem 0 0.4rem 0.9rem;
  margin-left: -1px;
  border-left: 2px solid transparent;
  font-size: 0.9rem;
  text-decoration: none;
  color: var(--secondary-text);
}

.contents a::before {
  content: counter(chapter) '. ';
  color: var(--hairline);
}

.contents a:hover {
  color: var(--primary-text);
  border-left-color: var(--hairline);
}

.contents a.current {
  color: var(--primary-text);
  border-left-color: var(--alarm);
}

.manual-body > section {
  padding: 0 0 3.5rem 0;
  border-top: none;
}

.manual-body > section + section {
  padding-top: 3.5rem;
  border-top: 1px solid rgba(93, 83, 66, 0.45);
}

.manual-body h2 { scroll-margin-top: 5rem; }

.manual-body > section > *:not(figure):not(.grid):not(.table-scroll) {
  max-width: var(--prose);
}

/* ------------------------------------------------------------------ formulae

   A formula is set apart rather than run into the prose, because the two
   audiences for this site read a mechanic differently: one reads the sentence
   and stops, one skips to the numbers. Making the numbers findable is what
   stops somebody writing their own wiki with the numbers guessed wrong. */

.formula {
  margin: 1.5rem 0;
  padding: 1.25rem 1.35rem;
  background-color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 6px;
  box-shadow: inset 0 1px 0 var(--moonlit-edge);
  overflow-x: auto;
}

.formula code, .formula pre {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--primary-text);
  margin: 0;
  white-space: pre;
}

.formula .term { color: var(--achievement); }
/* The note under a formula is prose, so it is set in the prose face. `inherit` was wrong
   and looked deliberate: it inherits from the <pre> it sits inside, which is monospace, so
   the sentence explaining the formula read as another line of the formula. */
.formula .note {
  display: block;
  margin-top: 1rem;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 0.9rem;
  line-height: 1.55;
  color: var(--secondary-text);
  white-space: normal;
}

code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.92em;
  background-color: rgba(15, 19, 28, 0.8);
  border: 1px solid rgba(93, 83, 66, 0.6);
  border-radius: 3px;
  padding: 0.05em 0.35em;
}

/* --------------------------------------------------------------------- tables */

.table-scroll {
  overflow-x: auto;
  margin: 1.5rem 0;
  border: 1px solid var(--hairline);
  border-radius: 6px;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
}

th, td {
  text-align: left;
  padding: 0.7rem 1rem;
  border-bottom: 1px solid rgba(93, 83, 66, 0.4);
  vertical-align: top;
}

thead th {
  background-color: var(--ink);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--secondary-text);
  white-space: nowrap;
}

tbody tr:last-child td { border-bottom: none; }

td .name {
  font-weight: 600;
  white-space: nowrap;
}

/* ------------------------------------------------------------------- listings */

.plain {
  margin: 1.25rem 0;
  padding: 0;
  list-style: none;
}

.plain li {
  padding: 0.6rem 0 0.6rem 1.5rem;
  border-bottom: 1px solid rgba(93, 83, 66, 0.35);
  position: relative;
}

.plain li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 1.15rem;
  width: 0.5rem;
  height: 1px;
  background-color: var(--alarm);
}

.plain li:last-child { border-bottom: none; }

.plain strong { font-weight: 600; }

/* ----------------------------------------------------------------------- form */

form { max-width: var(--prose); }

label {
  display: block;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--secondary-text);
  margin-bottom: 0.4rem;
}

.field { margin-bottom: 1.25rem; }

textarea, input[type="text"], input[type="email"] {
  width: 100%;
  font: inherit;
  font-size: 1rem;
  color: var(--primary-text);
  background-color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 5px;
  padding: 0.7rem 0.85rem;
}

textarea {
  min-height: 9rem;
  resize: vertical;
}

textarea:focus, input:focus {
  border-color: var(--primary-text);
  outline: none;
}

button.send {
  appearance: none;
  font: inherit;
  font-weight: 700;
  font-size: 0.9rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--lacquer);
  background-color: var(--alarm);
  border: none;
  border-radius: 5px;
  padding: 0.8rem 1.6rem;
  cursor: pointer;
}

button.send:hover { filter: brightness(1.1); }
button.send:disabled { opacity: 0.5; cursor: default; }

.feedback-status {
  margin-top: 1rem;
  font-size: 0.95rem;
  color: var(--secondary-text);
}

.feedback-status[data-state="sent"] { color: var(--achievement); }

/* A field no person sees and no person fills. Scrapers fill everything, so
   anything arriving in here is a bot. Hidden with a stylesheet rule rather than
   the hidden attribute, because some bots skip what is explicitly hidden. */
.decoy {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* --------------------------------------------------------------------- footer */

footer {
  border-top: 1px solid var(--hairline);
  padding: 2.5rem 0 3.5rem 0;
  color: var(--secondary-text);
  font-size: 0.9rem;
}

footer .page {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2.5rem;
  align-items: baseline;
}

footer .rule { margin-right: auto; }
