/* ── fonts.css ────────────────────────────────── */
/*
 * GENERATED by tools/fetch-fonts.mjs — do not edit by hand.
 *
 * Self-hosted so that no render-blocking request has to cross to another origin
 * before the page can paint. Re-generate with `npm run fonts`.
 */

@font-face {
  font-family: "Cinzel";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/cinzel-700-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Cinzel";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/cinzel-700-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Spectral";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/spectral-400-italic-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Spectral";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/spectral-400-italic-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Spectral";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/spectral-400-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Spectral";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/spectral-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Spectral";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/fonts/spectral-600-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Spectral";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/fonts/spectral-600-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Spectral";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/spectral-700-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Spectral";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/spectral-700-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}


/* ── tokens.css ────────────────────────────────── */
/*
 * Design tokens — the single place to restyle the site.
 *
 * Change a value here and it propagates everywhere; nothing below this file should
 * contain a raw colour or font name.
 *
 * The palette is built on one idea: warm candlelight against a cold night. The deep
 * orange is the studio's own brand colour, carried over unchanged; everything around
 * it is cold and desaturated so that the orange is the only thing in the page that
 * burns. Text is bone rather than pure white — a dead-flat #fff on near-black glares,
 * and the warm cast sits with the orange instead of fighting it.
 *
 * Every foreground/background pair below meets WCAG AA for normal text (4.5:1); the
 * lowest in use is 4.86:1. Interactive states always get BRIGHTER, never darker —
 * a hover that lowers contrast reads as the control going away.
 */

:root {
  /* ── Colour: the night ──────────────────────────────── */
  --color-bg: #12151a; /* cold near-black, faint blue cast */
  --color-bg-deep: #0a0c10; /* behind the banner, footer */
  --color-bg-raised: #1c212a; /* cards, wells */

  /* ── Colour: bone ───────────────────────────────────── */
  --color-text: #e4e0d7; /* body copy — 13.9:1 */
  --color-text-muted: #9ba3ad; /* meta, captions — 7.2:1 */
  --color-heading: #f6f2e9; /* 16.4:1 */

  /* ── Colour: fire (brand) ───────────────────────────── */
  --color-primary: #ff5722; /* the studio's deep orange, unchanged */
  --color-primary-bright: #ff7043; /* hover — brighter, 6.7:1 against the label */
  --color-primary-dark: #d84315; /* borders and rules only, never behind text */

  /* ── Colour: candle ─────────────────────────────────── */
  --color-accent: #f2b230; /* links — 9.7:1 */
  --color-accent-hover: #ffd776; /* link hover — 13.3:1, brighter than rest */

  /* ── Colour: verdigris ──────────────────────────────── */
  /* The cold counterweight. Without it every accent is warm and nothing recedes. */
  --color-secondary: #5f8c85;
  --color-secondary-bright: #7faea6;

  /* Text sitting ON a solid brand fill — near-black, not white: white on this orange
     is 3.16:1 and fails. */
  --color-on-primary: #12151a;

  --color-border: #2a3038;
  --color-focus: var(--color-accent);

  /* ── Typography ─────────────────────────────────────── */
  /*
   * Two families, not three. Cinzel is an inscriptional Roman capital — it carries
   * the occult/literary register of the catalogue without tipping into a novelty
   * horror face. Spectral is a serif drawn for screens, which is what makes long
   * copy on a dark background readable where a display face or a light sans is not.
   */
  --font-display: "Cinzel", "Trajan Pro", Georgia, serif; /* h1–h2 only */
  --font-ui: "Spectral", Georgia, serif; /* nav, buttons, h3–h6 */
  --font-body: "Spectral", Georgia, serif;

  /*
   * The scale is fluid: each size interpolates with the viewport instead of jumping at
   * a breakpoint. A stepped scale has to pick one size for every screen between two
   * breakpoints, so a heading that is right at 1440 is oversized at 1024 and cramped
   * at 1600. The clamp bounds are the sizes that are actually right at each end.
   */
  --text-base: clamp(1.0625rem, 1.03rem + 0.18vw, 1.1875rem);
  --text-sm: clamp(0.9375rem, 0.92rem + 0.08vw, 1rem);
  --text-xs: 0.8125rem;

  /* The floor is set for the narrowest phone: Cinzel is a wide face and carries
     letter-spacing, so a larger minimum overflows a 360px screen. */
  --text-h1: clamp(2.125rem, 1.4rem + 4.4vw, 4.25rem);
  --text-h2: clamp(1.875rem, 1.45rem + 2.1vw, 2.75rem);
  --text-h3: clamp(1.375rem, 1.25rem + 0.6vw, 1.625rem);
  --text-h4: clamp(1.1875rem, 1.13rem + 0.28vw, 1.3125rem);
  /* Must scale with --text-base, not sit at a fixed rem: a heading that ends up
     smaller than the body text it introduces inverts the hierarchy. */
  --text-h5: clamp(1.125rem, 1.07rem + 0.28vw, 1.25rem);
  --text-h6: 0.9375rem; /* an uppercase, letter-spaced label — small on purpose */

  /* One step above body copy, for an opening paragraph that carries the pitch. */
  --text-lead: clamp(1.1875rem, 1.11rem + 0.38vw, 1.4375rem);

  --leading-body: 1.75;
  --leading-tight: 1.15;
  /* Inscriptional caps need air between letters; body text does not. */
  --tracking-display: 0.06em;
  --tracking-ui: 0.08em;

  /* ── 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;
  /* The gap between one section and the next — the page's main rhythm. */
  --space-section: clamp(3.5rem, 7vw, 7rem);

  /* ── Layout ─────────────────────────────────────────── */
  --wrap-max: 1140px;
  --wrap-narrow: 68ch; /* measure, not pixels: ~66 characters per line */
  --gutter: var(--space-lg);
  --header-height: 84px;

  /* ── Effects ────────────────────────────────────────── */
  --radius: 2px; /* near-square: carved, not app-like */
  --radius-lg: 4px;
  --shadow: 0 10px 40px rgb(0 0 0 / 60%);
  --transition: 180ms ease;
}

/* Roomier gutters once there is space for them. Type is fluid and needs no step. */
@media (min-width: 48rem) {
  :root {
    --gutter: var(--space-xl);
  }
}


/* ── 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: a light weight on a dark background optically thins out and the
     copy stops holding together at reading size. */
  font-weight: 400;
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
}

