/* components.css — shared components. No level-specific colours here;
   everything reads from the theme-surface variables. */

/* Texture layers -------------------------------------------------------- */

.texture {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: 0.5;
}

.texture--grain::after,
.texture--scanline::after,
.texture--starfield::after {
  content: "";
  position: absolute;
  inset: 0;
}

.texture--scanline::after {
  background: repeating-linear-gradient(
    to bottom,
    color-mix(in srgb, var(--ink) 5%, transparent) 0 1px,
    transparent 1px 3px
  );
  opacity: 0.6;
}

.texture--grain::after {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3'/></filter><rect width='120' height='120' filter='url(%23n)' opacity='0.35'/></svg>");
  opacity: 0.28;
  mix-blend-mode: overlay;
}

.texture--starfield::after {
  background-image:
    radial-gradient(1px 1px at 20% 30%, var(--ink) 50%, transparent 50%),
    radial-gradient(1px 1px at 70% 12%, var(--ink) 50%, transparent 50%),
    radial-gradient(1.5px 1.5px at 44% 71%, var(--ink) 50%, transparent 50%),
    radial-gradient(1px 1px at 88% 55%, var(--ink) 50%, transparent 50%),
    radial-gradient(1px 1px at 12% 82%, var(--ink) 50%, transparent 50%),
    radial-gradient(1.5px 1.5px at 62% 40%, var(--ink) 50%, transparent 50%);
  background-size: 340px 340px;
  opacity: 0.5;
}

.site-main {
  position: relative;
  z-index: 1;
}

/* Hero ------------------------------------------------------------------ */

.hero {
  min-height: 100svh;
  display: grid;
  grid-template-rows: auto 1fr auto;
  padding-block: var(--space-6) var(--space-7);
  position: relative;
}

/* .hero is a .page, so it carries the page max-width and the art was being
   cut off at the gutters with a hard vertical edge on each side. Break the
   art out to the full viewport. left/margin rather than a transform, so no
   containing block is created for anything inside. */
.hero__art {
  position: absolute;
  inset: 0 auto 0 50%;
  width: 100vw;
  margin-left: -50vw;
  z-index: -1;
  display: grid;
  place-items: center;
  overflow: hidden;
}

.hero__art svg,
.hero__art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Every hero art ends in a hard horizontal cut where the section does, and
   the corner metadata sits right on that edge. This dissolves the bottom of
   the art into the page so there is no seam, and gives the metadata a dark
   base to read against whatever the art is doing there. */
.hero__art::before {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 34%;
  z-index: 2;
  background: linear-gradient(
    180deg,
    transparent 0%,
    color-mix(in srgb, var(--field) 55%, transparent) 46%,
    var(--field) 92%
  );
  pointer-events: none;
}

/* Hero art can be busy right where the metadata sits, so the text layer
   always wins. */
.hero__eyebrow,
.hero__foot,
.hero__title {
  position: relative;
  z-index: 2;
}

.hero__eyebrow {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ink);
}

.hero__eyebrow::before,
.hero__eyebrow::after {
  content: "";
  flex: 1;
  height: var(--rule);
  background: var(--rule-colour);
}

.hero__title {
  font-size: var(--display-hero);
  line-height: 0.82;
  text-align: center;
  letter-spacing: -0.045em;
  align-self: center;
  margin-block: var(--space-5);
}

/* Hero art is dense on every level, so the metadata always needs a halo of
   the page's own field colour to sit on — and muted grey over busy art is
   unreadable no matter how big the halo. Full ink; the small size and wide
   tracking carry the hierarchy instead. */
.hero__eyebrow,
.hero__foot p {
  text-shadow:
    0 0 0.5rem var(--field),
    0 0 1rem var(--field),
    0 0 2rem var(--field),
    0 0 3.4rem var(--field);
}

.hero__foot {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: var(--space-5);
  align-items: end;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
}

.hero__foot p {
  margin: 0;
}

.hero__lede {
  text-align: center;
  max-width: 46ch;
  margin-inline: auto;
  letter-spacing: 0.04em;
  text-transform: none;
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--ink);
}

.hero__meta-r {
  text-align: right;
}

.hero__rule {
  grid-column: 1 / -1;
  height: var(--rule);
  background: var(--rule-colour);
  margin-top: var(--space-5);
}

/* Countdown ------------------------------------------------------------- */

.countdown {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: none;
}

.countdown__entry {
  border-top: var(--rule) solid var(--rule-colour);
}

.countdown__entry:last-child {
  border-bottom: var(--rule) solid var(--rule-colour);
}

.countdown__hit {
  display: grid;
  grid-template-columns: 4.5rem 1.6rem 1fr auto;
  grid-template-areas:
    "rank mark name cta"
    "rank mark by   cta"
    "rank mark tag  cta";
  gap: 0 var(--space-5);
  align-items: baseline;
  padding-block: var(--space-5);
  /* Clears the palette rail, which sits at left:0 and was running straight
     into the rank number. Fixed rather than applied on hover, so the row
     does not shift sideways when you point at it. */
  padding-inline: var(--space-5) 0;
  text-decoration: none;
  color: inherit;
  transition: background var(--dur) var(--ease);
}

a.countdown__hit:hover,
a.countdown__hit:focus-visible {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.countdown__hit--soon {
  opacity: 0.42;
}

/* Grid items default to min-width:auto, so a long name refuses to shrink
   below its content and drags the row past the viewport on a phone. */
.countdown__hit > * {
  min-width: 0;
}

.countdown__name {
  overflow-wrap: anywhere;
}

.countdown__rank {
  grid-area: rank;
  font-family: var(--font-mono);
  font-size: var(--step-2);
  color: var(--accent);
  letter-spacing: -0.02em;
  align-self: start;
}

.countdown__name {
  grid-area: name;
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--step-5);
  letter-spacing: -0.035em;
  line-height: 1;
}

.countdown__by {
  grid-area: by;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  padding-top: var(--space-2);
}

/* A crew too long for one line: "HOST & 21 more", the names on hover/focus.
   Shared by the home list and the level header (render.credit_html). */
.credit {
  position: relative;
  display: inline;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-align: inherit;
  text-shadow: inherit;
  cursor: help;
}

button.credit:focus-visible {
  outline: 1px solid currentColor;
  outline-offset: 0.3em;
}

.credit__lead {
  border-bottom: 1px dotted color-mix(in srgb, currentColor 55%, transparent);
}

.credit__all {
  position: absolute;
  top: calc(100% + 0.6rem);
  left: 0;
  z-index: 40;
  box-sizing: border-box;
  width: max-content;
  max-width: min(34rem, calc(100vw - 2 * var(--gutter, 1rem)));
  padding: 0.85rem 1rem 0.95rem;
  background: var(--field);
  color: var(--ink);
  border: 1px solid color-mix(in srgb, var(--ink) 32%, transparent);
  box-shadow: 0 1.2rem 2.4rem rgba(0, 0, 0, 0.45);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: 400;
  line-height: 1.65;
  letter-spacing: 0.02em;
  text-transform: none;
  text-shadow: none;
  text-align: left;
  white-space: normal;
  overflow-wrap: anywhere;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transform: translateY(0.25rem);
  transition: opacity 0.14s ease, transform 0.14s ease, visibility 0s 0.14s;
}

.credit__head {
  display: block;
  margin-bottom: 0.4rem;
  color: var(--muted);
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

/* Hover keeps it open while the pointer is on the summary; the list itself
   is not hoverable (pointer-events: none), so it can't trap the pointer. */
.credit:hover .credit__all,
.credit:focus-within .credit__all,
.countdown__hit:focus-visible .credit__all {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition-delay: 0s;
}

/* The header line sits at the bottom of the hero, so it opens upward. */
.hero__foot .credit__all {
  top: auto;
  bottom: calc(100% + 0.6rem);
  transform: translateY(-0.25rem);
}

@media (prefers-reduced-motion: reduce) {
  .credit__all { transition: none; transform: none; }
}

.countdown__tag {
  grid-area: tag;
  color: var(--muted);
  font-size: var(--step--1);
  max-width: 54ch;
  padding-top: var(--space-2);
}

.countdown__cta {
  grid-area: cta;
  align-self: center;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent);
  white-space: nowrap;
}

.countdown__hit--soon .countdown__cta {
  color: var(--muted);
}

@media (max-width: 44rem) {
  .countdown__hit {
    padding-inline: var(--space-4) 0;
    grid-template-columns: 3rem 1.4rem 1fr;
    grid-template-areas:
      "rank mark name"
      "rank mark by"
      "tag  tag  tag"
      "cta  cta  cta";
  }
  .countdown__cta {
    padding-top: var(--space-3);
  }
}

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

/* Raw --accent is unreadable against its own field on several levels
   (deep red on deep red). Blending toward ink keeps the accent's character
   while guaranteeing it separates. */
.level-hero__rank {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--accent) 45%, var(--ink));
}

