/* playcharnel.com, drawn the way the game draws itself. Every ink and the CRT
   come out of crt.css, which scripts/site_crt.py makes. Text sits at the game's
   own 1x, and the big pieces double it on a wide screen. */

@font-face {
  font-family: "VGA";
  src: url("/fonts/PxPlus_IBM_VGA_8x16.ttf") format("truetype");
  font-display: block;
}

/* Alagard's pixel is 64 of its 1024 units, so it is crisp at multiples of 16px. */
@font-face {
  font-family: "Alagard";
  src: url("/fonts/Alagard.ttf") format("truetype");
  font-display: block;
}

/* --s is the scene pixel, and each ink is the CRT's cell at that size. */
:root {
  --s: 1px;
  --bar: 48px;
  --bg: var(--bg-s1);
  --panel: var(--panel-s1);
  --strip: var(--strip-s1);
  --rail: var(--rail-s1);
  --bone: var(--bone-s1);
  --linen: var(--linen-s1);
  --chrome: var(--chrome-s1);
  --pick: var(--pick-s1);
  --hint: var(--hint-s1);
  --read: var(--read-s1);
}

/* On a wide screen the hook and the chevron double the scene pixel. A headline
   takes one and a half, so that Alagard's pixel is two scene pixels, as in the
   game. */
@media (min-width: 1024px) {
  .big,
  .down {
    --s: 2px;
    --bg: var(--bg-s2);
    --panel: var(--panel-s2);
    --strip: var(--strip-s2);
    --rail: var(--rail-s2);
    --bone: var(--bone-s2);
    --linen: var(--linen-s2);
    --chrome: var(--chrome-s2);
    --pick: var(--pick-s2);
    --hint: var(--hint-s2);
    --read: var(--read-s2);
  }

  h1,
  h2 {
    --s: 1.5px;
    --linen: var(--linen-s15);
  }
}

/* Reading text and buttons are 16px, and 24px on a wide retina screen, where
   each pixel of the face is three device pixels and takes the game's 3x cell. */
@media (min-width: 1024px) and (min-resolution: 2dppx), (min-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) {
  .pitch p,
  .card p,
  .press > p:not(.fine),
  .list li,
  .cta:not(.small) {
    --s: 1.5px;
    --bone: var(--bone-s15);
    --linen: var(--linen-s15);
    --chrome: var(--chrome-s15);
    --pick: var(--pick-s15);
    --read: var(--read-s15);
    font-size: 24px;
    line-height: 36px;
  }
}

* {
  box-sizing: border-box;
}

/* Headings take LINEN_C, the game's ink for a line brighter than the bone
   round it. Reading text takes the warmer ink of the game's own readings. */
html {
  --ink: var(--read);
  background-color: var(--bg-flat);
  background-image: var(--bg);
  background-size: var(--s) var(--s);
  font: 16px/24px "VGA", ui-monospace, monospace;
  font-synthesis: none;
  -webkit-font-smoothing: none;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scrollbar-color: var(--rail-flat) var(--bg-flat);
}

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

body {
  margin: 0;
}

[id] {
  scroll-margin-top: calc(var(--bar) + 24px);
}

/* The Steam page's backdrop, a lit crypt with nobody in it, far down behind
   the page, as the title card shows a room through its veil. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    linear-gradient(rgba(11, 9, 8, 0.82), rgba(11, 9, 8, 0.82)),
    url("/media/backdrop.png") 50% 50% / cover no-repeat;
}

/* The corners of the glass, over everything and under no pointer. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 10;
  pointer-events: none;
  background: var(--vignette);
}

/* Text takes its ink through the CRT: the ink's cell, tiled inside the glyphs. */
h1,
h2,
p,
li,
dt,
dd,
figcaption,
strong,
a,
span {
  background-image: var(--ink);
  background-size: var(--s) var(--s);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  image-rendering: pixelated;
}

