/* The home page: a hero, the cover wall, four tinted chapters, a gallery and the beta invitation. */

.home {
  --chapter-tint: var(--color-surface);
}

.section-title {
  margin: 0;
  font-size: var(--text-display);
  font-weight: 700;
  line-height: 1.12;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.section-lede {
  margin: var(--space-md) 0 0;
  color: var(--color-ink-2);
  font-size: var(--text-md);
  font-weight: 500;
  line-height: 1.45;
  text-wrap: pretty;
}

.status {
  font-weight: 600;
}

.status-playing {
  color: var(--color-teal-text);
}

.status-played {
  color: var(--color-blue-text);
}

.status-want {
  color: var(--color-pink-text);
}

/* The colour drift washes behind the hero and the invitation. */
.hero-wash,
.join-wash {
  position: absolute;
  inset-inline: 0;
  z-index: -1;
  background: url("./assets/colour-drift.svg") center / 100% 100% no-repeat;
  opacity: var(--backdrop-opacity);
  pointer-events: none;
}

/* The home header starts clear and turns to glass on scroll. */
@supports (animation-timeline: scroll()) {
  .home .site-header::before {
    animation: header-glass linear both;
    animation-timeline: scroll(root);
    animation-range: 0 6rem;
  }
}

@keyframes header-glass {
  from {
    border-color: transparent;
    background: transparent;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

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

.hero {
  position: relative;
  isolation: isolate;
  margin-top: calc(-1 * var(--header-height) - env(safe-area-inset-top));
  padding-top: calc(var(--header-height) + env(safe-area-inset-top) + clamp(2rem, 6vh, 5rem));
}

.hero-wash {
  top: 0;
  height: clamp(40rem, 110vh, 64rem);
  -webkit-mask-image: linear-gradient(#000 55%, transparent);
  mask-image: linear-gradient(#000 55%, transparent);
}

.hero-copy {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  text-align: center;
}

.hero-title {
  margin: 0;
  font-size: min(var(--text-hero), 9.1vw);
  font-weight: 700;
  line-height: 1.12;
  letter-spacing: -0.025em;
}

.hero-title > span {
  display: block;
}

/* The first two statements stay on one line; the last may wrap. */
.hero-title > span:not(:last-child) {
  white-space: nowrap;
}

.hero-title > span:last-child {
  text-wrap: balance;
}

.hero-actions {
  width: 100%;
  margin-top: clamp(1.75rem, 4vh, 2.5rem);
  display: grid;
  justify-items: center;
  gap: var(--space-sm);
}

.hero-buttons {
  width: 100%;
  max-width: 30rem;
}

.hero-buttons .download-button {
  flex-basis: 13rem;
}

.hero-buttons .download-button,
.join-buttons .download-button {
  gap: var(--space-sm);
}

.hero-buttons .download-button-label,
.join-buttons .download-button-label {
  white-space: nowrap;
}

.hero-note {
  margin: 0;
  color: var(--color-ink-2);
  font-size: var(--text-sm);
  font-weight: 500;
}

.hero-stage {
  position: relative;
  isolation: isolate;
  width: min(calc(100% - 2 * var(--page-gutter)), 32rem);
  margin: clamp(2.5rem, 6vh, 4rem) auto 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
  gap: var(--space-md);
}

.hero-device {
  grid-area: 1 / 1;
  min-width: 0;
  margin: 0;
  container-type: inline-size;
  text-align: center;
}

.hero-device:nth-child(2) {
  grid-column: 2;
  padding-top: clamp(1.5rem, 4vw, 3rem);
}

.hero-device:first-child .device {
  rotate: -2deg;
}

.hero-device:nth-child(2) .device {
  rotate: 2deg;
}

.hero-platform {
  margin-top: var(--space-lg);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  color: var(--color-ink-2);
  font-size: var(--text-sm);
  font-weight: 600;
}

.hero-platform-logo {
  width: 1.125rem;
  height: 1.125rem;
  flex: none;
}

.hero-device .device {
  --device-width: 100cqi;
}

/* A shared beta code waits for desktop visitors, who need to install on their phone. */
.hero-scan {
  display: none;
}

/*
 * A wide screen: the copy on the left and the stage on the right, so the first screen shows the
 * iPhone and Android pair as well as the promise.
 */
@media (min-width: 72rem) {
  .hero-layout {
    max-width: calc(var(--page-width) + 2 * var(--page-gutter));
    margin-inline: auto;
    padding-inline: var(--page-gutter);
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: start;
    column-gap: var(--space-xl);
  }

  .hero-copy {
    padding-top: var(--space-2xl);
    padding-inline: 0;
    container-type: inline-size;
    justify-items: start;
    text-align: start;
  }

  /* The compact hook takes three lines beside the larger phone pair. */
  .hero-title {
    font-size: clamp(1.625rem, 7.3cqi, 2.625rem);
  }

  .hero-title > span {
    white-space: nowrap;
  }

  .hero-actions {
    justify-items: start;
  }

  .hero-stage {
    width: 38rem;
    margin: 0;
  }

}

@media (min-width: 72rem) and (hover: hover) and (pointer: fine) {
  .hero-scan {
    margin-top: var(--space-lg);
    display: flex;
    align-items: center;
    gap: var(--space-md);
  }

  .hero-scan img {
    width: 5.5rem;
    height: 5.5rem;
    flex: none;
    border-radius: var(--radius-sm);
    outline: var(--rule-thin) solid var(--color-rule);
  }

  .hero-scan p {
    max-width: 14rem;
    margin: 0;
    color: var(--color-ink-2);
    font-size: var(--text-sm);
    font-weight: 500;
    line-height: 1.4;
  }
}

/* The rebuilt app components, the cover wall and the screenshot gallery are artwork: they never
   start a text selection or a drag. Headings, ledes and captions around them stay selectable. */
.hero-stage,
.stage,
.wall-stage,
.cover-wall,
.more-gallery {
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

/* Stages: one phone with the app's own components floating over it ------------------------- */

.stage {
  position: relative;
  display: grid;
  justify-items: center;
}

.stage-phone {
  --device-width: 15.5rem;
  grid-area: 1 / 1;
}

.stage-float {
  position: relative;
  z-index: 2;
  width: min(21.875rem, calc(100% - 2 * var(--page-gutter)));
}

/* Tracking on a phone: the journal over the playthrough, the playing stack beneath it. */
.track-stage .stage-float-start {
  margin-top: -16rem;
}

.track-stage .stage-float-end {
  margin: var(--space-sm) 0 var(--space-lg);
}

/* The Currently playing card keeps Home's width, so its scrim falls as it does there. */
.stage .stage-float-playing {
  width: min(19.625rem, calc(100% - 2 * var(--page-gutter)));
}

/* Friends on a phone: their latest over the activity, the reviews beneath. Discover likewise lays
   the community's ratings over Explore, with the lists beneath. */
.friends-stage .stage-float-start,
.discover-stage .stage-float-start {
  margin-top: -16rem;
}

.friends-stage .rail,
.discover-stage .rail {
  margin-top: 0;
}

@media (min-width: 62rem) {
  .stage {
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    align-items: center;
  }

  .stage-phone {
    --device-width: 17rem;
    grid-area: 1 / 2 / span 2;
  }

  .stage-float {
    width: 21.875rem;
  }

  .stage .stage-float {
    display: block;
    margin: 0;
  }

  .stage .stage-float-start {
    grid-area: 2 / 1;
    justify-self: end;
    align-self: start;
    margin-inline-end: -4.5rem;
  }

  .stage .stage-float-end {
    grid-area: 1 / 3;
    justify-self: start;
    align-self: end;
    margin-block-end: 2rem;
    margin-inline-start: -4.5rem;
  }

  .stage .stage-float-playing {
    width: 19.625rem;
  }

  /* Lowered clear of the phone's status bar, over the playthrough's hero instead. */
  .track-stage .stage-float-end {
    top: 6rem;
  }
}

/* Cover wall --------------------------------------------------------------------------------- */

.wall {
  padding-top: clamp(4rem, 10vh, 7rem);
}

.wall-intro {
  position: relative;
  z-index: 1;
  max-width: calc(38rem + 2 * var(--page-gutter));
  text-align: center;
}

/* A month of the diary laid over the wall of covers. */
.wall-stage {
  margin-top: clamp(2.5rem, 6vh, 4rem);
  display: grid;
  place-items: center;
}

.wall-stage > * {
  grid-area: 1 / 1;
}

.wall-float {
  /* A stronger lift than the other cards, so they stand off the covers behind them. */
  --shadow-lifted: 0 2px 18px rgb(0 0 0 / 0.2), 0 1px 4px rgb(0 0 0 / 0.14);
  position: relative;
  z-index: 1;
  width: min(21.875rem, calc(100% - 2 * var(--page-gutter)));
}

/* Dark mode casts no card shadows. */
@media (prefers-color-scheme: dark) {
  .wall-float {
    --shadow-lifted: none;
  }
}

.cover-wall {
  --cover: 6.5rem;
  --cover-gap: calc(var(--cover) * 0.065);
  --wall-height: 30rem;
  /* The wall grows past the stage at both ends, so the longer fades take no layout space. */
  --wall-extend: 6rem;
  position: relative;
  justify-self: stretch;
  height: calc(var(--wall-height) + 2 * var(--wall-extend));
  margin-block: calc(-1 * var(--wall-extend));
  overflow: hidden;
  pointer-events: none;
  /* Quiet enough to sit behind the diary, easing into the page over the top and bottom 40%. */
  opacity: var(--wall-opacity);
  --wall-fade: linear-gradient(
    transparent,
    rgb(0 0 0 / 0.043) 5%,
    rgb(0 0 0 / 0.156) 10%,
    rgb(0 0 0 / 0.316) 15%,
    rgb(0 0 0 / 0.5) 20%,
    rgb(0 0 0 / 0.684) 25%,
    rgb(0 0 0 / 0.844) 30%,
    rgb(0 0 0 / 0.957) 35%,
    #000 40%,
    #000 60%,
    rgb(0 0 0 / 0.957) 65%,
    rgb(0 0 0 / 0.844) 70%,
    rgb(0 0 0 / 0.684) 75%,
    rgb(0 0 0 / 0.5) 80%,
    rgb(0 0 0 / 0.316) 85%,
    rgb(0 0 0 / 0.156) 90%,
    rgb(0 0 0 / 0.043) 95%,
    transparent
  );
  -webkit-mask-image: var(--wall-fade);
  mask-image: var(--wall-fade);
}

.cover-wall-rows {
  position: absolute;
  top: 50%;
  left: 50%;
  display: grid;
  gap: var(--cover-gap);
  translate: -50% -50%;
  rotate: -8deg;
}

.cover-wall-row {
  display: flex;
  gap: var(--cover-gap);
  width: max-content;
  padding-inline-end: var(--cover-gap);
}

.wall-cover {
  position: relative;
  flex: none;
  width: var(--cover);
  aspect-ratio: 3 / 4;
}

.wall-cover > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: calc(var(--cover) * 0.05);
  background: var(--color-card);
}

@media (min-width: 62rem) {
  .cover-wall {
    --cover: 8.75rem;
    --wall-height: 34rem;
    --wall-extend: 8rem;
  }

  .wall-float {
    width: 24rem;
  }
}

@media (prefers-reduced-motion: no-preference) {
  .cover-wall-row {
    animation: wall-drift var(--drift, 140s) linear infinite;
    animation-delay: var(--drift-offset, 0s);
  }

  .cover-wall-row:nth-child(even) {
    animation-direction: reverse;
  }
}

/* A row is two identical stretches, so moving it by one stretch returns it to the same picture.
   The drift runs from a quarter-row right of centre to a quarter-row left of it: the row always
   reaches past both edges of the wall, so neither end is ever empty. */
@keyframes wall-drift {
  from {
    translate: 25% 0;
  }

  to {
    translate: -25% 0;
  }
}

/* Chapters ----------------------------------------------------------------------------------- */

.chapter {
  padding-top: var(--space-xs);
}

.chapter-discover {
  --chapter-tint: var(--color-pink-soft);
  margin-top: clamp(4rem, 10vh, 7rem);
}

.chapter-track {
  --chapter-tint: var(--color-teal-soft);
}

.chapter-friends {
  --chapter-tint: var(--color-blue-soft);
}

.chapter-history {
  --chapter-tint: var(--color-coral-soft);
}

.chapter-panel {
  margin-inline: var(--space-xs);
  padding: clamp(3rem, 8vh, 5rem) var(--page-gutter) 0;
  overflow: hidden;
  border-radius: var(--radius-xl);
  background: var(--chapter-tint);
}

.chapter-copy {
  max-width: 48rem;
  margin-inline: auto;
  text-align: center;
}

.chapter-copy .section-lede {
  max-width: 36rem;
  margin-inline: auto;
}

/* The tints are darker than the paper, so the secondary ink darkens a little to keep 4.5:1 on each. */
@media not (prefers-color-scheme: dark) {
  .chapter-copy .section-lede {
    color: hsl(40 6% 35%);
  }
}

.chapter-stage {
  margin-top: clamp(2rem, 5vh, 3rem);
}

/* A row of the app's cards beneath the phone, swiped like the app's own carousels. */
.rail {
  position: relative;
  z-index: 2;
  justify-self: stretch;
  margin: -10rem calc(-1 * var(--page-gutter)) 0;
  padding: var(--space-md) var(--page-gutter) var(--space-lg);
  display: flex;
  align-items: flex-start;
  gap: 12px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--page-gutter);
  scrollbar-width: none;
}

.rail::-webkit-scrollbar {
  display: none;
}

.rail > * {
  flex: 0 0 min(21.875rem, calc(100% - 1.75rem));
  scroll-snap-align: start;
}

.rail.is-dragging {
  scroll-snap-type: none;
  cursor: grabbing;
}

.rail + .gallery-dots {
  margin-bottom: var(--space-md);
}

/* History: two phones, cut by the foot of the panel. */
/* The import card sits over the tops of the two phones, which the panel crops at its foot. */
.history-stage {
  margin-top: clamp(2rem, 5vh, 3rem);
  display: grid;
  grid-template-columns: repeat(2, auto);
  grid-template-rows: auto 22rem;
  justify-content: center;
  column-gap: var(--space-sm);
}

.history-stage .device {
  --device-width: min(44vw, 14rem);
  grid-row: 2;
  align-self: start;
}

.history-steam {
  grid-column: 1;
}

.history-log {
  grid-column: 2;
  margin-top: 3rem;
}

/* The panel already insets it from the page, so it can take the panel's whole width. */
.history-float {
  grid-area: 1 / 1 / 2 / 3;
  justify-self: center;
  width: min(21.875rem, 100%);
  margin-bottom: -7rem;
}

@media (min-width: 62rem) {
  .chapter-panel {
    width: calc(100% - 2 * var(--page-gutter));
    max-width: var(--page-width);
    margin-inline: auto;
    padding-top: 5.5rem;
  }

  .chapter-stage {
    height: 36rem;
    align-content: start;
  }

  .rail {
    display: contents;
  }

  .rail > * {
    position: relative;
    z-index: 2;
    width: 21.875rem;
  }

  .rail > :nth-child(1) {
    grid-area: 1 / 1;
    justify-self: end;
    align-self: end;
    margin: 0 -3.5rem 0.75rem 0;
  }

  .rail > :nth-child(2) {
    grid-area: 1 / 3 / span 2;
    justify-self: start;
    margin: 2rem 0 0 -3.5rem;
  }

  .rail > :nth-child(3) {
    grid-area: 2 / 1;
    justify-self: end;
    align-self: start;
    margin: 0.75rem 1.5rem 0 0;
  }

  /* Friends and Discover: a float takes the lower left, so the rail's second and third cards share
     the right. */
  .friends-stage .rail > :nth-child(2),
  .discover-stage .rail > :nth-child(2) {
    grid-area: 1 / 3;
    align-self: end;
    margin: 0 0 0.75rem -3.5rem;
  }

  .friends-stage .rail > :nth-child(3),
  .discover-stage .rail > :nth-child(3) {
    grid-area: 2 / 3;
    justify-self: start;
    margin: 0.75rem 0 0 -1.5rem;
  }

  .history-stage {
    grid-template-columns: repeat(3, auto);
    grid-template-rows: 36rem;
    column-gap: 0;
  }

  .history-stage .device {
    --device-width: 16rem;
    grid-row: 1;
  }

  .history-log {
    grid-column: 3;
  }

  .history-float {
    grid-area: 1 / 2;
    align-self: start;
    width: 20rem;
    margin: 2rem -2.5rem 0;
  }
}

/* More: a gallery of whole phones ------------------------------------------------------------ */

.more {
  padding-block: clamp(4rem, 10vh, 7rem) clamp(3rem, 8vh, 5rem);
}

.more-intro {
  max-width: calc(38rem + 2 * var(--page-gutter));
  text-align: center;
}

.more-gallery {
  margin-top: clamp(2rem, 5vh, 3rem);
  padding: var(--space-md) max(var(--page-gutter), calc((100% - var(--page-width)) / 2)) var(--space-lg);
  display: flex;
  gap: var(--space-lg);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: max(var(--page-gutter), calc((100% - var(--page-width)) / 2));
  scrollbar-width: none;
}

.more-gallery::-webkit-scrollbar {
  display: none;
}

.more-gallery.is-dragging {
  scroll-snap-type: none;
  cursor: grabbing;
}

.more-gallery:focus-visible {
  outline-offset: -2px;
}

.more-item {
  flex: none;
  margin: 0;
  scroll-snap-align: start;
}

.more-item .device {
  --device-width: 14rem;
}

.more-item figcaption {
  max-width: 14rem;
  margin-top: var(--space-md);
  color: var(--color-ink-2);
  font-size: var(--text-sm);
  font-weight: 600;
  line-height: 1.35;
}

/* Gallery dots, added by screenshots.js where a row scrolls. */
.gallery-dots {
  display: flex;
  justify-content: center;
}

.gallery-dots[hidden] {
  display: none;
}

.gallery-dots button {
  min-width: 1.25rem;
  min-height: 2rem;
  padding: 0 var(--space-2xs);
  display: grid;
  place-items: center;
  border: 0;
  background: none;
  cursor: pointer;
}

.gallery-dots button::before {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 0.25rem;
  background: var(--color-rule-strong);
  transition:
    width var(--dur-short) var(--ease-out),
    background-color var(--dur-short) var(--ease-out);
}

.gallery-dots button[aria-current="true"]::before {
  width: 1.5rem;
  background: var(--color-ink-2);
}

/* Join --------------------------------------------------------------------------------------- */

.join {
  position: relative;
  isolation: isolate;
  margin-top: clamp(1rem, 4vh, 3rem);
  padding-block: clamp(4rem, 10vh, 7rem);
  overflow: hidden;
}

.join-wash {
  inset-block: 0;
  rotate: 180deg;
}

.join-inner {
  max-width: calc(44rem + 2 * var(--page-gutter));
  display: grid;
  justify-items: center;
  text-align: center;
}

.join-icon {
  width: 5.5rem;
  height: 5.5rem;
  margin-bottom: var(--space-lg);
  border-radius: 22.5%;
  box-shadow: var(--shadow-artwork);
}

.join-buttons {
  width: 100%;
  max-width: 32rem;
  margin-top: var(--space-xl);
  justify-content: center;
}

.join-steps {
  width: 100%;
  margin-top: var(--space-xl);
  display: grid;
  gap: var(--space-lg);
  text-align: start;
}

.join-step-group h3 {
  margin: 0 0 var(--space-xs);
  font-size: var(--text-sm);
  font-weight: 700;
}

.steps {
  margin: 0;
  padding-inline-start: 1.25rem;
  color: var(--color-ink-2);
  font-size: var(--text-sm);
  line-height: 1.5;
}

.steps li + li {
  margin-top: var(--space-2xs);
}

/* The maker's note: who builds Playthroughs, and where feedback goes. */
.join-maker {
  max-width: 30rem;
  margin: var(--space-xl) auto 0;
  color: var(--color-ink-2);
  font-size: var(--text-sm);
  font-weight: 500;
  line-height: 1.45;
  text-wrap: pretty;
}

.join-web-note {
  margin: var(--space-xl) 0 0;
  color: var(--color-ink-2);
  font-size: var(--text-xs);
}

@media (min-width: 40rem) {
  .join-steps {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-xl);
  }
}

/* The app's components, rebuilt at its own metrics (app/components/ui, features/explore) ------ */

/* LiftedCard at radii.md. */
.lifted {
  overflow: hidden;
  border-radius: 16px;
  background: var(--color-lifted);
  box-shadow: var(--shadow-lifted);
  color: var(--color-ink);
  font-family: var(--font-app);
  line-height: normal;
  text-align: start;
}

.lifted p {
  margin: 0;
}

/* RatingBadge: an ink numeral on the squircle, 0.6 of its height. */
.rating {
  --size: 27px;
  --numeral: 15px;
  flex: none;
  display: inline-block;
  width: var(--size);
  height: var(--size);
  background: var(--rating-fill);
  color: var(--color-on-rating);
  font: 800 var(--numeral) / var(--size) var(--font-app);
  letter-spacing: 0;
  text-align: center;
  -webkit-mask: url("./assets/squircle.svg") center / 100% 100% no-repeat;
  mask: url("./assets/squircle.svg") center / 100% 100% no-repeat;
}

.rating-lg {
  --size: 29px;
  --numeral: 17px;
}

[data-rating="1"] { --rating-fill: var(--color-rating-1); }
[data-rating="2"] { --rating-fill: var(--color-rating-2); }
[data-rating="3"] { --rating-fill: var(--color-rating-3); }
[data-rating="4"] { --rating-fill: var(--color-rating-4); }
[data-rating="5"] { --rating-fill: var(--color-rating-5); }
[data-rating="6"] { --rating-fill: var(--color-rating-6); }
[data-rating="7"] { --rating-fill: var(--color-rating-7); }
[data-rating="8"] { --rating-fill: var(--color-rating-8); }
[data-rating="9"] { --rating-fill: var(--color-rating-9); }
[data-rating="10"] { --rating-fill: var(--color-rating-10); }

/* Avatar: the member's photo, on the quiet fill while it loads. */
.avatar {
  flex: none;
  display: inline-grid;
  place-items: center;
  overflow: hidden;
  border-radius: 50%;
  background: var(--color-surface);
  color: var(--color-ink-2);
  font-weight: 600;
  letter-spacing: 0.2px;
}

.avatar > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.avatar-xs {
  width: 20px;
  height: 20px;
  font-size: 9px;
  line-height: 11px;
}

/* GameCover: 3:4 with its hairline outline, radii.xxs at the xs size. */
.cover,
.list-strip img,
.stack-cover > img {
  aspect-ratio: 3 / 4;
  object-fit: cover;
  background: var(--color-card);
  outline: 1px solid var(--color-hairline);
  outline-offset: -1px;
}

.cover-xs {
  width: 32px;
  height: auto;
  flex: none;
  border-radius: 3px;
}

.cover-sm {
  width: 54px;
  height: auto;
  flex: none;
  border-radius: 6px;
}

/* TrendingReviewCards: the score leads a line of title and reviewer, then the cover with the review
   beside it. */
.review-card {
  padding: 16px;
}

.review-head {
  display: flex;
  align-items: center;
  gap: 12px;
}

.review-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  font-size: 15px;
  font-weight: 600;
  line-height: 20px;
  letter-spacing: 0.2px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.review-year {
  color: var(--color-ink-2);
  font-size: 13px;
  font-weight: 300;
}

/* A long name gives way before the title does. */
.reviewer {
  max-width: 140px;
  display: flex;
  align-items: center;
  gap: 4px;
}

.reviewer-name {
  min-width: 0;
  overflow: hidden;
  font-size: 13px;
  font-weight: 600;
  line-height: 17px;
  letter-spacing: 0.2px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.review-main {
  margin-top: 12px;
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

/* Cut after five lines and faded into the card over its lower half, as ExpandableView does. */
.review-card .review-body {
  flex: 1;
  min-width: 0;
  max-height: 85px;
  overflow: hidden;
  color: var(--color-ink-2);
  font-size: 13px;
  line-height: 17px;
  -webkit-mask-image: linear-gradient(#000 42.5px, transparent 85px);
  mask-image: linear-gradient(#000 42.5px, transparent 85px);
}

/*
 * TrendingListCards: eight small covers overlapping to fill the width, the leftmost on top. Each
 * cover after the first tucks under its neighbour by (8 × 54 − width) / 7, as the app measures it;
 * a percentage margin resolves against the strip's width.
 */
.list-strip {
  display: flex;
  overflow: hidden;
}

.list-strip img {
  position: relative;
  flex: none;
  width: 54px;
  max-width: none;
  height: 72px;
  object-fit: cover;
  border-radius: 6px;
  background: var(--color-card);
}

.list-strip img + img {
  margin-left: min(0px, calc((100% - 432px) / 7));
}

.list-strip img:nth-child(1) { z-index: 8; }
.list-strip img:nth-child(2) { z-index: 7; }
.list-strip img:nth-child(3) { z-index: 6; }
.list-strip img:nth-child(4) { z-index: 5; }
.list-strip img:nth-child(5) { z-index: 4; }
.list-strip img:nth-child(6) { z-index: 3; }
.list-strip img:nth-child(7) { z-index: 2; }
.list-strip img:nth-child(8) { z-index: 1; }

.list-caption {
  padding: 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.list-card .list-title {
  flex: 1;
  display: -webkit-box;
  overflow: hidden;
  font-size: 15px;
  font-weight: 600;
  line-height: 20px;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

.list-author {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  font-weight: 600;
  line-height: 17px;
  letter-spacing: 0.2px;
  white-space: nowrap;
}

/* Text variants: caption 13/17 light, and the tabler glyphs at iconSizes xs (14) and sm (18). */
.caption {
  color: var(--color-ink-2);
  font-size: 13px;
  font-weight: 300;
  line-height: 17px;
  letter-spacing: 0.2px;
}

.icon-xs,
.icon-sm {
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.icon-xs {
  width: 14px;
  height: 14px;
}

.icon-sm {
  width: 18px;
  height: 18px;
}

/* A diary or journal month: labelBody on the fill, inset by Container. */
.month-heading {
  padding: 4px 20px;
  background: var(--color-surface);
  font-size: 15px;
  font-weight: 600;
  line-height: 20px;
  letter-spacing: 0.2px;
}

.diary-rows,
.journal-rows {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The day a row belongs to: weekday over the date, in a bordered tile as tall as an xs cover. */
.day-tile {
  flex: none;
  width: 42.667px;
  height: 42.667px;
  display: grid;
  place-content: center;
  justify-items: center;
  border: 1px solid var(--color-rule);
  border-radius: 3px;
  color: var(--color-ink-2);
}

.day-weekday {
  font-size: 11px;
  font-weight: 500;
  line-height: 13px;
}

.day-number {
  font-size: 20px;
  font-weight: 700;
  line-height: 22px;
  letter-spacing: -0.3px;
}

/* DiaryEntryRow: tile, cover, title, then the rating inline with the platform and status. */
.diary-row {
  position: relative;
  padding: 8px 12px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.diary-row + .diary-row::before {
  content: "";
  position: absolute;
  top: 0;
  left: 102.667px;
  right: 0;
  border-top: var(--hairline) solid var(--color-hairline);
}

.diary-row .cover-xs {
  height: 42.667px;
}

.diary-body {
  flex: 1;
  min-width: 0;
  padding-right: 8px;
}

.diary-title {
  font-size: 15px;
  font-weight: 600;
  line-height: 20px;
  letter-spacing: 0.2px;
}

.diary-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--color-ink-2);
}

.diary-rating-and-text {
  display: flex;
  align-items: center;
  flex-shrink: 1;
  min-width: 0;
}

.diary-meta .rating {
  --size: 21px;
  --numeral: 13px;
}

.diary-meta .caption {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Preserve the spaces around the first dot, matching the app's inline rating and text. */
  white-space: pre;
}

/* JournalEntryRow: tile, then the note or milestone with the session's playtime beside it. */
.journal-rows {
  padding: 8px 20px 4px;
}

.journal-row {
  padding-block: 8px;
  display: flex;
  align-items: flex-start;
  column-gap: 16px;
}

.journal-row + .journal-row {
  position: relative;
  margin-top: 12px;
}

.journal-row + .journal-row::before {
  content: "";
  position: absolute;
  top: 0;
  left: 58.667px;
  right: 0;
  border-top: var(--hairline) solid var(--color-hairline);
}

.journal-body {
  flex: 1;
  min-width: 0;
  min-height: 42.667px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.journal-top {
  min-height: 22px;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  column-gap: 12px;
}

.journal-top-milestones {
  padding-top: 4px;
}

.journal-note {
  flex: 1;
  font-size: 15px;
  line-height: 20px;
}

.journal-note-below {
  margin-top: 8px !important;
}

.journal-playtime {
  min-height: 22px;
  display: flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
}

.milestone {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 15px;
  font-weight: 600;
  line-height: 20px;
  letter-spacing: 0.2px;
}

.milestone-playing {
  color: var(--color-activity-playing);
}

/* CoverStackCarousel: the front cover at the leading edge, the rest waiting beneath it to the
   right and those passed tucked away to the left. motion.js places each cover and caption. */
.stack-card {
  --stack-cover: 96px;
  padding: 16px 0;
}

.cover-stack {
  position: relative;
  height: calc(var(--stack-cover) * 4 / 3);
  /* Horizontal drags move the stack; vertical ones still scroll the page. */
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}

.cover-stack.is-dragging .stack-cover {
  cursor: grabbing;
}

.stack-cover {
  position: absolute;
  top: 0;
  left: 20px;
  width: var(--stack-cover);
  aspect-ratio: 3 / 4;
  overflow: hidden;
  border-radius: 5px;
  box-shadow: var(--shadow-artwork);
  cursor: pointer;
}

.stack-cover > img {
  width: 100%;
  height: 100%;
  border-radius: 5px;
}

/* The shadow of the cover above, falling from its edge and easing out to nothing. */
.stack-edge {
  position: absolute;
  inset-block: 0;
  left: calc(var(--stack-overlap, 27px) - 5px);
  width: 21px;
  background: linear-gradient(
    to right,
    rgb(0 0 0 / 0.25) 23.8%,
    rgb(0 0 0 / 0.191) 33.3%,
    rgb(0 0 0 / 0.141) 42.9%,
    rgb(0 0 0 / 0.098) 52.4%,
    rgb(0 0 0 / 0.0625) 61.9%,
    rgb(0 0 0 / 0.035) 71.4%,
    rgb(0 0 0 / 0.016) 81%,
    rgb(0 0 0 / 0.004) 90.5%,
    transparent
  );
  opacity: 0;
  pointer-events: none;
}

.stack-shade {
  position: absolute;
  inset: 0;
  background: #000;
  opacity: 0;
  pointer-events: none;
}

.stack-context {
  position: relative;
  margin-top: 4px;
  display: grid;
}

.stack-caption {
  grid-area: 1 / 1;
  min-width: 0;
  padding-inline: 21px 20px;
}

.stack-caption + .stack-caption {
  opacity: 0;
}

.stack-title,
.stack-details {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.stack-title {
  font-size: 15px;
  font-weight: 600;
  line-height: 20px;
  letter-spacing: 0.2px;
}

/* FriendActivityStack (features/home): the cover stack, with the friend and what they did in a
   pill on each cover waiting behind the front one, and who did what beneath the front cover. */
.friend-pill {
  position: absolute;
  top: 3px;
  right: 3px;
  display: flex;
  align-items: center;
  gap: 3px;
  height: 21px;
  padding: 0 6px 0 2px;
  overflow: hidden;
  border-radius: 999px;
  background: color-mix(in srgb, var(--color-surface) 90%, transparent);
  color: var(--color-ink);
  opacity: 0;
  pointer-events: none;
}

.friend-pill .avatar {
  width: 17px;
  height: 17px;
  font-size: 7px;
  line-height: 9px;
  background: var(--color-lifted);
}

.friend-pill svg {
  flex: none;
  width: 12px;
  height: 12px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.icon-filled,
.friend-pill .icon-filled {
  fill: currentColor;
  stroke: none;
}

.friend-caption {
  display: flex;
  align-items: center;
  gap: 4px;
}

.friend-caption > .avatar {
  width: 40px;
  height: 40px;
  font-size: 15px;
  line-height: 18px;
}

.friend-caption-text {
  flex: 1;
  min-width: 0;
}

.friend-headline {
  overflow: hidden;
  color: var(--color-ink-2);
  font-size: 15px;
  line-height: 20px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.friend-headline strong {
  color: var(--color-ink);
  font-weight: 600;
  letter-spacing: 0.2px;
}

.friend-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 20px;
  color: var(--color-ink-2);
}

.friend-meta .caption {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rating-xs {
  --size: 20px;
  --numeral: 12px;
}

/* CurrentlyPlayingCard (features/playthrough/common), at the 1.66 shape it takes with its button.
   The artwork sits under ContrastScrim: a light blur and a black scrim, both easing in along the
   same sine curve over the fade, then solid behind the title's foot, the line and the button. */
.playing-card {
  /* One line of title; the solid band runs from its foot through the line and the button. */
  --title: 26px;
  --solid: 71px;
  /* UIBlurEffect.regular twelve per cent of the way in, calibrated against the simulator. */
  --playing-blur-radius: 7px;
  --playing-blur-saturation: 2.2;
  --playing-blur-tint: rgb(255 255 255 / 0.04);
  container-type: inline-size;
  position: relative;
  overflow: hidden;
  aspect-ratio: 1.66;
  border-radius: 16px;
  background: #000;
  box-shadow: var(--shadow-artwork);
  color: #fff;
  font-family: var(--font-app);
  line-height: normal;
}

.playing-card p {
  margin: 0;
}

/* A short card has no room for the full fade, so it stops short of the line at the top. */
.playing-card > * {
  --fade: calc(min(80px, 100cqw / 1.66 - 52px - var(--solid) - var(--title)) + var(--title));
}

/* Wider than the card by the carousel's 24px drift, as on Home, so the artwork is framed the same. */
.playing-art {
  position: absolute;
  inset: 0 -24px;
  width: calc(100% + 48px);
  height: 100%;
  object-fit: cover;
}

.playing-blur,
.playing-scrim {
  position: absolute;
  inset: auto 0 0;
  height: calc(var(--fade) + var(--solid));
  border-radius: 0 0 16px 16px;
}

/* UIBlurEffect's regular style at an intensity of 12, darkened by 0.1 black. */
.playing-blur {
  --mask: linear-gradient(
    rgb(0 0 0 / 0) calc(var(--fade) * 0),
    rgb(0 0 0 / 0.0096) calc(var(--fade) * 0.0625),
    rgb(0 0 0 / 0.0381) calc(var(--fade) * 0.125),
    rgb(0 0 0 / 0.0843) calc(var(--fade) * 0.1875),
    rgb(0 0 0 / 0.1464) calc(var(--fade) * 0.25),
    rgb(0 0 0 / 0.2222) calc(var(--fade) * 0.3125),
    rgb(0 0 0 / 0.3087) calc(var(--fade) * 0.375),
    rgb(0 0 0 / 0.4025) calc(var(--fade) * 0.4375),
    rgb(0 0 0 / 0.5) calc(var(--fade) * 0.5),
    rgb(0 0 0 / 0.5975) calc(var(--fade) * 0.5625),
    rgb(0 0 0 / 0.6913) calc(var(--fade) * 0.625),
    rgb(0 0 0 / 0.7778) calc(var(--fade) * 0.6875),
    rgb(0 0 0 / 0.8536) calc(var(--fade) * 0.75),
    rgb(0 0 0 / 0.9157) calc(var(--fade) * 0.8125),
    rgb(0 0 0 / 0.9619) calc(var(--fade) * 0.875),
    rgb(0 0 0 / 0.9904) calc(var(--fade) * 0.9375),
    #000 var(--fade)
  );
  background: linear-gradient(rgb(0 0 0 / 0.1) 0 0), var(--playing-blur-tint);
  -webkit-backdrop-filter: blur(var(--playing-blur-radius)) saturate(var(--playing-blur-saturation));
  backdrop-filter: blur(var(--playing-blur-radius)) saturate(var(--playing-blur-saturation));
  -webkit-mask: var(--mask);
  mask: var(--mask);
}

.playing-scrim {
  background: linear-gradient(
    rgb(0 0 0 / 0) calc(var(--fade) * 0),
    rgb(0 0 0 / 0.0058) calc(var(--fade) * 0.0625),
    rgb(0 0 0 / 0.0228) calc(var(--fade) * 0.125),
    rgb(0 0 0 / 0.0506) calc(var(--fade) * 0.1875),
    rgb(0 0 0 / 0.0879) calc(var(--fade) * 0.25),
    rgb(0 0 0 / 0.1333) calc(var(--fade) * 0.3125),
    rgb(0 0 0 / 0.1852) calc(var(--fade) * 0.375),
    rgb(0 0 0 / 0.2415) calc(var(--fade) * 0.4375),
    rgb(0 0 0 / 0.3) calc(var(--fade) * 0.5),
    rgb(0 0 0 / 0.3585) calc(var(--fade) * 0.5625),
    rgb(0 0 0 / 0.4148) calc(var(--fade) * 0.625),
    rgb(0 0 0 / 0.4667) calc(var(--fade) * 0.6875),
    rgb(0 0 0 / 0.5121) calc(var(--fade) * 0.75),
    rgb(0 0 0 / 0.5494) calc(var(--fade) * 0.8125),
    rgb(0 0 0 / 0.5772) calc(var(--fade) * 0.875),
    rgb(0 0 0 / 0.5942) calc(var(--fade) * 0.9375),
    rgb(0 0 0 / 0.6) var(--fade)
  );
}

/* The line at the top sits on a lighter shade, solid behind the padding and easing out below. */
.playing-scrim-top {
  position: absolute;
  inset: 0 0 auto;
  height: 52px;
  background: linear-gradient(
    to top,
    rgb(0 0 0 / 0) 0,
    rgb(0 0 0 / 0.0038) 2.5px,
    rgb(0 0 0 / 0.0152) 5px,
    rgb(0 0 0 / 0.0337) 7.5px,
    rgb(0 0 0 / 0.0586) 10px,
    rgb(0 0 0 / 0.0889) 12.5px,
    rgb(0 0 0 / 0.1235) 15px,
    rgb(0 0 0 / 0.161) 17.5px,
    rgb(0 0 0 / 0.2) 20px,
    rgb(0 0 0 / 0.239) 22.5px,
    rgb(0 0 0 / 0.2765) 25px,
    rgb(0 0 0 / 0.3111) 27.5px,
    rgb(0 0 0 / 0.3414) 30px,
    rgb(0 0 0 / 0.3663) 32.5px,
    rgb(0 0 0 / 0.3848) 35px,
    rgb(0 0 0 / 0.3962) 37.5px,
    rgb(0 0 0 / 0.4) 40px
  );
}

.playing-status,
.playing-meta {
  color: rgb(255 255 255 / 0.85);
  font-size: 13px;
  line-height: 17px;
}

.playing-status {
  position: absolute;
  inset: 12px 12px auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.playing-details {
  position: absolute;
  inset: auto 78px 48px 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.playing-title {
  font-size: 26px;
  font-weight: 700;
  line-height: 26px;
  letter-spacing: -0.3px;
}

.playing-year {
  color: rgb(255 255 255 / 0.85);
  font-size: 15px;
  font-weight: 300;
  letter-spacing: 0;
}

.playing-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  overflow: hidden;
  white-space: nowrap;
}

.playing-dot {
  opacity: 0.55;
}

.playing-platform {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

/* PlatformArtwork: the hardware drawing, with its silhouette ring in the overlay text colour. */
.platform-art {
  position: relative;
  flex: none;
  width: 27px;
  height: 18px;
  background: url("./assets/platforms/steam-deck.png") center / contain no-repeat;
}

.platform-art::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: scale(1.0625);
  background: #fff;
  -webkit-mask: url("./assets/platforms/steam-deck-outline.png") center / contain no-repeat;
  mask: url("./assets/platforms/steam-deck-outline.png") center / contain no-repeat;
}

.playing-action {
  position: absolute;
  bottom: 12px;
  left: 12px;
  display: flex;
  align-items: center;
  gap: 6px;
  height: 28px;
  padding: 0 12px 0 10px;
  border-radius: 999px;
  background: rgb(255 255 255 / 0.18);
  font-size: 12px;
  font-weight: 500;
  white-space: nowrap;
}

.playing-action svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.playing-cover {
  position: absolute;
  right: 12px;
  bottom: 12px;
  width: 54px;
  height: 72px;
  object-fit: cover;
  border: var(--hairline) solid var(--color-ink-2);
  border-radius: 6px;
}

@media (prefers-color-scheme: dark) {
  .playing-card {
    --playing-blur-tint: rgb(0 0 0 / 0.04);
  }
}

/* NumberPicker: a wrapping wheel of None and 1 to 10, five items to its width. The centre item
   stands at full size; its neighbours shrink to 0.85, their badges dim to 0.2 and their numerals
   grey, fading out towards the far side of the wheel. motion.js sets each item's slot and styling
   as the wheel turns; without it the wheel rests on 9. */
.picker-card {
  position: relative;
  z-index: 1;
  width: calc(100% - 2rem);
  /* Apart from the diary: in dark mode neither casts a shadow, so overlapping cards would merge. */
  margin: 12px 0 0 auto;
  padding: 20px 0 18px;
}

.number-picker {
  position: relative;
  height: 60px;
  border-radius: 12px;
  cursor: grab;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}

.number-picker.is-dragging {
  cursor: grabbing;
}

/* Inside the card's edges, which clip the wheel. */
.number-picker:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: -2px;
}

.picker-item {
  --scale: 0.85;
  --text: 0.75;
  --badge: 0.2;
  --selected: 0%;
  position: absolute;
  top: 0;
  left: 40%;
  width: 20%;
  height: 60px;
  display: grid;
  place-items: center;
  translate: calc(var(--slot) * 100%) 0;
  scale: var(--scale);
}

.picker-item.is-centre {
  --scale: 1;
  --text: 1;
  --badge: 1;
  --selected: 100%;
}

.picker-badge {
  position: absolute;
  inset: 0;
  width: 60px;
  height: 60px;
  margin: auto;
  background: var(--rating-fill);
  opacity: var(--badge);
  -webkit-mask: url("./assets/squircle.svg") center / 100% 100% no-repeat;
  mask: url("./assets/squircle.svg") center / 100% 100% no-repeat;
}

.picker-numeral {
  position: relative;
  color: color-mix(in srgb, var(--color-on-rating) var(--selected), var(--color-ink-2));
  opacity: var(--text);
  font: 800 36px / 60px var(--font-app);
  letter-spacing: 0;
}

/* None has no badge and never takes the selected colour. */
.picker-none .picker-numeral {
  color: var(--color-ink-2);
  font-weight: 300;
}

.picker-card .picker-label {
  margin-top: 16px;
  font-size: 13px;
  font-weight: 600;
  line-height: 17px;
  letter-spacing: 0.2px;
  text-align: center;
}

@media (min-width: 62rem) {
  .picker-card {
    width: 20rem;
    margin-right: -6rem;
  }
}

/* GameRatingsSummary: the distribution beside the average and its count, under the game it belongs
   to. Each column is the chart's full height and clips its bar, which moves rather than resizes,
   as the app's GrowingBar does, so a bar keeps its rounded top as it rises and falls. */
.ratings-card {
  padding: 16px;
}

.ratings-head {
  display: flex;
  align-items: center;
  gap: 12px;
  transition: opacity var(--dur-short) var(--ease-out);
}

.ratings-head.is-changing {
  opacity: 0;
}

.ratings-summary {
  margin-top: 16px;
  display: flex;
  align-items: center;
  gap: 12px;
}

.ratings-chart {
  --chart-height: 42px;
  --grow-easing: cubic-bezier(0.25, 1, 0.5, 1);
  flex: 1;
  min-width: 0;
  height: var(--chart-height);
  display: flex;
  align-items: flex-end;
}

.ratings-axis {
  font-size: 13px;
  font-weight: 600;
  line-height: 17px;
  letter-spacing: 0.2px;
}

.ratings-axis:first-child {
  padding-right: 8px;
}

.ratings-axis:last-child {
  padding-left: 8px;
}

.ratings-column {
  flex: 1;
  align-self: stretch;
  padding-inline: 1.5px;
  overflow: hidden;
}

.ratings-bar {
  /* The app's bar: its share of the chart's height, at least 3px once anyone has given that rating
     and never under 5%. The min() switches the 3px floor off for an empty column. */
  --bar: max(
    calc(var(--v) * var(--chart-height)),
    calc(0.05 * var(--chart-height)),
    min(3px, calc(var(--v) * 1000000px))
  );
  display: block;
  height: 100%;
  border-radius: 3px 3px 0 0;
  background: var(--color-ink);
  opacity: calc(0.4 + var(--v) * 0.6);
  translate: 0 calc(var(--chart-height) - var(--bar));
}

/* The first growth is the app's; later moves between games take longer, a column at a time. */
.ratings-chart[data-motion="grow"] .ratings-bar {
  transition:
    translate 320ms var(--grow-easing) 80ms,
    opacity 320ms var(--grow-easing) 80ms;
}

.ratings-chart[data-motion="move"] .ratings-bar {
  transition:
    translate 640ms var(--grow-easing) calc(var(--i) * 40ms),
    opacity 640ms var(--grow-easing) calc(var(--i) * 40ms);
}

.ratings-total {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.ratings-total .rating {
  margin-top: 8px;
}

.rating-xxl {
  --size: 46px;
  --numeral: 26px;
}

/* An average's label is a character wider, so its badge widens by 1.4. */
.rating-average {
  width: calc(var(--size) * 1.4);
  -webkit-mask-image: url("./assets/squircle-wide.svg");
  mask-image: url("./assets/squircle-wide.svg");
  transition: background-color 640ms var(--ease-out);
}

.ratings-count {
  color: var(--color-ink-2);
  font-size: 9px;
  font-weight: 300;
  line-height: 17px;
  letter-spacing: 0.2px;
  white-space: nowrap;
}

/* MigrationProgressScreen: the journey, the heading and stage, then the counters so far. */
.import-card {
  padding: 20px;
}

/* ImportJourney: the source's tile, a path of brand-coloured dots, then the Playthroughs mark. */
.import-journey {
  display: flex;
  align-items: center;
}

/* ImportSourceTile: Steam's hardware on a fill of its case colour (platformFillEndColor below). */
.import-source,
.import-destination {
  position: relative;
  flex: none;
  display: grid;
  place-items: center;
  width: 72px;
  height: 72px;
  border-radius: 26%;
}

.import-source {
  overflow: hidden;
  background: linear-gradient(#2b2f36, #212938);
}

.import-source-art {
  position: relative;
  width: 56px;
  height: 37.333px;
  background: url("./assets/platforms/steam.png") center / contain no-repeat;
}

.import-source-art::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: scale(1.0625);
  background: #fff;
  -webkit-mask: url("./assets/platforms/steam-outline.png") center / contain no-repeat;
  mask: url("./assets/platforms/steam-outline.png") center / contain no-repeat;
}

.import-path {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: space-evenly;
  padding-inline: 8px;
}

.import-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-teal);
  opacity: 0.7;
}

.import-dot:nth-child(2) {
  background: var(--color-blue);
}

.import-dot:nth-child(3) {
  background: var(--color-coral);
}

.import-dot:nth-child(4) {
  background: var(--color-pink);
}

.import-chevron {
  width: 18px;
  height: 18px;
  color: var(--color-ink-2);
}

.import-destination {
  background: var(--color-card);
}

.import-mark img,
.import-spinner {
  display: block;
  width: 45px;
  height: 45px;
}

/* GameControllerSpinner takes the mark's place while the import works. */
.import-spinner,
.is-working .import-mark {
  display: none;
}

.is-working .import-spinner {
  display: block;
}

.import-spinner-edge {
  display: none;
  fill: #fff;
}

.import-spinner-pad {
  transform-box: view-box;
  transform-origin: 50% 50%;
  animation: import-turn 6000ms infinite;
}

/* Clockwise from the top, each button sinks over 70ms and springs back over 200ms, 110ms after
   the one before. */
.import-button {
  --face: var(--color-teal);
  --held: var(--color-teal-pressed);
  fill: var(--face);
  transform-box: fill-box;
  transform-origin: center;
  animation: import-press 1500ms linear infinite;
  animation-delay: calc(var(--press) * 110ms);
}

.import-button-blue {
  --face: var(--color-blue);
  --held: var(--color-blue-pressed);
}

.import-button-pink {
  --face: var(--color-pink);
  --held: var(--color-pink-pressed);
}

.import-button-coral {
  --face: var(--color-coral);
  --held: var(--color-coral-pressed);
}

@keyframes import-press {
  0% {
    fill: var(--face);
    scale: 1;
  }

  4.6667% {
    fill: var(--held);
    scale: 0.88;
  }

  18%,
  100% {
    fill: var(--face);
    scale: 1;
  }
}

/* After each 1.5 second combo the pad turns a quarter, 8 degrees past it and back. Four loops
   bring every button home. */
@keyframes import-turn {
  0% {
    rotate: 0deg;
    animation-timing-function: linear;
  }

  15.8333% {
    rotate: 0deg;
    animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);
  }

  22.5% {
    rotate: 98deg;
    animation-timing-function: cubic-bezier(0.5, 1, 0.89, 1);
  }

  25% {
    rotate: 90deg;
    animation-timing-function: linear;
  }

  40.8333% {
    rotate: 90deg;
    animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);
  }

  47.5% {
    rotate: 188deg;
    animation-timing-function: cubic-bezier(0.5, 1, 0.89, 1);
  }

  50% {
    rotate: 180deg;
    animation-timing-function: linear;
  }

  65.8333% {
    rotate: 180deg;
    animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);
  }

  72.5% {
    rotate: 278deg;
    animation-timing-function: cubic-bezier(0.5, 1, 0.89, 1);
  }

  75% {
    rotate: 270deg;
    animation-timing-function: linear;
  }

  90.8333% {
    rotate: 270deg;
    animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);
  }

  97.5% {
    rotate: 368deg;
    animation-timing-function: cubic-bezier(0.5, 1, 0.89, 1);
  }

  100% {
    rotate: 360deg;
  }
}

/* Once everything has arrived the mark wears a check, ringed in the card's colour. */
.import-arrived {
  position: absolute;
  right: -6px;
  bottom: -6px;
  display: none;
  padding: 2px;
  border-radius: 28px;
  background: var(--color-lifted);
  color: var(--color-success);
}

.import-arrived svg {
  display: block;
  width: 28px;
  height: 28px;
}

.is-complete .import-arrived {
  display: block;
}

.import-head {
  margin-top: 24px;
  transition: opacity 220ms var(--ease-out);
}

.import-head.is-changing {
  opacity: 0;
}

/* Text variant h1, with room for two lines so the card keeps its height between stages. */
.import-card .import-title {
  min-height: 62px;
  font-size: 26px;
  font-weight: 700;
  line-height: 31px;
  letter-spacing: -0.3px;
}

/* ImportStagePill: hidden, not removed, once the import stops working. */
.import-card .import-stage {
  display: flex;
  align-items: center;
  gap: 8px;
  width: fit-content;
  margin-top: 12px;
  padding: 8px 12px;
  border-radius: 999px;
  background: var(--color-card);
  font-size: 13px;
  font-weight: 600;
  line-height: 17px;
  letter-spacing: 0.2px;
}

.is-complete .import-stage {
  visibility: hidden;
}

.import-stage-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-teal);
}

.import-so-far {
  margin-top: 32px;
}

.import-card .import-label {
  color: var(--color-ink-2);
  font-size: 13px;
  font-weight: 600;
  line-height: 17px;
  letter-spacing: 0.2px;
}

/* ImportCounterGrid: rows of two, an odd tile out keeping its half-width. */
.import-counters {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  margin-top: 12px;
}

.import-counter {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 12px 16px;
  border-radius: 12px;
  background: var(--color-card);
}

.import-counter-value {
  font-size: 20px;
  font-weight: 700;
  line-height: 24px;
  letter-spacing: -0.3px;
  font-variant-numeric: tabular-nums;
}

.import-counter-value.is-empty {
  color: var(--color-ink-2);
}

.import-counter-label {
  color: var(--color-ink-2);
  font-size: 13px;
  font-weight: 300;
  line-height: 17px;
  letter-spacing: 0.2px;
}

@media (prefers-color-scheme: dark) {
  .import-spinner-edge {
    display: inline;
  }
}

@media (prefers-reduced-motion: reduce) {
  .is-working .import-spinner {
    display: none;
  }

  .is-working .import-mark {
    display: block;
  }
}

/* Device frames ----------------------------------------------------------------------------- */

.device {
  --device-width: 18rem;
  position: relative;
  display: block;
  flex: none;
  width: var(--device-width);
  /* Body ratio 149.6 / 71.5; the screenshot keeps Apple's 1206 / 2622 display ratio. */
  padding: calc(var(--device-width) * 0.024313) calc(var(--device-width) * 0.03);
  border-radius: calc(var(--device-width) * 0.2);
  box-shadow: var(--shadow-device);
  text-decoration: none;
}

/* Vector silhouettes avoid the abrupt curvature of circular CSS border-radius corners. */
.device::before,
.device::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  -webkit-mask: url("./assets/iphone-17-body-mask.svg") center / 100% 100% no-repeat;
  mask: url("./assets/iphone-17-body-mask.svg") center / 100% 100% no-repeat;
}

.device::before {
  background: var(--color-device-edge);
}

.device::after {
  inset: calc(var(--device-width) * 0.002);
  background: var(--color-device);
}

.device-screen {
  position: relative;
  z-index: 1;
  aspect-ratio: 1206 / 2622;
  overflow: hidden;
  background: var(--color-card);
  -webkit-mask: url("./assets/iphone-17-screen-mask.svg") center / 100% 100% no-repeat;
  mask: url("./assets/iphone-17-screen-mask.svg") center / 100% 100% no-repeat;
}

/* Android captures retain their own proportions and rounded display, without the iPhone mask. */
.device-android {
  border-radius: calc(var(--device-width) * 0.1);
}

.device-android::before,
.device-android::after {
  -webkit-mask: none;
  mask: none;
  border-radius: inherit;
}

.device-android .device-screen {
  aspect-ratio: 1080 / 2400;
  border-radius: calc(var(--device-width) * 0.075);
  -webkit-mask: none;
  mask: none;
}

.device-android .device-screen::after {
  content: "";
  position: absolute;
  top: calc(var(--device-width) * 0.016);
  left: 50%;
  translate: -50% 0;
  width: calc(var(--device-width) * 0.028);
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--color-device);
  pointer-events: none;
}

.device-screen > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
}

a.device {
  transition: scale var(--dur-short) var(--ease-out);
}

a.device:active {
  scale: 0.99;
}

/* Native dialog keeps the page and gallery positions while an original image is open. */
.screenshot-viewer {
  width: min(calc(100% - 2 * var(--space-sm)), 26rem);
  height: min(calc(100dvh - 2 * var(--space-sm)), 56rem);
  max-width: 100%;
  max-height: calc(100dvh - 2 * var(--space-sm));
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: #fff;
  color-scheme: dark;
  overflow: auto;
  overscroll-behavior: contain;
  opacity: 0;
  transform: translateY(0.75rem) scale(0.98);
}

.screenshot-viewer[open] {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
}

.screenshot-viewer.is-visible {
  opacity: 1;
  transform: none;
}

.screenshot-viewer:not([open]) {
  display: none;
}

.screenshot-viewer::backdrop {
  background: hsl(0 0% 0% / 0);
}

.screenshot-viewer.is-visible::backdrop {
  background: hsl(0 0% 0% / 0.92);
}

.screenshot-viewer-header {
  position: sticky;
  top: 0;
  z-index: 1;
  padding-block: max(var(--space-sm), env(safe-area-inset-top)) var(--space-sm);
  padding-inline: max(var(--space-md), env(safe-area-inset-left))
    max(var(--space-md), env(safe-area-inset-right));
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  background: hsl(0 0% 8% / 0.95);
  border-radius: var(--radius-md);
}

.screenshot-viewer-header h2 {
  margin: 0;
  font-size: var(--text-md);
}

.screenshot-viewer-header form {
  margin: 0;
}

.screenshot-viewer-header button {
  border: var(--rule-thin) solid hsl(0 0% 100% / 0.25);
  background: hsl(0 0% 100% / 0.12);
  color: #fff;
  font-family: inherit;
  cursor: pointer;
}

.screenshot-viewer-header button:hover {
  background: hsl(0 0% 100% / 0.22);
}

.screenshot-viewer-header button:focus-visible {
  outline-color: #fff;
}

.screenshot-viewer-content {
  min-height: 0;
  container-type: size;
  display: grid;
  place-items: center;
  padding: var(--space-md);
  padding-bottom: max(var(--space-md), env(safe-area-inset-bottom));
}

.screenshot-viewer-content .device {
  /* The iPhone 17 body is 149.6 / 71.5 times as tall as its width. */
  --device-width: min(100cqi, calc(100cqb / 2.092308), calc(48rem / 2.092308));
}

.screenshot-viewer-content .device-android {
  --device-width: min(100cqi, calc(100cqb / 2.137515), calc(48rem / 2.137515));
}

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

/* motion.js reveals content once as it scrolls into view; without it, everything is visible. */
.home .reveal {
  animation: none;
}

@media (prefers-reduced-motion: no-preference) {
  .home .hero-title .rise {
    animation-duration: var(--dur-reveal);
  }

  .home .hero-title .rise:nth-child(2) {
    --delay: 220ms;
  }

  .home .hero-title .rise:nth-child(3) {
    --delay: 440ms;
  }

  .home .hero-actions.rise {
    --delay: 640ms;
  }

  .home .hero-stage.rise {
    animation-duration: 1400ms;
  }

  .js-reveal .reveal.is-pending {
    opacity: 0;
    translate: 0 1.5rem;
  }

  .js-reveal .reveal.is-revealed {
    transition:
      opacity var(--dur-reveal) var(--ease-reveal),
      translate var(--dur-reveal) var(--ease-reveal);
  }

  .screenshot-viewer {
    transition:
      opacity var(--dur-short) var(--ease-out),
      transform var(--dur-short) var(--ease-out);
  }

  .screenshot-viewer::backdrop {
    transition: background-color var(--dur-short) var(--ease-out);
  }

  /* The chapters' phones rise a little more slowly than the page scrolls past them. */
  @supports (animation-timeline: view()) {
    .chapter-stage .stage-phone,
    .history-stage .device {
      animation: phone-depth linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 50%;
    }
  }
}

@keyframes phone-depth {
  from {
    translate: 0 3rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .gallery-dots button::before,
  a.device {
    transition: none;
  }
}

/* Footer: the wordmark across the page's width, above the legal links. The join section just above
   already carries the tagline, so the home page leaves it out. */
/* The logo's drawing is padded by 24 of its 1,070 units; widening it past the container by that
   much lines the mark up with the text beneath. */
.site-footer-wordmark img {
  display: block;
  width: 104.71%;
  max-width: none;
  height: auto;
  margin-inline: -2.355%;
}
