/* ===== base ============================================================== */

*,
*::before,
*::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--black);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 900;
  font-stretch: 115%;
  line-height: 0.92;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

p { margin: 0; }

a {
  color: inherit;
  text-decoration-color: var(--pea);
  text-underline-offset: 0.25em;
}

:focus-visible {
  outline: 2px solid var(--pea);
  outline-offset: 3px;
}

canvas, img { display: block; }

.shell {
  width: min(100% - (var(--gutter) * 2), var(--maxw));
  margin-inline: auto;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  white-space: nowrap;
  clip-path: inset(50%);
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  margin: 0;
  font-size: var(--text-micro);
  font-weight: 700;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--text-dim);
}

.eyebrow::before {
  content: '';
  width: 1.75rem;
  height: 1px;
  background: var(--pea);
}

.lede {
  max-width: 46ch;
  font-size: var(--text-lead);
  color: var(--text-dim);
  text-wrap: pretty;
}

.noscript-note {
  margin-block: 1.5rem;
  padding: 1rem 1.25rem;
  border: 1px solid var(--hair-loud);
  background: var(--surface);
  font-size: var(--text-small);
  color: var(--text-dim);
}

/* Values that genuinely are not decided yet — never invented numbers. */
.tba {
  /* Tabular figures and a fixed-width glyph set keep the line from twitching
     while the reel spins through it. */
  font-variant-numeric: tabular-nums;
  font-family: var(--font-body);
  font-size: var(--text-small);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
}

/* ===== top bar =========================================================== */

.topbar {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.85rem var(--gutter);
  border-bottom: 1px solid var(--hair);
  background: var(--black);
}

.topbar__logo { width: clamp(118px, 13vw, 154px); }

.topbar__side {
  display: flex;
  align-items: center;
  gap: clamp(0.75rem, 2vw, 1.5rem);
}

.status {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-size: var(--text-micro);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.status__dot {
  width: 6px;
  height: 6px;
  background: var(--pea);
}

/* Only one of the two labels is ever shown; which one depends on the clock. */
.status__night { display: none; }

.is-curfew .status__day { display: none; }
.is-curfew .status__night { display: inline; }

/* ===== hero ============================================================== */

.hero {
  position: relative;
  overflow: hidden;
  padding-block: clamp(3rem, 2rem + 6vw, 7rem) var(--space-band);
  isolation: isolate;
}

/* The retirement home behind the title: grey, barely lit, cropped by the
   section. It is already greyscale from the build, so only the brightness is
   left to the browser. Deliberately almost invisible — it should register as a
   place, not as a picture. */
.hero__scene {
  position: absolute;
  inset: 0;
  z-index: -2;
  overflow: hidden;
  pointer-events: none;
}

.hero__scene .vault {
  position: absolute;
  inset: 0;
  aspect-ratio: auto;
}

.hero__scene .vault__canvas {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 32%;
  /* Dark enough that body text clears 4.5:1 anywhere over it. A darkening
     panel behind the copy would be the obvious alternative, but a gradient
     clipped to a box shows its edges — this has none. */
  filter: brightness(0.22) contrast(1.12);
}

/* Sinks the building into the black before the tape, and pulls the left and
   right ends in so it never stops at a seam. */
.hero::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    linear-gradient(180deg, transparent 34%, var(--black) 94%),
    linear-gradient(90deg, var(--black) 0%, transparent 20%, transparent 80%, var(--black) 100%);
}

.hero__grid {
  display: grid;
  gap: clamp(2.5rem, 1rem + 5vw, 4.5rem);
  align-items: end;
}

@media (min-width: 60rem) {
  .hero__grid { grid-template-columns: minmax(0, 1fr) auto; }
}

.hero__title {
  margin-block: 1.2rem 1.75rem;
  font-size: var(--text-hero);
  text-transform: uppercase;
  letter-spacing: -0.035em;
}

.hero__title span { display: block; }

/* Outlined second line — scale contrast alone is not hierarchy. */
.hero__title span:last-child {
  padding-left: 0.06em;
  color: transparent;
  -webkit-text-stroke: clamp(1px, 0.18vw, 2.5px) var(--acid);
}

.hero__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1.75rem;
  margin-top: 2.25rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--hair);
  font-size: var(--text-small);
  /* One step brighter than the usual faint label: these sit over the building,
     where --text-faint measured 3.4:1 against the lit parts of the facade. */
  color: var(--text-dim);
}

