
@font-face {
  font-family: 'Baloo 2';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('/fonts/baloo2-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/fonts/outfit-latin.woff2') format('woff2');
}

:root {
  /* Day. #6b4a3a on cream is 7.9:1; #c23d74 is 4.94:1 and is the only pink allowed
     to carry running text. #e4548f measures 3.48:1, so it is display type and fills
     only, never a sentence. */
  --ink: #6b4a3a;
  --ink-soft: rgba(107, 74, 58, .74);
  --pink-ink: #c23d74;
  --pink: #e4548f;
  --cream: #fffdf8;
  --outline: #c9a27e;
  --sparkle: #e8a4c4;
  --hairline: rgba(107, 74, 58, .16);
  --shot-shadow: 0 26px 60px rgba(150, 60, 90, .22);
  --star-ink: rgba(107, 74, 58, .5);

  /* The sky, sliced. Each section picks the two stops that continue the one above. */
  --sky-0: #f4e9fd;
  --sky-1: #fff6f1;
  --sky-2: #ffeef5;
  --sky-3: #ffdbe8;
  --night-0: #241a3f;
  --night-1: #191136;
  --night-2: #2a1740;

  --r-sm: 12px;
  --r-md: 16px;
  --r-lg: 20px;
  --r-shot: 26px;
  --pill: 999px;

  /* 4 / 8 / 12 / 20 / 32 / 52 / 84 / 136 */
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 20px;
  --s5: 32px; --s6: 52px; --s7: 84px; --s8: 136px;

  --page: min(1120px, 100% - 2 * var(--gutter));
  --gutter: clamp(20px, 5vw, 56px);
}

/* After dusk the same tokens sink into plum. Identical to the app's .night block:
   same hues, same job, so a screenshot of night mode sits in a page that agrees
   with it. */
.night {
  /* Re-declaring color matters: body resolved var(--ink) once against :root, and a
     child that only redefines the custom property would inherit the day value. */
  color: var(--ink);
  --ink: #f4e8f2;
  --ink-soft: rgba(244, 232, 242, .72);
  --pink-ink: #ff9dc4;
  --pink: #ff9dc4;
  --cream: #4e3c6c;
  --outline: #d9c2f0;
  --sparkle: #ffe9a8;
  --hairline: rgba(244, 232, 242, .18);
  --shot-shadow: 0 26px 60px rgba(10, 4, 26, .55);
  --star-ink: rgba(255, 246, 226, .85);
}

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

html {
  -webkit-text-size-adjust: 100%;
  /* Only for people who did not ask for stillness. */
  scroll-behavior: auto;
}
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  font-family: 'Outfit', system-ui, sans-serif;
  font-size: 17px;
  line-height: 1.65;
  color: var(--ink);
  /* Dawn, so the header sits in the same sky the hero opens with. Every band below
     paints its own slice, and the footer closes on night. */
  background: var(--sky-0);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Paper grain. Three hundred bytes of turbulence, and the only texture on the page:
   it takes the flat gradient off the screen without shipping a single raster tile. */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 90;
  pointer-events: none;
  opacity: .05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

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

a { color: var(--pink-ink); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--ink); }

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

.skip {
  position: absolute;
  left: var(--s4);
  top: -100px;
  z-index: 100;
  background: var(--cream);
  color: var(--pink-ink);
  padding: var(--s3) var(--s4);
  border-radius: var(--pill);
  font-weight: 600;
  text-decoration: none;
  transition: top .2s ease;
}
.skip:focus { top: var(--s4); }

/* ---------- type ---------- */

h1, h2, .display {
  font-family: 'Baloo 2', 'Outfit', sans-serif;
  font-weight: 800;
  line-height: 1.02;
  letter-spacing: .005em;
  text-wrap: balance;
}
h1 { font-size: clamp(40px, 8.5vw, 88px); }
h2 { font-size: clamp(29px, 4.4vw, 50px); font-weight: 700; }
h3 {
  font-family: 'Baloo 2', 'Outfit', sans-serif;
  font-weight: 700;
  font-size: 19px;
  line-height: 1.2;
}

.kicker {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--pink-ink);
  display: flex;
  align-items: center;
  gap: var(--s2);
}
.kicker .spark { width: 13px; height: 13px; color: var(--sparkle); }