.level-hero__title {
  font-size: var(--display-hero);
}

/* Stat block ------------------------------------------------------------ */

.statblock {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  /* Column gap is load-bearing: without it a cell's label butts straight
     against the previous cell's value and reads as one word. */
  gap: 0 var(--space-7);
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--1);
}

/* Label above value. Values here run from "2.1" to "#13 on the Demonlist
   (22 September 2023)", and no side-by-side row survives that range. */
.statblock__row {
  display: grid;
  gap: var(--space-1);
  align-content: start;
  /* Top rule only. Rows stack with no row gap, so a bottom rule as well
     would double up against the next row's. */
  padding-block: var(--space-4) var(--space-5);
}

/* An accent tick per cell, matching the rule the arc rail uses, so "The
   record" and "The shape of it" read as the same family. */
.statblock__row {
  border-top: 2px solid color-mix(in srgb, var(--accent) 40%, transparent);
  transition: border-color var(--dur) var(--ease);
}

.statblock__row:hover {
  border-top-color: var(--accent);
}

.statblock__key {
  color: var(--muted);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-size: var(--step--2);
  line-height: 1.3;
}

/* The value carries the cell, not the label: these were the same size and
   the block read as an undifferentiated wall of mono. */
.statblock__val {
  margin: 0;
  text-align: left;
  font-size: var(--step-0);
  line-height: 1.4;
  letter-spacing: -0.01em;
  color: var(--ink);
  overflow-wrap: anywhere;
  text-wrap: pretty;
}

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

/* Voice ----------------------------------------------------------------- */

.voice__hook {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--step-4);
  line-height: 1.05;
  letter-spacing: -0.03em;
  max-width: 24ch;
  margin-bottom: var(--space-6);
  color: var(--accent);
}

.voice__body {
  font-size: var(--step-1);
  line-height: 1.65;
}

.voice__drafted {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  border-left: 2px solid var(--rule-colour);
  padding-left: var(--space-4);
  margin-top: var(--space-6);
}

/* Video + player -------------------------------------------------------- */

.videoframe__load {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  width: 100%;
  aspect-ratio: 16 / 9;
  justify-content: center;
  background: color-mix(in srgb, var(--ink) 6%, transparent);
  border: var(--rule) solid var(--rule-colour);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease);
}

.videoframe__load:hover {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}

.videoframe__play {
  font-size: var(--step-3);
  color: var(--accent);
}

.videoframe iframe {
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 0;
}

.videoframe__credit,
.videoframe__note {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--muted);
  letter-spacing: 0.1em;
  margin-top: var(--space-3);
}

/* The song is a credit, not a heading: give the track the display face and
   let the artist sit under it in the same mono as the stat block keys. */
.player__title {
  display: grid;
  gap: var(--space-2);
  margin: 0 0 var(--space-4);
  border-left: 3px solid color-mix(in srgb, var(--accent) 60%, transparent);
  padding-left: var(--space-4);
}

.player__name {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--step-3);
  line-height: 1.02;
  letter-spacing: -0.02em;
  color: var(--ink);
  text-wrap: balance;
}

.player__artist {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}

.player__ng {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
}

/* Rank nav + sources ---------------------------------------------------- */

.ranknav {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: var(--space-5);
  padding-block: var(--space-7);
  border-top: var(--rule) solid var(--rule-colour);
}

.ranknav__link {
  display: grid;
  gap: var(--space-1);
  text-decoration: none;
  /* Grid tracks default to min-width:auto, so a long neighbour name kept the
     1fr column from shrinking and pushed the row past the viewport. */
  min-width: 0;
}

.ranknav__name {
  overflow-wrap: anywhere;
}

/* On a phone the three-across layout has nowhere left to go: stack it. */
@media (max-width: 34rem) {
  .ranknav {
    grid-template-columns: 1fr;
    justify-items: start;
    gap: var(--space-6);
  }

  .ranknav__link--next {
    text-align: left;
  }
}

.ranknav__link--next {
  text-align: right;
}

.ranknav__dir,
.ranknav__rank,
.ranknav__home {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
}

.ranknav__name {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--step-2);
  letter-spacing: -0.02em;
  color: var(--ink);
}

.sources ul {
  list-style: none;
  margin: 0;
  padding: 0;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  display: grid;
  gap: var(--space-2);
}

.sources a {
  color: var(--muted);
  overflow-wrap: anywhere;
}

/* Footer ---------------------------------------------------------------- */

.site-foot {
  border-top: var(--rule) solid var(--rule-colour);
  padding-block: var(--space-7);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.1em;
  color: var(--muted);
  position: relative;
  z-index: 1;
}

/* Roster ---------------------------------------------------------------- */
/* Mega-collab credit lists are too long for the stat grid, which shows a
   count instead; the names live here, in build order with the host first. */

.roster {
  margin-top: var(--space-8);
}

.roster__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--ink);
}

.roster__list li + li::before {
  content: "/";
  color: var(--muted);
  opacity: 0.5;
  margin-right: var(--space-4);
}

.roster__list li:first-child {
  color: var(--accent);
}

/* Arc ------------------------------------------------------------------- */
/* How the level moves, in order. Stops without a stated timing leave the
   marker slot empty rather than guessing one, and the empty slot still
   reserves its row so every label lines up. */

.arc__track {
  list-style: none;
  margin: 0 0 var(--space-6);
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: var(--space-6);
}

.arc__stop {
  display: grid;
  align-content: start;
  gap: var(--space-2);
  padding-top: var(--space-3);
  border-top: 2px solid color-mix(in srgb, var(--accent) 55%, transparent);
}

.arc__at {
  /* An explicit line-height so a marked stop and an empty one build the same
     line box; without it the body's 1.6 makes marked stops taller and the
     labels beneath them fall out of line. */
  line-height: 1.3;
  min-height: 1.3em;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.2em;
  color: var(--accent);
}

.arc__label {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--step-1);
  line-height: 1.05;
}

.arc__note {
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--muted);
}

.arc__caveat {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  /* No opacity here: muted at 0.75 measures 4.18:1 on Deimos's field, under
     the 4.5:1 the rest of the site holds itself to. */
  color: var(--muted);
}

/* Long-form commentary --------------------------------------------------- */

.voice__body--long > p:first-child::first-letter {
  float: left;
  font-family: var(--font-display);
  font-weight: 900;
  font-size: 4.1em;
  line-height: 0.76;
  padding: 0.04em 0.1em 0 0;
  color: var(--accent);
}

.voice__body--long > p + p {
  margin-top: 1.15em;
}

.player__nong {
  margin: var(--space-2) 0 0;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.1em;
  color: var(--muted);
}

/* Snow ------------------------------------------------------------------ */
/* Two levels declare texture "snow" and nothing implemented it, so the page
   behind their content was bare. Stars sit still; two flake layers fall at
   different rates for parallax. One element, three background layers -- no
   DOM node per flake.

   Flakes are drawn at their true pixel size inside a large tile, NOT scaled
   up to fill one: background-size sets the TILE, so a flake that fills its
   own viewBox renders as large as the tile and swamps the page. */

.texture--snow::after,
.texture--snow::before {
  content: "";
  position: absolute;
  inset: -20% 0 -20% 0;
  background-repeat: repeat;
}

.texture--snow::before {
  background-image:
    radial-gradient(1.5px 1.5px at 18% 22%, var(--ink) 50%, transparent 50%),
    radial-gradient(1px 1px at 62% 9%, var(--ink) 50%, transparent 50%),
    radial-gradient(1px 1px at 84% 44%, var(--ink) 50%, transparent 50%),
    radial-gradient(1.7px 1.7px at 35% 68%, var(--ink) 50%, transparent 50%),
    radial-gradient(1px 1px at 9% 87%, var(--ink) 50%, transparent 50%),
    radial-gradient(1.2px 1.2px at 71% 79%, var(--ink) 50%, transparent 50%);
  background-size: 300px 300px;
  opacity: 0.55;
}

