/* ==========================================================================
   STUDIO SITE — shared styles
   Placeholder markers to replace later: PLACEHOLDER
   ========================================================================== */

:root {
  /* Dark theme. --bg is a warm off-black rather than pure #000 so the glow has
     something to sit against — on true black a blurred halo reads as grey haze
     instead of light. */
  --bg: #0d0d10;
  --bg-raised: #17171b;
  --ink: #f2f1ef;
  --ink-soft: #9a9aa2;
  --line: #2a2a31;
  --placeholder: #2e2e35;      /* dark banner fill */
  --placeholder-2: #35353d;    /* dark banner fill, lighter */
  --radius-card: 20px;
  --radius-pill: 999px;
  --maxw: 1080px;

  /* One measure for prose and one gap between sections, so the page has a
     system rather than a different width and margin for every block. */
  --measure: 60ch;
  --section-gap: 104px;

  /* Header logo height. No bleed — this is a wordmark, so clipping it at the
     edge would read as broken rather than deliberate. */
  --logo-size: 30px;

  /* Glow tuning — all three banners read from these. */
  --glow-blur: 54px;
  --glow-spread: 1.07;   /* how far past the banner edge the halo reaches */
  --glow-opacity: 0.55;
  --glow-lift: 2%;       /* pushes the halo down, like light pooling below */
  /* Albert Sans — the face emmaravs.com uses. Free on Google Fonts, so this is
     the real thing rather than a substitute. Weights 400/500/600/700. */
  --font: "Albert Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }

/* Visible to screen readers, hidden on screen — keeps the page's heading
   structure intact where a visible title isn't wanted. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  /* Tells the browser to render form controls and scrollbars dark too */
  color-scheme: dark;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  /* `clip` rather than `hidden`: the glow can bleed past the viewport edge
     without adding a horizontal scrollbar, and unlike `hidden` it doesn't
     create a scroll container that would break the sticky header. */
  overflow-x: clip;
}

a { color: inherit; }

.wrap {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 24px;
}

/* ----- Header -------------------------------------------------------------- */

/* Transparent so the first banner's glow bleeds up behind the nav rather than
   stopping at an opaque bar. No backdrop blur either — that would frost the
   glow and give the header a visible edge. */
.site-header {
  padding: 28px 0;
  position: sticky;
  top: 0;
  background: transparent;
  z-index: 10;
}

.site-header .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  /* Keeps the nav readable when banner artwork scrolls under the transparent
     header — cheaper than reintroducing a background bar. */
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.7);
}

/* ----- Logo -----------------------------------------------------------------
   The wordmark is inlined as SVG rather than an <img> so an SVG filter can act
   on it directly. Referencing filter: url(#…) from an HTML <img> is patchy in
   Safari; applying a filter to inline SVG content is solid everywhere.

   On hover the letterforms ripple like liquid — feTurbulence generates noise,
   feDisplacementMap pushes the paths around by it, and the script drives the
   displacement scale and noise frequency. See assets/site.js. */
.logo {
  position: relative;
  display: flex;
  align-items: center;
  text-decoration: none;
  --lx: 50%;
  --ly: 50%;
}

.logo__svg {
  position: relative;
  z-index: 1;
  height: var(--logo-size);
  width: auto;
  aspect-ratio: 620 / 78;
  flex: 0 0 auto;
  display: block;
  overflow: visible;   /* lets the ripple spill past the tight viewBox */
}

/* A soft light that blooms behind the wordmark and follows the cursor. Same
   idea as the ambilight under the banners, scaled down — the colours are the
   three games', so the header hints at the page below. Sits behind the letters
   rather than recolouring them, so the wordmark stays crisp white.

   This is a free-floating elliptical blob positioned at the cursor, not a
   gradient painted inside a box. Backgrounds are clipped to their element's
   border-box, so a box-shaped layer leaves visible straight edges wherever the
   gradient hasn't fully faded — the light ends up looking boxed in. An ellipse
   whose gradients finish well inside it has no edge to clip, and the blur
   spills past the element freely. */
