/* ==========================================================================
   Farting Pixel neon-arcade theme
   Inspired by the RandALL app: deep-navy base, drifting glow blobs,
   per-app neon accents, glowing glass cards, gradient-sweep title.
   Dark-only by design (neon doesn't read on white). Motion is gated behind
   prefers-reduced-motion.
   ========================================================================== */

:root {
  color-scheme: dark;

  /* Surfaces (RandALL "deep navy" dark palette) */
  --bg-base: #070811;
  --bg-elevated: #11131f;
  --bg-sunken: #04050b;

  /* Text */
  --text: #f3eefe;
  --text-secondary: #a59fbd;
  --text-tertiary: #6f6a8a;

  /* Brand */
  --accent: #a78bff;          /* electric violet, overridden per card */
  --accent-soft: #7a66c8;

  --radius-card: 22px;
  --radius-pill: 999px;
  --max-width: 1080px;

  --shadow-card: 0 10px 30px rgba(0, 0, 0, 0.45);
}

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

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

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  font-size: 16px;
  line-height: 1.6;
  color: var(--text);
  background: var(--bg-base);
  min-height: 100vh;
  position: relative;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Background stack, back to front:
     .glow-field    (-3)  drifting glow blobs
     body::after    (-2)  vignette, keeps cards legible
     .pixel-field   (-1)  the farting pixels
   All three sit below page content, which is at the default z-index. */

/* ----- Glow blobs ------------------------------------------------------ */

/* The mesh used to be one blurred layer, so all four glows drifted in lockstep
   and the backdrop read as a still image. One span per blob, each wandering on
   its own clock (co-prime-ish durations, so the mix never visibly repeats),
   keeps the colours re-blending forever — and with the phases offset,
   something is always mid-move instead of everything pausing at once.
   No filter: the gradients carry their own falloff, and only transform and
   opacity animate, so the whole field stays on the compositor. */
.glow-field {
  position: fixed;
  inset: 0;
  z-index: -3;
  overflow: hidden;
  pointer-events: none;
}
.glow-field__blob {
  position: absolute;
  aspect-ratio: 1;
  border-radius: 50%;
  /* Centering lives here, not in the keyframes' start pose, so a blob with
     animation: none (reduced motion) still sits centered on its anchor. */
  transform: translate(-50%, -50%);
  will-change: transform, opacity;
}
.glow-field__blob--violet {
  width: 48vmax;
  left: 18%; top: 20%;
  background: radial-gradient(closest-side, rgba(91, 58, 255, 0.34), rgba(91, 58, 255, 0.16) 45%, transparent 72%);
  animation: blobDriftA 26s ease-in-out -8s infinite alternate;
}
.glow-field__blob--cyan {
  width: 42vmax;
  left: 82%; top: 28%;
  background: radial-gradient(closest-side, rgba(61, 208, 255, 0.22), rgba(61, 208, 255, 0.10) 45%, transparent 72%);
  animation: blobDriftB 19s ease-in-out -4s infinite alternate;
}
.glow-field__blob--pink {
  width: 50vmax;
  left: 72%; top: 82%;
  background: radial-gradient(closest-side, rgba(255, 95, 168, 0.22), rgba(255, 95, 168, 0.10) 45%, transparent 72%);
  animation: blobDriftC 31s ease-in-out -14s infinite alternate;
}
.glow-field__blob--orange {
  width: 44vmax;
  left: 22%; top: 86%;
  background: radial-gradient(closest-side, rgba(255, 138, 61, 0.16), rgba(255, 138, 61, 0.07) 45%, transparent 72%);
  animation: blobDriftD 23s ease-in-out -11s infinite alternate;
}
/* A faint whiff of the fart green, so the gas colour lives in the backdrop too. */
.glow-field__blob--green {
  width: 30vmax;
  left: 52%; top: 56%;
  background: radial-gradient(closest-side, rgba(67, 224, 138, 0.11), rgba(67, 224, 138, 0.05) 45%, transparent 72%);
  animation: blobDriftE 37s ease-in-out -20s infinite alternate;
}

/* Faint top-to-bottom darkening + vignette so cards stay legible. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  background:
    radial-gradient(120% 80% at 50% -10%, transparent 40%, rgba(4, 5, 11, 0.75) 100%),
    linear-gradient(180deg, rgba(7, 8, 17, 0.1), rgba(7, 8, 17, 0.55));
  pointer-events: none;
}

/* ----- Farting pixels -------------------------------------------------- */

