/* ---------------------------------------------------------------
   Silly Game Studio — landing page
   Single stylesheet, no build step, no dependencies.

   Palette rule: six soft rainbow hues, each with a deeper "ink"
   variant for text. Pastels are for fills, borders and shadows;
   the deep variants are for anything you actually have to read.
   Each card commits to ONE hue — that's what keeps a rainbow from
   turning garish.
   --------------------------------------------------------------- */

:root {
  /* pastel fill            deep text-safe variant

     The -ink variants are deliberately darker than they need to look
     nice — every one of them clears 4.5:1 against its own tinted pill
     background. Lightening them is how this palette breaks. */
  --rb-pink:   #f0a0c0;    --rb-pink-ink:   #a83e69;
  --rb-peach:  #f7ab8b;    --rb-peach-ink:  #9e4526;
  --rb-butter: #f2cf85;    --rb-butter-ink: #7a5a0e;
  --rb-mint:   #93d9bd;    --rb-mint-ink:   #1a6b4f;
  --rb-sky:    #92c7ea;    --rb-sky-ink:    #285f80;
  --rb-lilac:  #b9abe8;    --rb-lilac-ink:  #5c48a8;

  --bg:       #fffaf8;
  --bg-card:  #fffdfc;
  --ink:      #4a3f52;   /* soft plum instead of harsh black */
  --ink-soft: #6f6379;
  --rule:     #e8dfe6;

  --radius: 26px;
  --measure: 64rem;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg:       #1d1926;
    --bg-card:  #292336;
    --ink:      #f6f0f8;
    --ink-soft: #a89db4;
    --rule:     #3d3450;
  }
}

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

body {
  position: relative;
  margin: 0;
  padding: clamp(1.5rem, 5vw, 4rem) clamp(1.25rem, 5vw, 2rem) 3rem;
  background: var(--bg);
  color: var(--ink);
  font-family: ui-rounded, "SF Pro Rounded", "Hiragino Maru Gothic ProN",
               "Varela Round", "Segoe UI", system-ui, -apple-system, sans-serif;
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* Soft pastel wash in the corners. Very low opacity — it should read
   as "warm paper", not as a gradient anyone notices. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(46rem 30rem at 8% -6%,  var(--rb-pink)  0%, transparent 60%),
    radial-gradient(40rem 26rem at 96% 4%,  var(--rb-sky)   0%, transparent 60%),
    radial-gradient(44rem 28rem at 50% 104%, var(--rb-butter) 0%, transparent 60%);
  opacity: .2;
}

@media (prefers-color-scheme: dark) {
  body::before { opacity: .12; }
}

/* --- Hero ------------------------------------------------------

   The title is white, so the panel behind it has to carry the colour,
   and every stop has to be dark enough for white to read on it
   (white on a pastel is about 1.9:1 — nowhere near enough).

   That constraint is why this gradient runs rose → violet → indigo →
   blue → teal → green and skips yellow: a yellow dark enough for white
   text is, by definition, olive-brown, and it drags the neighbouring
   stops into mud. The full six-hue rainbow still appears on the rule
   under "The games" and across the cards, where the backgrounds are
   light and the pastels can be themselves.

   Worst stop against white is 4.95:1. Re-colouring? Check every stop.
   --------------------------------------------------------------- */

:root {
  --hero-1: #b0407a;  /* rose   5.44:1 vs #fff */
  --hero-2: #7c46a8;  /* violet 6.36:1 */
  --hero-3: #4f4bb0;  /* indigo 7.08:1 */
  --hero-4: #2f6f96;  /* blue   6.90:1 */
  --hero-5: #17706a;  /* teal   5.90:1 */
  --hero-6: #1b6b48;  /* green  6.46:1 */
}

.hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  max-width: var(--measure);
  margin: 0 auto clamp(2.5rem, 7vw, 4.5rem);
  padding: clamp(3.25rem, 11vw, 6.5rem) clamp(1.5rem, 5vw, 3rem);
  border-radius: clamp(28px, 5vw, 44px);
  text-align: center;
  background: linear-gradient(112deg,
    var(--hero-1)   0%,
    var(--hero-2)  20%,
    var(--hero-3)  39%,
    var(--hero-4)  60%,
    var(--hero-5)  80%,
    var(--hero-6) 100%);
  box-shadow: 0 26px 54px -30px var(--hero-3);
}

/* Soft highlights so the panel reads as lit rather than flat. These sit
   behind the title and lighten it, which costs white-text contrast — the
   alphas are kept low deliberately. Raising them is the quickest way to
   push the title under AA. */
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(34rem 22rem at 18% 0%, rgba(255,255,255,.18) 0%, transparent 62%),
    radial-gradient(30rem 20rem at 88% 112%, rgba(255,255,255,.07) 0%, transparent 60%);
}