.hero__meta b {
  color: var(--text);
  font-weight: 500;
}

/* The key art stands on its own — a frame around it read as a picture pinned
   to the page rather than as the game's own branding. */
.hero__art {
  width: clamp(200px, 26vw, 326px);
  justify-self: center;
}

/* Flat placeholder while the canvas is reassembled — no shimmer, no gradient. */
.hero__art[data-vault-state='pending']::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--surface-2);
}

/* ===== tape ============================================================== */

/* `clip` and not `hidden`: hiding one axis forces the other to become a scroll
   container, and the rotated tape has to stay visible above and below its own
   box. `clip` holds the sides without that side effect. */
.tape-clip {
  overflow-x: clip;
}

.tape {
  position: relative;
  z-index: 3;
  display: flex;
  overflow: hidden;
  padding-block: 0.7rem;
  rotate: -1.1deg;
  background: var(--acid);
  color: #0e1a04;
  /* Wider than the wrapper, so the rotation can never pull the slanted ends
     into view and leave a bare triangle at the left and right edges. */
  width: 104%;
  margin-left: -2%;
}

.tape__run {
  display: flex;
  flex: none;
  gap: 2.5rem;
  padding-right: 2.5rem;
  font-family: var(--font-display);
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  white-space: nowrap;
  animation: marquee 34s linear infinite;
}

@keyframes marquee { to { transform: translateX(-100%); } }

@media (prefers-reduced-motion: reduce) {
  .tape__run { animation-play-state: paused; }
}

/* ===== the punks ========================================================= */

/* The characters live in the background of this band: oversized, cropped by
   the viewport edge, and shaded down until they read as atmosphere rather
   than as pictures pasted on top. */
.cast {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding-block: clamp(5rem, 3rem + 9vw, 10rem);
  border-bottom: 1px solid var(--hair);
}

.cast__art {
  position: absolute;
  inset: 0;
  z-index: -2;
  pointer-events: none;
}

/* The corridor, far back behind the two figures. Same treatment as the hero:
   grey from the build, darkened until the copy on top clears its contrast. */
.cast__scene {
  position: absolute;
  inset: 0;
  z-index: -3;
  overflow: hidden;
  pointer-events: none;
}

.cast__scene .vault {
  position: absolute;
  inset: 0;
  aspect-ratio: auto;
}

.cast__scene .vault__canvas {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 42%;
  filter: brightness(0.22) contrast(1.12);
  transition: filter 1.2s var(--ease-out);
}

/* After ten the corridor warms up, as if the lamps had taken over from the
   skylight. The grey is baked into the file, so sepia is what turns it back
   towards lamplight. */
.is-curfew .cast__scene .vault__canvas {
  /* Same brightness as by day: the warmth comes from the sepia, and lifting
     it as well cost the body text a third of its contrast margin. */
  filter: brightness(0.22) contrast(1.12) sepia(0.45);
}

/* The rule that predicted it gets its moment. */
.is-curfew .rules__item--curfew .rules__tag {
  color: var(--text);
  font-weight: 700;
}

.cast__scene::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    var(--black) 0%,
    transparent 20%,
    transparent 80%,
    var(--black) 100%
  );
}

/* Scoped to the container so these win over the shared `.vault` base rules,
   which come later in this file and would otherwise reset the positioning. */
