/* ==========================================================================
   Zome — zome-site
   Phi Labs Technology Ltd

   One stylesheet, no build step, no framework. It is served straight off
   nginx, which is why it is written to be read rather than compiled.

   Type: Instrument Serif (display) + Switzer (text), both self-hosted from
   fonts/. Inter and Nunito remain in fonts/ for the legal pages, which are
   set in the app's own faces and are not touched by this file.

   Colour: the app's palette, verbatim. --ink and --blue are the same values
   ZomeColor.ink and ZomeColor.accentBlue resolve to in the iOS build, so the
   site and the product cannot drift apart.
   ========================================================================== */

/* ---------------------------------------------------------------- fonts */

@font-face {
  font-family: 'Instrument Serif';
  src: url('../fonts/instrument-serif-400.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Nunito';
  src: url('../fonts/nunito-900.woff2') format('woff2');
  font-weight: 900;
  font-display: swap;
}
@font-face {
  font-family: 'Switzer';
  src: url('../fonts/switzer-400.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Switzer';
  src: url('../fonts/switzer-500.woff2') format('woff2');
  font-weight: 500;
  font-display: swap;
}
@font-face {
  font-family: 'Switzer';
  src: url('../fonts/switzer-600.woff2') format('woff2');
  font-weight: 600;
  font-display: swap;
}

/* ---------------------------------------------------------------- tokens */

:root {
  /* Brand. Taken from ios/Zome/DesignSystem/Generated/DesignTokens.swift. */
  --ink: #001C35;
  --blue: #0088FF;
  --deep: #005299;
  --sky: #E7F6FF;

  /* Neutrals, biased a few degrees toward the brand blue rather than a flat
     grey, so the page reads as one family instead of brand colour dropped
     onto stock chrome. */
  /* Sand, from the map. ZomeColor.dayDone and .sandWall are the footprint
     and the wall pressed into it; the third is the unwalked ground. This is
     the one part of Zome's world nothing else in health looks like, and it
     was sitting unused while this site was built out of cool grey. */
  --sand: #E5B06E;
  --sand-wall: #AC7F5E;
  --sand-ground: #F6EFE4;

  --paper: #FFFFFF;
  --paper-2: #F7FAFC;
  --paper-3: #EFF5FA;
  --rule: #E1EBF3;
  --slate: #5B7086;
  --slate-2: #7C8FA3;

  /* The five capability accents, and a text-safe darkening of each.
     Yellow, green and cyan fail WCAG AA on white at full strength — the app
     records the same three in ZomeColor.accentsUnsafeForText — so anything
     set in them uses the -ink variant. Every pair below is >= 4.5:1. */
  --a-assess: #0088FF;      --a-assess-ink: #0076DE;   /* 4.52:1 */
  --a-path: #FFCC00;        --a-path-ink: #8F7200;     /* 4.59:1 */
  --a-goals: #34C759;       --a-goals-ink: #23873D;    /* 4.56:1 */
  --a-zomy: #6155F5;        --a-zomy-ink: #6155F5;     /* 5.09:1 */
  --a-data: #FF383C;        --a-data-ink: #E03135;     /* 4.51:1 */

  /* Type */
  /* The display face, and the four values that go with it.
     Nunito Black, which is what sets every heading and every number inside
     the app, so the site and the product now speak in the same voice.

     Instrument Serif is still loaded and is one edit away: swap --display
     back and take the other three to 400, -0.045em and 1.0, which are the
     metrics a serif at this size wants. They are separate values precisely
     because the two faces do not want the same ones: Nunito has a far larger
     x-height and rounder forms, so it needs looser tracking and more leading
     to read as a headline rather than as a wall. */
  --display: 'Nunito', 'Avenir Next', Avenir, 'Trebuchet MS', system-ui, sans-serif;
  --display-weight: 900;
  --display-track: -0.03em;
  --display-lh: 1.06;
  --text: 'Switzer', 'Inter', system-ui, -apple-system, sans-serif;

  /* Shape */
  /* A capsule, because ZomePillButtonStyle is a capsule. The 10px radius and
     the blue halo under it were the reference site's button, not Zome's. */
  --r-btn: 999px;
  --r-card: 20px;
  --r-pill: 999px;

  /* The solid pill carries no shadow in the app. Neither does it here. */
  --lift-plain: 0 1px 3px rgba(0, 28, 53, .05);
  --lift-card: 0 1px 2px rgba(0, 28, 53, .04), 0 8px 24px rgba(0, 28, 53, .06);

  /* Layout */
  /* Pool has no max-width container at all: at 1440 its grid spans 1312,
     which is the window less 64 either side. The 1200 box here was holding
     content 140 in from the edge and making the page feel smaller than the
     window it is in. */
  --wide: none;
  --prose: 760px;
  /* 64 matches theirs on a desktop and is absurd on a phone: it left 247px of
     usable width on a 375 screen. Narrowed below 900. */
  --gutter: 64px;

}

/* ---------------------------------------------------------------- reset */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* The anchor nav lands under a floating header; this keeps headings clear
     of it without a scroll-offset hack in JS. */
  scroll-padding-top: 110px;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--text);
  font-size: 17px;
  line-height: 1.6;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, video, svg { max-width: 100%; height: auto; display: block; }
a { color: inherit; }
button { font: inherit; color: inherit; }

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: var(--display-weight);
  line-height: var(--display-lh);
  letter-spacing: var(--display-track);
  margin: 0;
  text-wrap: balance;
}

