/* =========================================================================
   danieru.net: the landing site's styles. Pure CSS, no build step.

   The brand's values come from tokens.css, which scripts/brand.mjs writes
   from packages/shared: the palette (--palette-*), spacing, radii, type and
   shadows, both themes under the names the web app gives them (--color-*),
   and the shared Button, Card and Badge recipes (--button-*, --card-*,
   --badge-*). Use them
   rather than writing a value. `npm run lint` fails on a color that is
   neither the palette's nor the logo's, and on a @media width that is not a
   shared breakpoint (sm 480, md 768, lg 1024).
   ========================================================================= */

/* What only this page needs, per theme. */
:root {
  --page-width: 75rem;
  /* The header: its row of brand and actions, then, under md, a row for the
     section links. --header-height is all of it with its border, which
     in-page links scroll clear of. */
  --nav-row-height: 4rem;
  --sections-row-height: 2.5rem;
  --header-height: calc(var(--nav-row-height) + var(--sections-row-height) + 1px);
  /* The logo's blush, for the good-deeds half of About. */
  --logo-blush: #ffb6c1;
  /* The wash and the screentone dots behind the hero's art. */
  --hero-glow: var(--palette-indigo-200);
  --hero-tone: var(--palette-indigo-200);
  /* The marker behind "do good": brand.primary is 4.2:1 on it in light, 5.0:1
     in dark, over the 3:1 WCAG asks of text this large. */
  --hero-mark: var(--palette-indigo-200);
  --art-shadow: color-mix(in srgb, var(--palette-indigo-900) 22%, transparent);
  /* The screentone dots in About's corners. */
  --about-tone: var(--palette-indigo-200);
  /* About's two halves: the anime one in the brand's tint, the good-deeds
     one in red's (the hero's heart). */
  --anime-tint: var(--color-brand-subtle);
  --good-tint: var(--color-status-error-subtle);
  /* The glow at the head of the featured clubs. */
  --clubs-glow: color-mix(in srgb, var(--palette-indigo-100) 80%, transparent);
}

@media (min-width: 768px) {
  :root {
    --header-height: calc(var(--nav-row-height) + 1px);
  }
}

@media (prefers-color-scheme: dark) {
  :root {
    --hero-glow: var(--palette-indigo-800);
    --hero-tone: var(--palette-indigo-900);
    --hero-mark: var(--palette-indigo-800);
    --art-shadow: color-mix(in srgb, var(--palette-black) 45%, transparent);
    --about-tone: var(--palette-indigo-900);
    /* The theme's tints, softened into the card (slate 700): red 900 whole
       was a block of red. Their inks keep 5.6:1 (indigo 300) and 5.9:1
       (red 300), the text 7.5:1 or more. */
    --anime-tint: color-mix(in srgb, var(--palette-indigo-900) 60%, var(--palette-slate-700));
    --good-tint: color-mix(in srgb, var(--palette-red-900) 50%, var(--palette-slate-700));
    --clubs-glow: color-mix(in srgb, var(--palette-indigo-900) 55%, transparent);
  }
}

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-padding-top: var(--header-height);
}

/* The header's links glide to their section, unless less motion is asked for. */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

body {
  margin: 0;
  background: var(--color-bg-default);
  color: var(--color-text-primary);
  font-family: var(--font-family);
  font-size: var(--font-size-md);
  line-height: var(--line-height-normal);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1,
h2,
h3 {
  line-height: var(--line-height-tight);
}

a {
  color: var(--color-brand-primary);
}

/* One focus ring for every control, as the web app draws it: the theme's
   focus color, 3:1 on every surface, for keyboard focus only. */
:focus-visible {
  outline: 2px solid var(--color-border-focus);
  outline-offset: 2px;
}

/* Where "Skip to content" moves focus: not a control, so no ring. */
main[tabindex="-1"]:focus {
  outline: none;
}

.skip-link {
  position: fixed;
  top: var(--space-2);
  left: var(--space-2);
  z-index: 100;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--color-brand-primary);
  color: var(--color-text-inverse);
  font-weight: var(--font-weight-semibold);
  text-decoration: none;
  transform: translateY(-200%);
}

.skip-link:focus {
  transform: none;
}

/* A section's column: the gutter narrows on a phone, as in the web app. */
.container {
  width: 100%;
  max-width: var(--page-width);
  margin-inline: auto;
  padding-inline: var(--space-4);
}

@media (min-width: 480px) {
  .container {
    padding-inline: var(--space-6);
  }
}

@media (min-width: 1024px) {
  .container {
    padding-inline: var(--space-8);
  }
}

/* Buttons: the shared Button, size md unless --lg -------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--button-md-gap);
  min-height: var(--button-md-min-height);
  padding: var(--button-md-padding);
  border-radius: var(--button-radius);
  font-family: inherit;
  font-size: var(--button-md-font-size);
  line-height: var(--line-height-tight);
  text-align: center;
  text-decoration: none;
  transition: background-color 120ms ease;
}

.btn--lg {
  gap: var(--button-lg-gap);
  min-height: var(--button-lg-min-height);
  padding: var(--button-lg-padding);
  font-size: var(--button-lg-font-size);
}

.btn--primary {
  border: var(--button-primary-border);
  background: var(--button-primary-bg);
  color: var(--button-primary-text);
  font-weight: var(--button-primary-weight);
  box-shadow: var(--button-primary-shadow);
}

.btn--primary:hover {
  background: var(--button-primary-bg-hover);
}

.btn--secondary {
  border: var(--button-secondary-border);
  background: var(--button-secondary-bg);
  color: var(--button-secondary-text);
  font-weight: var(--button-secondary-weight);
  box-shadow: var(--button-secondary-shadow);
}

.btn--secondary:hover {
  background: var(--button-secondary-bg-hover);
}

.btn--ghost {
  border: var(--button-ghost-border);
  background: var(--button-ghost-bg);
  color: var(--button-ghost-text);
  font-weight: var(--button-ghost-weight);
  box-shadow: var(--button-ghost-shadow);
}

.btn--ghost:hover {
  background: var(--button-ghost-bg-hover);
}

/* Header ------------------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  border-bottom: 1px solid var(--color-border-default);
  background: color-mix(in srgb, var(--color-bg-default) 88%, transparent);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}

/* Brand and actions on one row, the section links under them; from md, all
   three on one row, the links between. */