.texture--snow::after {
  background-image:
    url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260' viewBox='0 0 260 260'%3E%3Cg stroke='%23ffffff' fill='none' stroke-linecap='round'%3E%3Cg stroke-width='0.9'%3E%3Cline x1='34.00' y1='52.00' x2='34.00' y2='49.00'/%3E%3Cline x1='34.00' y1='50.25' x2='33.12' y2='49.50'/%3E%3Cline x1='34.00' y1='50.25' x2='34.88' y2='49.50'/%3E%3Cline x1='34.00' y1='52.00' x2='36.60' y2='50.50'/%3E%3Cline x1='35.52' y1='51.12' x2='35.73' y2='49.99'/%3E%3Cline x1='35.52' y1='51.12' x2='36.60' y2='51.51'/%3E%3Cline x1='34.00' y1='52.00' x2='36.60' y2='53.50'/%3E%3Cline x1='35.52' y1='52.88' x2='36.60' y2='52.49'/%3E%3Cline x1='35.52' y1='52.88' x2='35.73' y2='54.01'/%3E%3Cline x1='34.00' y1='52.00' x2='34.00' y2='55.00'/%3E%3Cline x1='34.00' y1='53.75' x2='34.88' y2='54.50'/%3E%3Cline x1='34.00' y1='53.75' x2='33.12' y2='54.50'/%3E%3Cline x1='34.00' y1='52.00' x2='31.40' y2='53.50'/%3E%3Cline x1='32.48' y1='52.88' x2='32.27' y2='54.01'/%3E%3Cline x1='32.48' y1='52.88' x2='31.40' y2='52.49'/%3E%3Cline x1='34.00' y1='52.00' x2='31.40' y2='50.50'/%3E%3Cline x1='32.48' y1='51.12' x2='31.40' y2='51.51'/%3E%3Cline x1='32.48' y1='51.12' x2='32.27' y2='49.99'/%3E%3C/g%3E%3Cg stroke-width='0.8'%3E%3Cline x1='150.00' y1='128.00' x2='150.00' y2='125.50'/%3E%3Cline x1='150.00' y1='126.54' x2='149.27' y2='125.92'/%3E%3Cline x1='150.00' y1='126.54' x2='150.73' y2='125.92'/%3E%3Cline x1='150.00' y1='128.00' x2='152.17' y2='126.75'/%3E%3Cline x1='151.26' y1='127.27' x2='151.44' y2='126.33'/%3E%3Cline x1='151.26' y1='127.27' x2='152.17' y2='127.59'/%3E%3Cline x1='150.00' y1='128.00' x2='152.17' y2='129.25'/%3E%3Cline x1='151.26' y1='128.73' x2='152.17' y2='128.41'/%3E%3Cline x1='151.26' y1='128.73' x2='151.44' y2='129.67'/%3E%3Cline x1='150.00' y1='128.00' x2='150.00' y2='130.50'/%3E%3Cline x1='150.00' y1='129.46' x2='150.73' y2='130.08'/%3E%3Cline x1='150.00' y1='129.46' x2='149.27' y2='130.08'/%3E%3Cline x1='150.00' y1='128.00' x2='147.83' y2='129.25'/%3E%3Cline x1='148.74' y1='128.73' x2='148.56' y2='129.67'/%3E%3Cline x1='148.74' y1='128.73' x2='147.83' y2='128.41'/%3E%3Cline x1='150.00' y1='128.00' x2='147.83' y2='126.75'/%3E%3Cline x1='148.74' y1='127.27' x2='147.83' y2='127.59'/%3E%3Cline x1='148.74' y1='127.27' x2='148.56' y2='126.33'/%3E%3C/g%3E%3Cg stroke-width='0.9'%3E%3Cline x1='86.00' y1='206.00' x2='86.00' y2='202.75'/%3E%3Cline x1='86.00' y1='204.10' x2='85.05' y2='203.29'/%3E%3Cline x1='86.00' y1='204.10' x2='86.95' y2='203.29'/%3E%3Cline x1='86.00' y1='206.00' x2='88.81' y2='204.38'/%3E%3Cline x1='87.64' y1='205.05' x2='87.87' y2='203.82'/%3E%3Cline x1='87.64' y1='205.05' x2='88.82' y2='205.47'/%3E%3Cline x1='86.00' y1='206.00' x2='88.81' y2='207.62'/%3E%3Cline x1='87.64' y1='206.95' x2='88.82' y2='206.53'/%3E%3Cline x1='87.64' y1='206.95' x2='87.87' y2='208.18'/%3E%3Cline x1='86.00' y1='206.00' x2='86.00' y2='209.25'/%3E%3Cline x1='86.00' y1='207.90' x2='86.95' y2='208.71'/%3E%3Cline x1='86.00' y1='207.90' x2='85.05' y2='208.71'/%3E%3Cline x1='86.00' y1='206.00' x2='83.19' y2='207.62'/%3E%3Cline x1='84.36' y1='206.95' x2='84.13' y2='208.18'/%3E%3Cline x1='84.36' y1='206.95' x2='83.18' y2='206.53'/%3E%3Cline x1='86.00' y1='206.00' x2='83.19' y2='204.38'/%3E%3Cline x1='84.36' y1='205.05' x2='83.18' y2='205.47'/%3E%3Cline x1='84.36' y1='205.05' x2='84.13' y2='203.82'/%3E%3C/g%3E%3Cg stroke-width='0.8'%3E%3Cline x1='214.00' y1='34.00' x2='214.00' y2='31.50'/%3E%3Cline x1='214.00' y1='32.54' x2='213.27' y2='31.92'/%3E%3Cline x1='214.00' y1='32.54' x2='214.73' y2='31.92'/%3E%3Cline x1='214.00' y1='34.00' x2='216.17' y2='32.75'/%3E%3Cline x1='215.26' y1='33.27' x2='215.44' y2='32.33'/%3E%3Cline x1='215.26' y1='33.27' x2='216.17' y2='33.59'/%3E%3Cline x1='214.00' y1='34.00' x2='216.17' y2='35.25'/%3E%3Cline x1='215.26' y1='34.73' x2='216.17' y2='34.41'/%3E%3Cline x1='215.26' y1='34.73' x2='215.44' y2='35.67'/%3E%3Cline x1='214.00' y1='34.00' x2='214.00' y2='36.50'/%3E%3Cline x1='214.00' y1='35.46' x2='214.73' y2='36.08'/%3E%3Cline x1='214.00' y1='35.46' x2='213.27' y2='36.08'/%3E%3Cline x1='214.00' y1='34.00' x2='211.83' y2='35.25'/%3E%3Cline x1='212.74' y1='34.73' x2='212.56' y2='35.67'/%3E%3Cline x1='212.74' y1='34.73' x2='211.83' y2='34.41'/%3E%3Cline x1='214.00' y1='34.00' x2='211.83' y2='32.75'/%3E%3Cline x1='212.74' y1='33.27' x2='211.83' y2='33.59'/%3E%3Cline x1='212.74' y1='33.27' x2='212.56' y2='32.33'/%3E%3C/g%3E%3Cg stroke-width='0.85'%3E%3Cline x1='196.00' y1='176.00' x2='196.00' y2='173.25'/%3E%3Cline x1='196.00' y1='174.40' x2='195.20' y2='173.71'/%3E%3Cline x1='196.00' y1='174.40' x2='196.80' y2='173.71'/%3E%3Cline x1='196.00' y1='176.00' x2='198.38' y2='174.62'/%3E%3Cline x1='197.39' y1='175.20' x2='197.58' y2='174.16'/%3E%3Cline x1='197.39' y1='175.20' x2='198.39' y2='175.55'/%3E%3Cline x1='196.00' y1='176.00' x2='198.38' y2='177.38'/%3E%3Cline x1='197.39' y1='176.80' x2='198.39' y2='176.45'/%3E%3Cline x1='197.39' y1='176.80' x2='197.58' y2='177.84'/%3E%3Cline x1='196.00' y1='176.00' x2='196.00' y2='178.75'/%3E%3Cline x1='196.00' y1='177.60' x2='196.80' y2='178.29'/%3E%3Cline x1='196.00' y1='177.60' x2='195.20' y2='178.29'/%3E%3Cline x1='196.00' y1='176.00' x2='193.62' y2='177.38'/%3E%3Cline x1='194.61' y1='176.80' x2='194.42' y2='177.84'/%3E%3Cline x1='194.61' y1='176.80' x2='193.61' y2='176.45'/%3E%3Cline x1='196.00' y1='176.00' x2='193.62' y2='174.62'/%3E%3Cline x1='194.61' y1='175.20' x2='193.61' y2='175.55'/%3E%3Cline x1='194.61' y1='175.20' x2='194.42' y2='174.16'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"),
    url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='390' height='390' viewBox='0 0 390 390'%3E%3Cg stroke='%23ffffff' fill='none' stroke-linecap='round'%3E%3Cg stroke-width='0.7'%3E%3Cline x1='70.00' y1='96.00' x2='70.00' y2='93.75'/%3E%3Cline x1='70.00' y1='94.69' x2='69.34' y2='94.12'/%3E%3Cline x1='70.00' y1='94.69' x2='70.66' y2='94.12'/%3E%3Cline x1='70.00' y1='96.00' x2='71.95' y2='94.88'/%3E%3Cline x1='71.14' y1='95.34' x2='71.30' y2='94.49'/%3E%3Cline x1='71.14' y1='95.34' x2='71.95' y2='95.63'/%3E%3Cline x1='70.00' y1='96.00' x2='71.95' y2='97.12'/%3E%3Cline x1='71.14' y1='96.66' x2='71.95' y2='96.37'/%3E%3Cline x1='71.14' y1='96.66' x2='71.30' y2='97.51'/%3E%3Cline x1='70.00' y1='96.00' x2='70.00' y2='98.25'/%3E%3Cline x1='70.00' y1='97.31' x2='70.66' y2='97.88'/%3E%3Cline x1='70.00' y1='97.31' x2='69.34' y2='97.88'/%3E%3Cline x1='70.00' y1='96.00' x2='68.05' y2='97.12'/%3E%3Cline x1='68.86' y1='96.66' x2='68.70' y2='97.51'/%3E%3Cline x1='68.86' y1='96.66' x2='68.05' y2='96.37'/%3E%3Cline x1='70.00' y1='96.00' x2='68.05' y2='94.88'/%3E%3Cline x1='68.86' y1='95.34' x2='68.05' y2='95.63'/%3E%3Cline x1='68.86' y1='95.34' x2='68.70' y2='94.49'/%3E%3C/g%3E%3Cg stroke-width='0.75'%3E%3Cline x1='250.00' y1='44.00' x2='250.00' y2='41.50'/%3E%3Cline x1='250.00' y1='42.54' x2='249.27' y2='41.92'/%3E%3Cline x1='250.00' y1='42.54' x2='250.73' y2='41.92'/%3E%3Cline x1='250.00' y1='44.00' x2='252.17' y2='42.75'/%3E%3Cline x1='251.26' y1='43.27' x2='251.44' y2='42.33'/%3E%3Cline x1='251.26' y1='43.27' x2='252.17' y2='43.59'/%3E%3Cline x1='250.00' y1='44.00' x2='252.17' y2='45.25'/%3E%3Cline x1='251.26' y1='44.73' x2='252.17' y2='44.41'/%3E%3Cline x1='251.26' y1='44.73' x2='251.44' y2='45.67'/%3E%3Cline x1='250.00' y1='44.00' x2='250.00' y2='46.50'/%3E%3Cline x1='250.00' y1='45.46' x2='250.73' y2='46.08'/%3E%3Cline x1='250.00' y1='45.46' x2='249.27' y2='46.08'/%3E%3Cline x1='250.00' y1='44.00' x2='247.83' y2='45.25'/%3E%3Cline x1='248.74' y1='44.73' x2='248.56' y2='45.67'/%3E%3Cline x1='248.74' y1='44.73' x2='247.83' y2='44.41'/%3E%3Cline x1='250.00' y1='44.00' x2='247.83' y2='42.75'/%3E%3Cline x1='248.74' y1='43.27' x2='247.83' y2='43.59'/%3E%3Cline x1='248.74' y1='43.27' x2='248.56' y2='42.33'/%3E%3C/g%3E%3Cg stroke-width='0.7'%3E%3Cline x1='168.00' y1='268.00' x2='168.00' y2='265.75'/%3E%3Cline x1='168.00' y1='266.69' x2='167.34' y2='266.12'/%3E%3Cline x1='168.00' y1='266.69' x2='168.66' y2='266.12'/%3E%3Cline x1='168.00' y1='268.00' x2='169.95' y2='266.88'/%3E%3Cline x1='169.14' y1='267.34' x2='169.30' y2='266.49'/%3E%3Cline x1='169.14' y1='267.34' x2='169.95' y2='267.63'/%3E%3Cline x1='168.00' y1='268.00' x2='169.95' y2='269.12'/%3E%3Cline x1='169.14' y1='268.66' x2='169.95' y2='268.37'/%3E%3Cline x1='169.14' y1='268.66' x2='169.30' y2='269.51'/%3E%3Cline x1='168.00' y1='268.00' x2='168.00' y2='270.25'/%3E%3Cline x1='168.00' y1='269.31' x2='168.66' y2='269.88'/%3E%3Cline x1='168.00' y1='269.31' x2='167.34' y2='269.88'/%3E%3Cline x1='168.00' y1='268.00' x2='166.05' y2='269.12'/%3E%3Cline x1='166.86' y1='268.66' x2='166.70' y2='269.51'/%3E%3Cline x1='166.86' y1='268.66' x2='166.05' y2='268.37'/%3E%3Cline x1='168.00' y1='268.00' x2='166.05' y2='266.88'/%3E%3Cline x1='166.86' y1='267.34' x2='166.05' y2='267.63'/%3E%3Cline x1='166.86' y1='267.34' x2='166.70' y2='266.49'/%3E%3C/g%3E%3Cg stroke-width='0.75'%3E%3Cline x1='330.00' y1='210.00' x2='330.00' y2='207.50'/%3E%3Cline x1='330.00' y1='208.54' x2='329.27' y2='207.92'/%3E%3Cline x1='330.00' y1='208.54' x2='330.73' y2='207.92'/%3E%3Cline x1='330.00' y1='210.00' x2='332.17' y2='208.75'/%3E%3Cline x1='331.26' y1='209.27' x2='331.44' y2='208.33'/%3E%3Cline x1='331.26' y1='209.27' x2='332.17' y2='209.59'/%3E%3Cline x1='330.00' y1='210.00' x2='332.17' y2='211.25'/%3E%3Cline x1='331.26' y1='210.73' x2='332.17' y2='210.41'/%3E%3Cline x1='331.26' y1='210.73' x2='331.44' y2='211.67'/%3E%3Cline x1='330.00' y1='210.00' x2='330.00' y2='212.50'/%3E%3Cline x1='330.00' y1='211.46' x2='330.73' y2='212.08'/%3E%3Cline x1='330.00' y1='211.46' x2='329.27' y2='212.08'/%3E%3Cline x1='330.00' y1='210.00' x2='327.83' y2='211.25'/%3E%3Cline x1='328.74' y1='210.73' x2='328.56' y2='211.67'/%3E%3Cline x1='328.74' y1='210.73' x2='327.83' y2='210.41'/%3E%3Cline x1='330.00' y1='210.00' x2='327.83' y2='208.75'/%3E%3Cline x1='328.74' y1='209.27' x2='327.83' y2='209.59'/%3E%3Cline x1='328.74' y1='209.27' x2='328.56' y2='208.33'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
  background-size: 260px 260px, 390px 390px;
  background-position: 0 0, 130px 60px;
  opacity: 0.42;
  animation: snow-fall 52s linear infinite;
}