body.nav-open {
  overflow: hidden;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0 0 var(--space-md);
  color: var(--color-heading);
  line-height: var(--leading-tight);
}

/*
 * The display face is reserved for the two levels that actually announce a page.
 * Running it down to h5 gave five headings one voice, so a subheading differed from
 * a page title only by size — a size ramp, not a hierarchy. Below h2 the headings
 * step down in weight and letter-spacing instead, which is what makes them read as
 * a different rank rather than a smaller shout.
 */
h1,
h2 {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: var(--tracking-display);
}

h3,
h4,
h5,
h6 {
  font-family: var(--font-ui);
  font-weight: 600;
  letter-spacing: 0.01em;
}

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); }

/* The smallest heading earns its rank from case and tracking, not from size. */
h6 {
  font-size: var(--text-h6);
  letter-spacing: var(--tracking-ui);
  text-transform: uppercase;
  color: var(--color-secondary-bright);
}

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;
}

: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);
  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-section);
}

/*
 * Sections alternate ground. Every band being the same colour with the same padding is
 * what makes a page read as one long undifferentiated stack — the eye gets no signal
 * that one thing has ended and another begun. A change of ground does that work
 * without adding any furniture to the page.
 */
/*
 * Border on the top edge only. With a border on both edges the last section's bottom
 * rule lands directly against the footer's own top rule, drawing two lines a few
 * pixels apart. Going into the next block, the change of ground is the boundary — it
 * does not need a line as well.
 */
.section--sunken {
  background-color: var(--color-bg-deep);
  border-top: 1px solid var(--color-border);
}

/*
 * The heading is bone, and the orange is a rule above it rather than the text colour.
 * Orange headings competed directly with the orange buttons — two different things
 * shouting in the same voice — and an inscriptional face set in a mid-tone colour
 * loses most of its weight. This way the brand colour marks structure and the heading
 * keeps its contrast.
 */
/*
 * No font-size here on purpose. This class is applied to whichever heading level the
 * section actually needs — an h1 on the homepage, an h2 elsewhere — and a size set
 * here outranks the element's own rule, so the page title came out at h2 size and the
 * top two levels became indistinguishable. The element decides how big it is.
 */
.section-heading {
  margin-bottom: var(--space-lg);
  color: var(--color-heading);
}

.section-heading::before {
  content: "";
  display: block;
  width: 3rem;
  height: 3px;
  margin-bottom: var(--space-md);
  background-color: var(--color-primary);
}

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

