/* Lunar Aspect — the marketing page's own stylesheet.

   NOT moon.css. That one dresses privacy.html and support.html, which are the
   two URLs an App Store reviewer opens, and it is deliberately the most boring
   thing in the repo: no animation, no web font, nothing that can fail in five
   years' time. Nothing here is allowed to reach those pages, so they get their
   own file and this one is never linked from them.

   The palette is lifted from the app's own css/style.css rather than
   approximated, so a person arriving from the App Store badge lands on
   something coloured like the thing they just tapped:

     --bg      #05070c   the theme-color, and the lunar sky at noon
     --ink     #e8edf7   --ink
     --dim     #8f9db8   --dim
     --accent  #7fb2ff   --accent, earthlight blue
     --warm    #ffd9a0   --warm, the low sun on regolith
     --rule    rgba(140,170,220,.16)

   Dark only, on purpose, and every colour painted explicitly rather than
   inherited. The one fact the app exists to make felt is that the lunar sky
   stays black at noon; a light-mode variant would argue with it.

   MOTION. Everything that moves is either (a) slow enough to read as drift
   rather than animation, or (b) illustrating a real fact from the app — the
   day bar is 14.8 days of light and 14.8 of dark to scale, and the phase
   crossfade is three real captures in the order the app shows them. Nothing
   moves to prove it can. The whole lot is switched off under
   prefers-reduced-motion at the bottom of this file, in one block, so there is
   exactly one place to check that the promise is kept. */

@font-face {
  font-family: Sora;
  src: url('fonts/sora-regular.ttf') format('truetype');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: Sora;
  src: url('fonts/sora-medium.ttf') format('truetype');
  font-weight: 500 600;
  font-display: swap;
}

:root {
  --bg:     #05070c;
  --ink:    #e8edf7;
  --dim:    #8f9db8;
  --accent: #7fb2ff;
  --warm:   #ffd9a0;
  --rule:   rgba(140, 170, 220, 0.16);
  --panel:  rgba(10, 14, 24, 0.72);
  --display: Sora, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--body);
  font-size: 1.02rem;
  line-height: 1.7;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
}

/* ---------------------------------------------------------------- sky ---
   Two starfields, fixed, drifting at different rates. Radial-gradient dots
   rather than elements, so the whole sky costs no DOM, no image request and
   nothing that can reflow — and it is painted on ::before/::after of <body>,
   which means it cannot be scrolled off or interact with anything.

   The drift is 300s and 420s for the two layers. That is slow enough that you
   never catch it moving, which is the point: it should feel like the page is
   alive, not like it is animating at you. */
body::before,
body::after {
  content: "";
  position: fixed;
  inset: -20%;
  pointer-events: none;
  z-index: 0;
}
body::before {
  background-image:
    radial-gradient(1.1px 1.1px at 12% 18%, rgba(232,237,247,.55) 50%, transparent 51%),
    radial-gradient(1px   1px   at 78% 9%,  rgba(232,237,247,.42) 50%, transparent 51%),
    radial-gradient(1.3px 1.3px at 34% 62%, rgba(232,237,247,.50) 50%, transparent 51%),
    radial-gradient(1px   1px   at 91% 47%, rgba(232,237,247,.35) 50%, transparent 51%),
    radial-gradient(1.2px 1.2px at 57% 88%, rgba(232,237,247,.45) 50%, transparent 51%),
    radial-gradient(1px   1px   at 22% 91%, rgba(232,237,247,.30) 50%, transparent 51%),
    radial-gradient(1.4px 1.4px at 68% 33%, rgba(232,237,247,.52) 50%, transparent 51%),
    radial-gradient(1px   1px   at 45% 7%,  rgba(232,237,247,.28) 50%, transparent 51%),
    radial-gradient(1.1px 1.1px at 6% 54%,  rgba(232,237,247,.40) 50%, transparent 51%),
    radial-gradient(1px   1px   at 84% 72%, rgba(232,237,247,.33) 50%, transparent 51%);
  animation: drift-a 300s linear infinite;
}
body::after {
  background-image:
    radial-gradient(1px   1px   at 27% 31%, rgba(200,220,255,.30) 50%, transparent 51%),
    radial-gradient(0.9px 0.9px at 63% 14%, rgba(200,220,255,.24) 50%, transparent 51%),
    radial-gradient(1.2px 1.2px at 49% 74%, rgba(200,220,255,.28) 50%, transparent 51%),
    radial-gradient(0.9px 0.9px at 15% 82%, rgba(200,220,255,.20) 50%, transparent 51%),
    radial-gradient(1px   1px   at 88% 26%, rgba(200,220,255,.26) 50%, transparent 51%),
    radial-gradient(1.1px 1.1px at 72% 59%, rgba(200,220,255,.22) 50%, transparent 51%);
  animation: drift-b 420s linear infinite;
}
@keyframes drift-a { to { transform: translate3d(-3%, 2%, 0); } }
@keyframes drift-b { to { transform: translate3d(2.5%, -1.5%, 0); } }