@keyframes snow-fall {
  from { background-position: 0 0, 130px 60px; }
  to   { background-position: 0 260px, 130px 450px; }
}

/* Ember ----------------------------------------------------------------- */
/* Declared by four levels and, like snow was, never implemented. Motes rise
   instead of falling, and the drift is slower than the snow so it reads as
   heat rather than weather. */

.texture--ember::after,
.texture--ember::before {
  content: "";
  position: absolute;
  inset: -25% 0 -25% 0;
  background-repeat: repeat;
}

.texture--ember::before {
  background-image:
    radial-gradient(2px 2px at 16% 24%, var(--accent) 50%, transparent 50%),
    radial-gradient(1.2px 1.2px at 58% 11%, var(--accent2) 50%, transparent 50%),
    radial-gradient(1.5px 1.5px at 86% 47%, var(--accent) 50%, transparent 50%),
    radial-gradient(1px 1px at 31% 66%, var(--accent2) 50%, transparent 50%),
    radial-gradient(2.2px 2.2px at 8% 85%, var(--accent) 50%, transparent 50%),
    radial-gradient(1.3px 1.3px at 74% 81%, var(--accent2) 50%, transparent 50%);
  background-size: 280px 280px;
  opacity: 0.5;
  animation: ember-rise 64s linear infinite;
}

@keyframes ember-rise {
  from { background-position: 0 0; }
  to   { background-position: 40px -280px; }
}

/* Pentagrams ------------------------------------------------------------ */
/* Slaughterhouse only: the star field, inverted. Same construction as the
   snow flakes -- drawn at true size inside a large tile so background-size
   paints them 1:1 rather than scaling one glyph up to fill the tile.

   Bigger than the flakes (13-16px, not 5-6) because below about 12px the
   star's own gaps close up under antialiasing and it reads as a dot. Opacity
   carries the restraint instead of size. */