.lead {
  font-size: clamp(17px, 2.1vw, 21px);
  line-height: 1.6;
  max-width: 46ch;
  color: var(--ink-soft);
  text-wrap: pretty;
}
.body { max-width: 62ch; color: var(--ink-soft); text-wrap: pretty; }
.fine { font-size: 13.5px; line-height: 1.55; color: var(--ink-soft); }

/* ---------- sky sections ---------- */

section { position: relative; }
.wrap { width: var(--page); margin-inline: auto; }

/* Each band starts where the one above it stopped, so the page reads as one sky
   rather than as a stack of coloured boxes. */
.sky-hero   { background: linear-gradient(180deg, var(--sky-0) 0%, var(--sky-1) 100%); }
.sky-rule   { background: linear-gradient(180deg, var(--sky-1) 0%, var(--sky-2) 100%); }
.sky-worlds { background: linear-gradient(180deg, var(--sky-2) 0%, var(--sky-3) 100%); }
.sky-deep-a { background: linear-gradient(180deg, var(--night-0) 0%, var(--night-1) 100%); }
.sky-deep-b { background: linear-gradient(180deg, var(--night-1) 0%, var(--night-2) 100%); }
.sky-end    { background: var(--night-2); }

.band { padding-block: clamp(56px, 7.2vw, 104px); }

/* ---------- decor ---------- */

.deco, .spark, .stars, .constel { pointer-events: none; }
.deco { position: absolute; }
.spark { color: var(--sparkle); width: 14px; height: 14px; }
.stars { position: absolute; inset: 0; width: 100%; height: 100%; opacity: .9; }

/* Two hand-drawn bodies and three sparkles, placed rather than scattered: they live
   in the margins the 1120px column leaves empty, so they can never sit on a word.
   Below 1280px there is no margin wide enough to hold them and they are simply not
   drawn: percentage-placed decoration is exactly how a sparkle ends up on top of an
   h1 at 768px. The sky and the two starfields carry the atmosphere on their own. */
.deco, .deco-spark { display: none; }
@media (min-width: 1280px) {
  .deco, .deco-spark { display: block; }
  .deco-spark { position: absolute; }
  .planet-hero { top: 12%; right: 12px; width: 54px; }
  .moon-worlds { top: 14%; right: 16px; width: 38px; }
  .sp-a { top: 24%; left: 18px; width: 15px; }
  .sp-b { top: 62%; left: 34px; width: 11px; }
  .sp-c { top: 78%; right: 20px; width: 13px; }
}

@media (prefers-reduced-motion: no-preference) {
  @keyframes drift { 0%, 100% { transform: translateY(0) rotate(var(--tilt, 0deg)); } 50% { transform: translateY(-13px) rotate(var(--tilt, 0deg)); } }
  @keyframes twinkle { 0%, 100% { opacity: .2; } 50% { opacity: .9; } }
  .deco { animation: drift 7s ease-in-out infinite; }
  .moon-worlds { animation-duration: 9s; animation-delay: 1.4s; }
  .deco-spark { animation: twinkle 4s ease-in-out infinite; }
  .sp-b { animation-delay: 1.1s; }
  .sp-c { animation-delay: 2.3s; }
}

/* ---------- shipaton ribbon ---------- */

/* The hackathon announcement, meant to be the first thing the eye lands on:
   the one saturated band on a pastel page. Deep pink, not the display pink:
   #c23d74 is the only pink allowed to carry running text (4.9:1 with white). */