/* Decorative bubbles. Purely visual — aria-hidden in the markup. */
.hero__bubble {
  position: absolute;
  z-index: -1;
  border-radius: 50%;
  background: rgba(255, 255, 255, .08);
  animation: float 7s ease-in-out infinite;
}

/* Pushed into the corners, clear of the tagline's box. A bubble sitting
   behind small white text costs about 0.5 of a contrast point, which is
   the whole margin. */
.hero__bubble:nth-of-type(1) { width: 7rem;   height: 7rem;   top: -1.5rem; left: -1rem;   animation-delay: -.4s; }
.hero__bubble:nth-of-type(2) { width: 3.5rem; height: 3.5rem; bottom: -.75rem; left: 8%;   animation-delay: -2.1s; }
.hero__bubble:nth-of-type(3) { width: 5rem;   height: 5rem;   top: -1rem;   right: 4%;     animation-delay: -3.4s; }
.hero__bubble:nth-of-type(4) { width: 2.5rem; height: 2.5rem; bottom: -.5rem; right: 12%;  animation-delay: -1.2s; }

@keyframes float {
  0%, 100% { transform: translateY(0)      scale(1);    }
  50%      { transform: translateY(-14px)  scale(1.06); }
}

.wordmark {
  display: inline-block;
  /* Capped so "SillyGame.studio" stays on one line at the hero's max
     width — above ~5.5rem the ".studio" drops to its own line and reads
     as a mistake rather than a choice. */
  font-size: clamp(2.4rem, 9vw, 5.5rem);
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.05;
  text-decoration: none;
  color: #fff;
  text-shadow: 0 3px 0 rgba(60, 30, 60, .16);
}

/* Each letter is its own inline-block so it can bounce independently. */
.wordmark span {
  display: inline-block;
  transition: transform .22s cubic-bezier(.34, 1.7, .64, 1);
}

.wordmark:hover span { transform: translateY(-6px) rotate(-5deg); }
.wordmark:hover span:nth-child(even) { transform: translateY(-6px) rotate(5deg); }

/* Staggered delay so the bounce ripples left-to-right. */
.wordmark span:nth-child(1) { transition-delay: .00s }
.wordmark span:nth-child(2) { transition-delay: .03s }
.wordmark span:nth-child(3) { transition-delay: .06s }
.wordmark span:nth-child(4) { transition-delay: .09s }
.wordmark span:nth-child(5) { transition-delay: .12s }
.wordmark span:nth-child(6) { transition-delay: .15s }
.wordmark span:nth-child(7) { transition-delay: .18s }
.wordmark span:nth-child(8) { transition-delay: .21s }
.wordmark span:nth-child(9) { transition-delay: .24s }

.wordmark__suffix {
  color: rgba(255, 255, 255, .82);
  font-weight: 600;
  letter-spacing: -0.03em;
}

.tagline {
  margin: 1.5rem auto 0;
  max-width: 34rem;
  color: #fff;
  font-size: clamp(1.05rem, 2.4vw, 1.25rem);
  text-wrap: balance;
}

/* --- Section heading ------------------------------------------ */

.section-heading {
  max-width: var(--measure);
  margin: 0 auto 1.75rem;
  font-size: .8rem;
  font-weight: 800;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  display: flex;
  align-items: center;
  gap: 1rem;
}

/* The one place all six hues appear together. */
.section-heading::after {
  content: "";
  flex: 1;
  height: 5px;
  border-radius: 999px;
  opacity: .65;
  background: linear-gradient(90deg,
    var(--rb-pink), var(--rb-peach), var(--rb-butter),
    var(--rb-mint), var(--rb-sky), var(--rb-lilac));
}

/* --- Game grid ------------------------------------------------ */

.game-grid {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17.5rem), 1fr));
  gap: 1.5rem;
}

/* Hue classes set --hue (pastel) and --hue-deep (readable). They must
   NOT set --hue-ink: that is assigned on `.card` alone, in two
   mutually-exclusive media contexts, so the light/dark swap can never
   lose a specificity fight. Setting the hues inline on the element
   breaks exactly this. */
.card {
  --hue: var(--rb-lilac);
  --hue-deep: var(--rb-lilac-ink);
  --hue-ink: var(--hue-deep);

  position: relative;
  overflow: hidden;            /* clips the thumbnail to the card radius */
  background: var(--bg-card);
  background: color-mix(in srgb, var(--hue) 9%, var(--bg-card));
  border-radius: var(--radius);
  box-shadow: 0 16px 28px -18px var(--hue-ink);
  transition: transform .2s cubic-bezier(.34, 1.5, .64, 1),
              box-shadow .2s ease;
}