/* Low-res canvas blown up to viewport size. `pixelated` is the whole point:
   the pixels must stay hard-edged squares, not a blurry upscale. */
.pixel-field {
  position: fixed;
  inset: 0;
  z-index: -1;
  width: 100%;
  height: 100%;
  pointer-events: none;
  image-rendering: crisp-edges;   /* fallback first; pixelated wins where supported */
  image-rendering: pixelated;
}

/* Press F: a brief green haze creeps in from the edges. */
body.is-fart-flash .pixel-field {
  animation: fartFlash 900ms ease-out;
}

img { max-width: 100%; height: auto; display: block; }

a { color: inherit; text-decoration: none; }

h1, h2, h3 { line-height: 1.15; font-weight: 800; letter-spacing: -0.03em; }

.skip-link { position: absolute; left: -9999px; top: 0; }
.skip-link:focus {
  left: 1rem; top: 1rem;
  background: var(--bg-elevated);
  color: var(--text);
  padding: 0.5rem 1rem;
  border-radius: 8px;
  z-index: 10;
}

.container {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 1.5rem;
}

/* ----- Nav ------------------------------------------------------------- */

.nav { padding: 1.5rem 0; }
.nav__inner { display: flex; align-items: center; }

.nav-logo {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-weight: 800;
  font-size: 1.05rem;
  letter-spacing: -0.02em;
}
.nav-logo__dot {
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 3px;
  background: linear-gradient(135deg, #a78bff, #3dd0ff);
  box-shadow: 0 0 14px rgba(167, 139, 255, 0.8);
  animation: pixelPulse 3.2s ease-in-out infinite;
}

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

.hero { padding: 4.5rem 0 1.75rem; }

.hero__eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.28em;
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--text-tertiary);
  margin-bottom: 1rem;
}

.hero__title {
  font-size: clamp(3rem, 9vw, 5.5rem);
  letter-spacing: -0.045em;
  background: linear-gradient(100deg, #a78bff, #3dd0ff, #ff5fa8, #ffc83d, #a78bff);
  background-size: 300% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 0 28px rgba(167, 139, 255, 0.35));
  animation: titleSweep 9s ease-in-out infinite;
}

.hero__subtitle {
  margin-top: 1rem;
  color: var(--text-secondary);
  font-size: clamp(1.05rem, 2.4vw, 1.3rem);
  /* Never wrap to a second line. A wrapping subtitle used to shove the whole
     page down as the tagline rotated. Single line, fixed height, always. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-height: 1.6em;
}
.hero__subtitle-line {
  display: inline-block;
  transition: opacity 0.35s ease, transform 0.35s ease;
}

/* ----- Latest drop ----------------------------------------------------- */

/* One slim line under the hero pointing at the newest release, so "what did he
   just put out?" is answerable without scrolling. It links to the same place as
   that project's card further down — a shortcut, not a duplicate card. */
.latest {
  margin-top: 2rem;
  display: inline-flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.5rem 0.8rem;
  padding: 0.7rem 1.1rem;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--accent) 10%, rgba(255, 255, 255, 0.03));
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}
.latest:hover {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4),
              0 0 30px color-mix(in srgb, var(--accent) 25%, transparent);
}

.latest__flag {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-size: 0.64rem;
  font-weight: 800;
  color: var(--accent);
}
.latest__blip {
  width: 6px;
  height: 6px;
  background: var(--accent);
  box-shadow: 0 0 10px var(--accent);
  animation: pixelBlip 1.8s steps(1, end) infinite;
}
.latest__name { font-weight: 800; letter-spacing: -0.02em; }
.latest__tagline { color: var(--text-tertiary); font-size: 0.9rem; }
.latest__cta {
  font-size: 0.82rem;
  font-weight: 700;
  color: color-mix(in srgb, var(--accent) 70%, var(--text));
}

/* ----- Android testers callout ----------------------------------------- */

/* Temporary by design: Google will not release Carpet Diem on the Play Store
   until 12 testers keep it installed for 14 days, so the recruiting pitch and
   all three steps sit right on the home page instead of behind a click. The
   whole block is driven by the `testers` object in scripts/build-home.js and
   disappears (with this CSS) once the test is over. */