img,
video {
  display: block;
  max-width: 100%;
  height: auto;
  image-rendering: auto;
}

p {
  margin: 0;
}

a {
  --ink: var(--pick);
  text-decoration: none;
}

a:focus-visible,
button:focus-visible,
video:focus-visible {
  outline: 2px solid var(--pick-flat);
  outline-offset: 4px;
}

::selection {
  background: #3d3527;
  color: #ece4ce;
  -webkit-text-fill-color: #ece4ce;
}

/* Alagard in capitals, which is how the game heads a card. */
h1,
h2 {
  --ink: var(--linen);
  margin: 0;
  font: 32px/36px "Alagard", serif;
  font-weight: normal;
  text-transform: uppercase;
}

@media (min-width: 1024px) {
  h1,
  h2 {
    font-size: 48px;
    line-height: 54px;
  }
}

@media (min-width: 1024px) {
  .big {
    font-size: 32px;
    line-height: 48px;
  }
}

.hook {
  --ink: var(--linen);
}

.fine {
  --ink: var(--hint);
}

.fine a {
  --ink: var(--chrome);
}

.fine a:hover {
  --ink: var(--pick);
}

/* The game's sel_blink: 0.4 to 1 and back on a sine, once a second. */
@keyframes blink {
  to {
    opacity: 0.4;
  }
}

.chev {
  --ink: var(--pick);
}

/* ── frames ─────────────────────────────────────────────────────────────── */

/* Two rules round a picture, one scene pixel each and three apart. Only the
   rules double on a wide screen, so what is inside keeps its own scale. */
.frame {
  position: relative;
}

a.frame,
.frame > a {
  display: block;
}