.logo__glow {
  position: absolute;
  left: var(--lx);
  top: var(--ly);
  z-index: 0;
  width: calc(var(--logo-size) * 11);
  height: calc(var(--logo-size) * 7);
  transform: translate(-50%, -50%);
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  /* Offset centres so the colours pool and mix rather than sitting concentric */
  background:
    radial-gradient(closest-side circle at 44% 50%,
      rgba(212, 56, 79, 0.5) 0%, rgba(212, 56, 79, 0) 62%),
    radial-gradient(closest-side circle at 56% 46%,
      rgba(111, 99, 168, 0.48) 0%, rgba(111, 99, 168, 0) 64%),
    radial-gradient(closest-side circle at 50% 56%,
      rgba(143, 174, 134, 0.4) 0%, rgba(143, 174, 134, 0) 66%);
  filter: blur(26px);
  /* Slight lag on the position so the light drifts after the cursor */
  transition: opacity 0.45s ease, left 0.25s ease-out, top 0.25s ease-out;
}

.logo:hover .logo__glow,
.logo:focus-visible .logo__glow {
  opacity: 1;
}

/* Filter definitions live in a zero-size SVG; nothing to lay out. */
.svg-defs {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

.nav {
  display: flex;
  align-items: center;
  gap: 28px;
}

.nav a {
  text-decoration: none;
  font-size: 15px;
  /* Albert Sans Black — the heaviest weight in the family */
  font-weight: 900;
  text-transform: lowercase;
  letter-spacing: -0.01em;
  color: var(--ink-soft);
  padding-bottom: 2px;
  border-bottom: 1.5px solid transparent;
  transition: color 0.15s ease, border-color 0.15s ease;
}

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

.nav a[aria-current="page"] {
  color: var(--ink);
  border-bottom-color: var(--ink);
}

/* ----- Page shell --------------------------------------------------------- */

/* Tight top padding so the first banner sits close enough for its glow to
   reach up past the transparent header. */
main { padding: 4px 0 96px; }

.page-title {
  font-size: clamp(28px, 4vw, 40px);
  line-height: 1.15;
  letter-spacing: -0.02em;
  margin: 8px 0 32px;
}

/* ----- Game banners ------------------------------------------------------- */

.games {
  display: flex;
  flex-direction: column;
  gap: 28px;
}

.game-card {
  position: relative;
  display: block;
  border-radius: var(--radius-card);
  overflow: hidden;
  text-decoration: none;
  background: var(--placeholder);
  min-height: 340px;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

/* Alternate greys so the three cards read as distinct while unstyled */
.game-card--b { background: var(--placeholder-2); }
.game-card--c { background: #c4c4c4; }

/* Only cards that are links get the hover lift + cursor */
a.game-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.14);
}

/* Banner art. Replace background-image via inline style or per-card class. */
.game-card__art {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
}

/* Scrim so tags stay legible once real art is dropped in */
.game-card__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.35) 0%, rgba(0, 0, 0, 0) 55%);
}

.game-card__body {
  position: relative;
  height: 100%;
  min-height: 340px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 28px;
}

.game-card__name {
  margin: 0;
  font-size: clamp(24px, 3.5vw, 34px);
  letter-spacing: -0.02em;
  color: #fff;
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.25);
}

/* ==========================================================================
   LAYERED BANNERS
   Each banner is a container-query context, so every element inside can be
   sized in `cqw` units (1cqw = 1% of the banner's width). That means the whole
   composition scales proportionally at any viewport width — no breakpoint
   fiddling needed for the artwork itself.
   ========================================================================== */

/* --- Ambilight glow -------------------------------------------------------
   Each .game wrapper paints a scaled, blurred copy of its banner artwork
   directly behind the banner. The blur bleeds past the edges, so the banner
   appears to cast coloured light onto the dark page — green under bird a day,
   purple under A Thing or Too.

   The art is declared once per game as --art and read by both the banner's
   background and the glow, so the two can never drift apart. */

.game {
  position: relative;
}

.game::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--radius-card);
  background: var(--art, none) center / cover no-repeat;
  /* Saturation boost compensates for the colour a heavy blur washes out */
  filter: blur(var(--glow-blur)) saturate(1.7);
  transform: scale(var(--glow-spread)) translateY(var(--glow-lift));
  opacity: var(--glow-opacity);
  pointer-events: none;
}

