/* ── tokens.css ────────────────────────────────── */
/*
 * Design tokens — the single place to restyle the site.
 *
 * These are the ACTUAL values from the live WordPress site (Twenty Seventeen with the
 * `colors-dark` scheme), read out of the theme stylesheet archived at
 * _import/reference/theme.css. The scheme is deliberately neutral and monochrome —
 * grey on near-black, white for emphasis — so the hand-tinted screenshots carry all
 * the colour. Do not "warm it up": that changes the design, it does not port it.
 *
 * Change a value here and it propagates everywhere. Nothing below this file should
 * contain a raw colour or font name.
 */

:root {
  /* ── Colour (Twenty Seventeen, colors-dark) ─────────── */
  --color-bg: #222; /* page background */
  --color-bg-raised: #262626; /* site header */
  --color-bg-deep: #1a1a1a; /* footer bar */

  --color-text: #eee; /* body copy */
  --color-heading: #ddd; /* headings, titles, links at rest */
  --color-text-muted: #bbb; /* meta, captions, site description */
  --color-text-faint: #666; /* icons, dividers */

  /* Emphasis in this theme is lightness, not hue: links brighten to white. */
  --color-accent: #fff;
  --color-accent-hover: #fff;

  /* Buttons invert — white block, dark label. */
  --color-primary: #fff;
  --color-primary-dark: #bbb;
  --color-on-primary: #222;

  --color-border: #333;
  --color-border-strong: #444;
  --color-focus: #fff;

  --color-overlay: rgb(0 0 0 / 30%); /* gradient over panel images */

  /* ── Typography ─────────────────────────────────────── */
  /* Girassol for every heading and title; Philosopher for everything else. */
  --font-display: "Girassol", "Palatino Linotype", Palatino, serif;
  --font-ui: "Philosopher", Georgia, serif;
  --font-body: "Philosopher", Georgia, serif;

  --text-base: 1.0625rem;
  --text-sm: 0.9375rem;
  --text-xs: 0.8125rem;

  --text-h1: 2.5rem;
  --text-h2: 2rem;
  --text-h3: 1.5rem;
  --text-h4: 1.25rem;
  --text-h5: 1.125rem;
  --text-h6: 1rem;

  --leading-body: 1.75;
  --leading-tight: 1.25;
  --tracking-display: 0.01em;

  /* ── Space ──────────────────────────────────────────── */
  --space-2xs: 0.25rem;
  --space-xs: 0.5rem;
  --space-sm: 0.75rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --space-xl: 2.5rem;
  --space-2xl: 4rem;
  --space-3xl: 6rem;

  /* ── Layout ─────────────────────────────────────────── */
  --wrap-max: 1100px;
  --wrap-narrow: 700px;
  --gutter: var(--space-lg);
  --header-height: 76px;
  --panel-image-height: clamp(240px, 56vw, 700px);

  /* ── Effects ────────────────────────────────────────── */
  --radius: 3px;
  --radius-lg: 6px;
  --shadow: 0 6px 28px rgb(0 0 0 / 55%);
  --transition: 180ms ease;
}

@media (min-width: 48rem) {
  :root {
    --gutter: var(--space-xl);
    --text-h1: 3.25rem;
    --text-h2: 2.5rem;
  }
}


/* ── base.css ────────────────────────────────── */
/* Element defaults. No component styling here. */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Anchored sections must clear the fixed header. */
  scroll-padding-top: calc(var(--header-height) + var(--space-md));
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--text-base);
  /*
   * 400, not 300. Philosopher is a serif, and light serif strokes bloom and thin out
   * against a near-black background — the page read as washed out at 17px.
   */
  font-weight: 400;
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
}

body.nav-open {
  overflow: hidden;
}

h1,
h2,
h3,
h4,
h5 {
  margin: 0 0 var(--space-md);
  color: var(--color-heading);
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: var(--tracking-display);
  line-height: var(--leading-tight);
}

h1 { font-size: var(--text-h1); }
h2 { font-size: var(--text-h2); }
h3 { font-size: var(--text-h3); }
h4 { font-size: var(--text-h4); }
h5 { font-size: var(--text-h5); }