.frame::before {
  --gap: calc(var(--s) * 3);
  content: "";
  position: absolute;
  inset: calc(var(--s) * -6);
  pointer-events: none;
  background-image: var(--chrome);
  background-size: var(--s) var(--s);
  image-rendering: pixelated;
  -webkit-mask:
    linear-gradient(#000 0 0) top left / 100% var(--s) no-repeat,
    linear-gradient(#000 0 0) bottom left / 100% var(--s) no-repeat,
    linear-gradient(#000 0 0) top left / var(--s) 100% no-repeat,
    linear-gradient(#000 0 0) top right / var(--s) 100% no-repeat,
    linear-gradient(#000 0 0) left var(--gap) top var(--gap) / calc(100% - 2 * var(--gap)) var(--s) no-repeat,
    linear-gradient(#000 0 0) left var(--gap) bottom var(--gap) / calc(100% - 2 * var(--gap)) var(--s) no-repeat,
    linear-gradient(#000 0 0) left var(--gap) top var(--gap) / var(--s) calc(100% - 2 * var(--gap)) no-repeat,
    linear-gradient(#000 0 0) right var(--gap) top var(--gap) / var(--s) calc(100% - 2 * var(--gap)) no-repeat;
  mask:
    linear-gradient(#000 0 0) top left / 100% var(--s) no-repeat,
    linear-gradient(#000 0 0) bottom left / 100% var(--s) no-repeat,
    linear-gradient(#000 0 0) top left / var(--s) 100% no-repeat,
    linear-gradient(#000 0 0) top right / var(--s) 100% no-repeat,
    linear-gradient(#000 0 0) left var(--gap) top var(--gap) / calc(100% - 2 * var(--gap)) var(--s) no-repeat,
    linear-gradient(#000 0 0) left var(--gap) bottom var(--gap) / calc(100% - 2 * var(--gap)) var(--s) no-repeat,
    linear-gradient(#000 0 0) left var(--gap) top var(--gap) / var(--s) calc(100% - 2 * var(--gap)) no-repeat,
    linear-gradient(#000 0 0) right var(--gap) top var(--gap) / var(--s) calc(100% - 2 * var(--gap)) no-repeat;
}

@media (min-width: 1024px) {
  .frame::before {
    --s: 2px;
    --chrome: var(--chrome-s2);
  }
}

/* ── buttons and links ──────────────────────────────────────────────────── */

/* A button is the game's picked menu row: the chevron pulsing, and the words
   in gold. */
.cta {
  --ink: none;
  display: inline-block;
}

.cta .text {
  --ink: var(--pick);
}

.cta .chev {
  margin-right: 1ch;
  animation: blink 0.5236s ease-in-out infinite alternate;
}

.cta:hover .text,
.cta:focus-visible .text {
  --ink: var(--linen);
}

/* A plain link row, as the game's idle menu rows: chrome, and gold with the
   chevron while the pointer is on it. */
.menu {
  display: flex;
  justify-content: center;
  gap: 4ch;
}

.menu .label {
  --ink: none;
  position: relative;
}

.menu .text {
  --ink: var(--chrome);
}

.menu .chev {
  position: absolute;
  right: calc(100% + 1ch);
  visibility: hidden;
}

.menu a:hover .text,
.menu a:focus-visible .text {
  --ink: var(--pick);
}

.menu a:hover .chev,
.menu a:focus-visible .chev {
  visibility: visible;
  animation: blink 0.5236s ease-in-out infinite alternate;
}

/* The stairwell's chevron, the game's turned >, which says the way is down. */
.down {
  display: block;
  width: 40px;
  height: 24px;
  margin: 16px auto 0;
  background-image: var(--pick);
  background-size: var(--s) var(--s);
  -webkit-background-clip: border-box;
  background-clip: border-box;
  image-rendering: pixelated;
  --glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6' shape-rendering='crispEdges'%3E%3Cpath d='M0 0h2v2H0zM8 0h2v2H8zM2 2h2v2H2zM6 2h2v2H6zM4 4h2v2H4z'/%3E%3C/svg%3E");
  -webkit-mask: var(--glyph) 0 0 / 100% 100% no-repeat;
  mask: var(--glyph) 0 0 / 100% 100% no-repeat;
  animation: blink 0.5236s ease-in-out infinite alternate;
}

/* ── the bar ────────────────────────────────────────────────────────────── */

/* The run's top line, as the game writes it over a floor. It comes in once the
   title's button has gone off the top. */
.bar {
  position: fixed;
  top: 0;
  right: 0;
  left: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 16px;
  height: var(--bar);
  padding: 0 24px;
  background: rgba(9, 7, 6, 0.94);
  border-bottom: 1px solid var(--rail-flat);
  visibility: hidden;
  opacity: 0;
  transition:
    opacity 0.2s linear,
    visibility 0s linear 0.2s;
}

.bar.shown {
  visibility: visible;
  opacity: 1;
  transition: opacity 0.2s linear;
}

.bar .home {
  --ink: var(--bone);
}

.bar .links {
  --ink: var(--rail);
}

.bar .links a {
  --ink: var(--chrome);
}

.bar .links a:hover {
  --ink: var(--pick);
}

.bar .cta {
  margin-left: auto;
}

@media (max-width: 640px) {
  .bar .links {
    display: none;
  }
}

/* ── the title ──────────────────────────────────────────────────────────── */

.title {
  --room-h: clamp(260px, 44svh, 60vw);
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding-bottom: 24px;
  text-align: center;
}

/* The page's own dark stands behind the key art, so the backdrop never meets
   its ruins. On a wide screen it fades out over the rest of the title, and on
   a phone over a short span under the art. */
.title::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-color: var(--bg-flat);
  background-image: var(--bg);
  background-size: var(--s) var(--s);
  -webkit-mask-image: linear-gradient(to bottom, #000 var(--room-h), transparent var(--blackout-end, 100%));
  mask-image: linear-gradient(to bottom, #000 var(--room-h), transparent var(--blackout-end, 100%));
}

@media (max-width: 1023.98px) {
  .title {
    --blackout-end: calc(var(--room-h) + 120px);
  }
}

/* The Steam library art: the player's lamp aimed up into the name. */
.room {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  align-self: stretch;
  height: var(--room-h);
  background: #000 url("/media/charnel-hero.png") 50% 25% / cover no-repeat;
  -webkit-mask-image: linear-gradient(to bottom, #000 55%, transparent);
  mask-image: linear-gradient(to bottom, #000 55%, transparent);
}

.room h1 {
  margin-top: clamp(16px, 4svh, 48px);
}

.room img {
  width: 320px;
}

@media (min-width: 1024px) {
  .room img {
    width: 640px;
  }
}

.title .cta {
  margin-top: 8px;
}

/* On a phone the hook doubles, the button grows by half, and every link gets
   a target as tall as a thumb. */
@media (max-width: 1023.98px) {
  .hook {
    --s: 2px;
    --linen: var(--linen-s2);
    font-size: 32px;
    line-height: 40px;
    text-wrap: balance;
  }

  .title .cta {
    --s: 1.5px;
    --pick: var(--pick-s15);
    --linen: var(--linen-s15);
    padding: 12px 16px;
    font-size: 24px;
    line-height: 32px;
  }

  .title .menu {
    gap: 24px;
  }

  .title .menu a {
    padding: 12px 8px;
  }

  .bar .cta {
    padding: 12px 0;
  }
}

/* ── the page ───────────────────────────────────────────────────────────── */

main {
  max-width: 1008px;
  margin: 0 auto;
  padding: 0 24px;
}

.screen {
  position: relative;
  max-width: 880px;
  margin: 16px auto 0;
}

.screen video {
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
}

/* The teaser waits under a veil with one framed row on it, as a card does. */
.play {
  position: absolute;
  inset: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 0;
  border: 0;
  font: inherit;
  background: rgba(12, 10, 9, 0.55);
  cursor: pointer;
  transition: opacity 0.3s linear;
}

.play[hidden] {
  display: none;
}

.play .cta {
  pointer-events: none;
}

.play:hover .cta .text {
  --ink: var(--linen);
}

.playing .play {
  opacity: 0;
  pointer-events: none;
}

.pitch,
.card p {
  max-width: 560px;
  margin: 0 auto;
  text-align: center;
}

.pitch {
  margin: 56px auto;
}

.card {
  margin: 96px 0;
  text-align: center;
}

.card h2 {
  margin-bottom: 16px;
}

/* A card's picture is a step smaller than the teaser above it. */
.shot {
  max-width: 800px;
  margin: 36px auto 0;
}

.shot img,
.shot video {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  background: #000;
}


@media (max-width: 1023.98px) {
  .card {
    margin: 64px 0;
  }

  .shot {
    margin-top: 24px;
  }
}

/* A card arrives the way a place's name does: a straight fade, line by line. */
.js .card > * {
  opacity: 0;
  transition: opacity 0.6s linear;
}

.js .card.shown > * {
  opacity: 1;
}

.js .card.shown > :nth-child(2) {
  transition-delay: 0.15s;
}

.js .card.shown > :nth-child(3) {
  transition-delay: 0.3s;
}

.end {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  margin: 120px 0 48px;
  text-align: center;
}

.end img {
  width: 320px;
}

footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 32px 24px 48px;
  text-align: center;
}

.credit a {
  text-decoration: underline;
  text-decoration-color: var(--rail-flat);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

/* The game's own CRT filter switch, so that the glass can come off. */
.switch {
  padding: 0;
  border: 0;
  font: inherit;
  background: none;
  cursor: pointer;
}

.switch span {
  --ink: var(--chrome);
}

.switch:hover span {
  --ink: var(--pick);
}

html:not(.js) .switch {
  display: none;
}

html.flat body::after {
  display: none;
}

/* ── the press kit, and the 404 page with it ─────────────────────────────── */

.press {
  padding-top: calc(var(--bar) + 24px);
}

.press h1 {
  text-align: center;
  margin: 24px 0 16px;
}

.press h2 {
  text-align: center;
  margin: 80px 0 24px;
}

.press > p,
.press .lead {
  max-width: 560px;
  margin: 0 auto;
  text-align: center;
}

.press .lead {
  margin-top: 12px;
}

.press .center {
  margin-top: 24px;
  text-align: center;
}

/* The gun card's panel: a darker fill, a header strip, and an inset rule. */
.panel {
  max-width: 480px;
  margin: 0 auto;
  background-color: var(--panel-flat);
  background-image: var(--panel);
  background-size: var(--s) var(--s);
}

.panel header {
  padding: 8px 12px;
  background-color: var(--strip-flat);
  background-image: var(--strip);
  background-size: var(--s) var(--s);
}

.panel header p:first-child {
  --ink: var(--linen);
}

.panel header p + p {
  --ink: var(--chrome);
}

.rule {
  height: 1px;
  margin: 0 12px;
  background-color: var(--rail-flat);
}

.panel dl {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0 16px;
  margin: 0;
  padding: 8px 12px 12px;
}

.panel dt {
  --ink: var(--chrome);
}

.panel dd {
  margin: 0;
  text-align: right;
}

.list {
  max-width: 560px;
  margin: 0 auto;
  padding: 0;
  list-style: none;
  text-align: center;
}

.list li + li {
  margin-top: 40px;
}

/* A feature's title is a small headline, Alagard in capitals as on the home
   page, at a whole pixel of its own. */
.list strong {
  --s: 1px;
  --linen: var(--linen-s1);
  --ink: var(--linen);
  display: block;
  margin-bottom: 8px;
  font: 16px/20px "Alagard", serif;
  text-transform: uppercase;
}

@media (min-width: 1024px) {
  .list strong {
    font-size: 32px;
    line-height: 36px;
  }
}

.grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 36px;
  padding: 12px;
}

.grid figure {
  margin: 0;
}

.grid img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: contain;
  background: #000;
}

.grid figcaption {
  padding-top: 16px;
  text-align: center;
}

@media (max-width: 640px) {
  .grid {
    grid-template-columns: 1fr;
  }
}

.lost {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  min-height: 80svh;
  text-align: center;
}

/* The bigger reading text keeps about sixty characters to a line. */
@media (min-width: 1024px) and (min-resolution: 2dppx), (min-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) {
  .pitch,
  .card p,
  .press > p:not(.fine),
  .list {
    max-width: 720px;
  }
}

/* ── the title's arrival, and the fade between pages ─────────────────────── */

@media (prefers-reduced-motion: no-preference) {
  .room {
    animation: arrive 1.2s linear both;
  }

  .room h1 {
    animation: arrive 0.6s linear 0.4s both;
  }

  .title > :not(.room) {
    animation: arrive 0.5s linear 0.9s both;
  }

  /* What shows under the title on the first screen arrives after it. */
  .title + main > .screen {
    animation: arrive 0.5s linear 1.1s both;
  }

  .title + main > .pitch {
    animation: arrive 0.5s linear 1.3s both;
  }

  @view-transition {
    navigation: auto;
  }

  ::view-transition-old(root) {
    animation: 0.2s linear both leave;
  }

  ::view-transition-new(root) {
    animation: 0.4s linear 0.15s both arrive;
  }
}

@keyframes arrive {
  from {
    opacity: 0;
  }
}

@keyframes leave {
  to {
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .js .card > * {
    opacity: 1;
    transition: none;
  }

  .chev,
  .down {
    animation: none !important;
  }
}

/* Forced colors draw no backgrounds, so the text keeps a color of its own. */
@media (forced-colors: active) {
  h1,
  h2,
  p,
  li,
  dt,
  dd,
  figcaption,
  strong,
  a,
  span {
    color: CanvasText;
    -webkit-text-fill-color: CanvasText;
  }

  a,
  a span {
    color: LinkText;
    -webkit-text-fill-color: LinkText;
  }
}