p { margin: 0; }

/* Visible keyboard focus everywhere. The reference site ships zero focus
   rules and relies on the browser default; that is one stylesheet reset away
   from having none at all. */
:focus-visible {
  outline: 3px solid var(--blue);
  outline-offset: 3px;
  border-radius: 4px;
}

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip {
  position: absolute; left: 50%; top: 8px;
  transform: translate(-50%, -200%);
  background: var(--ink); color: #fff;
  padding: 10px 18px; border-radius: var(--r-pill);
  z-index: 200; text-decoration: none; font-weight: 600;
}
.skip:focus { transform: translate(-50%, 0); }

/* ---------------------------------------------------------------- layout */

.wrap {
  width: 100%;
  max-width: var(--wide);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
/* Reading measures stay reading measures: only the page's edges move out.
   The questions are not one of them. They share the story's grid so that the
   heading lands on the page margin and the list lands on the panel, and a
   1240 cap here pushed both inboard by a hundred points. */
.doc-wrap { max-width: 1240px; margin-inline: auto; }

section { position: relative; }

.band { padding-block: 96px; }
.band--tight { padding-block: 64px; }

/* Ranged left, like everything else. Centring every heading down one column
   is what makes a page look generated: nothing has a reason to be where it
   is, so everything goes in the middle. */
.lede { max-width: 20ch; }
.lede p { color: var(--slate); font-size: 18px; margin-top: 16px; max-width: 42ch; }

.eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--text);
  font-size: 13px; font-weight: 600;
  letter-spacing: .04em; text-transform: uppercase;
  padding: 7px 14px;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--accent, var(--blue)) 12%, transparent);
  color: var(--accent-ink, var(--deep));
  margin-bottom: 20px;
}
.eyebrow::before {
  content: '';
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent, var(--blue));
}

/* One headline size for the whole page.
   Theirs runs every statement as an h1 at a flat 48: the hero, each step of
   the story and the closing line are all the same size, and there is not a
   single h2 anywhere on the page. Ours had three sizes, which made the hero
   shout and each section after it sound quieter than the last. Now a headline
   is a headline wherever it is.

   h3 stays small because it is not a headline here: it labels a research
   card. The footer's is smaller still, and set in the text face. */
h1, h2 { font-size: clamp(40px, 5.4vw, 62px); }
h3 { font-size: clamp(22px, 2.4vw, 26px); }

/* ---------------------------------------------------------------- buttons */

/* ZomeControl.height is 58, the label is bold, the shape is a capsule and the
   press is scale .97 with the fill dropped to .88 opacity. All four are the
   app's, so a person who has seen one recognises the other. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  font-family: var(--text);
  font-size: 17px; font-weight: 600;
  min-height: 58px;
  padding: 0 34px;
  border-radius: var(--r-btn);
  border: 0; cursor: pointer;
  text-decoration: none;
  transition: transform .16s cubic-bezier(.23, 1, .32, 1),
              opacity .16s ease, background-color .16s ease;
}
.btn:active { transform: scale(.97); opacity: .88; }

.btn--brand { background: var(--blue); color: #fff; }
.btn--brand:hover { background: #0A93FF; }

.btn--plain {
  background: var(--paper); color: var(--ink);
  box-shadow: var(--lift-plain);
  border: 1px solid rgba(0, 28, 53, .12);
}
.btn--plain:hover { background: var(--paper-2); }

.btn--ghost {
  background: rgba(255, 255, 255, .1); color: #fff;
  border: 1px solid rgba(255, 255, 255, .22);
}
.btn--ghost:hover { background: rgba(255, 255, 255, .18); }

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

.announce {
  background: linear-gradient(90deg, var(--deep), var(--blue));
  color: #fff;
  text-align: center;
  font-size: 14px; font-weight: 500;
  padding: 10px var(--gutter);
}
.announce a { color: #fff; text-decoration: underline; text-underline-offset: 3px; }

.site-head {
  position: sticky; top: 0;
  z-index: 100;
  padding: 12px var(--gutter);
}

/* Progressive blur, the way theirs is built.
   One flat backdrop-filter gives a pane of frosted glass with a hard edge
   along the bottom, and the edge is the thing you notice. This is eight
   layers instead, each blurring twice as hard as the last, and each masked to
   its own band of the strip, so the blur ramps from almost nothing at the
   bottom to 8px at the top and has no edge anywhere. A white gradient sits
   under them to carry the fade. */