.texture--ember.texture--pentagram::after {
  background-image:
    url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300' viewBox='0 0 300 300'%3E%3Cg stroke='%23ff1a1a' fill='none' stroke-linejoin='miter'%3E%3Cpath d='M46.00 73.50 L41.59 59.93 L53.13 68.32 L38.87 68.32 L50.41 59.93 Z' stroke-width='1.1'/%3E%3Cpath d='M186.00 156.50 L182.18 144.74 L192.18 152.01 L179.82 152.01 L189.82 144.74 Z' stroke-width='1.0'/%3E%3Cpath d='M104.00 248.00 L99.30 233.53 L111.61 242.47 L96.39 242.47 L108.70 233.53 Z' stroke-width='1.1'/%3E%3Cpath d='M252.00 50.50 L248.18 38.74 L258.18 46.01 L245.82 46.01 L255.82 38.74 Z' stroke-width='1.0'/%3E%3Cpath d='M232.00 231.00 L227.89 218.34 L238.66 226.16 L225.34 226.16 L236.11 218.34 Z' stroke-width='1.05'/%3E%3C/g%3E%3C/svg%3E"),
    url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='440' height='440' viewBox='0 0 440 440'%3E%3Cg stroke='%23ff6a1a' fill='none' stroke-linejoin='miter'%3E%3Cpath d='M80.00 121.50 L76.77 111.55 L85.23 117.70 L74.77 117.70 L83.23 111.55 Z' stroke-width='0.9'/%3E%3Cpath d='M300.00 62.00 L296.47 51.15 L305.71 57.85 L294.29 57.85 L303.53 51.15 Z' stroke-width='0.9'/%3E%3Cpath d='M186.00 321.50 L182.77 311.55 L191.23 317.70 L180.77 317.70 L189.23 311.55 Z' stroke-width='0.9'/%3E%3Cpath d='M376.00 254.00 L372.47 243.15 L381.71 249.85 L370.29 249.85 L379.53 243.15 Z' stroke-width='0.9'/%3E%3C/g%3E%3C/svg%3E");
  background-size: 300px 300px, 440px 440px;
  background-position: 0 0, 150px 70px;
  opacity: 0.3;
  animation: pentagram-rise 78s linear infinite;
}

@keyframes pentagram-rise {
  from { background-position: 0 0, 150px 70px; }
  to   { background-position: -30px -300px, 120px -370px; }
}

/* Caustics --------------------------------------------------------------- */
/* Water light: two offset ripple fields sliding across each other, so the
   interference pattern never quite repeats. */

.texture--caustics::before,
.texture--caustics::after {
  content: "";
  position: absolute;
  inset: -15%;
  background-repeat: repeat;
  mix-blend-mode: screen;
}

.texture--caustics::before {
  background-image:
    radial-gradient(ellipse 60px 22px at 30% 40%,
      color-mix(in srgb, var(--accent) 26%, transparent) 0%, transparent 70%),
    radial-gradient(ellipse 44px 16px at 74% 68%,
      color-mix(in srgb, var(--accent2) 22%, transparent) 0%, transparent 70%);
  background-size: 240px 170px;
  opacity: 0.5;
  animation: caustic-a 26s linear infinite;
}

.texture--caustics::after {
  background-image:
    radial-gradient(ellipse 80px 26px at 55% 25%,
      color-mix(in srgb, var(--accent2) 20%, transparent) 0%, transparent 72%);
  background-size: 330px 220px;
  opacity: 0.4;
  animation: caustic-b 37s linear infinite;
}

@keyframes caustic-a {
  from { background-position: 0 0; }
  to   { background-position: 240px 85px; }
}

@keyframes caustic-b {
  from { background-position: 0 0; }
  to   { background-position: -330px 110px; }
}

/* Chrome ----------------------------------------------------------------- */
/* A brushed sheen: fine diagonal grain with one slow highlight travelling
   across it. */

.texture--chrome::before,
.texture--chrome::after {
  content: "";
  position: absolute;
  inset: 0;
}

.texture--chrome::before {
  background: repeating-linear-gradient(
    108deg,
    color-mix(in srgb, var(--ink) 6%, transparent) 0 1px,
    transparent 1px 4px
  );
  opacity: 0.45;
}

.texture--chrome::after {
  background: linear-gradient(
    108deg,
    transparent 38%,
    color-mix(in srgb, var(--accent) 16%, transparent) 50%,
    transparent 62%
  );
  background-size: 300% 100%;
  opacity: 0.55;
  animation: chrome-sweep 14s ease-in-out infinite;
}

@keyframes chrome-sweep {
  0%, 100% { background-position: 120% 0; }
  50%      { background-position: -20% 0; }
}

/* Documents (terms, privacy, credits) ----------------------------------- */

.hero--doc {
  min-height: 62svh;
}

.doc__title {
  max-width: 18ch;
}

.doc {
  font-size: var(--step-0);
  line-height: 1.7;
}

.doc__lead {
  font-size: var(--step-1);
  line-height: 1.6;
  color: var(--ink);
  border-left: 3px solid var(--accent);
  padding-left: var(--space-4);
  margin-bottom: var(--space-7);
}

.doc h2 {
  font-size: var(--step-2);
  margin-top: var(--space-7);
  margin-bottom: var(--space-3);
}

.doc ul {
  padding-left: 1.1em;
  margin: 0 0 1em;
}

.doc li {
  margin-bottom: 0.5em;
}

.doc code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  padding: 0.1em 0.35em;
  border-radius: 3px;
  background: color-mix(in srgb, var(--ink) 10%, transparent);
}

.doc a {
  color: var(--accent);
  text-underline-offset: 0.2em;
}

.doc__meta {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--muted);
  margin: var(--space-6) 0 0;
}

/* Loud on purpose: an unset contact address should be impossible to miss. */
.doc__todo {
  color: var(--accent);
  border-bottom: 2px solid var(--accent);
}

.docnav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-6);
  padding-block: var(--space-6);
  border-top: var(--rule) solid var(--rule-colour);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.site-foot__nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  list-style: none;
  margin: var(--space-4) 0 0;
  padding: 0;
}

.site-foot__nav a {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}

.site-foot__nav a:hover,
.site-foot__nav a:focus-visible {
  color: var(--ink);
}

/* Hero title legibility -------------------------------------------------- */
/* There used to be a tinted scrim behind every hero title here. It read as a
   dark elliptical glow sitting on the artwork, which fought the brighter
   levels. The themed tier now carries a soft full-width haze baked into the
   art itself (tools/gen_themed.py, band()), and the bespoke levels each
   handle their own title -- carved stone on Deimos, a white rim on Antarctic
   Lights, an outline on Tidal Wave. Slaughterhouse keeps a real scrim,
   defined in its own stylesheet, because the skull glow directly behind its
   title is the brightest backdrop on the site. */

.hero__title {
  position: relative;
}

/* Countdown palette rail -------------------------------------------------- */
/* Every row already carries its level's palette inline, so each one can
   preview the page it leads to at no extra cost in data or requests. */

.countdown__hit {
  position: relative;
}

.countdown__chip {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 4px;
  border-radius: 0 2px 2px 0;
  background: linear-gradient(
    180deg,
    var(--accent) 0%,
    color-mix(in srgb, var(--accent) 50%, var(--accent2)) 50%,
    var(--accent2) 100%
  );
  opacity: 0.7;
  transition: width var(--dur) var(--ease), opacity var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease);
}

a.countdown__hit:hover .countdown__chip,
a.countdown__hit:focus-visible .countdown__chip {
  width: 8px;
  opacity: 1;
  box-shadow: 0 0 1.4rem color-mix(in srgb, var(--accent) 65%, transparent);
}

.countdown__hit--soon .countdown__chip {
  opacity: 0.3;
}

/* The row's own accent, so hovering previews the destination's colour. */
a.countdown__hit:hover,
a.countdown__hit:focus-visible {
  background: linear-gradient(
    90deg,
    color-mix(in srgb, var(--accent) 16%, transparent) 0%,
    color-mix(in srgb, var(--accent) 4%, transparent) 55%,
    transparent 100%
  );
}

/* Rank nav swatch --------------------------------------------------------- */

.ranknav__link {
  position: relative;
}

.ranknav__swatch {
  display: block;
  height: 3px;
  width: 100%;
  margin-bottom: var(--space-3);
  border-radius: 2px;
  background: linear-gradient(90deg, var(--accent) 0%, var(--accent2) 100%);
  opacity: 0.75;
  transition: opacity var(--dur) var(--ease), height var(--dur) var(--ease);
}

.ranknav__link:hover .ranknav__swatch,
.ranknav__link:focus-visible .ranknav__swatch {
  opacity: 1;
  height: 6px;
}

/* The name takes the destination's accent, not this page's. */
.ranknav__link:hover .ranknav__name,
.ranknav__link:focus-visible .ranknav__name {
  color: var(--accent);
}

/* Sources ----------------------------------------------------------------- */

.sources ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-3);
}

.sources li a {
  display: grid;
  gap: 0.15em;
  text-decoration: none;
  padding-block: var(--space-2);
  border-bottom: 1px solid var(--rule-colour);
}

.sources__name {
  font-size: var(--step-0);
  color: var(--ink);
}

.sources li a:hover .sources__name,
.sources li a:focus-visible .sources__name {
  color: var(--accent);
}

/* Kept visible but secondary: the reader can still see exactly where a link
   goes before following it. */
.sources__url {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--muted);
  overflow-wrap: anywhere;
}

/* Spotlight entrance ------------------------------------------------------ */
/* The hidden state is scoped to [data-enter], an attribute only scroll.js
   sets. If the script never runs the panel is plainly visible, so the
   entrance can never hide the content it is meant to introduce.

   The hidden state carries transition:none on purpose. scroll.js adds the
   attribute after first paint, so a transition here would make the panel
   visibly fade OUT before it fades in. Hiding snaps; only revealing
   animates. */

