/* Gaming Life — gaminglife.com
 *
 * The studio should look like the thing it makes: the games' own palette, flat fills, hard offset
 * shadows like a screen-printed sign, chunky rounded geometry. No gradients, no soft drop shadows.
 * Colours are the tokens from src/art/palette.js, restated in OKLCH so lightness steps are
 * perceptually even and the neutrals can be tinted toward the brand hues.
 */

/* Self-hosted variable fonts — two files, 69 KB, every weight. See fonts/README.md for why.
   `font-display: swap` so text is readable immediately and the page never blocks on a font. */
@font-face {
  font-family: 'Gabarito';
  src: url('fonts/gabarito-var.woff2') format('woff2-variations');
  font-weight: 400 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Hanken Grotesk';
  src: url('fonts/hanken-grotesk-var.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Ground: warm cream, tinted toward the sun accent. */
  --cream: oklch(97.6% 0.021 88);
  --cream-deep: oklch(93.5% 0.03 86);
  --cream-edge: oklch(88% 0.038 84);

  /* Ink: the games' #2b3a4a. Neutrals tinted toward it rather than toward grey. */
  --ink: oklch(34% 0.038 248);
  --ink-soft: oklch(50% 0.03 248);
  /* 4.6:1 on --cream. Was 64% lightness, which measured 3.13:1 and failed WCAG AA for normal
     text — and it carries the footer and the "last updated" line on the legal pages, which is
     exactly the text a parent reads on a phone. Don't lighten it again without re-measuring. */
  --ink-faint: oklch(55% 0.025 248);

  /* Accents, straight from the palette. Used sparingly — they work because they're rare. */
  --sun: oklch(86.5% 0.155 88);
  --amber: oklch(80% 0.155 72);
  --coral: oklch(70% 0.165 18);
  --mint: oklch(82% 0.09 178);
  --blue: oklch(66% 0.15 260);

  --shadow: 0 4px 0 var(--ink);
  --shadow-lg: 0 7px 0 var(--ink);

  /* 4pt scale, semantic names. */
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 16px;
  --space-lg: 24px;
  --space-xl: 40px;
  --space-2xl: 64px;
  --space-3xl: clamp(72px, 11vw, 128px);

  --radius: 14px;
  --radius-lg: 26px;

  --measure: 68ch;

  --font-display: Gabarito, ui-rounded, 'Segoe UI', system-ui, sans-serif;
  --font-body: 'Hanken Grotesk', system-ui, -apple-system, sans-serif;
}

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

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

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3 {
  font-family: var(--font-display);
  font-weight: 800;
  line-height: 1.02;
  letter-spacing: -0.022em;
  margin: 0;
  text-wrap: balance;
}

a {
  color: inherit;
}

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

/* ---------------------------------------------------------------- header */

.masthead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-lg);
  padding-block: var(--space-lg);
  flex-wrap: wrap;
}

.mark {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 20px;
  letter-spacing: -0.02em;
  text-decoration: none;
}

.mark svg {
  display: block;
}

.masthead nav {
  display: flex;
  gap: var(--space-lg);
  font-size: 15px;
  font-weight: 600;
}

.masthead nav a {
  text-decoration: none;
  color: var(--ink-soft);
  padding-bottom: 2px;
  border-bottom: 2px solid transparent;
  transition: color 0.15s cubic-bezier(0.22, 1, 0.36, 1), border-color 0.15s;
}

.masthead nav a:hover {
  color: var(--ink);
  border-bottom-color: var(--sun);
}

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

.hero {
  padding-block: clamp(var(--space-2xl), 8vw, 100px) var(--space-3xl);
}

/* `ch` here resolves against the h1's own font-size, not the body's — which is the point.
   Constrain the measure on the element that sets the type size, never on its container. */
.hero h1 {
  font-size: clamp(2.9rem, 8.5vw, 5.5rem);
  max-width: 13ch;
}

/* The one flourish: the studio name sits on a hand-drawn sun highlight. */
.hero h1 .lit {
  position: relative;
  white-space: nowrap;
}

.hero h1 .lit::after {
  content: '';
  position: absolute;
  inset: 0.62em -0.06em 0.02em -0.06em;
  background: var(--sun);
  border-radius: 4px 10px 5px 8px;
  z-index: -1;
}

.hero p {
  max-width: 40ch;
  margin: var(--space-lg) 0 0;
  font-size: clamp(1.05rem, 2.1vw, 1.32rem);
  line-height: 1.5;
  color: var(--ink-soft);
}