h6 {
  margin: 0 0 var(--space-md);
  color: var(--color-heading);
  font-family: var(--font-ui);
  font-size: var(--text-h6);
  line-height: var(--leading-tight);
}

p {
  margin: 0 0 var(--space-md);
}

a {
  color: var(--color-accent);
  text-decoration: none;
  transition: color var(--transition);
}

a:hover,
a:focus-visible {
  color: var(--color-accent-hover);
  text-decoration: underline;
}

/*
 * Links inside running text are underlined at rest.
 *
 * This theme signals emphasis with lightness, not hue: body copy is #eee and links
 * are #fff. That is a contrast ratio of 1.16:1 between a link and the words around
 * it — for practical purposes invisible, and a WCAG 1.4.1 failure, since colour was
 * the only thing distinguishing them. Twenty Seventeen underlines its own body
 * links; leaving them bare was a porting slip, not a design decision.
 *
 * Chrome links (nav, footer bar, buttons) stay undecorated: they sit alone, so there
 * is nothing to tell them apart from.
 */
.prose a,
.press__section a:not(.btn),
.contact__email a,
.post-list__excerpt a {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}

.prose a:hover,
.prose a:focus-visible,
.press__section a:not(.btn):hover,
.press__section a:not(.btn):focus-visible,
.contact__email a:hover,
.contact__email a:focus-visible {
  text-decoration-thickness: 2px;
}

/* An underline under a picture is just a stray line. */
.prose a:has(img),
.press__section a:has(img),
[data-gallery] a {
  text-decoration: none;
}

:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

img,
svg,
video {
  max-width: 100%;
  height: auto;
  vertical-align: middle;
}

figure {
  margin: 0 0 var(--space-lg);
}

figure img {
  border-radius: var(--radius);
}

blockquote {
  margin: 0 0 var(--space-lg);
  padding: var(--space-sm) 0 var(--space-sm) var(--space-lg);
  border-left: 3px solid var(--color-primary);
  color: var(--color-text-muted);
  font-style: italic;
}

blockquote p:last-child {
  margin-bottom: 0;
}

hr,
.rule {
  height: 1px;
  margin: var(--space-xl) 0;
  border: 0;
  background: linear-gradient(
    to right,
    transparent,
    var(--color-border) 20%,
    var(--color-border) 80%,
    transparent
  );
}

ul,
ol {
  margin: 0 0 var(--space-md);
  padding-left: 1.25em;
}

li {
  margin-bottom: var(--space-2xs);
}

code,
pre {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.9em;
}

table {
  width: 100%;
  margin-bottom: var(--space-lg);
  border-collapse: collapse;
}

th,
td {
  padding: var(--space-xs) var(--space-sm);
  border-bottom: 1px solid var(--color-border);
  text-align: left;
}

/* Visually hidden but available to assistive technology. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/*
 * Legacy WordPress anchor targets (#contactForm, #mc4wp-form-1). Zero-size markers
 * that keep old inbound links scrolling to the right place.
 */
.anchor-alias {
  display: block;
  position: relative;
  top: calc(var(--header-height) * -1);
  visibility: hidden;
}

.skip-link {
  position: absolute;
  top: -100vh;
  left: var(--space-md);
  z-index: 200;
  padding: var(--space-xs) var(--space-md);
  background: var(--color-primary);
  /* `#fff` here put white text on the white block — the first thing a keyboard
     user reaches, and it was unreadable. */
  color: var(--color-on-primary);
  border-radius: 0 0 var(--radius) var(--radius);
}

.skip-link:focus {
  top: 0;
}


/* ── layout.css ────────────────────────────────── */
/* Page scaffolding: wrapper, header, sections, footer skeleton. */