.testers {
  padding: 1.6rem 1.7rem 1.7rem;
  border-radius: var(--radius-card);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent) 9%, rgba(255, 255, 255, 0.03)),
    rgba(255, 255, 255, 0.01));
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  box-shadow: var(--shadow-card),
              0 0 40px color-mix(in srgb, var(--accent) 16%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.testers__head {
  display: flex;
  align-items: center;
  gap: 1.25rem;
}
.testers__icon {
  width: clamp(72px, 12vw, 104px);
  height: auto;
  aspect-ratio: 1;
  border-radius: 24%;
  flex: none;
  box-shadow:
    0 8px 22px rgba(0, 0, 0, 0.5),
    0 0 28px color-mix(in srgb, var(--accent) 42%, transparent);
}
.testers__flag {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-size: 0.64rem;
  font-weight: 800;
  color: var(--accent);
}
.testers__blip {
  width: 6px;
  height: 6px;
  background: var(--accent);
  box-shadow: 0 0 10px var(--accent);
  animation: pixelBlip 1.8s steps(1, end) infinite;
}
.testers__title {
  margin-top: 0.2rem;
  font-size: clamp(1.3rem, 3.4vw, 1.7rem);
  letter-spacing: -0.02em;
}
.testers__pitch {
  margin-top: 0.45rem;
  color: var(--text-secondary);
  font-size: 0.95rem;
  max-width: 58ch;
}
.testers__pitch strong { color: var(--text); }

.testers__steps {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 1rem;
  margin-top: 1.4rem;
}
.testers__step {
  position: relative;
  padding: 1rem 1.1rem 1.1rem 3.1rem;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
}
/* Square, like every other pixel on this page — not a circle. */
.testers__num {
  position: absolute;
  top: 1rem;
  left: 1.05rem;
  width: 1.5rem;
  height: 1.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.85rem;
  font-weight: 800;
  color: #0a0b16;
  background: var(--accent);
  box-shadow: 0 0 14px color-mix(in srgb, var(--accent) 60%, transparent);
}
.testers__step-title {
  font-size: 0.98rem;
  letter-spacing: -0.01em;
}
.testers__btn {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-top: 0.65rem;
  padding: 0.5rem 0.95rem;
  border-radius: var(--radius-pill);
  font-size: 0.82rem;
  font-weight: 700;
  text-decoration: none;
  background: var(--accent);
  color: #0a0b16;
  box-shadow: 0 0 18px color-mix(in srgb, var(--accent) 45%, transparent);
  transition: background 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
.testers__btn:hover {
  transform: translateY(-1px);
  background: color-mix(in srgb, var(--accent) 82%, white);
  box-shadow: 0 8px 24px color-mix(in srgb, var(--accent) 55%, transparent);
}
.testers__note {
  margin-top: 0.55rem;
  color: var(--text-tertiary);
  font-size: 0.85rem;
}
.testers__fine {
  margin-top: 1.1rem;
  color: var(--text-tertiary);
  font-size: 0.85rem;
}
.testers__fine a {
  color: color-mix(in srgb, var(--accent) 70%, var(--text));
  text-decoration-color: color-mix(in srgb, var(--accent) 50%, transparent);
}

@media (max-width: 560px) {
  .testers__head { flex-direction: column; align-items: flex-start; }
}

/* ----- Ship or Die strip ------------------------------------------------ */

/* Live status line fed by ship-or-die.com's public missions API: the active
   mission's name and a countdown to its 30-day deadline (filled in by JS in
   main.js). Without JS or data it is simply a link to the profile. One slim
   row, deliberately quieter than the testers callout above it. */
.ship-strip {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.4rem 1rem;
  padding: 1rem 1.4rem;
  border-radius: var(--radius-card);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent) 7%, rgba(255, 255, 255, 0.03)),
    rgba(255, 255, 255, 0.01));
  border: 1px solid color-mix(in srgb, var(--accent) 32%, transparent);
  transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}
