@font-face {
  font-family: "Stack Sans Headline";
  src:
    url("assets/fonts/stack-sans-headline-regular.woff2") format("woff2"),
    url("assets/fonts/stack-sans-headline-regular.ttf") format("truetype");
  font-style: normal;
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Stack Sans Headline";
  src:
    url("assets/fonts/stack-sans-headline-bold.woff2") format("woff2"),
    url("assets/fonts/stack-sans-headline-bold.ttf") format("truetype");
  font-style: normal;
  font-weight: 700;
  font-display: swap;
}
/* Glow Sans SC (未来荧黑, SIL OFL 1.1), subset by tools/subset-cjk-font.py to
   the Han characters the pages set; re-run it when Chinese copy changes. The
   range covers the CJK blocks, so a character outside the subset falls to
   the system CJK face instead of a blank. Its squarer, open frame matches the
   geometric Latin of Stack Sans, which sets all Latin text ahead of it. */
@font-face {
  font-family: "Glow Sans SC";
  src: url("assets/fonts/glow-sans-sc-regular.woff2?v=1") format("woff2");
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  unicode-range: U+2E80-2FFF, U+3000-9FFF, U+F900-FAFF, U+FE30-FE4F,
    U+FF00-FFEF;
}
@font-face {
  font-family: "Glow Sans SC";
  src: url("assets/fonts/glow-sans-sc-medium.woff2?v=1") format("woff2");
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  unicode-range: U+2E80-2FFF, U+3000-9FFF, U+F900-FAFF, U+FE30-FE4F,
    U+FF00-FFEF;
}
@font-face {
  font-family: "Glow Sans SC";
  src: url("assets/fonts/glow-sans-sc-bold.woff2?v=1") format("woff2");
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  unicode-range: U+2E80-2FFF, U+3000-9FFF, U+F900-FAFF, U+FE30-FE4F,
    U+FF00-FFEF;
}
@property --vivid {
  syntax: "<color>";
  inherits: true;
  initial-value: #5E9CF5;
}
@property --deep {
  syntax: "<color>";
  inherits: true;
  initial-value: #0B2A5B;
}
/* The design pixel (see :root), registered as a length so it resolves once
   on the root. */
@property --u {
  syntax: "<length>";
  inherits: true;
  initial-value: 1px;
}
@font-face {
  font-family: "Stack Sans Text";
  src:
    url("assets/fonts/stack-sans-text-regular.woff2") format("woff2"),
    url("assets/fonts/stack-sans-text-regular.ttf") format("truetype");
  font-style: normal;
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Stack Sans Text";
  src:
    url("assets/fonts/stack-sans-text-semibold.woff2") format("woff2"),
    url("assets/fonts/stack-sans-text-semibold.ttf") format("truetype");
  font-style: normal;
  font-weight: 600;
  font-display: swap;
}
:root {
  color-scheme: light;
  --paper: #ffffff;
  --ink: #182126;
  --muted: #5e6b72;
  --soft: #68777d;
  --line: #d6dde0;
  --tint: #e8eef1;
  --accent: #375865;
  --screen: #202429;
  --night: #0f1518;
  /* Stack Sans has no Han glyphs. Han text sets in Glow Sans SC; the system
     CJK faces cover any character outside its subset. */
  --cjk: "Glow Sans SC", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",
    "Noto Sans SC", "Noto Sans CJK SC";
  --body: "Stack Sans Text", var(--cjk), sans-serif;
  --display: "Stack Sans Headline", var(--cjk), sans-serif;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-inout: cubic-bezier(0.65, 0, 0.35, 1);
  /* The sweep a new work arrives on, and everything that turns with it
     (script.js's SWEEP). */
  --ease-sweep: cubic-bezier(0.76, 0, 0.24, 1);
  --dur-sweep: 1000ms;
  --dur-fast: 160ms;
  --dur: 280ms;
  --dur-focal: 1300ms;
  /* The header's gather into its capsule, and its morph between pages. */
  --gather: 640ms;
  /* The design pixel. Sizes are drawn for a view 1440px wide and 860px tall,
     where --u is 1px, as it is on every smaller screen (the narrower queries
     below keep plain px). Past that the page grows with the screen, so a
     large display shows the same composition larger rather than a small one
     afloat in white. The shorter side leads, so a wide, short window keeps
     the air around the work and its label in the first view. Growth stops
     at 1.5×: browser zoom narrows the view and so returns --u toward 1px,
     and text grown 1.5× or less still doubles within Safari's 300% zoom
     (WCAG 1.4.4). Hairlines, rails, focus rings and motion distances stay
     in px. */
  --u: clamp(1px, min(100vw / 1440, 100vh / 860), 1.5px);
  --header: calc(88 * var(--u));
  --gutter: 48px;
  interpolate-size: allow-keywords;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-synthesis: none;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header) + 20 * var(--u));
  /* The page ends where its content ends: no rubber band, down or sideways
     (a diagonal push carries both), pulls blank canvas past the night at the
     bottom (body too, for older WebKit). A push past the end goes into the
     footer's floor instead. */
  overscroll-behavior: none;
  scrollbar-color: #9eacb3 var(--paper);
  accent-color: var(--ink);
  caret-color: var(--accent);
}
* {
  box-sizing: border-box;
}
body {
  margin: 0;
  overscroll-behavior: none;
}
/* The page is as wide as the screen and never scrolls sideways. Whatever
   reaches past the edge is cut there: on narrow phones, the launch icon's
   glow, and for a moment the release status while the header gathers.
   clip, unlike hidden, makes no scroll container, so the header and the
   stage still stick to the viewport. */
.site-header,
main {
  overflow-x: clip;
}
h1,
h2,
p,
figure,
dl,
dd,
ol {
  margin: 0;
}
h1,
h2 {
  font-family: var(--display);
  font-weight: 400;
  text-wrap: balance;
}
button,
a {
  -webkit-tap-highlight-color: transparent;
}
button {
  font: inherit;
  color: inherit;
  cursor: pointer;
}
a {
  color: inherit;
  text-decoration: none;
  text-underline-offset: 4px;
  text-decoration-thickness: 1px;
}
img,
video {
  display: block;
  max-width: 100%;
}
svg {
  width: calc(20 * var(--u));
  height: calc(20 * var(--u));
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex-shrink: 0;
}
::selection {
  background: #cadce5;
  color: var(--ink);
}
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 5px;
  border-radius: 3px;
}
/* The way up hands the focus to the page itself (site.js); the page is not a
   control, so it wears no ring. */
body:focus {
  outline: none;
}
[hidden] {
  display: none !important;
}
.page-width {
  width: min(1248 * var(--u), 100% - 2 * var(--gutter));
  margin-inline: auto;
}
.icon-definitions {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}
.skip-link {
  position: fixed;
  z-index: 30;
  top: 12px;
  left: 12px;
  padding: 12px 20px;
  color: white;
  background: var(--ink);
  transform: translateY(-160%);
}
.skip-link:focus {
  transform: translateY(0);
}

/* Between pages. The header's bar is one element across the framed pages:
   it morphs from where it was (capsule or rest row) to where the next page
   has it, on the gather's own time and curve. The rest of the page leaves
   as a sheet lifted away and the next is laid down. Arriving by the
   language key (html.is-switched, from each page's head) the twin keeps
   the plain crossfade: the same layout in other words. */
.header-bar {
  view-transition-name: site-header;
}
.site-header.is-probe .header-bar {
  view-transition-name: none;
}
::view-transition-group(site-header) {
  animation-duration: var(--gather);
  animation-timing-function: var(--ease);
}
::view-transition-old(site-header),
::view-transition-new(site-header) {
  animation-duration: 320ms;
}
@media (prefers-reduced-motion: no-preference) {
  html:not(.is-switched)::view-transition-old(root) {
    animation: sheet-out 280ms cubic-bezier(0.5, 0, 0.75, 0) both;
  }
  html:not(.is-switched)::view-transition-new(root) {
    animation: sheet-in 760ms var(--ease) 60ms both;
  }
}
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(site-header) {
    animation-duration: 0s;
  }
}
@keyframes sheet-out {
  to {
    opacity: 0;
    transform: translateY(calc(-12 * var(--u))) scale(0.985);
  }
}
@keyframes sheet-in {
  from {
    opacity: 0;
    transform: translateY(calc(28 * var(--u)));
  }
}

/* Header. At rest it is an open row set large on the paper: the app icon,
   the name in bold display, and the sections with their glyphs. Once the
   page moves under it, the row gathers into a long floating capsule of
   frosted glass: the name folds into the icon, the glyphs fold away and
   every control rounds to stay concentric with the capsule. The glass is
   flat: a translucent mist under a deep blur, no outline and no shadow.

   The header's box is always the band the capsule floats in (--header), so
   the stage pins beneath it and nothing below moves as it gathers. The taller
   rest row hangs into a margin under the band.

   Rest sizes are fluid from 761px to 1280px. From 1251px the full row (icon,
   name, five glyphs and their words, the language key and the status) fits
   the column; below that the words fold to glyph keys (the 1250px query).
   Every term is in design pixels (--u), so past 1440px each size holds its
   maximum while the page grows. The pricing page and the account world
   (account.css) wear this header, and the footer below, as they are. */
