@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("assets/fonts/ibm-plex-sans-latin-400-normal.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("assets/fonts/ibm-plex-sans-latin-600-normal.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("assets/fonts/ibm-plex-sans-latin-700-normal.woff2") format("woff2");
}

@font-face {
  font-family: "Press Start 2P";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("assets/fonts/press-start-2p-latin-400-normal.woff2") format("woff2");
}

/* Structure that every page shares. theme.css sets the colors, and the story sets them in
   game.css. Each look has a file that sets --radius, --dialog-radius and --dialog-width. */
:root {
  --pixel: "Press Start 2P", "Courier New", monospace;
  --sans: "IBM Plex Sans", arial, sans-serif;
  /* The space beside the header, the main part and the footer, so their content lines up at 1120px
     or less. */
  --page-gutter: max(16px, calc((100vw - 1120px) / 2));
  /* The width of one frame of the tallest character in character-pictures.js at the base size of the
     characters, and how long a sprite strip shows each frame. A frame of Jack, 256 px, shows one pixel
     of the art on one pixel on screen. characters.js stops with an error when a loaded sprite of the
     tallest character has frames of another size. */
  --sprite-size: 256px;
  --sprite-frame-time: calc(1s / 12);
  /* How long each frame of the sprite strips shows in the art as drawn. */
  --art-frame-time: 200ms;
  /* The layers in front of the page, from back to front. Jack beside the dark area of the fun
     homepage scrolls with the page, so he passes behind the sticky header. */
  --layer-jack-beside-area: 1;
  --layer-header: calc(var(--layer-jack-beside-area) + 1);
  --layer-trap: calc(var(--layer-header) + 1);
  --layer-way-out: calc(var(--layer-trap) + 1);
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  min-height: 100vh;
  font: 400 16px/1.5 var(--sans);
  background: var(--bg);
  color: var(--text);
}
/* The glow of theme.css does not move while the page scrolls, so it lights the top of the window on
   a long page as on a short one. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background: var(--page-glow);
  pointer-events: none;
}

h1, h2, h3, p { margin: 0; }

[hidden] { display: none !important; }
dialog:not([open]) { display: none; }

.muted { color: var(--muted); }

button:focus-visible, a:focus-visible, input:focus-visible, [tabindex="0"]:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
}

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px;
}

.btn {
  display: inline-block;
  text-decoration: none;
  cursor: pointer;
}

.actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }

/* A sprite of characters.js shows one frame of its file. Each place sets a width of --sprite-size
   times or divided by a power of two, so every pixel of the art of the tallest character covers the
   same number of pixels on screen. A standing figure scales the frames of every other character by
   his height. Each frame is square, so the full height of the picture shows one frame. A strip
   shows its frames one after another while the system allows motion, over and over, or
   --strip-rounds times after --strip-delay where a place sets them. A strip with a rest frame shows
   that frame and plays its frames only where a look starts it, once. */
/* The picture takes its height from the width of the frame, which every place sets, because a
   percentage height is lost where the frame sits in a row whose height comes from its content. */
.character-sprite { width: var(--sprite-size); aspect-ratio: 1; overflow: hidden; container-type: inline-size; }
.character-sprite img {
  display: block;
  width: auto;
  max-width: none;
  height: 100cqi;
  image-rendering: pixelated;
}
@media (prefers-reduced-motion: no-preference) {
  .character-sprite-strip:not(.character-sprite-once) img {
    animation: sprite-frames calc(var(--frames) * var(--sprite-frame-time)) steps(var(--frames)) var(--strip-delay, 0s) var(--strip-rounds, infinite) backwards;
  }
}
.character-sprite-once img { transform: translateX(calc(-100% * var(--rest-frame) / var(--frames))); }
@keyframes sprite-frames { from { transform: none; } to { transform: translateX(-100%); } }

/* A character styled by standingFigureStyle in characters.js. His frames are --frame-width wide:
   --width times --frame-scale, so each character stands at his height, times --pose-scale, so each
   sprite shows him at one size. His figure, between --figure-left and --figure-right percent of the
   picture, is --figure-width wide. standingFigure gives the class standing-figure, and a place that
   only measures a figure gives the class figure-size. */
.standing-figure, .figure-size {
  --frame-width: calc(var(--width) * var(--frame-scale) * var(--pose-scale));
  --figure-width: calc((var(--figure-right) - var(--figure-left)) * var(--frame-width) / 100);
}
/* A character standing beside others covers only the width of his figure in a row, and stands with
   the bottom edge of his figure, --figure-bottom percent down the picture, on the bottom of the
   row, so the empty edges of a sprite frame do not open a gap and every character stands on one
   floor. */