.game--bird      { --art: url("assets/bird-a-day-bg.webp"); }
.game--atot      { --art: url("assets/a-thing-or-too-bg.webp"); }
.game--friendslop { --art: url("assets/friendslop-bg.webp"); }

/* Glow fades up as the banner arrives, so it feels lit rather than pasted on.
   Default is fully visible, so a failed script still shows the glow. */
.js .game::before {
  opacity: 0;
  transition: opacity 1.2s ease;
}

.js .game:has(.is-revealed)::before {
  opacity: var(--glow-opacity);
}

.banner-link {
  display: block;
  text-decoration: none;
  border-radius: var(--radius-card);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
  /* Sits above the absolutely-positioned glow without needing a negative
     z-index, which would risk dropping behind the page background. */
  position: relative;
  z-index: 1;
}

.banner-link:hover {
  transform: translateY(-3px);
}

.banner {
  position: relative;
  container-type: inline-size;
  aspect-ratio: 1307 / 525;   /* matches the supplied background art */
  width: 100%;
  overflow: hidden;
  border-radius: var(--radius-card);
  background: var(--placeholder);
}

.banner__bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
}

/* Per-banner background art, read from the --art set on the .game wrapper */
.banner__bg {
  background-image: var(--art, none);
}

/* Each banner sits on a flat colour matched to the average of its own artwork,
   so the instant before the image decodes shows roughly the right colour rather
   than a grey placeholder flashing through. */