.head-blur {
  position: absolute; inset: 0 0 auto 0;
  height: 148px;
  z-index: -1;
  pointer-events: none;
}
.head-blur span {
  position: absolute; inset: 0;
  backdrop-filter: blur(var(--b));
  -webkit-backdrop-filter: blur(var(--b));
  -webkit-mask-image: linear-gradient(to top,
    rgba(0, 0, 0, 0) var(--m0), #000 var(--m1), #000 100%);
          mask-image: linear-gradient(to top,
    rgba(0, 0, 0, 0) var(--m0), #000 var(--m1), #000 100%);
}
.head-blur::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(to bottom,
    rgba(255, 255, 255, .92) 0%, rgba(255, 255, 255, .72) 42%, rgba(255, 255, 255, 0) 100%);
}

/* Almost nothing. A mark, two or three quiet links, one thing to press.
   The dark floating pill that was here is a device every product site is
   using this year, and it announced itself louder than the headline under
   it. */
.nav {
  max-width: var(--wide); margin-inline: auto;
  display: flex; align-items: center; gap: 10px;
}

/* The mark on its own, at the height of the button opposite it, so the two
   ends of the bar are the same weight. The wordmark beside it was saying a
   name the mark already says. */
.nav__brand { display: flex; align-items: center; text-decoration: none; }
.nav__brand img { width: 58px; height: 58px; border-radius: 14px; display: block; }

.nav__links { display: flex; align-items: center; gap: 2px; margin: 0 auto; }
.nav__links a {
  color: var(--slate);
  text-decoration: none;
  font-size: 15px; font-weight: 500;
  padding: 8px 14px; border-radius: var(--r-pill);
  transition: color .16s ease;
}
.nav__links a:hover { color: var(--ink); }
.nav__links a[aria-current] { color: var(--ink); }

/* No size of its own. The button was 42 here and 58 everywhere else, which
   is two buttons wearing the same style. One control, one size: 58, which is
   ZomeControl.height in the app. The header gives the difference back out of
   its own padding so it does not grow by sixteen points. */

.nav__burger {
  display: none;
  margin-left: auto;
  width: 44px; height: 44px;
  align-items: center; justify-content: center;
  background: transparent; border: 0; cursor: pointer;
  border-radius: var(--r-pill);
}
.nav__burger span {
  display: block; width: 20px; height: 2px; background: var(--ink);
  border-radius: 2px; position: relative;
  transition: transform .22s cubic-bezier(.23, 1, .32, 1), opacity .16s ease;
}
.nav__burger span::before,
.nav__burger span::after {
  content: ''; position: absolute; left: 0;
  width: 20px; height: 2px; background: var(--ink); border-radius: 2px;
  transition: transform .22s cubic-bezier(.23, 1, .32, 1);
}
.nav__burger span::before { top: -6px; }
.nav__burger span::after { top: 6px; }

body.menu-open .nav__burger span { background: transparent; }
body.menu-open .nav__burger span::before { transform: translateY(6px) rotate(45deg); }
body.menu-open .nav__burger span::after { transform: translateY(-6px) rotate(-45deg); }

/* The drawer. Present in the DOM at all widths, shown only under 900. */
.drawer {
  position: fixed; inset: 0;
  z-index: 99;
  background: rgba(255, 255, 255, .98);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  padding: 92px var(--gutter) 40px;
  display: none;
  overflow-y: auto;
}
body.menu-open .drawer { display: block; }

.drawer__grid { display: grid; gap: 10px; max-width: 480px; margin-inline: auto; }

.drawer__item {
  display: flex; align-items: flex-start; gap: 15px;
  padding: 16px;
  border-radius: var(--r-card);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  text-decoration: none; color: var(--ink);
}
.drawer__dot {
  flex: none;
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--accent);
  display: grid; place-items: center;
  font-size: 19px;
}
.drawer__item b { display: block; font-size: 16px; font-weight: 600; }
.drawer__item span { color: var(--slate); font-size: 14.5px; line-height: 1.45; }

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

/* The story.
   One sentence broken across the scroll, with the product held still beside
   it. The left column advances through four statements; the right is a single
   sticky panel whose contents crossfade to match whichever statement is live.

   Sticky rather than a scroll pin: it needs no spacer, cannot collapse the
   column beside it, and holds the panel with JavaScript switched off. */
.story { padding-bottom: 40px; }
/* Two equal columns with 40 between them, which is what theirs is: 636 and
   636 inside a 1312 content width. The panel had been the narrower half and
   it made the product the supporting act. */
.story__grid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 40px; align-items: start;
}

.story__right { position: sticky; top: 80px; }
/* The sticky panel is released by the end of its containing block, so the
   column beside it needs to outlast the last frame. Without this the panel
   slid away while the final statement was still being read. */
.story__left { padding-bottom: 22vh; }

/* Each statement holds most of a screen, so the sentence is read at the pace
   somebody scrolls rather than flicking past. */
.frame {
  min-height: 84vh;
  display: flex; flex-direction: column; justify-content: center;
  padding-block: 40px;
}
.frame:first-child { min-height: 78vh; }
.frame h1, .frame h2 { max-width: 13ch; }
/* The emphasis is not coloured. One blue word in a serif headline reads as a
   link, and it was the only coloured type on the page. */
