/* Light.vn
 *
 * - light, and only light: the engine is 光, the audience is writers, and the site
 *   this replaces was light too
 * - the hero shows six lines of real script beside the frame they produce; the
 *   syntax colours are sampled from the editor, not invented
 * - Shippori Mincho carries the headings (a novel's face), Zen Kaku Gothic New the
 *   running text, JetBrains Mono the labels that echo the script editor
 * - the nav wraps rather than collapsing, so it needs no script; the only
 *   JavaScript on the site is the download-page slider arrows
 */

:root {
  color-scheme: light;

  --paper: #faf8f4;
  --paper-sunken: #f1ece3;
  --ink: #16181f;
  --muted: #5b6070;

  /* syntax colours, sampled out of the editor rather than invented */
  --s-comment: #048003;
  --s-red: #ff020c;
  --s-blue: #1a82d5;
  --s-warm: #ff6647;
  --s-violet: #9e3ad0;
  --s-number: #04028e;
  --s-plain: #1c1c1c;

  /* deep enough to read as text on paper */
  --lamp: #a55f08;
  /* the bright amber, for fills and marks */
  --lamp-fill: #f0b458;
  --lamp-fill-hover: #e2a340;
  /* the second accent, and the only one: amber means "act on this", teal means
   * "something is running". Built like the amber button — light fill, dark text
   * — so the two sit at the same weight and differ only in hue. */
  --fest: #6fd0b7;
  --fest-hover: #5cbfa6;
  --on-fest: var(--on-lamp);
  --on-lamp: #16181f;
  /* identity, not an accent: the brand dot and the favicon are one mark */
  --mark: #57b9d2;
  --mark-glow: rgba(87, 185, 210, 0.3);

  --line: rgba(22, 24, 31, 0.14);
  --line-strong: rgba(22, 24, 31, 0.28);
  --wash: rgba(22, 24, 31, 0.05);

  --font-display: "Shippori Mincho", "Hiragino Mincho ProN", "Yu Mincho", "MS PMincho", serif;
  --font-body: "Zen Kaku Gothic New", "Hiragino Sans", "Yu Gothic", "Meiryo", sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", Consolas, monospace;

  --wrap: 68rem;
  --wrap-narrow: 40rem;
  /* full-width media cap: at --wrap a 16:9 frame is 612px, nearly a whole
   * viewport, so a heading and its picture never share a screen */
  --media: 52rem;
  --measure: 34em;

  --t-display: clamp(2.5rem, 1.3rem + 5vw, 5rem);
  --t-h1: clamp(2rem, 1.4rem + 2.6vw, 3.25rem);
  --t-h2: clamp(1.5rem, 1.2rem + 1.4vw, 2.125rem);
  --t-h3: 1.125rem;
  --t-body: clamp(1rem, 0.97rem + 0.16vw, 1.0625rem);
  --t-small: 0.875rem;
  --t-label: 0.75rem;

  --section-y: clamp(3.5rem, 6vw, 5.5rem);
  --gutter: clamp(1.25rem, 4vw, 2.5rem);
  --radius: 4px;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--t-body);
  line-height: 1.95;
  letter-spacing: 0.04em;
  line-break: strict;
  overflow-wrap: anywhere;
}

img {
  display: block;
  max-width: 100%;
  height: auto;
}

ul,
ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

p {
  margin: 0;
}

h1,
h2,
h3 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.4;
  letter-spacing: 0.02em;
}

a {
  color: inherit;
}

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

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  left: 50%;
  top: 0;
  transform: translate(-50%, -120%);
  z-index: 20;
  padding: 0.75rem 1.25rem;
  background: var(--lamp-fill);
  color: var(--on-lamp);
  font-size: var(--t-small);
  text-decoration: none;
}

.skip-link:focus {
  transform: translate(-50%, 0);
}

.wrap {
  width: min(100% - var(--gutter) * 2, var(--wrap));
  margin-inline: auto;
}

.wrap--narrow {
  width: min(100% - var(--gutter) * 2, var(--wrap-narrow));
}

.section {
  padding-block: var(--section-y);
}

/* - a section's first child gets its spacing from the section padding; its own
 *   top margin is for when it follows a heading, and here it would just stack */
.section > .wrap > :first-child {
  margin-top: 0;
}

