/* The homepage's scroll: the hero screen from styles.css, then one screen
   per feature (a two-line title over the app on one brand shape), the
   app's features in four cards, and a closing spray around the button again. Motion
   lives in home.js; with reduced motion or no script everything simply
   sits still and visible. */

/* The hero title is always two set lines. On phones the longer line
   ("are in your camera roll.", about 9.3em) sets the size so it never
   wraps or overflows. */
h1.statement {
  text-wrap: initial;
  word-spacing: 0.05em;
  line-height: calc(1.04em + 2px);
}

/* The section titles' size, shared by the features and the FAQ. */
:root {
  --section-title: clamp(44px, min(3.6vw, 5.8vh), 54px);
}

/* The hero's line of copy gets Graphical's roomier leading. */
.hero p {
  line-height: 30px;
}

/* The hero's pieces scatter as the page scrolls away from it. home.js
   writes each seat's offset, fade and extra turn; the `translate` property
   composes outside the centering transform, so the hover tilt and spin keep
   working underneath. */
.landing-page .seat {
  translate: var(--sx, 0px) var(--sy, 0px);
  opacity: var(--so, 1);
}

/* Pieces leaving through the hero's bottom edge stay visible as they go,
   painted beneath the first feature; the sides still clip so the page
   never scrolls sideways. */
.landing-page {
  overflow: visible;
  overflow-x: clip;
  isolation: isolate;
}

.landing-page .wall {
  overflow: visible;
}

.seat picture {
  display: block;
}

.landing-page .seat.shape {
  transform: translate(-50%, -50%) rotate(calc(var(--r, 0deg) + var(--sr, 0deg)));
}

.motion .landing-page .seat {
  will-change: translate, opacity;
}

/* The page scrolls freely; in-page links glide. */
html {
  scroll-behavior: smooth;
}

/* While the cat walks the right edge she is the scroll bar, so the
   browser's own one stays out of sight. Without motion she stays home in
   the button and the usual scroll bar shows. */
html.motion {
  scrollbar-width: none;
}

html.motion::-webkit-scrollbar {
  display: none;
}

/* Features ---------------------------------------------------------------- */

.features {
  display: block;
}

.feature {
  /* A section is one screen: the phone takes whatever height the title,
     the gap and the padding leave, within limits. The phone's visible body
     is 916 x 1896 of the 1048 x 2160 Matte canvas. */
  --title: var(--section-title);
  --gap: clamp(52px, calc(5vh + 20px), 68px);
  --pad: clamp(40px, 8vh, 80px);
  --phone-h: min(600px, max(420px, calc(100svh - 2.32 * var(--title) - 6px - var(--gap) - 2 * var(--pad))));
  --phone-w: calc(var(--phone-h) * 0.4831);

  position: relative;
  display: flex;
  min-height: 100vh;
  min-height: 100svh;
  box-sizing: border-box;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--gap);
  padding: var(--pad) 24px;
  /* Snapped screens show nothing of their neighbors, so a shape that runs
     past its own screen is cut at the edge rather than peeking into the
     next one. */
  overflow: clip;
  isolation: isolate;
}

.feature h2,
.closing h2 {
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  text-align: center;
}

.feature h2 {
  position: relative;
  z-index: 2;
  font-size: var(--title);
  line-height: calc(1.16em + 3px);
  letter-spacing: -0.01em;
  word-spacing: 0.06em;
}

/* The spouse, team and features screens are as tall as what they hold, so
   the space around them is the same padding-to-padding gap as between the
   phone screens. */
.feature.flip-out,
.feature.team,
.feature.features {
  min-height: 0;
}

/* The spouse screen lets its hearts out past its top edge (see .hearts). */
.feature.flip-out {
  overflow: visible;
}

.feature h2.one-line {
  text-wrap: balance;
}

.feature h2 span,
.closing h2 span {
  display: block;
  white-space: nowrap;
}

.device-stage {
  position: relative;
  width: var(--phone-w);
  height: var(--phone-h);
  flex: 0 0 auto;
}

/* One brand shape, centered behind the phone at 2.1 phone widths, sitting a
   hair low. Sections alternate its tilt, which is where it rests; home.js
   turns it with the scroll (--spin). */
.device-shape {
  position: absolute;
  z-index: 0;
  top: 50%;
  left: 50%;
  width: calc(var(--phone-w) * 2.12);
  max-width: none;
  height: auto;
  pointer-events: none;
  user-select: none;
  transform: translate(-50%, -48%) rotate(calc(var(--tilt, 0deg) + var(--spin, 0deg)));
}

.device {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
}

/* The phone is drawn with its side buttons standing out of its body (428
   wide around a 420 body), so the drawing is a hair wider than the stage
   and centered on it: the body is exactly the stage. Its shadow belongs to
   the stage, so the animated screen never repaints a filter. */
.device::before {
  position: absolute;
  inset: 0;
  border-radius: 16.2% / 7.8%;
  box-shadow: 0 2px 4px rgba(58, 48, 40, 0.14), 0 26px 50px -14px rgba(58, 48, 40, 0.38);
  content: "";
}

.device-phone {
  position: absolute;
  top: 0;
  left: 50%;
  display: block;
  width: auto;
  max-width: none;
  height: 100%;
  aspect-ratio: 428 / 870;
  transform: translateX(-50%);
  user-select: none;
  -webkit-user-drag: none;
}

/* A title with a line of supporting copy under it. */
.feature-copy {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
}

/* An aside in the pencil hand of the nudge caption. */
.feature-sub.handwritten {
  max-width: 640px;
  color: #8e8e93;
  font-family: "Caveat", cursive;
  font-size: 30px;
  font-weight: 400;
  line-height: 1.15;
}

.feature-sub.handwritten a {
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1.5px;
  text-underline-offset: 0.12em;
}

.feature-sub.handwritten a:hover,
.feature-sub.handwritten a:focus-visible {
  color: var(--ink);
}