.site-nav {
  display: grid;
  grid-template-areas:
    "brand actions"
    "sections sections";
  grid-template-columns: auto auto;
  grid-template-rows: var(--nav-row-height) minmax(var(--sections-row-height), auto);
  align-items: center;
  justify-content: space-between;
  column-gap: var(--space-3);
}

@media (min-width: 768px) {
  .site-nav {
    grid-template-areas: "brand sections actions";
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-rows: var(--nav-row-height);
  }
}

.brand {
  grid-area: brand;
  justify-self: start;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--button-md-min-height);
  border-radius: var(--radius-md);
  color: var(--color-text-primary);
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-bold);
  text-decoration: none;
}

.brand__logo {
  width: 2.25rem;
  height: 2.25rem;
  flex: none;
}

/* Links to the page's sections: text, as the web app's nav draws its items. */
.site-nav__sections {
  grid-area: sections;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1);
  margin: 0;
  /* On a phone the first link's text lines up with the logo. */
  margin-inline-start: calc(-1 * var(--space-3));
  padding: 0 0 var(--space-1);
  list-style: none;
}

@media (min-width: 768px) {
  .site-nav__sections {
    justify-content: center;
    margin-inline-start: 0;
    padding: 0;
  }
}

.site-nav__sections li {
  display: flex;
}

.site-nav__sections a {
  display: inline-flex;
  align-items: center;
  min-height: var(--button-sm-min-height);
  padding-inline: var(--space-3);
  border-radius: var(--radius-md);
  color: var(--color-text-secondary);
  font-weight: var(--font-weight-medium);
  text-decoration: none;
  transition:
    background-color 120ms ease,
    color 120ms ease;
}

@media (min-width: 768px) {
  .site-nav__sections a {
    min-height: var(--button-md-min-height);
  }
}

.site-nav__sections a:hover {
  background: var(--button-ghost-bg-hover);
  color: var(--color-text-primary);
}

.site-nav__actions {
  grid-area: actions;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* On a phone the hero's own Join button is a glance away. */
.site-nav__signup {
  display: none;
}

@media (min-width: 480px) {
  .site-nav__signup {
    display: inline-flex;
  }
}

/* Hero --------------------------------------------------------------------- */
.hero {
  position: relative;
  isolation: isolate;
  overflow: clip;
  padding-block: var(--space-10) var(--space-16);
}

.hero__inner {
  display: grid;
  gap: var(--space-12);
  align-items: center;
}

.hero__copy {
  max-width: 40rem;
  margin-inline: auto;
  text-align: center;
}

@media (min-width: 1024px) {
  .hero {
    padding-block: var(--space-16) var(--space-20);
  }

  .hero__inner {
    grid-template-columns: minmax(0, 11fr) minmax(0, 9fr);
  }

  .hero__copy {
    margin-inline: 0;
    text-align: start;
  }
}

/* The shared Badge, brand tone, size md, over a section's heading. */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-5);
  padding: var(--badge-md-padding);
  border-radius: var(--badge-radius);
  background: var(--badge-brand-bg);
  color: var(--badge-brand-text);
  font-size: var(--badge-md-font-size);
  font-weight: var(--font-weight-semibold);
  line-height: var(--line-height-tight);
}

.eyebrow svg {
  width: 0.875rem;
  height: 0.875rem;
  flex: none;
  fill: currentColor;
}

.hero__title {
  margin: 0;
  font-size: clamp(var(--font-size-2xl), 1.1rem + 3.2vw, var(--font-size-4xl));
  font-weight: var(--font-weight-bold);
  letter-spacing: -0.02em;
  text-wrap: balance;
}

/* "do good", over a marker stroke. */
.hero__title em {
  position: relative;
  z-index: 0;
  color: var(--color-brand-primary);
  font-style: normal;
  white-space: nowrap;
}

.hero__title em::after {
  content: "";
  position: absolute;
  z-index: -1;
  inset: auto -0.12em 0.06em;
  height: 0.38em;
  border-radius: var(--radius-full);
  background: var(--hero-mark);
  transform: rotate(-1.5deg);
}

.hero__lede {
  max-width: 36rem;
  margin: var(--space-5) auto 0;
  color: var(--color-text-secondary);
}

@media (min-width: 768px) {
  .hero__lede {
    font-size: var(--font-size-lg);
  }
}

@media (min-width: 1024px) {
  .hero__lede {
    margin-inline: 0;
  }
}

.hero__actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-8);
}

@media (min-width: 480px) {
  .hero__actions {
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
  }
}

@media (min-width: 1024px) {
  .hero__actions {
    justify-content: flex-start;
  }
}

.hero__note {
  margin: var(--space-4) 0 0;
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
}

/* The phone apps, under the note. While no store has them, a line saying
   they are coming; once one does, its button (scripts/brand.mjs writes
   which). */
.hero__soon {
  margin: var(--space-2) 0 0;
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
}

/* In the line, so it stays with the words when they wrap. */
.hero__soon .icon {
  width: 1.125rem;
  height: 1.125rem;
  margin-inline-end: var(--space-1);
  color: var(--color-brand-primary);
  vertical-align: -0.25em;
}

.hero__stores {
  margin-top: var(--space-6);
}

.hero__stores-label {
  margin: 0 0 var(--space-3);
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
}

/* A row of stores, the App Store first: each a button to its listing, or,
   until the app is there, a dashed outline saying it is coming. Under sm
   they take the full width, as the hero's buttons do. */
.stores {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 480px) {
  .stores {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
  }
}

@media (min-width: 1024px) {
  .stores {
    justify-content: flex-start;
  }
}

.stores > li {
  display: flex;
}

.stores > li > .btn {
  flex: 1;
}

/* A large store button keeps lg's height but, under sm, md's type, so its
   words fit on one line from 360px. */
.stores .btn--lg {
  font-size: var(--button-md-font-size);
}