.wrap {
  position: relative;
  z-index: 1;
  max-width: 62rem;
  margin: 0 auto;
  padding: 2rem 1.25rem 4rem;
}

/* ------------------------------------------------------------- header --- */
.back {
  display: inline-block;
  margin-bottom: 2rem;
  color: var(--dim);
  font-size: .8rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  text-decoration: none;
  transition: color .2s;
}
.back:hover { color: var(--accent); }

.masthead {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 1.5rem;
  align-items: center;
  justify-content: space-between;
  padding-bottom: 1.4rem;
  border-bottom: 1px solid var(--rule);
}
.brand { display: flex; align-items: center; gap: .85rem; }
.brand img { border-radius: 22%; display: block; }
.wordmark {
  margin: 0;
  font-family: var(--display);
  font-weight: 500;
  font-size: 1.05rem;
  letter-spacing: .13em;
  line-height: 1.15;
}
.brand .sub {
  margin: 0;
  color: var(--warm);
  font-size: .76rem;
  letter-spacing: .05em;
}
.masthead nav { display: flex; gap: 1.4rem; font-size: .84rem; }
.masthead nav a {
  color: var(--dim);
  text-decoration: none;
  letter-spacing: .06em;
  text-transform: uppercase;
  transition: color .2s;
}
.masthead nav a:hover,
.masthead nav a[aria-current] { color: var(--ink); }

/* --------------------------------------------------------------- hero --- */
.hero {
  display: grid;
  grid-template-columns: 1.05fr .95fr;
  gap: 2.5rem;
  align-items: center;
  padding: 4rem 0 3.5rem;
}
.eyebrow {
  margin: 0 0 1.1rem;
  color: var(--accent);
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
}
h1 {
  margin: 0;
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(2.6rem, 8vw, 4.1rem);
  letter-spacing: .055em;     /* the game's own title screen, to the decimal */
  line-height: 1.08;
}
.hook {
  margin: 1.4rem 0 1.2rem;
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(1.1rem, 3vw, 1.4rem);
  line-height: 1.45;
  letter-spacing: .01em;
  color: var(--warm);
  text-wrap: balance;
}
.lede { margin: 0 0 1.8rem; color: #c3cde0; max-width: 34rem; }

/* The CTA sits above the fold-length prose deliberately: on a 375px phone one
   more paragraph is the difference between the badge being on the first screen
   and being off it. */
.cta { margin: 0 0 1rem; display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.25rem; }

/* Apple's own badge artwork, served unmodified from their marketing tools.
   Their terms govern it: never recoloured, never stretched, never overlaid,
   scaled only in proportion, never below 40px tall. So the hover moves the
   LINK, not the badge — no filter, no tint, no sweep across the artwork. */
.get {
  display: inline-block;
  line-height: 0;
  border-radius: 9px;
  transition: transform .25s cubic-bezier(.2,.7,.3,1), box-shadow .25s;
}
.get img { display: block; height: 54px; width: auto; }
.get:hover, .get:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 10px 34px rgba(127, 178, 255, .22);
}

.play-link {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  min-height: 54px;
  padding: 0 1.3rem;
  border: 1px solid rgba(172, 196, 223, .28);
  border-radius: 12px;
  background: rgba(17, 28, 43, .7);
  color: var(--ink);
  font-size: .92rem;
  letter-spacing: .02em;
  text-decoration: none;
  transition: border-color .25s, background .25s, transform .25s cubic-bezier(.2,.7,.3,1);
}
.play-link:hover, .play-link:focus-visible {
  border-color: rgba(127, 178, 255, .55);
  background: rgba(22, 38, 58, .82);
  transform: translateY(-2px);
}
.play-link .dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 0 rgba(127, 178, 255, .55);
  animation: ping 2.8s ease-out infinite;
}
@keyframes ping {
  0%       { box-shadow: 0 0 0 0   rgba(127, 178, 255, .5); }
  70%,100% { box-shadow: 0 0 0 9px rgba(127, 178, 255, 0);  }
}