.cast__art .cast__fig {
  position: absolute;
  width: clamp(150px, 23vw, var(--fig-max, 257px));
  /* Shading, two ways: darkened so the copy stays the brightest thing on
     screen, and feathered on every side so the symbol's own rectangular card
     never ends on a hard edge. */
  filter: brightness(0.4) contrast(1.06) saturate(0.8);
  /* Both radii stay under 50% so the fade finishes inside the box. Any larger
     and the gradient is still opaque at the edge, which puts the symbol's own
     rectangular card back on screen. */
  -webkit-mask-image: radial-gradient(ellipse 48% 49% at 50% 45%, #000 30%, transparent 100%);
  mask-image: radial-gradient(ellipse 48% 49% at 50% 45%, #000 30%, transparent 100%);
}

.cast__art .cast__fig--left {
  left: -4%;
  bottom: -10%;
}

.cast__art .cast__fig--right {
  --fig-max: 238px;
  right: -4%;
  top: -10%;
}

/* Keeps them from reading as two stickers: a slow sway on the figure and a
   slower breath on the canvas inside it. Two elements, so the two motions can
   run on separate transforms instead of fighting over one. Both animate only
   `transform`, which the compositor handles without repainting the masked,
   filtered artwork underneath.

   The pairs are deliberately mismatched in length and offset — matching them
   would make the two punks sway in lockstep, which reads as mechanical. */
@media (prefers-reduced-motion: no-preference) {
  .cast__art .cast__fig--left {
    animation: punk-sway-left 13s ease-in-out infinite;
  }

  .cast__art .cast__fig--right {
    animation: punk-sway-right 17s ease-in-out -4s infinite;
  }

  .cast__art .cast__fig--left .vault__canvas {
    animation: punk-breath 6.5s ease-in-out infinite;
  }

  .cast__art .cast__fig--right .vault__canvas {
    animation: punk-breath 8.2s ease-in-out -2.5s infinite;
  }
}

@keyframes punk-sway-left {
  0%, 100% { transform: translate3d(0, 0, 0) rotate(0deg); }
  50% { transform: translate3d(1.1%, -2.4%, 0) rotate(-1.2deg); }
}

@keyframes punk-sway-right {
  0%, 100% { transform: translate3d(0, 0, 0) rotate(0deg); }
  50% { transform: translate3d(-1.3%, 2.1%, 0) rotate(1.4deg); }
}

@keyframes punk-breath {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.022); }
}

/* Blends the whole band into the black above and below it. */
.cast::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    var(--black) 0%,
    transparent 24%,
    transparent 76%,
    var(--black) 100%
  );
}

.cast__copy {
  display: grid;
  gap: 1rem;
  justify-items: start;
}

/* On a phone the band is narrow — push the figures further off the edge and
   darker, so they never compete with the copy. */
@media (max-width: 46rem) {
  .cast__art .cast__fig {
    width: 52vw;
    filter: brightness(0.26) contrast(1.06) saturate(0.8);
  }

  .cast__art .cast__fig--left { left: -18%; }
  .cast__art .cast__fig--right { right: -18%; }
}

/* ===== volatility ======================================================== */

/* The folder is scenery, not a picture on the page: oversized, pushed past the
   edge, shaded down behind the copy. */
.volatility {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding-block: var(--space-band);
  border-bottom: 1px solid var(--hair);
}

.volatility__art {
  position: absolute;
  inset: 0;
  z-index: -2;
  pointer-events: none;
}

.volatility__art .volatility__folder {
  position: absolute;
  right: -6%;
  top: 50%;
  translate: 0 -50%;
  width: clamp(190px, 28vw, 326px);
  rotate: 4deg;
  filter: brightness(0.3) contrast(1.05) saturate(0.75);
  /* Both radii stay under 50% so the fade finishes inside the box. Any larger
     and the gradient is still opaque at the edge, which puts a hard rectangle
     back on screen. */
  -webkit-mask-image: radial-gradient(ellipse 49% 49% at 50% 50%, #000 32%, transparent 100%);
  mask-image: radial-gradient(ellipse 49% 49% at 50% 50%, #000 32%, transparent 100%);
}

/* Blends the band into the black above and below it. */
.volatility::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    var(--black) 0%,
    transparent 26%,
    transparent 74%,
    var(--black) 100%
  );
}

.volatility__copy {
  display: grid;
  gap: 1rem;
  justify-items: start;
}

.meter {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  align-items: center;
  gap: clamp(0.3rem, 1.2vw, 0.9rem);
  max-width: 34rem;
  margin: 0.75rem 0 0;
  padding: 0;
  list-style: none;
}

.meter__step {
  display: grid;
  place-items: center;
  padding: clamp(0.3rem, 1vw, 0.7rem);
  border: 1px solid var(--hair);
  background: var(--surface);
}

/* The two frames sit side by side on a strip twice as wide as the window;
   the animation slides the strip. Stacking them and cross-fading opacity does
   not work here: the open frame has a see-through gap where the mouth is, so
   the closed frame's lower teeth showed through it. With a strip, only one
   frame can ever be in view. */
.meter__frames {
  position: relative;
  display: block;
  overflow: hidden;
  width: 100%;
  aspect-ratio: 154 / 135;
}

.meter__reel {
  display: flex;
  width: 200%;
  height: 100%;
}