@media (min-width: 480px) {
  .stores .btn--lg {
    font-size: var(--button-lg-font-size);
  }
}

/* Not a button: there is nowhere to go yet. The dashed edge is
   border.strong, 3:1 on the page. */
.store-soon {
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--button-md-min-height);
  padding: var(--button-md-padding);
  border: 1px dashed var(--color-border-strong);
  border-radius: var(--button-radius);
  color: var(--color-text-secondary);
  font-size: var(--button-md-font-size);
  line-height: var(--line-height-tight);
}

.store-soon__name {
  color: var(--color-text-primary);
  font-weight: var(--font-weight-semibold);
}

/* The shared Badge, brand tone, size sm. */
.store-soon__when {
  padding: var(--badge-sm-padding);
  border-radius: var(--badge-radius);
  background: var(--badge-brand-bg);
  color: var(--badge-brand-text);
  font-size: var(--badge-sm-font-size);
  font-weight: var(--font-weight-semibold);
  white-space: nowrap;
}

/* The art: inline SVG, colored by the palette and the logo. Its badges take
   the theme's surfaces, so they sit on the page in either theme. */
.hero__art {
  position: relative;
  width: min(100%, 30rem);
  margin-inline: auto;
}

/* A glow, and a manga's screentone dots, fading out around the art (the
   hero's, and the phone in Apps). */
.hero__art::before,
.apps__art::before {
  content: "";
  position: absolute;
  inset: -6% -10%;
  z-index: -1;
  background:
    radial-gradient(closest-side, var(--hero-glow), transparent),
    radial-gradient(circle, var(--hero-tone) 1.5px, transparent 2px) 0 0 / 18px 18px;
  -webkit-mask-image: radial-gradient(closest-side, black 35%, transparent);
  mask-image: radial-gradient(closest-side, black 35%, transparent);
  opacity: 0.9;
}

.hero__art svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

.art__card {
  fill: var(--color-surface-default);
  stroke: var(--color-border-default);
  stroke-width: 1;
}

.art__pop {
  filter: drop-shadow(0 8px 14px var(--art-shadow));
}

.art__ink {
  fill: var(--color-text-secondary);
  opacity: 0.45;
}

.art__track {
  fill: var(--color-border-default);
}

.art__ring {
  stroke: var(--color-surface-default);
}

.art__text {
  fill: var(--color-text-primary);
  font-family: var(--font-family);
  font-size: 22px;
  font-weight: var(--font-weight-bold);
}

/* Motion: the art arrives, its badges pop in, the sparkles twinkle twice,
   petals drift into place and someone types in the chat. All of it is over
   within five seconds (WCAG 2.2.2 asks for a pause button for anything
   longer), and none of it plays for anyone who has asked for less motion. */
@media (prefers-reduced-motion: no-preference) {
  .hero__art svg {
    animation: art-rise 800ms cubic-bezier(0.2, 0.7, 0.2, 1) both;
  }

  .art__pop,
  .art__sparkle,
  .art__dot {
    transform-box: fill-box;
    transform-origin: center;
  }

  .art__pop {
    animation: art-pop 600ms cubic-bezier(0.3, 1.4, 0.5, 1) both;
  }

  .art__pop--1 {
    animation-delay: 400ms;
  }

  .art__pop--2 {
    animation-delay: 550ms;
  }

  .art__pop--3 {
    animation-delay: 700ms;
  }

  .art__pop--4 {
    animation-delay: 850ms;
  }

  .art__dot {
    animation: art-typing 900ms ease-in-out 1300ms 3;
  }

  .art__dot:nth-of-type(2) {
    animation-delay: 1450ms;
  }

  .art__dot:nth-of-type(3) {
    animation-delay: 1600ms;
  }

  .art__sparkle {
    animation: art-twinkle 1400ms ease-in-out 2 both;
  }

  .art__sparkle--1 {
    animation-delay: 300ms;
  }

  .art__sparkle--2 {
    animation-delay: 700ms;
  }

  .art__sparkle--3 {
    animation-delay: 1100ms;
  }

  .art__sparkle--4 {
    animation-delay: 500ms;
  }

  .art__petal {
    animation: art-drift 2600ms cubic-bezier(0.2, 0.6, 0.3, 1) both;
  }

  .art__petal--2 {
    animation-delay: 300ms;
  }

  .art__petal--3 {
    animation-delay: 600ms;
  }

  .art__petal--4 {
    animation-delay: 900ms;
  }
}

@keyframes art-rise {
  from {
    opacity: 0;
    transform: translateY(1.5rem) scale(0.98);
  }
}

@keyframes art-pop {
  from {
    opacity: 0;
    transform: translateY(12px) scale(0.85);
  }
}

@keyframes art-typing {
  0%,
  60%,
  100% {
    transform: none;
  }

  30% {
    transform: translateY(-6px);
  }
}

@keyframes art-twinkle {
  0%,
  100% {
    opacity: 1;
    transform: none;
  }

  50% {
    opacity: 0.6;
    transform: scale(0.55);
  }
}

@keyframes art-drift {
  from {
    opacity: 0;
    transform: translate(-30px, -60px) rotate(-90deg);
  }
}

/* About and mission ---------------------------------------------------------- */
.about {
  position: relative;
  isolation: isolate;
  overflow: clip;
  padding-block: var(--space-16);
  border-top: 1px solid var(--color-border-default);
  background: linear-gradient(var(--color-bg-subtle), var(--color-bg-default) 40rem);
}

@media (min-width: 1024px) {
  .about {
    padding-block: var(--space-20);
  }
}

/* A manga's screentone dots, fading out from two corners. */
.about::before,
.about::after {
  content: "";
  position: absolute;
  z-index: -1;
  width: min(26rem, 80%);
  height: 18rem;
  background: radial-gradient(circle, var(--about-tone) 1.5px, transparent 2px) 0 0 / 18px 18px;
}

.about::before {
  top: 0;
  right: 0;
  -webkit-mask-image: radial-gradient(farthest-side at 100% 0, black, transparent);
  mask-image: radial-gradient(farthest-side at 100% 0, black, transparent);
}