.shipaton {
  position: relative;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 11px clamp(14px, 4vw, 28px);
  background: linear-gradient(100deg, #b43a6e, #c23d74 45%, #d24580);
  color: #fff;
  text-decoration: none;
  font-weight: 700;
  font-size: 14.5px;
  line-height: 1.35;
  text-align: center;
  overflow: hidden;
}
.shipaton b { font-weight: 800; text-decoration: underline; text-underline-offset: 3px; white-space: nowrap; }
.shipaton:hover { filter: brightness(1.07); }
.shipaton:focus-visible { outline: 3px solid #ffd77c; outline-offset: -3px; }
.shipaton .spark { width: 15px; height: 15px; color: #ffd77c; flex: none; }
/* A shine sweeping the band every few seconds: the "look here" without a single
   extra pixel of height. Decoration only, so reduced motion turns it all off. */
.shipaton::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0; left: -90px;
  width: 70px;
  background: linear-gradient(90deg, rgba(255,255,255,0), rgba(255,255,255,.28), rgba(255,255,255,0));
  transform: skewX(-18deg);
  pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
  .shipaton .spark { animation: shipaton-twinkle 2.6s ease-in-out infinite; }
  .shipaton .spark:last-of-type { animation-delay: 1.3s; }
  .shipaton::after { animation: shipaton-shine 5.5s ease-in-out infinite; }
}
@keyframes shipaton-twinkle {
  0%, 100% { opacity: .55; transform: scale(.8) rotate(-8deg); }
  50% { opacity: 1; transform: scale(1.12) rotate(8deg); }
}
@keyframes shipaton-shine {
  0%, 55% { transform: translateX(0) skewX(-18deg); }
  100% { transform: translateX(calc(100vw + 180px)) skewX(-18deg); }
}

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

.top {
  position: relative;
  z-index: 3;
  display: flex;
  align-items: center;
  /* Wraps rather than pushing the language link off the right edge: at 280px the
     wordmark plus two links is 3px too wide for one line. */
  flex-wrap: wrap;
  gap: var(--s1) var(--s3);
  width: var(--page);
  margin-inline: auto;
  padding-block: var(--s4);
}
.mark { display: flex; align-items: center; gap: 10px; text-decoration: none; color: inherit; }
.mark .bun { width: 38px; }
.mark b {
  font-family: 'Baloo 2', sans-serif;
  font-weight: 800;
  font-size: 16px;
  letter-spacing: .07em;
  text-transform: uppercase;
}
.top nav { margin-left: auto; display: flex; align-items: center; gap: clamp(4px, 1.6vw, 18px); }
/* padding-block, not line-height: the links were 23px tall, one pixel under the 24px
   WCAG 2.5.8 minimum, and a nav link is not covered by the inline-text exception. */
.top nav a {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-soft);
  text-decoration: none;
  padding: 9px 8px;
  border-radius: var(--r-sm);
}
.top nav a:hover { color: var(--pink-ink); }
.top .lang {
  font-variant: small-caps;
  letter-spacing: .06em;
  border-left: 1px solid var(--hairline);
  margin-left: clamp(4px, 1.4vw, 14px);
  padding-left: clamp(12px, 2vw, 22px);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
}
@media (max-width: 620px) {
  .top nav .hide-s { display: none; }
}
/* The wordmark gives up a little size so the header still fits on one line at 360px.
   Wrapping is the safety net, not the plan: a two-line header eats the top of the
   fold, which is the most expensive space on the page. */
@media (max-width: 480px) {
  .mark .bun { width: 32px; }
  .mark b { font-size: 14px; letter-spacing: .05em; }
  .top nav a { padding-inline: 6px; }
}
@media (max-width: 380px) {
  .mark .bun { width: 28px; }
  .mark b { font-size: 12.5px; letter-spacing: .03em; }
  .top nav a { font-size: 13px; padding-inline: 4px; }
  .top .lang { padding-left: 10px; margin-left: 2px; }
}

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

.hero {
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, .98fr);
  gap: clamp(28px, 5vw, 64px);
  align-items: center;
  padding-block: clamp(20px, 4vw, 56px) clamp(64px, 9vw, 120px);
}
.hero h1 { font-size: clamp(38px, 6.4vw, 76px); margin-bottom: var(--s4); }
.hero .kicker { margin-bottom: var(--s4); }
.hero .lead { margin-bottom: var(--s5); }
.hero-note { margin-top: var(--s4); }

/* The board loop, bare. No phone frame and no card: a frame says "here is a picture
   of an app", the naked board says "here is the game". The mask feathers all four
   edges so the capture dissolves into the sky instead of sitting on top of it, and
   the video's own background is that same sky, so the seam is invisible. */
