/* Raree's launch palette, with the portal as the only pool of light. */
:root {
  color-scheme: dark;
  --ground: #0e1020;
  --ink: #f1eff8;
  --dim: #b8bbce;
  --faint: #949bb5;
  --accent: #68d8c3;
  --lavender: #c4adff;
  --edge: rgba(204, 237, 255, .13);
  --serif: "New York", "Iowan Old Style", Palatino, Georgia, serif;
  --sans: -apple-system, "SF Pro Text", system-ui, "Segoe UI", sans-serif;
}

* {
  box-sizing: border-box;
}

[hidden] {
  display: none !important;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  min-height: 100vh;
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
  background: var(--ground);
  color: var(--ink);
  font: 17px/1.6 var(--sans);
  -webkit-font-smoothing: antialiased;
  display: flex;
  flex-direction: column;
}

::selection {
  background: var(--accent);
  color: var(--ground);
}

a {
  -webkit-tap-highlight-color: transparent;
}

a:focus-visible, button:focus-visible, summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 5px;
}

main {
  flex: 1;
  width: 100%;
  max-width: 40rem;
  margin: 0 auto;
  padding: 6rem 1.75rem 4rem;
}

p {
  margin: 1.15rem 0 0;
  color: var(--dim);
}

p.tight {
  margin-top: .5rem;
}

p.small {
  font-size: .9rem;
  color: var(--faint);
  max-width: 42ch;
}

strong {
  color: var(--ink);
  font-weight: 600;
}

h1 {
  font: 400 clamp(2.6rem, 9vw, 4.1rem)/1.06 var(--serif);
  letter-spacing: -.025em;
  margin: 1.5rem 0 0;
}

h2 {
  font: 600 .8rem/1.4 var(--sans);
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 2.6rem 0 0;
}

em {
  font-weight: 400;
}

.lede {
  font: 400 1.2rem/1.55 var(--serif);
  color: var(--dim);
  margin-top: 1.2rem;
  max-width: 35ch;
}

.rule {
  height: 1px;
  background: var(--edge);
  border: 0;
  margin: 3rem 0 0;
}

main :where(a:not(.btn, .mark)) {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--edge);
  text-underline-offset: .25em;
}

main :where(a:not(.btn, .mark)):hover {
  text-decoration-color: var(--accent);
}

.mark {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  color: var(--ink);
  text-decoration: none;
}

.mark .app-mark {
  width: 30px;
  height: 38px;
  object-fit: contain;
}

.wordmark {
  display: block;
  width: 4.6rem;
  height: auto;
}

.site-header {
  width: 100%;
  max-width: 72rem;
  margin: 0 auto;
  padding: 1.75rem 2rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
}

.site-header nav {
  display: flex;
  align-items: center;
  gap: 1.75rem;
}

.site-header nav > a:not(.btn) {
  color: var(--dim);
  font-size: .85rem;
  text-decoration: none;
  padding: .7rem 0;
}

.site-header nav > a:hover {
  color: var(--ink);
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: .7rem;
  margin-top: 2rem;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .85rem;
  min-height: 48px;
  border: 1px solid transparent;
  cursor: pointer;
  padding: .85rem 1.3rem;
  border-radius: 999px;
  font: 600 .9rem/1.3 var(--sans);
  text-decoration: none;
  color: var(--ground);
  background: var(--accent);
  transition: background .2s ease, transform .2s ease;
}

.btn:hover {
  background: #92e6d6;
  transform: translateY(-1px);
}

.btn.ghost {
  color: var(--ink);
  background: transparent;
  border-color: var(--edge);
}

.btn.compact {
  white-space: nowrap;
  min-height: 44px;
  padding: .65rem 1.1rem;
  font-size: .85rem;
}

.note {
  margin-top: 1.5rem;
  padding: .95rem 1.1rem;
  border: 1px solid var(--edge);
  border-radius: 12px;
  color: var(--dim);
  font-size: .95rem;
}

.actions .note {
  margin: 0;
  padding: 0;
  border: 0;
}

.availability {
  font-size: .75rem;
  margin-top: .8rem;
  color: var(--faint);
}

.availability a {
  color: var(--dim);
  text-decoration: underline;
  text-decoration-color: var(--edge);
  text-underline-offset: .2em;
}

.availability a:hover {
  color: var(--ink);
}

.text-link {
  display: inline-block;
  margin-top: 1.5rem;
  font-size: .85rem;
  padding: .65rem 0;
}

main.home, main.world {
  max-width: 72rem;
  padding-top: 2rem;
}

.hero {
  display: grid;
  gap: 3rem;
  align-items: center;
  min-height: 39rem;
}

.eyebrow {
  margin: 0;
  color: var(--accent);
  font: 500 .7rem/1.5 var(--sans);
  letter-spacing: .13em;
  text-transform: uppercase;
}

.hero-title {
  margin: 0;
  line-height: 0;
}

.hero-title .wordmark {
  width: clamp(10rem, 15vw, 12.5rem);
}

.home .lede, .world .lede {
  font: 400 1rem/1.75 var(--sans);
  max-width: 36ch;
  margin-top: 1.5rem;
}

.home .say {
  align-self: center;
  padding-bottom: 4rem;
}

.home .actions {
  margin-top: 2rem;
}