.ship-strip:hover {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4),
              0 0 30px color-mix(in srgb, var(--accent) 22%, transparent);
}
.ship-strip__flag {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-size: 0.64rem;
  font-weight: 800;
  color: var(--accent);
}
.ship-strip__blip {
  width: 6px;
  height: 6px;
  background: var(--accent);
  box-shadow: 0 0 10px var(--accent);
  animation: pixelBlip 1.8s steps(1, end) infinite;
}
.ship-strip__mission { font-weight: 800; letter-spacing: -0.02em; }
.ship-strip__timer {
  color: color-mix(in srgb, var(--accent) 80%, var(--text));
  font-size: 0.9rem;
  font-weight: 700;
  /* Fixed-width digits so the ticking seconds don't make the row shimmy. */
  font-variant-numeric: tabular-nums;
}
.ship-strip__cta {
  margin-left: auto;
  font-size: 0.82rem;
  font-weight: 700;
  color: color-mix(in srgb, var(--accent) 70%, var(--text));
}

@media (max-width: 560px) {
  /* Once the row wraps, a right-pinned CTA floats oddly mid-wrap — let it
     flow with the rest instead. */
  .ship-strip__cta { margin-left: 0; }
}

/* ----- Shelves --------------------------------------------------------- */

.section { padding: 1.5rem 0 3.25rem; }
/* Slim status row right under the testers callout — no double padding. */
.section--ship { padding-top: 0; }
.section--soon { padding-bottom: 5.5rem; }

.shelf-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.35rem 0.85rem;
  margin-bottom: 1.35rem;
  padding-bottom: 0.85rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}
.shelf-head__title {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-size: clamp(1.5rem, 4vw, 2rem);
  letter-spacing: -0.03em;
}
/* A single square pixel as the shelf marker. */
.shelf-head__pip {
  width: 0.65rem;
  height: 0.65rem;
  background: linear-gradient(135deg, #a78bff, #3dd0ff);
  box-shadow: 0 0 14px rgba(167, 139, 255, 0.7);
}
.shelf-head__blurb {
  color: var(--text-tertiary);
  font-size: 0.9rem;
}
.shelf-head__count {
  margin-left: auto;
  font-size: 0.78rem;
  font-weight: 800;
  color: var(--text-tertiary);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--radius-pill);
  padding: 0.1rem 0.6rem;
}

/* ----- Project grid ---------------------------------------------------- */

.project-grid {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
  gap: 1.5rem;
}

.project-card {
  position: relative;
  display: flex;
  flex-direction: column;
  border-radius: var(--radius-card);
  overflow: hidden;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.012));
  border: 1px solid color-mix(in srgb, var(--accent) 26%, transparent);
  box-shadow: var(--shadow-card);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition: transform 0.28s cubic-bezier(0.2, 0.7, 0.3, 1),
              box-shadow 0.28s ease,
              border-color 0.28s ease;
}
.project-card:hover {
  transform: translateY(-5px);
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  box-shadow: 0 20px 55px rgba(0, 0, 0, 0.55),
              0 0 46px color-mix(in srgb, var(--accent) 30%, transparent);
}
.project-card:focus-within {
  border-color: color-mix(in srgb, var(--accent) 60%, transparent);
}

/* Corner badges — "New" on the latest release, "Prototype" on works in
   progress. A card can carry both, so they stack in a row. */
.project-card__badges {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  z-index: 3;
  display: flex;
  gap: 0.35rem;
}
.project-card__badge {
  padding: 0.26rem 0.62rem;
  border-radius: var(--radius-pill);
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  background: rgba(6, 7, 16, 0.62);
  border: 1px solid color-mix(in srgb, var(--accent) 50%, transparent);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
/* The newest thing shipped: solid accent fill so it wins the corner. */
.project-card__badge--new {
  color: #0a0b16;
  background: var(--accent);
  border-color: transparent;
  box-shadow: 0 0 16px color-mix(in srgb, var(--accent) 55%, transparent);
}

.project-card__media {
  position: relative;
  aspect-ratio: 16 / 10;
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    radial-gradient(125% 120% at 50% 0%, color-mix(in srgb, var(--accent) 40%, transparent), transparent 62%),
    linear-gradient(160deg, color-mix(in srgb, var(--accent) 24%, #0a0b16), #0a0b16);
}
.project-card__media::after {
  /* thin glowing underline where media meets body */
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--accent) 70%, transparent), transparent);
}
.project-card__glyph {
  font-size: 4rem;
  line-height: 1;
  filter: drop-shadow(0 0 20px color-mix(in srgb, var(--accent) 75%, transparent));
  transition: transform 0.28s cubic-bezier(0.2, 0.7, 0.3, 1);
}
.project-card:hover .project-card__glyph { transform: scale(1.12) rotate(-4deg); }