.loop {
  position: relative;
  -webkit-mask-image: radial-gradient(ellipse 74% 74% at 50% 50%, #000 58%, transparent 100%);
  mask-image: radial-gradient(ellipse 74% 74% at 50% 50%, #000 58%, transparent 100%);
}
.loop video, .loop img { width: 100%; }
/* Outside the masked element, or the control would fade out with the artwork. */
.loop-col { position: relative; }
.loop-btn {
  position: absolute;
  right: 1%;
  bottom: 1%;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: var(--pill);
  background: rgba(255, 253, 248, .82);
  color: var(--ink);
  cursor: pointer;
  padding: 0;
}
.loop-btn svg { width: 15px; height: 15px; }
.loop-btn:hover { background: var(--cream); }

/* ---------- calls to action ---------- */

/* Three ways in, one row, one optical height. The playable daily and the two stores
   carry the same weight because they are the same offer seen from two sides.
   Google bakes its clear space into the artwork (the black box is 168px inside a
   250px canvas); Apple does not. Both are drawn at a 44px badge with the 11px of
   clear space each vendor requires, which is why the two heights differ here and
   match on screen. Neither badge is recoloured, animated or cropped. */
.cta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); }
.cta .badge { display: block; }
.cta .badge img { width: auto; }
.cta .badge-apple img { height: 44px; margin: 11px; }
.cta .badge-play img { height: 66px; }

.daily {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 1px;
  min-height: 44px;
  margin: 11px 0;
  padding: 6px var(--s4);
  border-radius: var(--pill);
  background: var(--pink);
  color: #fff;
  text-decoration: none;
  box-shadow: 0 10px 26px rgba(228, 84, 143, .34);
  transition: transform .18s cubic-bezier(.34, 1.4, .64, 1), box-shadow .18s ease;
}
.daily b { font-size: 15.5px; font-weight: 700; letter-spacing: .01em; }
.daily span { font-size: 11.5px; opacity: .86; line-height: 1.2; }
.daily:hover { color: #fff; transform: translateY(-2px); box-shadow: 0 14px 32px rgba(228, 84, 143, .42); }
.night .daily { color: #2a1740; box-shadow: 0 10px 26px rgba(255, 140, 190, .3); }
.night .daily:hover { color: #2a1740; }

@media (prefers-reduced-motion: reduce) {
  .daily { transition: none; }
}

/* ---------- the rule ---------- */

.rule-head { max-width: 52ch; margin-bottom: var(--s7); }
.rule-head .kicker { margin-bottom: var(--s3); }
.rule-head h2 { margin-bottom: var(--s4); }

.steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: clamp(28px, 5vw, 56px);
}
.step { display: grid; gap: var(--s3); justify-items: start; }
.step .mini { width: clamp(140px, 22vw, 176px); height: auto; filter: drop-shadow(0 10px 22px rgba(150, 60, 90, .16)); }
.step h3 { color: var(--ink); }
.step p { font-size: 15.5px; color: var(--ink-soft); max-width: 34ch; }
/* The three boards are one board, three moves: stepping each one further down says so
   without a caption. The whole step moves, not just the drawing, or the third board
   would land on its own heading. */
.step:nth-child(2) { transform: translateY(clamp(8px, 2vw, 22px)); }
.step:nth-child(3) { transform: translateY(clamp(16px, 4vw, 44px)); }

/* ---------- the playable grid ---------- */
/* The third rule card is pushed down by 44px, so this block clears it rather than
   colliding with it. */
.try { margin-top: clamp(56px, 9vw, 108px); text-align: center; }
.try h3 { color: var(--ink); font-size: clamp(20px, 3.4vw, 28px); }
.try .lead { margin: var(--s3) auto var(--s5); max-width: 46ch; }

/* A phone-shaped window. aspect-ratio rather than a fixed height: the frame keeps
   its proportions at every width, and the grid inside sizes itself against the
   viewport it is given. */
.try-frame {
  width: min(340px, 88vw);
  aspect-ratio: 9 / 16;
  margin: 0 auto;
  border-radius: 26px;
  overflow: hidden;
  border: 1px solid rgba(var(--line-rgb), .22);
  box-shadow: 0 18px 50px rgba(150, 60, 90, .18);
  background: #f4e9fd;
}
.try-frame iframe { width: 100%; height: 100%; border: 0; display: block; }
.try-out { margin-top: var(--s4); font-size: 15px; }

/* ---------- twelve worlds ---------- */

.worlds-head { max-width: 52ch; margin-bottom: var(--s6); }
.worlds-head .kicker { margin-bottom: var(--s3); }
.worlds-head h2 { margin-bottom: var(--s4); }

.rail-wrap { position: relative; }
/* The dotted line the twelve nodes hang from: one constellation, drawn once, rather
   than twelve identical cards with a border. It belongs to the wrapper so it stays
   put while the nodes scroll past it. */
.rail-wrap::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 2px;
  background: repeating-linear-gradient(90deg, var(--hairline) 0 6px, transparent 6px 14px);
}
.rail {
  display: flex;
  gap: clamp(20px, 3vw, 38px);
  align-items: center;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--gutter);
  padding: var(--s6) var(--gutter);
  scrollbar-width: none;
}
.rail::-webkit-scrollbar { display: none; }
.world {
  position: relative;
  flex: 0 0 auto;
  width: 148px;
  scroll-snap-align: center;
  scroll-snap-stop: always;
  display: grid;
  justify-items: center;
  gap: var(--s2);
  text-align: center;
}
/* Alternating above and below the line, with the odd nodes drawn slightly larger:
   a constellation is uneven, a row of chips is not. */