/* Hue classes set --hue (pastel) and --hue-deep (readable). Two ordering
   rules keep this working, and both are load-bearing:
   1. These must come AFTER `.card`. Same specificity, so the later rule
      wins — declared above `.card`, every card silently falls back to
      the lilac default.
   2. They must NOT set --hue-ink. That is assigned on `.card` alone, in
      two mutually-exclusive media contexts, so the light/dark swap can
      never lose a specificity fight. Setting hues inline on the element
      breaks that too, since inline styles beat any stylesheet rule. */
.card--pink   { --hue: var(--rb-pink);   --hue-deep: var(--rb-pink-ink);   }
.card--peach  { --hue: var(--rb-peach);  --hue-deep: var(--rb-peach-ink);  }
.card--butter { --hue: var(--rb-butter); --hue-deep: var(--rb-butter-ink); }
.card--mint   { --hue: var(--rb-mint);   --hue-deep: var(--rb-mint-ink);   }
.card--sky    { --hue: var(--rb-sky);    --hue-deep: var(--rb-sky-ink);    }
.card--lilac  { --hue: var(--rb-lilac);  --hue-deep: var(--rb-lilac-ink);  }

@media (prefers-color-scheme: dark) {
  /* Pastels are light, so on a dark card they become the readable tone
     and the deep variants would be dark-on-dark. */
  .card { --hue-ink: var(--hue); }
}

.card__link {
  display: flex;
  flex-direction: column;
  height: 100%;
  text-decoration: none;
  color: inherit;
}

/* --- Thumbnail ---

   Fixed aspect so the grid stays even whatever the source image is.
   Swapping the SVG for a real screenshot needs no CSS change: any wide
   image gets cropped to 16:10 by object-fit. */
.card__thumb {
  position: relative;
  display: block;
  padding: .8rem .8rem 0;
}

/* The ratio lives on the image, not the wrapper: the wrapper is
   border-box, so an aspect-ratio there would be consumed by the padding
   rather than sit inside it. */
.card__thumb img {
  display: block;
  width: 100%;
  height: auto;              /* without this the width/height attributes on
                                the <img> win and aspect-ratio is ignored */
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-radius: calc(var(--radius) - .8rem);
  transition: transform .4s cubic-bezier(.34, 1.3, .64, 1);
}

.card:not(.card--empty):hover .card__thumb img { transform: scale(1.045); }

.card__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  padding: 2.5rem 1.5rem 1.35rem;   /* top clears the overlapping badge */
}

.card:not(.card--empty):hover,
.card:focus-within {
  transform: translateY(-6px) rotate(-.75deg);
  box-shadow: 0 24px 34px -20px var(--hue-ink);
}

.card:nth-child(even):not(.card--empty):hover,
.card:nth-child(even):focus-within { transform: translateY(-6px) rotate(.75deg); }

.card__link:focus-visible {
  outline: 3px solid var(--hue-ink);
  outline-offset: 5px;
  border-radius: calc(var(--radius) - 6px);
}

/* Sparkle that pops on hover. Decorative only. */
.card:not(.card--empty) .card__link::after {
  content: "✦";
  position: absolute;
  top: .9rem;
  right: 1.1rem;
  font-size: 1rem;
  color: var(--hue-ink);
  opacity: 0;
  transform: scale(.4) rotate(-30deg);
  transition: opacity .22s ease, transform .3s cubic-bezier(.34, 1.8, .64, 1);
}

.card:hover .card__link::after,
.card:focus-within .card__link::after {
  opacity: .85;
  transform: scale(1) rotate(0deg);
}

/* Emoji sits in a soft organic blob, straddling the thumbnail's lower
   edge so the image and the card body read as one object. */
.card__art {
  position: absolute;
  left: 1.5rem;              /* lines up with the body text below */
  bottom: -1.6rem;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3.6rem;
  height: 3.6rem;
  font-size: 1.9rem;
  line-height: 1;
  background: var(--hue);
  background: color-mix(in srgb, var(--hue) 55%, #fff);
  border-radius: 46% 54% 52% 48% / 52% 46% 54% 48%;
  box-shadow: 0 0 0 4px var(--bg-card);
  transition: transform .35s cubic-bezier(.34, 1.6, .64, 1),
              border-radius .35s ease;
}

.card:hover .card__art {
  transform: rotate(-8deg) scale(1.06);
  border-radius: 54% 46% 48% 52% / 46% 54% 48% 52%;
}

.card__title {
  margin: 0 0 .5rem;
  font-size: 1.32rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--hue-ink);
}