.meter__reel .vault {
  flex: 0 0 50%;
  height: 100%;
  aspect-ratio: auto;
}

/* Hard cuts, no easing: a chattering toy snaps, it does not glide. Three
   snaps up front, then a pause — a constant rattle would be exhausting to sit
   next to while reading. */
@keyframes chatter {
  0%, 5.9% { transform: translateX(0); }
  6%, 11.9% { transform: translateX(-50%); }
  12%, 17.9% { transform: translateX(0); }
  18%, 23.9% { transform: translateX(-50%); }
  24%, 29.9% { transform: translateX(0); }
  30%, 35.9% { transform: translateX(-50%); }
  36%, 100% { transform: translateX(0); }
}

@media (prefers-reduced-motion: no-preference) {
  /* Only the reached steps chatter. The one the rating does not reach stays
     shut, which says "not this far" without relying on the colour alone. */
  .meter__step:not(.meter__step--off) .meter__reel {
    animation: chatter 1.7s linear infinite;
  }

  .meter__step:nth-child(2) .meter__reel { animation-delay: -0.2s; }
  .meter__step:nth-child(3) .meter__reel { animation-delay: -0.4s; }
  .meter__step:nth-child(4) .meter__reel { animation-delay: -0.6s; }
}

/* The step the rating does not reach: no colour, dashed frame. */
.meter__step--off {
  border-style: dashed;
  background: transparent;
}

.meter__step--off .vault__canvas {
  filter: grayscale(1) brightness(0.62) contrast(0.95);
}