.frame h1 em, .frame h2 em { font-style: normal; color: inherit; }
.frame p {
  font-size: 19px; color: var(--slate);
  max-width: 34ch; margin-top: 22px;
}
.frame .btn { margin-top: 32px; align-self: flex-start; }
.frame__note { margin-top: 18px; font-size: 14.5px; color: var(--slate-2); }

/* The panel. */
/* 780 tall at radius 24, off theirs. It was 620, which left the phone
   floating in a panel barely bigger than itself; the height is what gives the
   screen somewhere to stand and the chips somewhere to sit. */
.stage {
  position: relative;
  border-radius: 24px;
  background: var(--paper-2);
  padding: 48px 32px;
  display: grid; place-items: center;
  height: 780px;
  overflow: hidden;
}
.stage__shot {
  grid-area: 1 / 1;
  display: grid; place-items: center;
  width: 100%;
}
/* Only the first is painted at rest. The others are brought in by script, so
   with no JavaScript the panel simply shows the first screen rather than a
   stack of four on top of one another. */
.stage__shot + .stage__shot { display: none; }
html.js .stage__shot + .stage__shot { display: grid; }

/* The crossfade, in CSS rather than in GSAP.
   0.55s on cubic-bezier(0.44, 0, 0.56, 1) and blur(8px) are read off the site
   this is modelled on. The curve is the part that matters: it is symmetric,
   easing in as hard as it eases out, where a plain ease-out leaves on a
   different footing from the one it arrived on. A crossfade is one gesture in
   two halves, so both halves want the same shape. */
html.js .stage__shot {
  opacity: 0;
  filter: blur(8px);
  transition: opacity .55s cubic-bezier(.44, 0, .56, 1),
              filter .55s cubic-bezier(.44, 0, .56, 1);
}
html.js .stage__shot.is-live { opacity: 1; filter: blur(0px); }

/* The chips travel a little as they arrive, the way the tiles do there. */
html.js .stage__shot .chip {
  translate: 0 10px;
  transition: opacity .55s cubic-bezier(.44, 0, .56, 1),
              translate .55s cubic-bezier(.44, 0, .56, 1);
}
html.js .stage__shot.is-live .chip { translate: 0 0; }
/* The screenshot, in a device.
   The frame is a transparent PNG of an iPhone 17 at 388x800, and the screen
   cutout inside it was measured off the file's alpha channel: it begins 17px
   in and 14px down and is 354 by 772. Those four numbers are the percentages
   below, so the screenshot sits in the cutout at any size the panel takes.

   The screenshots are 644 by 1400, an aspect of 0.460 against the cutout's
   0.459, so `cover` crops about a pixel. They are anchored to the top anyway,
   because the status bar is the part that would look wrong cropped. */
.stage .phone { width: 296px; }
.phone {
  position: relative;
  aspect-ratio: 388 / 800;
}
.phone__shot {
  position: absolute; z-index: 1;
  left: 4.381%; top: 1.75%;
  width: 91.237%; height: 96.5%;
  object-fit: cover; object-position: top center;
  /* A real corner, not an ellipse: 14.1% of the screen's width and 5.1% of
     its height are the same number of pixels at every size, which is what
     keeps the radius circular as the panel shrinks. 14% of the screen width
     is the corner Apple actually draws. */
  border-radius: 14.1% / 5.1%;
}
.phone__frame {
  position: relative; z-index: 2;
  display: block; width: 100%; height: 100%;
  pointer-events: none;
}

/* Chips. Each is a real thing the app does, floated over the panel.
   They belong to a shot and come and go with it. */