.section--muted {
  background: var(--paper-sunken);
  border-block: 1px solid var(--line);
}

.section__title {
  font-size: var(--t-h2);
  max-width: var(--measure);
}

.section__more {
  margin-top: 2.5rem;
}

/* ── header ─────────────────────────────────────────────────────────── */

.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  border-bottom: 1px solid var(--line);
  background: rgba(250, 248, 244, 0.88);
  backdrop-filter: blur(10px);
}

.site-header__inner {
  width: min(100% - var(--gutter) * 2, var(--wrap));
  margin-inline: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1.5rem;
  padding-block: 0.875rem;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  text-decoration: none;
}

/* the same mark as the favicon, at the same colours */
.brand__mark {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--mark);
  box-shadow: 0 0 0 3px var(--mark-glow);
}

.brand__name {
  font-family: var(--font-display);
  font-size: 1.1875rem;
  font-weight: 700;
  letter-spacing: 0.06em;
}

.site-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.5rem;
  margin-left: auto;
}

.site-nav__list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1.5rem;
}

.site-nav__link {
  display: inline-block;
  padding-block: 0.25rem;
  font-size: var(--t-small);
  text-decoration: none;
  color: var(--muted);
  transition: color 0.15s ease;
}

.site-nav__link:hover,
.site-nav__link[aria-current="page"] {
  color: var(--ink);
}

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

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1rem;
  margin-top: 2.5rem;
}

.button {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.8rem 1.6rem;
  border: 1px solid var(--lamp-fill);
  border-radius: var(--radius);
  background: var(--lamp-fill);
  color: var(--on-lamp);
  font-size: var(--t-small);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-decoration: none;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.button:hover {
  background: var(--lamp-fill-hover);
  border-color: var(--lamp-fill-hover);
}

.button--ghost {
  background: transparent;
  border-color: var(--line-strong);
  color: var(--ink);
}

.button--ghost:hover {
  background: var(--wash);
  border-color: var(--ink);
}

.button--small {
  padding: 0.5rem 1.1rem;
  font-size: var(--t-label);
}

/* the running event, beside the download button but never mistaken for it */
.button--event {
  background: var(--fest);
  border-color: var(--fest);
  color: var(--on-fest);
}

.button--event:hover {
  background: var(--fest-hover);
  border-color: var(--fest-hover);
}

.textlink {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: var(--t-small);
  font-weight: 500;
  color: var(--lamp);
  text-decoration: none;
  border-bottom: 1px solid rgba(165, 95, 8, 0.4);
  padding-bottom: 1px;
  transition: border-color 0.15s ease;
}

.textlink:hover {
  border-bottom-color: var(--lamp);
}

.ext {
  font-size: 0.75em;
  opacity: 0.7;
}

/* ── video embeds ───────────────────────────────────────────────────── */

/* the original columns these per section (COL/12, COL/6, COL/4); the modifier
 * carries that rather than letting track width decide it */
.videos--one {
  max-width: var(--media);
  margin-inline: auto;
}

.videos {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  display: grid;
  gap: clamp(1.5rem, 3vw, 2rem);
}

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

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

.video__frame {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-sunken);
}

/* captions sit on the page's centre axis, like everything around them */
.video__title {
  margin-top: 0.75rem;
  text-align: center;
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: 0.06em;
  color: var(--muted);
}

/* ── hero ───────────────────────────────────────────────────────────── */

.hero {
  padding-top: clamp(3rem, 6vw, 5rem);
  padding-bottom: var(--section-y);
}

.hero__inner {
  text-align: center;
}

.hero__logo {
  /* its natural width, which is what the original serves it at */
  width: min(100%, 62.5rem);
  margin: 0 auto clamp(1.5rem, 4vw, 2.5rem);
}

.hero__title {
  /* capped so the headline holds one line at full width; it still wraps on narrow screens */
  font-size: clamp(1.75rem, 1.1rem + 2.4vw, 2.75rem);
  line-height: 1.5;
  text-wrap: balance;
}

.hero__subtitle {
  margin-top: 1.5rem;
  font-size: clamp(1rem, 0.95rem + 0.3vw, 1.1875rem);
}

.hero__body {
  margin-top: 0.5rem;
  font-size: var(--t-small);
  color: var(--muted);
}

.actions--centred {
  justify-content: center;
}