.spotlight[data-enter] .spotlight__big,
.spotlight[data-enter] .eyebrow,
.spotlight[data-enter] .spotlight__sub {
  opacity: 0;
  transition: none;
}

.spotlight[data-enter] .eyebrow { transform: translateY(18px); }
.spotlight[data-enter] .spotlight__sub { transform: translateY(20px); }

/* The headline's entrance is picked by the level's own signature. */
[data-signature="flood"]    .spotlight[data-enter] .spotlight__big { transform: translateY(26%); }
[data-signature="descend"]  .spotlight[data-enter] .spotlight__big { transform: translateY(-28%); }
[data-signature="surge"]    .spotlight[data-enter] .spotlight__big { transform: translateX(-14%); }
[data-signature="slash"]    .spotlight[data-enter] .spotlight__big { transform: translateX(12%) skewX(9deg); }
[data-signature="ignite"]   .spotlight[data-enter] .spotlight__big { transform: scale(0.8); }
[data-signature="pulse"]    .spotlight[data-enter] .spotlight__big { transform: scale(1.2); }
[data-signature="orbit"]    .spotlight[data-enter] .spotlight__big { transform: rotate(-6deg) scale(0.9); }
[data-signature="fracture"] .spotlight[data-enter] .spotlight__big { transform: scale(1.1) rotate(3deg); }
[data-signature="prism"]    .spotlight[data-enter] .spotlight__big { transform: skewY(5deg) translateY(14%); }
[data-signature="static"]   .spotlight[data-enter] .spotlight__big { transform: scale(0.96); }
[data-signature="twin"]     .spotlight[data-enter] .spotlight__big { transform: translateY(18%) scaleY(0.7); }
[data-signature="whiteout"] .spotlight[data-enter] .spotlight__big { transform: translateX(-18%) skewX(-6deg); }
[data-signature="overgrow"] .spotlight[data-enter] .spotlight__big { transform: scale(0.7) rotate(-4deg); }
[data-signature="iris"]     .spotlight[data-enter] .spotlight__big { transform: scale(1.35); }
[data-signature="corrupt"]  .spotlight[data-enter] .spotlight__big { transform: translateX(9%) skewY(-4deg); }
[data-signature="ascend"]   .spotlight[data-enter] .spotlight__big { transform: translateY(-24%) scale(0.88); }

/* The [data-signature=...] rules above are (0,4,0); a plain
   .spotlight[data-enter="in"] .spotlight__big is (0,3,0) and loses to them,
   which would leave the headline parked at its entrance transform forever.
   The resting state has to carry the same [data-signature] prefix to win. */
.spotlight[data-enter="in"] .spotlight__big,
.spotlight[data-enter="in"] .eyebrow,
.spotlight[data-enter="in"] .spotlight__sub,
[data-signature] .spotlight[data-enter="in"] .spotlight__big {
  opacity: 1;
  transform: none;
  transition: opacity 0.7s var(--ease), transform 0.9s var(--ease);
}

.spotlight[data-enter="in"] .spotlight__sub { transition-delay: 0.12s; }

/* Song: the track, and the upload the level actually points at ----------- */

.player__as {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  line-height: 1.7;
  color: var(--muted);
  max-width: 52ch;
  margin: calc(var(--space-3) * -1) 0 var(--space-4);
}

.player__as-name {
  color: color-mix(in srgb, var(--accent) 40%, var(--ink));
}

/* 404 — the death screen ------------------------------------------------- */
/* 404 reads as 40.4%, so the page is a run that fell short. Everything here
   is drawn in CSS: this file is served for any missing path, and a missing
   path is exactly when you do not want to be fetching more assets. */

.dead {
  min-height: 100svh;
  display: grid;
  align-content: center;
  justify-items: start;
  gap: var(--space-4);
  position: relative;
  padding-block: var(--space-8);
}

.dead__art {
  position: absolute;
  inset: 0 auto 0 50%;
  width: 100vw;
  margin-left: -50vw;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}

.dead__glow {
  position: absolute;
  inset: 0;
  background: radial-gradient(
    ellipse 60% 46% at 34% 46%,
    color-mix(in srgb, var(--accent) 26%, transparent) 0%,
    transparent 70%
  );
}

/* The row that killed you. 24 spikes as one clip-path: no repeating
   gradient draws a reliable triangle across engines, and a row of elements
   for scenery is not worth the markup. */
.dead__spikes {
  position: absolute;
  inset: auto 0 0 0;
  height: 4.5rem;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--accent) 40%, var(--field)) 0%,
    var(--field) 100%
  );
  clip-path: polygon(0% 100%, 2.083% 0%, 4.167% 100%, 6.25% 0%, 8.333% 100%, 10.42% 0%, 12.5% 100%, 14.58% 0%, 16.67% 100%, 18.75% 0%, 20.83% 100%, 22.92% 0%, 25% 100%, 27.08% 0%, 29.17% 100%, 31.25% 0%, 33.33% 100%, 35.42% 0%, 37.5% 100%, 39.58% 0%, 41.67% 100%, 43.75% 0%, 45.83% 100%, 47.92% 0%, 50% 100%, 52.08% 0%, 54.17% 100%, 56.25% 0%, 58.33% 100%, 60.42% 0%, 62.5% 100%, 64.58% 0%, 66.67% 100%, 68.75% 0%, 70.83% 100%, 72.92% 0%, 75% 100%, 77.08% 0%, 79.17% 100%, 81.25% 0%, 83.33% 100%, 85.42% 0%, 87.5% 100%, 89.58% 0%, 91.67% 100%, 93.75% 0%, 95.83% 100%, 97.92% 0%, 100% 100%);
  opacity: 0.75;
}

.dead__attempt {
  font-family: var(--font-mono);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0;
}

.dead__n {
  color: color-mix(in srgb, var(--accent) 55%, var(--ink));
}

.dead__pct {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: clamp(4rem, 22vw, 16rem);
  line-height: 0.82;
  letter-spacing: -0.05em;
  margin: 0;
  color: var(--ink);
}

.dead__sign {
  font-size: 0.42em;
  color: var(--accent);
  vertical-align: baseline;
  margin-left: 0.06em;
}

/* The bar stops where the run did. */
.dead__bar {
  position: relative;
  width: min(100%, 46rem);
  height: 0.55rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--ink) 12%, transparent);
  overflow: visible;
}

.dead__fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 40.4%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--accent2) 0%, var(--accent) 100%);
}

.dead__cursor {
  position: absolute;
  top: 50%;
  left: 40.4%;
  width: 0.2rem;
  height: 2.2rem;
  translate: -50% -50%;
  border-radius: 2px;
  background: var(--ink);
  box-shadow: 0 0 1.4rem color-mix(in srgb, var(--accent) 70%, transparent);
}

.dead__lede {
  color: var(--muted);
  margin: var(--space-3) 0 0;
}

.dead__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-top: var(--space-3);
}

.dead__go {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
  padding: var(--space-3) var(--space-5);
  border: var(--rule) solid var(--rule-colour);
  border-radius: 2px;
  transition: background var(--dur) var(--ease),
    border-color var(--dur) var(--ease);
}

.dead__go:hover,
.dead__go:focus-visible {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-color: color-mix(in srgb, var(--accent) 60%, transparent);
}

.dead__go--primary {
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.dead__best {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--muted);
  margin: var(--space-4) 0 0;
}

@media (max-width: 44rem) {
  .dead__cursor { height: 1.6rem; }
  .dead__go { padding: var(--space-3) var(--space-4); }
}

/* The run ---------------------------------------------------------------- */
/* Geometry Dash shows you one number while you play: how far through you
   are. The site is a countdown, so every page carries the same readout —
   a fill across the top and a percentage in the corner. It is the thing
   that makes thirty separate pages feel like one attempt. */

.attempt {
  position: fixed;
  inset: 0 0 auto 0;
  height: 3px;
  z-index: 90;
  background: color-mix(in srgb, var(--ink) 9%, transparent);
  pointer-events: none;
}

.attempt__fill {
  display: block;
  height: 100%;
  width: 100%;
  transform: scaleX(0);
  transform-origin: left center;
  background: linear-gradient(90deg, var(--accent2) 0%, var(--accent) 100%);
  box-shadow: 0 0 0.9rem color-mix(in srgb, var(--accent) 70%, transparent);
  /* No transition: the value is written every frame from the scroll
     position, and easing a per-frame write only makes it lag the page. */
}