.site-header {
  --rest-h: clamp(88 * var(--u), 2.3vw + 70.5 * var(--u), 100 * var(--u));
  --rest-control: clamp(48 * var(--u), 0.77vw + 42.1 * var(--u), 52 * var(--u));
  --rest-mark: clamp(44 * var(--u), 0.77vw + 38.1 * var(--u), 48 * var(--u));
  --rest-size: clamp(14 * var(--u), 0.58vw + 9.6 * var(--u), 17 * var(--u));
  --rest-pad: clamp(10 * var(--u), 1.16vw + 1.2 * var(--u), 16 * var(--u));
  --rest-glyph: clamp(18 * var(--u), 0.385vw + 15.07 * var(--u), 20 * var(--u));
  --rest-inset: clamp(16 * var(--u), 0.77vw + 10.1 * var(--u), 20 * var(--u));
  --rest-keys-gap: clamp(6 * var(--u), 1.54vw - 5.7 * var(--u), 14 * var(--u));
  --brand-size: clamp(20 * var(--u), 0.77vw + 14.1 * var(--u), 24 * var(--u));
  --bar-h: calc(60 * var(--u));
  --bar-pad: calc(8 * var(--u));
  --bar-top: calc(20 * var(--u));
  --cap-min: calc(760 * var(--u));
  --cap-mark: calc(32 * var(--u));
  --cap-size: calc(16 * var(--u));
  --cap-pad: calc(10 * var(--u));
  --cap-brand: calc(18 * var(--u));
  --control-h: var(--rest-control);
  --mark: var(--rest-mark);
  --link-size: var(--rest-size);
  --link-pad: var(--rest-pad);
  --glyph: var(--rest-glyph);
  --glyph-gap: calc(var(--glyph) * 0.4);
  --label: auto;
  --gap: calc(10 * var(--u));
  --nav-gap: calc(2 * var(--u));
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: flex-start;
  height: var(--header);
  margin-bottom: calc(var(--rest-h) - var(--header));
  background-color: var(--paper);
  transition: background-color var(--dur) ease;
}
.site-header.is-scrolled {
  --control-h: calc(var(--bar-h) - 2 * var(--bar-pad));
  --mark: var(--cap-mark);
  --link-size: var(--cap-size);
  --brand-size: var(--cap-brand);
  --link-pad: var(--cap-pad);
  --glyph: 0px;
  --glyph-gap: 0px;
  --label: auto;
  --gap: calc(12 * var(--u));
  --nav-gap: calc(2 * var(--u));
  background-color: transparent;
}
/* Arriving by the language key partway down, the header is gathered from the
   first frame and follows the page to where the reader was without easing
   anywhere (site.js lifts this once the page stands). */
.site-header.is-still,
.site-header.is-still *,
.site-header.is-still *::before,
.site-header.is-still *::after {
  transition: none !important;
}
.header-bar {
  display: flex;
  align-items: center;
  gap: var(--gap);
  width: min(1248 * var(--u), 100% - 2 * var(--gutter));
  height: var(--rest-h);
  margin: 0 auto;
  border-radius: calc(var(--bar-h) / 2);
  background-color: rgb(226 232 236 / 0);
  -webkit-backdrop-filter: blur(0) saturate(1);
  backdrop-filter: blur(0) saturate(1);
  transition:
    width var(--gather) var(--ease),
    height var(--gather) var(--ease),
    margin var(--gather) var(--ease),
    padding var(--gather) var(--ease),
    gap var(--gather) var(--ease),
    background-color var(--dur) ease,
    -webkit-backdrop-filter var(--dur) ease,
    backdrop-filter var(--dur) ease;
}
/* The capsule is at least --cap-min long, with the icon at one end and the
   sections and status at the other. --capsule is its content width, measured
   by site.js on a hidden copy of the header in its gathered state, so the
   width eases between two lengths. */
.is-scrolled .header-bar {
  width: min(100% - 2 * var(--gutter), max(var(--cap-min), var(--capsule, 0px)));
  height: var(--bar-h);
  margin-top: var(--bar-top);
  padding-inline: var(--bar-pad);
  background-color: rgb(226 232 236 / 0.64);
  -webkit-backdrop-filter: blur(48px) saturate(1.6);
  backdrop-filter: blur(48px) saturate(1.6);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .is-scrolled .header-bar {
    background-color: rgb(226 232 236 / 0.96);
  }
}
.header-bar > * {
  flex-shrink: 0;
}
.site-header.is-probe {
  position: absolute;
  inset: 0 0 auto;
  visibility: hidden;
  pointer-events: none;
}
.site-header.is-probe .header-bar {
  width: max-content;
}
/* The header's links already sit inside the page's scroll padding, so they
   cancel it; otherwise focusing one scrolls the page back to the top. */
.site-header a {
  position: relative;
  border-radius: var(--r);
  scroll-margin-top: calc(-20 * var(--u) - var(--header));
}
.site-header a:focus-visible {
  outline-offset: 3px;
  border-radius: var(--r);
}
/* The icon's drawn squircle sits a tenth inside its image; on the rest row
   its edge lands on the column edge. */
.brand {
  --r: calc(14 * var(--u));
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  margin-left: calc((var(--mark) - var(--control-h)) / 2 - var(--mark) * 0.1);
  white-space: nowrap;
  transition:
    grid-template-columns var(--gather) var(--ease),
    margin var(--gather) var(--ease);
}
.brand-mark {
  display: grid;
  /* One cell the disc's size, so an icon larger than the disc (the home page's
     rest row) overflows it evenly instead of growing the cell. */
  grid-template: minmax(0, 1fr) / minmax(0, 1fr);
  place-items: center;
  width: var(--control-h);
  height: var(--control-h);
  border-radius: 50%;
  transition:
    width var(--gather) var(--ease),
    height var(--gather) var(--ease),
    background-color var(--dur-fast) ease;
}
.brand-mark > * {
  grid-area: 1 / 1;
  transition:
    width var(--gather) var(--ease),
    height var(--gather) var(--ease),
    transform 420ms var(--ease),
    opacity 240ms ease,
    filter 240ms ease;
}
/* The icon is brand/app-icon.svg as an image, cached across pages. It is a
   drawing, not a glyph: it keeps its own paint, not the page's line style, and
   its own size, which may be larger than the disc it sits in. */
.brand-icon {
  width: var(--mark);
  height: var(--mark);
  max-width: none;
  forced-color-adjust: none;
}
/* The arrow's --rise is written by site.js while it carries the page up: 0 is
   the middle of the disc, -1 just clear of its top, +1 just below it. */
.brand-top {
  width: calc(18 * var(--u));
  height: calc(18 * var(--u));
  stroke-width: 2.5;
  opacity: 0;
  filter: blur(2px);
  transform: translateY(12px) rotate(-90deg);
  translate: 0 calc(var(--rise, 0) * (var(--control-h) / 2 + 7 * var(--u)));
}
.brand-name {
  min-width: 0;
  overflow-x: clip;
  font-family: var(--display);
  font-size: var(--brand-size);
  font-weight: 700;
  letter-spacing: -0.03em;
  transition:
    font-size var(--gather) var(--ease),
    opacity var(--dur) ease 160ms,
    filter var(--dur) ease 160ms;
}
.brand-name > span {
  display: block;
  padding-left: calc(6 * var(--u));
}
/* In the capsule the wordmark stays, set smaller beside the icon, as a logo
   leads a toolbar. Phones fold it into the icon. */
.is-scrolled .brand {
  --r: calc(var(--control-h) / 2);
  margin-left: 0;
}
/* On the home page the capsule's icon shows where it leads: it lifts away
   and the way back to the top rises into its place. Pressed, it carries the
   page up: arrows pass up through the disc in step with the page, and the
   last one settles as the page lands (is-rising, site.js). Every other page
   leaves the arrow out: there the icon leads home, not up, so it stays. */
.is-scrolled.is-rising .brand-mark,
.is-scrolled .brand:has(.brand-top):focus-visible .brand-mark,
.is-scrolled .brand:has(.brand-top):hover .brand-mark {
  background-color: rgb(24 33 38 / 0.06);
}
.is-scrolled.is-rising .brand-icon,
.is-scrolled .brand:has(.brand-top):focus-visible .brand-icon,
.is-scrolled .brand:has(.brand-top):hover .brand-icon {
  opacity: 0;
  filter: blur(2px);
  transform: translateY(-12px) scale(0.6);
}
.is-scrolled.is-rising .brand-top,
.is-scrolled .brand:focus-visible .brand-top,
.is-scrolled .brand:hover .brand-top {
  opacity: 1;
  filter: none;
  transform: rotate(-90deg);
}
/* The disc is the window the arrows pass through. As the header opens, the
   last arrow fades where it settled rather than sinking away. */
.is-rising .brand-mark {
  overflow: clip;
}
.is-rising .brand-top {
  transform: rotate(-90deg);
}
.site-header nav {
  position: relative;
  display: flex;
  gap: var(--nav-gap);
  transition: gap var(--gather) var(--ease);
  margin-left: auto;
}
.site-header nav a {
  --r: calc(14 * var(--u));
  display: inline-flex;
  justify-content: center;
  align-items: center;
  min-width: var(--control-h);
  height: var(--control-h);
  padding-inline: var(--link-pad);
  color: var(--ink);
  font-size: var(--link-size);
  font-weight: 600;
  white-space: nowrap;
  transition:
    background-color var(--dur-fast) ease,
    border-radius var(--gather) var(--ease),
    min-width var(--gather) var(--ease),
    height var(--gather) var(--ease),
    padding var(--gather) var(--ease),
    font-size var(--gather) var(--ease);
}
.site-header nav a:hover {
  background-color: rgb(24 33 38 / 0.06);
}
.site-header nav a:active {
  background-color: rgb(24 33 38 / 0.1);
}
/* Section glyphs, drawn from the same set as the features and alive on the
   same hovers. They fold away as the header gathers. Where the rest row is
   narrow the labels fold instead (--label: 0px) and the glyphs stand alone as
   keys; the words stay in the accessibility tree either way. */
.nav-icon {
  width: var(--glyph);
  height: var(--rest-glyph);
  margin-right: var(--glyph-gap);
  stroke-width: 1.75;
  overflow: visible;
  transition:
    width var(--gather) var(--ease),
    margin var(--gather) var(--ease),
    opacity var(--dur) ease 120ms;
}
.is-scrolled .nav-icon {
  opacity: 0;
  transition:
    width var(--gather) var(--ease),
    margin var(--gather) var(--ease),
    opacity 160ms ease;
}
/* The FAQ bubble answers its question: the hook of the "?" folds down into
   its stem, and the stroke rises straight as the bar of a "!". Each stroke is
   one dash the length of its path; the offset slides the dash off the path
   toward the point where the two meet. */
.nav-icon .i-hook {
  stroke-dasharray: 13 16;
  transition: stroke-dashoffset 280ms var(--ease) 120ms;
}
.nav-icon .i-bar {
  stroke-dasharray: 6 12;
  stroke-dashoffset: 7;
  transition: stroke-dashoffset 180ms var(--ease-inout);
}
.nav-label {
  display: block;
  width: var(--label);
  overflow-x: clip;
  transition:
    width var(--gather) var(--ease),
    opacity var(--dur) ease 120ms;
}
/* The marker names the current section in the capsule, or on the pricing
   page the page itself; at the top of the home page the current section is
   always the first. While the links resize, site.js holds it under the words
   (is-following). Once that link wears its hover ground, the ground marks it
   and the marker steps aside: an ink rule inside a 6% ink pill reads as two
   marks on one word. */