.hero__video {
  max-width: var(--media);
  margin-inline: auto;
  margin-top: clamp(2.5rem, 5vw, 4rem);
}

/* ── section headings, as on the original ───────────────────────────── */

.section__title--centred {
  max-width: none;
  text-align: center;
}

.section__bracket {
  margin-top: 1rem;
  text-align: center;
  font-family: var(--font-mono);
  font-size: var(--t-small);
  letter-spacing: 0.06em;
  color: var(--lamp);
}

.section__more--centred {
  display: flex;
  justify-content: center;
}

/* ── the two halves of the pitch ────────────────────────────────────── */

.vision__stage {
  max-width: var(--media);
  margin-inline: auto;
  width: 100%;
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.vision {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  display: grid;
  gap: clamp(2.5rem, 5vw, 3.5rem);
}

.vision__title {
  font-size: var(--t-h3);
}

.vision__figures {
  margin-top: 2rem;
  display: grid;
  gap: 1.25rem;
}

.shot {
  margin: 0;
}

.shot__image {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
}

.shot figcaption {
  margin-top: 0.625rem;
  text-align: center;
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: 0.06em;
  color: var(--muted);
}

@media (min-width: 52rem) {
  .vision,
  .vision__figures {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* ── tutorial ───────────────────────────────────────────────────────── */

.tutorial__banner {
  max-width: var(--media);
  margin-inline: auto;
  width: 100%;
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.videos--two {
  grid-template-columns: 1fr;
}

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

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

/* ── feature cards ──────────────────────────────────────────────────── */

.card__image {
  width: 100%;
  border-radius: var(--radius);
  border: 1px solid var(--line);
}

/* ── sample game ────────────────────────────────────────────────────── */

.sample {
  margin-top: clamp(3rem, 6vw, 4.5rem);
  padding-top: clamp(2.5rem, 5vw, 3.5rem);
  border-top: 1px solid var(--line);
  display: grid;
  gap: clamp(1.5rem, 4vw, 2.5rem);
  align-items: center;
}

.sample__image {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.sample__line {
  margin-top: 0.75rem;
  font-size: var(--t-small);
  color: var(--muted);
}

@media (min-width: 52rem) {
  .sample {
    grid-template-columns: 1fr 1fr;
  }
}

/* ── download page ──────────────────────────────────────────────────── */

.gallery {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  display: grid;
  gap: 1.25rem;
}

.gallery__image {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.shots {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  display: grid;
  gap: clamp(1.5rem, 3vw, 2.25rem);
}

/* body copy on this page is centred under a centred heading */
.prose--centred {
  margin: clamp(2rem, 4vw, 2.75rem) auto 0;
  max-width: 56em;
  text-align: center;
  /* the author hard-wraps this copy; the breaks are line breaks, not paragraph
   * breaks, so they get line-height and not the p + p gap */
  white-space: pre-line;
}

.prose--centred p {
  font-size: var(--t-small);
  color: var(--muted);
}

.prose--centred p + p {
  margin-top: 0.75rem;
}

/* the licence's own wrapping is what sets its measure, so it takes no cap */
.prose--terms {
  max-width: none;
}

/* the closing sign-off: four works in a row, as the original columns them */
.closers {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  display: grid;
  gap: 1.25rem;
}

/* the four sources are 16:9 give or take a pixel, so pin the ratio and let the
 * bottom edges line up rather than sitting a hair off each other */
.closers img {
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

@media (min-width: 42rem) {
  .gallery,
  .shots,
  .closers {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 64rem) {
  .closers {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* the three people behind the engine, one card each */
.people {
  display: grid;
  gap: clamp(2rem, 4vw, 2.75rem);
}

.person {
  display: flex;
  flex-direction: column;
}

/* the three source images are three different ratios, so the cards fix one and
 * letterbox rather than crop — one of them is a logo */
.person__image {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: contain;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.person__role {
  margin-top: 1.25rem;
  font-size: var(--t-h3);
}

.person__name {
  margin-top: 0.5rem;
  font-family: var(--font-mono);
  font-size: var(--t-small);
  letter-spacing: 0.06em;
  color: var(--lamp);
}

.person__line {
  margin-top: 0.5rem;
  font-size: var(--t-small);
  color: var(--muted);
}

/* the button sits on the card's baseline so a shorter card doesn't float it */
.person .actions {
  margin-top: auto;
  padding-top: 1.5rem;
  justify-content: flex-start;
}

/* headed sub-blocks, side by side as the original columns them */
.notes {
  margin-top: clamp(2rem, 4vw, 2.75rem);
  display: grid;
  gap: clamp(1.5rem, 3vw, 2.25rem);
}

.note__title {
  font-size: var(--t-h3);
}

.note__body {
  margin-top: 0.75rem;
  font-size: var(--t-small);
  color: var(--muted);
}

@media (min-width: 52rem) {
  .people {
    grid-template-columns: repeat(3, 1fr);
  }

  .notes {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* ── slider ─────────────────────────────────────────────────────────── */

/* a native scroll container, so swipe, trackpad and keyboard all work without
 * script; the arrows are plain anchors pointing at the neighbouring slide */
.slider {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  /* no scroll-behavior: smooth here — with mandatory snapping it cancels the
   * arrows' programmatic scrolls outright. Swipe is smooth natively anyway. */
  overscroll-behavior-x: contain;
  border-radius: var(--radius);
  /* the scrollbar is the affordance that there is more than one slide */
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
  padding-bottom: 0.75rem;
}

.slide {
  position: relative;
  flex: 0 0 100%;
  scroll-snap-align: center;
}

.slide__image {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.slider-group {
  position: relative;
}

/* hidden until slider.js reveals them, so no-JS never shows dead controls */
.slide__arrow {
  position: absolute;
  top: calc(50% - 0.75rem);
  translate: 0 -50%;
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(20, 18, 15, 0.55);
  color: #fff;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  backdrop-filter: blur(2px);
  transition: background 0.15s ease;
}

.slide__arrow[hidden] {
  display: none;
}

.slide__arrow:hover {
  background: rgba(20, 18, 15, 0.82);
}

.slide__arrow--prev {
  left: 0.75rem;
}

.slide__arrow--next {
  right: 0.75rem;
}

.slide__count {
  position: absolute;
  right: 0.75rem;
  bottom: 0.75rem;
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  background: rgba(20, 18, 15, 0.55);
  color: #fff;
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: 0.08em;
}

/* ── works carousel ─────────────────────────────────────────────────── */

.carousel {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  display: flex;
  gap: 1.25rem;
  overflow-x: auto;
  padding-bottom: 1.25rem;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
  overscroll-behavior-x: contain;
}

.work {
  flex: 0 0 clamp(15rem, 62vw, 18rem);
  scroll-snap-align: start;
}

.work__link {
  display: grid;
  gap: 0.625rem;
  text-decoration: none;
}

.work__cover {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: opacity 0.15s ease;
}

.work__link:hover .work__cover {
  opacity: 0.85;
}

.work__title {
  font-family: var(--font-display);
  font-size: 0.9375rem;
  font-weight: 700;
  line-height: 1.7;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.work__creator {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: 0.06em;
  color: var(--muted);
}

/* ── lists ──────────────────────────────────────────────────────────── */

.ticks {
  margin-top: 2rem;
  display: grid;
  gap: 0.75rem;
}

.ticks li {
  position: relative;
  padding-left: 1.5rem;
  font-size: var(--t-small);
  line-height: 1.85;
}

.ticks li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.85em;
  width: 0.5rem;
  height: 2px;
  background: var(--lamp-fill);
}

/* ── cards ──────────────────────────────────────────────────────────── */

/* - no rules between the cells: every card leads with an image, and the images
 *   separate them better than hairlines did back when these were text-only */
.cards {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  display: grid;
  gap: clamp(1.75rem, 3.5vw, 2.75rem);
}

.card {
  display: flex;
  flex-direction: column;
  gap: 0.875rem;
  align-items: flex-start;
}

.card__title {
  font-size: var(--t-h3);
}

.card__body {
  font-size: var(--t-small);
  color: var(--muted);
}

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

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


/* ── motion ─────────────────────────────────────────────────────────── */

/* Scroll reveal. The hidden state hangs off [data-reveal], which reveal.js puts
 * on <html> at parse time, so with the script absent, IntersectionObserver
 * missing, or reduced motion asked for, the attribute never appears and every
 * block renders normally. Nothing here can strand content invisible.
 * A CSS-only version using animation-timeline: view() was tried first and never
 * ran; see docs/tech-debt.md. */
@media (prefers-reduced-motion: no-preference) {
  [data-reveal] .section > .wrap > *,
  [data-reveal] .announce__inner > * {
    opacity: 0;
    transform: translateY(1.25rem);
  }

  /* mirrors the selectors above rather than shortening to [data-shown]: the
   * hidden rule scores (0,3,0) on the section branch, so a plain attribute
   * selector at (0,2,0) loses the cascade and the block never appears */
  [data-reveal] .section > .wrap > [data-shown],
  [data-reveal] .announce__inner > [data-shown] {
    opacity: 1;
    transform: none;
    transition:
      opacity 0.5s ease,
      transform 0.5s cubic-bezier(0.2, 0.6, 0.3, 1);
  }

  /* the two grids you actually point at */
  .card,
  .work {
    transition: transform 0.18s ease;
  }

  .card:hover,
  .work:hover {
    transform: translateY(-3px);
  }
}

/* ── quotes ─────────────────────────────────────────────────────────── */

.quotes {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  display: grid;
  gap: clamp(2rem, 4vw, 3rem);
}

.quote {
  margin: 0;
  padding-left: 1.5rem;
  border-left: 1px solid var(--line-strong);
}

.quote__body {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.0625rem;
  line-height: 2;
}

.quote__author {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  margin-top: 1.25rem;
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: 0.1em;
  color: var(--muted);
}

.quote__portrait {
  flex: none;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  border: 1px solid var(--line);
  object-fit: cover;
}

@media (min-width: 56rem) {
  .quotes {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* ── page header ────────────────────────────────────────────────────── */

.page-header {
  padding-top: clamp(3.5rem, 7vw, 5.5rem);
  padding-bottom: clamp(2rem, 4vw, 3rem);
  border-bottom: 1px solid var(--line);
}

.page-header__title {
  font-size: var(--t-h1);
}

.page-header__lead {
  margin-top: 1.25rem;
  /* wider than --measure: a one-sentence lead under an H1 should sit on one
   * line, and the longest of them needs 36.4em */
  max-width: 38em;
  color: var(--muted);
  white-space: pre-line;
}

/* ── features: formula ──────────────────────────────────────────────── */

.formula__goal {
  margin-top: 1.25rem;
  font-family: var(--font-mono);
  font-size: var(--t-small);
  letter-spacing: 0.06em;
  color: var(--lamp);
}

.formula {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  display: grid;
  gap: clamp(2rem, 4vw, 3rem);
}

/* the three screenshots are three different ratios, so pin one and letterbox
 * rather than crop: two of them are editor panes and lose meaning cropped */
.formula__image {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: contain;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  margin-bottom: 1.25rem;
}

@media (min-width: 52rem) {
  .formula {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* ── features: catalogue ────────────────────────────────────────────── */

.catalogue {
  display: grid;
  gap: clamp(3rem, 6vw, 4.5rem);
}

.catalogue__group {
  display: grid;
  gap: 1.5rem;
}

.catalogue__title {
  font-size: var(--t-h3);
  padding-top: 0.35rem;
  border-top: 2px solid var(--lamp-fill);
}

.catalogue__list {
  display: grid;
  gap: 1.25rem;
}

.catalogue__item {
  padding-bottom: 1.25rem;
  border-bottom: 1px solid var(--line);
}

.catalogue__text {
  display: block;
  font-size: var(--t-small);
  font-weight: 500;
}

.catalogue__note {
  margin-top: 0.5rem;
  max-width: var(--measure);
  font-size: var(--t-label);
  line-height: 1.9;
  color: var(--muted);
}

@media (min-width: 56rem) {
  .catalogue__group {
    grid-template-columns: 14rem 1fr;
    gap: clamp(2rem, 4vw, 3rem);
  }

  .catalogue__title {
    position: sticky;
    top: 5rem;
    align-self: start;
  }
}

/* ── start: steps ───────────────────────────────────────────────────── */

.steps {
  display: grid;
  gap: clamp(2.5rem, 5vw, 4rem);
}

.step {
  display: grid;
  gap: 1rem;
}

.step__index {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  font-weight: 500;
  letter-spacing: 0.2em;
  color: var(--lamp);
}

.step__body {
  display: grid;
  gap: 1rem;
  max-width: var(--measure);
}

.step__title {
  font-size: var(--t-h3);
}

.step__body p {
  font-size: var(--t-small);
  color: var(--muted);
}

.step__links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.5rem;
  margin-top: 0.5rem;
}

.step__image {
  width: 100%;
  margin-top: 0.75rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

@media (min-width: 52rem) {
  .step {
    grid-template-columns: 5rem 1fr;
    gap: 0 clamp(2rem, 4vw, 3rem);
  }

  .step__index {
    padding-top: 0.5rem;
    border-top: 2px solid var(--lamp-fill);
  }
}

/* ── support: rows ──────────────────────────────────────────────────── */

.rows {
  display: grid;
  gap: clamp(1.5rem, 3vw, 2.25rem);
}

.row {
  display: grid;
  gap: 0.75rem;
  justify-items: start;
  padding-bottom: clamp(1.5rem, 3vw, 2.25rem);
  border-bottom: 1px solid var(--line);
  max-width: var(--wrap-narrow);
}

.rows li:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}

.row__image {
  width: 100%;
  max-width: 28rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.row__copy {
  display: grid;
  gap: 0.75rem;
  justify-items: start;
}

.row__title {
  font-size: var(--t-h3);
}

.row__body {
  font-size: var(--t-small);
  color: var(--muted);
  /* same hard-wrapping convention as the download page's prose */
  white-space: pre-line;
}

/* a solid button needs more air under the copy than a text link does */
.row__copy .button {
  margin-top: 0.5rem;
}

/* the two rows that carry media run the full measure rather than the narrow one */
.row--split,
.row--wide {
  max-width: none;
}

/* the works behind the system-support claim */
.row__grid {
  /* .row sets justify-items: start, which would shrink this to its content */
  width: 100%;
  margin-top: clamp(1.75rem, 3.5vw, 2.5rem);
  display: grid;
  gap: 1rem;
}

.row__shot {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

@media (min-width: 42rem) {
  .row__grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 52rem) {
  /* image one side, copy the other — the page's one two-column row */
  .row--split {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: clamp(1.5rem, 4vw, 2.5rem);
    align-items: center;
    justify-items: stretch;
  }

  /* the image fills its column here rather than keeping the stacked-row cap */
  .row--split .row__image {
    max-width: none;
  }
}

@media (min-width: 64rem) {
  .row__grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* ── story: the essay ───────────────────────────────────────────────── */

.essay__header {
  padding-top: clamp(3.5rem, 7vw, 6rem);
  padding-bottom: clamp(2rem, 4vw, 3rem);
  text-align: center;
}

.essay__title {
  font-size: var(--t-h1);
  letter-spacing: 0.12em;
}

.essay__subtitle {
  margin-top: 1rem;
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: 0.16em;
  color: var(--muted);
}

.essay__lead-image {
  width: min(100% - var(--gutter) * 2, var(--wrap));
  margin: 0 auto clamp(3rem, 6vw, 4.5rem);
  border-radius: var(--radius);
}

.essay__body {
  padding-bottom: var(--section-y);
  font-family: var(--font-display);
  font-size: 1.0625rem;
  line-height: 2.15;
}

.essay__body p {
  margin-block: 2em;
}

.essay__body img {
  width: 100%;
  margin-block: 3em;
  border-radius: var(--radius);
}

.essay__body blockquote {
  margin: 3em 0;
  padding: 0;
  text-align: center;
  font-size: 1.1875rem;
  color: var(--lamp);
}

.essay__body blockquote p {
  margin: 0;
}

.essay__author {
  margin-top: 4em;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: 0.14em;
  color: var(--muted);
  text-align: right;
}

/* ── closing ────────────────────────────────────────────────────────── */

.closing {
  border-top: 1px solid var(--line);
}

.closing__title {
  font-size: var(--t-h2);
}

.closing__body {
  margin-top: 1.25rem;
  max-width: var(--measure);
  color: var(--muted);
}

.notfound {
  padding-block: clamp(5rem, 12vw, 9rem);
}

/* ── footer ─────────────────────────────────────────────────────────── */

/* the floor of the page, not another section — so it sits off the paper/muted
 * alternation and stays right whatever section happens to land above it.
 * Redefining the four tokens here re-colours every rule below without touching them. */
/* ── home: the running contest ───────────────────────────────────────── */

/* same four-token override the footer uses, so amber reads as amber on dark
 * and the bar cannot be mistaken for another section--muted band */
.announce {
  --ink: #f2efe7;
  /* lighter than the site's --muted: over the art the usual grey measures 3.18
   * against the worst patch, this measures 5.20 */
  --muted: #cfc8bb;
  --line: rgba(242, 239, 231, 0.14);
  --lamp: #f0b458;

  padding-block: clamp(2rem, 4vw, 2.75rem);
  /* the key art under a flat wash. Stacked, the copy runs the whole width, so
   * the overlay has to hold everywhere: 86% puts the worst patch at ink 8.41 /
   * muted 5.81. The wide layout relaxes it into a gradient below. */
  background:
    linear-gradient(rgba(42, 38, 32, 0.86), rgba(42, 38, 32, 0.86)),
    var(--paper-sunken) url(../img/contest-bg.webp) center / cover no-repeat;
  color: var(--ink);
}

.announce__inner {
  display: grid;
  gap: clamp(1.25rem, 3vw, 2rem);
  align-items: center;
}

/* the name keeps the left edge it shares with the lines below; the pill trails it */
.announce__head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
}

.announce__pill {
  padding: 0.25rem 0.7rem;
  border-radius: 999px;
  background: var(--fest);
  color: var(--on-fest);
  font-family: var(--font-mono);
  font-size: var(--t-label);
  font-weight: 700;
  letter-spacing: 0.08em;
}

.announce__title {
  font-size: var(--t-h3);
}

/* what the contest is, then when it closes — the perishable line is quietest */
.announce__subtitle {
  margin-top: 0.5rem;
  font-size: var(--t-small);
}

.announce__body {
  margin-top: 0.35rem;
  font-size: var(--t-small);
  color: var(--muted);
}

/* the button keeps the light-ground amber; it is legible on this ground too.
 * justify-self so the grid does not stretch it full width when it stacks */
.announce .button {
  justify-self: start;
  white-space: nowrap;
}

@media (min-width: 46rem) {
  .announce__inner {
    grid-template-columns: 1fr auto;
  }

  /* side by side, the copy only occupies the left, so the wash can ease off
   * over the artwork on the right. Worst ink under the text there: 7.53. */
  .announce {
    background:
      linear-gradient(
        90deg,
        rgba(42, 38, 32, 0.93) 0%,
        rgba(42, 38, 32, 0.93) 45%,
        rgba(42, 38, 32, 0.55) 100%
      ),
      var(--paper-sunken) url(../img/contest-bg.webp) center / cover no-repeat;
  }
}

.site-footer {
  --ink: #f2efe7;
  --muted: #a49c8d;
  --line: rgba(242, 239, 231, 0.14);
  --lamp: #f0b458;

  padding-block: clamp(3rem, 6vw, 4.5rem) 2.5rem;
  background: #2a2620;
  color: var(--ink);
}

.site-footer__inner {
  width: min(100% - var(--gutter) * 2, var(--wrap));
  margin-inline: auto;
  display: grid;
  gap: clamp(2rem, 4vw, 3rem);
}

.site-footer__tagline {
  margin-top: 0.75rem;
  max-width: 20em;
  font-family: var(--font-display);
  font-size: var(--t-small);
  color: var(--muted);
}

.site-footer__heading {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  font-weight: 500;
  letter-spacing: 0.16em;
  color: var(--muted);
}

.site-footer__group ul {
  margin-top: 1rem;
  display: grid;
  gap: 0.625rem;
}

.site-footer__link {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: var(--t-small);
  text-decoration: none;
  color: var(--ink);
  transition: color 0.15s ease;
}

.site-footer__link:hover {
  color: var(--lamp);
}

.site-footer__legal {
  width: min(100% - var(--gutter) * 2, var(--wrap));
  margin: clamp(3rem, 6vw, 4.5rem) auto 0;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: 0.08em;
  color: var(--muted);
}

.site-footer__legal a {
  color: inherit;
  text-decoration: none;
}

.site-footer__legal a:hover {
  color: var(--ink);
}

@media (min-width: 56rem) {
  .site-footer__inner {
    grid-template-columns: 1.4fr repeat(3, 1fr);
  }
}

/* ── motion ─────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
  }
}