.volatility__read {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 0.75rem;
  margin-top: 0.5rem;
  font-size: var(--text-micro);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.volatility__read b {
  color: var(--pea);
  font-weight: 700;
}

@media (max-width: 46rem) {
  .volatility__art .volatility__folder {
    width: 54vw;
    right: -22%;
    filter: brightness(0.24) contrast(1.05) saturate(0.75);
  }
}

/* ===== sections ========================================================== */

.section { padding-block: var(--space-section); }

.section--tight { padding-block: var(--space-band); }

.section__head {
  display: grid;
  gap: 1rem;
  max-width: 62rem;
  margin-bottom: clamp(2.5rem, 1.5rem + 3vw, 4rem);
}

.section__title { font-size: var(--text-h2); }

/* ===== house rules ======================================================= */

.rules__board {
  display: grid;
  gap: 1px;
  margin: 0;
  padding: 0;
  border: 1px solid var(--hair);
  background: var(--hair);
  list-style: none;
}

.rules__item {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem 1rem;
  padding: clamp(0.8rem, 0.6rem + 0.7vw, 1.2rem) clamp(0.9rem, 0.7rem + 1vw, 1.75rem);
  background: var(--surface);
  transition: background var(--normal) var(--ease-out);
}

.rules__item:hover { background: var(--surface-2); }

/* Fixed tracks from tablet up. A plain flex row leaves the comment ragged
   against the right edge with a hole in the middle; with tracks every comment
   starts at the same x and the board reads as the sign it came from. */
@media (min-width: 46rem) {
  .rules__item {
    display: grid;
    grid-template-columns: auto minmax(0, 20rem) 1fr;
    gap: 0 clamp(1.25rem, 3vw, 3.5rem);
    align-items: baseline;
  }
}

/* The sign on the wall has tick boxes, so this one does too. */
.rules__box {
  position: relative;
  flex: none;
  width: 0.95em;
  height: 0.95em;
  align-self: center;
  border: 1px solid var(--hair-loud);
}

.rules__box::before,
.rules__box::after {
  content: '';
  position: absolute;
  left: -12%;
  right: -12%;
  top: 50%;
  border-top: 2px solid var(--acid);
  transform: scaleX(0);
  transition: transform 240ms var(--ease-out);
}

.rules__box::before { rotate: 41deg; }
.rules__box::after { rotate: -41deg; }

/* `<s>` carries the meaning on its own; the drawn line is the performance.
   justify-self keeps it at its own width so the strike does not run on into
   the empty part of the track. */
.rules__text {
  position: relative;
  justify-self: start;
  font-family: var(--font-display);
  font-size: clamp(1.05rem, 0.9rem + 0.8vw, 1.45rem);
  font-weight: 900;
  font-stretch: 112%;
  text-decoration: none;
  color: var(--text);
}

/* Scoped to the rule itself: a line drawn across the whole row would cross the
   sprayed comment, and both are the same green. */
.rules__text::after {
  content: '';
  position: absolute;
  left: -0.2em;
  right: -0.2em;
  top: 54%;
  height: 3px;
  background: var(--acid);
  rotate: -0.9deg;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 520ms var(--ease-out);
}

.rules__tag {
  font-size: var(--text-small);
  letter-spacing: 0.04em;
  color: var(--acid);
  rotate: -1.4deg;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 320ms var(--ease-out), transform 320ms var(--ease-out);
}

/* Staggered so the board reads as being worked down, not wiped in one go. */
/* Written out: `:is()` does not take pseudo-elements, and a selector list with
   one invalid entry is dropped whole — the strikes simply never appeared. */
.rules__board.is-in .rules__box::before,
.rules__board.is-in .rules__box::after,
.rules__board.is-in .rules__text::after {
  transform: scaleX(1);
}

.rules__board.is-in .rules__tag {
  opacity: 1;
  transform: none;
}

.rules__item:nth-child(1) .rules__box::before,
.rules__item:nth-child(1) .rules__box::after,
.rules__item:nth-child(1) .rules__text::after { transition-delay: 0ms; }
.rules__item:nth-child(1) .rules__tag { transition-delay: 150ms; }
.rules__item:nth-child(2) .rules__box::before,
.rules__item:nth-child(2) .rules__box::after,
.rules__item:nth-child(2) .rules__text::after { transition-delay: 110ms; }
.rules__item:nth-child(2) .rules__tag { transition-delay: 260ms; }
.rules__item:nth-child(3) .rules__box::before,
.rules__item:nth-child(3) .rules__box::after,
.rules__item:nth-child(3) .rules__text::after { transition-delay: 220ms; }
.rules__item:nth-child(3) .rules__tag { transition-delay: 370ms; }
.rules__item:nth-child(4) .rules__box::before,
.rules__item:nth-child(4) .rules__box::after,
.rules__item:nth-child(4) .rules__text::after { transition-delay: 330ms; }
.rules__item:nth-child(4) .rules__tag { transition-delay: 480ms; }
.rules__item:nth-child(5) .rules__box::before,
.rules__item:nth-child(5) .rules__box::after,
.rules__item:nth-child(5) .rules__text::after { transition-delay: 440ms; }
.rules__item:nth-child(5) .rules__tag { transition-delay: 590ms; }
.rules__item:nth-child(6) .rules__box::before,
.rules__item:nth-child(6) .rules__box::after,
.rules__item:nth-child(6) .rules__text::after { transition-delay: 550ms; }
.rules__item:nth-child(6) .rules__tag { transition-delay: 700ms; }
.rules__item:nth-child(7) .rules__box::before,
.rules__item:nth-child(7) .rules__box::after,
.rules__item:nth-child(7) .rules__text::after { transition-delay: 660ms; }
.rules__item:nth-child(7) .rules__tag { transition-delay: 810ms; }

/* The meaning must survive without the animation, so skip straight to struck. */
@media (prefers-reduced-motion: reduce) {
  .rules__box::before,
  .rules__box::after,
  .rules__text::after {
    transition: none;
    transform: scaleX(1);
  }

  .rules__tag {
    transition: none;
    transform: none;
    opacity: 1;
  }
}

/* ===== facts ============================================================= */

/* A datasheet, not a grid of boxes: label column, value column, hairline
   between rows. The values carry the weight; the labels stay out of the way. */
.specs {
  margin: 0;
  border-top: 1px solid var(--hair);
}

.specs__row {
  display: grid;
  gap: 0.3rem 2rem;
  align-items: baseline;
  padding: clamp(0.9rem, 0.6rem + 0.8vw, 1.4rem) clamp(0.5rem, 1vw, 1rem);
  border-bottom: 1px solid var(--hair);
  transition: background var(--normal) var(--ease-out);
}

@media (min-width: 40rem) {
  .specs__row { grid-template-columns: minmax(7rem, 13rem) 1fr; }
}

.specs__row:hover { background: var(--surface); }

.specs__key {
  font-size: var(--text-micro);
  font-weight: 700;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--text-faint);
}

.specs__val {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.35rem, 1rem + 1.5vw, 2.1rem);
  font-weight: 900;
  font-stretch: 115%;
  line-height: 1;
  letter-spacing: -0.015em;
}