.nav-marker {
  position: absolute;
  left: 0;
  bottom: calc(6 * var(--u));
  width: 1px;
  height: 1px;
  background: var(--ink);
  transform-origin: left;
  transform: translateX(var(--x, 0)) scaleX(var(--w, 0));
  opacity: 0;
  transition:
    transform 520ms var(--ease),
    opacity var(--dur) ease;
}
.nav-marker.is-following {
  transition: opacity var(--dur) ease;
}
.is-scrolled .nav-marker.is-visible {
  opacity: 1;
}
.site-header nav:has(a[aria-current]:hover) .nav-marker {
  opacity: 0;
}
/* The language key names the other page in its own script (中文 here,
   English on the Chinese page), so its readers find it at a glance. It stands
   apart from the sections as a quiet key beside the release status. Its glyph
   folds away as the header gathers, as theirs do, and on tablets, where the
   sections stand as glyphs alone, it keeps its word instead; phones find it
   in the capsule. Pointed at, the glyph writes that script's character
   again, stroke by stroke. */
.lang-switch {
  --r: calc(14 * var(--u));
  --glyph-gap: calc(var(--glyph) * 0.4);
  display: inline-flex;
  justify-content: center;
  align-items: center;
  min-width: var(--control-h);
  height: var(--control-h);
  margin-left: var(--rest-keys-gap);
  padding-inline: var(--link-pad);
  color: var(--ink);
  background-color: rgb(24 33 38 / 0.06);
  font-size: var(--link-size);
  font-weight: 600;
  white-space: nowrap;
  transition:
    background-color var(--dur-fast) ease,
    border-radius var(--gather) var(--ease),
    min-width var(--gather) var(--ease),
    height var(--gather) var(--ease),
    margin var(--gather) var(--ease),
    padding var(--gather) var(--ease),
    font-size var(--gather) var(--ease),
    transform 120ms ease;
}
.lang-switch:hover {
  background-color: rgb(24 33 38 / 0.1);
}
.is-scrolled .lang-switch {
  margin-left: 0;
}
/* Each stroke is a dash its own length (--l, measured from the path) and a
   gap a hair longer, so one period is twice the stroke. Moving the offset by
   a whole period ends where it began, the stroke whole, but on the way the
   dash runs off the path and the next runs on: the stroke is wiped away,
   then written again from its start, or from its end (i-back) where the path
   runs against the hand. --write shapes the pass for each stroke: a quick
   wipe shared by all, then each written in its turn. */
.i-lang path {
  --period: calc(2 * var(--l) + 0.2px);
  stroke-dasharray: var(--l) calc(var(--l) + 0.2px);
}
.i-lang :nth-child(1) {
  --l: 1px;
  --write: linear(0, 0.5 12%, 0.5 16%, 1 22%, 1);
}
.i-lang :nth-child(2) {
  --l: 12px;
  --write: linear(0, 0.5 12%, 0.5 26%, 1 46%, 1);
}
.i-lang :nth-child(3) {
  --l: 12.1px;
  --write: linear(0, 0.5 12%, 0.5 50%, 1 72%, 1);
}
.i-lang :nth-child(4) {
  --l: 8.5px;
  --write: linear(0, 0.5 12%, 0.5 76%, 1 94%, 1);
}
.i-lang :nth-child(5) {
  --l: 22.4px;
  --write: linear(0, 0.5 14%, 0.5 18%, 1 66%, 1);
}
.i-lang :nth-child(6) {
  --l: 6px;
  --write: linear(0, 0.5 14%, 0.5 74%, 1 94%, 1);
}
/* The release status leads with the Apple mark: a quiet tinted key at rest,
   the ink action in the capsule. Pointed at, the mark slips out, the words
   make room and an arrow slides in to say it goes somewhere. */
.release-link {
  --r: calc(14 * var(--u));
  /* --inset is the side padding and the marks' optical inset; --lead is the
     room the Apple mark takes, which the words give back on hover. */
  --inset: var(--rest-inset);
  --sign: var(--rest-glyph);
  --lead: calc(var(--sign) + 2 * var(--u));
  display: inline-flex;
  align-items: center;
  height: var(--control-h);
  padding: 0 var(--inset) 0 calc(var(--inset) + var(--lead));
  color: var(--ink);
  background-color: rgb(24 33 38 / 0.06);
  font-size: var(--link-size);
  font-weight: 600;
  white-space: nowrap;
  transition:
    color var(--dur) ease,
    background-color var(--dur) ease,
    border-radius var(--gather) var(--ease),
    height var(--gather) var(--ease),
    padding var(--gather) var(--ease),
    font-size var(--gather) var(--ease);
}
.release-link:hover {
  background-color: rgb(24 33 38 / 0.1);
}
.is-scrolled .release-link {
  --inset: calc(18 * var(--u));
  --lead: calc(17 * var(--u));
  --sign: calc(16 * var(--u));
  color: #fff;
  background-color: var(--ink);
}
.is-scrolled .release-link:hover {
  background-color: var(--accent);
}
.release-link > * {
  transition:
    width var(--gather) var(--ease),
    height var(--gather) var(--ease),
    top var(--gather) var(--ease),
    left var(--gather) var(--ease),
    right var(--gather) var(--ease),
    transform 420ms var(--ease),
    opacity 240ms ease,
    filter 240ms ease;
}
.release-link svg {
  position: absolute;
  top: calc(50% - var(--sign) / 2);
  width: var(--sign);
  height: var(--sign);
}
.release-apple {
  left: calc(var(--inset) - 3 * var(--u));
  transform-origin: left center;
}
.release-arrow {
  right: calc(var(--inset) - 3 * var(--u));
  opacity: 0;
  filter: blur(2px);
  transform: translateX(100%) scaleX(0.5);
  transform-origin: right center;
}
.release-link:focus-visible .release-apple {
  opacity: 0;
  filter: blur(2px);
  transform: translateX(-100%) scaleX(0.5);
}
.release-link:focus-visible span {
  transform: translateX(calc(-1 * var(--lead)));
}
.release-link:focus-visible .release-arrow {
  opacity: 1;
  filter: none;
  transform: none;
}
@media (hover: hover) {
  .release-link:hover .release-apple {
    opacity: 0;
    filter: blur(2px);
    transform: translateX(-100%) scaleX(0.5);
  }
  .release-link:hover span {
    transform: translateX(calc(-1 * var(--lead)));
  }
  .release-link:hover .release-arrow {
    opacity: 1;
    filter: none;
    transform: none;
  }
}
/* The section glyphs play their hovers: the FAQ question turns into an
   answer, and the heart beats. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .site-header nav a:hover .i-hook {
    stroke-dashoffset: -14;
    transition: stroke-dashoffset 180ms var(--ease-inout);
  }
  .site-header nav a:hover .i-bar {
    stroke-dashoffset: 0;
    transition: stroke-dashoffset 280ms var(--ease) 120ms;
  }
  .site-header nav a:hover .i-buoy {
    transform: rotate(90deg);
  }
  /* The other language writes itself: 文 in stroke order (dot, bar, the
     falling left, the falling right), or A (the legs, then the bar). Leaving
     snaps back to a whole stroke, which looks the same. */
  .lang-switch:lang(zh):hover .i-zh,
  .lang-switch:lang(en):hover .i-en {
    stroke-dashoffset: calc(-1 * var(--period));
    transition: stroke-dashoffset 640ms var(--write);
  }
  .lang-switch:lang(en):hover .i-en {
    transition-duration: 520ms;
  }
  .lang-switch:lang(zh):hover .i-zh.i-back,
  .lang-switch:lang(en):hover .i-en.i-back {
    stroke-dashoffset: var(--period);
  }
}
.is-scrolled :is(nav a, .lang-switch, .release-link) {
  --r: calc(var(--control-h) / 2);
}

h1 {
  font-size: clamp(50 * var(--u), 5.5vw, 82 * var(--u));
  font-weight: 700;
  line-height: 1.02;
  letter-spacing: -0.04em;
}
/* Each headline line is its own mask so the words can rise out of it. The
   padding keeps descenders inside the clip; the negative margin returns it. */
h1 .line {
  display: block;
  overflow: clip;
  padding-bottom: 0.12em;
  margin-bottom: -0.12em;
}
h1 .line > span {
  display: block;
}
h1 .muted {
  color: var(--soft);
}
.faq details p a,
.lang-link {
  text-decoration: underline;
  text-decoration-color: #9eacb3;
  transition: text-decoration-color var(--dur-fast) ease;
}
.faq details p a:hover,
.lang-link:hover {
  color: var(--ink);
  text-decoration-color: currentColor;
}
/* The Dock's glass, as macOS 27 draws it in the dark appearance: the picture
   behind, lightly blurred and held at half its light over a grey
   (0.5 × backdrop + 24), a faint glow inside the top and bottom edges, a
   rim lit along the top and bottom and nearly absent on the sides, and a
   thin dark outline with a soft shadow outside. Measured against a real
   Dock on macOS 27. Every Dock and shelf on the site wears it: this Mac's,
   the account world's (desktop.css) and the sponsor wall's shelves
   (sponsors.css). --gu is one unit of the glass, a point on a Dock whose
   icons are 44pt; the others set it to their own scale. */
.mac-screen .dock,
.desk-dock,
.dock-shelf {
  background:
    linear-gradient(rgb(255 255 255 / 0.13), rgb(255 255 255 / 0) calc(6 * var(--gu, 1px))),
    linear-gradient(to top, rgb(255 255 255 / 0.1), rgb(255 255 255 / 0) calc(7 * var(--gu, 1px))),
    rgb(48 48 48 / 0.5);
  box-shadow:
    inset 0 var(--gu, 1px) 0 rgb(255 255 255 / 0.4),
    inset 0 calc(-1 * var(--gu, 1px)) 0 rgb(255 255 255 / 0.34),
    0 0 0 1px rgb(0 0 0 / 0.3),
    0 calc(2 * var(--gu, 1px)) calc(14 * var(--gu, 1px)) rgb(0 0 0 / 0.28);
  -webkit-backdrop-filter: blur(calc(6 * var(--gu, 1px)));
  backdrop-filter: blur(calc(6 * var(--gu, 1px)));
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .mac-screen .dock,
  .desk-dock,
  .dock-shelf {
    background-color: rgb(34 34 38 / 0.9);
  }
}
.nav-icon * {
  transform-box: fill-box;
  transform-origin: center;
  transition:
    transform 560ms var(--ease),
    opacity 320ms ease,
    stroke 320ms ease;
}
/* Priority Support's life buoy turns a quarter about its centre when pointed at
   (the hover rules), and turns back when let go: .nav-icon *'s transition. */
.i-buoy {
  transform-box: fill-box;
  transform-origin: center;
}