.wrap {
  width: 100%;
  max-width: var(--wrap-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section {
  padding-block: var(--space-2xl);
}

/*
 * One heading treatment site-wide. This used to add `text-transform: uppercase`,
 * which gave the site two competing heading styles — Girassol is already a
 * small-caps display face, so forcing caps on it fights the page and panel titles
 * set in the same face right above it.
 */
.section-heading {
  margin-bottom: var(--space-lg);
  color: var(--color-primary);
  font-size: var(--text-h2);
}

/* ── Header ───────────────────────────────────────────── */

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background-color: var(--color-bg-deep);
  border-bottom: 1px solid var(--color-border);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  min-height: var(--header-height);
}

.site-header__brand {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  color: inherit;
  text-decoration: none;
}

.site-header__brand:hover,
.site-header__brand:focus-visible {
  text-decoration: none;
}

.site-header__logo {
  width: auto;
  height: 52px;
}

.site-header__text {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
}

.site-title {
  color: var(--color-heading);
  font-family: var(--font-ui);
  font-size: 1.25rem;
  font-weight: 700;
}

.site-tagline {
  color: var(--color-text-muted);
  font-family: var(--font-ui);
  font-size: var(--text-xs);
}

/* ── Navigation ───────────────────────────────────────── */

.site-nav__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  /* Tighter than the old --space-lg to pay for the links' new vertical padding, so
     the header bar is the same height as before. */
  gap: var(--space-md);
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-nav__item {
  margin: 0;
}

.site-nav__link {
  display: inline-block;
  /* The bare text box was 19px tall — under the 24px WCAG 2.5.8 minimum. The mobile
     menu was already fine; this is the desktop bar. */
  padding-block: var(--space-2xs);
  color: var(--color-text);
  font-family: var(--font-ui);
  font-size: 1.0625rem;
  text-decoration: none;
  white-space: nowrap;
}

.site-nav__link:hover,
.site-nav__link:focus-visible {
  color: var(--color-accent-hover);
  text-decoration: none;
}

.site-nav__link.is-current {
  color: var(--color-accent);
}

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 44px;
  height: 44px;
  padding: 0 10px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: transparent;
  cursor: pointer;
}

.nav-toggle__bar {
  display: block;
  height: 2px;
  background-color: var(--color-text);
  transition: transform var(--transition), opacity var(--transition);
}