.world:nth-child(odd) { transform: translateY(-38px); }
.world:nth-child(even) { transform: translateY(38px); }
.world .bun { width: 62px; }
.world:nth-child(3n+1) .bun { width: 76px; }
.world b { font-family: 'Baloo 2', sans-serif; font-weight: 700; font-size: 15px; line-height: 1.15; }
.world small { font-size: 11.5px; color: var(--ink-soft); letter-spacing: .01em; }
.world::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 7px;
  height: 7px;
  margin: -3.5px 0 0 -3.5px;
  border-radius: var(--pill);
  background: var(--sparkle);
}
.world:nth-child(odd)::after { top: calc(50% + 38px); }
.world:nth-child(even)::after { top: calc(50% - 38px); }
.rail-hint { font-size: 12.5px; color: var(--ink-soft); letter-spacing: .04em; }

@media (max-width: 620px) {
  .world:nth-child(odd) { transform: translateY(-24px); }
  .world:nth-child(even) { transform: translateY(24px); }
  .world:nth-child(odd)::after { top: calc(50% + 24px); }
  .world:nth-child(even)::after { top: calc(50% - 24px); }
}

/* ---------- the horizon ---------- */

.dusk-copy { background: var(--sky-3); padding-block: clamp(56px, 8vw, 100px) 0; }
.dusk-copy .inner { max-width: 46ch; }
.dusk-copy .kicker { margin-bottom: var(--s3); }
.dusk-copy h2 { margin-bottom: var(--s4); }

/* The whole idea of the page, in one element: the sky changes at the middle of the
   band, and the same home screen is caught on both sides of that line. Nothing is
   composited or faked, the two images are the real day and night captures of the
   same screen, and the lower one is clipped at exactly the height where the
   gradient turns. */
.horizon {
  position: relative;
  /* Above the section that follows, so the caption hanging below the band is not
     painted over by the next band's own background. */
  z-index: 1;
  display: grid;
  place-items: center;
  /* Symmetric padding, so the figure's centre is the band's centre and the gradient
     stop lands exactly on the image's clip. The caption hangs into the night below
     rather than adding height here, which is what used to break the alignment. */
  padding-block: clamp(40px, 6vw, 76px);
  background: linear-gradient(180deg,
    var(--sky-3) 0%,
    #f3c3d8 26%,
    #b98db0 42%,
    #5c4270 50%,
    var(--night-0) 60%,
    var(--night-0) 100%);
}
.horizon figure { position: relative; width: min(272px, 62vw); }
.horizon img {
  width: 100%;
  border-radius: var(--r-shot);
  box-shadow: 0 30px 70px rgba(60, 20, 60, .38);
}
.horizon .after {
  position: absolute;
  inset: 0;
  clip-path: inset(50% 0 0 0);
}
/* The caption stays out of the flow at every width. In the flow it made the figure
   taller than its image, which moved the image's centre off the band's centre and
   slid the horizon off the device by up to 27px on narrow screens. */
.horizon figcaption {
  position: absolute;
  left: 50%;
  bottom: -38px;
  transform: translateX(-50%);
  width: max-content;
  max-width: min(88vw, 360px);
  text-align: center;
  font-size: 12.5px;
  line-height: 1.4;
  color: rgba(244, 232, 242, .72);
}

/* ---------- night facts ---------- */