/* FAQ, after Buy Me a Coffee's: one centred column. The heading and three
   ways out (pricing, support, source) sit above a stack of soft tiles, ink at
   5% with no rule and no shadow. Each question opens in place and its chevron
   turns over; the first stands open, so the section shows an answer before it
   asks for a click. */
.faq {
  margin-top: calc(104 * var(--u));
  padding-top: calc(56 * var(--u));
  border-top: 1px solid var(--line);
}
.faq-intro {
  text-align: center;
}
.faq-intro > p {
  margin-top: calc(16 * var(--u));
  color: var(--muted);
  font-size: calc(15 * var(--u));
  line-height: 1.7;
}
.faq-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: calc(10 * var(--u));
  margin-top: calc(28 * var(--u));
}
.faq-links a {
  display: inline-flex;
  align-items: center;
  min-height: calc(44 * var(--u));
  padding: 0 calc(18 * var(--u));
  border: 1.5px solid var(--ink);
  border-radius: calc(30 * var(--u));
  font-size: calc(14 * var(--u));
  font-weight: 600;
  transition:
    background-color var(--dur-fast) ease,
    border-color var(--dur-fast) ease,
    transform 120ms ease;
}
.faq-links a:hover {
  border-color: var(--accent);
  background: var(--tint);
}
.faq-links a:active {
  transform: scale(0.97);
}
.faq-links a:focus-visible {
  border-radius: calc(30 * var(--u));
}
.faq-list {
  display: grid;
  gap: calc(12 * var(--u));
  width: min(100%, 680 * var(--u));
  margin: calc(48 * var(--u)) auto 0;
}
.faq details {
  border-radius: calc(16 * var(--u));
  background: rgb(24 33 38 / 0.05);
  transition: background-color var(--dur-fast) ease;
}
.faq details:has(> summary:hover) {
  background: rgb(24 33 38 / 0.08);
}
/* Focus rings the whole tile; without :has the summary keeps its own ring. */
.faq details:has(> summary:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
.faq details:has(> summary:focus-visible) > summary {
  outline: none;
}
.faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: calc(20 * var(--u));
  min-height: calc(44 * var(--u));
  padding: calc(20 * var(--u)) calc(24 * var(--u));
  font-family: var(--display);
  font-size: calc(20 * var(--u));
  line-height: 1.4;
  letter-spacing: -0.01em;
  cursor: pointer;
  list-style: none;
}
.faq summary::-webkit-details-marker {
  display: none;
}
.faq summary svg {
  width: calc(20 * var(--u));
  height: calc(20 * var(--u));
  stroke-width: 2;
  transition: transform var(--dur) var(--ease);
}
.faq details[open] summary svg {
  transform: rotate(-180deg);
}
.faq details p {
  padding: 0 calc(64 * var(--u)) calc(22 * var(--u)) calc(24 * var(--u));
  color: var(--muted);
  font-size: calc(15 * var(--u));
  line-height: 1.75;
}
/* The measured numbers in the power answer: three columns of figures that
   line up, ruled by hairlines, at the answer's own size. */
.faq-table {
  width: calc(100% - 88 * var(--u));
  margin: 0 calc(64 * var(--u)) calc(16 * var(--u)) calc(24 * var(--u));
  border-collapse: collapse;
  color: var(--ink);
  font-size: calc(14 * var(--u));
  font-variant-numeric: tabular-nums;
  line-height: 1.45;
}
.faq-table th,
.faq-table td {
  padding: calc(9 * var(--u)) calc(16 * var(--u)) calc(9 * var(--u)) 0;
  border-bottom: 1px solid var(--line);
  text-align: left;
  vertical-align: top;
}
.faq-table thead th {
  color: var(--muted);
  font-weight: 600;
}
.faq-table tbody th {
  font-weight: 400;
}
.faq-table td:last-child {
  font-weight: 600;
}
.faq-table + p {
  padding-top: 0;
}
@supports selector(::details-content) {
  .faq details::details-content {
    block-size: 0;
    overflow: clip;
    opacity: 0;
    transition:
      block-size 420ms var(--ease),
      opacity 300ms ease,
      content-visibility 420ms allow-discrete;
  }
  .faq details[open]::details-content {
    block-size: auto;
    opacity: 1;
  }
}

/* Launch: the page's closing bookend. The app icon stands as a Mac app, lit
   from behind in the tones of the work that hangs (script.js hands --vivid
   and --deep to this section as it does to the footer), and "Mac"
   wears the same tones, so the page closes on the wallpaper you last saw.
   No rule above it: the glow and the air set it apart from the sponsor
   wall. */
.launch {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: calc(112 * var(--u));
  padding-block: calc(112 * var(--u)) calc(104 * var(--u));
  text-align: center;
  transition:
    --vivid 1200ms ease,
    --deep 1200ms ease;
}
.launch-mark {
  position: relative;
  width: calc(136 * var(--u));
  height: calc(136 * var(--u));
}
/* The glow: the work's vivid tone over its deep one, soft enough to read as
   light falling on the paper rather than a coloured field. */
.launch-mark::before {
  content: "";
  position: absolute;
  inset: -70% -140%;
  z-index: -1;
  background:
    radial-gradient(
      closest-side,
      color-mix(in oklab, var(--vivid) 34%, transparent),
      color-mix(in oklab, var(--vivid) 10%, transparent) 55%,
      transparent
    ),
    radial-gradient(
      closest-side at 50% 70%,
      color-mix(in oklab, var(--deep) 16%, transparent),
      transparent
    );
  pointer-events: none;
}
/* The icon is the machine, and a button the size of its plate (the squircle
   spans 824 of the drawing's 1024 units). It is brand/app-icon.svg drawn in
   layers so it can work: the white plate, then the face, which holds the
   screen, the frame and the gear. At rest it holds still and matches the
   file. */
.launch-icon {
  position: absolute;
  inset: 9.766%;
  padding: 0;
  border: 0;
  border-radius: 22.4%;
  background: none;
  filter: drop-shadow(0 1px 1px rgb(24 33 38 / 0.14))
    drop-shadow(
      0 calc(22 * var(--u)) calc(24 * var(--u))
        color-mix(in oklab, var(--deep) 38%, transparent)
    );
  transition: transform 160ms var(--ease);
}
.launch-icon:focus-visible {
  outline-offset: 4px;
  border-radius: 22.4%;
}
/* A press is felt: the plate gives a little under it. */
.launch-icon:active {
  transform: scale(0.97);
}
/* Pointed at, the icon turns to face the pointer (script.js writes --tilt-x
   and --tilt-y here) and its face lifts off the plate, so the white plate
   slides under the screen as it turns. It follows quickly and settles flat
   slowly once the pointer leaves. */
.launch-tilt {
  position: absolute;
  inset: -12.136%;
  transform: perspective(calc(640 * var(--u))) rotateX(var(--tilt-x, 0deg))
    rotateY(var(--tilt-y, 0deg));
  transform-style: preserve-3d;
  transition: transform 900ms var(--ease);
}
.launch-icon.is-near .launch-tilt {
  transition-duration: 320ms;
}
.launch-face {
  position: absolute;
  inset: 0;
  transition: transform 700ms var(--ease);
}
.launch-icon.is-near .launch-face {
  transform: translateZ(calc(16 * var(--u)));
}
.launch-plate,
.launch-parts,
.launch-pixels {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  fill: initial;
  stroke: initial;
  stroke-width: initial;
  stroke-linecap: initial;
  stroke-linejoin: initial;
  forced-color-adjust: none;
}
/* The pointer's light (src/client/launch-light.ts, which script.js loads
   where WebGPU runs): one canvas shades the plate, under the face; the other
   lights the face, over its frame and gear. Both are empty at rest. */
.launch-light {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
/* The screen, cut to the icon's lens: a disc inside the ring. */
.launch-screen {
  position: absolute;
  inset: 25.39%;
  border-radius: 50%;
  overflow: hidden;
  forced-color-adjust: none;
}
/* The wallpaper the lens bends (src/client/launch-glass.ts). The screen's
   disc clips it; the filter defs sit in the screen and do not paint. */
.launch-glass-stage {
  position: absolute;
  inset: 0;
}
.launch-glass-defs {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
  pointer-events: none;
}
/* The centre frame: white over a wider navy underlay, as the app icon's
   pixel frame is drawn. It never turns. */
path.launch-gear {
  fill: #fff;
}
path.launch-gear-under {
  fill: rgb(11 42 91 / 0.6);
}
/* Pressing the icon lights the pixels (script.js sets .is-rippling on
   .launch-icon and clears it when the last cell is done): each cell dips and
   comes back, a diagonal at a time, from the top-left (--d = column + row). */
@media (prefers-reduced-motion: no-preference) {
  .launch-icon.is-rippling .launch-cell {
    animation: cell-ripple 600ms var(--ease-inout) calc(var(--d, 0) * 60ms) both;
  }
}
@keyframes cell-ripple {
  50% {
    opacity: 0.35;
  }
}
/* The closing echo of the hero heading, so it shares the hero's weight. */
.launch h2 {
  margin-top: calc(36 * var(--u));
  font-size: clamp(40 * var(--u), 4.5vw, 64 * var(--u));
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1.06;
}
.launch h2 > span {
  color: var(--soft);
}
/* The shortcut that starts a capture, in ink against the slate line, as the hero
   sets its selected "Mac". */
.launch-mac {
  color: var(--ink);
}
/* The closing keys are the hero's own (.hero-button, below): the GitHub key
   with its marks, Priority Support with its turning buoy, both
   pressing to 0.98, so the page opens and closes on one set. The row is as
   wide as its keys and, once they wrap, each spans the column, as under the
   hero. */
.launch-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: calc(12 * var(--u));
  width: fit-content;
  max-width: 100%;
  margin: calc(40 * var(--u)) auto 0;
}
/* The documentation is a third key of the same set, with no ground until it
   is pointed at, so it is the quietest of the three. Lucide's book-open
   leads it, and under the pointer its right-hand leaf turns over to the
   left: the leaf is a copy of that page, shown only while it turns, so the
   book at rest is drawn once. It opens the source's README in a new tab, so
   it ends in Lucide's arrow-up-right, which comes to full strength and
   nudges out as the byline's does; the arrow's margins take back the air
   inside its box, so the key's ends look even. */