.feature-sub {
  max-width: 560px;
  margin: 0;
  color: var(--ink-muted);
  font-size: 20px;
  font-weight: 500;
  line-height: 30px;
  text-align: center;
  text-wrap: balance;
}

/* Notifications ----------------------------------------------------------- */

/* A native iOS notification in light mode: frosted light grey, the sender's
   picture with the app's badge, name and time on one line, the message
   under it. Sized in em so a container's font size scales the whole card;
   .is-dark is the dark-mode version. */
@font-face {
  font-family: "Caveat";
  src: url("/couple-app-landing/assets/fonts/caveat-400.woff2") format("woff2");
  font-style: normal;
  font-weight: 400;
  font-display: swap;
}

/* A notification is a liquid glass pane (see styles.css), a touch more
   frosted than the buttons, like iOS's own. glass.js lets a mouse pick one
   up and drag it about; let go and it springs home. */
.note {
  --note-fill: rgba(232, 232, 237, 0.78);
  --note-ink: #000000;
  --note-meta: rgba(60, 60, 67, 0.6);
  --glass-tint: rgba(255, 255, 255, 0.34);
  --glass-frost: 3px;
  position: relative;
  display: flex;
  width: 25.4em;
  max-width: calc(100vw - 48px);
  box-sizing: border-box;
  align-items: center;
  gap: 0.73em;
  padding: 0.8em 1.07em 0.8em 0.8em;
  border-radius: 1.6em;
  color: var(--note-ink);
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Arial, sans-serif;
  text-align: left;
  -webkit-font-smoothing: antialiased;
}

.note.is-dark {
  --note-fill: rgba(44, 44, 48, 0.78);
  --note-ink: #ffffff;
  --note-meta: rgba(235, 235, 245, 0.6);
  --glass-tint: rgba(28, 28, 32, 0.55);
}

.note.is-dragging {
  user-select: none;
}

@media (hover: hover) and (pointer: fine) {
  .note {
    cursor: grab;
  }

  .note.is-dragging {
    cursor: grabbing;
  }
}

.note-avatar {
  position: relative;
  flex: 0 0 auto;
  width: 2.67em;
  height: 2.67em;
}

.flip-out .note-avatar {
  width: 3.6em;
  height: 3.6em;
}

.note-avatar img,
.note-monogram {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
}

.note-avatar img.nudge-icon {
  border-radius: 12px;
  object-fit: contain;
}

/* The sending app's icon, on iOS's rounded square. */
.note-avatar img.app-icon {
  border-radius: 22.5%;
}

.flip-out .note-app,
.flip-out .note[hidden] {
  display: none;
}

/* iOS's contact monogram, for a sender with no picture. */
.note-monogram {
  display: flex;
  align-items: center;
  justify-content: center;
  color: #ffffff;
  background: linear-gradient(180deg, #a5abb8, #858994);
  font-family: ui-rounded, "SF Pro Rounded", -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 1.13em;
  font-weight: 600;
}

.note-app {
  position: absolute;
  right: -0.2em;
  bottom: -0.2em;
  width: 1.2em;
  height: 1.2em;
  border-radius: 0.36em;
  box-shadow: 0 0 0 0.1em var(--note-fill);
}

.note-body {
  display: flex;
  min-width: 0;
  flex: 1 1 auto;
  flex-direction: column;
}

.note-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6em;
}