/*
 * The open state. This transition existed but nothing ever triggered it: the
 * hamburger stayed a hamburger with the menu open, so there was no visible way to
 * close it. The bars are 2px with a 5px gap, so each end bar travels 7px to meet
 * the middle one.
 */
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:first-child {
  transform: translateY(7px) rotate(45deg);
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(2) {
  opacity: 0;
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bar:last-child {
  transform: translateY(-7px) rotate(-45deg);
}

/* ── Page bodies ──────────────────────────────────────── */

.page-header {
  padding-block: var(--space-xl) var(--space-md);
}

.page-title {
  margin: 0;
  color: var(--color-primary);
}

.post-meta {
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

.prose {
  max-width: var(--wrap-narrow);
}

.prose > :last-child {
  margin-bottom: 0;
}

.page-body {
  padding-bottom: var(--space-2xl);
}

/* Wide pages (legal text, long game write-ups) read better with a measure cap,
   but images inside them may still run full width. */
.prose img {
  border-radius: var(--radius);
}

/* ── Footer ───────────────────────────────────────────── */

.site-footer {
  position: relative;
  margin-top: var(--space-2xl);
  padding-top: var(--space-2xl);
  background-color: var(--color-bg);
  border-top: 1px solid var(--color-border);
  color: var(--color-text-muted);
}

.site-footer h2 {
  color: var(--color-primary);
}

/*
 * The exclusions are load-bearing. `.site-footer a` (0,1,1) outranks a plain
 * component class like `.btn` (0,1,0), so this rule used to repaint anything inside
 * the footer that carries its own colour:
 *
 *   .btn              → white label on the white Steam button; invisible
 *   .to-top           → white arrow on the white disc; invisible
 *   .social-list__link → overrode the pill's own text colour
 *
 * Anything added to the footer with its own colour must be excluded here too.
 */
.site-footer a:not(.btn, .to-top, .social-list__link) {
  color: var(--color-accent);
}

.site-footer a:not(.btn, .to-top, .social-list__link):hover,
.site-footer a:not(.btn, .to-top, .social-list__link):focus-visible {
  color: var(--color-accent-hover);
}

.site-footer__bar {
  margin-top: var(--space-xl);
  padding-block: var(--space-md);
  background-color: var(--color-bg-deep);
}

.site-footer__bar-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
}

.site-footer__copyright {
  margin: 0;
  font-size: var(--text-sm);
}

.footer-nav__list {
  display: flex;
  gap: var(--space-lg);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-sm);
}

.footer-nav__list li {
  margin: 0;
}

.to-top {
  position: fixed;
  right: var(--space-md);
  bottom: var(--space-md);
  z-index: 90;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  background-color: var(--color-primary);
  border-radius: 50%;
  /* The button inverts, like .btn. `#fff` here put a white arrow on a white disc. */
  color: var(--color-on-primary);
  font-size: 1.25rem;
  text-decoration: none;
  /*
   * Hidden until the visitor has scrolled a screen, and hidden again once the footer
   * is in view — it used to sit over the opening paragraph and cover the footer's
   * legal link. `site.js` toggles .is-visible.
   */
  opacity: 0.85;
  /*
   * `visibility` is deliberately NOT transitioned. Transitioning it holds the old
   * value for the whole duration and only flips at the end, so the property gets
   * stuck whenever the animation does not run to completion. Fade the opacity;
   * switch the visibility outright.
   */
  transition: opacity var(--transition), background-color var(--transition);
}

/*
 * Hidden by default only when JavaScript is running, because site.js is what brings
 * it back. Without the `.js` flag (set in head.njk before first paint) the button
 * stays permanently visible — the behaviour this site had before, and the right
 * fallback for a page that is meant to work with scripting off.
 */
.js .to-top {
  opacity: 0;
  visibility: hidden;
}

.js .to-top.is-visible {
  opacity: 0.85;
  visibility: visible;
}

.to-top:hover,
.to-top:focus-visible {
  background-color: var(--color-primary-dark);
  color: var(--color-on-primary);
  opacity: 1;
  text-decoration: none;
}

/* Keyboard users must be able to reach it even before the scroll threshold. */
.to-top:focus-visible {
  visibility: visible;
}

/* ── Small screens ────────────────────────────────────── */

@media (max-width: 61.99rem) {
  .nav-toggle {
    display: flex;
  }

  .site-nav {
    position: fixed;
    inset: var(--header-height) 0 0;
    z-index: 99;
    display: none;
    padding: var(--space-lg) var(--gutter);
    background-color: var(--color-bg-deep);
    overflow-y: auto;
  }

  .site-nav.is-open {
    display: block;
  }

  .site-nav__list {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
  }

  .site-nav__item {
    border-bottom: 1px solid var(--color-border);
  }

  .site-nav__link {
    display: block;
    padding-block: var(--space-md);
    font-size: 1.125rem;
  }

  .site-tagline {
    display: none;
  }
}

@media (max-width: 30rem) {
  .site-title {
    font-size: 1rem;
  }

  .site-header__logo {
    height: 40px;
  }
}


/* ── components.css ────────────────────────────────── */
/* Reusable pieces: buttons, hero, panels, archives, press kit, lists. */

/* ── Buttons ──────────────────────────────────────────── */

/* This theme inverts buttons: a white block with a dark label. */
.btn {
  display: inline-block;
  padding: var(--space-sm) var(--space-xl);
  background-color: var(--color-primary);
  border: 2px solid var(--color-primary);
  border-radius: var(--radius);
  color: var(--color-on-primary);
  font-family: var(--font-ui);
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-decoration: none;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color var(--transition), border-color var(--transition),
    color var(--transition);
}

.btn:hover,
.btn:focus-visible {
  background-color: var(--color-primary-dark);
  border-color: var(--color-primary-dark);
  color: var(--color-on-primary);
  text-decoration: none;
}

.btn--ghost {
  background-color: transparent;
  color: var(--color-heading);
  border-color: var(--color-border-strong);
}

.btn--ghost:hover,
.btn--ghost:focus-visible {
  background-color: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-on-primary);
}

/* ── Site header image ────────────────────────────────── */