.about::after {
  bottom: 0;
  left: 0;
  -webkit-mask-image: radial-gradient(farthest-side at 0 100%, black, transparent);
  mask-image: radial-gradient(farthest-side at 0 100%, black, transparent);
}

/* Icons: Lucide's, stroked in the text color around them. */
.icon {
  width: 1.25rem;
  height: 1.25rem;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

.about__intro,
.clubs__intro {
  max-width: 46rem;
  margin-inline: auto;
  text-align: center;
}

.about__title,
.clubs__title,
.apps__title {
  margin: 0;
  font-size: clamp(var(--font-size-xl), 1rem + 2.4vw, var(--font-size-3xl));
  font-weight: var(--font-weight-bold);
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.about__title em,
.clubs__title em,
.apps__title em {
  color: var(--color-brand-primary);
  font-style: normal;
}

/* "Or start your own." and "On your phone." keep to a line of their own
   when they have to wrap. */
.clubs__title em,
.apps__title em {
  display: inline-block;
}

.about__lede,
.clubs__lede,
.apps__lede {
  max-width: 38rem;
  margin: var(--space-5) auto 0;
  color: var(--color-text-secondary);
}

@media (min-width: 768px) {
  .about__lede,
  .clubs__lede,
  .apps__lede {
    font-size: var(--font-size-lg);
  }
}

.about__subtitle {
  margin: 0;
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-bold);
}

/* The two halves, anime and good deeds: stacked, then side by side from lg,
   with a heart where they meet. Each is the shared Card, elevated. */
.pillars {
  display: grid;
  grid-template-rows: auto var(--space-6) auto;
  max-width: 40rem;
  margin: var(--space-12) auto 0;
}

@media (min-width: 1024px) {
  .pillars {
    grid-template-columns: minmax(0, 1fr) var(--space-6) minmax(0, 1fr);
    grid-template-rows: auto;
    max-width: none;
  }
}

.pillar {
  display: flex;
  flex-direction: column;
  border: var(--card-elevated-border);
  border-radius: var(--card-radius);
  background: var(--card-elevated-bg);
  box-shadow: var(--card-elevated-shadow);
}

/* Each half's colors, and each club's (below): a tint, a color for its
   words and icons, and its medal. The ink is brand.primary or status.error,
   which the theme keeps at 4.5:1 on the card and on their tints. */
.pillar--anime,
.club--anime {
  --kind-tint: var(--anime-tint);
  --kind-ink: var(--color-brand-primary);
  --kind-medal: linear-gradient(
    140deg,
    var(--palette-indigo-400),
    var(--palette-indigo-700) 60%,
    var(--palette-indigo-900)
  );
}

.pillar--good,
.club--good {
  --kind-tint: var(--good-tint);
  --kind-ink: var(--color-status-error);
  --kind-medal: linear-gradient(140deg, var(--logo-blush), var(--palette-red-400) 55%, var(--palette-red-600));
}

.pillar__head {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--card-padding-lg);
  border-bottom: 1px solid var(--color-border-default);
  border-radius: calc(var(--card-radius) - 1px) calc(var(--card-radius) - 1px) 0 0;
  background: var(--kind-tint);
}

.pillar__medal {
  position: relative;
  display: grid;
  flex: none;
  place-items: center;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: var(--radius-lg);
  background: var(--kind-medal);
  box-shadow: 0 6px 12px var(--art-shadow);
  color: var(--palette-white);
}

.pillar__medal .icon {
  width: 1.75rem;
  height: 1.75rem;
}

.pillar--good .pillar__medal .icon {
  fill: var(--palette-white);
}

.pillar__spark {
  position: absolute;
  top: -0.5rem;
  right: -0.5rem;
  width: 1.25rem;
  height: 1.25rem;
  fill: var(--palette-yellow-400);
}

.pillar__kicker {
  margin: 0;
  color: var(--kind-ink);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
}

.pillar__title {
  margin: var(--space-1) 0 0;
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-bold);
}

.features {
  display: grid;
  gap: var(--space-6);
  margin: 0;
  padding: var(--card-padding-lg);
  list-style: none;
}

/* On a phone the icon heads its title and the words take the card's width;
   from sm the icon stands beside them all. */
.feature {
  display: grid;
  grid-template-columns: 2.5rem minmax(0, 1fr);
  column-gap: var(--space-3);
  align-items: center;
}

.feature > :not(.feature__icon, .feature__title) {
  grid-column: 1 / -1;
}

@media (min-width: 480px) {
  .feature {
    column-gap: var(--space-4);
    align-items: start;
  }

  .feature__icon {
    grid-row: span 3;
  }

  .feature > :not(.feature__icon, .feature__title) {
    grid-column: 2;
  }
}

.feature__icon {
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-md);
  background: var(--kind-tint);
  color: var(--kind-ink);
}

.feature__title {
  margin: 0;
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-semibold);
  line-height: var(--line-height-snug);
}

.feature__text {
  margin: var(--space-2) 0 0;
  color: var(--color-text-secondary);
}

@media (min-width: 480px) {
  .feature__text {
    margin-top: var(--space-1);
  }
}

/* The causes: the shared Badge, neutral, size sm. */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-3) 0 0;
  padding: 0;
  list-style: none;
}

.chips li {
  padding: var(--badge-sm-padding);
  border-radius: var(--badge-radius);
  background: var(--badge-neutral-bg);
  color: var(--badge-neutral-text);
  font-size: var(--badge-sm-font-size);
  font-weight: var(--font-weight-medium);
  line-height: var(--line-height-tight);
}

.pillar__note {
  display: flex;
  gap: var(--space-2);
  /* At the foot of the card, however tall its neighbor makes it. */
  margin: auto var(--card-padding-lg) var(--card-padding-lg);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border-default);
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
}

.pillar__note .icon {
  width: 1.125rem;
  height: 1.125rem;
  margin-top: 0.1em;
  color: var(--kind-ink);
}

.pillars__join {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  place-self: center;
  width: 3rem;
  height: 3rem;
  border: var(--card-elevated-border);
  border-radius: var(--radius-full);
  background: var(--card-elevated-bg);
  box-shadow: var(--shadow-sm);
  color: var(--palette-red-500);
}