.note-name {
  overflow: hidden;
  font-size: 1em;
  font-weight: 600;
  line-height: 1.33;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.note-time {
  flex: 0 0 auto;
  color: var(--note-meta);
  font-size: 0.87em;
  line-height: 1.33;
}

.note-text {
  font-size: 1em;
  line-height: 1.33;
}

/* The section: a loose pile, newest on top, each a little off-center and
   tilted. They arrive one after another when the section shows. */
.notes {
  position: relative;
  z-index: 1;
  display: flex;
  margin: 0;
  padding: 0;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  font-size: 19px;
  list-style: none;
}

.notes .note {
  transform: translateX(var(--nx, 0px)) rotate(var(--nr, 0deg));
  transition:
    opacity 320ms ease var(--nd, 0ms),
    transform 560ms cubic-bezier(0.2, 1.25, 0.4, 1) var(--nd, 0ms);
}

.motion .flip-out:not(.played) .note {
  opacity: 0;
  transform: translateX(var(--nx, 0px)) translateY(-18px) rotate(var(--nr, 0deg)) scale(0.96);
}

/* The handwritten nudge caption sits under the title; a softly bent pencil
   arrow drops from its middle into
   the pile, so the pile starts just under the arrow rather than a whole
   gap below the title. */
.notes-wrap {
  position: relative;
  z-index: 1;
  margin-top: calc(12px - var(--gap));
}

.pointer-arrow {
  display: block;
  width: 24px;
  height: 40px;
  margin: 0 auto 18px;
  fill: none;
  stroke: #8e8e93;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2;
}

/* Hearts wreathe the nudges as the section arrives, like reactions on a
   live video: home.js sends each one from low under the pile, out around
   its bottom corner, up its side and off the top of the screen. A heart
   shows only while it flies (no waiting seeds, no fading). The layer
   reaches a screen above the section, so they keep rising past its top
   edge and off the top of the screen. */
.hearts {
  position: absolute;
  z-index: 3;
  top: -100vh;
  right: 0;
  bottom: 0;
  left: 0;
  overflow: hidden;
  pointer-events: none;
}

.heart {
  position: absolute;
  top: 0;
  left: 0;
  width: var(--hs);
  height: var(--hs);
  opacity: 0;
  pointer-events: none;
  user-select: none;
  will-change: transform;
}

/* Emoji in the nudges' copy: Microsoft's Fluent 3D set, sitting on the line. */
img.emoji {
  display: inline-block;
  width: 1.2em;
  height: 1.2em;
  margin-left: 0.08em;
  vertical-align: -0.24em;
}

/* In the hero they sit among the stickers, a size smaller. */
.note-seat .note {
  font-size: 13px;
}

.seat.mobile-only {
  display: none;
}

/* Intimacy Lab ------------------------------------------------------------- */

/* A phone section with a line under its title has a phone a little shorter
   on wide screens, so title, line and phone still make one screen: two
   lines of copy (--sub-h) under the canvas, games and questions titles,
   one handwritten line under Intimacy Lab's. */
@media (min-width: 1024px) {
  .feature:has(.device-stage):has(.feature-sub) {
    --phone-h: min(600px, max(420px, calc(100svh - 2.32 * var(--title) - 6px - var(--gap) - 2 * var(--pad) - var(--sub-h, 80px))));
  }

  .feature.lab {
    --sub-h: 56px;
  }
}

/* The live screen, laid over the drawn one's (assets/phones/intimacy-lab.svg:
   a 396 x 846 screen 12 in from the 420 x 870 body, its corners 56 round).
   Everything on it is sized in its own width (cqw), so the app's proportions
   hold at any phone size; a card swiped off goes out at the screen's edge. */
.lab-screen {
  position: absolute;
  inset: 1.379% 2.857%;
  overflow: hidden;
  border-radius: 14.14% / 6.62%;
  container-type: size;
}

/* Below the app's header, from 210 to 676 down the screen; a finger moving
   sideways swipes, up and down still scrolls the page. */
.lab-deck {
  position: absolute;
  top: 24.8%;
  right: 7.07%;
  left: 7.07%;
  height: 55.1%;
  touch-action: pan-y;
}

/* The app's card: white, the title on top and the art filling the rest.
   The cards behind rise and shrink a step each and fade a little, as in the
   app (--i, from lab.js); a fourth waits unseen. */
.lab-card {
  position: absolute;
  z-index: calc(10 - var(--i, 0));
  inset: 0;
  display: flex;
  box-sizing: border-box;
  flex-direction: column;
  align-items: center;
  gap: 2.4cqw;
  padding: 5cqw 5cqw 4cqw;
  overflow: hidden;
  border-radius: 7cqw;
  background: var(--paper);
  box-shadow: 0 3cqw 8cqw rgba(0, 0, 0, 0.45);
  color: var(--ink);
  cursor: grab;
  opacity: calc(1 - var(--i, 0) * 0.2);
  text-align: center;
  transform: translateY(calc(var(--i, 0) * -3.6cqw)) scale(calc(1 - var(--i, 0) * 0.05));
  transform-origin: 50% 0;
  transition: transform 0.35s cubic-bezier(0.2, 0.8, 0.2, 1), opacity 0.35s ease;
  user-select: none;
  -webkit-user-select: none;
}

.lab-card[aria-hidden="true"] {
  cursor: default;
}

.lab-card.is-back {
  opacity: 0;
}

.lab-card.is-dragging {
  cursor: grabbing;
  transition: none;
}

.lab-card.is-leaning {
  transition: transform 0.6s cubic-bezier(0.3, 0, 0.3, 1);
}

/* Off it goes, over the rest. */
.lab-card.is-gone {
  z-index: 20;
  opacity: 0;
  transition: transform 0.42s cubic-bezier(0.4, 0, 0.8, 0.6), opacity 0.42s ease-in;
}

.lab-tag {
  flex: 0 0 auto;
  padding: 1.5cqw 3cqw;
  border-radius: 999px;
  font-size: 2.9cqw;
  font-weight: 600;
  letter-spacing: 0.08em;
  line-height: 1;
  text-transform: uppercase;
}

.lab-card.position .lab-tag {
  color: #3d57f0;
  background: rgba(79, 106, 255, 0.12);
}

.lab-card.dare .lab-tag {
  color: #e8432a;
  background: rgba(255, 107, 74, 0.14);
}

.lab-card .lab-title {
  flex: 0 0 auto;
  margin: 0;
  font-family: var(--display);
  font-size: 5.6cqw;
  font-weight: 600;
  line-height: 1.2;
  text-wrap: balance;
}

.lab-card.dare .lab-title {
  margin-top: 3cqw;
  font-size: 5.3cqw;
}

.lab-art {
  flex: 1 1 0;
  width: 100%;
  min-height: 0;
  object-fit: contain;
  pointer-events: none;
  -webkit-user-drag: none;
}

.lab-emoji {
  display: flex;
  flex: 1 1 0;
  align-items: center;
  justify-content: center;
  gap: 1cqw;
  pointer-events: none;
}

.lab-emoji img {
  width: 20cqw;
  height: auto;
  -webkit-user-drag: none;
}

/* What letting go would say, pressed onto the card like a rubber stamp; it
   shows as the card leans (--yes, --no, --maybe from lab.js). */
.lab-stamp {
  position: absolute;
  top: 5cqw;
  padding: 1.2cqw 2.6cqw 1cqw;
  border: 0.75cqw solid currentColor;
  border-radius: 2.4cqw;
  background: rgba(255, 255, 255, 0.86);
  font-family: var(--display);
  font-size: 6.4cqw;
  font-weight: 700;
  letter-spacing: 0.04em;
  line-height: 1;
  text-transform: uppercase;
  pointer-events: none;
}

.lab-stamp.yes {
  left: 4.5cqw;
  color: #19a25c;
  opacity: var(--yes, 0);
  rotate: -14deg;
}

.lab-stamp.no {
  right: 4.5cqw;
  color: #f0443a;
  opacity: var(--no, 0);
  rotate: 14deg;
}

.lab-stamp.maybe {
  top: auto;
  bottom: 7cqw;
  left: 50%;
  color: #e89a00;
  opacity: var(--maybe, 0);
  rotate: -6deg;
  translate: -50% 0;
}

/* No, maybe and yes, as round glass buttons under the deck, 734 down the
   screen; maybe is the small one. */
.lab-actions {
  position: absolute;
  z-index: 15;
  top: 86.8%;
  right: 0;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5cqw;
  translate: 0 -50%;
}

/* Nearly white, so the glass reads on the dark screen. */
.lab-button {
  --glass-tint: rgba(255, 255, 255, 0.9);
  position: relative;
  display: grid;
  width: 14cqw;
  height: 14cqw;
  padding: 0;
  place-items: center;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.lab-button.maybe {
  width: 11.5cqw;
  height: 11.5cqw;
}

.lab-button svg {
  width: 6.4cqw;
  height: 6.4cqw;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2.6;
}

.lab-button.no {
  color: #f0443a;
}

.lab-button.maybe {
  color: #e89a00;
}

.lab-button.maybe svg {
  width: 5.4cqw;
  height: 5.4cqw;
}

.lab-button.yes {
  color: #ff2d6f;
}

.lab-button.yes svg {
  fill: currentColor;
  stroke: none;
}

.lab-button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* A match, as the app tells it: a glass banner that drops from the Dynamic
   Island for a moment, K's face on the left. */
.lab-match {
  --glass-tint: rgba(255, 255, 255, 0.93);
  --glass-frost: 14px;
  position: absolute;
  z-index: 30;
  top: 6.6%;
  right: 3%;
  left: 3%;
  display: flex;
  align-items: center;
  gap: 3cqw;
  margin: 0;
  padding: 2.6cqw 3.6cqw 2.6cqw 2.6cqw;
  border-radius: 6.5cqw;
  color: var(--ink);
  font-size: 3.6cqw;
  line-height: 1.3;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-140%) scale(0.94);
  transition: opacity 0.25s ease, transform 0.45s cubic-bezier(0.2, 0.9, 0.3, 1.2);
}

.lab-match.is-shown {
  opacity: 1;
  transform: none;
}

.lab-match-face {
  flex: 0 0 auto;
  width: 10.5cqw;
  height: 10.5cqw;
  border-radius: 50%;
  object-fit: cover;
}

.lab-match strong {
  display: flex;
  align-items: center;
  gap: 1cqw;
  font-weight: 600;
}

.lab-match strong img {
  width: 4.4cqw;
  height: 4.4cqw;
}

@media (prefers-reduced-motion: reduce) {
  .lab-card,
  .lab-card.is-gone,
  .lab-match {
    transition: none;
  }
}

/* Team ------------------------------------------------------------------------ */

.polaroids {
  display: flex;
  justify-content: center;
  gap: clamp(24px, 3vw, 48px);
}

/* The app's polaroid: paper-white frame, deep bottom band with the name in
   Caveat, the time-lapse polaroid's handwriting. */
.polaroid {
  --pw: clamp(220px, 22vw, 320px);
  width: var(--pw);
  box-sizing: border-box;
  margin: 0;
  padding: calc(var(--pw) * 0.06) calc(var(--pw) * 0.06) 0;
  background: #ffffff;
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.06),
    0 1px 2px rgba(0, 0, 0, 0.06);
  transform: rotate(var(--pr, 0deg));
}