.launch-actions .launch-docs {
  background-color: transparent;
}
.launch-actions .launch-docs:hover {
  background-color: rgb(24 33 38 / 0.06);
}
.launch-docs .i-leaf {
  opacity: 0;
  transform-box: view-box;
  transform-origin: 12px 12px;
}
.launch-docs-out {
  margin: 0 calc(-5 * var(--u)) 0 calc(-3 * var(--u));
  opacity: 0.45;
  transition:
    opacity var(--dur-fast) ease,
    transform var(--dur) var(--ease);
}
.launch-docs:is(:hover, :focus-visible) .launch-docs-out {
  opacity: 1;
  transform: translate(2px, -2px);
}
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .launch-docs:hover .i-leaf {
    animation: leaf 720ms var(--ease-inout);
  }
}
@keyframes leaf {
  from {
    opacity: 1;
    transform: none;
  }
  to {
    opacity: 1;
    transform: scaleX(-1);
  }
}
.lang-switch:active,
.release-link:active {
  transform: scale(0.98);
}

/* Footer: the page signs off. The paper sheet ends in two rounded corners
   over the night ground beneath it. On the night, the name spans the measure
   and sinks into the bottom edge, lit from below in the app icon's Ocean blue.
   The page tokens are remapped here, so every footer rule that says ink, muted
   or accent reads correctly on the dark. Muted is a pale tint of the Ocean
   blue, not a grey, so the small print over the brightest part of the glow
   still reads at 4.5:1. --pull is how hard the page is pushed past its end
   (see the floor, below): the vivid glow swells with it. */
.site-footer {
  --lip: clamp(24 * var(--u), 3.4vw, 48 * var(--u));
  --pull: 0;
  --ink: var(--paper);
  --muted: #d6e5f7;
  --accent: #b9ccd4;
  position: relative;
  isolation: isolate;
  padding: calc(var(--lip) + 72 * var(--u)) 0 calc(26 * var(--u));
  color: var(--ink);
  background:
    radial-gradient(
      ellipse 56% calc(62% + var(--pull) * 28%) at 50% 112%,
      color-mix(in oklab, var(--vivid) calc(34% + var(--pull) * 30%), transparent),
      transparent 72%
    ),
    radial-gradient(
      ellipse 96% 78% at 50% 100%,
      color-mix(in oklab, var(--deep) 72%, transparent),
      transparent 78%
    ),
    var(--night);
  transition:
    --vivid 1200ms ease,
    --deep 1200ms ease;
}
/* Pricing and the account world hang no work, so their footer wears the
   site's own wallpaper (the app icon's colours folded like silk): its tones
   light the glow and its folds rise inside the name from the floor. The works
   carry third-party rights and stay on the home page. The floor repaints on
   every frame of a push, so it takes the wallpaper as a raster
   (wallpaper.webp, rendered from wallpaper.svg), whose blur filters would
   otherwise be drawn again each time. */
.site-footer[data-wallpaper="brand"] {
  --vivid: #5E9CF5;
  --deep: #0B2A5B;
  --poster: url("/assets/desktop/wallpaper.webp");
  --poster-y: 50%;
}
/* The end of the paper sheet: a strip of paper whose rounded lower corners
   let the night show through. It reaches a pixel above the footer so a
   fractional page height never leaves a hairline of night at the seam. */