/*
 * The header image is a COVER CROP, not a picture placed on the page.
 *
 * Twenty Seventeen gave the front page `height: 100vh` with the image at
 * `object-fit: cover`, and hung the site title off the bottom edge. The port had
 * `max-height: 80vh` with no height, which means `object-fit` never engaged: the
 * image simply drew at its own 5:3 proportions and the page opened with a large
 * letterboxed screenshot instead of a filled banner.
 *
 * The art is a 2000x1200 Steam capsule, so a literal 100vh crop on a phone would
 * show a narrow vertical slice of it. Full height on wide screens, a 3:2 band on
 * narrow ones.
 */
.site-hero {
  position: relative;
  aspect-ratio: 3 / 2;
  background-color: var(--color-bg-deep);
}

@media (min-width: 48rem) {
  .site-hero {
    aspect-ratio: auto;
    height: min(100vh, 860px);
  }
}

.site-hero__image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
}

.site-hero__overlay {
  position: absolute;
  inset: auto 0 0;
  padding: var(--space-2xl) var(--gutter) var(--space-xl);
  background: linear-gradient(to top, rgb(0 0 0 / 78%), transparent);
  text-align: center;
}

.site-hero__title {
  margin: 0;
  color: #fff;
  font-family: var(--font-display);
  font-size: clamp(2rem, 7vw, 4.5rem);
  font-weight: 400;
  letter-spacing: var(--tracking-display);
  line-height: 1.05;
}

.site-hero__tagline {
  margin: var(--space-2xs) 0 0;
  color: var(--color-text-muted);
  font-family: var(--font-ui);
  font-size: clamp(1rem, 2.5vw, 1.5rem);
}

/* ── Front-page panels ────────────────────────────────── */

/*
 * Reproduces the WordPress front page: each panel is a full-bleed cover image,
 * followed by that panel's content.
 *
 * The original used background-attachment: fixed for a parallax effect. It is gone,
 * deliberately. A fixed background is sized to the VIEWPORT, not to the element, so
 * the panel only ever showed an arbitrary window onto the artwork — the intro panel
 * rendered as a band of legs and feet with every face cropped away. Scrolling the
 * image with the page costs a parallax effect and buys back the picture.
 */

/*
 * With the screenshots gone the panels have to carry their own rhythm, or the front
 * page is one unbroken column of text. Alternating ground plus a rule under each
 * title does that without inventing decoration the studio never supplied.
 */
.panel {
  border-bottom: 1px solid var(--color-border);
}

.panel:nth-of-type(even) {
  background-color: var(--color-bg-raised);
}

.panel__title::after {
  content: "";
  display: block;
  width: 4rem;
  height: 2px;
  margin-top: var(--space-sm);
  background-color: var(--color-text-faint);
}

.panel__image {
  position: relative;
  /* 16:9, which is the ratio the live theme reserved (`padding-top: 56.25%`). */
  aspect-ratio: 16 / 9;
  max-height: var(--panel-image-height);
  background-color: var(--color-bg-deep);
  background-image: var(--panel-image);
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
}

/*
 * The gradient has to land on the page background, not on 30% black. Ending it at
 * `--color-overlay` left every panel image stopping at a hard horizontal seam where
 * the picture met the #222 page.
 */
.panel__image::after {
  content: "";
  position: absolute;
  inset: 25% 0 0;
  background: linear-gradient(to bottom, rgb(0 0 0 / 0%) 0%, var(--color-bg) 100%);
}

.panel__content {
  padding-block: var(--space-3xl);
}

.panel__title {
  margin-bottom: var(--space-lg);
  color: var(--color-heading);
  font-family: var(--font-display);
  font-size: var(--text-h2);
  font-weight: 400;
  letter-spacing: var(--tracking-display);
  line-height: var(--leading-tight);
}

.panel__title a {
  color: inherit;
}

/* These titles link to the panel's own page. Say so on hover — at rest they are
   indistinguishable from an ordinary heading. */
.panel__title a:hover,
.panel__title a:focus-visible {
  color: var(--color-accent);
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.12em;
}

/* The statement that opens the page: narrower measure, easier to read at length. */
#statement .prose {
  max-width: 46rem;
}