.polaroid-photo {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  background: #e2e3e5;
  object-fit: cover;
}

.polaroid figcaption {
  padding: 0.35em 0 0.45em;
  font-family: "Caveat", cursive;
  font-size: clamp(26px, 2.4vw, 34px);
  line-height: 1.2;
  text-align: center;
}

/* The one print of the two of us, bigger, with a strip of washi tape across
   its top edge: translucent marigold, finely striped, both ends torn. */
.polaroid.taped {
  --pw: clamp(260px, 26vw, 360px);
  position: relative;
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.06),
    0 2px 4px rgba(0, 0, 0, 0.06),
    0 18px 40px -12px rgba(58, 48, 40, 0.22);
}

.polaroid .caption-heart {
  color: #ff6b4a;
}

.polaroid.taped::before {
  position: absolute;
  z-index: 1;
  top: calc(var(--pw) * -0.06);
  left: 50%;
  width: 42%;
  height: calc(var(--pw) * 0.12);
  background:
    repeating-linear-gradient(-45deg, rgba(255, 255, 255, 0.28) 0 5px, rgba(255, 255, 255, 0) 5px 11px),
    rgba(255, 192, 46, 0.72);
  box-shadow: 0 1px 2px rgba(58, 48, 40, 0.12);
  clip-path: polygon(0 6%, 4% 0, 2% 22%, 5% 40%, 1% 58%, 4% 78%, 0 100%, 100% 94%, 97% 76%, 100% 58%, 96% 40%, 99% 22%, 96% 6%, 100% 0);
  content: "";
  transform: translateX(-50%) rotate(-4deg);
}

/* Features ----------------------------------------------------------------- */

/* What there is to do together, in a 2 x 2 grid of light grey modules,
   each marked by a brand shape, wide enough that every title holds one
   line. */
.promises {
  display: grid;
  width: min(880px, 100%);
  margin: 0;
  padding: 0;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
  list-style: none;
}

/* Each card is to play with: hovered, it lifts and its shape spins (the
   page's spinner sets --r); tapped, the shape springs (.is-popped, from
   home.js) and plays its note (sounds.js). */
.promise {
  display: flex;
  box-sizing: border-box;
  flex-direction: column;
  gap: 8px;
  padding: 28px 32px 30px;
  border-radius: 28px;
  background: #f4f4f5;
  cursor: pointer;
  text-align: left;
  transition: background-color 0.25s ease, transform 0.35s cubic-bezier(0.2, 0.9, 0.3, 1.3);
  -webkit-tap-highlight-color: transparent;
}

.promise:hover {
  background: #efeff1;
  transform: translateY(-4px);
}

.promise:active {
  transform: translateY(-1px) scale(0.985);
}