.banner--bird       { background-color: #778774; }
.banner--atot       { background-color: #6b576d; }
.banner--friendslop { background-color: #3b3d79; }

/* A positioned layer. Give it a modifier class to place it. */
.banner__layer {
  position: absolute;
}

/* Empty slot marker — shows where art will land. Remove `is-empty` once the
   real element is dropped in. */
.banner .is-empty {
  border: 1.5px dashed rgba(255, 255, 255, 0.55);
  border-radius: 2cqw;
  display: grid;
  place-items: center;
  color: rgba(255, 255, 255, 0.8);
  font-size: 1.5cqw;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: center;
  padding: 1cqw;
}

/* --- Bird a Day layout ---------------------------------------------------- */

/* Wordmark, left side. Height derives from the SVG's own 460:138 ratio, so it
   never distorts — set the width and everything follows. */
.banner--bird .layer-logo {
  left: 5.5%;
  top: 20%;
  width: 34%;
  aspect-ratio: 460 / 138;
}

.banner--bird .layer-logo img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: left center;
  display: block;
}

/* Funder logo, centred under the wordmark. It shares the wordmark's left edge
   and width, so the two stay optically aligned even if either logo is resized —
   the image just centres itself inside that same column. */
.banner--bird .layer-funder {
  left: 5.5%;
  top: 53%;
  width: 34%;
  display: grid;
  place-items: center;
}

.banner--bird .layer-funder img {
  width: 72%;
  height: auto;
  display: block;
}

/* Photo-card cluster, right side */
.banner--bird .layer-cards {
  right: 2%;
  top: 3%;
  width: 51%;
  height: 94%;
}

/* Individual cards inside the cluster.
   Two nested elements so the motions never fight each other:
     .card        owns position + the deal-in entrance animation
     .card__tilt  owns the resting tilt and the hover nudge
   Putting both on one element would mean the entrance animation's `both` fill
   permanently outranks any hover transform.

   The tilt transform is composed from custom properties (--rot base angle,
   --nudge hover delta), so hover adds to the resting angle rather than
   replacing it. transform-origin sits below the card, so rotation swings it
   like a card in a fanned hand instead of spinning about its middle. */
.banner--bird .layer-cards .card {
  position: absolute;
  width: 44%;
  aspect-ratio: 1;
}

.banner--bird .card__tilt {
  width: 100%;
  height: 100%;
  transform-origin: 50% 135%;
  transform:
    rotate(calc(var(--rot, 0deg) + var(--nudge, 0deg)))
    translateY(var(--lift, 0));
  /* Tight contact shadow + wide soft shadow. drop-shadow follows the PNG's
     alpha, so it traces the rounded corners instead of drawing a rectangle
     behind them. */
  filter:
    drop-shadow(0 0.2cqw 0.55cqw rgba(30, 40, 30, 0.14))
    drop-shadow(0 0.95cqw 2.3cqw rgba(30, 40, 30, 0.22));
  transition:
    transform 0.5s cubic-bezier(0.2, 0.7, 0.3, 1),
    filter 0.5s ease;
  will-change: transform;
}

.banner--bird .layer-cards .card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Resting positions. --rot is the card's own tilt; --from-x/--from-y are where
   it flies in from. */
.banner--bird .card--1 {
  left: 0%;  top: 20%; --rot: -8deg; z-index: 1;
  --from-x: -14%; --from-y: 16%;
}
.banner--bird .card--2 {
  left: 28%; top: 4%;  --rot: 6deg;  z-index: 2;
  --from-x: 0%;   --from-y: 22%;
}
.banner--bird .card--3 {
  left: 56%; top: 30%; --rot: -3deg; z-index: 3;
  --from-x: 14%;  --from-y: 18%;
}

/* ==========================================================================
   BANNER ENTRANCE SEQUENCE
   Every banner is a [data-reveal] target. When it scrolls into view the script
   adds .is-revealed and the contents arrive in reading order: wordmark, funder
   credit, artwork, then the descriptor pills, then the link arrow.

   The hidden start state is scoped to `html.js`, which an inline <head> script
   sets before first paint — so if the script never runs, everything is simply
   visible rather than stuck at opacity 0.
   ========================================================================== */

.js .banner__layer,
.js .banner__tags > .tag,
.js .banner__out {
  opacity: 0;
}

/* The card cluster is a layer too, but its children animate individually —
   the container itself must stay visible. */
.js .banner .layer-cards {
  opacity: 1;
}

@keyframes rise-in {
  from { opacity: 0; transform: translate3d(0, var(--rise, 14%), 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

@keyframes card-deal {
  from {
    opacity: 0;
    transform: translate(var(--from-x, 0), var(--from-y, 20%)) scale(0.9);
  }
  to {
    opacity: 1;
    transform: translate(0, 0) scale(1);
  }
}

/* 1 — wordmark (or its text stand-in) */
.js .banner__title { opacity: 0; }

.js .banner.is-revealed .layer-logo,
.js .banner.is-revealed .banner__title {
  animation: rise-in 0.75s cubic-bezier(0.16, 0.84, 0.28, 1) both;
  animation-delay: 0.06s;
}

/* 1b — foreground art layers. Any .banner__layer starts hidden, so every new
   layer needs an entry here or it never becomes visible. */
.js .banner.is-revealed .layer-wave {
  --rise: 10%;
  animation: rise-in 0.9s cubic-bezier(0.16, 0.84, 0.28, 1) both;
  animation-delay: 0.14s;
}

.js .banner.is-revealed .layer-drops {
  --rise: 18%;
  animation: rise-in 0.9s cubic-bezier(0.16, 0.84, 0.28, 1) both;
  animation-delay: 0.28s;
}

/* 2 — funder credit, a touch softer than the wordmark above it */
.js .banner.is-revealed .layer-funder {
  --rise: 26%;
  animation: rise-in 0.75s cubic-bezier(0.16, 0.84, 0.28, 1) both;
  animation-delay: 0.22s;
}

/* 3 — artwork deals in */
.js .banner.is-revealed .card {
  animation: card-deal 0.85s cubic-bezier(0.16, 0.84, 0.28, 1) both;
}

.js .banner.is-revealed .card--1 { animation-delay: 0.18s; }
.js .banner.is-revealed .card--2 { animation-delay: 0.30s; }
.js .banner.is-revealed .card--3 { animation-delay: 0.42s; }

/* 4 — descriptor pills, left to right */
.js .banner.is-revealed .banner__tags > .tag {
  --rise: 45%;
  animation: rise-in 0.6s cubic-bezier(0.16, 0.84, 0.28, 1) both;
}

.js .banner.is-revealed .banner__tags > .tag:nth-child(1) { animation-delay: 0.46s; }
.js .banner.is-revealed .banner__tags > .tag:nth-child(2) { animation-delay: 0.55s; }
.js .banner.is-revealed .banner__tags > .tag:nth-child(3) { animation-delay: 0.64s; }

/* 5 — link arrow, last */
.js .banner.is-revealed .banner__out {
  animation: rise-in 0.5s ease both;
  animation-delay: 0.74s;
}

/* --- Hover: a small shift, mostly a deepening shadow -----------------------
   No scale-up and no z-index change, so cards never jump in front of each
   other — the card just leans and lifts off the background slightly. */

.banner--bird .layer-cards .card:hover .card__tilt {
  --nudge: 2.5deg;
  --lift: -2%;
  filter:
    drop-shadow(0 0.4cqw 0.95cqw rgba(30, 40, 30, 0.16))
    drop-shadow(0 1.8cqw 3.7cqw rgba(30, 40, 30, 0.28));
}

/* Keyboard focus on the banner link lifts the whole cluster a little */
.banner-link:focus-visible .banner--bird .card__tilt {
  --lift: -1.5%;
}

/* --- External-link arrow, top-right --------------------------------------- */

.banner__out {
  position: absolute;
  top: 5%;
  right: 3%;
  z-index: 6;
  width: 3.4cqw;
  height: 3.4cqw;
  min-width: 20px;
  min-height: 20px;
  display: grid;
  place-items: center;
  color: #fff;
  /* Soft shadow keeps the white arrow legible over light artwork */
  filter: drop-shadow(0 0.15cqw 0.5cqw rgba(20, 30, 20, 0.45));
}

/* Hover motion lives on the inner icon, not the wrapper — the wrapper's
   entrance animation uses `both` fill, which would otherwise pin its transform
   and swallow the hover nudge. */
.banner__out svg,
.banner__out img {
  width: 100%;
  height: 100%;
  display: block;
  opacity: 0.9;
  transition: transform 0.3s cubic-bezier(0.2, 0.7, 0.3, 1), opacity 0.3s ease;
}

.banner-link:hover .banner__out svg,
.banner-link:hover .banner__out img,
.banner-link:focus-visible .banner__out svg,
.banner-link:focus-visible .banner__out img {
  opacity: 1;
  transform: translate(14%, -14%);
}

/* Tags overlaid on the banner, bottom-left */
.banner__tags {
  position: absolute;
  left: 5%;
  bottom: 8%;
  z-index: 4;
}

/* Text stand-in for a wordmark, used until real logo art exists */
.banner__title {
  position: absolute;
  left: 5.5%;
  top: 13%;
  z-index: 4;
  margin: 0;
  font-size: 4.2cqw;
  font-weight: 900;
  letter-spacing: -0.02em;
  color: #fff;
  text-shadow: 0 0.3cqw 1.4cqw rgba(10, 5, 30, 0.55);
}

/* --- A Thing or Too layout ------------------------------------------------ */
/* Source art is 2310x1300 (1.78:1); the banner keeps the shared 2.49:1 frame
   and cover-crops it, so all three banners sit at a consistent height. */

.banner--atot .banner__bg {
  background-position: center 45%;
}

/* Wordmark top-left, matching the bird a day treatment */
.banner--atot .layer-logo {
  left: 5.5%;
  top: 15%;
  width: 25%;
  aspect-ratio: 324 / 175;
}

.banner--atot .layer-logo img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  filter: drop-shadow(0 0.4cqw 1.2cqw rgba(0, 0, 0, 0.35));
}

/* --- Untitled Project / friendslop layout --------------------------------- */
/* Two foreground layers over the gradient: a red wave crossing the full width,
   and the drop cluster (all three drops live in one PNG, already arranged) on
   top of it, deliberately cropped by the right and bottom edges. */

.banner--friendslop .layer-wave {
  left: 0;
  right: 0;
  top: -12%;
  width: 100%;
  aspect-ratio: 2 / 1;
  z-index: 1;
}

.banner--friendslop .layer-wave img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  filter: drop-shadow(0 1cqw 2.4cqw rgba(10, 5, 30, 0.45));
}

.banner--friendslop .layer-drops {
  right: -5%;
  top: -4%;
  width: 56%;
  aspect-ratio: 1;
  z-index: 2;
}

.banner--friendslop .layer-drops img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  filter: drop-shadow(0 1.2cqw 3cqw rgba(10, 5, 30, 0.4));
}

/* Wordmark sits top-left like the other two, above both art layers */
.banner--friendslop .layer-logo {
  left: 5.5%;
  top: 15%;
  width: 25%;
  z-index: 3;
}

/* --- Mobile: banner gets taller, composition restacks ---------------------- */

@media (max-width: 640px) {
  .banner { aspect-ratio: 4 / 5; }

  .banner--bird .layer-logo   { left: 8%; top: 7%;  width: 66%; height: auto; }
  .banner--bird .layer-funder { left: 8%; top: 26%; width: 52%; height: auto; }
  .banner--bird .layer-cards  { left: 8%; right: 8%; top: 40%; width: auto; height: 36%; }
  .banner__tags { left: 8%; bottom: 6%; }
  .banner .is-empty { font-size: 3cqw; }

  /* Cards tuck tighter on a narrow banner */
  .banner--bird .layer-cards .card { width: 50%; }
  .banner--bird .card--1 { left: 0%;  top: 12%; }
  .banner--bird .card--2 { left: 25%; top: 0%;  }
  .banner--bird .card--3 { left: 50%; top: 20%; }
  .banner__out { top: 3.5%; right: 5%; width: 7cqw; height: 7cqw; }

  /* Kept as left/width rather than a centring translate — the entrance
     animation owns `transform`, so positioning must not rely on it. */
  .banner--atot .layer-logo { left: 8%; top: 10%; width: 54%; }

  .banner--friendslop .layer-wave  { top: 24%; }
  .banner--friendslop .layer-drops { right: -14%; top: 30%; width: 88%; }
}

/* ----- Tag pills ---------------------------------------------------------- */

.tags {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Pills always sit on top of banner artwork, never on the page background, so
   they keep a light chip with dark text regardless of the site theme. Using
   var(--ink) here would make them white-on-white now that the theme is dark. */
.tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 16px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.78);
  backdrop-filter: blur(6px);
  color: #16161a;
  font-size: 13.5px;
  font-weight: 500;
  white-space: nowrap;
}

.tag--funded { background: rgba(255, 255, 255, 0.92); }

.tag__check {
  display: inline-grid;
  place-items: center;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #16161a;
  color: #fff;
  font-size: 10px;
  line-height: 1;
  font-weight: 700;
}

/* ----- About page ---------------------------------------------------------
   Type scale, largest first:
     40px  opening statement
     24px  closing creed
     20px  section headings
     17px  body / principles
     15px  bios
   Previously the statement, headings and creed all sat within 3px of each
   other at similar weights, so nothing read as first.
   -------------------------------------------------------------------------- */

.mission {
  position: relative;
  margin-bottom: var(--section-gap);
  padding-top: 56px;
}

/* Soft bloom behind the opening statement in the three games' colours — the
   same ambilight language as the banners, so the two pages feel related.
   Static rather than cursor-following: this is atmosphere, not interaction. */
.mission::before {
  content: "";
  position: absolute;
  left: -12%;
  top: 0;
  width: 90%;
  max-width: 760px;
  height: 130%;
  border-radius: 50%;
  pointer-events: none;
  background:
    radial-gradient(closest-side circle at 30% 42%,
      rgba(143, 174, 134, 0.28) 0%, rgba(143, 174, 134, 0) 68%),
    radial-gradient(closest-side circle at 62% 38%,
      rgba(111, 99, 168, 0.28) 0%, rgba(111, 99, 168, 0) 70%),
    radial-gradient(closest-side circle at 48% 70%,
      rgba(212, 56, 79, 0.18) 0%, rgba(212, 56, 79, 0) 66%);
  filter: blur(50px);
}

/* Holds the text above the bloom without resorting to a negative z-index */
.mission > * {
  position: relative;
  z-index: 1;
}

.mission p {
  max-width: var(--measure);
  font-size: clamp(16px, 1.9vw, 17px);
  line-height: 1.7;
  color: var(--ink-soft);
  margin: 0 0 20px;
}

.mission p.mission__lead {
  font-size: clamp(27px, 4.4vw, 40px);
  line-height: 1.18;
  font-weight: 600;
  letter-spacing: -0.028em;
  color: var(--ink);
  margin-bottom: 0;
  text-wrap: pretty;
}

/* ----- Section headings ---------------------------------------------------
   Deliberately small. They label a section rather than compete with the
   statement above — the space beneath them does the separating. */
.section-heading {
  font-size: clamp(17px, 2vw, 20px);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 0 0 28px;
}

/* ----- Principles ---------------------------------------------------------
   Three across, matching the team grid below — three promises, three people,
   so the page keeps one rhythm rather than inventing a second one. */
.principles {
  margin-bottom: var(--section-gap);
}

.principles__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 32px;
}