.attempt__read {
  position: fixed;
  top: 0.75rem;
  right: var(--gutter);
  z-index: 90;
  margin: 0;
  display: flex;
  gap: 0.55rem;
  align-items: baseline;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.16em;
  color: var(--muted);
  pointer-events: none;
  padding: 0.25rem 0.6rem;
  border-radius: 0 0 3px 3px;
  /* Not mix-blend-mode. It was unpredictable across thirty palettes and
     this readout is information, so it gets a guaranteed ground instead. */
  /* 92%, not 82%: --muted only clears 4.5:1 against a full --field, and
     this sits over whatever the hero art is doing up there. */
  background: color-mix(in srgb, var(--field) 92%, transparent);
  backdrop-filter: blur(6px);
}

.attempt__rank {
  color: color-mix(in srgb, var(--accent) 60%, var(--ink));
}

[data-run="complete"] .attempt__read {
  color: var(--accent);
}

@media (max-width: 44rem) {
  /* The readout sits where a phone browser puts its own chrome, and it is
     the least important thing on the page. */
  .attempt__read { display: none; }
}

/* Countdown marks -------------------------------------------------------- */
/* Thirty rows of identical text became thirty rows with a glyph each,
   built from the signature the level already declares. currentColor, so
   the row's own palette drives it. */

.countdown__mark {
  grid-area: mark;
  align-self: start;
  display: block;
  color: color-mix(in srgb, var(--accent) 62%, var(--muted));
  opacity: 0.7;
  transition: opacity var(--dur) var(--ease), color var(--dur) var(--ease),
    transform var(--dur) var(--ease);
}

a.countdown__hit:hover .countdown__mark,
a.countdown__hit:focus-visible .countdown__mark {
  opacity: 1;
  color: var(--accent);
  transform: rotate(-8deg) scale(1.14);
}

.countdown__hit--soon .countdown__mark { opacity: 0.28; }

/* The charts ------------------------------------------------------------- */

.nchart {
  margin: 0 0 var(--space-8);
}

.nchart__head {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--space-4);
  align-items: baseline;
  margin-bottom: var(--space-4);
}

.nchart__title {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--step-2);
  letter-spacing: -0.02em;
}

.nchart__note,
.nchart__caveat {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.nchart__bars {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  align-items: end;
  gap: clamp(2px, 0.5vw, 7px);
  height: clamp(7rem, 18vh, 12rem);
  border-bottom: var(--rule) solid var(--rule-colour);
}

.nchart__bar {
  position: relative;
  height: 100%;
  display: flex;
  align-items: flex-end;
  min-width: 0;
}

.nchart__fill {
  display: block;
  width: 100%;
  min-height: 2px;
  border-radius: 2px 2px 0 0;
  background: linear-gradient(
    180deg,
    var(--accent) 0%,
    color-mix(in srgb, var(--accent2) 70%, transparent) 100%
  );
  transition: filter var(--dur) var(--ease);
}

/* A level with no count is drawn as an empty column rather than dropped.
   Hiding it would make the chart look complete and overstate what is
   actually known, which is the opposite of what every level page does with
   a missing fact. */
.nchart__bar--nil .nchart__fill {
  height: 100% !important;
  background: repeating-linear-gradient(
    -45deg,
    color-mix(in srgb, var(--ink) 12%, transparent) 0 3px,
    transparent 3px 7px
  );
  border-radius: 2px;
}

.nchart__bar:hover .nchart__fill {
  filter: brightness(1.35);
}

.nchart__tip {
  position: absolute;
  bottom: calc(100% + 0.5rem);
  left: 50%;
  translate: -50% 0;
  white-space: nowrap;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  padding: 0.3rem 0.55rem;
  border-radius: 3px;
  background: color-mix(in srgb, var(--field) 92%, var(--ink));
  border: var(--rule) solid var(--rule-colour);
  color: var(--ink);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur) var(--ease);
  z-index: 3;
}

.nchart__bar:hover .nchart__tip { opacity: 1; }

.nchart__axis {
  display: flex;
  justify-content: space-between;
  gap: var(--space-4);
  margin: var(--space-3) 0 0;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--muted);
}

@media (max-width: 44rem) {
  /* Thirty bars in a phone's width is four pixels each. The tooltip needs a
     hover that is not there anyway, so the chart becomes a strip: still the
     shape of the data, no longer pretending to be readable per level. */
  .nchart__bars { height: 5.5rem; gap: 1px; }
  .nchart__tip { display: none; }
}

/* The tabs, top right of the hero --------------------------------------- */

.hero--nav {
  /* Room for the tabs above the eyebrow rule, so the two never share a line. */
  padding-block-start: calc(var(--space-6) + 2.6rem);
}

.topnav {
  position: absolute;
  top: var(--space-3);
  right: var(--gutter);
  z-index: 6;
  display: flex;
  gap: var(--space-2);
}

.topnav__link {
  font: 500 var(--step--2) / 1 var(--font-mono);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
  padding: 0.55rem 0.95rem;
  border: var(--rule) solid var(--rule-colour);
  border-radius: 999px;
  background: color-mix(in srgb, var(--field) 72%, transparent);
  transition: background var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease);
}

.topnav__link:hover,
.topnav__link:focus-visible {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  border-color: color-mix(in srgb, var(--accent) 60%, transparent);
}

.topnav__link[aria-current="page"] {
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}

/* The scroll readout lives top right on every page, which is where the tabs
   are. On the landing page it moves to the left; on the game, scroll position
   means nothing and the bar across the top shows the game instead. */
[data-level="index"] .attempt__read,
[data-level="home"] .attempt__read { right: auto; left: var(--gutter); }
[data-level="home"] .attempt__read { display: none; }
[data-level="game"] .attempt__read { display: none; }

/* ---------------------------------------------------------------------------
   Games, made loud. Most visitors come for the games, so the tab glows, the
   landing page offers them above the fold, and the games sit right under the
   hero before the countdown starts.
   --------------------------------------------------------------------------- */