.facts {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin: 1.4rem 0 0;
  padding: 0;
  list-style: none;
}
.facts li {
  padding: .3rem .72rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  color: var(--dim);
  font-size: .78rem;
  letter-spacing: .04em;
}
.cta-note { margin: 1rem 0 0; color: var(--dim); font-size: .84rem; }

/* The hero. A rounded window onto the Moon's sky, NOT a circle: the first
   version masked the photograph with border-radius 50%, which cropped the
   corona and left something that read as a CSS donut rather than a picture of
   anything. The frame is the app's viewport, so it should look like one.

   Two layers, and the order matters. The <img> paints first and is the
   Largest Contentful Paint. The <canvas> sits on top at opacity 0 and is only
   revealed - by .live, which hero.js adds - after a real frame has been drawn
   into it. So the page never shows an empty black box, and a browser where
   the engine fails simply keeps the photograph. */
.hero-shot {
  position: relative;
  display: block;
  justify-self: center;
  width: min(100%, 27rem);
  aspect-ratio: 1;
  border: 1px solid var(--rule);
  border-radius: 20px;
  overflow: hidden;
  background: #04050a;
  text-decoration: none;
  transition: border-color .35s, box-shadow .35s, transform .35s cubic-bezier(.2,.7,.3,1);
}
.hero-shot:hover, .hero-shot:focus-visible {
  border-color: rgba(127, 178, 255, .42);
  box-shadow: 0 18px 60px rgba(0, 0, 0, .55), 0 0 44px rgba(127, 178, 255, .1);
  transform: translateY(-3px);
}
.hero-shot img,
.hero-shot canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
}
.hero-shot canvas { opacity: 0; transition: opacity .9s ease; }
.hero-shot.live canvas { opacity: 1; }

/* The affordance. It has to say the picture is a door, because a live sky
   with no chrome on it does not look clickable. */
.hero-open {
  position: absolute;
  left: 50%;
  bottom: 14px;
  transform: translateX(-50%);
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .42rem .95rem;
  border: 1px solid rgba(172, 196, 223, .3);
  border-radius: 999px;
  background: rgba(8, 12, 20, .72);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: var(--ink);
  font-size: .8rem;
  letter-spacing: .05em;
  opacity: .82;
  transition: opacity .3s, border-color .3s, background .3s;
}
.hero-shot:hover .hero-open,
.hero-shot:focus-visible .hero-open {
  opacity: 1;
  border-color: rgba(127, 178, 255, .55);
  background: rgba(14, 24, 38, .85);
}
.hero-open .dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent);
  animation: ping 2.8s ease-out infinite;
}

/* -------------------------------------------------------------- bands --- */
.band { padding: 3.2rem 0; border-top: 1px solid var(--rule); }
.band:first-of-type { border-top: 0; }