/* Below the hero reads like a short essay, not a brochure: its parts are set apart by space alone. */
.about, .also, .exhibits, .get {
  margin-top: 8rem;
  text-align: center;
}

.statement {
  max-width: 34ch;
  margin: 0 auto;
  color: var(--dim);
  font: 400 clamp(1.55rem, 3vw, 2.25rem)/1.38 var(--serif);
  letter-spacing: -.015em;
  text-wrap: balance;
}

.statement strong {
  color: var(--ink);
  font-weight: 400;
}

.home h2 {
  text-wrap: balance;
  font: 400 clamp(2rem, 4vw, 2.7rem)/1.16 var(--serif);
  letter-spacing: -.025em;
  text-transform: none;
  color: var(--ink);
  margin: 0;
}

.home h2 em {
  color: var(--dim);
}

.home .tagline {
  margin: 1.5rem 0 0;
  color: var(--dim);
  font: 400 clamp(1.35rem, 2.1vw, 1.7rem)/1.35 var(--serif);
  letter-spacing: -.01em;
}

.home .tagline em {
  color: var(--lavender);
}

ol.steps {
  list-style: none;
  counter-reset: step;
  margin: 1.5rem 0 0;
  padding: 0;
}

ol.steps li {
  counter-increment: step;
  position: relative;
  padding-left: 2.4rem;
  margin-top: 1.1rem;
  color: var(--dim);
}

ol.steps li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: .05rem;
  width: 1.65rem;
  height: 1.65rem;
  border: 1px solid var(--edge);
  border-radius: 50%;
  display: grid;
  place-items: center;
  font: 500 .8rem/1 var(--sans);
  color: var(--faint);
}

ol.steps b {
  color: var(--ink);
  font-weight: 600;
}

.making-lede {
  max-width: 40ch;
  margin: 1.75rem auto 0;
  color: var(--faint);
  font-size: .95rem;
}

/* The two ways a person makes a world, each a row of small portals with arrows between. */
.ways {
  display: grid;
  gap: 3.5rem;
  margin-top: 3.25rem;
}

.way h3 {
  margin: 0;
  color: var(--lavender);
  font: italic 400 1.45rem/1.3 var(--serif);
}

.way > p {
  max-width: 30ch;
  margin: .4rem auto 0;
  color: var(--faint);
  font-size: .9rem;
}

.path {
  list-style: none;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  gap: 2.4rem;
  margin: 1.75rem 0 0;
  padding: 0;
}

.path li {
  position: relative;
  flex-shrink: 0;
  display: grid;
  justify-items: center;
  gap: .55rem;
  width: 6.5rem;
}

.path li span {
  color: var(--dim);
  font-size: .82rem;
  line-height: 1.35;
}

/* The arrow into a portal from the one before: a short line into a solid head, level with the portals'
   middles and centered in the gap (2.4rem, so its left edge sits 1.2rem plus half its width back). */
.path .arrow {
  position: absolute;
  top: 4.4rem;
  left: -1.9rem;
  width: 1.4rem;
  height: auto;
  color: var(--lavender);
  overflow: visible;
}

.arrow path {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.arrow path:last-child {
  fill: currentColor;
  stroke-width: 1;
}

/* Between the two ways: one, or the other. */
.or {
  display: flex;
  align-items: center;
  gap: .9rem;
  width: 100%;
  max-width: 14rem;
  margin: 0 auto;
  color: var(--faint);
  font: italic 400 1.2rem/1 var(--serif);
}

.or::before, .or::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--edge);
}

.also-lede, .exhibits-lede {
  max-width: 46ch;
  margin: 1rem auto 0;
  color: var(--dim);
  font-size: .95rem;
  text-wrap: balance;
}

/* A few of the built-in worlds, each a little portal that opens the world's own page. */
.exhibit-list {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 2rem 1.75rem;
  margin: 2.75rem 0 0;
  padding: 0;
}

.exhibit-list a {
  display: grid;
  justify-items: center;
  gap: .75rem;
  width: 7.5rem;
  color: inherit;
  text-decoration: none;
}

.exhibit-list img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 280 / 398;
  object-fit: cover;
  border: 3px solid #68d8c3;
  border-radius: 50%;
  transition: transform .3s ease;
}

.exhibit-list a:hover img {
  transform: translateY(-3px);
}

.exhibit-list span {
  color: var(--lavender);
  font: italic 400 .95rem/1.35 var(--serif);
  text-align: center;
}

.stage {
  display: block;
  width: 100%;
  height: auto;
}

.stage-words {
  fill: #3e6e66;
  font: italic 400 13px var(--serif);
}

.stage .caret {
  animation: blink 1.1s steps(1) infinite;
}

.making-after {
  max-width: 44ch;
  margin: 3.5rem auto 0;
  color: var(--dim);
  font: 400 1.15rem/1.6 var(--serif);
  text-wrap: pretty;
}

/* The rest, set like a printed programme: a name, then a line about it. */
/* "Each world includes", set as a small label on the list it introduces. */
.notes-intro {
  max-width: 44rem;
  margin: 3rem auto 0;
  color: var(--faint);
  font: 600 .72rem/1.4 var(--sans);
  letter-spacing: .13em;
  text-transform: uppercase;
  text-align: left;
}

.notes-intro + .notes {
  margin-top: .9rem;
}

.notes {
  max-width: 44rem;
  margin: 3rem auto 0;
  text-align: left;
}