.promise img {
  width: 40px;
  height: auto;
  margin-bottom: 14px;
  rotate: var(--r, 0deg);
  user-select: none;
  -webkit-user-drag: none;
}

.promise img.is-popped {
  animation: promise-pop 0.55s cubic-bezier(0.3, 0.7, 0.3, 1);
}

@keyframes promise-pop {
  35% {
    transform: scale(1.32) rotate(18deg);
  }

  70% {
    transform: scale(0.92) rotate(-6deg);
  }

  100% {
    transform: none;
  }
}

.promise strong {
  font-family: var(--display);
  font-size: 24px;
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.01em;
}

.promise span {
  color: var(--ink-muted);
  font-size: 17px;
  font-weight: 500;
  line-height: 24px;
}

/* With the keychain running (keychain.js), its glass bubbles take the
   cards' place, hanging from just under the title, and the cards stay in
   the page for screen readers only. Under the bunch, the caption names the
   bubble under the pointer or in hand. */
.keychain {
  display: none;
}

.has-keychain .keychain {
  position: relative;
  display: flex;
  width: min(820px, 100%);
  flex-direction: column;
  align-items: center;
  gap: 6px;
  margin-top: calc(var(--gap) * -0.6);
}

.keychain-slot {
  width: 100%;
  height: clamp(420px, min(66vh, 112vw), 620px);
}

html.keychain-over,
html.keychain-over * {
  cursor: grab !important;
}

html.keychain-holding,
html.keychain-holding * {
  cursor: grabbing !important;
  user-select: none;
}

.keychain-caption {
  display: flex;
  max-width: 440px;
  min-height: 4.2em;
  margin: 0;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  opacity: 0;
  text-align: center;
}

.keychain-caption.is-in {
  animation: keychain-caption 0.4s ease both;
}