h2 {
  margin: 0 0 1.4rem;
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(1.4rem, 3.6vw, 1.85rem);
  letter-spacing: .01em;
  line-height: 1.25;
}
h3 {
  margin: 0 0 .45rem;
  font-family: var(--display);
  font-weight: 500;
  font-size: 1rem;
  letter-spacing: .015em;
}
p { margin: 0 0 1.05rem; }
p:last-child { margin-bottom: 0; }
strong { color: #fff; font-weight: 600; }
a { color: var(--accent); text-underline-offset: 3px; }
a:hover { color: var(--warm); }

.intro { font-size: 1.12rem; line-height: 1.75; color: #ccd6e6; max-width: 46rem; }
.sub-note {
  margin: -.9rem 0 1.6rem;
  color: var(--dim);
  font-size: .82rem;
  letter-spacing: .06em;
  text-transform: uppercase;
}

/* ----------------------------------------------- full-bleed quote band ---
   Breaks the 62rem column and runs edge to edge. The regolith photograph is a
   real capture, and the black above the horizon in it IS the point being made
   in the text sitting on top of it — so the image is content here, not
   decoration, even though it is set as a background. */
.bleed {
  position: relative;
  margin: 3.2rem calc(50% - 50vw);
  width: 100vw;
  min-height: 23rem;
  display: grid;
  place-items: center;
  padding: 4rem 1.5rem;
  background: #0a0c10 center / cover no-repeat;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  text-align: center;
  overflow: hidden;
}
.bleed-regolith { background-image: url('band-regolith.webp'); background-position: center 62%; }
/* The eclipse band is the one image with a bright object dead centre, so the
   text cannot sit on top of it. On a wide screen the ring moves right and the
   words go left; on a narrow one there is no room for that, so the ring drops
   to the bottom and the scrim gets heavier instead. */
.bleed-eclipse { background-image: url('band-eclipse.webp'); background-position: 64% center; }
.bleed-eclipse::before {
  background: linear-gradient(180deg, rgba(5,7,12,.86) 0%, rgba(5,7,12,.78) 46%, rgba(5,7,12,.3) 100%);
}
@media (min-width: 820px) {
  .bleed-eclipse { place-items: center start; }
  .bleed-eclipse::before {
    background: linear-gradient(90deg, rgba(5,7,12,.95) 0%, rgba(5,7,12,.86) 40%, rgba(5,7,12,.15) 100%);
  }
  .bleed-eclipse blockquote { text-align: left; margin-left: max(1.5rem, calc(50vw - 31rem)); max-width: 26rem; }
}

.bleed::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(5,7,12,.55) 0%, rgba(5,7,12,.18) 45%, rgba(5,7,12,.7) 100%);
}
.bleed blockquote {
  position: relative;
  margin: 0;
  max-width: 34rem;
}
.bleed q {
  display: block;
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(1.5rem, 5vw, 2.3rem);
  line-height: 1.3;
  letter-spacing: .01em;
  quotes: none;
  text-wrap: balance;
}
.bleed cite {
  display: block;
  margin-top: 1.1rem;
  color: var(--dim);
  font-size: .9rem;
  font-style: normal;
  line-height: 1.6;
}

/* ------------------------------------------------------------ surprises --- */
.surprises { display: grid; gap: 1.5rem; grid-template-columns: repeat(3, 1fr); }
.surprise {
  padding: 1.5rem 1.4rem;
  border: 1px solid var(--rule);
  border-radius: 16px;
  background: linear-gradient(180deg, rgba(20,28,44,.5), rgba(10,14,24,.35));
  transition: border-color .3s, transform .3s cubic-bezier(.2,.7,.3,1);
}
.surprise:hover { border-color: rgba(127,178,255,.34); transform: translateY(-3px); }
.surprise p { color: var(--dim); font-size: .95rem; }
.surprise .fig {
  display: block;
  margin-bottom: .9rem;
  font-family: var(--display);
  font-weight: 500;
  font-size: 1.9rem;
  line-height: 1;
  color: var(--warm);
  letter-spacing: .01em;
}

/* The lunar day, to scale. 14.8 days of sunlight then 14.8 of night, and the
   marker crosses it in 30s — one second per Earth day, which is roughly the
   fastest speed the app itself offers. The bar is not a decoration of the
   fact, it is the fact. */
.daybar {
  position: relative;
  height: 10px;
  margin: 1.1rem 0 .6rem;
  border-radius: 999px;
  overflow: hidden;
  background: linear-gradient(90deg,
    #ffd9a0 0%, #ffd9a0 46%,
    #6b5a44 50%,
    #0d1420 54%, #0d1420 100%);
}
.daybar i {
  position: absolute;
  top: -3px;
  left: 0;
  width: 2px;
  height: 16px;
  background: var(--accent);
  box-shadow: 0 0 8px 1px rgba(127,178,255,.8);
  animation: sweep 30s linear infinite;
}
@keyframes sweep { from { left: 0; } to { left: 100%; } }
.daybar-key {
  display: flex;
  justify-content: space-between;
  color: var(--dim);
  font-size: .72rem;
  letter-spacing: .05em;
}

/* --------------------------------------------------------------- phase ---
   Three real captures in the order the app cycles them: earthshine, crescent,
   full. Stacked and crossfaded rather than animated in CSS, because a
   CSS-drawn terminator would be a cartoon of the thing the app renders
   properly — and this page's whole claim is that the pictures are real. */
.phase {
  position: relative;
  width: min(100%, 15rem);
  aspect-ratio: 1;
  margin: 0 auto;
  border-radius: 50%;
  overflow: hidden;
}
.phase img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  animation: phase-cycle 18s ease-in-out infinite;
}
.phase img:nth-child(1) { animation-delay: 0s; }
.phase img:nth-child(2) { animation-delay: 6s; }
.phase img:nth-child(3) { animation-delay: 12s; }
@keyframes phase-cycle {
  0%, 27%  { opacity: 1; }
  38%, 94% { opacity: 0; }
  100%     { opacity: 1; }
}