.facts-head { max-width: 52ch; margin-bottom: var(--s6); }
.facts-head .kicker { margin-bottom: var(--s3); }

/* A definition list, not four cards with an icon each. The content is four facts;
   four facts is what it should look like. */
.facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
  gap: 0 clamp(28px, 5vw, 72px);
  margin: 0;
}
.facts > div {
  padding-block: var(--s5);
  border-top: 1px solid var(--hairline);
}
.facts dt { font-family: 'Baloo 2', sans-serif; font-weight: 700; font-size: 19px; margin-bottom: var(--s2); }
.facts dd { margin: 0; font-size: 15.5px; color: var(--ink-soft); max-width: 40ch; }

/* ---------- gallery ---------- */

.gal-head { max-width: 52ch; margin-bottom: var(--s6); }
.gal-head .kicker { margin-bottom: var(--s3); }

/* Six tall portrait captures side by side would be a two-thousand-pixel wall, so the
   grid keeps them small and lets them scatter: a filmstrip of screens rather than a
   shelf of product shots. */
.shots {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(22px, 3vw, 44px);
  align-items: start;
  justify-items: center;
}
@media (min-width: 620px) { .shots { grid-template-columns: repeat(3, 1fr); } }
.shots figure { margin: 0; display: grid; gap: var(--s3); justify-items: center; width: 100%; max-width: 244px; }
.shots img { width: 100%; border-radius: 18px; box-shadow: var(--shot-shadow); }
.shots figcaption { font-family: 'Baloo 2', sans-serif; font-weight: 700; font-size: 14.5px; text-align: center; }
/* Scattered, not aligned: a shelf of six identical rectangles is the thing every
   app page does. */
.shots figure:nth-child(2) { transform: translateY(clamp(14px, 3vw, 40px)); }
.shots figure:nth-child(4) { transform: translateY(clamp(8px, 2vw, 26px)); }
.shots figure:nth-child(6) { transform: translateY(clamp(14px, 3vw, 40px)); }

/* ---------- closing ---------- */

.end { text-align: center; display: grid; justify-items: center; gap: var(--s4); }
.end .constel { width: min(260px, 72vw); color: var(--sparkle); }
.end .lead { margin-inline: auto; }
.end .cta { justify-content: center; margin-top: var(--s3); }

footer {
  background: var(--night-2);
  color: rgba(244, 232, 242, .68);
  padding-block: var(--s6) var(--s7);
}
footer .links { display: flex; flex-wrap: wrap; gap: var(--s1) var(--s4); justify-content: center; font-size: 14px; font-weight: 600; }
footer .links a { padding: 9px 10px; border-radius: var(--r-sm); }
footer a { color: rgba(244, 232, 242, .82); text-decoration: none; }
footer a:hover { color: #ff9dc4; }
footer .credits {
  margin-top: var(--s5);
  padding-top: var(--s4);
  border-top: 1px solid rgba(244, 232, 242, .14);
  text-align: center;
  font-size: 11.5px;
  line-height: 1.6;
  color: rgba(244, 232, 242, .5);
}
footer .credits p + p { margin-top: var(--s1); }

/* ---------- motion, added only where it is supported and wanted ---------- */

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    @keyframes lift { from { opacity: 0; transform: translateY(26px); } to { opacity: 1; transform: none; } }
    /* Exactly two entrance animations on the page. A uniform fade-in on every
       section is the tell this redesign exists to remove. */
    .hero h1, .end .constel {
      animation: lift .8s ease both;
    }
    @keyframes rise-far { from { transform: translateY(6%); } to { transform: translateY(-6%); } }
    .stars {
      animation: rise-far linear both;
      animation-timeline: view();
      animation-range: cover;
    }
  }
}

/* ---------- legal documents ---------- */