/* Its closing line stands alone. */
#statement .prose > p:last-child {
  margin-top: var(--space-xl);
  color: var(--color-accent);
  font-family: var(--font-display);
  font-size: var(--text-h3);
}

.section-heading a {
  color: inherit;
}

.section-heading a:hover,
.section-heading a:focus-visible {
  color: var(--color-accent);
  text-decoration: none;
}

/* ── Archives ─────────────────────────────────────────── */

.archive-kind {
  margin: 0 0 var(--space-2xs);
  color: var(--color-text-muted);
  font-family: var(--font-ui);
  font-size: var(--text-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/*
 * The featured image. Every post on the live site had one, printed full width above
 * the title on the post and as a thumbnail in the archives. The port rendered none
 * of them until `npm run audit:elements` said so.
 */
.post-featured {
  margin-bottom: var(--space-xl);
  background-color: var(--color-bg-deep);
}

.post-featured img {
  display: block;
  width: 100%;
  max-height: 60vh;
  object-fit: cover;
}

.post-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Decorative in the listing: the title next to it is the real link. */
.post-list__thumb {
  display: block;
  float: right;
  width: 140px;
  margin: 0 0 var(--space-sm) var(--space-lg);
}

.post-list__thumb img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-radius: var(--radius);
}

.post-list__item::after {
  content: "";
  display: block;
  clear: both;
}

@media (max-width: 34rem) {
  .post-list__thumb {
    float: none;
    width: 100%;
    margin: 0 0 var(--space-sm);
  }
}

/* ── Post taxonomy footer ─────────────────────────────── */

/*
 * Categories and tags. The live post pages ended with these, and they were the only
 * route from a post into the category and tag archives — without them those archives
 * were built but unreachable.
 */
.post-taxonomy {
  padding-top: var(--space-lg);
  border-top: 1px solid var(--color-border);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
}

.post-taxonomy__row {
  margin-bottom: var(--space-2xs);
}

/*
 * Categories and tags sit in one paragraph so the text matches the live site exactly
 * (see post.njk). They are told apart visually with a separator, not a label — the
 * labels are screen-reader only, as they were on the live site.
 */
.post-taxonomy__group + .post-taxonomy__group::before {
  content: "·";
  margin: 0 var(--space-xs);
  color: var(--color-text-faint);
}

.post-taxonomy a {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}

.post-list__item {
  margin: 0;
  padding-block: var(--space-lg);
  border-bottom: 1px solid var(--color-border);
}

.post-list__title {
  margin-bottom: var(--space-2xs);
  font-family: var(--font-display);
  font-size: var(--text-h3);
  font-weight: 400;
}

.post-list__title a {
  color: var(--color-heading);
}

.post-list__title a:hover,
.post-list__title a:focus-visible {
  color: var(--color-accent);
  text-decoration: none;
}

.post-list__excerpt {
  margin-bottom: var(--space-xs);
  color: var(--color-text-muted);
}

/* The live site's per-post "read more" wording. It had no styling at all and read as
   a leftover line of prose. */
.post-list__more {
  margin-bottom: 0;
  font-family: var(--font-ui);
  font-size: var(--text-sm);
}

.post-list__more a {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}

.post-list__more a:hover,
.post-list__more a:focus-visible {
  text-decoration-thickness: 2px;
}

.back-link {
  margin-top: var(--space-xl);
}

/* ── Press kit ────────────────────────────────────────── */

.press__section {
  padding-block: var(--space-xl);
  border-bottom: 1px solid var(--color-border);
}

.press__section:last-child {
  border-bottom: 0;
}

.factsheet {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--space-xs) var(--space-lg);
  margin: 0;
  max-width: var(--wrap-narrow);
}

.factsheet dt {
  color: var(--color-text-muted);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
}

.factsheet dd {
  margin: 0;
}

.press__features,
.press__credits,
.press__contacts,
.press__trailers {
  margin: 0;
  padding: 0;
  list-style: none;
}

.press__features li {
  position: relative;
  margin-bottom: var(--space-sm);
  padding-left: var(--space-lg);
  max-width: var(--wrap-narrow);
}

.press__features li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.65em;
  width: 6px;
  height: 6px;
  background-color: var(--color-text-faint);
  border-radius: 50%;
}