.split {
  display: grid;
  grid-template-columns: 1fr 15rem;
  gap: 2.5rem;
  align-items: center;
}

/* ---------------------------------------------------------------- try ---
   A link to the app, not the app. It was an inline iframe; the app sizes its
   panels against the viewport and the one an iframe here can give it is too
   small for them, so the controls piled into each other and the demo made the
   product look worse than it is. The hero above is the live engine instead -
   that one has no UI to squeeze. */
.try-frame {
  position: relative;
  display: block;
  border: 1px solid var(--rule);
  border-radius: 18px;
  overflow: hidden;
  background: #05070c;
  aspect-ratio: 16 / 10;
  max-width: 46rem;
  margin: 0 auto;
  text-decoration: none;
  transition: border-color .35s, box-shadow .35s, transform .35s cubic-bezier(.2,.7,.3,1);
}
.try-frame:hover, .try-frame:focus-visible {
  border-color: rgba(127, 178, 255, .42);
  box-shadow: 0 18px 54px rgba(0, 0, 0, .55);
  transform: translateY(-3px);
}
.try-frame img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  opacity: .9;
  transition: opacity .4s, transform 6s ease-out;
}
.try-frame:hover img { opacity: 1; transform: scale(1.03); }
.try-start {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: .7rem;
  align-items: center;
  justify-content: center;
  background: radial-gradient(ellipse at center, rgba(5,7,12,.12) 30%, rgba(5,7,12,.7) 100%);
  color: var(--ink);
  font-family: var(--display);
  font-size: 1rem;
  letter-spacing: .04em;
}
.try-start .ring {
  display: grid;
  place-items: center;
  width: 62px;
  height: 62px;
  border: 1px solid rgba(127,178,255,.5);
  border-radius: 50%;
  font-size: 1.1rem;
  color: var(--accent);
  transition: transform .3s cubic-bezier(.2,.7,.3,1), border-color .3s, box-shadow .3s;
}
.try-frame:hover .ring, .try-frame:focus-visible .ring {
  transform: scale(1.08);
  border-color: var(--accent);
  box-shadow: 0 0 34px rgba(127,178,255,.3);
}
.try-start small { color: var(--dim); font-family: var(--body); letter-spacing: 0; font-size: .82rem; }
.try-note { margin: 1rem 0 0; color: var(--dim); font-size: .88rem; }

/* -------------------------------------------------------------- shots --- */
.shots {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 1.5rem;
}
.shots figure { margin: 0; }
.shots figure.wide { grid-column: 1 / -1; }
.shots figure.wide img { aspect-ratio: 2 / 1; object-fit: cover; }
.shots img {
  width: 100%;
  height: auto;
  display: block;
  border: 1px solid var(--rule);
  border-radius: 18px;
  background: #000;
  transition: transform .45s cubic-bezier(.2,.7,.3,1), border-color .45s, box-shadow .45s;
}
.shots figure:hover img {
  transform: translateY(-5px);
  border-color: rgba(127,178,255,.36);
  box-shadow: 0 18px 44px rgba(0,0,0,.6);
}
.shots figcaption {
  margin-top: .7rem;
  color: var(--dim);
  font-size: .86rem;
  line-height: 1.5;
}

/* ------------------------------------------------------------- feats ---- */
.feats { display: grid; gap: 1.4rem 1.8rem; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }
.feat { padding-left: 1rem; border-left: 2px solid rgba(127,178,255,.3); }
.feat p { color: var(--dim); font-size: .95rem; margin: 0; }

.nots { display: grid; gap: 1.2rem; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); }
.not { padding: 1.1rem 1.2rem; border: 1px solid var(--rule); border-radius: 14px; }
.not-h { margin: 0 0 .25rem; font-family: var(--display); font-weight: 500; color: var(--warm); }
.not p { margin: 0; color: var(--dim); font-size: .9rem; }

.cta-again {
  margin: 2.2rem 0 0;
  padding: 1.2rem 1.3rem;
  border: 1px solid rgba(127,178,255,.28);
  border-radius: 14px;
  background: rgba(127,178,255,.05);
  font-size: 1.02rem;
}

/* ---------------------------------------------------------------- faq --- */
.faq { display: grid; gap: 1.6rem; }
.qa p { color: var(--dim); margin: 0; }