/*
 * The translucent blur lives on a pseudo-element, NOT on .site-header itself.
 * `backdrop-filter` makes an element the containing block for any `position: fixed`
 * descendant — and the mobile menu panel is exactly that. With the filter on the
 * header, the panel resolved `inset: var(--header-height) 0 0` against the 85px header
 * instead of the viewport, collapsed to 48px, and clipped its own links away.
 */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  border-bottom: 1px solid var(--color-border);
}

.site-header::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-color: color-mix(in srgb, var(--color-bg-deep) 88%, transparent);
  backdrop-filter: blur(10px);
}

/* Browsers without color-mix get the solid colour rather than a transparent header. */
@supports not (background-color: color-mix(in srgb, red 50%, transparent)) {
  .site-header::before {
    background-color: var(--color-bg-deep);
  }
}

.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;
  gap: var(--space-lg);
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-nav__item {
  margin: 0;
}

.site-nav__link {
  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);
}

/* Menu and social sit on one row on a wide screen. */
.site-nav {
  display: flex;
  align-items: center;
  gap: var(--space-lg);
}

.site-header__social .social-list {
  margin: 0;
  gap: var(--space-2xs);
  flex-wrap: nowrap;
}

.site-header__social .social-list__link {
  gap: 0;
  padding: var(--space-2xs);
  min-width: 44px;
  min-height: 44px;
  justify-content: center;
  background: none;
  border: 0;
}

/*
 * Icons only on a wide screen, where the labels would crowd the bar — the text stays
 * in the markup for screen readers rather than being deleted. Inside the slide-out
 * panel there is room, so the labels come back (see the small-screen block below).
 */
@media (min-width: 62rem) {
  .site-header__social .social-list__label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

.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);
}

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

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

/* Same treatment as a section heading — bone, over an orange rule — so an inner page
   and the homepage look like the same site. */
.page-title {
  margin: 0;
  color: var(--color-heading);
}

.page-title::before {
  content: "";
  display: block;
  width: 3rem;
  height: 3px;
  margin-bottom: var(--space-md);
  background-color: var(--color-primary);
}

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

/* `min(..., 100%)` so the measure can never be wider than whatever contains it —
   `ch` is a font-relative unit, so 68ch is far wider than a phone screen. */
.prose {
  max-width: min(var(--wrap-narrow), 100%);
}

/*
 * A page body carries both classes at once. `.wrap` centres a 1140px column; `.prose`
 * then narrows the max-width to a reading measure — and the wrap's `auto` side margins
 * centre THAT, so the text drifts inward and stops lining up with the page title above
 * it. Keep the wrap's geometry, and cap the measure on the children instead.
 */
.wrap.prose {
  max-width: var(--wrap-max);
}

.wrap.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 ───────────────────────────────────────────── */

/*
 * No margin-top. The section above already ends in its own padding, so a margin here
 * added a band of page background between that section and the footer's top rule —
 * an empty strip with a line floating in it, which reads as a mistake rather than as
 * separation.
 */
.site-footer {
  position: relative;
  padding-top: var(--space-2xl);
  background-color: var(--color-bg);
  border-top: 1px solid var(--color-border);
  color: var(--color-text-muted);
}

/* Footer headings label a block; they do not announce a page, so they sit two steps
   down from a section heading rather than matching one. */
.site-footer .section-heading {
  font-size: var(--text-h3);
}

.site-footer a {
  color: var(--color-accent);
}

.site-footer a:hover,
.site-footer a: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;
}

/*
 * `a.to-top`, not `.to-top`: this button sits inside the footer, so the plain class
 * loses to `.site-footer a` above and the label inherits the footer link colour
 * instead of its own.
 */
a.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%;
  color: var(--color-on-primary);
  font-size: 1.25rem;
  text-decoration: none;
  opacity: 0.85;
  transition: opacity var(--transition), background-color var(--transition);
}

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

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

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

  /*
   * The nav becomes a full-height panel below the header, so it stacks: the menu, then
   * the social links under it. `inset` uses the same custom property the header's
   * min-height does, so the panel always starts exactly where the header ends.
   */
  .site-nav {
    position: fixed;
    inset: var(--header-height) 0 0;
    z-index: 99;
    display: none;
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-lg);
    padding: var(--space-lg) var(--gutter);
    background-color: var(--color-bg-deep);
    overflow-y: auto;
  }

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

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

  /* In the panel there is room for the labels, and a 44px row is easier to hit than
     a bare icon. */
  .site-header__social .social-list {
    flex-wrap: wrap;
    gap: var(--space-md);
  }

  .site-header__social .social-list__link {
    gap: var(--space-xs);
    justify-content: flex-start;
  }

  .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, cards, forms, lists. */

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