.pillars__join .icon {
  fill: currentColor;
}

/* How it works: three numbered steps, joined by a dashed line, down the
   page and then across it from md. */
.how {
  margin-top: var(--space-16);
  text-align: center;
}

.steps {
  display: grid;
  gap: var(--space-8);
  max-width: 40rem;
  margin: var(--space-8) auto 0;
  padding: 0;
  list-style: none;
  text-align: start;
}

@media (min-width: 768px) {
  .steps {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    max-width: none;
  }
}

.step {
  position: relative;
  display: grid;
  grid-template-columns: 2.5rem minmax(0, 1fr);
  align-content: start;
  gap: var(--space-4);
}

@media (min-width: 768px) {
  .step {
    grid-template-columns: none;
    gap: var(--space-3);
  }
}

.step:not(:last-child)::after {
  content: "";
  position: absolute;
  top: calc(2.5rem + var(--space-2));
  bottom: calc(var(--space-2) - var(--space-8));
  left: calc(1.25rem - 1px);
  border-left: 2px dashed var(--color-border-strong);
}

@media (min-width: 768px) {
  .step:not(:last-child)::after {
    top: calc(1.25rem - 1px);
    right: calc(var(--space-2) - var(--space-8));
    bottom: auto;
    left: calc(2.5rem + var(--space-2));
    border-top: 2px dashed var(--color-border-strong);
    border-left: 0;
  }
}

.step__num {
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-full);
  background: var(--color-brand-primary);
  color: var(--color-text-inverse);
  font-weight: var(--font-weight-bold);
}

.step__title {
  margin: 0;
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semibold);
  line-height: var(--line-height-snug);
}

.step__text {
  margin: var(--space-1) 0 0;
  color: var(--color-text-secondary);
}

/* Privacy, on the shared Card, subtle. */
.privacy {
  display: grid;
  gap: var(--space-4);
  max-width: 48rem;
  margin: var(--space-16) auto 0;
  padding: var(--card-padding-lg);
  border: var(--card-subtle-border);
  border-radius: var(--card-radius);
  background: var(--card-subtle-bg);
  box-shadow: var(--card-subtle-shadow);
}

@media (min-width: 768px) {
  .privacy {
    grid-template-columns: auto minmax(0, 1fr);
    gap: var(--space-6);
    padding: var(--space-8);
  }
}

.privacy__icon {
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  border-radius: var(--radius-md);
  background: var(--color-brand-subtle);
  color: var(--color-brand-primary);
}

.privacy__icon .icon {
  width: 1.5rem;
  height: 1.5rem;
}

.privacy__text {
  margin: var(--space-2) 0 0;
  color: var(--color-text-secondary);
}

.privacy__list {
  display: grid;
  gap: var(--space-2);
  margin: var(--space-3) 0 0;
  padding-inline-start: var(--space-5);
  color: var(--color-text-secondary);
}

.privacy__list li::marker {
  color: var(--color-brand-primary);
}

/* Featured clubs ------------------------------------------------------------- */
.clubs {
  position: relative;
  isolation: isolate;
  overflow: clip;
  padding-block: var(--space-16);
  border-top: 1px solid var(--color-border-default);
  background: linear-gradient(var(--color-bg-default), var(--color-bg-subtle));
}

@media (min-width: 1024px) {
  .clubs {
    padding-block: var(--space-20);
  }
}

/* A glow over the intro, in the brand's indigo. */
.clubs::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0 0 auto;
  height: 24rem;
  background: radial-gradient(60% 100% at 50% 0, var(--clubs-glow), transparent);
}

/* One column, then two from md and three from lg. */
.clubs__list {
  display: grid;
  gap: var(--space-6);
  max-width: 40rem;
  margin: var(--space-12) auto 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 768px) {
  .clubs__list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    max-width: none;
  }
}

@media (min-width: 1024px) {
  .clubs__list {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* A club: the shared Card, elevated. Its name is the link, and the link
   covers the card, so the whole of it opens the club. Read in order, a
   club is its name, its description, then what it is for; the medal is
   decoration. */
.club {
  position: relative;
  display: grid;
  grid-template-areas:
    "medal kind"
    "name name"
    "text text";
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: auto auto 1fr;
  align-items: center;
  gap: var(--space-4) var(--space-3);
  padding: var(--card-padding-lg);
  border: var(--card-elevated-border);
  border-radius: var(--card-radius);
  background: var(--card-elevated-bg);
  box-shadow: var(--card-elevated-shadow);
  transition:
    border-color 120ms ease,
    box-shadow 120ms ease;
}

.club:hover,
.club:focus-within {
  border-color: var(--kind-ink);
  box-shadow: var(--shadow-lg);
}

.club__medal {
  grid-area: medal;
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  border-radius: var(--radius-lg);
  background: var(--kind-medal);
  box-shadow: 0 6px 12px var(--art-shadow);
  color: var(--palette-white);
}

.club__medal .icon {
  width: 1.5rem;
  height: 1.5rem;
}

/* What it is for: the shared Badge's shape, in the club's tint and ink. */
.club__kind {
  grid-area: kind;
  margin: 0;
  padding: var(--badge-sm-padding);
  border-radius: var(--badge-radius);
  background: var(--kind-tint);
  color: var(--kind-ink);
  font-size: var(--badge-sm-font-size);
  font-weight: var(--font-weight-semibold);
  line-height: var(--line-height-tight);
}

.club__name {
  grid-area: name;
  margin: 0;
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semibold);
  line-height: var(--line-height-snug);
}

.club__link {
  border-radius: var(--radius-sm);
  color: var(--color-text-primary);
  text-decoration: none;
}

/* The rest of the card, as part of the link. */
.club__link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--card-radius);
}