.principles__list li {
  font-size: clamp(16px, 1.9vw, 17px);
  line-height: 1.6;
  color: var(--ink);
  text-wrap: pretty;
}

/* ----- Closing creed ------------------------------------------------------
   Last thing on the page. Full container width and no max-width so it holds
   one unbroken line — as a single line it reads as a statement, wrapped it
   reads as a paragraph. */
.creed {
  margin: var(--section-gap) 0 0;
  font-size: clamp(19px, 2.4vw, 24px);
  line-height: 1.35;
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--ink);
}

/* ----- Team ---------------------------------------------------------------
   Three across, matching the principles grid above. Drops to two columns at
   860px and one at 620px (see the media queries at the foot of the file). */
.team {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 40px 32px;
}

.member__photo {
  width: 100%;
  /* height:auto is load-bearing. The <img> carries height="700", which browsers
     apply as a presentational hint — and because an explicit height beats
     aspect-ratio, the photo would render 700px tall instead of square. */
  height: auto;
  aspect-ratio: 1 / 1;
  border-radius: 16px;
  background: var(--placeholder);
  margin-bottom: 18px;
  object-fit: cover;
  display: block;
}

.member__name {
  margin: 0 0 4px;
  font-size: 19px;
  letter-spacing: -0.01em;
}