.standing-figure {
  margin-inline: calc(var(--figure-left) * var(--frame-width) / -100) calc((100 - var(--figure-right)) * var(--frame-width) / -100);
  margin-bottom: calc((100 - var(--figure-bottom)) * var(--frame-width) / -100);
}
.standing-figure .character { width: var(--frame-width); }

/* The pixel sprites of Shelly and Jack are drawn for a dark background, and parts of their edge, such
   as the white tail of Jack, are as light as the light theme. So in the light theme they get a dark
   outline on every page. */
:root[data-theme="light"] .character {
  filter: drop-shadow(1px 0 0 var(--text)) drop-shadow(-1px 0 0 var(--text))
    drop-shadow(0 1px 0 var(--text)) drop-shadow(0 -1px 0 var(--text));
}

/* Read by screen readers and not shown. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- The header, the same on both websites and the legal pages, and the footer of every page --- */

/* The header sets its own font, so it has the same height on every page. */
.nav {
  position: sticky;
  top: 0;
  z-index: var(--layer-header);
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px var(--page-gutter);
  background: color-mix(in oklab, var(--bg) 88%, transparent);
  border-bottom: 1px solid var(--line);
  backdrop-filter: blur(8px);
  font: 400 15px/1.4 var(--sans);
}
/* site-header.js sets .scrolled while the page is scrolled down. */
.nav.scrolled { box-shadow: 0 4px 12px var(--shadow); }
.logo {
  font: 700 20px/1 var(--sans);
  color: var(--text);
  text-decoration: none;
}
.nav-links { display: flex; gap: 20px; }
.nav-links a { color: var(--text-2); text-decoration: none; }
.nav-links a:hover { color: var(--text); }
.icon-button {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: transparent;
  color: var(--text);
  cursor: pointer;
}
.menu-button { display: none; }
.settings-button { margin-left: auto; }

/* One column for each group of links, and the copyright below them. Leaves room at the bottom for
   the traps that open along the bottom edge. The footer sets its fonts, so it looks the same on every
   page. */
.site-footer {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, max-content));
  gap: 24px 48px;
  padding: 40px var(--page-gutter) 120px;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font: 400 14px/1.5 var(--sans);
}
.site-footer h2 { margin-bottom: 8px; color: var(--text); font: 600 14px/1.5 var(--sans); }
.site-footer ul { display: grid; gap: 6px; margin: 0; padding: 0; list-style: none; }
.site-footer a { color: inherit; text-decoration: none; }
.site-footer a:hover { color: var(--text); text-decoration: underline; }
.copyright { grid-column: 1 / -1; }

/* --- Layout of both websites --- */

main { padding: 0 var(--page-gutter); }

.section {
  display: grid;
  gap: 16px;
  padding: 56px 0;
  scroll-margin-top: var(--header-height);
}

/* --- Traps --- */

/* A trap is a dialog opened with show(). Each kind below places it on the page. */
.trap {
  position: fixed;
  inset: auto;
  z-index: var(--layer-trap);
  width: auto;
  margin: 0;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 16px 48px var(--shadow);
  animation: slide-up 0.35s ease-out both;
}
@keyframes slide-up { from { transform: translateY(24px); opacity: 0; } }

.trap-close {
  margin-left: auto;
  background: none;
  border: 0;
  color: var(--muted);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  padding: 4px 8px;
}
.trap-quiet {
  background: none;
  border: 0;
  color: var(--muted);
  font: 400 14px/1.4 var(--sans);
  text-decoration: underline;
  cursor: pointer;
  padding: 8px 0;
}
.trap-bait {
  background: var(--jack-purple);
  color: var(--on-jack);
  border: 0;
  border-radius: 8px;
  padding: 14px 24px;
  font: 700 16px/1 var(--sans);
  cursor: pointer;
}
.trap-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 20px; }

.trap-cookies {
  left: 16px;
  right: 16px;
  bottom: 16px;
  max-width: 720px;
  margin: 0 auto;
  padding: 20px;
  display: grid;
  gap: 12px;
}
.file-chip {
  justify-self: start;
  font: 600 14px/1 var(--sans);
  color: var(--text-2);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 8px 10px;
}

.trap-promo {
  top: 76px;
  left: 16px;
  right: 16px;
  max-width: 560px;
  margin: 0 auto;
  padding: 18px;
  display: flex;
  align-items: flex-start;
  gap: 12px;
}
.trap-promo > div { display: grid; gap: 8px; }
.promo-link {
  justify-self: start;
  background: none;
  border: 0;
  padding: 0;
  font: 600 16px/1.4 var(--sans);
  color: var(--accent);
  text-decoration: underline;
  cursor: pointer;
}

.trap-notice {
  top: 76px;
  right: 16px;
  width: min(400px, calc(100vw - 32px));
  padding: 18px;
  display: grid;
  gap: 12px;
}
.notice-head { display: flex; align-items: flex-start; gap: 8px; }