.club:hover .club__link {
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

/* After the name's last word, and on its line. */
.club__last {
  white-space: nowrap;
}

.club__arrow {
  width: 1.125rem;
  height: 1.125rem;
  margin-inline-start: var(--space-2);
  color: var(--kind-ink);
  vertical-align: -0.15em;
}

.club__text {
  grid-area: text;
  align-self: start;
  margin: 0;
  color: var(--color-text-secondary);
}

/* A club lifts, and its arrow leans on, under the pointer. */
@media (prefers-reduced-motion: no-preference) {
  .club {
    transition:
      border-color 120ms ease,
      box-shadow 120ms ease,
      transform 120ms ease;
  }

  .club:hover {
    transform: translateY(-2px);
  }

  .club__arrow {
    transition: transform 120ms ease;
  }

  .club:hover .club__arrow {
    transform: translateX(3px);
  }
}

.clubs__more {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  margin-top: var(--space-12);
  text-align: center;
}

.clubs__note {
  margin: 0;
  color: var(--color-text-secondary);
}

/* Apps: the phone apps, or until they're in the stores, the web app on a
   phone --------------------------------------------------------------------- */
.apps {
  position: relative;
  isolation: isolate;
  overflow: clip;
  padding-block: var(--space-16);
  border-top: 1px solid var(--color-border-default);
  background: linear-gradient(var(--color-bg-subtle), var(--color-bg-default));
}

/* Words above the phone; from lg the phone on the left, the words beside it.
   The phone is decoration, so moving it changes no reading or focus order. */
.apps__inner {
  display: grid;
  gap: var(--space-12);
  align-items: center;
}

.apps__copy {
  max-width: 42rem;
  margin-inline: auto;
  text-align: center;
}

@media (min-width: 1024px) {
  .apps {
    padding-block: var(--space-20);
  }

  /* The phone needs a third; the words take the rest, so both stores'
     buttons fit on one row at 1440px. */
  .apps__inner {
    grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
  }

  .apps__art {
    order: -1;
  }

  .apps__copy {
    margin-inline: 0;
    text-align: start;
  }

  .apps__lede {
    margin-inline: 0;
  }
}

/* The buttons: under sm they take the full width, as the hero's do. */
.apps__actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin-top: var(--space-8);
}

@media (min-width: 480px) {
  .apps__actions {
    align-items: center;
  }
}

@media (min-width: 1024px) {
  .apps__actions {
    align-items: flex-start;
  }
}

.apps__art {
  position: relative;
  width: min(100%, 20rem);
  margin-inline: auto;
}

/* The phone; the app's icon inside it is an <svg> of its own, sized by its attributes. */
.apps__art > svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

.apps__float {
  filter: drop-shadow(0 8px 14px var(--art-shadow));
}

/* Newsletter (P-5.5): the signup form, on a card of its own ---------------- */
/* P-7.6: the launch party, written into the page only once its date is set. */
.launch-event {
  padding-block: var(--space-16);
  border-top: 1px solid var(--color-border-default);
}

.launch-event__card {
  max-width: 40rem;
  margin-inline: auto;
  padding: var(--space-8) var(--card-padding-lg);
  border: var(--card-outlined-border);
  border-radius: var(--card-radius);
  background: var(--card-outlined-bg);
  box-shadow: var(--shadow-lg);
  text-align: center;
}

.launch-event__title {
  margin: 0;
  font-size: clamp(var(--font-size-xl), 1rem + 2.4vw, var(--font-size-3xl));
  font-weight: var(--font-weight-bold);
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.launch-event__lede {
  margin: var(--space-5) 0 var(--space-6);
  color: var(--color-text-secondary);
}

.news {
  position: relative;
  isolation: isolate;
  overflow: clip;
  padding-block: var(--space-16);
  border-top: 1px solid var(--color-border-default);
  background: radial-gradient(60% 70% at 50% 0%, var(--clubs-glow), transparent);
}

@media (min-width: 1024px) {
  .news {
    padding-block: var(--space-20);
  }
}

.news__card {
  position: relative;
  max-width: 40rem;
  margin-inline: auto;
  padding: var(--space-8) var(--card-padding-lg);
  border: var(--card-outlined-border);
  border-radius: var(--card-radius);
  background: var(--card-outlined-bg);
  box-shadow: var(--shadow-lg);
  text-align: center;
}

@media (min-width: 768px) {
  .news__card {
    padding: var(--space-12);
  }
}

/* The envelope over the heading, as a club's medal. */
.news__medal {
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  margin: 0 auto var(--space-4);
  border-radius: var(--radius-lg);
  background: var(--color-brand-primary);
  box-shadow: 0 6px 12px var(--art-shadow);
  color: var(--color-text-inverse);
}

.news__medal .icon {
  width: 1.5rem;
  height: 1.5rem;
}

.news__title {
  margin: 0;
  font-size: clamp(var(--font-size-xl), 1rem + 2.4vw, var(--font-size-3xl));
  font-weight: var(--font-weight-bold);
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.news__title em {
  display: inline-block;
  color: var(--color-brand-primary);
  font-style: normal;
}

.news__lede {
  margin: var(--space-5) 0 0;
  color: var(--color-text-secondary);
}

/* The form: a field as the web app's FormField lays one out, then the
   button beside it from sm, under it on a phone. */
.signup {
  margin-top: var(--space-8);
  text-align: start;
}

.signup__field {
  display: grid;
  gap: var(--space-1);
  min-width: 0;
}

.signup__label {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
}

.signup__hint {
  margin: 0;
  color: var(--color-text-secondary);
  font-size: var(--font-size-xs);
}

/* The field, what is wrong with it right under it, then the button; from sm
   the button sits beside the field. */
.signup__row {
  display: grid;
  grid-template-areas:
    "input"
    "error"
    "submit";
  gap: 0 var(--space-3);
  margin-top: var(--space-1);
}

@media (min-width: 480px) {
  .signup__row {
    grid-template-areas:
      "input submit"
      "error error";
    grid-template-columns: minmax(0, 1fr) auto;
  }
}

.signup__input {
  grid-area: input;
}

.signup__submit {
  grid-area: submit;
  margin-top: var(--space-3);
}

@media (min-width: 480px) {
  .signup__submit {
    margin-top: 0;
  }
}

.signup__error {
  grid-area: error;
}

/* The shared Input: inputColors and INPUT_RADIUS in ui/variants.ts. */
.signup__input {
  width: 100%;
  min-width: 0;
  min-height: var(--button-md-min-height);
  padding: var(--space-3);
  border: 1px solid var(--input-border);
  border-radius: var(--input-radius);
  background: var(--input-bg);
  color: var(--input-text);
  font: inherit;
  font-size: var(--font-size-md);
}

.signup__input::placeholder {
  color: var(--input-placeholder);
}

.signup__input[aria-invalid="true"] {
  border-color: var(--input-border-invalid);
}

/* A <button> drawn as the page's links are: the shared Button. */
.signup__submit {
  cursor: pointer;
  white-space: nowrap;
}

.signup__submit[aria-disabled="true"] {
  cursor: progress;
}

/* What is wrong with the address, under the field, as the web app's FieldError. */
.signup__error {
  display: flex;
  align-items: flex-start;
  gap: var(--space-1);
  margin: var(--space-1) 0 0;
  color: var(--color-status-error);
  font-size: var(--font-size-sm);
}

.signup__error[hidden] {
  display: none;
}

.signup__error .icon,
.signup__outcome .icon {
  width: 1rem;
  height: 1.25rem;
}

/* The status line and the alert: empty until there is something to say. */
.signup__status,
.signup__alert,
.signup__outcome {
  margin: 0;
  border-radius: var(--radius-md);
  font-size: var(--font-size-sm);
}

/* Empty, they take no room, but stay on the page (and in the accessibility
   tree), so what they come to say is read out. */
.signup__status:not(:empty),
.signup__alert:not(:empty) {
  margin-top: var(--space-4);
  padding: var(--space-3);
}

.signup__status:not(:empty),
.signup__outcome--status {
  background: var(--badge-success-bg);
  color: var(--badge-success-text);
  font-weight: var(--font-weight-medium);
}

.signup__alert:not(:empty),
.signup__outcome--field,
.signup__outcome--alert {
  background: var(--badge-error-bg);
  color: var(--badge-error-text);
}

/* Without JavaScript the API sends the browser back to #newsletter-<outcome>:
   that outcome's message shows. Once the script answers, it says instead. */
.signup__outcome {
  display: none;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-3);
}

.signup__outcome:target {
  display: flex;
  margin-top: var(--space-4);
}

.signup--live .signup__outcome:target {
  display: none;
}

.signup__outcome:focus {
  outline: none;
}

.signup__note {
  margin: var(--space-4) 0 0;
  color: var(--color-text-secondary);
  font-size: var(--font-size-xs);
}

/* The trap for bots: kept from sight, from Tab and from screen readers. */
.signup__trap {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Page not found (404.html, P-5.6) ----------------------------------------- */
.not-found {
  padding-block: var(--space-16) var(--space-20);
}

.not-found__inner {
  max-width: 40rem;
  text-align: center;
}

.not-found__title {
  margin: 0;
  font-size: clamp(var(--font-size-2xl), 1.1rem + 3.2vw, var(--font-size-4xl));
  font-weight: var(--font-weight-bold);
  letter-spacing: -0.02em;
}

.not-found__text {
  margin: var(--space-5) 0 0;
  color: var(--color-text-secondary);
}

.not-found__actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-8);
}