@property --games-spin {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

.topnav__link[data-tab="games"] {
  position: relative;
  padding-left: 1.95rem;
  font-weight: 700;
  color: #fff;
  border: 2px solid transparent;
  background:
    linear-gradient(color-mix(in srgb, var(--field) 88%, #1a0b2e), color-mix(in srgb, var(--field) 88%, #1a0b2e)) padding-box,
    conic-gradient(from var(--games-spin), #21e6ff, #ff2bd6, #ffd23f, #a8ff2e, #21e6ff) border-box;
  box-shadow: 0 0 1.4rem -0.3rem rgb(255 43 214 / 0.55), 0 0 2.2rem -0.8rem rgb(33 230 255 / 0.6);
  animation: games-spin 5s linear infinite;
}

.topnav__link[data-tab="games"]::before {
  content: "";
  position: absolute;
  left: 0.85rem;
  top: 50%;
  border-left: 0.5rem solid #a8ff2e;
  border-block: 0.32rem solid transparent;
  transform: translateY(-50%);
  filter: drop-shadow(0 0 0.3rem #a8ff2e);
}

.topnav__link[data-tab="games"]:hover,
.topnav__link[data-tab="games"]:focus-visible {
  background:
    linear-gradient(#2a0f45, #2a0f45) padding-box,
    conic-gradient(from var(--games-spin), #21e6ff, #ff2bd6, #ffd23f, #a8ff2e, #21e6ff) border-box;
  transform: translateY(-1px);
}

@keyframes games-spin { to { --games-spin: 360deg; } }

/* The call to play, in the middle of the landing page's hero foot. */
.hero__mid { display: grid; justify-items: center; gap: var(--space-4); }

.herocta {
  display: inline-flex;
  align-items: center;
  gap: 0.9rem;
  padding: 0.65rem 1.4rem 0.65rem 0.7rem;
  border-radius: 999px;
  text-decoration: none;
  color: #fff;
  text-transform: none;
  letter-spacing: 0;
  border: 2px solid transparent;
  background:
    linear-gradient(#12081f, #12081f) padding-box,
    conic-gradient(from var(--games-spin), #21e6ff, #ff2bd6, #ffd23f, #a8ff2e, #21e6ff) border-box;
  box-shadow: 0 0 2.4rem -0.4rem rgb(255 43 214 / 0.5), 0 0 3.5rem -1rem rgb(33 230 255 / 0.55);
  animation: games-spin 5s linear infinite;
  transition: transform var(--dur-fast) var(--ease);
}

.herocta:hover, .herocta:focus-visible { transform: translateY(-2px) scale(1.02); }

.herocta__play {
  flex: none;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #c8ff6a, #57c800 70%);
  box-shadow: inset 0 -3px 0 rgb(0 0 0 / 0.25), 0 0 1rem rgb(168 255 46 / 0.6);
  position: relative;
}

.herocta__play::after {
  content: "";
  position: absolute;
  left: 54%;
  top: 50%;
  transform: translate(-50%, -50%);
  border-left: 0.85rem solid #ffe14a;
  border-block: 0.55rem solid transparent;
  filter: drop-shadow(0 1px 0 rgb(0 0 0 / 0.35));
}

.herocta__text { display: grid; text-align: left; line-height: 1.15; }
.herocta__big { font: 800 var(--step-1) / 1.1 var(--font-display); }
.herocta__small { font: 500 var(--step--2) / 1.3 var(--font-mono); letter-spacing: 0.08em; color: #d7cfff; }

/* The games, right under the hero. */
.gband { padding-block: var(--space-7) var(--space-8); }

.gband__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-3) var(--space-5);
  margin-bottom: var(--space-6);
}

.gband__title {
  margin: 0;
  font: 400 var(--step-6) / 0.9 "Bungee", var(--font-display);
  letter-spacing: 0.02em;
  background: linear-gradient(90deg, #21e6ff, #ff2bd6 45%, #ffd23f);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.gband__lede { margin: 0; color: var(--muted); flex: 1 1 18rem; }
.gband__all { font: 600 var(--step--2) var(--font-mono); letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink); }

.gtiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 19rem), 1fr));
  gap: var(--space-5);
}

.gtile {
  position: relative;
  display: grid;
  grid-template-rows: 11rem auto;
  overflow: hidden;
  border-radius: 1rem;
  text-decoration: none;
  color: var(--ink);
  background: #0d0918;
  border: 1px solid rgb(255 255 255 / 0.1);
  transition: transform var(--dur) var(--ease), border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.gtile:hover, .gtile:focus-visible {
  transform: translateY(-4px);
  border-color: rgb(255 255 255 / 0.35);
  box-shadow: 0 1.2rem 3rem -1.2rem var(--tile-glow, rgb(255 43 214 / 0.6));
}

/* The first tile is the newest game and leads. */
.gtiles > .gtile:first-child { grid-column: 1 / -1; grid-template-rows: 15rem auto; }
@media (min-width: 60rem) {
  .gtiles { grid-template-columns: 1.6fr 1fr 1fr; }
  .gtiles > .gtile:first-child { grid-column: auto; grid-row: span 1; grid-template-rows: 15rem auto; }
  .gtiles > .gtile:not(:first-child) { grid-template-rows: 15rem auto; }
}

.gtile__art { position: relative; overflow: hidden; display: block; }
.gtile__art svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }

.gtile__badge {
  position: absolute;
  top: 0.9rem;
  left: 0.9rem;
  z-index: 2;
  font: 400 0.8rem / 1 "Bungee", var(--font-display);
  letter-spacing: 0.06em;
  padding: 0.45rem 0.65rem 0.35rem;
  border-radius: 0.4rem;
  color: #12081f;
  background: #ffd23f;
  box-shadow: 0 0 1.2rem rgb(255 210 63 / 0.6);
}

.gtile__body { display: grid; gap: 0.35rem; padding: var(--space-5); align-content: start; }
.gtile__title { font: 400 var(--step-2) / 1.05 "Bungee", var(--font-display); letter-spacing: 0.01em; }
.gtile__desc { color: var(--muted); font-size: var(--step--1); }
.gtile__go {
  margin-top: 0.4rem;
  font: 600 var(--step--2) var(--font-mono);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--tile-go, #a8ff2e);
}
.gtile__go::after { content: " \25B8"; }

.gtile--gg { --tile-glow: rgb(255 43 214 / 0.65); --tile-go: #21e6ff; }
.gtile--demondle { --tile-glow: rgb(83 201 120 / 0.6); --tile-go: #7ee29b; }
.gtile--names { --tile-glow: rgb(70 140 255 / 0.6); --tile-go: #8db8ff; }

/* tile art motion */
.gart-scroll { animation: gart-scroll 6s linear infinite; }
@keyframes gart-scroll { to { transform: translateX(-200px); } }
.gart-bob { animation: gart-bob 1.2s var(--ease) infinite alternate; transform-box: fill-box; transform-origin: center; }
@keyframes gart-bob { from { transform: translateY(0) rotate(0deg); } to { transform: translateY(-14px) rotate(90deg); } }
.gart-flip { animation: gart-flip 4.8s var(--ease) infinite; transform-box: fill-box; transform-origin: center; animation-delay: calc(var(--i) * 0.12s); }
@keyframes gart-flip { 0%, 12% { transform: scaleY(1); } 18% { transform: scaleY(0); } 24%, 100% { transform: scaleY(1); } }
.gart-fill { animation: gart-fill 7s steps(1) infinite; animation-delay: calc(var(--i) * 0.11s); }
@keyframes gart-fill { 0% { fill: #1c2236; } 30%, 100% { fill: #2f6fd8; } }

@media (prefers-reduced-motion: reduce) {
  .topnav__link[data-tab="games"], .herocta, .gart-scroll, .gart-bob, .gart-flip, .gart-fill { animation: none; }
}

/* The landing page's way to the ranked list: small, on purpose. */
.listlink { white-space: nowrap; color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--rule-colour); padding-bottom: 0.1rem; }
.listlink:hover, .listlink:focus-visible { border-color: var(--ink); }

.listnote {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1.5rem;
  padding-block: var(--space-6) var(--space-8);
  border-top: var(--rule) solid var(--rule-colour);
}
.listnote__title { margin: 0; font: 600 var(--step--2) / 1 var(--font-mono); letter-spacing: 0.28em; text-transform: uppercase; }
.listnote__text { margin: 0; color: var(--muted); flex: 1 1 22rem; }
.listnote__go { font: 600 var(--step--2) var(--font-mono); letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink); }
.listnote__go::after { content: " \2192"; }

/* The "still unfinished" notice on the list and the level pages (src/js/wip.js).
   Its own fixed colours, not the page's: level pages repaint --field and --ink (Nhelv
   is white), and this should read as the site talking, not the level. */
.wip {
  --wip-amber: #ffc53d;
  --wip-ground: #0c0d13;
  --wip-ink: #f2f2f4;
  --wip-muted: #a3a6b4;
  width: min(38rem, calc(100vw - 2rem));
  max-height: calc(100dvh - 2rem);
  overflow: auto;
  padding: 0 var(--space-6) var(--space-6);
  color: var(--wip-ink);
  background: var(--wip-ground);
  border: 2px solid var(--wip-amber);
  border-radius: 6px;
  box-shadow: 0 1.5rem 4rem rgba(0, 0, 0, 0.6), 0 0 0 6px color-mix(in srgb, var(--wip-amber) 18%, transparent);
  color-scheme: dark;
  font-family: "Archivo", "Helvetica Neue", Arial, sans-serif;
  animation: wip-in 0.5s var(--ease) both;
}

/* A hazard stripe across the top: under construction. */
.wip::before {
  content: "";
  display: block;
  height: 0.7rem;
  margin: 0 calc(-1 * var(--space-6)) var(--space-5);
  background: repeating-linear-gradient(135deg, var(--wip-amber) 0 0.85rem, #111 0.85rem 1.7rem);
}

.wip:focus { outline: none; }

.wip::backdrop {
  background: color-mix(in srgb, #06070b 62%, transparent);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  animation: wip-fade 0.5s ease both;
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .wip::backdrop { background: color-mix(in srgb, #06070b 93%, transparent); }
}

html:has(.wip[open]) { overflow: hidden; }

@keyframes wip-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes wip-in {
  from { opacity: 0; transform: translateY(0.9rem) scale(0.98); }
  to   { opacity: 1; transform: none; }
}

.wip__kicker {
  margin: 0 0 var(--space-3);
  font: 600 var(--step--2) / 1.4 "IBM Plex Mono", ui-monospace, Menlo, monospace;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--wip-amber);
}

.wip__title {
  margin: 0 0 var(--space-4);
  font: 900 clamp(1.9rem, 1.4rem + 2.6vw, 3rem) / 1.02 "Archivo", "Helvetica Neue", Arial, sans-serif;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  color: var(--wip-ink);
}

.wip__body p {
  margin: 0 0 var(--space-3);
  font-size: var(--step-0);
  line-height: 1.6;
  color: var(--wip-ink);
}

.wip__body .wip__sign {
  margin-top: var(--space-4);
  font: var(--step--1) / 1.4 "IBM Plex Mono", ui-monospace, Menlo, monospace;
  color: var(--wip-muted);
}

.wip__actions { margin: var(--space-5) 0 0; }

.wip__btn {
  width: 100%;
  padding: 1rem 1.2rem;
  font: 700 var(--step-0) / 1 "IBM Plex Mono", ui-monospace, Menlo, monospace;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #111;
  background: var(--wip-amber);
  border: 0;
  border-radius: 4px;
  cursor: pointer;
}

.wip__btn:hover { background: color-mix(in srgb, var(--wip-amber) 85%, #fff); }
.wip__btn:focus-visible { outline: 2px solid var(--wip-ink); outline-offset: 3px; }

@media (max-width: 44rem) {
  .wip { padding: 0 var(--space-5) var(--space-5); }
  .wip::before { margin: 0 calc(-1 * var(--space-5)) var(--space-4); }
}

@media (prefers-reduced-motion: reduce) {
  .wip,
  .wip::backdrop { animation: none; }
}