.notes div {
  display: grid;
  gap: .25rem;
  padding: 1.3rem 0;
  border-top: 1px solid var(--edge);
}

.notes div:last-child {
  border-bottom: 1px solid var(--edge);
}

.notes dt {
  color: var(--lavender);
  font: italic 400 1.25rem/1.35 var(--serif);
}

.notes dd {
  margin: 0;
  color: var(--dim);
  font-size: .95rem;
}

/* The close: one more way to the App Store, saying where Raree runs. */
.get-lede {
  max-width: 36ch;
  margin: 1rem auto 0;
  color: var(--dim);
  font: 400 1.15rem/1.6 var(--serif);
  text-wrap: balance;
}

.get .actions {
  justify-content: center;
  margin-top: 2rem;
}

.also .text-link {
  display: inline-block;
  margin-top: 2.5rem;
}

@keyframes blink {
  50% {
    opacity: 0;
  }
}

footer {
  width: 100%;
  max-width: 40rem;
  margin: 2rem auto 0;
  padding: 2rem 1.75rem 3rem;
  color: var(--faint);
  font-size: .75rem;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 1.2rem;
}

footer a {
  color: var(--faint);
  text-decoration: none;
  padding: .5rem 0;
}

footer a:hover {
  color: var(--ink);
}

.home + footer, .world + footer {
  max-width: 72rem;
}

footer .credit {
  flex-basis: 100%;
  margin: 1.25rem 0 0;
  text-align: center;
}

footer .credit a {
  padding: 0;
}

/* The app icon's portal, opened wider: a glass rim around worlds in layers, one at a time. */
.peep {
  --stage-height: 34rem;
  position: relative;
  width: 100%;
  margin: 0;
  min-width: 0;
  max-width: 26rem;
  margin-inline: auto;
  text-align: center;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}

.portal-stage {
  width: 100%;
  height: var(--stage-height);
  position: relative;
  display: grid;
  place-items: center;
  perspective: 1000px;
}

.portal-tilt {
  width: min(22rem, calc(100% - 2rem));
  transform: rotateY(calc(var(--px, 0) * 16deg)) rotateX(calc(var(--py, 0) * -7deg));
  transform-style: preserve-3d;
}

.portal-bloom {
  position: relative;
  transform-origin: center center;
}

/* The light the opening throws round itself. */
.portal-bloom::before {
  content: "";
  position: absolute;
  inset: -14% -22%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(104, 216, 195, .2), rgba(104, 216, 195, .07) 60%, transparent);
  pointer-events: none;
}

.portal-rim {
  --rim-width: 10px;
  position: relative;
  isolation: isolate;
  width: 100%;
  aspect-ratio: 280 / 398;
  border-radius: 50%;
  padding: var(--rim-width);
  background: #68d8c3;
  border: 1px solid rgba(224, 255, 245, .5);
  box-shadow: inset 2px 2px 3px rgba(255, 255, 255, .5),
    inset -2px -2px 4px rgba(34, 98, 87, .18),
    0 16px 36px rgba(0, 0, 0, .16), 0 0 36px -16px rgba(104, 216, 195, .24);
}

.portal-rim::before, .portal-rim::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  padding: var(--rim-width);
  border-radius: inherit;
  pointer-events: none;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
}

.portal-rim::before {
  background: rgba(255, 255, 255, .06);
  -webkit-backdrop-filter: blur(6px) saturate(1.15);
  backdrop-filter: blur(6px) saturate(1.15);
  box-shadow: inset 0 1px 2px rgba(255, 255, 255, .65),
    inset 0 -1px 2px rgba(27, 88, 76, .2);
}

/* A passing glint is masked to the rim, so the world behind it stays clear. */
.portal-rim::after {
  background: linear-gradient(115deg, transparent 42%, rgba(255, 255, 255, .8) 49%, transparent 56%);
  background-size: 300% 300%;
  background-position: 130% 50%;
  opacity: 0;
}

.portal-window {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  overflow: hidden;
  position: relative;
  background: var(--ground);
}