@keyframes keychain-caption {
  from {
    opacity: 0;
    transform: translateY(6px);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

.keychain-caption strong {
  font-family: var(--display);
  font-size: 24px;
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.01em;
}

.keychain-caption span {
  color: var(--ink-muted);
  font-size: 17px;
  font-weight: 500;
  line-height: 24px;
}

.has-keychain .promises {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Get the app, in the corner -------------------------------------------- */

/* Pops in from its own center once the hero's button has left the screen:
   from 90% with a very quick fade and a little spring. With a mouse, the
   capsule grows into the hero's QR card, anchored at its top right. */
.sticky-cta {
  position: fixed;
  z-index: 50;
  top: 20px;
  right: 24px;
  width: 146px;
  height: 44px;
  pointer-events: none;
}

.sticky-cta-button {
  position: absolute;
  top: 0;
  right: 0;
  display: flex;
  width: 146px;
  height: 44px;
  box-sizing: border-box;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: 1000px;
  /* Glass a little whiter than the hero's: the whole page scrolls under it. */
  --glass-tint: rgba(255, 255, 255, 0.6);
  color: var(--ink);
  text-decoration: none;
  isolation: isolate;
  opacity: 0;
  transform: scale(0.9);
  /* Settling back into the capsule: smooth, no bounce. */
  transition:
    opacity 90ms linear,
    transform 280ms cubic-bezier(0.34, 1.56, 0.64, 1),
    width 320ms cubic-bezier(0.32, 0.72, 0, 1),
    height 320ms cubic-bezier(0.32, 0.72, 0, 1),
    border-radius 280ms cubic-bezier(0.32, 0.72, 0, 1),
    box-shadow 240ms ease;
}

.sticky-cta.shown {
  pointer-events: auto;
}

.sticky-cta.shown .sticky-cta-button {
  opacity: 1;
  transform: scale(1);
}

.sticky-cta-button:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 3px;
}

.sticky-cta-text {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 16px;
  font-weight: 600;
  line-height: 20px;
  white-space: nowrap;
  /* The label comes back once the card has mostly closed, sharpening as it
     fades in. */
  transition:
    opacity 180ms ease 130ms,
    filter 220ms ease 130ms;
}

.sticky-cta-text svg {
  width: 16px;
  height: 16px;
  margin-top: -2px;
}

.sticky-cta-qr {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 24px;
  opacity: 0;
  pointer-events: none;
  filter: blur(6px);
  transform: scale(0.92);
  /* The code leaves at once, softening, before the card closes over it. */
  transition:
    opacity 100ms ease,
    filter 160ms ease,
    transform 200ms ease;
}

.sticky-cta-qr img {
  display: block;
  width: 184px;
  height: 184px;
}

@media (hover: hover) and (pointer: fine) {
  .sticky-cta.shown:hover .sticky-cta-button,
  .sticky-cta.shown .sticky-cta-button:focus-visible {
    width: 264px;
    height: 304px;
    border-radius: 28px;
    transition:
      opacity 90ms linear,
      transform 280ms cubic-bezier(0.34, 1.56, 0.64, 1),
      width 280ms cubic-bezier(0.34, 1.56, 0.64, 1),
      height 280ms cubic-bezier(0.34, 1.56, 0.64, 1),
      border-radius 220ms cubic-bezier(0.22, 1, 0.36, 1),
      box-shadow 280ms ease;
    --glass-tint: rgba(255, 255, 255, 0.55);
    --glass-frost: 14px;
    --glass-lift:
      0 60px 140px -20px rgba(34, 34, 34, 0.32),
      0 30px 80px -10px rgba(34, 34, 34, 0.2),
      0 8px 24px rgba(34, 34, 34, 0.16);
  }

  .sticky-cta.shown:hover .sticky-cta-text,
  .sticky-cta.shown .sticky-cta-button:focus-visible .sticky-cta-text {
    opacity: 0;
    filter: blur(4px);
    transition:
      opacity 100ms ease,
      filter 120ms ease;
  }

  .sticky-cta.shown:hover .sticky-cta-qr,
  .sticky-cta.shown .sticky-cta-button:focus-visible .sticky-cta-qr {
    opacity: 1;
    filter: blur(0);
    transform: scale(1);
    transition:
      opacity 160ms ease 90ms,
      filter 200ms ease 90ms,
      transform 260ms cubic-bezier(0.34, 1.56, 0.64, 1) 60ms;
  }

  /* The hero's and the closing section's buttons morph the same way. */
  .cta {
    transition:
      width 320ms cubic-bezier(0.32, 0.72, 0, 1),
      height 320ms cubic-bezier(0.32, 0.72, 0, 1),
      border-radius 280ms cubic-bezier(0.32, 0.72, 0, 1),
      box-shadow 240ms ease;
  }

  .cta-text {
    transition:
      opacity 180ms ease 130ms,
      filter 220ms ease 130ms;
  }

  .cta-qr {
    filter: blur(6px);
    transform: scale(0.92);
    transition:
      opacity 100ms ease,
      filter 160ms ease,
      transform 200ms ease;
  }

  .cta-shell:hover .cta,
  .cta-shell:has(:focus-visible) .cta {
    transition:
      width 280ms cubic-bezier(0.34, 1.56, 0.64, 1),
      height 280ms cubic-bezier(0.34, 1.56, 0.64, 1),
      border-radius 220ms cubic-bezier(0.22, 1, 0.36, 1),
      box-shadow 280ms ease;
  }

  .cta-shell:hover .cta-text,
  .cta-shell:has(:focus-visible) .cta-text {
    filter: blur(4px);
    transition:
      opacity 100ms ease,
      filter 120ms ease;
  }

  .cta-shell:hover .cta-qr,
  .cta-shell:has(:focus-visible) .cta-qr {
    filter: blur(0);
    transform: scale(1);
    transition:
      opacity 160ms ease 90ms,
      filter 200ms ease 90ms,
      transform 260ms cubic-bezier(0.34, 1.56, 0.64, 1) 60ms;
  }
}

/* FAQ ------------------------------------------------------------------------ */

/* After graphicalui.com: a left-set title over a 768px column, roomy rows
   split by hairlines, the question in ink with a thin chevron, the answer
   in grey. Native details; the open one's chevron turns over. */
.faq {
  position: relative;
  isolation: isolate;
  display: flex;
  box-sizing: border-box;
  justify-content: center;
  padding: clamp(40px, 8vh, 80px) 24px;
}

.faq-column {
  width: min(768px, 100%);
}

.faq h2 {
  margin: 0 0 16px;
  font-family: var(--display);
  font-size: var(--section-title);
  font-weight: 600;
  line-height: 1.16;
  letter-spacing: -0.01em;
}

.faq-intro {
  max-width: 680px;
  margin: 0 0 clamp(40px, 6vh, 64px);
  color: var(--ink-muted);
  font-size: 20px;
  line-height: 1.5;
}

.faq details {
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}

.faq details:last-child {
  border-bottom: 0;
}

.faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 32px 0;
  cursor: pointer;
  font-size: 20px;
  font-weight: 500;
  line-height: 28px;
  letter-spacing: -0.01em;
  list-style: none;
}

.faq details:first-child summary {
  padding-top: 0;
}

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

.faq summary:focus-visible {
  border-radius: 6px;
  outline: 2px solid var(--accent);
  outline-offset: 4px;
}

.faq summary svg {
  width: 24px;
  height: 24px;
  flex: 0 0 auto;
  fill: none;
  stroke: #90908d;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.6;
  transition: transform 240ms ease;
}

.faq details[open] summary svg {
  transform: rotate(180deg);
}

.faq details p {
  margin: 0;
  padding: 0 48px 32px 0;
  color: var(--ink-muted);
  font-size: 20px;
  font-weight: 500;
  line-height: 32px;
}

/* Where the browser can animate details, the answer slides open. */
@supports (interpolate-size: allow-keywords) {
  .faq {
    interpolate-size: allow-keywords;
  }

  .faq details::details-content {
    height: 0;
    overflow: clip;
    transition:
      height 280ms cubic-bezier(0.2, 0.8, 0.2, 1),
      content-visibility 280ms allow-discrete;
  }

  .faq details[open]::details-content {
    height: auto;
  }
}

.faq-mail {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  color: var(--accent);
  text-decoration: none;
  white-space: nowrap;
}

.faq-mail:hover {
  text-decoration: underline;
}

.faq-mail svg {
  width: 20px;
  height: 20px;
  align-self: center;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.6;
}

/* The close ---------------------------------------------------------------- */

.closing {
  position: relative;
  height: calc(100vh - var(--footer-height));
  height: calc(100svh - var(--footer-height));
  min-height: 496px;
  overflow: hidden;
}

.closing-copy {
  position: absolute;
  z-index: 5;
  top: 50%;
  left: 50%;
  display: flex;
  width: min(800px, calc(100% - 48px));
  flex-direction: column;
  align-items: center;
  gap: 24px;
  transform: translate(-50%, -50%);
  text-align: center;
}

.closing h2 {
  font-size: clamp(52px, min(5.3vw, 8.8vh), 80px);
  line-height: calc(1.04em + 2px);
  letter-spacing: -0.01em;
  word-spacing: 0.05em;
}

/* Its spray holds until the section arrives, then deals in once. */
.motion .closing:not(.played) .pop {
  animation-play-state: paused;
}

/* A kitten peeks over the closing button (index.html, .cta-cat), drawn in
   the pencil grey of the handwritten lines. 52px wide, its 64-unit drawing
   meets the button's top edge 40 units down, 32.5px; its head is cut there.
   When the walking cat has leapt into the button (cat.js adds .cat-home),
   the head pops up out of it with a little overshoot and the paws land on
   the edge; now and then it blinks. Scrolled back up, it ducks again. */
.cta-cat {
  position: absolute;
  z-index: 2;
  top: -32.5px;
  left: calc(50% + 6px);
  width: 52px;
  height: auto;
  overflow: visible;
  pointer-events: none;
}

.cta-cat .cat-line {
  fill: none;
  stroke: #8e8e93;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2.3;
}

.cta-cat .cat-fill {
  fill: var(--paper);
}

.motion .cta-cat .cat-head {
  transition: transform 0.55s cubic-bezier(0.3, 1.6, 0.5, 1) 0.12s;
}

.motion .cta-cat .cat-paws {
  transition:
    opacity 0.2s ease 0.4s,
    transform 0.35s cubic-bezier(0.3, 1.6, 0.5, 1) 0.4s;
}

.motion .closing:not(.cat-home) .cta-cat .cat-head {
  transform: translateY(38px);
  transition-delay: 0.1s;
}

.motion .closing:not(.cat-home) .cta-cat .cat-paws {
  opacity: 0;
  transform: translateY(4px);
  transition-delay: 0s;
}

.motion .cta-cat .cat-eyes,
.cat-walk .cat-eyes {
  transform-box: fill-box;
  transform-origin: center;
  animation: cat-blink 4.2s ease-in-out 2.4s infinite;
}

@keyframes cat-blink {
  0%,
  93%,
  100% {
    transform: scaleY(1);
  }

  96.5% {
    transform: scaleY(0.12);
  }
}

/* With a mouse the button grows into the QR card, upward: the kitten rides
   its top edge, 238px up, on the card's own spring. */
@media (hover: hover) and (pointer: fine) {
  .cta-cat {
    transition: transform 280ms cubic-bezier(0.34, 1.56, 0.64, 1);
  }

  .cta-shell:hover .cta-cat,
  .cta-shell:has(:focus-visible) .cta-cat {
    transform: translateY(-238px);
  }
}

/* The cat on the scroll bar (index.html, .cat-walker; cat.js moves her).
   A 27 x 48 box at the right edge, beside the scroll bar; inside it the
   drawing (78 x 44 at 48 x 27, standing on y 40) is turned a quarter so her
   feet are on the edge and her head points the way she walks: up, or with
   --face at -1, mirrored, down. Turning round squashes her through the
   middle. cat.js sets her place (transform), her legs' swing (--a, --b)
   and a little bob (--bob) from the scroll. Without motion she stays home
   in the button. */
.cat-walker {
  display: none;
}

.motion .cat-walker {
  position: fixed;
  z-index: 40;
  top: 0;
  right: 0;
  display: block;
  width: 27px;
  height: 48px;
  pointer-events: none;
  transition: opacity 0.16s ease;
  will-change: transform;
}

.cat-walker.is-home {
  opacity: 0;
}

.cat-walk {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 48px;
  height: 27px;
  overflow: visible;
  transform: translate(-50%, -50%) rotate(-90deg) scaleX(var(--face, -1));
  transition: transform 0.22s ease;
}

.cat-walk .cat-line {
  fill: none;
  stroke: #8e8e93;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2.6;
}

.cat-walk .cat-thin {
  stroke-width: 1.3;
}

.cat-walk .cat-tail {
  stroke-width: 3;
}

.cat-walk .cat-fill {
  fill: var(--paper);
}

/* The far legs, behind her: a shade grey, their outline paler. */
.cat-walk .cat-far {
  fill: #f1f1f3;
  stroke-opacity: 0.6;
}

.cat-walk .leg-a {
  rotate: var(--a, 0deg);
}

.cat-walk .leg-b {
  rotate: var(--b, 0deg);
}

.cat-walk .cat-upper {
  translate: 0 var(--bob, 0px);
}

/* As she walks her head tilts from the neck and her tail swings from its
   root (--tilt, --tail from cat.js), settling straight when she stops. */
.cat-walk .cat-head {
  transform-origin: 49px 22px;
  rotate: var(--tilt, 0deg);
  transition: rotate 0.18s ease-out;
}

.cat-walk .cat-swish {
  transform-origin: 21.5px 24px;
  rotate: var(--tail, 0deg);
  transition: rotate 0.18s ease-out;
}

/* Mid-leap: front legs reaching ahead, back legs pushed out behind. */
.cat-walker.is-leaping .leg-front {
  rotate: -32deg;
}

.cat-walker.is-leaping .leg-back {
  rotate: 32deg;
}

/* Her tail also sways on its own (the drawing's <animate>). */

:root {
  --footer-height: 72px;
}

/* The footer: the app's name on the left, the main site's pages (the same
   three its own footer links) in the middle, the year on the right. The
   closing section leaves exactly its height free on the screen. */
.page-footer {
  display: grid;
  height: var(--footer-height);
  box-sizing: border-box;
  align-items: center;
  grid-template-columns: 1fr auto 1fr;
  gap: 24px;
  padding: 0 48px;
  border-top: 1px solid #ececee;
  font-size: 15px;
  font-weight: 500;
  line-height: 20px;
}

.page-footer a {
  color: var(--ink-muted);
  text-decoration: none;
}

.page-footer a:hover,
.page-footer a:focus-visible {
  color: var(--accent);
}

.page-footer .footer-brand {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  justify-self: start;
  color: var(--ink);
  font-family: var(--display);
  font-size: 17px;
  font-weight: 700;
}

.footer-brand img {
  width: 26px;
  height: 26px;
  border-radius: 22.5%;
  box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.08);
}