.site-footer::before {
  content: "";
  position: absolute;
  inset: -1px 0 auto;
  height: calc(var(--lip) + 1px);
  background: var(--paper);
  border-radius: 0 0 var(--lip) var(--lip);
  pointer-events: none;
}
.site-footer ::selection {
  background: #375865;
  color: var(--paper);
}
.footer-inner {
  container-type: inline-size;
}
.footer-top {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  column-gap: calc(16 * var(--u));
}
.footer-brand {
  display: block;
  border-radius: calc(10 * var(--u));
}
.footer-brand img {
  width: calc(44 * var(--u));
  height: calc(44 * var(--u));
}
.footer-line {
  display: flex;
  align-items: center;
  min-height: calc(44 * var(--u));
  color: var(--muted);
  font-size: calc(13 * var(--u));
  line-height: 1.5;
}
.footer-nav {
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: repeat(4, calc(40 * var(--u)));
  column-gap: calc(52 * var(--u));
  margin-top: calc(-8 * var(--u));
}
.footer-nav a {
  position: relative;
  justify-self: start;
  align-self: center;
  padding: calc(6 * var(--u)) 0;
  font-size: calc(14 * var(--u));
  color: color-mix(in oklab, var(--ink) 84%, transparent);
  transition: color var(--dur-fast) ease;
}
.footer-nav a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(3 * var(--u));
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 420ms var(--ease);
}
.footer-nav a:hover {
  color: var(--ink);
}
.footer-nav a:hover::after {
  transform: scaleX(1);
  transform-origin: left;
}
.footer-wordmark {
  margin: calc(52 * var(--u)) 0 0 -0.031em;
  padding-bottom: 0.14em;
  overflow: clip;
  font-family: var(--display);
  font-size: 22.07cqi;
  line-height: 0.9;
  letter-spacing: -0.04em;
  white-space: nowrap;
  user-select: none;
  /* Pushed, the name lifts a little and out of the floor's haze. */
  -webkit-mask-image: linear-gradient(#000 35%, rgb(0 0 0 / calc(0.16 + var(--pull) * 0.6)));
  mask-image: linear-gradient(#000 35%, rgb(0 0 0 / calc(0.16 + var(--pull) * 0.6)));
  translate: 0 calc(var(--pull) * -0.08em);
}
.footer-wordmark > span {
  position: relative;
  display: block;
  transition:
    transform 1500ms var(--ease),
    filter 1500ms var(--ease),
    opacity 900ms ease,
    clip-path 1300ms var(--ease-sweep);
}
/* Armed by site.js only when motion is allowed: the name rises out of the
   floor and sweeps in left to right, the way a work is hung above. */
.site-footer.is-armed:not(.is-in) .footer-wordmark > span {
  transform: translateY(58%);
  filter: blur(12px);
  opacity: 0;
  clip-path: inset(-20% 100% -40% 0);
}
.site-footer.is-armed .footer-wordmark > span {
  clip-path: inset(-20% -2% -40% 0);
}
/* The floor. The page stops at its end (overscroll-behavior on the root), so
   pushing past it no longer drags blank canvas into view. The push goes into
   the floor instead: the name lifts out of its haze, and the wallpaper that
   hangs rises inside its letters from the floor, its waterline lit in the
   work's vivid tone. site.js adds this layer (a copy of the name) and
   writes --pull; let go and the level drains back down. The letters' ink
   runs from 14.5% to 82.4% of the layer's height, so an empty floor sits
   just below it and a full one just above. */
.footer-fill {
  display: none;
}
@supports ((background-clip: text) or (-webkit-background-clip: text)) {
  .footer-fill {
    --level: calc(12% + var(--pull) * 80%);
    --wash:
      linear-gradient(
        to top,
        transparent calc(var(--level) - 9%),
        color-mix(in oklab, var(--vivid) 45%, white) var(--level)
      ),
      linear-gradient(
        to top,
        var(--vivid),
        color-mix(in oklab, var(--vivid) 30%, white)
      ),
      var(--poster);
    position: absolute;
    inset: -0.2em 0 -0.3em;
    display: block;
    padding-top: 0.2em;
    color: transparent;
    background-image: var(--wash);
    background-blend-mode: normal, multiply, normal;
    background-repeat: no-repeat;
    background-size:
      100% 100%,
      100% 100%,
      cover;
    background-position:
      0 0,
      0 0,
      50% var(--poster-y, 40%);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-mask-image: linear-gradient(to top, #000 var(--level), transparent calc(var(--level) + 2%));
    mask-image: linear-gradient(to top, #000 var(--level), transparent calc(var(--level) + 2%));
    pointer-events: none;
  }
}
/* Stacked copies of a glyph each soften the same edge, so what lies beneath
   shows through as a pale rim: the name under the layer, and each background
   layer clipped to the letters on its own. Where a mask can be clipped to
   text, the layer paints the whole name instead, ink above the waterline, as
   one picture cut to the letters once, and the name's own letters step
   aside. Firefox can't clip a mask to text and keeps the stack. */
@supports (-webkit-mask-clip: text) {
  .footer-wordmark > span:has(> .footer-fill) {
    color: transparent;
  }
  .footer-fill {
    background-image:
      linear-gradient(to top, transparent var(--level), var(--ink) calc(var(--level) + 2%)),
      var(--wash);
    background-blend-mode: normal, normal, multiply, normal;
    background-size:
      100% 100%,
      100% 100%,
      100% 100%,
      cover;
    background-position:
      0 0,
      0 0,
      0 0,
      50% var(--poster-y, 40%);
    -webkit-background-clip: border-box;
    background-clip: border-box;
    -webkit-mask-image: linear-gradient(#000, #000);
    mask-image: linear-gradient(#000, #000);
    -webkit-mask-clip: text;
  }
}
.back-top {
  display: inline-flex;
  align-items: center;
  gap: calc(9 * var(--u));
  min-height: calc(44 * var(--u));
  font-size: calc(12 * var(--u));
  white-space: nowrap;
  color: var(--ink);
  transition: color var(--dur-fast) ease;
}
.back-top:hover {
  color: var(--accent);
}
.back-top svg {
  width: calc(16 * var(--u));
  height: calc(16 * var(--u));
  transform: rotate(-90deg);
  transition: transform 220ms var(--ease);
}
.back-top:hover svg {
  transform: rotate(-90deg) translateX(3px);
}
.footer-bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: calc(18 * var(--u));
  margin-top: calc(18 * var(--u));
  color: var(--muted);
  font-size: calc(12 * var(--u));
  line-height: 1.7;
}
/* A 44px target around a line of 12px footer text, without moving the line. */
.lang-link {
  display: inline-block;
  padding-block: calc(12 * var(--u));
  margin-block: calc(-12 * var(--u));
}
/* The footer's last line: the home page credits the works it shows; pricing
   and the account world name Paddle there instead. */
.artwork-note,
.footer-note {
  margin-top: calc(6 * var(--u));
  color: var(--muted);
  font-size: calc(12 * var(--u));
  line-height: 1.7;
}

/* Section headings share one display step. */
.faq h2 {
  font-size: clamp(32 * var(--u), 3.2vw, 45 * var(--u));
  letter-spacing: -0.025em;
  line-height: 1.1;
}

/* Intro: the heading is centred over the exhibition. It stays in normal flow
   and scrolls away as the stage pins beneath the header. */
.gallery-intro {
  padding: calc(30 * var(--u)) 0 calc(28 * var(--u));
  text-align: center;
}
/* The home page's first view holds the demo desktop under the heading, as the
   approved composition (.impeccable/mocks/home-a.png) sets it: a lighter
   headline, room under it for the word's reading, keys a little lower than the
   rest row's, and a tight run down to the stage, so the whole desktop shows.
   Phones keep the shared headline size (the 760px query). */
@media (min-width: 761px) {
  #hero-title {
    font-size: clamp(40 * var(--u), 4.05vw, 58 * var(--u));
  }
}
.gallery-intro:has(#hero-title) {
  padding-top: calc(22 * var(--u));
  padding-bottom: calc(26 * var(--u));
}
/* The keys come up under the word's reading, which hangs in the headline's own
   last 36px (.capture-word's margin), as close over them as the composition
   draws it. The headline keeps its line masks' negative margin inside itself
   (flow-root), so it never collapses with the keys' negative margin into one. */
#hero-title {
  display: flow-root;
}
.gallery-intro:has(#hero-title) .hero-actions {
  margin-top: calc(29 * var(--u) - 36px);
}
.gallery-intro:has(#hero-title) .hero-note {
  margin-top: calc(13 * var(--u));
  margin-bottom: 0;
}
/* The selected word and the words it reads with stay on one line: a box like
   the word's always offers a break on either side, which a no-break space
   cannot hold shut. */
.hero-keep {
  white-space: nowrap;
}
/* "Mac", selected: OhMyShot's selection edge (a white line between two 1px dark
   ones, at 2px so it holds at the headline's size) around the word's line box,
   with the thick corners the app draws on a frame that can still be adjusted
   (3px white over a 5.5px outline at 0.32, round caps, 18px arms), and inside
   the frame the reading capsule, centred under its edge, with the frame's size
   in device pixels (script.js measures it), so it follows the frame as it is
   dragged out. The word is a key: it takes the visitor to the demo with Capture
   Area armed. */
.capture-word {
  position: relative;
  display: inline-block;
  /* Room under the word for its reading: the line that holds it grows by this
     much, so where the headline wraps (Chinese on a phone) the next line stands
     clear of the reading instead of under it. */
  margin-bottom: 36px;
  color: var(--ink);
  cursor: crosshair;
}
.capture-word:focus-visible {
  outline-offset: calc(0.08em + 6px);
}
.capture-frame {
  position: absolute;
  inset: -0.02em -0.08em;
  border: 2px solid #fff;
  box-shadow:
    0 0 0 1px rgb(0 0 0 / 0.32),
    inset 0 0 0 1px rgb(0 0 0 / 0.32);
  pointer-events: none;
}
/* The corners sit on the edge's centre line, 1px out from the frame's inside. */
.capture-corners {
  position: absolute;
  top: -1px;
  left: -1px;
  width: calc(100% + 2px);
  height: calc(100% + 2px);
  overflow: visible;
}
.capture-size {
  position: absolute;
  /* 6px under the frame's outer edge, where the composition hangs it close over
     the keys (the app's own reading stands 10pt under a selection). */
  top: calc(100% + 2px + 6px);
  left: 50%;
  translate: -50% 0;
  display: inline-flex;
  align-items: center;
  height: 24px;
  padding: 0 11px;
  border: 0.5px solid rgb(0 0 0 / 0.08);
  border-radius: 12px;
  background: rgb(255 255 255 / 0.96);
  box-shadow: 0 2px 8px rgb(0 0 0 / 0.18);
  color: rgb(0 0 0 / 0.85);
  font: 600 12px / 1 ui-rounded, "SF Pro Rounded", system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  white-space: pre;
  pointer-events: none;
}
.capture-size:empty {
  display: none;
}
@media (forced-colors: active) {
  .capture-frame {
    border-color: Highlight;
  }
  .capture-corners {
    display: none;
  }
  .capture-size {
    border: 1px solid CanvasText;
  }
}
/* The two ways in, under the heading: the App Store key in ink beside the
   direct download as a quiet key. They are set as the rest row's keys are
   (52px, 14px corners, 17px semibold), so the header and the hero read as
   one set. The row is only as wide as its buttons; once they no longer fit
   side by side they wrap, and each then spans the column. */
.hero-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: calc(12 * var(--u));
  width: fit-content;
  max-width: 100%;
  margin: calc(32 * var(--u)) auto 0;
}
/* The stage sets no padding of its own, so the note under the row keeps its
   own air. */
.hero-note {
  margin-bottom: calc(18 * var(--u));
}
.hero-button {
  --r: calc(14 * var(--u));
  flex: 1 1 auto;
  display: inline-flex;
  justify-content: center;
  align-items: center;
  gap: calc(10 * var(--u));
  min-height: calc(45 * var(--u));
  padding: 0 calc(22 * var(--u)) 0 calc(20 * var(--u));
  border: 0;
  border-radius: var(--r);
  color: var(--ink);
  background-color: rgb(24 33 38 / 0.06);
  font-size: calc(17 * var(--u));
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
  transition:
    background-color var(--dur-fast) ease,
    transform 120ms ease;
}
.hero-button:hover {
  background-color: rgb(24 33 38 / 0.1);
}
.hero-button:active {
  transform: scale(0.98);
}
.hero-button:focus-visible {
  border-radius: var(--r);
}
.hero-button svg {
  width: calc(20 * var(--u));
  height: calc(20 * var(--u));
  stroke-width: 1.75;
}
/* The download plays the release status's hover: the Apple mark slips out,
   the words make room and an arrow slides in to say it goes somewhere, to
   the latest release. The marks ride with the words, so they stay beside
   them when the key spans the column. --inset is the side padding, --sign
   the arrow's size, --apple-h and --apple the Apple mark's height and width,
   and --lead the room it takes, which the words give back on hover. */
.hero-download {
  --inset: calc(22 * var(--u));
  --sign: calc(20 * var(--u));
  --apple-h: 1.1em;
  --apple: calc(var(--apple-h) * 0.8);
  --lead: calc(var(--apple) + 12 * var(--u));
  padding: 0 var(--inset);
  color: #fff;
  background-color: var(--ink);
}
/* The download keeps its ink under the pointer; the marks are its hover. */
.hero-download:hover {
  background-color: var(--ink);
}
.hero-download-body {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: inherit;
  padding-left: var(--lead);
}
.hero-download-body > * {
  transition:
    transform 420ms var(--ease),
    opacity 240ms ease,
    filter 240ms ease;
}
.hero-download svg {
  position: absolute;
  top: calc(50% - var(--sign) / 2);
  width: var(--sign);
  height: var(--sign);
}
/* The Apple mark is drawn as Alcove draws its hero's: its box is cut to the
   mark (the viewBox, 4:5), 1.1em tall (about 1.6 capitals), with its middle
   a hair (0.04em) above the key's, where the words' capitals sit. Its edge
   stands where Priority Support's glyph starts, and it keeps the same room to
   the words. */
.hero-download .hero-apple {
  top: calc(50% - var(--apple-h) / 2 - 0.04em);
  left: 0;
  width: var(--apple);
  height: var(--apple-h);
  transform-origin: left center;
}
/* Until the first release the key builds from source and leads with the GitHub
   mark, which is square and drawn a little larger than the Apple mark. */
.hero-download:has(.hero-mark) {
  --apple-h: 1.3em;
  --apple: 1.3em;
}
.hero-arrow {
  right: 0;
  opacity: 0;
  filter: blur(2px);
  transform: translateX(100%) scaleX(0.5);
  transform-origin: right center;
}
.hero-download:focus-visible .hero-apple {
  opacity: 0;
  filter: blur(2px);
  transform: translateX(-100%) scaleX(0.5);
}
.hero-download:focus-visible .hero-download-body > span {
  transform: translateX(calc(-1 * var(--lead)));
}
.hero-download:focus-visible .hero-arrow {
  opacity: 1;
  filter: none;
  transform: none;
}
@media (hover: hover) {
  .hero-download:hover .hero-apple {
    opacity: 0;
    filter: blur(2px);
    transform: translateX(-100%) scaleX(0.5);
  }
  .hero-download:hover .hero-download-body > span {
    transform: translateX(calc(-1 * var(--lead)));
  }
  .hero-download:hover .hero-arrow {
    opacity: 1;
    filter: none;
    transform: none;
  }
}
/* Keep the growing heart's stroke visible at the peak of its pulse. */
.hero-direct svg {
  overflow: visible;
}
.hero-direct .i-buoy {
  transition: transform 560ms var(--ease);
}
/* Pointed at, Priority Support's buoy turns a quarter, as the header's does. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .hero-direct:hover .i-buoy {
    transform: rotate(90deg);
  }
}
/* A small turn and two glints answer the GitHub key; transitions reverse
   smoothly if the pointer leaves early. Reduced motion keeps only the fill. */
.hero-github svg {
  overflow: visible;
}
.hero-github .i-star {
  transform-box: view-box;
  transform-origin: 12px 12px;
  fill: currentColor;
  fill-opacity: 0;
}
.hero-github .i-star-glint {
  opacity: 0;
  stroke-width: 1.4;
}
.hero-github:focus-visible .i-star {
  fill-opacity: 0.2;
}
@media (hover: hover) and (pointer: fine) {
  .hero-github:hover .i-star {
    fill-opacity: 0.2;
  }
}
@media (prefers-reduced-motion: no-preference) {
  .hero-github .i-star {
    transition: transform 320ms var(--ease), fill-opacity 200ms ease;
  }
  .hero-github .i-star-glint {
    transform: translate(-1px, 1px);
    transition: transform 320ms var(--ease), opacity 200ms ease;
  }
  .hero-github:focus-visible .i-star {
    transform: rotate(15deg) scale(1.1);
  }
  .hero-github:focus-visible .i-star-glint {
    transform: none;
    opacity: 1;
  }
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .hero-github:hover .i-star {
    transform: rotate(15deg) scale(1.1);
  }
  .hero-github:hover .i-star-glint {
    transform: none;
    opacity: 1;
  }
}
/* Under the keys, what the app needs and which build it is, in hint type
   (label at 400) in Slate Grey. Each fact keeps to one line and the row
   wraps between them; the dots that part them are silent to screen
   readers, which hear a list. */
.hero-note {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  margin: calc(16 * var(--u)) 0 0;
  padding: 0;
  list-style: none;
  color: var(--muted);
  font-size: calc(13 * var(--u));
  line-height: 1.6;
}
.hero-note li {
  white-space: nowrap;
}
.hero-note li + li::before {
  content: "·";
  content: "·" / "";
  margin: 0 calc(8 * var(--u));
}

/* Opening: the artwork is hung. The frame opens from a slit, the picture
   settles into it, the headline rises out of its line masks, and the fittings
   arrive last. Keyed to html.intro, which site.js removes when it is over. */
.intro h1 .line > span {
  animation: rise 1000ms var(--ease) 80ms both;
}
.intro h1 .line + .line > span {
  animation-delay: 170ms;
}
/* The hero's second line holds the selected word, whose frame and reading
   reach past the line box. Its mask is a clip that leaves them room above and
   below, and its words rise from under that room. */
#hero-title .line + .line {
  overflow: visible;
  clip-path: inset(-0.5em -100vw calc(-0.3em - 40px));
}
.intro #hero-title .line + .line > span {
  animation-name: rise-under;
}
/* The two ways in arrive as the heading lands, a beat apart, and the note
   under them a beat later. */