.specs__val--live { color: var(--pea); }

.specs__note {
  max-width: 58ch;
  margin-top: clamp(1.25rem, 1rem + 1vw, 2rem);
  font-size: var(--text-small);
  color: var(--text-dim);
}

/* ===== vault canvas ====================================================== */

.vault {
  position: relative;
  display: block;
  margin: 0;
  aspect-ratio: var(--vault-ratio, 1);
}

.vault__canvas {
  width: 100%;
  height: 100%;
  user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
  pointer-events: none;
}

/* ===== 404 =============================================================== */

/* The 404 has no artwork to decode, so it carries its own wordmark instead of
   pulling the logo canvas and its decoder onto a dead end. */
.topbar__logo--plain { width: auto; }

.topbar__wordmark {
  font-family: var(--font-display);
  font-size: var(--text-small);
  font-weight: 900;
  font-stretch: 112%;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text);
}

.lost {
  display: flex;
  flex-direction: column;
  min-height: 100svh;
}

.lost__main {
  display: grid;
  gap: 1.25rem;
  justify-items: start;
  align-content: center;
  flex: 1;
  padding-block: var(--space-section);
}

.lost__title {
  margin: 0;
  font-family: var(--font-display);
  /* Smaller than the hero scale: this one has no second column holding it in,
     and the longest word in either language has to fit on one line. */
  font-size: clamp(2.6rem, 0.6rem + 9vw, 7rem);
  font-weight: 900;
  font-stretch: 115%;
  line-height: 0.92;
  letter-spacing: -0.035em;
  text-transform: uppercase;
}

.lost__title span { display: block; }

.lost__title span:last-child {
  padding-left: 0.06em;
  color: transparent;
  -webkit-text-stroke: clamp(1px, 0.18vw, 2.5px) var(--acid);
}

/* ===== cta + footer ====================================================== */

.cta {
  display: grid;
  gap: 1.75rem;
  padding: clamp(2rem, 1rem + 5vw, 4.5rem);
  border: 1px solid var(--hair);
  background: var(--surface);
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  justify-self: start;
  padding: 0.95rem 1.6rem;
  border: 1px solid var(--pea);
  background: transparent;
  color: var(--pea);
  font-family: var(--font-display);
  font-size: 0.8rem;
  font-weight: 800;
  font-stretch: 112%;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--fast) var(--ease-out), color var(--fast) var(--ease-out);
}

.btn:is(:hover, :focus-visible) {
  background: var(--pea);
  color: var(--black);
}

/* ===== signup ============================================================ */

.signup {
  display: grid;
  gap: 1rem;
  width: 100%;
  max-width: 34rem;
}

.signup__line {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
}

.signup__field {
  flex: 1 1 15rem;
  display: block;
}

.signup__input {
  width: 100%;
  padding: 0.95rem 1.1rem;
  border: 1px solid var(--hair-loud);
  background: var(--black);
  color: var(--text);
  font: inherit;
  font-size: var(--text-body);
  transition: border-color var(--fast) var(--ease-out);
}

.signup__input::placeholder { color: var(--text-faint); }

.signup__input:is(:hover, :focus-visible) { border-color: var(--pea); }

/* The invalid style would otherwise fire while the field is still empty. */
.signup__input:not(:placeholder-shown):invalid { border-color: #ff5c5c; }

.signup__consent {
  display: flex;
  align-items: start;
  gap: 0.6rem;
  font-size: var(--text-small);
  line-height: 1.5;
  color: var(--text-dim);
  cursor: pointer;
}

.signup__consent input {
  flex: none;
  width: 1.05rem;
  height: 1.05rem;
  margin-top: 0.18rem;
  accent-color: var(--pea);
}

.signup__status {
  margin: 0;
  min-height: 1.4em;
  font-size: var(--text-small);
  color: var(--text-dim);
}

.signup__status[data-state='done'] { color: var(--pea); }
.signup__status[data-state='failed'] { color: #ff8a8a; }

.site-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding-block: 2.5rem 3.5rem;
  border-top: 1px solid var(--hair);
  font-size: var(--text-small);
  color: var(--text-faint);
}

/* ===== scroll reveal ===================================================== */

.js-ready .rise {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity var(--slow) var(--ease-out), transform var(--slow) var(--ease-out);
}

.js-ready .rise.is-in {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .js-ready .rise { opacity: 1; transform: none; }
}