@media (min-width: 480px) {
  .not-found__actions {
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
  }
}

/* Footer ------------------------------------------------------------------- */
.site-footer {
  border-top: 1px solid var(--color-border-default);
  padding-block: var(--space-8);
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
}

.site-footer p {
  margin: 0;
}

/* How to reach us, and the legal pages (the web app's), which the App Store
   listing's support and privacy links rely on. */
.site-footer .site-footer__help {
  margin-top: var(--space-2);
}

.site-footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-6);
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
}

.site-footer__links a {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
}

/* The stores' trademarks, as Apple and Google ask wherever the page names them. */
.site-footer .site-footer__legal {
  margin-top: var(--space-2);
  font-size: var(--font-size-xs);
}

/* Scroll motion ---------------------------------------------------------------
   What moves as the visitor scrolls, and only then: each part is tied to
   where its section is on screen (a view timeline), so it stands still while
   the page does and is never a loop to pause. It moves with `translate`,
   `rotate` and `scale`, which add to a part's own transform (the art's
   attributes, a club's hover lift) rather than replace it, and never fades
   words. A browser without scroll timelines, or anyone who has asked for
   less motion, gets the page at rest, which is every part in its place.

   - The hero's art comes apart in depth: the stars and the far city lag,
     the moon sets behind the city, the mascot holds still and the badges,
     sparkles and petals pass in front, faster than the page.
   - About's two halves start apart and meet, and the heart between them
     fills in as they do.
   - How it works draws the line from step to step.
   - The clubs rise into place, one after another.
   - The phone in Apps stands up as it arrives, its badges on planes of
     their own.
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .hero__art {
      view-timeline: --hero-art block;
    }

    /* At rest (none) when the art is in the middle of the screen, which is
       where it opens on a wide screen; offset before and after. In the
       art's own units, so a phone's smaller art travels less. */
    .art__plane {
      animation: linear both;
      animation-timeline: --hero-art;
    }

    .art__plane--stars {
      animation-name: plane-stars;
    }

    .art__plane--moon {
      animation-name: plane-moon;
    }

    .art__plane--far {
      animation-name: plane-far;
    }

    .art__plane--near {
      animation-name: plane-near;
    }

    .art__plane--badge-1 {
      animation-name: plane-badge-1;
    }

    .art__plane--badge-2 {
      animation-name: plane-badge-2;
    }

    .art__plane--badge-3 {
      animation-name: plane-badge-3;
    }

    .art__plane--badge-4 {
      animation-name: plane-badge-4;
    }

    .art__plane--petals {
      animation-name: plane-petals;
    }

    /* The two halves (the signature). Stacked, each slides in from its own
       side as it comes on screen, and the heart between them fills in as it
       reaches the middle of it. Side by side (lg, below), they travel
       toward each other together. */
    .pillar,
    .pillars__join,
    .pillars__join .icon {
      animation: linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 70%;
    }

    .pillars__join,
    .pillars__join .icon {
      animation-range: entry 40% cover 50%;
    }

    .pillar--anime {
      animation-name: half-anime;
    }

    .pillar--good {
      animation-name: half-good;
    }

    .pillars__join {
      animation-name: join-heart;
    }

    .pillars__join .icon {
      animation-name: join-fill;
    }

    /* How it works: the numbers arrive in turn, and the line between two
       steps draws itself from one to the next. */
    .steps {
      view-timeline: --steps block;
    }

    .step__num,
    .step:not(:last-child)::after {
      animation: linear both;
      animation-timeline: --steps;
    }

    .step__num {
      animation-name: step-num;
    }

    .step:nth-child(1) .step__num {
      animation-range: cover 5% cover 18%;
    }

    .step:nth-child(2) .step__num {
      animation-range: cover 26% cover 34%;
    }

    .step:nth-child(3) .step__num {
      animation-range: cover 42% cover 50%;
    }

    .step:not(:last-child)::after {
      animation-name: step-line-down;
      transform-origin: top;
    }

    .step:nth-child(1)::after {
      animation-range: cover 14% cover 28%;
    }

    .step:nth-child(2)::after {
      animation-range: cover 30% cover 44%;
    }

    /* The clubs: each rises as it comes on screen, settled before it is
       halfway up. */
    .club {
      animation: club-rise linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 80%;
    }

    .apps__art {
      view-timeline: --phone block;
    }

    .apps__art > svg,
    .apps__float {
      animation: linear both;
      animation-timeline: --phone;
    }

    .apps__art > svg {
      animation-name: phone-up;
      animation-range: entry 0% cover 45%;
      /* Pivoting on its foot's corner, it tips back without reaching below
         the section, which would make the section taller than its words. */
      transform-origin: 0 100%;
    }

    .apps__float--message {
      animation-name: float-near;
    }

    .apps__float--heart {
      animation-name: float-far;
    }
  }
}