/* Real app-icon tile, centered on the accent wash. */
.project-card__icon {
  width: clamp(112px, 38%, 148px);
  height: auto;
  aspect-ratio: 1;
  border-radius: 24%;
  box-shadow:
    0 8px 22px rgba(0, 0, 0, 0.5),
    0 0 28px color-mix(in srgb, var(--accent) 42%, transparent);
  transition: transform 0.28s cubic-bezier(0.2, 0.7, 0.3, 1);
}
.project-card:hover .project-card__icon { transform: scale(1.08) rotate(-3deg); }

.project-card__body {
  padding: 1.3rem 1.35rem 1.5rem;
  display: flex;
  flex-direction: column;
  flex: 1;
}
.project-card__name {
  font-size: 1.35rem;
  letter-spacing: -0.02em;
}
.project-card__name a { color: var(--text); transition: color 0.2s ease; }
.project-card__name a:hover { color: color-mix(in srgb, var(--accent) 65%, var(--text)); }
.project-card__tagline {
  margin-top: 0.3rem;
  color: color-mix(in srgb, var(--accent) 75%, var(--text-secondary));
  font-size: 0.95rem;
  font-weight: 600;
}
.project-card__desc {
  margin-top: 0.7rem;
  color: var(--text-secondary);
  font-size: 0.92rem;
}

.project-card__links {
  /* Pin to the bottom of the card body so buttons line up across a row,
     regardless of description length. padding-top keeps a min gap. */
  margin-top: auto;
  padding-top: 1.2rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.55rem;
}
.project-card__chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.5rem 0.95rem;
  border-radius: var(--radius-pill);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  cursor: pointer;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 34%, transparent);
  color: var(--text);
  transition: background 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
/* real links sit above the stretched primary link */
a.project-card__chip { z-index: 1; }
a.project-card__chip:hover {
  transform: translateY(-1px);
  background: color-mix(in srgb, var(--accent) 24%, transparent);
}
.project-card__chip--primary {
  background: var(--accent);
  color: #0a0b16;
  border-color: transparent;
  box-shadow: 0 0 18px color-mix(in srgb, var(--accent) 45%, transparent);
}
.project-card:hover .project-card__chip--primary {
  box-shadow: 0 0 26px color-mix(in srgb, var(--accent) 60%, transparent);
}
/* The primary chip is a solid accent fill with dark text, so the generic
   translucent chip hover above would turn it dark-on-dark. Override it with a
   brighten-and-lift that keeps the dark label crisp and reads as "lit up". */
.project-card a.project-card__chip--primary:hover {
  background: color-mix(in srgb, var(--accent) 82%, white);
  box-shadow: 0 8px 24px color-mix(in srgb, var(--accent) 55%, transparent);
}
/* ----- Coming soon ----------------------------------------------------- */

/* Not a product, so not a product card: a dashed full-width strip below both
   shelves instead of a seventh tile pretending to be shippable. */
.soon-strip {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  padding: 1.35rem 1.5rem;
  border-radius: var(--radius-card);
  border: 1px dashed color-mix(in srgb, var(--accent) 38%, transparent);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.025), rgba(255, 255, 255, 0.005));
}
.soon-strip__glyph {
  font-size: 2rem;
  line-height: 1;
  filter: drop-shadow(0 0 18px color-mix(in srgb, var(--accent) 70%, transparent));
}
.soon-strip__title {
  font-size: 1.1rem;
  color: color-mix(in srgb, var(--accent) 70%, var(--text));
}
.soon-strip__desc {
  margin-top: 0.15rem;
  color: var(--text-secondary);
  font-size: 0.9rem;
}

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

.footer {
  padding: 2.5rem 0 3rem;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  color: var(--text-tertiary);
  font-size: 0.875rem;
}
.footer p { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }

.footer__hint { display: inline-flex; align-items: center; gap: 0.4rem; }
.footer__hint[hidden] { display: none; }
.footer kbd {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.78em;
  font-weight: 700;
  line-height: 1;
  padding: 0.2em 0.45em;
  color: #43e08a;
  background: rgba(67, 224, 138, 0.1);
  border: 1px solid rgba(67, 224, 138, 0.35);
  border-radius: 5px;
}

/* ----- Focus visibility ------------------------------------------------ */

a:focus-visible {
  outline: 2px solid #3dd0ff;
  outline-offset: 3px;
  border-radius: 4px;
}

/* ----- Motion ---------------------------------------------------------- */

/* Every blob keyframe repeats the translate(-50%, -50%) centering because a
   keyframe transform replaces the base one wholesale. Wander distances are in
   vw/vh so the drift scales with the viewport. */
@keyframes blobDriftA {
  0%   { transform: translate(-50%, -50%) scale(1); opacity: 0.9; }
  50%  { transform: translate(-50%, -50%) translate(5vw, 3vh) scale(1.12); opacity: 1; }
  100% { transform: translate(-50%, -50%) translate(-3vw, 6vh) scale(0.94); opacity: 0.8; }
}
@keyframes blobDriftB {
  0%   { transform: translate(-50%, -50%) scale(1.05); opacity: 0.85; }
  50%  { transform: translate(-50%, -50%) translate(-6vw, 5vh) scale(0.92); opacity: 1; }
  100% { transform: translate(-50%, -50%) translate(2vw, -4vh) scale(1.1); opacity: 0.8; }
}
@keyframes blobDriftC {
  0%   { transform: translate(-50%, -50%) scale(0.96); opacity: 0.9; }
  50%  { transform: translate(-50%, -50%) translate(-5vw, -6vh) scale(1.1); opacity: 0.75; }
  100% { transform: translate(-50%, -50%) translate(4vw, 2vh) scale(1); opacity: 1; }
}
@keyframes blobDriftD {
  0%   { transform: translate(-50%, -50%) scale(1); opacity: 0.8; }
  50%  { transform: translate(-50%, -50%) translate(6vw, -4vh) scale(1.08); opacity: 1; }
  100% { transform: translate(-50%, -50%) translate(-2vw, -7vh) scale(0.95); opacity: 0.85; }
}
@keyframes blobDriftE {
  0%   { transform: translate(-50%, -50%) translate(-8vw, 4vh) scale(0.9); opacity: 0.75; }
  50%  { transform: translate(-50%, -50%) translate(6vw, -3vh) scale(1.15); opacity: 1; }
  100% { transform: translate(-50%, -50%) translate(10vw, 6vh) scale(1); opacity: 0.7; }
}
@keyframes titleSweep {
  0%, 100% { background-position: 0% 50%; }
  50%      { background-position: 100% 50%; }
}
@keyframes pixelPulse {
  0%, 100% { box-shadow: 0 0 10px rgba(167, 139, 255, 0.6); }
  50%      { box-shadow: 0 0 18px rgba(61, 208, 255, 0.9); }
}
/* steps(1) so it snaps on/off like a blinking cursor, not a soft pulse. */
@keyframes pixelBlip {
  0%, 60%  { opacity: 1; }
  61%, 100% { opacity: 0.25; }
}
@keyframes fartFlash {
  0%   { box-shadow: inset 0 0 0 rgba(67, 224, 138, 0); }
  18%  { box-shadow: inset 0 0 140px rgba(67, 224, 138, 0.32); }
  100% { box-shadow: inset 0 0 240px rgba(67, 224, 138, 0); }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .glow-field__blob,
  .hero__title,
  .nav-logo__dot,
  .latest__blip,
  .testers__blip,
  .ship-strip__blip {
    animation: none;
  }
  .hero__title { background-position: 0% 50%; }
  .project-card,
  .project-card__glyph,
  .project-card__chip,
  .testers__btn,
  .latest,
  .ship-strip {
    transition: none;
  }
}