.links { margin: 0; padding: 0; list-style: none; display: grid; gap: .9rem; }
.links li { display: flex; flex-wrap: wrap; gap: .2rem .7rem; align-items: baseline; }
.links span { color: var(--dim); font-size: .88rem; }

footer {
  margin-top: 3.5rem;
  padding-top: 1.6rem;
  border-top: 1px solid var(--rule);
  color: var(--dim);
  font-size: .86rem;
}
footer .key { color: rgba(143,157,184,.65); }

/* ------------------------------------------------------------- reveal ---
   The `js` class is added by the inline script in the page, so these two rules
   only ever apply where that script ran. With JavaScript off — or if it throws
   — every section is simply visible. The failure mode is "no animation", never
   "no content". */
.js .reveal { opacity: 0; transform: translateY(18px); }
.js .reveal.in {
  opacity: 1;
  transform: none;
  transition: opacity .7s ease, transform .7s cubic-bezier(.2,.7,.3,1);
}

/* -------------------------------------------------------------- narrow --- */
@media (max-width: 860px) {
  .hero { grid-template-columns: 1fr; gap: 1.6rem; padding: 2rem 0 2.5rem; }
  /* The image leads on a phone because it is the strongest thing on the page,
     but it is capped hard: at full width it pushed the App Store badge onto
     the third screen, and the badge is what this page is for. */
  .hero-shot { width: min(74%, 17rem); order: -1; }
  .surprises { grid-template-columns: 1fr; }
  .split { grid-template-columns: 1fr; }
  .phase { width: min(100%, 13rem); }
}
@media (max-width: 560px) {
  .wrap { padding: 1.2rem 1.1rem 3rem; }
  .back { margin-bottom: 1.2rem; }
  .masthead { gap: .8rem; padding-bottom: 1rem; }
  .hero-shot { width: min(72%, 14.5rem); }
  .hook { margin: 1rem 0 .9rem; }
  .lede { margin-bottom: 1.4rem; }
  .masthead nav { gap: 1.1rem; font-size: .78rem; }
  .cta { gap: .8rem; }
  .get, .play-link { width: 100%; }
  .get img { height: 52px; margin: 0 auto; }
  .play-link { justify-content: center; }
  .bleed { min-height: 16rem; padding: 3rem 1.2rem; }
  .shots { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1.1rem; }
}

/* ------------------------------------------------------- reduced motion ---
   One block, so there is exactly one place to check that the promise at the
   top of this file is kept. Everything that moves is listed here. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  body::before,
  body::after,
  .daybar i,
  .play-link .dot,
  .hero-open .dot { animation: none; }
  /* The hero canvas is handled in hero.js, not here: it draws a single frame
     and never starts its loop. A CSS rule cannot stop a requestAnimationFrame. */
  .hero-shot { transition: none; }
  .hero-shot:hover { transform: none; }
  .try-frame { transition: none; }
  .try-frame:hover { transform: none; }
  .try-frame:hover img { transform: none; }
  .daybar i { left: 50%; }
  .phase img { animation: none; opacity: 0; }
  .phase img:nth-child(2) { opacity: 1; }   /* hold the crescent, the best of the three */
  .get, .play-link, .surprise, .shots img, .try-frame img, .try-start .ring { transition: none; }
  .get:hover, .play-link:hover, .surprise:hover, .shots figure:hover img { transform: none; }
  .js .reveal { opacity: 1; transform: none; }
}

/* ---- the /moon/ catalogue, rendered into this page by promo/moon/page.js ----
   Cards are the real computed figures, so they carry no decoration of their own
   beyond a frame - the picture is the argument. */

.skycat {
  list-style: none; padding: 0; margin: 1.8rem 0 0;
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
}
.skycard a {
  display: block; text-decoration: none; color: var(--ink);
  border: 1px solid var(--rule); border-radius: 10px; overflow: hidden;
  background: var(--panel); height: 100%;
  transition: border-color .2s ease, transform .2s ease;
}
.skycard a:hover, .skycard a:focus-visible {
  border-color: var(--accent); transform: translateY(-2px);
}
/* Cropped squarer than the source frame on purpose: these renders are mostly
   black sky, and at the full 1200x630 ratio a card is largely empty. */
.skycard img {
  display: block; width: 100%; height: auto;
  aspect-ratio: 16/11; object-fit: cover; object-position: center;
  background: #05070c;
}
.skyq {
  display: block; padding: .8rem .9rem 1rem;
  font: 600 .92rem/1.35 var(--display); text-wrap: balance;
}
.skyall { margin-top: 1.4rem; }