.member__role {
  margin: 0 0 12px;
  font-size: 14px;
  color: var(--ink-soft);
}

.member__bio {
  margin: 0 0 16px;
  font-size: 15px;
  color: var(--ink);
}

.member__links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.member__links a {
  display: inline-block;
  padding: 6px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  font-size: 13.5px;
  font-weight: 500;
  text-decoration: none;
  color: var(--ink);
  background: var(--bg-raised);
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.member__links a:hover {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--bg);
}

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

.site-footer {
  border-top: 1px solid var(--line);
  padding: 32px 0 48px;
  font-size: 14px;
  color: var(--ink-soft);
}

.site-footer .wrap {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.site-footer a { text-decoration: none; }
.site-footer a:hover { text-decoration: underline; }

/* ----- Responsive --------------------------------------------------------- */

@media (max-width: 860px) {
  .team { grid-template-columns: repeat(2, 1fr); }
  .principles__list { grid-template-columns: repeat(2, 1fr); gap: 30px; }
}

@media (max-width: 620px) {
  .wrap { padding: 0 18px; }
  :root { --logo-size: 24px; }
  .site-header { padding: 20px 0; }
  .nav { gap: 20px; }
  .game-card,
  .game-card__body { min-height: 260px; }
  .game-card__body { padding: 20px; }
  .team { grid-template-columns: 1fr; gap: 36px; }
  .principles__list { grid-template-columns: 1fr; gap: 26px; }
  .creed { margin-top: 72px; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
  .banner-link:hover { transform: none; }

  /* Everything still arrives in sequence, but fades in place instead of moving */
  .js .banner.is-revealed .layer-logo,
  .js .banner.is-revealed .banner__title,
  .js .banner.is-revealed .layer-funder,
  .js .banner.is-revealed .layer-wave,
  .js .banner.is-revealed .layer-drops,
  .js .banner.is-revealed .card,
  .js .banner.is-revealed .banner__tags > .tag,
  .js .banner.is-revealed .banner__out {
    animation-name: fade-in;
    animation-duration: 0.45s;
    animation-timing-function: ease;
  }

  @keyframes fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
}