.doc-page { background: linear-gradient(180deg, var(--sky-0) 0%, var(--sky-1) 40%, var(--sky-3) 100%); min-height: 100vh; }
.doc-wrap { width: min(760px, 100% - 2 * var(--gutter)); margin-inline: auto; padding-block: var(--s5) var(--s7); }
.doc-back { display: inline-block; font-size: 14px; font-weight: 600; text-decoration: none; padding: 9px 10px; margin-left: -10px; border-radius: var(--r-sm); }
.doc-wrap h1 { font-size: clamp(32px, 5vw, 46px); margin: var(--s5) 0 var(--s2); }
.doc-wrap h2 { font-size: 21px; margin: var(--s5) 0 var(--s2); }
.doc-wrap h3 { font-size: 16.5px; margin: var(--s4) 0 var(--s1); }
.doc-wrap p, .doc-wrap li { font-size: 15.5px; line-height: 1.7; color: rgba(107, 74, 58, .92); }
.doc-wrap ul { padding-left: var(--s4); display: grid; gap: var(--s2); }
.doc-wrap p + p { margin-top: var(--s3); }

/* /get/: the two store badges ARE the page, so they get the room the legal
   pages give to prose. Centred because there is nothing else to align to. */
.get-wrap { text-align: center; max-width: 620px; }
.get-wrap .cta { justify-content: center; margin-top: var(--s5); }
.get-wrap .get-web { margin-top: var(--s6); font-size: 14.5px; opacity: .85; }
.doc {
  background: rgba(255, 253, 248, .74);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  padding: clamp(20px, 4vw, 34px);
  margin-top: var(--s4);
}
.muted { color: rgba(107, 74, 58, .66); font-size: 13.5px; }
/* The French half of a bilingual document is a section of the same page, not a
   second document: it keeps the visual weight of a title while staying an h2, so the
   page has one h1 and a heading order a screen reader can follow. */
.doc-lang { margin-top: var(--s7); }
.doc-lang .lang-title { font-size: clamp(30px, 4.6vw, 42px); margin-bottom: var(--s2); }
.doc-lang .doc h3 { font-size: 21px; margin: var(--s5) 0 var(--s2); }
.note {
  background: rgba(238, 111, 163, .08);
  border: 1px solid rgba(238, 111, 163, .24);
  border-radius: var(--r-md);
  padding: var(--s3) var(--s4);
  font-size: 14px;
  margin-top: var(--s4);
}
.doc-foot {
  margin-top: var(--s6);
  padding-top: var(--s4);
  border-top: 1px solid var(--hairline);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s1) var(--s4);
  justify-content: center;
  font-size: 14px;
  font-weight: 600;
}
.doc-foot a { padding: 9px 10px; border-radius: var(--r-sm); }

/* ---------- blog ---------- */

/* Articles ride the legal documents' layout (.doc-page, .doc-wrap, .doc): same sky,
   same card, same measure. Only what prose needs and legalese lacks is added here:
   code, a lede, and the index's post cards. */
.lede { font-size: 17.5px; color: var(--ink-soft); margin-bottom: var(--s4); }
.article code {
  font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  font-size: .88em;
  background: rgba(107, 74, 58, .08);
  border-radius: 6px;
  padding: 1px 6px;
}
.article pre {
  background: rgba(107, 74, 58, .07);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  padding: var(--s3) var(--s4);
  margin-block: var(--s3);
  overflow-x: auto;
  line-height: 1.6;
}
.article pre code { background: none; border-radius: 0; padding: 0; font-size: 13.5px; }
.article h2:first-of-type { margin-top: var(--s4); }
.post-list { display: grid; gap: var(--s4); margin-top: var(--s4); }
.post {
  background: rgba(255, 253, 248, .74);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  padding: clamp(20px, 4vw, 34px);
}
.post h2 { margin: 0; font-size: 24px; }
.post h2 a { text-decoration: none; }
.post h2 a:hover { text-decoration: underline; }
.post p { margin-top: var(--s2); }

/* ---------- narrow ---------- */

@media (max-width: 860px) {
  .hero { grid-template-columns: 1fr; }
  /* On a phone the board goes above the buttons: people decide whether this game is
     for them by looking at it, not by reading about it. */
  .hero .loop-col { order: -1; }
  .hero h1 { font-size: clamp(38px, 11vw, 60px); }
}
@media (max-width: 520px) {
  /* The playable daily takes the full width because it is the one thing you can do
     without leaving the page; the two badges keep their real size side by side under
     it, rather than being stretched into something neither vendor ships. */
  .cta { gap: var(--s2) var(--s3); }
  .daily { flex: 1 0 100%; align-items: center; }
  .cta .badge-apple img { height: 40px; margin: 10px; }
  .cta .badge-play img { height: 60px; }
  .shots figure:nth-child(n) { transform: none; }
}