.scene-defs {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* One world shows at a time. Each brings its own sky; --at is when it starts to open. */
[data-peep] .world {
  --at: 1.25s;
  position: absolute;
  inset: 0;
}

[data-peep] .world:not([data-on]) {
  display: none;
}

.world-alien {
  background: linear-gradient(#b8e0ff, #ccedff 40%, #e4f3f7 62%, #cfeae6);
}

.world-egypt {
  background: linear-gradient(#f8d3c4, #fde2cc 40%, #fff0d8 62%, #f2dfc0);
}

.world-candy {
  background: linear-gradient(#ffd4e6, #ffe2ee 40%, #fff0f3 62%, #f6e3e8);
}

/* Every layer is a canvas wider and taller than the opening. A layer moves with the eye by its
   --k: the horizon nearly as far as the eye moves, the shore at the opening hardly at all. */
.lyr {
  position: absolute;
  left: -30%;
  top: -10%;
  width: 160%;
  height: 120%;
  translate: calc(var(--px, 0) * var(--k) * -15%) calc(var(--py, 0) * var(--k) * -5%);
  transform-origin: 50% 62%;
  will-change: translate, scale;
}

/* What moves inside a layer turns and grows about its own box. */
.grow, .sprout, .sway, .bob, .rise, .arrive, .hover, .drift, .fly-in, .sail-in, .spin, .puff {
  transform-box: fill-box;
  transform-origin: 50% 50%;
}

/* The mist a world opens out of and closes into. It parts from the middle. */
@property --open {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 150%;
}

.veil {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(closest-side at 50% 60%, #ffffff, #e8f8f6 45%, #c3ebe6);
  -webkit-mask: radial-gradient(closest-side at 50% 60%, transparent var(--open), #000 calc(var(--open) + 45%));
  mask: radial-gradient(closest-side at 50% 60%, transparent var(--open), #000 calc(var(--open) + 45%));
}

.glass {
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .3), inset 2px 2px 5px rgba(32, 63, 69, .18);
}

.portal-floor {
  position: absolute;
  bottom: 1rem;
  left: 50%;
  width: 16rem;
  height: 1.4rem;
  border-radius: 50%;
  transform: translateX(-50%);
  background: radial-gradient(ellipse, rgba(104, 216, 195, .11), transparent 70%);
}

/* The sentence each world was made from, stacked so the line keeps its height as they change. */
.prompts {
  display: grid;
  margin: .25rem auto .75rem;
  padding: 0 1rem;
  max-width: 24rem;
}

.prompt {
  --at: 1.25s;
  grid-area: 1 / 1;
  align-self: center;
  margin: 0;
  min-width: 0;
  color: var(--dim);
  font: italic 400 .92rem/1.5 var(--serif);
  letter-spacing: .01em;
  text-wrap: balance;
  overflow-wrap: break-word;
}

.prompt:not([data-on]) {
  visibility: hidden;
}

[data-cycled] .world, [data-cycled] .prompt {
  --at: .15s;
}

.typed > span {
  position: relative;
}

.typed > span::after {
  content: "";
  position: absolute;
  top: .18em;
  bottom: .08em;
  right: -.06em;
  width: 1.5px;
  border-radius: 1px;
  background: var(--accent);
  opacity: 0;
}

/* Until peep.js starts the arrival, a page that will animate (the inline script's data-motion) shows neither the
   portal nor its sentence, so neither flashes up whole first. If the script never comes, they show anyway. */
[data-motion] [data-peep]:not([data-peep-ready]) .portal-stage,
[data-motion] [data-peep]:not([data-peep-ready]) .prompts {
  animation: wait-for-script 2.5s step-end;
}

/* The first arrival: the rim gathers out of a haze, full of mist. */
[data-peep-ready] .portal-bloom {
  animation: portal-open 1.5s cubic-bezier(.22, 1, .36, 1) .5s both;
}

[data-peep-ready] .portal-bloom::before {
  animation: halo-swell 2.6s ease-out .5s both;
}

[data-peep-ready] .portal-floor {
  animation: portal-light 1.6s ease .5s both;
}

/* Each world's arrival, timed from --at: the mist parts as the camera draws back through the opening
   (near layers start larger than far ones, so the depth shows at once), then the buildings rise, the
   plants open and the craft come in. Its sentence is typed once all that has settled. */
[data-peep-ready] .veil {
  animation: veil-clear 1.8s cubic-bezier(.45, 0, .35, 1) var(--at) both;
}

[data-peep-ready] .lyr {
  animation: dolly 2.6s cubic-bezier(.16, .84, .3, 1) var(--at) both;
}

[data-peep-ready] .rise {
  animation: rise 2s cubic-bezier(.2, .8, .3, 1) calc(var(--at) + .15s) both;
}

[data-peep-ready] .grow {
  transform-origin: 50% 100%;
  animation: grow 1s cubic-bezier(.2, .9, .3, 1.04) calc(var(--at) + .65s + var(--i, 0) * 70ms) both;
}

[data-peep-ready] .sprout {
  transform-origin: 50% 100%;
  animation: sprout .9s cubic-bezier(.2, .9, .3, 1.1) calc(var(--at) + 1.05s + var(--i, 0) * 80ms) both;
}

[data-peep-ready] .arrive {
  animation: arrive 1.2s cubic-bezier(.2, .8, .3, 1) calc(var(--at) + 1.55s) both;
}

[data-peep-ready] .fly-in {
  animation: fly-in 2.6s cubic-bezier(.2, .7, .3, 1) calc(var(--at) + .35s) both;
}

[data-peep-ready] .sail-in {
  animation: sail-in 2.4s cubic-bezier(.2, .7, .3, 1) calc(var(--at) + .55s) both;
}

[data-peep-ready] .sail-in.late {
  animation-delay: calc(var(--at) + .95s);
}

[data-peep-ready] .prompt[data-on] .typed > span {
  animation: type-in 16ms linear calc(var(--at) + 2.6s + var(--i) * 32ms) both;
}

[data-peep-ready] .prompt[data-on] .typed > span::after {
  animation: caret 32ms step-end calc(var(--at) + 2.6s + var(--i) * 32ms);
}

[data-peep-ready] .prompt[data-on] .typed > span:last-child::after {
  animation: caret-rest 1.8s step-end calc(var(--at) + 2.6s + var(--i) * 32ms);
}

/* A world's leaving: its sentence is erased from the end as the mist closes in and the camera leans
   through the opening. Only what was typed is erased (--typed, counted by peep.js as the world closes):
   a letter not yet typed is gone at once, rather than showing to be erased. */
[data-peep-ready] .prompt[data-closing] .typed > span {
  animation: erase 10ms linear calc((var(--typed, var(--n)) - 1 - var(--i)) * 14ms) both;
}

[data-peep-ready] .prompt[data-closing] .typed > span::after {
  animation: caret 14ms step-end calc((var(--typed, var(--n)) - 2 - var(--i)) * 14ms);
}

[data-peep-ready] .world[data-closing] .veil {
  animation: veil-close .95s cubic-bezier(.55, 0, .7, .4) both;
}

[data-peep-ready] .world[data-closing] .lyr {
  animation: lean-in .95s cubic-bezier(.55, 0, .7, .4) both;
}

/* What keeps moving while a world is open. */
[data-peep-ready] .mote {
  animation: mote-drift 12s ease-in-out var(--delay) infinite alternate;
}

[data-peep-ready] .ripples {
  animation: lake-light 7s ease-in-out infinite alternate;
}

[data-peep-ready] .bob {
  transform-origin: 50% 100%;
  animation: bob 4.6s ease-in-out infinite alternate;
}

[data-peep-ready] .drift {
  animation: drift 16s ease-in-out infinite alternate;
}

[data-peep-ready] .hover {
  animation: hover 4.2s ease-in-out infinite alternate;
}

[data-peep-ready] .sway {
  transform-origin: 50% 100%;
  animation: sway 7s ease-in-out infinite alternate;
}

[data-peep-ready] .slow {
  animation-duration: 11s;
  animation-delay: -4s;
}

[data-peep-ready] .aura {
  animation: shine 6s ease-in-out infinite alternate;
}

[data-peep-ready] .spin {
  animation: spin 30s linear infinite;
}

[data-peep-ready] .spin.quick {
  animation-duration: 6s;
}

[data-peep-ready] .spin.big {
  animation-duration: 60s;
  animation-direction: reverse;
}

[data-peep-ready] .spin.counter {
  animation-duration: 18s;
  animation-direction: reverse;
}

[data-peep-ready] .spin.big.counter {
  animation-duration: 32s;
  animation-direction: normal;
}

[data-peep-ready] .puff {
  animation: puff 3.6s ease-out infinite;
}

[data-peep-ready] .puff:nth-child(2) {
  animation-delay: -1.2s;
}

[data-peep-ready] .puff:nth-child(3) {
  animation-delay: -2.4s;
}

[data-peep-ready] .portal-rim::after {
  animation: rim-shimmer 14s ease-in-out -4.4s infinite;
}

/* Offscreen or in a hidden tab, everything holds where it is. */
[data-peep-dormant] *, [data-peep-dormant] *::before, [data-peep-dormant] *::after {
  animation-play-state: paused !important;
}

@keyframes wait-for-script {
  from {
    visibility: hidden;
  }
}

/* Letters fade, if only for a frame, rather than step: a finished animation can stop a hair short of its end
   (0.9999…), and a step held there would leave the letter as it started. */
@keyframes type-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes erase {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

@keyframes caret {
  from {
    opacity: 1;
  }
}

@keyframes caret-rest {
  0%, 33%, 67% {
    opacity: 1;
  }
  17%, 50%, 83%, 100% {
    opacity: 0;
  }
}

@keyframes portal-open {
  from {
    transform: scale(.7);
    opacity: 0;
    filter: blur(10px);
  }
  45% {
    opacity: 1;
  }
  to {
    transform: scale(1);
    opacity: 1;
    filter: blur(0);
  }
}

@keyframes halo-swell {
  from {
    opacity: 0;
    transform: scale(.6);
  }
  45% {
    opacity: 1;
    transform: scale(1.12);
  }
}

@keyframes portal-light {
  from {
    opacity: 0;
    transform: translateX(-50%) scale(.82);
  }
}

@keyframes veil-clear {
  from {
    opacity: 1;
    --open: -45%;
  }
  70% {
    opacity: 1;
  }
  to {
    opacity: 0;
    --open: 100%;
  }
}

@keyframes veil-close {
  from {
    opacity: 0;
    --open: 100%;
  }
  30% {
    opacity: 1;
  }
  to {
    opacity: 1;
    --open: -45%;
  }
}

@keyframes dolly {
  from {
    scale: var(--dolly);
  }
}

@keyframes lean-in {
  to {
    scale: calc(1 + (var(--dolly) - 1) * .4);
  }
}

@keyframes rise {
  from {
    transform: translateY(70px);
  }
}

@keyframes grow {
  from {
    transform: scaleY(0);
  }
}

@keyframes sprout {
  from {
    transform: scale(.15);
    opacity: 0;
  }
  40% {
    opacity: 1;
  }
}

@keyframes arrive {
  from {
    transform: translateY(40px) scale(.4);
    opacity: 0;
  }
}

@keyframes fly-in {
  from {
    transform: translateX(-260px);
    opacity: 0;
  }
  30% {
    opacity: 1;
  }
}

@keyframes sail-in {
  from {
    transform: translateX(220px);
    opacity: 0;
  }
  30% {
    opacity: 1;
  }
}

@keyframes mote-drift {
  from {
    transform: translate(0, 10px);
    opacity: .25;
  }
  to {
    transform: translate(5px, -18px);
    opacity: .9;
  }
}

@keyframes lake-light {
  from {
    opacity: .25;
  }
  to {
    opacity: .6;
  }
}

@keyframes bob {
  from {
    transform: translateY(-1px) rotate(1deg);
  }
  to {
    transform: translateY(3px) rotate(-2deg);
  }
}

@keyframes drift {
  to {
    transform: translate(34px, -8px);
  }
}

@keyframes hover {
  from {
    transform: translateY(-5px);
  }
  to {
    transform: translateY(6px);
  }
}

@keyframes sway {
  from {
    transform: rotate(-1.5deg);
  }
  to {
    transform: rotate(2deg);
  }
}

@keyframes shine {
  from {
    opacity: .55;
  }
  to {
    opacity: 1;
  }
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

@keyframes puff {
  from {
    transform: translate(0, 0) scale(.35);
    opacity: 0;
  }
  15% {
    opacity: .85;
  }
  to {
    transform: translate(-12px, -46px) scale(1.7);
    opacity: 0;
  }
}

@keyframes rim-shimmer {
  0%, 58% {
    background-position: 130% 50%;
    opacity: 0;
  }
  64% {
    opacity: .45;
  }
  82% {
    background-position: -30% 50%;
    opacity: .45;
  }
  88%, 100% {
    background-position: -30% 50%;
    opacity: 0;
  }
}

/* Shared-world pages keep the same oval and a quiet reading layout. */
.ios-only {
  display: none;
}

[data-ios] .ios-only {
  display: revert;
}

[data-ios] .not-ios {
  display: none;
}

.arch {
  position: relative;
  width: min(100%, 17rem);
  aspect-ratio: 280 / 398;
  overflow: hidden;
  border-radius: 50%;
  background: var(--ground);
  border: 7px solid var(--accent);
  box-shadow: 4px 5px 0 #456a70, 0 0 36px -20px rgba(104, 216, 195, .25);
}

.arch.shut {
  background: #151d30;
}

.arch.shut .veil {
  position: absolute;
  inset: -20%;
  background: radial-gradient(ellipse at 30% 35%, rgba(196, 173, 255, .45), transparent 55%), radial-gradient(ellipse at
    65% 80%, rgba(104, 216, 195, .4), transparent 60%);
  filter: blur(16px);
}

.arch.shut .veil::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(2px 2px at 33% 37%, #ccedff, transparent), radial-gradient(2px 2px at 68% 66%, #ffbedb,
    transparent), radial-gradient(1px 1px at 45% 73%, #ccedff, transparent);
}

/* The page with nothing behind it (404.html): the portal open on mist alone. The wisps turn round a dark middle,
   the inner ones faster than the outer, as water turns into a drain. Each layer is a circle wider than the oval,
   so it turns without its edge showing, and its wisps lie along the turn. */
.arch.empty {
  background: radial-gradient(closest-side at 50% 52%, #06070f, #121831 48%, #2a3258);
}

.arch.empty .mist {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 150%;
  aspect-ratio: 1;
  translate: -50% -50%;
  border-radius: 50%;
  pointer-events: none;
  animation: swirl 40s linear infinite;
}

/* The outer band: three soft arms of lavender, mint and white, broad and slow, thinning toward the middle. */
.arch.empty .mist.far {
  background: conic-gradient(transparent, rgba(196, 173, 255, .62) 45deg, transparent 105deg,
    rgba(104, 216, 195, .5) 165deg, transparent 225deg, rgba(241, 239, 248, .5) 285deg, transparent 345deg);
  filter: blur(22px);
  -webkit-mask: radial-gradient(closest-side, transparent 16%, #000 60%);
  mask: radial-gradient(closest-side, transparent 16%, #000 60%);
}

/* The inner wisps: paler, nearer the middle, faster, and breathing a little. Each lies along the turn. */
.arch.empty .mist.near {
  width: 112%;
  background:
    radial-gradient(ellipse 28% 9% at 42% 18%, rgba(241, 239, 248, .75), transparent 70%),
    radial-gradient(ellipse 9% 26% at 83% 42%, rgba(241, 239, 248, .55), transparent 70%),
    radial-gradient(ellipse 26% 9% at 60% 82%, rgba(196, 173, 255, .65), transparent 70%),
    radial-gradient(ellipse 9% 28% at 17% 58%, rgba(104, 216, 195, .55), transparent 70%),
    radial-gradient(ellipse 20% 7% at 64% 30%, rgba(196, 173, 255, .5), transparent 70%),
    radial-gradient(ellipse 7% 20% at 70% 64%, rgba(241, 239, 248, .45), transparent 70%),
    radial-gradient(ellipse 20% 7% at 36% 70%, rgba(241, 239, 248, .45), transparent 70%),
    radial-gradient(ellipse 7% 20% at 30% 36%, rgba(104, 216, 195, .45), transparent 70%);
  filter: blur(9px);
  -webkit-mask: radial-gradient(closest-side, transparent 10%, #000 45%);
  mask: radial-gradient(closest-side, transparent 10%, #000 45%);
  animation: swirl 18s linear infinite, breathe 7s ease-in-out infinite alternate;
}

@keyframes swirl {
  to {
    rotate: 1turn;
  }
}

@keyframes breathe {
  from {
    scale: .93;
    opacity: .7;
  }
  to {
    scale: 1.05;
    opacity: 1;
  }
}

.world-layout {
  display: grid;
  gap: 3rem;
  align-items: center;
  min-height: 36rem;
}

.world-copy {
  min-width: 0;
}

.world h1 {
  font-size: clamp(2.5rem, 4.5vw, 4rem);
  overflow-wrap: anywhere;
  margin-top: 1rem;
}

.world-picture {
  margin: 0;
  min-width: 0;
}

.world-picture .arch {
  margin: auto;
}

.world-preview {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.world-picture figcaption {
  margin-top: 1.5rem;
  font-size: .8rem;
  color: var(--faint);
  text-align: center;
}

.world-description {
  overflow-wrap: anywhere;
  max-height: 12rem;
  overflow: auto;
}

.open-guide {
  border-left: 1px solid var(--edge);
  padding-left: 1.1rem;
  margin-top: 1.75rem;
}

.open-guide h2 {
  font: 600 .95rem/1.4 var(--sans);
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  margin: 0;
}

.open-guide p {
  font-size: .9rem;
  margin-top: .4rem;
}

.copy-status {
  font-size: .85rem;
  margin-top: .75rem;
}

.copy-status:empty {
  display: none;
}

.share-note {
  font-size: .8rem;
  max-width: 46ch;
  color: var(--faint);
}

.world .text-link {
  margin-top: 1rem;
}

.copy-link {
  display: inline-block;
  min-height: 44px;
  padding: .6rem 0;
  margin-top: .25rem;
  font: 500 .85rem/1.4 var(--sans);
  color: var(--dim);
  border: 0;
  border-bottom: 1px solid var(--edge);
  background: transparent;
  cursor: pointer;
}

.copy-link:hover {
  color: var(--ink);
}

/* Help and policy pages preserve their narrow reading width and native disclosures. */
.toc {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: .55rem 1.1rem;
  margin: 1.8rem 0 0;
  padding: 0;
}

.toc li {
  font-size: .95rem;
}

main .toc a {
  color: var(--dim);
}

main .toc a:hover {
  color: var(--ink);
}

.guide {
  margin-top: 4rem;
  scroll-margin-top: 1.5rem;
}

/* Support: its guides in five groups, with the contents beside them on a wide screen and above them on a
   phone. A group opens with its name on a rule; the guides inside it follow at a smaller gap. */
main.support {
  max-width: 64rem;
}

.support-nav {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.4rem 1.25rem;
  margin-top: 2.25rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--edge);
}

.nav-title, .group-label {
  margin: 0;
  color: var(--accent);
  font: 600 .7rem/1.4 var(--sans);
  letter-spacing: .13em;
  text-transform: uppercase;
}

.support-nav ul {
  list-style: none;
  margin: .55rem 0 0;
  padding: 0;
}

.support-nav li {
  margin-top: .35rem;
  font-size: .92rem;
  line-height: 1.4;
}

main .support-nav a {
  color: var(--dim);
  text-decoration: none;
}

main .support-nav a:hover {
  color: var(--ink);
}

.guides {
  max-width: 40rem;
}

.group-label {
  margin-top: 5rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--edge);
  scroll-margin-top: 1.5rem;
}

.guides > .group-label:first-child {
  margin-top: 3rem;
  padding-top: 0;
  border-top: 0;
}

.group-label + .guide {
  margin-top: 1.4rem;
}

/* A group's few lines before its guides: what the group covers, with links into it. */
.group-label + .group-intro {
  margin-top: 1.4rem;
}

.group-intro ul.facts a {
  text-decoration: underline 1px var(--accent);
  text-underline-offset: .28em;
}

@media (min-width: 60rem) {
  .support-layout {
    display: grid;
    grid-template-columns: 13rem minmax(0, 40rem);
    gap: 4.5rem;
    align-items: start;
  }
  .support-nav {
    position: sticky;
    top: 2rem;
    grid-template-columns: 1fr;
    max-height: calc(100vh - 4rem);
    overflow-y: auto;
    margin-top: 3rem;
    padding: 0;
    border: 0;
  }
}

.guide h2 {
  font: 400 clamp(1.7rem, 6vw, 2.2rem)/1.15 var(--serif);
  letter-spacing: -.015em;
  text-transform: none;
  color: var(--ink);
  margin: 0;
}

.guide h3 {
  font: 600 .78rem/1.4 var(--sans);
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--faint);
  margin-top: 2.2rem;
}

.guide .lead, .group-intro .lead {
  font: 400 1.15rem/1.5 var(--serif);
  color: var(--ink);
  margin-top: .8rem;
}

.guide .note {
  margin-top: 1.3rem;
}

/* A price list, set like a printed tariff: the thing on the left, its price on the right, a hairline between. */
.prices {
  width: 100%;
  margin-top: .9rem;
  border-collapse: collapse;
  font-size: .95rem;
}

.prices th, .prices td {
  padding: .7rem 0;
  border-bottom: 1px solid var(--edge);
  text-align: left;
  vertical-align: baseline;
}

.prices td {
  padding-left: 1rem;
  color: var(--ink);
  text-align: right;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.prices tbody th {
  color: var(--dim);
  font-weight: 400;
}

.prices thead th {
  padding-top: 0;
  color: var(--faint);
  font: 600 .7rem/1.4 var(--sans);
  letter-spacing: .1em;
  text-transform: uppercase;
  text-align: right;
}

.guide .fine {
  margin-top: .7rem;
  color: var(--faint);
  font-size: .85rem;
}

.guide p.note {
  color: var(--dim);
}

ul.facts {
  list-style: none;
  margin: .4rem 0 0;
  padding: 0;
}

ul.facts li {
  margin-top: .85rem;
  color: var(--dim);
}

ul.facts b {
  color: var(--ink);
  font-weight: 600;
}

.more {
  margin-top: 2.2rem;
  border-bottom: 1px solid var(--edge);
}

.more h3 {
  margin: 0 0 .7rem;
}

/* A guide that is only its questions (Common problems) starts them close under its title. */
.guide h2 + .more {
  margin-top: 1.4rem;
}

.more details {
  border-top: 1px solid var(--edge);
}

.more summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 44px;
  padding: .7rem 0;
  color: var(--ink);
  font-weight: 600;
}

.more summary::-webkit-details-marker {
  display: none;
}

.more summary::after {
  content: "";
  flex: none;
  width: .5rem;
  height: .5rem;
  margin-right: .3rem;
  border-right: 1.5px solid var(--faint);
  border-bottom: 1.5px solid var(--faint);
  transform: translateY(-.15rem) rotate(45deg);
  transition: transform .2s ease;
}

.more details[open] summary::after {
  transform: translateY(.1rem) rotate(225deg);
}

.more details > p {
  margin: 0 0 1rem;
}

.more details > p + p {
  margin-top: -.4rem;
}

@media (min-width: 48rem) {
  /* The home page has no header: on a wide screen the portal starts lower, more so on a taller window. */
  main.home {
    padding-top: clamp(4rem, 11vh, 9rem);
  }
  .home .hero {
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    gap: 4rem;
  }
  .notes div {
    grid-template-columns: 15rem minmax(0, 1fr);
    gap: 2rem;
    align-items: baseline;
  }
  .world-layout {
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    gap: 4rem;
  }
}

/* Side by side where both ways fit at full size, each sized to its portals with the "or" between at equal gaps.
   Each way's heading shares the first row and its portals the second, so the "or" is placed in the portals' row:
   past the path's gap (1.75rem) to half a portal (6.5rem wide at 200:140), less half the "or" (1.2rem at 1).
   Narrower, the ways stack with the "or" between. */
@media (min-width: 64rem) {
  .ways {
    grid-template-columns: auto auto auto;
    grid-template-rows: auto auto;
    justify-content: center;
    column-gap: 3.5rem;
    row-gap: 0;
  }
  .way {
    display: contents;
  }
  .way h3 {
    grid-row: 1;
  }
  .way .path {
    grid-row: 2;
  }
  .way:first-child > * {
    grid-column: 1;
  }
  .way:last-child > * {
    grid-column: 3;
  }
  .or {
    display: block;
    grid-column: 2;
    grid-row: 2;
    align-self: start;
    width: auto;
    margin-top: calc(1.75rem + 6.5rem * 200 / 140 / 2 - .6rem);
  }
  .or::before, .or::after {
    content: none;
  }
}

@media (max-width: 47.99rem) {
  .site-header {
    padding: 1.3rem 1.5rem;
  }
  main.home, main.world {
    padding: 3rem 1.5rem 2rem;
  }
  .hero {
    min-height: auto;
    gap: 1rem;
  }
  .home .say {
    max-width: 31rem;
    margin: auto;
    width: 100%;
    padding-top: 0;
    padding-bottom: 0;
    text-align: center;
  }
  .hero-title .wordmark {
    width: 10.5rem;
    margin-inline: auto;
  }
  .home .tagline {
    margin-top: 1rem;
    font-size: 1.3rem;
  }
  .home .actions {
    justify-content: center;
    margin-top: 1.5rem;
  }
  .portal-tilt {
    width: 15.5rem;
  }
  .portal-floor {
    width: 13rem;
  }
  .peep {
    --stage-height: 24rem;
    max-width: 22rem;
  }
  .about, .also, .exhibits, .get {
    margin-top: 5.5rem;
  }
  .statement {
    font-size: 1.4rem;
  }
  .path {
    gap: 1.9rem;
  }
  .path li {
    width: 5.1rem;
  }
  .path .arrow {
    top: 3.44rem;
    left: -1.5rem;
    width: 1.1rem;
  }
  .exhibit-list {
    gap: 1.75rem 1.25rem;
  }
  .exhibit-list a {
    width: 6.2rem;
  }
  .world-layout {
    min-height: auto;
  }
  .world-picture .arch {
    max-width: 13rem;
  }
  footer {
    padding: 2rem 1.5rem;
  }
}

@media (max-width: 30rem) {
  .site-header nav {
    gap: 1.1rem;
  }
  .mark .app-mark {
    width: 24px;
    height: 32px;
  }
  .mark .wordmark {
    width: 4.1rem;
  }
  main {
    padding: 3rem 1.5rem;
  }
  .world .actions {
    flex-direction: column;
    align-items: stretch;
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  .btn, .more summary::after {
    transition: none;
  }
  .btn:hover {
    transform: none;
  }
  .portal-tilt {
    transform: none;
  }
  .stage .caret {
    animation: none;
  }
  .exhibit-list img {
    transition: none;
  }
  .lyr {
    translate: none;
  }
  .peep *, .peep *::before, .peep *::after {
    animation: none !important;
  }
  .arch.empty .mist {
    animation: none;
  }
}