.press__credits li,
.press__contacts li {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-md);
  padding-block: var(--space-xs);
  border-bottom: 1px solid var(--color-border);
}

.press__person,
.press__contact-name {
  min-width: 14rem;
  font-family: var(--font-ui);
  color: var(--color-heading);
}

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

.press__trailers li {
  margin-bottom: var(--space-lg);
}

.press__trailers h3 {
  font-family: var(--font-display);
  font-weight: 400;
}

/*
 * Embeds: video, the Steam widget, the Discord widget.
 *
 * Each keeps its own proportions — a 16:9 box is right for a trailer and wrong for a
 * 646x190 store widget or a 350x500 Discord panel, which would be stretched into
 * nonsense by a single shared ratio.
 */
.embed {
  position: relative;
  margin-block: var(--space-lg);
  max-width: 900px;
  background-color: var(--color-bg-deep);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  overflow: hidden;
}

.embed iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* Video: 16:9, as wide as the measure allows. */
.embed--video {
  aspect-ratio: 16 / 9;
}

/* Steam's store widget is a fixed 646x190 strip. */
.embed--steam {
  aspect-ratio: 646 / 190;
  max-width: 646px;
}

/*
 * Discord's widget is a tall panel with its own internal scrolling, so it gets a
 * real height rather than a ratio — squashing it hides the member list.
 */
.embed--discord {
  aspect-ratio: auto;
  height: 500px;
  max-width: 350px;
}

/* Anything without a modifier — a bare imported iframe — falls back to 16:9. */
.embed:not([class*="embed--"]) {
  aspect-ratio: 16 / 9;
}

.press__images {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr));
  gap: var(--space-md);
  margin: 0;
  padding: 0;
  list-style: none;
}

.press__images li {
  margin: 0;
}

.press__images img {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  transition: border-color var(--transition);
}

.press__images a:hover img,
.press__images a:focus-visible img {
  border-color: var(--color-accent);
}

/* ── Social ───────────────────────────────────────────── */

.social-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin: var(--space-md) 0 0;
  padding: 0;
  list-style: none;
}

.social-list__item {
  margin: 0;
}

.social-list__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-xs) var(--space-md) var(--space-xs) var(--space-sm);
  background-color: var(--color-bg-raised);
  border: 1px solid var(--color-border);
  border-radius: 999px;
  color: var(--color-text);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  text-decoration: none;
  transition: border-color var(--transition), color var(--transition);
}

.social-list__link:hover,
.social-list__link:focus-visible {
  border-color: var(--color-accent);
  color: var(--color-accent);
  text-decoration: none;
}

.social-list__icon {
  display: inline-flex;
}

/* ── Stores ───────────────────────────────────────────── */

.stores {
  margin-top: var(--space-xl);
}

.stores__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
}

/* ── Studio credit ────────────────────────────────────── */

.studio-credit {
  margin-top: var(--space-xl);
  padding-top: var(--space-lg);
  border-top: 1px solid var(--color-border);
}

.studio-credit__line {
  margin-bottom: var(--space-2xs);
  color: var(--color-text-muted);
  font-family: var(--font-ui);
}

/* Colour comes from `.site-footer a:not(.btn)`, which outranks anything set here. */
.studio-credit__link {
  font-size: var(--text-h4);
}

.studio-credit__more {
  margin-bottom: 0;
  font-size: var(--text-sm);
}

.studio-credit a {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}

.studio-credit a:hover,
.studio-credit a:focus-visible {
  text-decoration-thickness: 2px;
}

/* ── Contact ──────────────────────────────────────────── */

.contact__email {
  font-family: var(--font-ui);
  font-size: var(--text-h4);
}

/* ── Media in prose ───────────────────────────────────── */

.prose video,
.prose iframe {
  max-width: 100%;
  border-radius: var(--radius);
}

.prose img {
  height: auto;
}

/* ── Link to the statement ────────────────────────────── */

/*
 * The statement no longer opens the front page, so the one link to it from the game
 * description has to carry weight on its own rather than disappear into the flow of
 * the paragraph above it.
 */