/* ---------------------------------------------------------------- games */

.section-label {
  display: flex;
  align-items: baseline;
  gap: var(--space-md);
  margin-bottom: var(--space-xl);
}

.section-label h2 {
  font-size: 1.05rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 700;
}

.section-label .rule {
  flex: 1;
  height: 2px;
  background: var(--cream-edge);
}

.games {
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
  align-items: start;
}

.game {
  display: block;
  text-decoration: none;
  background: var(--cream-deep);
  border: 2px solid var(--ink);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  box-shadow: var(--shadow-lg);
}

/* Two games are a composition, not a repeating unit — the first one gets the weight. */
.game--lead {
  background: var(--sun);
}

/* No hover lift while these are <article> and not <a>. A card that rises under the cursor promises
   a click, and there is nothing to click yet. The art responds instead (see the idle motion below),
   which is honest. Restore the lift here when the games go live and these become links. */

.game figure {
  margin: 0 0 var(--space-lg);
  aspect-ratio: 16 / 10;
  display: grid;
  place-items: center;
  background: var(--cream);
  border: 2px solid var(--ink);
  border-radius: var(--radius);
  overflow: hidden;
}

.game figure svg {
  width: 100%;
  height: 100%;
  display: block;
}

.game h3 {
  font-size: 1.65rem;
}

.game p {
  margin: var(--space-sm) 0 var(--space-md);
  color: var(--ink-soft);
  font-size: 0.97rem;
  line-height: 1.5;
}

.game--lead p {
  color: oklch(40% 0.05 78);
}

.tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.tags span {
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 3px 11px;
  background: var(--cream);
}

/* The next game. Deliberately not a card — it's a gap, and it should read as one. */
.next {
  border: 2px dashed var(--cream-edge);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  display: grid;
  place-content: center;
  text-align: center;
  min-height: 190px;
  color: var(--ink-faint);
}

.next strong {
  display: block;
  font-family: var(--font-display);
  font-size: 1.35rem;
  color: var(--ink-soft);
  margin-bottom: var(--space-xs);
}

/* ---------------------------------------------------------------- how */

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

.how-grid {
  display: grid;
  gap: var(--space-xl) var(--space-2xl);
  grid-template-columns: repeat(auto-fit, minmax(255px, 1fr));
}

.how-grid h3 {
  font-size: 1.28rem;
  margin-bottom: var(--space-sm);
}

.how-grid p {
  margin: 0;
  color: var(--ink-soft);
  font-size: 0.97rem;
}

.how-grid .n {
  font-family: var(--font-display);
  font-size: 0.82rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  color: var(--coral);
  display: block;
  margin-bottom: var(--space-sm);
}

/* ---------------------------------------------------------------- about */

.about {
  padding: var(--space-xl);
  background: var(--ink);
  color: var(--cream);
  border-radius: var(--radius-lg);
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  align-items: start;
}

.about h2 {
  font-size: clamp(1.6rem, 3.4vw, 2.3rem);
  margin-bottom: var(--space-md);
}

.about p {
  margin: 0 0 var(--space-md);
  color: oklch(88% 0.02 240);
  max-width: 46ch;
}

.about p:last-child {
  margin-bottom: 0;
}

.about a {
  color: var(--sun);
  text-underline-offset: 3px;
}

.contact {
  background: oklch(40% 0.035 248);
  border-radius: var(--radius);
  padding: var(--space-lg);
  font-size: 0.93rem;
}

.contact dt {
  font-weight: 700;
  color: var(--sun);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin-bottom: 2px;
}

.contact dd {
  margin: 0 0 var(--space-md);
  color: oklch(90% 0.015 240);
}

.contact dd:last-child {
  margin-bottom: 0;
}

/* ---------------------------------------------------------------- footer */

.foot {
  margin-top: var(--space-3xl);
  padding-block: var(--space-xl);
  border-top: 2px solid var(--cream-edge);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md) var(--space-lg);
  justify-content: space-between;
  align-items: center;
  font-size: 0.87rem;
  color: var(--ink-faint);
}

.foot nav {
  display: flex;
  gap: var(--space-lg);
}

.foot a {
  color: var(--ink-soft);
  text-decoration: none;
  border-bottom: 1px solid var(--cream-edge);
}

.foot a:hover {
  border-bottom-color: var(--ink-soft);
}

/* ---------------------------------------------------------------- legal pages */

.legal {
  padding-block: var(--space-2xl) var(--space-xl);
  max-width: var(--measure);
}