.intro .hero-actions > * {
  animation: arrive 800ms var(--ease) 360ms both;
}
.intro .hero-actions > * + * {
  animation-delay: 420ms;
}
.intro .hero-note {
  animation: arrive 800ms var(--ease) 480ms both;
}
/* Once the headline has risen, "Mac" is selected the way the overlay selects an
   area: the frame is dragged out from the word's top-left corner and its
   reading counts up with it (script.js measures the frame whenever it resizes). */
@media (prefers-reduced-motion: no-preference) {
  .intro .capture-frame {
    animation: capture-drag var(--gather) var(--ease-inout) 900ms both;
  }
  .intro .capture-size {
    animation: arrive 320ms var(--ease) 900ms both;
  }
}
.intro .desk-frame {
  animation: hang var(--dur-focal) var(--ease) 260ms both;
}
.intro .desk-frame > .desk-scene {
  animation: settle 1900ms var(--ease) 260ms both;
}
.intro .desk-turns {
  animation: arrive 800ms var(--ease) 900ms both;
}
/* The header opens first: the icon rises into place, then the name, the
   sections and the status arrive in turn. */
.intro .brand-icon {
  animation: header-rise 900ms var(--ease) 40ms both;
}
.intro :is(.brand-name > span, .site-header nav, .lang-switch, .release-link) {
  animation: arrive 700ms var(--ease) 140ms both;
}
.intro .site-header nav {
  animation-delay: 220ms;
}
.intro .lang-switch {
  animation-delay: 260ms;
}
.intro .release-link {
  animation-delay: 300ms;
}
@keyframes header-rise {
  from {
    opacity: 0;
    transform: translateY(16px) scale(0.75) rotate(6deg);
  }
}
@keyframes rise {
  from {
    transform: translateY(108%);
  }
}
@keyframes rise-under {
  from {
    transform: translateY(calc(108% + 0.3em + 40px));
  }
}
@keyframes hang {
  from {
    clip-path: inset(49.5% 0 round calc(8 * var(--u)));
  }
  to {
    clip-path: inset(0 round calc(8 * var(--u)));
  }
}
@keyframes settle {
  from {
    transform: scale(1.14);
  }
}
@keyframes arrive {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
}
@keyframes capture-drag {
  from {
    right: calc(100% + 0.08em);
    bottom: calc(100% + 0.02em);
    opacity: 0;
  }
  6% {
    opacity: 1;
  }
}

@media (max-width: 1080px) {
  :root {
    --gutter: 32px;
  }
  .gallery-intro {
    padding: 26px 0 24px;
  }
  .hero-actions {
    margin-top: 28px;
  }
  .hero-button {
    min-height: 50px;
    font-size: 16px;
  }
}
/* Below 1251px the rest row cannot hold the five links as words beside the
   name, the language key and the status, so they stand as glyph keys on a
   mist ground (the words fold to zero width but stay in the accessibility
   tree). The capsule keeps its words. */
@media (max-width: 1250px) {
  .site-header {
    --glyph-gap: 0px;
    --label: 0px;
    --nav-gap: 6px;
  }
  .site-header:not(.is-scrolled) nav a {
    background-color: rgb(24 33 38 / 0.06);
  }
  .site-header:not(.is-scrolled) nav a:hover {
    background-color: rgb(24 33 38 / 0.1);
  }
  .site-header:not(.is-scrolled) .nav-label {
    opacity: 0;
    transition:
      width var(--gather) var(--ease),
      opacity 120ms ease;
  }
}
/* The home page opens on its demo, so its rest row is the approved
   composition's compact band: the icon's squircle at 52 (its image a fifth
   larger), the name a size up, the sections a size down and centred on the
   page, the keys drawn 34px tall and the language key in words alone, so the
   whole desktop fits the first view. Every link still takes a 44px press: an
   invisible band above and below each one. The band the capsule floats in
   shrinks with it; the gathered capsule is the same on every page. */
@media (min-width: 1081px) {
  .site-header[data-compact] {
    --header: calc(62 * var(--u));
    --rest-h: calc(62 * var(--u));
    --rest-control: calc(34 * var(--u));
    --rest-mark: calc(64 * var(--u));
    --rest-size: calc(15 * var(--u));
    --rest-glyph: calc(18 * var(--u));
    --rest-pad: calc(12 * var(--u));
    --rest-inset: calc(16 * var(--u));
  }
  .site-header[data-compact]:not(.is-scrolled) {
    --brand-size: calc(28 * var(--u));
  }
  .site-header[data-compact]:not(.is-scrolled) .brand-name > span {
    padding-left: calc(23 * var(--u));
  }
  .site-header[data-compact]:not(.is-scrolled) nav {
    margin-right: auto;
  }
  .site-header[data-compact]:not(.is-scrolled) :is(nav a, .lang-switch, .release-link)::after {
    content: "";
    position: absolute;
    inset: -5px 0;
  }
  .site-header[data-compact]:not(.is-scrolled) .lang-switch {
    --glyph: 0px;
    --glyph-gap: 0px;
  }
  .site-header[data-compact]:not(.is-scrolled) .lang-switch .nav-icon {
    opacity: 0;
  }
}
/* Below 921px the capsule folds the wordmark into the icon, which stays the
   way up on the home page and the way home everywhere else. */
@media (max-width: 920px) {
  .is-scrolled .brand {
    --r: 50%;
    grid-template-columns: auto 0fr;
  }
  .is-scrolled .brand-name {
    opacity: 0;
    filter: blur(3px);
    transition:
      font-size var(--gather) var(--ease),
      opacity 180ms ease,
      filter 180ms ease;
  }
}
@media (max-width: 760px) {
  :root {
    --header: 64px;
    --gutter: 22px;
  }
  /* Tablets: the keys, the status and the capsule step to fixed sizes, and
     the capsule spans the column. At rest the language key keeps its word
     and drops its glyph. */
  .site-header {
    --rest-h: 80px;
    --rest-control: 44px;
    --rest-mark: 40px;
    --rest-size: 13px;
    --rest-pad: 12px;
    --rest-glyph: 20px;
    --rest-inset: 14px;
    --rest-keys-gap: 6px;
    --brand-size: clamp(16px, 1.83vw + 6.1px, 20px);
    --bar-h: 52px;
    --bar-pad: 4px;
    --bar-top: 8px;
    --cap-min: 100%;
    --cap-mark: 30px;
    --cap-size: 14px;
    --cap-pad: 8px;
    --cap-brand: 16px;
  }
  .site-header:not(.is-scrolled) .lang-switch {
    --glyph: 0px;
  }
  .site-header:not(.is-scrolled) .lang-switch .nav-icon {
    opacity: 0;
  }
  .brand-name > span {
    padding-left: 4px;
  }
  h1 {
    font-size: clamp(34px, 7vw, 48px);
  }
  .hero-actions {
    gap: 10px;
    margin-top: 24px;
  }
  .launch-actions {
    gap: 10px;
  }
  .hero-button {
    gap: 8px;
    min-height: 48px;
    padding: 0 18px 0 16px;
    font-size: 15px;
  }
  .hero-button svg {
    width: 18px;
    height: 18px;
  }
  .hero-download {
    --inset: 18px;
    --sign: 18px;
    --lead: calc(var(--apple) + 10px);
    padding: 0 var(--inset);
  }
  .hero-note {
    margin-top: 14px;
    font-size: 12px;
  }

  .faq {
    margin-top: 78px;
    padding-top: 42px;
  }
  .faq-intro > p {
    margin-top: 13px;
  }
  .faq-list {
    margin-top: 40px;
  }
  .launch {
    margin-top: 80px;
    padding-block: 80px 72px;
  }
  .launch-mark {
    width: 112px;
    height: 112px;
  }
  .footer-top {
    grid-template-columns: auto minmax(0, 1fr);
    row-gap: 22px;
  }
  .footer-nav {
    grid-column: 1 / -1;
    grid-template-rows: repeat(4, 44px);
    margin-top: 0;
  }
  .footer-wordmark {
    margin-top: 40px;
  }
  .footer-bottom {
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
  }
}
/* Small tablets: at rest the page is on its first section, so its key makes
   room for the language; the capsule brings it back down to 661px. Below
   that the capsule too drops Try it and Guides (the footer carries them). */
@media (max-width: 680px) {
  .site-header:not(.is-scrolled) nav a:first-child {
    display: none;
  }
}
@media (max-width: 660px) {
  .nav-marker,
  .site-header nav a:first-child,
  .site-header nav a[href$="/guides/"] {
    display: none;
  }
}
/* Phones. The header's rest row is the icon, the name and the status; FAQ
   (from 481px), Support (from 431px), Pricing and the language join the
   capsule, and the footer carries every section. Pinned, the stage anchors
   under the header; the
   frame box is exactly the work's 16:9 while it hangs, then grows to 4:3 as
   it becomes the desktop (the work covers it, as any wallpaper fills its
   screen). The app window keeps its 8:5 and rises out of the desktop's bottom
   edge, inset on a strip of the work; the app in it is a picture there, and
   script.js's camera frames what each thing to try opens. */