.trap-form {
  right: 16px;
  bottom: 16px;
  width: min(380px, calc(100vw - 32px));
  max-height: calc(100dvh - 32px);
  overflow-y: auto;
  padding: 18px;
  display: grid;
  gap: 12px;
}
.form-step { display: grid; gap: 12px; }
.survey-question { font-weight: 600; }
.survey-answers { display: grid; gap: 8px; }
.survey-answer {
  font: 600 16px/1.4 var(--sans);
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  padding: 10px 12px;
  text-align: left;
  cursor: pointer;
}

.trap-qr {
  left: 16px;
  bottom: 16px;
  width: min(280px, calc(100vw - 32px));
  padding: 18px;
  display: grid;
  gap: 10px;
  justify-items: center;
  text-align: center;
}
.trap-qr .trap-close { position: absolute; top: 8px; right: 8px; }
.qr-button {
  display: grid;
  gap: 8px;
  justify-items: center;
  background: none;
  border: 0;
  font: 600 14px/1.4 var(--sans);
  cursor: pointer;
}
.qr {
  width: 160px;
  height: 160px;
  padding: 12px;
  background: var(--qr-paper);
  border-radius: 8px;
  image-rendering: pixelated;
}
.qr-caption { font: 600 14px/1.4 var(--sans); color: var(--muted); }

.trap-chat {
  right: 16px;
  bottom: 16px;
  width: min(360px, calc(100vw - 32px));
  padding: 16px;
  display: grid;
  gap: 12px;
}
.chat-head { display: flex; align-items: center; gap: 10px; }
.chat-avatar {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--chat-avatar);
  color: var(--on-chat-avatar);
  font: 700 14px/1 var(--sans);
}
.chat-name { font-weight: 600; }
.chat-status { font-size: 13px; color: var(--success); }
.chat-messages { display: grid; gap: 8px; min-height: 64px; }
.chat-bubble {
  justify-self: start;
  max-width: 90%;
  border-radius: 12px 12px 12px 4px;
  padding: 10px 12px;
  animation: slide-up 0.25s ease-out both;
}
.chat-input { display: flex; gap: 8px; }
.chat-input input { flex: 1; min-width: 0; }

/* --- Dialogs: the sign-in trap, result cards and settings --- */

.trap-login, .result, .settings {
  display: grid;
  width: min(var(--dialog-width), calc(100vw - 32px));
  max-height: calc(100dvh - 32px);
  padding: 24px;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--dialog-radius);
}
.trap-login { gap: 14px; }
.trap-login h2, .result h2, .settings h2 { font: 700 20px/1.3 var(--sans); }
.login-head { display: flex; align-items: center; }
.login-form { display: grid; gap: 14px; }
.login-form label { display: grid; gap: 6px; font-weight: 600; }
.login-form input, .chat-input input {
  font: 16px/1.4 var(--sans);
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  padding: 10px 12px;
}

.caught { --tone: var(--danger); }
.spotted { --tone: var(--success); }
.flags { margin: 0; padding-left: 20px; display: grid; gap: 6px; }
.result > .btn { justify-self: start; }

.settings { width: min(360px, calc(100vw - 32px)); }
.settings form { justify-self: start; }
.setting {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
  font-weight: 600;
}
.setting input {
  appearance: none;
  position: relative;
  width: 44px;
  height: 24px;
  margin: 0;
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  background: var(--bg);
  cursor: pointer;
}
.setting input::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--muted);
  transition: transform 0.15s;
}
.setting input:checked { background: var(--accent); border-color: var(--accent); }
.setting input:checked::after { transform: translateX(20px); }
.segmented {
  display: flex;
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
}
.segmented > * {
  padding: 4px 10px;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-decoration: none;
  cursor: pointer;
}
.segmented > * + * { border-left: 1px solid var(--line); }
.segmented :is([aria-pressed="true"], [aria-current]) { background: var(--accent); color: var(--on-accent); }

/* --- Phone --- */

/* home.css and the stylesheet of each website size the homepage and the headings for phones, below
   the values for wide screens. On a phone the links show below the header while the menu button is
   pressed. */
@media (width <= 760px) {
  .site-header .menu-button { display: grid; order: 1; }
  .nav-links {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    gap: 0;
    padding: 4px var(--page-gutter) 12px;
    background: var(--bg);
    border-bottom: 1px solid var(--line);
  }
  .nav-links a { padding: 12px 0; }
  .menu-button[aria-expanded="true"] ~ .nav-links { display: flex; }
}

@media (prefers-reduced-motion: no-preference) {
  .nav { transition: box-shadow 0.3s; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .trap, .chat-bubble { animation: none; }
  .setting input::after { transition: none; }
}