/*
 * The label is near-black, not white. White on this orange is 3.16:1 and fails AA at
 * button size; the near-black is 5.78:1 — so the brand colour is kept exactly as it
 * is and the label moves instead.
 */
.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: 600;
  letter-spacing: var(--tracking-ui);
  text-decoration: none;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color var(--transition), border-color var(--transition),
    color var(--transition);
}

/* Hover brightens to 6.66:1. Darkening it, as this did before, dropped the label to
   4.12:1 — the control got harder to read the moment it was aimed at. */
.btn:hover,
.btn:focus-visible {
  background-color: var(--color-primary-bright);
  border-color: var(--color-primary-bright);
  color: var(--color-on-primary);
  text-decoration: none;
}

.btn--ghost {
  background-color: transparent;
  color: var(--color-primary);
}

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

/* ── Hero ─────────────────────────────────────────────── */

.hero {
  position: relative;
  background-color: var(--color-bg-deep);
}

/*
 * Height is set directly rather than with `aspect-ratio` + `max-height`. Those two
 * together make the browser derive the WIDTH back from the clamped height, so the
 * banner shrinks away from the right edge and sits in dead space instead of running
 * full-bleed. The source images are 2000x1200 (5:3); `object-fit: cover` on the image
 * absorbs the difference whenever the box is not exactly that ratio.
 */
.hero__track {
  position: relative;
  width: 100%;
  height: clamp(17rem, 58vw, 78vh);
  overflow: hidden;
  touch-action: pan-y;
}

/*
 * `visibility` (not just opacity) is what keeps an off-screen slide out of the tab
 * order — an invisible but focusable call-to-action is reachable by keyboard and
 * silently steals focus. It flips back instantly on the way in, and only after the
 * fade has finished on the way out.
 */
.hero__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  transition: opacity 600ms ease, visibility 0s linear 600ms;
}

.hero__slide.is-active {
  opacity: 1;
  visibility: visible;
  transition: opacity 600ms ease, visibility 0s;
}

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

/* The slide itself is the link, so it fills the frame. */
.hero__link {
  display: block;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

/*
 * With no button on top, nothing announces that the banner is clickable. A slow push
 * in on hover supplies that: the picture reacts, so it reads as a control rather than
 * as decoration.
 */
.hero__image {
  transition: transform 700ms ease, filter 700ms ease;
}

.hero__link:hover .hero__image,
.hero__link:focus-visible .hero__image {
  transform: scale(1.03);
  filter: brightness(1.08);
}

@media (prefers-reduced-motion: reduce) {
  .hero__image,
  .hero__link:hover .hero__image,
  .hero__link:focus-visible .hero__image {
    transition: none;
    transform: none;
  }
}

/*
 * The image is the only affordance, so the focus ring has to be unmistakable — it is
 * drawn inside the frame, because an outline on a full-bleed element would otherwise
 * sit off the edge of the viewport where it cannot be seen.
 */
.hero__link:focus-visible {
  outline: 3px solid var(--color-accent);
  outline-offset: -6px;
}

.hero__dots {
  position: absolute;
  inset: auto 0 var(--space-md);
  z-index: 1;
  display: flex;
  justify-content: center;
  gap: var(--space-xs);
}

/*
 * The dot is a 12px circle inside a 44px-square hit area: a 12px tap target fails
 * WCAG and is unusable on a phone, but a 44px visible dot would swamp the banner.
 */
.hero__dot {
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  display: grid;
  place-items: center;
  -webkit-tap-highlight-color: transparent;
}

.hero__dot::before {
  content: "";
  width: 12px;
  height: 12px;
  border: 2px solid #fff;
  border-radius: 50%;
  background-color: transparent;
  box-shadow: 0 1px 3px rgb(0 0 0 / 0.5);
  transition:
    background-color var(--transition),
    border-color var(--transition),
    transform var(--transition);
}

.hero__dot:hover::before {
  transform: scale(1.2);
}

.hero__dot.is-active::before {
  background-color: var(--color-primary);
  border-color: var(--color-primary);
}

.hero__dot:focus-visible {
  outline: none;
}

.hero__dot:focus-visible::before {
  outline: 2px solid #fff;
  outline-offset: 3px;
}

@media (max-width: 48rem) {
  .hero__track {
    height: clamp(16rem, 70vw, 60vh);
  }
}

/*
 * Without this the crossfade still runs for someone who asked for less motion — the
 * rotation itself is already disabled in site.js.
 */
@media (prefers-reduced-motion: reduce) {
  .hero__slide,
  .hero__slide.is-active {
    transition: none;
  }
}

/* ── About ────────────────────────────────────────────── */

/*
 * Two columns, not a stack. The prose is capped at a readable measure, so on a wide
 * screen a single column leaves the right-hand third of the section empty and the
 * text hanging off the left edge. Putting the mark and its note in that space uses it
 * and gives the section a second focal point.
 */
/*
 * The single column is `minmax(0, 1fr)`, not implicit. An implicit grid track is sized
 * `auto`, which resolves to the content's max-content width — and the prose inside
 * asks for a 68ch measure, so on a narrow screen the track grew past the viewport and
 * the text was cut off at the edge rather than wrapping.
 */
.about {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-xl);
  align-items: start;
}