@media (max-width: 600px) {
  .site-header {
    --rest-h: 72px;
    --rest-mark: 36px;
    --rest-size: 12px;
    --brand-size: 19px;
  }
  .nav-marker,
  .site-header:not(.is-scrolled) :is(nav, .lang-switch) {
    display: none;
  }
  .site-header:not(.is-scrolled) .release-link {
    margin-left: auto;
  }
  .hero-note {
    margin-bottom: 16px;
  }
}
@media (max-width: 540px) {
  :root {
    --gutter: 18px;
  }

  .gallery-intro {
    padding: 22px 0 16px;
  }
  h1 {
    font-size: clamp(30px, 9.7vw, 48px);
    line-height: 1.08;
  }
  /* The row no longer holds all three facts: the version takes a line of
     its own, so no dot is left leading a line. */
  .hero-note li:last-child {
    flex-basis: 100%;
  }
  .hero-note li:last-child::before {
    content: none;
  }
  .faq h2 {
    font-size: 35px;
  }
  .faq {
    margin-top: 69px;
    padding-top: 36px;
  }
  .faq-links a {
    padding: 0 15px;
    font-size: 13px;
  }
  .faq-list {
    gap: 10px;
    margin-top: 32px;
  }
  .faq details {
    border-radius: 12px;
  }
  .faq summary {
    gap: 14px;
    padding: 16px 16px 16px 18px;
    font-size: 17px;
  }
  .faq summary svg {
    width: 18px;
    height: 18px;
  }
  .faq details p {
    padding: 0 18px 18px;
    font-size: 14px;
  }
  .launch {
    margin-top: 66px;
    padding-block: 64px 56px;
  }
  .launch-mark {
    width: 96px;
    height: 96px;
  }
  .launch h2 {
    margin-top: 28px;
    font-size: clamp(32px, 9vw, 40px);
  }
  .launch-actions {
    margin-top: 32px;
  }
  .site-footer {
    padding-top: calc(var(--lip) + 48px);
  }
  .footer-nav {
    column-gap: 36px;
  }
}
/* Phones up to 480px: FAQ leaves the capsule, so Support and the language
   keep their place (the footer still carries it). */
@media (max-width: 480px) {
  .site-header nav a[href$="#faq"] {
    display: none;
  }
}
/* Narrow phones: the name steps down and the release status sheds its marks;
   at the 320px class Support leaves the capsule too (the footer still
   carries it), and the language and the status hold the far end. */
@media (max-width: 380px) {
  .site-header {
    --brand-size: 18px;
  }
  .release-link {
    --lead: 0px;
  }
  .is-scrolled .release-link {
    --inset: 14px;
    --lead: 0px;
  }
  .release-link svg {
    display: none;
  }
  /* Nor the two requirements (the Chinese pair runs past 314px): each fact
     stands on its own line. */
  .hero-note {
    flex-direction: column;
  }
  .hero-note li + li::before {
    content: none;
  }
}
@media (max-width: 350px) {
  .site-header {
    --brand-size: 14px;
  }
  /* As the release status does, the download sheds its marks to keep its
     words inside the column. */
  .hero-download {
    --lead: 0px;
  }
  .hero-download svg {
    display: none;
  }
  .site-header nav {
    display: none;
  }
  .site-header .lang-switch {
    margin-left: auto;
  }
}
/* Reduced motion keeps color, opacity and state changes and removes travel:
   no pinned walk (script.js leaves the page unpinned), no opening, no camera
   moves, no zooms, no shimmer. Swaps become dissolves. */
/* Shared with the account world and the home page's sponsor wall
   (sponsors.css): tokens, a hidden label, a Supporter's portrait and the
   pill buttons. */
:root {
  --control-border: #bac6cc;
  --frame-ground: #e0e6e9;
}
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* A supporter's picture: a squircle cut like an app icon, so a shelf of them
   reads as a Dock. Photos fill it. Monograms are the app icon's own colours
   (brand/app-icon.svg: its Ocean gradient, blue to teal, seen through one of
   six windows that data-cut picks) with the initial in white. Shared by the
   sponsor wall and the account previews. */
.portrait {
  --size: calc(64 * var(--u));
  position: relative;
  display: grid;
  place-items: center;
  flex: none;
  width: var(--size);
  height: var(--size);
  overflow: hidden;
  border-radius: calc(var(--size) * 0.225);
  background: var(--frame-ground);
}
/* A hairline edge, so a pale photo keeps its shape on the paper. */
.portrait::after {
  content: "";
  position: absolute;
  inset: 0;
  border: 1px solid rgb(24 33 38 / 0.08);
  border-radius: inherit;
  pointer-events: none;
}
.portrait img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Each portrait shows its own window of the app icon's Ocean gradient. The
   initial is decorative: the name is always written beside or under it. */
.portrait[data-kind="monogram"] {
  --window: 70%;
  background-image:
    radial-gradient(circle at 73% 27%, rgb(255 255 255 / 0.5), transparent 37.5%),
    linear-gradient(135deg, #5E9CF5, #37CDBE);
  background-repeat: no-repeat;
  background-size:
    100% 100%,
    300% 300%;
  background-position:
    0 0,
    var(--window) var(--window);
}
.portrait[data-cut="0"] {
  --window: 50%;
}
.portrait[data-cut="1"] {
  --window: 60%;
}
.portrait[data-cut="3"] {
  --window: 80%;
}
.portrait[data-cut="4"] {
  --window: 90%;
}
.portrait[data-cut="5"] {
  --window: 100%;
}
.portrait-initial {
  color: #fff;
  font-family: var(--display);
  font-size: calc(var(--size) * 0.46);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.02em;
  user-select: none;
}

/* Actions: pills. Ink for the main action, outlined for everything else. */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: calc(10 * var(--u));
  align-self: flex-start;
  min-height: calc(48 * var(--u));
  margin-top: calc(32 * var(--u));
  padding: 0 calc(24 * var(--u));
  border: 1px solid var(--control-border);
  border-radius: 999px;
  background: transparent;
  color: var(--ink);
  font: 600 calc(14 * var(--u))/1 var(--body);
  text-align: center;
  cursor: pointer;
  transition:
    background-color 160ms ease,
    border-color 160ms ease,
    color 160ms ease,
    transform 120ms ease;
}
.button:hover {
  border-color: var(--accent);
  background: var(--tint);
}
.button:active {
  transform: scale(0.98);
}
.button-ink {
  border-color: var(--ink);
  background: var(--ink);
  color: #fff;
}
.button-ink:hover {
  border-color: var(--accent);
  background: var(--accent);
}
.button svg {
  flex: none;
  width: calc(16 * var(--u));
  height: calc(16 * var(--u));
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 220ms var(--ease);
}
.button:disabled,
.button[aria-disabled="true"] {
  opacity: 0.45;
  cursor: default;
}
.button[aria-disabled="true"]:hover {
  border-color: var(--control-border);
  background: transparent;
}
.button[aria-disabled="true"]:active {
  transform: none;
}
.button-ink[aria-disabled="true"]:hover {
  border-color: var(--ink);
  background: var(--ink);
}

@media (prefers-reduced-motion: reduce) {
  :root {
    scroll-behavior: auto;
  }
  .back-top:hover svg {
    transform: none;
  }
  .launch-docs:is(:hover, :focus-visible) .launch-docs-out {
    transform: none;
  }
  /* The header still gathers into its capsule, as a dissolve: the geometry
     changes at once and only colour and blur fade. On the home page the icon
     hands over to the arrow in place, and the release link keeps its Apple
     mark. */
  .header-bar {
    transition:
      background-color var(--dur) ease,
      -webkit-backdrop-filter var(--dur) ease,
      backdrop-filter var(--dur) ease;
  }
  .brand {
    transition: none;
  }
  .brand-mark {
    transition: background-color var(--dur-fast) ease;
  }
  .site-header nav a,
  .lang-switch,
  .release-link,
  .hero-button {
    transition:
      color var(--dur-fast) ease,
      background-color var(--dur-fast) ease;
  }
  .brand-mark > *,
  .nav-icon,
  .is-scrolled .nav-icon,
  .nav-label,
  .site-header:not(.is-scrolled) .nav-label,
  .release-link > * {
    transition: opacity var(--dur) ease;
  }
  .brand-top,
  .is-scrolled .brand:is(:hover, :focus-visible) .brand-top {
    filter: none;
    transform: rotate(-90deg);
  }
  .is-scrolled .brand:has(.brand-top):is(:hover, :focus-visible) .brand-icon {
    filter: none;
    transform: none;
  }
  .release-link:is(:hover, :focus-visible) .release-apple {
    opacity: 1;
    filter: none;
    transform: none;
  }
  .release-link:is(:hover, :focus-visible) span {
    transform: none;
  }
  .release-link:is(:hover, :focus-visible) .release-arrow {
    opacity: 0;
  }
  .hero-download-body > * {
    transition: opacity var(--dur) ease;
  }
  .hero-download:is(:hover, :focus-visible) .hero-apple {
    opacity: 1;
    filter: none;
    transform: none;
  }
  .hero-download:is(:hover, :focus-visible) .hero-download-body > span {
    transform: none;
  }
  .hero-download:is(:hover, :focus-visible) .hero-arrow {
    opacity: 0;
  }
  .back-top svg {
    transform: rotate(-90deg);
  }
  .nav-marker {
    transition-property: opacity;
  }
  .faq details::details-content,
  .faq summary svg {
    transition: none;
  }
  /* Pushing the floor still fills the name; it no longer lifts. */
  .footer-wordmark {
    translate: none;
  }
}
@media (forced-colors: active) {
  .is-scrolled .header-bar {
    outline: 1px solid CanvasText;
  }
  /* Forced colours flatten the night to Canvas; trace the sheet's edge. */
  .site-footer::before {
    border: 1px solid CanvasText;
    border-top: 0;
  }
  .lang-switch,
  .release-link,
  .hero-button {
    border: 1px solid ButtonText;
  }
  .faq details {
    border: 1px solid CanvasText;
  }
  .footer-fill,
  .launch-mark::before {
    display: none;
  }
  .launch-glass-stage {
    filter: none !important;
  }
  .footer-wordmark > span:has(> .footer-fill) {
    color: CanvasText;
  }
  .launch-mac {
    color: CanvasText;
  }
}

/* Chinese. Han glyphs keep their own spacing, and the Latin-tuned line boxes
   (1.02-1.15) sit too close to the top of the glyphs for the headline masks
   and multi-line headings, so the Chinese page raises them to 1.2. Heights
   already at or above 1.2 (the wall label's title, feature titles) are left
   alone. */
:is(h1, h2, h3):lang(zh) {
  letter-spacing: 0;
}
/* The Latin names in the Chinese headline keep the English tracking, so they
   read as the same wordmark and still fit one line on a phone. */
h1:lang(zh) [lang="en"] {
  letter-spacing: -0.04em;
}
/* The Chinese headline breaks only where a phrase ends: after its full stops
   and commas and at the spaces around "Mac", never inside a word such as 录屏. */
h1:lang(zh) {
  word-break: keep-all;
}
html:lang(zh)
  :is(
    h1,
    .launch h2,
    .faq h2
  ) {
  line-height: 1.2;
}