/* Side by side, the halves start further apart, and the steps' line runs across. */
@media (prefers-reduced-motion: no-preference) and (min-width: 768px) {
  @supports (animation-timeline: view()) {
    .step:not(:last-child)::after {
      animation-name: step-line-across;
      transform-origin: left;
    }
  }
}

/* Side by side, the halves start further apart and travel together, over
   most of a screen's scroll: they meet, and the heart fills, as the heart
   reaches the middle of the screen. */
@media (prefers-reduced-motion: no-preference) and (min-width: 1024px) {
  @supports (animation-timeline: view()) {
    .pillars {
      view-timeline: --halves block;
    }

    .pillar,
    .pillars__join,
    .pillars__join .icon {
      animation-timeline: --halves;
      animation-range: cover 5% cover 45%;
    }

    .pillar--anime {
      animation-name: half-anime-wide;
    }

    .pillar--good {
      animation-name: half-good-wide;
    }
  }
}

@keyframes plane-stars {
  from {
    translate: 0 -36px;
  }

  50% {
    translate: none;
  }

  to {
    translate: 0 44px;
  }
}

@keyframes plane-moon {
  from {
    translate: 0 -30px;
  }

  50% {
    translate: none;
  }

  to {
    translate: 0 150px;
  }
}

@keyframes plane-far {
  from {
    translate: 0 -24px;
  }

  50% {
    translate: none;
  }

  to {
    translate: 0 28px;
  }
}

@keyframes plane-near {
  from {
    translate: 0 -10px;
  }

  50% {
    translate: none;
  }

  to {
    translate: 0 12px;
  }
}

@keyframes plane-badge-1 {
  from {
    translate: -14px 60px;
  }

  50% {
    translate: none;
  }

  to {
    translate: -18px -70px;
  }
}

@keyframes plane-badge-2 {
  from {
    translate: 10px 40px;
  }

  50% {
    translate: none;
  }

  to {
    translate: 16px -56px;
  }
}

@keyframes plane-badge-3 {
  from {
    translate: 16px 80px;
  }

  50% {
    translate: none;
  }

  to {
    translate: 22px -96px;
  }
}

@keyframes plane-badge-4 {
  from {
    translate: -10px 100px;
  }

  50% {
    translate: none;
  }

  to {
    translate: -14px -120px;
  }
}

@keyframes plane-petals {
  from {
    translate: 0 120px;
  }

  50% {
    translate: none;
  }

  to {
    translate: 0 -150px;
  }
}

@keyframes half-anime {
  from {
    translate: -1.5rem 0;
  }
}

@keyframes half-good {
  from {
    translate: 1.5rem 0;
  }
}

@keyframes half-anime-wide {
  from {
    translate: -5rem 1rem;
    rotate: -3deg;
  }
}

@keyframes half-good-wide {
  from {
    translate: 5rem 1rem;
    rotate: 3deg;
  }
}

/* The heart: small and outlined while the halves are apart, a beat bigger
   as they meet, then at rest, filled. */
@keyframes join-heart {
  from {
    scale: 0.4;
    rotate: -20deg;
  }

  80% {
    scale: 1.2;
    rotate: none;
  }

  to {
    scale: none;
  }
}

@keyframes join-fill {
  from,
  70% {
    fill: transparent;
  }
}

@keyframes step-num {
  from {
    scale: 0.3;
  }
}

@keyframes step-line-down {
  from {
    scale: 1 0;
  }
}

@keyframes step-line-across {
  from {
    scale: 0 1;
  }
}

@keyframes club-rise {
  from {
    translate: 0 3rem;
  }
}

@keyframes phone-up {
  from {
    rotate: -10deg;
  }
}

@keyframes float-near {
  from {
    translate: -16px 48px;
  }

  to {
    translate: 8px -40px;
  }
}

@keyframes float-far {
  from {
    translate: 12px 24px;
  }

  to {
    translate: -6px -20px;
  }
}

/* Windows' high-contrast themes draw no backgrounds: give the controls and
   the badge an edge. */
@media (forced-colors: active) {
  .btn,
  .eyebrow,
  .chips li,
  .step__num,
  .pillar__medal,
  .pillars__join,
  .club__medal,
  .club__kind,
  .store-soon__when,
  .news__medal,
  .signup__outcome,
  .signup__status:not(:empty),
  .signup__alert:not(:empty) {
    border: 1px solid CanvasText;
  }
}