/* ----- Graceful fallback for browsers without color-mix() -------------- */
@supports not (background: color-mix(in srgb, red, blue)) {
  .project-card { border-color: rgba(167, 139, 255, 0.3); }
  .project-card__media { background: linear-gradient(160deg, #1a1340, #0a0b16); }
  .project-card__chip { background: rgba(255, 255, 255, 0.06); border-color: rgba(255, 255, 255, 0.18); }
  .project-card__chip--primary { background: var(--accent); color: #0a0b16; }
  .latest { background: rgba(255, 255, 255, 0.05); border-color: rgba(255, 255, 255, 0.18); }
  .latest__cta { color: var(--accent); }
  .soon-strip { border-color: rgba(255, 255, 255, 0.2); }
  .soon-strip__title { color: var(--accent); }
  .testers { background: rgba(255, 255, 255, 0.04); border-color: rgba(232, 160, 60, 0.45); }
  .testers__step { border-color: rgba(232, 160, 60, 0.25); }
  .testers__fine a { color: var(--accent); }
  .ship-strip { background: rgba(255, 255, 255, 0.04); border-color: rgba(255, 85, 102, 0.35); }
  .ship-strip__timer, .ship-strip__cta { color: var(--accent); }
}

/* --- PLAY menu ------------------------------------------------------------
   Games that exist on more than one platform put the choice behind PLAY rather
   than picking for you. <details>/<summary>, so it works with no JavaScript and
   is keyboard- and screen-reader-operable for free.

   The whole card is already covered by a stretched ::after on
   .project-card__link, so anything clickable inside needs to sit above it —
   hence position:relative and the z-index, same trick as a.project-card__chip. */
.playmenu {
  position: relative;
  z-index: 2;
}
.playmenu > summary {
  list-style: none;          /* Firefox */
  user-select: none;
}
.playmenu > summary::-webkit-details-marker { display: none; }
.playmenu > summary::marker { content: ""; }
/* Two hovers, because the summary wears two costumes (see project-card.ejs): as a
   plain chip it is translucent with light text, so it darkens the way a link chip
   does; as the primary it is a solid accent fill with dark text, and brightens
   toward white instead. Giving both the brighten would put light text on a
   near-white fill on the games that lead with a store link. */
.playmenu > summary:hover {
  background: color-mix(in srgb, var(--accent) 24%, transparent);
}
.playmenu > summary.project-card__chip--primary:hover {
  background: color-mix(in srgb, var(--accent) 82%, white);
}
.playmenu > summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
.playmenu__caret {
  font-size: 0.7em;
  opacity: 0.75;
  transition: transform 0.18s ease;
}
.playmenu[open] .playmenu__caret { transform: rotate(180deg); }

.playmenu__list {
  position: absolute;
  left: 0;
  top: calc(100% + 0.45rem);
  z-index: 3;
  margin: 0;
  padding: 0.3rem;
  list-style: none;
  min-width: 12.5rem;
  border-radius: 0.85rem;
  background: var(--surface, #14161f);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.55);
}
.playmenu__list a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.8rem;
  padding: 0.55rem 0.7rem;
  border-radius: 0.6rem;
  text-decoration: none;
  color: var(--text);
  font-size: 0.85rem;
  font-weight: 650;
}
.playmenu__list a:hover,
.playmenu__list a:focus-visible {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  outline: none;
}
.playmenu__note {
  font-size: 0.74rem;
  font-weight: 500;
  color: var(--text-secondary);
  white-space: nowrap;
}

/* On a narrow screen a floating panel that can escape the card is more trouble
   than it is worth, so it becomes an inline list instead. */
@media (max-width: 560px) {
  .playmenu__list {
    position: static;
    box-shadow: none;
    margin-top: 0.45rem;
    min-width: 0;
  }
}

/* The card is overflow:hidden so its rounded corners clip the media block, which
   also clipped the open PLAY panel to the card's bottom edge. Let the card spill
   only while a menu is actually open, and hand the clipping job to the media
   element for the duration so the corners still look right. */
.project-card:has(.playmenu[open]) {
  overflow: visible;
  z-index: 3;                 /* above neighbouring cards in the grid */
}
.project-card:has(.playmenu[open]) .project-card__media {
  border-top-left-radius: var(--radius-card);
  border-top-right-radius: var(--radius-card);
  overflow: hidden;
}
/* Without :has() the panel would be invisible rather than merely ugly, so fall
   back to an inline list that cannot be clipped by anything. */
@supports not selector(:has(*)) {
  .playmenu__list {
    position: static;
    box-shadow: none;
    margin-top: 0.45rem;
    min-width: 0;
  }
}