.statement-link {
  margin-block: var(--space-xl);
  padding-left: var(--space-lg);
  border-left: 3px solid var(--color-primary);
  font-family: var(--font-display);
  font-size: var(--text-h4);
  line-height: var(--leading-tight);
}

.statement-link a {
  color: var(--color-accent);
  text-decoration: none;
}

.statement-link a:hover,
.statement-link a:focus-visible {
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.15em;
}

/* ── Galleries ────────────────────────────────────────── */

/*
 * WordPress gallery blocks laid images out in a fixed grid — two columns on the team
 * page, three (cropped to equal height) on the giveaway page. The importer records
 * that as data-columns / data-cropped on the wrapper; the layout is decided here, so
 * no presentational classes travel in the content files.
 *
 * The column COUNT is no longer taken literally. Two fixed columns inside a 700px
 * measure drew each team portrait at ~342px — far larger than the source file and
 * far larger than a contact sheet of eight faces wants to be. The grid now fits as
 * many tiles as the available width allows, so it responds to the actual screen
 * instead of stepping between three hard-coded breakpoints.
 *
 * `--gallery-tile` is the smallest a tile may get before the grid drops a column.
 * data-columns survives as an intent: a gallery the studio laid out in one or two
 * columns wants bigger tiles than one they laid out in five.
 */

[data-gallery] {
  --gallery-tile: 150px;
  display: grid;
  gap: var(--space-md);
  margin-block: var(--space-lg);
  /*
   * `1fr` as the upper bound, not a fixed length: with a fixed maximum the tracks
   * stop stretching, auto-fit counts columns against that maximum instead of the
   * minimum, and the grid collapses to two columns at every width. The tracks have
   * to stay flexible for this to respond to the screen at all.
   *
   * `min(100%, …)` keeps a single tile from overflowing a narrow phone.
   */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--gallery-tile)), 1fr));
}

/* One column means a single feature image: let it have the full measure. */
[data-gallery][data-columns="1"] { --gallery-tile: 100%; }

[data-gallery] figure {
  margin: 0;
}

/* Inside a grid an image fills its column, as it did in the original gallery. */
[data-gallery] img {
  display: block;
  width: 100%;
  height: auto;
}

/*
 * The 'is-cropped' variant: every tile the same height, image cropped to fill. The
 * explicit ratio is what makes the row heights equal now that the column count
 * changes with the viewport.
 */
[data-gallery][data-cropped] figure {
  height: 100%;
}

[data-gallery][data-cropped] img {
  height: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

[data-gallery] figcaption {
  margin-top: var(--space-2xs);
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

/* --- Mailing list signup ------------------------------------------------- */

.signup {
  margin: var(--space-lg) 0;
  max-width: 26rem;
}

.signup__required,
.signup__field {
  margin: 0 0 var(--space-md);
}

.signup__required {
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

.signup__asterisk {
  color: var(--color-accent);
}

.signup__field label {
  display: block;
  margin-bottom: var(--space-2xs);
}

.signup__field input {
  display: block;
  width: 100%;
  padding: var(--space-sm);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius);
  background: var(--color-bg-deep);
  color: var(--color-text);
  font: inherit;
  font-size: var(--text-base);
}

.signup__field input:focus-visible,
.signup__submit:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

.signup__group {
  margin: 0 0 var(--space-md);
}

.signup__group p {
  margin: 0 0 var(--space-2xs);
}

.signup__group ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.signup__group li {
  display: flex;
  align-items: baseline;
  gap: var(--space-sm);
  margin-bottom: var(--space-2xs);
}

.signup__group input {
  accent-color: var(--color-accent);
}

/* Bot trap: off-screen rather than hidden, which is what Mailchimp expects. */
.signup__trap {
  position: absolute;
  left: -5000px;
}

.signup__submit {
  padding: var(--space-sm) var(--space-lg);
  border: 0;
  border-radius: var(--radius);
  background: var(--color-primary);
  color: var(--color-on-primary);
  font: inherit;
  font-size: var(--text-base);
  font-weight: 700;
  cursor: pointer;
}

.signup__submit:hover {
  background: var(--color-primary-dark);
}