.legal h1 {
  font-size: clamp(2.2rem, 6vw, 3.4rem);
  margin-bottom: var(--space-md);
}

.legal .updated {
  color: var(--ink-faint);
  font-size: 0.87rem;
  margin: 0 0 var(--space-xl);
}

.legal h2 {
  font-size: 1.45rem;
  margin: var(--space-2xl) 0 var(--space-md);
}

.legal h3 {
  font-size: 1.08rem;
  margin: var(--space-xl) 0 var(--space-sm);
}

.legal p,
.legal li {
  color: var(--ink-soft);
}

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

.legal ul {
  margin: 0 0 var(--space-md);
  padding-left: 1.15em;
}

.legal li {
  margin-bottom: var(--space-sm);
}

.legal a {
  color: var(--ink);
  text-underline-offset: 3px;
}

.legal strong {
  color: var(--ink);
}

/* The plain-language summary that opens each legal page — the part parents actually read. */
.gist {
  background: var(--sun);
  border: 2px solid var(--ink);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  margin-bottom: var(--space-2xl);
  box-shadow: var(--shadow);
}

.gist h2 {
  margin: 0 0 var(--space-sm);
  font-size: 1.2rem;
}

.gist p,
.gist li {
  color: oklch(36% 0.05 78);
}

.gist p:last-child,
.gist ul:last-child {
  margin-bottom: 0;
}

table.plain {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 var(--space-lg);
  font-size: 0.93rem;
}

table.plain th,
table.plain td {
  text-align: left;
  padding: 10px 12px;
  border-bottom: 1px solid var(--cream-edge);
  vertical-align: top;
  color: var(--ink-soft);
}

table.plain th {
  font-weight: 700;
  color: var(--ink);
  border-bottom: 2px solid var(--ink);
}

/* ---------------------------------------------------------------- responsive */

@media (max-width: 720px) {
  .about {
    grid-template-columns: 1fr;
  }

  .hero {
    max-width: none;
  }
}

/* ---------------------------------------------------------------- motion
 *
 * Two layers, and no more: one orchestrated page-load reveal, and one slow idle loop per game card.
 * The idle loops are not decoration — each one animates the thing its game actually does, so the
 * card shows the verb before you've read a word. Transform and opacity only; nothing here can
 * trigger layout.
 */

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
}

/* The signature moment: the sun highlight wipes in under the word after the headline lands. */
@keyframes wipe {
  from {
    transform: scaleX(0);
  }
}

@keyframes road {
  to {
    transform: translateX(-76px);
  } /* exactly one dash pitch, so the loop is seamless */
}

@keyframes march {
  to {
    stroke-dashoffset: -20;
  } /* one dash+gap period of the 2/18 pattern */
}

@keyframes spin {
  to {
    transform: rotate(-360deg);
  } /* anticlockwise: the surface travels left, toward the hero */
}

.masthead,
.hero h1,
.hero p,
#games .section-label,
.games > *,
.how,
.about {
  animation: rise 0.62s cubic-bezier(0.22, 1, 0.36, 1) both;
}

.hero h1 { animation-delay: 60ms; }
.hero p { animation-delay: 140ms; }
#games .section-label { animation-delay: 220ms; }
.games > *:nth-child(1) { animation-delay: 280ms; }
.games > *:nth-child(2) { animation-delay: 340ms; }
.games > *:nth-child(3) { animation-delay: 400ms; }
.how { animation-delay: 460ms; }
.about { animation-delay: 520ms; }

.hero h1 .lit::after {
  transform-origin: left center;
  animation: wipe 0.5s cubic-bezier(0.22, 1, 0.36, 1) 520ms both;
}

/* Idle loops. Deliberately slow — a studio page with three things flashing is a fairground. */
.art-road {
  animation: road 2.6s linear infinite;
}

.art-traj {
  animation: march 1.6s linear infinite;
}

.art-planet {
  transform-box: view-box;
  transform-origin: 160px 352px; /* the planet's centre, well below the frame */
  animation: spin 44s linear infinite;
}

/* No hover speed-up. Changing animation-duration mid-loop re-derives progress from elapsed time,
   so the planet visibly jumps — and half the audience is on a phone with no hover anyway. The
   always-on idle is the whole effect. */

/* ---------------------------------------------------------------- focus */

a:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 3px;
  border-radius: 4px;
}

.cta:focus-visible,
.mark:focus-visible {
  outline-offset: 5px;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    /* Essential, and easy to miss: without this the *infinite* idle loops above would run at
       0.01ms per iteration forever — a strobe, which is the exact opposite of the request. */
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}