.card__blurb {
  margin: 0 0 1.5rem;
  color: var(--ink-soft);
  font-size: .95rem;
  text-wrap: pretty;
}

/* Pill, so it reads as a friendly button rather than raw URL text. */
.card__cta {
  align-self: flex-start;
  margin-top: auto;
  padding: .4rem .85rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--hue) 26%, #fff);
  color: var(--hue-ink);
  font-size: .78rem;
  font-weight: 800;
  letter-spacing: .01em;
  word-break: break-word;
}

@media (prefers-color-scheme: dark) {
  .card__art { background: color-mix(in srgb, var(--hue) 32%, transparent); }
  /* Kept low: the pill sits behind pastel text here, so a heavier tint
     closes the contrast gap rather than opening it. */
  .card__cta { background: color-mix(in srgb, var(--hue) 10%, transparent); }
}

/* Muted via explicit colours rather than `opacity`, which would quietly
   drag the text contrast below AA along with everything else. */
.card--empty {
  background: transparent;
  box-shadow: none;
}

/* No screenshot yet, so a pseudo-element stands in for the image and
   holds the same box. */
.card--empty .card__thumb::before {
  content: "";
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  border-radius: calc(var(--radius) - .8rem);
  background: repeating-linear-gradient(-45deg,
    var(--bg-card) 0 14px,
    var(--rule)    14px 28px);
  opacity: .5;
}

.card--empty .card__art { background: var(--rule); filter: grayscale(.7); }
.card--empty .card__title { color: var(--ink-soft); }
.card--empty .card__blurb { color: var(--ink-soft); }
.card--empty .card__cta { background: var(--rule); color: var(--ink); }

@media (prefers-color-scheme: dark) {
  .card--empty .card__cta { color: var(--ink-soft); }
}

/* --- Colophon ------------------------------------------------- */

.colophon {
  max-width: var(--measure);
  margin: clamp(3rem, 9vw, 5rem) auto 0;
  padding-top: 1.5rem;
  border-top: 2px dotted var(--rule);
  color: var(--ink-soft);
  font-size: .85rem;
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1.5rem;
  justify-content: space-between;
}

.colophon p { margin: 0; }

.colophon a {
  color: var(--rb-lilac-ink);
  font-weight: 700;
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
  text-decoration-style: wavy;
}

.colophon a:hover { color: var(--rb-pink-ink); }

@media (prefers-color-scheme: dark) {
  .colophon a { color: var(--rb-lilac); }
  .colophon a:hover { color: var(--rb-pink); }
}

/* --- 404 ------------------------------------------------------ */

.notfound {
  max-width: 34rem;
  margin: clamp(3rem, 12vh, 7rem) auto;
  text-align: center;
}

.notfound__code {
  margin: 0;
  font-size: clamp(4.5rem, 19vw, 8.5rem);
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1;
}

.notfound__code span { display: inline-block; }
.notfound__code span:nth-child(1) { color: var(--rb-pink-ink); }
.notfound__code span:nth-child(2) { color: var(--rb-butter-ink); }
.notfound__code span:nth-child(3) { color: var(--rb-sky-ink); }

@media (prefers-color-scheme: dark) {
  .notfound__code span:nth-child(1) { color: var(--rb-pink); }
  .notfound__code span:nth-child(2) { color: var(--rb-butter); }
  .notfound__code span:nth-child(3) { color: var(--rb-sky); }
}

.notfound h1 { font-size: 1.6rem; font-weight: 800; margin: .75rem 0 0; }
.notfound__msg { color: var(--ink-soft); margin: .75rem 0 2rem; }

.button {
  display: inline-block;
  padding: .8rem 1.6rem;
  background: color-mix(in srgb, var(--rb-mint) 30%, #fff);
  border: 2.5px solid var(--rb-mint);
  border-radius: 999px;
  box-shadow: 0 5px 0 -1px var(--rb-mint);
  color: var(--rb-mint-ink);
  font-weight: 800;
  text-decoration: none;
  transition: transform .2s cubic-bezier(.34, 1.5, .64, 1), box-shadow .2s ease;
}

.button:hover {
  transform: translateY(-4px);
  box-shadow: 0 9px 0 -1px var(--rb-mint);
}

@media (prefers-color-scheme: dark) {
  .button {
    background: color-mix(in srgb, var(--rb-mint) 18%, transparent);
    color: var(--rb-mint);
  }
}

/* --- Motion opt-out ------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition: none !important;
    animation: none !important;
  }
  .card:hover, .card:focus-within,
  .card:nth-child(even):not(.card--empty):hover,
  .button:hover { transform: none; }
  .wordmark:hover span,
  .wordmark:hover span:nth-child(even),
  .card:hover .card__art { transform: none; }
}