@media (min-width: 62rem) {
  .about {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
    gap: var(--space-3xl);
  }
}

.about__lead > :last-child,
.about__note > :last-child {
  margin-bottom: 0;
}

/* The lead paragraph carries the studio's pitch, so it is set a step larger than
   body copy — the one place on the page where size alone marks importance. */
.about__lead {
  font-size: var(--text-lead);
  line-height: 1.6;
}

.about__aside {
  display: grid;
  gap: var(--space-lg);
  justify-items: start;
}

.about__figure {
  margin: 0;
  max-width: 15rem;
}

.about__figure img {
  width: 100%;
}

/*
 * The note is an aside about the logo, so it is set as marginalia — smaller, muted,
 * and hung off a rule in the cold accent so it reads as commentary rather than as a
 * second paragraph of the pitch.
 */
.about__note {
  padding-left: var(--space-md);
  border-left: 2px solid var(--color-secondary);
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

/* ── Game grid ────────────────────────────────────────── */

.game-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  gap: var(--space-lg);
  margin: 0 0 var(--space-xl);
  padding: 0;
  list-style: none;
}

.game-card {
  margin: 0;
  overflow: hidden;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background-color: var(--color-bg-raised);
  transition: border-color var(--transition), transform var(--transition);
}

.game-card:hover {
  border-color: var(--color-primary);
  transform: translateY(-3px);
}

@media (prefers-reduced-motion: reduce) {
  .game-card {
    transition: border-color var(--transition);
  }

  .game-card:hover {
    transform: none;
  }
}

/* A fixed ratio, so three images of different proportions still line up as a row
   rather than stepping up and down. */
.game-card__image {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
}

/* The closing paragraph sits under a full-width grid, so it is centred on the section
   rather than left-aligned under the first card. */
.games__body {
  margin-inline: auto;
  text-align: center;
}

/* ── Single game page ─────────────────────────────────── */

.game__status {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.game__cover {
  margin-bottom: var(--space-xl);
}

.game__cover img {
  width: 100%;
  max-height: 60vh;
  object-fit: cover;
}

.game__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  margin-top: var(--space-xl);
}

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

.social-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  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-xs);
  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-primary);
  color: var(--color-primary);
  text-decoration: none;
}

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

/* ── Partners ─────────────────────────────────────────── */

.partners__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xl) var(--space-2xl);
  margin: 0;
  padding: 0;
  list-style: none;
}

.partners__item {
  margin: 0;
  display: flex;
  align-items: center;
}

/*
 * Each logo arrives at its own size and its own proportions, so the declared widths
 * alone leave them sitting on different optical baselines. Capping the HEIGHT is what
 * makes a row of unrelated marks look deliberate — they then occupy the same band
 * whatever their aspect. `width: auto` overrides the per-partner inline width.
 */
.partners__item img {
  width: auto !important;
  max-width: 13rem;
  height: auto;
  max-height: 3.5rem;
  object-fit: contain;
  /* Supplied dark-on-transparent; lifted onto the dark ground. */
  filter: invert(1) grayscale(1) contrast(1.15);
  opacity: 0.8;
  transition: opacity var(--transition);
}

.partners__item a:hover img,
.partners__item a:focus-visible img {
  opacity: 1;
}

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

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

/* ── Post list ────────────────────────────────────────── */

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

.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-size: var(--text-h3);
}

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

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

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

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