.chip {
  position: absolute;
  /* Over the device, not under it. The frame is z-index 2 so that it sits on
     top of the screenshot it surrounds, and the chips are its siblings, so
     without a number of their own they lost to it and disappeared behind the
     phone. */
  z-index: 3;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 15px;
  border-radius: var(--r-pill);
  font-size: 14px; font-weight: 600;
  white-space: nowrap;
  box-shadow: 0 2px 10px rgba(0, 28, 53, .1);
}
.chip svg { width: 17px; height: 17px; }
.chip--a { top: 11%; left: 3%; }
.chip--b { top: 33%; right: 3%; }
.chip--c { bottom: 29%; left: 3%; }
.chip--d { bottom: 11%; right: 3%; }
.chip--red   { color: #C2543F; background: #FDEDE8; }
.chip--blue  { color: #0076DE; background: #E6F2FE; }
.chip--green { color: #23873D; background: #E8F7EC; }
.chip--amber { color: #8F7200; background: #FDF6DF; }
.chip--sand  { color: var(--sand-wall); background: #F6EFE4; }

/* The closing call: three rings turning behind it.
   Durations and the linear timing are Pool's own — 120s, 110s, 82s — because
   the point of the effect is that nothing ever visibly repeats. At those rates
   no two rings line up again inside a session, so it reads as drift rather
   than as a loop. */
.close {
  text-align: center;
  padding: 170px 0 180px;
  min-height: 1180px;
  display: grid; align-items: center;
  position: relative;
  overflow: hidden;
  background: var(--paper);
}
.close__mid { position: relative; z-index: 2; }
.close h2 { max-width: 14ch; margin-inline: auto; }
.close p { color: var(--slate); margin: 20px auto 0; max-width: 36ch; font-size: 17px; }
.close__mark {
  width: 104px; height: 104px; border-radius: 24px;
  margin: 0 auto 26px; display: block;
  box-shadow: 0 10px 30px rgba(0, 28, 53, .16);
}
.close__form { margin-top: 30px; }

/* The waitlist form.
   These rules were lost in one of the section rewrites and the input had been
   rendering as a browser default ever since: 22 points tall, an inset grey
   border and square corners, sitting next to a 58pt capsule button. Written
   out again, set for paper rather than for the navy panel it first sat on. */
.signup {
  display: flex; gap: 10px; flex-wrap: wrap;
  justify-content: center;
  max-width: 520px; margin: 0 auto;
}
.signup input[type='email'] {
  flex: 1 1 250px;
  font: inherit; font-size: 16px;
  min-height: 58px;
  padding: 0 22px;
  border-radius: var(--r-btn);
  border: 1px solid rgba(0, 28, 53, .14);
  background: var(--paper);
  color: var(--ink);
  box-shadow: 0 1px 3px rgba(0, 28, 53, .05);
}
.signup input::placeholder { color: var(--slate-2); }

/* The honeypot.
   A field a person never sees and a script fills in anyway. Off-screen rather
   than `display: none`, which is the first thing a script checks for, and
   `aria-hidden` with `tabindex="-1"` so it is out of the reading order and out
   of the tab order too. A browser that ignores CSS entirely shows a labelled
   text box, which is why the label says Company rather than "leave blank". */
.hp {
  position: absolute;
  left: -9999px; top: auto;
  width: 1px; height: 1px;
  overflow: hidden;
}

.signup__msg {
  margin-top: 16px; font-size: 15px; min-height: 22px;
  color: var(--slate);
}
.signup__msg[data-state='error'] { color: #C2333A; }
.signup__msg[data-state='ok'] { color: #23873D; }

.btn[aria-busy='true'] { opacity: .7; pointer-events: none; }


/* A soft hole in the middle, so the words sit on paper rather than on a
   moving field. Without it the form is unreadable every time an orb passes
   behind it. */
.close::before {
  content: ''; position: absolute; inset: 0; z-index: 1;
  background: radial-gradient(ellipse 21% 27% at 50% 42%,
    var(--paper) 0%, rgba(255, 255, 255, .82) 62%, rgba(255, 255, 255, 0) 100%);
}

/* No mask, and centred.
   A ring that fades away along the top is not a ring any more, it is an arc
   with an apology on the end of it. An even ring should be even the whole way
   round, so the field is plain and the circle sits in the middle. */
/* Faded out of the top.
   The ring still turns evenly and the marks still sit sixty degrees apart:
   this only hides the part of the circle that would otherwise ride up beside
   the bar. In theirs the top of the section is clear white and the icons
   gather below it, and a mark passing behind the navigation is the one place
   the motion becomes a distraction. */
.rings {
  position: absolute; inset: 0; z-index: 0;
  /* Clear for the top quarter, and only at full strength past the middle, so
     what rises past the bar is gone rather than faint. The marks never reach
     full opacity either: at .82 they read as something moving behind the page
     rather than on it. */
  opacity: .82;
  -webkit-mask-image: linear-gradient(to bottom,
    rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 26%, rgba(0, 0, 0, .22) 38%,
    rgba(0, 0, 0, .62) 50%, #000 64%, #000 100%);
          mask-image: linear-gradient(to bottom,
    rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 26%, rgba(0, 0, 0, .22) 38%,
    rgba(0, 0, 0, .62) 50%, #000 64%, #000 100%);
}


/* Centred, because the ring is meant to read as a ring. */
.ring {
  position: absolute; top: 50%; left: 50%;
  border-radius: 50%;
  will-change: rotate;
  animation-name: ring-spin;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
.orb {
  position: absolute; top: 50%; left: 50%;
  width: 0; height: 0;
  /* Sit on the circle: turn to the orb's angle, then step out by the radius.
     `--rs` scales every radius at once, because the radii are written on the
     orbs themselves and a narrow screen needs all of them pulled in. */
  transform: rotate(var(--a)) translateY(calc(var(--r) * var(--rs, 1) * -1));
}
.orb__in {
  position: absolute; top: 0; left: 0;
  width: var(--s); height: var(--s);
  border-radius: 26%;
  display: grid; place-items: center;
  background: var(--paper);
  border: 1px solid var(--rule);
  box-shadow: 0 4px 14px rgba(0, 28, 53, .07);
  color: var(--slate-2);

  /* Each one keeps its own tilt while it travels.
     Theirs sit at all sorts of angles, mostly inside twenty degrees either
     way, and none of them is upside down: the tilt is fixed per icon, not a
     tumble. So the ring's turn still comes off, and `--tilt` goes back on.

     `translate` and `rotate` are their own properties rather than functions
     inside `transform`, which is what lets the animation drive one without
     overwriting the centring. */
  translate: -50% -50%;
  /* The ring's turn comes off and nothing goes back on, so every mark stays
     level the whole way round. */
  rotate: calc(0deg - var(--a));
  filter: blur(var(--blur, 0px));
  animation-name: ring-unspin;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
.orb__in svg { width: calc(var(--s) * 0.46); height: calc(var(--s) * 0.46); }
/* These are the real app icons and they carry their own grounds: Health and
   Calendar are white, Fitness is near-black, Messages is green. So the orb
   stops drawing a card behind them and becomes the icon itself, with a
   hairline to give the white ones an edge on a white page. */
.orb__in--logo {
  padding: 0; border: 0; background: transparent;
  border-radius: 23%;
  box-shadow: 0 4px 14px rgba(0, 28, 53, .12),
              inset 0 0 0 1px rgba(0, 28, 53, .06);
  overflow: hidden;
}
.orb__in--logo img { width: 100%; height: 100%; display: block; }

/* Orderly, not scattered.
   The site this is modelled on throws its icons about: every one a different
   size, a different blur, a different tilt, at an arbitrary distance. That is
   their character. It is wrong here, because these are six brand marks, and
   brand marks want to be legible and evenly spaced rather than tumbling in a
   depth-of-field haze.

   One ring of six logos, sixty degrees apart, one size, upright, sharp. A
   second ring of drawn icons behind it, thirty degrees apart, one size, one
   blur. Both turning at the same rate in the same direction. Nothing here is
   random: every angle is arithmetic. */
/* The duration. Without it `animation-name` has nothing to run over and the
   whole field stands still: it was dropped with the block this replaced, and
   the rings stopped turning. Both the ring and the counter-rotation on each
   orb must carry the same number or the marks would not stay level. */
.ring, .orb__in { animation-duration: 104s; }
.ring { width: 0; height: 0; }

/* Far enough out to clear the words. At 300 the logos at ninety and two
   hundred and seventy degrees were landing on the ends of the email field. */
.ring--inner .orb { --r: 386px; }
.ring--inner .orb__in { --s: 84px; }

.ring--mid .orb { --r: 560px; }
.ring--mid .orb__in { --s: 50px; --blur: 1.5px; }

.ring--outer { display: none; }


@keyframes ring-spin   { from { rotate: 0deg; } to { rotate: -360deg; } }
@keyframes ring-unspin {
  from { rotate: calc(0deg - var(--a)); }
  to   { rotate: calc(0deg - var(--a) + 360deg); }
}

/* Motion that never stops has to be motion somebody can switch off. */
@media (prefers-reduced-motion: reduce) {
  .ring, .orb__in { animation: none; }
}

/* The panel the research page closes on. Same loss again. */
.waitlist {
  text-align: center;
  border-radius: 28px;
  padding: 72px 32px;
  background: var(--paper-2);
  border: 1px solid var(--rule);
}
.waitlist h2 { max-width: 16ch; margin-inline: auto; }
.waitlist p { color: var(--slate); max-width: 40ch; margin: 18px auto 0; }


/* ---------------------------------------------------------------- footer */

/* Pool's shape: white, no rules anywhere, and column headings set at the same
   size and weight as the links under them. The uppercase, letter-spaced 12px
   heading is where every footer ends up by default, and dropping it is most
   of why theirs reads quiet rather than like a sitemap.

   This whole section had been lost in a block replacement, leaving only the
   three mobile overrides further down. The footer was rendering as a plain
   unordered list with disc bullets and underlined links. */
.site-foot {
  background: var(--paper);
  color: var(--slate);
  /* Vertical only. The .wrap inside already applies the gutter, so setting it
     here as well put the footer on a 128 margin while every headline above it
     was on 64, and the columns read as inset from the page. */
  padding: 64px 0 56px;
  font-size: 16px;
  letter-spacing: -0.03em;
}
.foot__grid {
  display: grid;
  /* Five since Follow became a section of its own rather than two icons on
     the copyright line. Even columns, so the headings all begin on a line. */
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 32px;
  align-items: start;
}
.site-foot h4 {
  font-family: var(--text);
  font-size: 16px; font-weight: 400;
  letter-spacing: -0.03em;
  color: var(--ink);
  margin: 0 0 16px;
}
.site-foot ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.site-foot li { line-height: 1.4; }
.site-foot a { text-decoration: none; color: var(--slate); }
.site-foot a:hover { color: var(--ink); }
.foot__legal {
  margin-top: 64px;
  font-size: 15px; color: var(--slate-2);
}


/* ---------------------------------------------------------------- faq */

/* The accordion. These rules went with one of the section rewrites, so every
   question was rendering as a bare <details>: a default disclosure triangle,
   no pill, no divider, no chevron. */
/* The heading column beside the questions, which stays put while they scroll.
   Lost with the rest of this section; the heading was rendering as a plain
   block at the top of a single column. */
/* The same two columns the story uses: equal halves, 40 between them. The
   heading then begins on the page margin, level with every other headline,
   and the questions start where the panel above them starts and run to the
   same width. At 360 and 1fr the heading sat inboard of the margin and the
   questions began somewhere of their own. */
.faq {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 40px; align-items: start;
}
.faq__side { position: sticky; top: 120px; }
.faq__side p { color: var(--slate); margin-top: 14px; }

.faq__list { display: grid; gap: 10px; }

details.qa {
  background: var(--paper-2);
  border: 1px solid var(--rule);
  border-radius: 16px;
  overflow: hidden;
}
details.qa summary {
  list-style: none; cursor: pointer;
  padding: 20px 56px 20px 22px;
  font-weight: 600; font-size: 17px;
  position: relative;
}
details.qa summary::-webkit-details-marker { display: none; }
details.qa summary::after {
  content: ''; position: absolute; right: 24px; top: 50%;
  width: 9px; height: 9px;
  border-right: 2px solid var(--slate);
  border-bottom: 2px solid var(--slate);
  transform: translateY(-70%) rotate(45deg);
  transition: transform .22s cubic-bezier(.44, 0, .56, 1);
}
details.qa[open] summary::after { transform: translateY(-25%) rotate(225deg); }
details.qa:hover { background: var(--paper-3); }
details.qa .qa__body { padding: 0 22px 22px; color: var(--slate); }
details.qa .qa__body a { color: var(--blue); }


/* ---------------------------------------------------------------- research */

/* The index: a masthead over a grid of cards.
   These rules were lost in one of the section rewrites, so every card was
   rendering as a bare anchor 53 points tall on a grid that was not a grid. */
.papers--grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
  margin-top: 52px;
}
.card {
  display: block; text-decoration: none; color: inherit;
  border: 1px solid var(--rule);
  border-radius: 18px;
  overflow: hidden;
  background: var(--paper);
  transition: border-color .18s cubic-bezier(.44, 0, .56, 1),
              transform .18s cubic-bezier(.44, 0, .56, 1);
}
.card:hover { border-color: #C7D8E6; }
.card:active { transform: scale(.995); }

/* The art panel carries a wash of the card's own accent. It means nothing
   beyond keeping a grid of six from being six identical grey rectangles. */
.card__art {
  aspect-ratio: 16 / 10;
  background:
    radial-gradient(85% 70% at 50% 20%,
      color-mix(in srgb, var(--accent, var(--blue)) 16%, transparent), transparent 72%),
    var(--paper-2);
}
/* A card with a real picture on it rather than the tinted panel. The panel is
   the fallback for a piece with no image; this is what one with an image gets,
   and it is what makes the research index look like a shelf. */
.card__art--img {
  background: var(--paper-2);
  display: block;
  overflow: hidden;
}
.card__art--img img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}

.card__body { padding: 20px 22px 26px; }
.card__body time { font-size: 13px; color: var(--slate-2); }
.card__body h3 { margin-top: 8px; line-height: 1.2; }


@media (max-width: 900px) {
  /* 24, which is theirs: 327 of content in a 375 screen. */
  :root { --gutter: 24px; }

  /* No burger and no drawer: theirs has neither. The bar is the mark and the
     section names as plain text, which for three links is less to read than a
     button that opens a screen of the same three. The join button goes too,
     as it does there; the closing section is where somebody joins. */
  .nav > .btn { display: none; }
  .nav { justify-content: space-between; gap: 4px; }
  .nav__links { display: flex; margin: 0 0 0 auto; gap: 0; }
  .nav__links a { padding: 8px 10px; font-size: 15px; }
  .nav__brand img { width: 46px; height: 46px; border-radius: 12px; }

  /* The panel leads and then pins.
     Theirs is order 0 with `position: sticky; top: 0`, so on a phone the
     screen arrives first, sticks under the bar, and the sentence scrolls
     underneath it while the shot inside changes to keep up. That is the same
     device as the desktop two-column, folded into one column, and it is why
     their phone layout still tells a story rather than listing four headlines.
     Mine had the panel merely sitting between paragraphs, going nowhere. */
  /* `align-items: start` is on the base grid rule, and it carried into the
     flex column, where the cross axis is horizontal: every child shrank to
     its own content. The panel came out 222 wide in a 366 column, sitting
     against the left gutter with the phone off-centre inside it. Stretch is
     what a column wants. */
  .story__grid {
    display: flex; flex-direction: column; gap: 0;
    align-items: stretch;
  }
  .story__left { display: contents; }
  .story__right {
    order: 0;
    position: sticky; top: 78px;
    z-index: 2;
    padding-bottom: 20px;
    background: var(--paper);
  }
  .frame[data-frame='0'] { order: 1; }
  .frame[data-frame='1'] { order: 2; }
  .frame[data-frame='2'] { order: 3; }
  .frame[data-frame='3'] { order: 4; }

  /* Centred, as theirs is at this width. */
  .frame {
    min-height: 62vh;
    padding-block: 18px;
    text-align: center;
    align-items: center;
  }
  .frame:first-child { min-height: 52vh; }
  .frame h1, .frame h2, .frame p { max-width: none; margin-inline: auto; }
  .frame .btn { align-self: center; }

  /* The stack has to stay stacked here, because the crossfade now runs on a
     phone too: the guard in the script is that the panel is sticky, and it
     is. Hiding the other three would leave one shot that never changes. */
  html.js .stage__shot + .stage__shot { display: grid; }
  /* Tall enough to hold the whole screen. At 406 with 26 of padding the
     panel gave the phone 354 to stand in and the image is 2.17 times its
     own width, so the status bar and the tab bar were both cut off. The
     height now follows the phone rather than the phone being trimmed to
     the height. */
  .stage { height: 448px; min-height: 0; padding: 22px 16px; }
  .stage .phone { width: 184px; }
  .chip { font-size: 12px; padding: 6px 11px; }

  .close { padding: 90px 0 100px; min-height: 700px; }
  .rings { --rs: .5; }
  .ring--inner .orb__in { --s: 58px; }
  .ring--mid .orb__in { --s: 38px; }
  .close__mark { width: 76px; height: 76px; border-radius: 19px; }

  /* Footer: two columns at 24, theirs. */
  .site-foot { padding-block: 24px; }
  .foot__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; }
  /* On a phone the row becomes two lines, left-aligned, with the marks under
     the company rather than pushed to the far edge of a 327 point screen. */
  .foot__legal { margin-top: 48px; }

  .faq { grid-template-columns: 1fr; gap: 28px; }
  .faq__side { position: static; }
  .papers--grid { grid-template-columns: 1fr; }
}

@media (max-width: 640px) {
  body { font-size: 16.5px; }
  .paper { flex-wrap: wrap; gap: 12px; }
  .paper__meta { width: auto; }
  .paper__go { margin-left: 0; }
  .waitlist { padding: 56px 22px; border-radius: 24px; }
  /* One row, not two stacked slabs.
     Stacked, the field and the button were the full width of the screen and
     58 points each, and the pair was the heaviest thing on the page. Side by
     side they read as a form again. The stack was only ever a workaround for
     `flex-basis` meaning height in a column, which a basis of zero settles
     without giving up the row. The 58 stays: it is the app's control height,
     and every control on the site is that tall. */
  .signup { flex-wrap: nowrap; gap: 8px; }
  .signup input[type='email'] { flex: 1 1 0; min-width: 0; padding: 0 18px; }
  .signup .btn { flex: 0 0 auto; padding: 0 26px; }
  .hero__stage { margin-top: 40px; }

  /* The hole in the ring field is sized in percentages of the section, so on
     a phone it was 87 points wide behind a form 366 wide, and the logos rode
     straight across the field and the button. Wider, and solid paper the
     whole way out to the form's edge. */
  .close::before {
    background: radial-gradient(ellipse 66% 26% at 50% 44%,
      var(--paper) 0%, var(--paper) 66%,
      rgba(255, 255, 255, .88) 84%, rgba(255, 255, 255, 0) 100%);
  }
}

/* ---------------------------------------------------------------- 404 */

/* A 404 that is a set of doors rather than an apology.
   Somebody arrives here having asked for something specific, so the useful
   thing is every place the site actually goes, each named for what is behind
   it. The number is set small and grey: it is the least interesting fact on
   the page. */
.lost { padding: 120px 0 120px; }
.lost__code {
  font-family: var(--text);
  font-size: 13px; font-weight: 600; letter-spacing: .12em;
  color: var(--slate-2);
  margin: 0 0 18px;
}
.lost h1 { font-size: clamp(38px, 5.4vw, 62px); max-width: 16ch; }
.lost__lead {
  color: var(--slate); font-size: 18px;
  max-width: 46ch; margin: 20px 0 0;
}

.lost__ways {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
  margin-top: 48px;
}
.lost__ways a {
  display: block; text-decoration: none;
  padding: 20px 22px 22px;
  border: 1px solid var(--rule);
  border-radius: var(--r-card);
  background: var(--paper);
  transition: border-color .18s cubic-bezier(.44, 0, .56, 1),
              transform .18s cubic-bezier(.44, 0, .56, 1);
}
.lost__ways a:hover { border-color: #C7D8E6; }
.lost__ways a:active { transform: scale(.995); }
.lost__ways b {
  display: block; font-weight: 600; margin-bottom: 7px;
}
.lost__ways span {
  display: block; color: var(--slate); font-size: 15px; line-height: 1.5;
}

.lost__help {
  margin-top: 40px; color: var(--slate-2); font-size: 15px;
}
.lost__help a { color: var(--blue); text-decoration: underline; text-underline-offset: 3px; }

@media (max-width: 900px) {
  .lost { padding: 64px 0 80px; }
  .lost__ways { grid-template-columns: 1fr; gap: 10px; }
}