/* The app icon wiggles hello under the pointer. */
.footer-brand:hover img {
  animation: icon-wiggle 0.5s ease-in-out;
}

@keyframes icon-wiggle {
  25% {
    transform: rotate(-12deg) scale(1.08);
  }

  60% {
    transform: rotate(9deg) scale(1.08);
  }

  100% {
    transform: none;
  }
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px 28px;
}

.footer-note {
  margin: 0;
  justify-self: end;
  color: var(--ink-muted);
  font-size: 13px;
}

/* Phones and small tablets ---------------------------------------------- */

@media (max-width: 1023px) {
  h1.statement,
  .closing h2 {
    font-size: min(48px, calc((100vw - 48px) / 9.5));
  }

  /* On phones a section grows to hold its content with real room above and
     below, rather than being squeezed into one screen: a title never sits
     tight under the corner button. The phone is two thirds of the screen
     wide. */
  .feature {
    --gap: 52px;
    --pad: 96px;
    --phone-h: min(600px, 137vw);
    /* At least a full screen (with Safari's toolbar tucked away), the
       content centered, so a short section never shows the next title. */
    min-height: 100vh;
    min-height: 100lvh;
  }

  .seat.mobile-only {
    display: block;
  }

  :root {
    --section-title: clamp(28px, 8.2vw, 34px);
  }

  .hero p {
    line-height: 26px;
  }

  /* The hero's copy sits a hair below center, under the heavier top of
     the spray. */
  .hero {
    top: calc(50% + 16px);
  }

  h1.statement {
    line-height: calc(1.06em + 2px);
  }

  /* Phones show the photo stickers baked with their margin and shadow
     (assets/cutouts/phone/, from tools/bake-stickers.mjs) instead of
     running the #sticker filter, which iOS draws on the CPU whenever a
     sticker paints. Each file holds 20px of room on every side at 118px
     tall; the negative margin hands it back, so the seat still centers on
     the photo. */
  .seat.cutout .sticker {
    height: calc(var(--mh, var(--h)) * 158 / 118);
    margin: calc(var(--mh, var(--h)) * -20 / 118);
    filter: none;
  }

  .feature-sub {
    max-width: 340px;
    font-size: 17px;
    line-height: 26px;
  }

  .feature-sub.handwritten {
    font-size: 24px;
    line-height: 1.15;
  }

  .note {
    max-width: calc(100vw - 64px);
  }

  /* Hearts show through the frosted cards less on a narrow screen. */
  .flip-out .note {
    --note-fill: rgba(232, 232, 237, 0.92);
    --glass-tint: rgba(255, 255, 255, 0.72);
    --glass-frost: 10px;
  }

  .notes {
    gap: 12px;
    font-size: 14px;
  }


  /* On phones they stack, the shape beside the words. */
  .promises {
    grid-template-columns: minmax(0, 1fr);
    gap: 10px;
  }

  .promise {
    display: grid;
    grid-template-columns: 32px minmax(0, 1fr);
    align-items: center;
    gap: 2px 14px;
    padding: 16px 18px;
    border-radius: 22px;
  }

  .promise img {
    grid-row: span 2;
    width: 32px;
    margin: 0;
  }

  .promise strong {
    font-size: 18px;
  }

  .promise span {
    font-size: 14px;
    line-height: 20px;
  }

  .sticky-cta {
    top: 14px;
    right: 16px;
  }

  .sticky-cta,
  .sticky-cta-button {
    width: 136px;
    height: 40px;
  }

  .sticky-cta-text {
    font-size: 15px;
  }

  .notes .note {
    transform: translateX(calc(var(--nx, 0px) * 0.2)) rotate(calc(var(--nr, 0deg) * 0.6));
  }

  .pointer-arrow {
    width: 20px;
    height: 33px;
    margin-bottom: 15px;
  }

  .motion .flip-out:not(.played) .note {
    transform: translateX(calc(var(--nx, 0px) * 0.2)) translateY(-18px) rotate(calc(var(--nr, 0deg) * 0.6)) scale(0.96);
  }

  /* A loose pile of prints, big enough for faces. Each lies on the next,
     overlapping only its white top border, so every name shows. */
  .polaroids {
    flex-direction: column;
    align-items: center;
    gap: 0;
  }

  .polaroid {
    --pw: 40vw;
    margin: -3vw 0 0;
  }

  .polaroid:first-child {
    z-index: 3;
    margin-top: 0;
  }

  .polaroid:nth-child(2) {
    z-index: 2;
  }

  .polaroid:nth-child(odd) {
    translate: -12vw 0;
  }

  .polaroid:nth-child(even) {
    translate: 12vw 0;
  }

  .polaroid figcaption {
    font-size: 22px;
  }

  .polaroid.taped {
    --pw: min(68vw, 320px);
    translate: none;
  }

  .faq {
    padding: 96px 24px 64px;
  }

  .faq h2 {
    margin-bottom: 48px;
  }

  .faq summary {
    gap: 16px;
    padding: 24px 0;
    font-size: 18px;
    line-height: 24px;
  }

  .faq details p {
    margin-top: 0;
    padding: 0 0 24px;
    font-size: 17px;
    line-height: 26px;
  }

  /* Stacked and centered: name, pages (up to three rows of them), year,
     with room between the top line and the name. */
  :root {
    --footer-height: 214px;
  }

  .page-footer {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 14px;
    padding: 30px 24px max(32px, env(safe-area-inset-bottom));
  }

  .page-footer .footer-brand,
  .footer-note {
    justify-self: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .promise,
  .promise:hover,
  .promise:active {
    transform: none;
  }

  .promise img.is-popped,
  .footer-brand:hover img {
    animation: none;
  }

  .sticky-cta-button,
  .sticky-cta-text,
  .sticky-cta-qr {
    transition: none;
  }

  .landing-page .seat {
    translate: none;
    opacity: 1;
  }
}

/* The green paint (paint.js): the shapes' paint shows only while it runs;
   the questions' clover is then drawn by its paint layer, which can empty.
   The flying flower and the drips sit behind the page's text, shapes and
   phones (the body keeps them above its own white). */
.paint-shape {
  visibility: hidden;
}

.paint-on .paint-shape {
  visibility: visible;
}

.paint-on #questions .device-shape:not(.paint-shape) {
  visibility: hidden;
}

html.paint-on body {
  isolation: isolate;
}
