/* Synchr web — white field, typographic, with the opening stage panel
   carrying the page. This is the new brand direction; the iOS app follows it. */

/* Self-hosted webfonts. Served from this origin rather than Google Fonts:
   nothing for browser shields to block, and no visitor IPs sent to Google.
   Subset unicode-ranges are Google's own, so only the needed file loads.
     Arimo                — sans, variable 400-700, roman + italic
     Hedvig Letters Serif — display serif, 400 only, no italic
     IBM Plex Mono        — labels and code, 400/500/600 */
@font-face {
  font-family: "Arimo";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/fonts/arimo-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Arimo";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/fonts/arimo-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Arimo";
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url("/fonts/arimo-latin-italic.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Arimo";
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url("/fonts/arimo-latin-ext-italic.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Hedvig Letters Serif";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/hedvig-letters-serif-latin-400.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Hedvig Letters Serif";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/hedvig-letters-serif-latin-ext-400.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/ibm-plex-mono-latin-400.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/ibm-plex-mono-latin-ext-400.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/fonts/ibm-plex-mono-latin-500.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/fonts/ibm-plex-mono-latin-ext-500.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/ibm-plex-mono-latin-600.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/ibm-plex-mono-latin-ext-600.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* ---------------------------------------------------------------------------
   Design tokens. Values are lifted directly from the Paper "Production" page
   rather than eyeballed, so the two stay in sync.
--------------------------------------------------------------------------- */
:root {
  --bg: #ffffff;

  --ink: #16151b;
  --muted: #57575c;
  --hair: rgba(22, 21, 27, 0.08);
  --outline: #888891;

  /* The Synchr card. Its hairlines are a pale blue, not a neutral grey —
     they tie the card to the portal behind it. */
  --card-bg: #fcfdfe;
  --card-ink: #1a1a1e;
  --card-muted: #71717a;
  /* One pale blue for every rule on the site — the card's dividers, the blog
     table, and the footer. Was #B8CFFF, which read too bright at full width. */
  --rule: #d9e6ff;
  --accent: #4c6fcc;

  /* In-app chrome for the phone mock. */
  --app-ink: #15141a;
  --app-muted: #68647a;
  --app-indigo: #5366d1;

  --serif: "Hedvig Letters Serif", Georgia, serif;
  --sans: "Arimo", Arial, Helvetica, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  /* The phone mock imitates iOS chrome, so it uses the viewer's system UI
     face. IBM Plex Sans is what the Paper file specifies but it is not among
     the self-hosted subsets, and adding it for ~40 characters is not worth
     the request. */
  --app-sans: system-ui, -apple-system, "Segoe UI", sans-serif;

  --display: var(--sans);
  --text: var(--serif);

  /* The content lane. 1226px inside a 1440px canvas leaves 107px each side,
     which is where the nav, hero and footer all align. */
  --lane: 1226px;

  /* Raised-cosine falloff. Values are the eased curve, not linear stops —
     see .portal for why. */
  --portal-mask: linear-gradient(to right,
    rgba(0,0,0,0) 0%, rgba(0,0,0,0.001) 0.6%, rgba(0,0,0,0.007) 1.5%,
    rgba(0,0,0,0.028) 3%, rgba(0,0,0,0.069) 4.8%, rgba(0,0,0,0.156) 7.5%,
    rgba(0,0,0,0.282) 10.5%, rgba(0,0,0,0.425) 13.5%, rgba(0,0,0,0.575) 16.5%,
    rgba(0,0,0,0.718) 19.5%, rgba(0,0,0,0.844) 22.5%, rgba(0,0,0,0.931) 25.2%,
    rgba(0,0,0,0.972) 27%, rgba(0,0,0,0.993) 28.5%, rgba(0,0,0,0.999) 29.4%,
    rgba(0,0,0,1) 30%, rgba(0,0,0,1) 50%, rgba(0,0,0,0.999) 50.7%,
    rgba(0,0,0,0.993) 51.75%, rgba(0,0,0,0.972) 53.5%, rgba(0,0,0,0.931) 55.6%,
    rgba(0,0,0,0.844) 58.75%, rgba(0,0,0,0.718) 62.25%, rgba(0,0,0,0.575) 65.75%,
    rgba(0,0,0,0.425) 69.25%, rgba(0,0,0,0.282) 72.75%, rgba(0,0,0,0.156) 76.25%,
    rgba(0,0,0,0.069) 79.4%, rgba(0,0,0,0.028) 81.5%, rgba(0,0,0,0.007) 83.25%,
    rgba(0,0,0,0.001) 84.3%, rgba(0,0,0,0) 85%);
}

* { box-sizing: border-box; }

/* Clipped horizontally only. The portal sticks out past the right edge and
   would otherwise scroll the page sideways — but clipping vertically as well
   would cut the drop shadows off the card and the phone. */
html { background: var(--bg); overflow-x: clip; }

html, body {
  margin: 0; padding: 0;
  color: var(--ink);
  font-family: var(--text);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Hedvig Letters Serif's "ff" ligature rasterises as a single heavy f at
     body sizes — "effectively" came out as "efectively". It's correct at 34px
     and above, and its fi/fl/ffi ligatures are fine at every size, so this is
     the f_f glyph specifically. CSS can't disable one ligature, so common
     ligatures are off site-wide; the loss of fi/fl is invisible at 17px. */
  font-variant-ligatures: no-common-ligatures;
}

a { color: inherit; text-decoration: none; }
a:hover { text-decoration: underline; }

::selection { background: rgba(22, 21, 27, 0.14); }
:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; border-radius: 2px; }

/* Skip link: visually hidden until it takes keyboard focus, then it drops in at
   the top-left so a keyboard user can jump past the nav to the hero. */
.skip-link {
  position: fixed; top: 8px; left: 8px; z-index: 100;
  transform: translateY(-150%);
  padding: 10px 16px; border-radius: 8px;
  background: var(--ink); color: var(--bg);
  font-size: 15px; line-height: 1;
}
.skip-link:focus { transform: none; text-decoration: none; }
#main:focus { outline: none; }

img, svg { max-width: 100%; }

/* The single horizontal rhythm every section aligns to. */
.lane {
  width: 100%;
  max-width: var(--lane);
  margin-inline: auto;
  /* Narrower gutter on small phones — 24px each side eats 13% of a 375px
     screen, which is what pushed the headline into wrapping badly. */
  padding-inline: clamp(18px, 5vw, 24px);
}

/* ---------------------------------------------------------------------------
   Nav — two slots: the wordmark on the left, the "More" disclosure on the
   right. Both grow equally, so neither can push the other off the lane.
--------------------------------------------------------------------------- */
/* Sticky and solid on every page but the landing one, where the nav sits over
   the portal and a white bar would cut a hole in the gradient. The background
   only spans the lane, which is invisible because the page ground is also
   white — if --bg ever stops being white this needs a full-bleed layer. */
.site-nav {
  display: flex; align-items: center;
  padding-top: 41px; padding-bottom: 18px;
  position: sticky; top: 0; z-index: 20;
  background: var(--bg);
}
.page-home .site-nav {
  position: relative; z-index: 2;
  padding-bottom: 0;
  background: none;
}
.site-nav .mark-slot { display: flex; align-items: center; flex: 1 1 0; }
.site-nav .contact-slot { display: flex; align-items: center; flex: 1 1 0; justify-content: flex-end; }

.mark {
  font-family: var(--display); font-size: 22px;
  letter-spacing: -0.01em; line-height: 28px; color: var(--ink);
  white-space: nowrap;
}
.site-nav a {
  font-family: var(--display); font-size: 17px; line-height: 22px;
  color: var(--ink); white-space: nowrap;
  transition: color 0.2s ease;
}
/* The wordmark is excluded — it's the identity, not a nav item, so it holds
   its colour on hover. */
.site-nav a:not(.mark):hover { color: var(--muted); text-decoration: none; }

/* ---------------------------------------------------------------------------
   Dropdown menus. The labels are buttons, not links: their job is to open the
   panel, and giving them a destination would mean inventing one. The `.open`
   class that initNavMenus() toggles supports keyboard and touch. Desktop
   mouse users can also open on hover; mobile keeps tap-to-toggle so sticky
   touch hover cannot hold the panel open.
--------------------------------------------------------------------------- */
.menu { position: relative; display: flex; align-items: center; }
.menu-label {
  border: 0; padding: 0; background: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--display); font-size: 17px; line-height: 22px;
  color: var(--ink); white-space: nowrap;
  transition: color 0.2s ease;
}
.menu.open .menu-label { color: var(--muted); }
/* The chevron points at the panel it opens. */
.menu-label .chevron { transition: transform 0.16s ease; }
.menu.open .menu-label .chevron { transform: rotate(180deg); }

.menu-panel {
  position: absolute; top: 100%; left: -20px; z-index: 50;
  /* Transparent padding, not margin — it bridges the gap from label to card
     so the menu doesn't close as the cursor travels down. */
  padding-top: 14px;
  opacity: 0; visibility: hidden; transform: translateY(-4px);
  transition: opacity 0.16s ease, transform 0.16s ease, visibility 0.16s;
}
.menu.open .menu-panel { opacity: 1; visibility: visible; transform: none; }
@media (min-width: 721px) and (hover: hover) and (pointer: fine) {
  .menu:hover .menu-label { color: var(--muted); }
  .menu:hover .menu-label .chevron { transform: rotate(180deg); }
  .menu:hover .menu-panel { opacity: 1; visibility: visible; transform: none; }
}
/* The one menu sits at the right edge of the lane, so its panel opens
   leftward — otherwise the card hangs off the page. */
.contact-slot .menu-panel { left: auto; right: -20px; }

/* Glass, not a panel: the page reads through it, blurred. The menu opens at the
   right end of the nav, directly over the portal, so what it picks up is the
   gradient — which is the point. saturate() lifts the colour the blur would
   otherwise wash out. */
/* Two columns, filled top-to-bottom rather than left-to-right: Synchr above
   Support, Blog above Contact. `grid-auto-flow: column` gets that without
   reordering the markup, and it keeps the pairs meaningful — the columns are
   the footer's Product and Company groups. */
.menu-card {
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: auto auto;
  column-gap: 34px;
  min-width: 190px; padding: 14px 20px;
  /* A gradient tint rather than a flat one — brighter along the top edge,
     thinning toward the bottom. Real glass is lit from above, and that falloff
     is most of what separates it from a translucent rectangle. */
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.34) 0%,
    rgba(255, 255, 255, 0.14) 100%
  );
  -webkit-backdrop-filter: blur(22px) saturate(2);
  backdrop-filter: blur(22px) saturate(2);
  /* Saturation climbs as the tint drops: the less white sitting over the
     backdrop, the more the blur alone flattens its colour toward grey. */
  /* No edge at all. A light border traced a visible white rim around the
     rounded corners wherever the card sat over the gradient; the shadow is
     what separates it from the page. */
  border: 0;
  border-radius: 10px;
  box-shadow: 0 10px 30px rgba(22, 21, 27, 0.09);
}
/* Without backdrop-filter the card would be half-transparent over live text
   with nothing blurring it, which is unreadable — fall back to near-opaque. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  /* The near-opaque fallback has no gradient showing through to define it, so
     here a hairline earns its place. */
  .menu-card { background: rgba(252, 252, 253, 0.96); border: 1px solid var(--hair); }
}
.menu-card a {
  display: block; padding: 7px 0;
  font-family: var(--display); font-size: 15px; line-height: 22px;
  color: var(--ink); white-space: nowrap;
}
/* Synchr fills with the hero's mesh on hover. It uses the exported still,
   clipped to the glyphs, rather than the live shader: the hero's knockout
   needs a white backdrop to disappear against, and this sits on the menu
   card's #F6F7F9. Clipping composites cleanly over any colour. */
.menu-card a.mesh-link:hover span {
  background-image: url("/img/mesh.png");
  background-size: 100% 100%;
  /* Clipped on the inline span, not the block <a> — the anchor fills the card's
     width, so the gradient would spread over 150px and only its left third
     would land on the word. The span hugs the text, so the full sweep shows. */
  background-size: 100% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.mark { transition: none; }
.mark:hover { color: var(--ink); text-decoration: none; }


/* ---------------------------------------------------------------------------
   Stage — hero over the portal.
--------------------------------------------------------------------------- */
.stage { position: relative; }

/* The portal. Everything here mirrors the Paper node: the trapezoid is a
   clip-path, the soft sides are a mask, and the blue comes from a screen wash
   over the saturated mesh rather than from the mesh colours themselves.
   The mask uses ~32 eased stops instead of four hard ones — with four, the
   slope breaks at 30% and 50% read as two vertical bands down the middle. */
/* The column starts 700px above the document rather than 200px, so dragging the
   page past the top doesn't reveal where it ends. The clip percentages are
   recomputed against the taller box to hold the original slant exactly: the top
   edge still drops 112px from right to left (112/1900 = 5.9%) and the bottom
   still sits at y=1032 (91.2% of 1900, less the 700px head start). Only the top
   of the trapezoid moved; its shape and its bottom are unchanged. */
.portal {
  position: absolute;
  top: -700px;
  left: calc(50% + 161px);
  width: 420px; height: 1900px;
  clip-path: polygon(0% 5.9%, 100% 0%, 100% 100%, 0% 91.2%);
  /* Intersect the soft sides with a bottom fade, ending 800px down the
     page (the portal starts 700px above it), before the prose continues. */
  -webkit-mask-image: var(--portal-mask), linear-gradient(to bottom,
    #000 1100px, rgba(0,0,0,0.93) 1180px, rgba(0,0,0,0.72) 1260px,
    rgba(0,0,0,0.28) 1380px, rgba(0,0,0,0.07) 1460px, transparent 1500px);
  mask-image: var(--portal-mask), linear-gradient(to bottom,
    #000 1100px, rgba(0,0,0,0.93) 1180px, rgba(0,0,0,0.72) 1260px,
    rgba(0,0,0,0.28) 1380px, rgba(0,0,0,0.07) 1460px, transparent 1500px);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
  filter: blur(34px) saturate(1.2) hue-rotate(-22deg);
  opacity: 0.92;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}
.portal-mesh { position: absolute; inset: 0; }
.portal-mesh canvas { display: block; width: 100%; height: 100%; }
/* Second, larger mesh offset from the first so its blobs don't line up with
   the base layer — breaks up the vertical striping the stretch introduces. */
.portal-mesh-b { position: absolute; inset: -300px -100px; opacity: 0.5; }
.portal-wash {
  position: absolute; inset: 0; z-index: 1;
  background: #a0c0f5;
  mix-blend-mode: screen;
}

/* Where an address enters the column. These are plain circles — the portal's
   own blur(34px) is what turns them into glows, so nothing here needs a soft
   edge of its own. Two kinds, fired a beat apart: `lift` screens a pale bloom
   over the gradient, `deepen` multiplies an indigo one under it. A single
   bright flash reads as a light switch; the pair reading against each other is
   what makes it shimmer. js/waitlist.js positions and animates them. */
.portal-bloom {
  position: absolute; z-index: 2;
  border-radius: 50%;
  opacity: 0; pointer-events: none;
}
.portal-bloom.lift {
  background: radial-gradient(circle, rgba(255,255,255,0.6) 0%, rgba(196,219,255,0.3) 40%, rgba(196,219,255,0) 70%);
  mix-blend-mode: screen;
}
.portal-bloom.deepen {
  background: radial-gradient(circle, rgba(90,86,200,0.28) 0%, rgba(120,110,210,0.14) 45%, rgba(120,110,210,0) 72%);
  mix-blend-mode: multiply;
}

/* Spacing below is derived from the Paper node coordinates, not estimated.
   Nav occupies 41..69, so 139 puts the headline's top at 208 as designed. */
.hero { position: relative; z-index: 1; padding-top: 100px; }
/* Left-aligned in the lane. Wide enough that "Interactive Dimension" stays on
   one line at 68px — if it wraps, the knockout plate breaks. */
.hero-inner { max-width: 660px; }
.hero h1 {
  font-family: var(--display); font-weight: 400;
  font-size: clamp(40px, 4.72vw, 68px);
  line-height: 1.015; letter-spacing: -0.025em;
  margin: 0;
  /* The headline's ink sits ~6px inside its box at 68px, while every other
     element on the page sits ~1px inside its own. Pulling it back aligns the
     ink rather than the boxes. Scales with the type size. */
  margin-left: -0.074em;
}
/* "Interactive Dimension" is filled with a live mesh, using the same knockout
   the design uses: a white plate in `lighten` over the shader lets only the
   black glyphs through. `isolation` keeps the blend inside this box so the
   plate doesn't lighten the page. It needs a white backdrop to disappear
   against — fine here, since the headline sits well clear of the portal. */
/* Structured exactly like the Paper node: a box clipped to the line, the mesh
   filling it, and a white plate that overhangs on every side so it covers the
   mesh completely. The overhang is what makes the knockout clean — and the
   clip is what stops that overhang painting over the line above, which would
   otherwise slice the descender off "Explore the".
   Block-level with fit-content width, not inline-block: an inline-block with
   `overflow: hidden` takes its baseline from its bottom edge, which throws the
   line box off. */
.hero h1 .l1 { display: block; }
/* The white background is load-bearing, not decoration. Two of the four mesh
   colours carry alpha (#241D9AB3, #F7509280); they only reach the pale tone in
   the design by compositing over white. Without it the canvas sits on
   transparency, the blend multiplies the colour down instead, and the letters
   come out dark navy. */
.mesh-word {
  position: relative; display: block; width: fit-content; max-width: 100%;
  overflow: hidden;
  isolation: isolate;
  background: #fff;
}
.mesh-word .hero-mesh { position: absolute; inset: 0; }
.mesh-word .hero-mesh canvas { display: block; width: 100%; height: 100%; }
.mesh-word .plate {
  position: relative; display: block;
  padding: 24px 0; margin: -24px 0;
  background: #fff; color: #000;
  mix-blend-mode: lighten;
}
.hero .lede {
  font-family: var(--display);
  font-size: 19px; line-height: 27px; letter-spacing: 0.01em;
  color: var(--muted);
  max-width: 530px;
  margin: 15px 0 0;
}

/* One baseline row: "learn more" then the waitlist field. The 18px inset and
   10px band are the Paper node's own padding — they set the row a step in from
   the lede's left edge rather than stacking a second flush-left column. */
.hero-actions {
  display: flex; align-items: baseline; flex-wrap: wrap;
  gap: 28px;
  /* Left inset is double the Paper node's 18px — the row reads as a deliberate
     step in from the lede rather than a near-miss of its left edge. */
  /* No left inset: the pill's own edge is what lines up with the lede above it,
     so the row starts flush and the button's 12px padding sits inside that. */
  padding: 10px 18px 10px 0;
  margin-top: 32px;
}
.hero-actions a {
  font-family: var(--display); font-size: 17px; line-height: 24px;
  color: var(--muted);
  transition: color 0.2s ease;
}
.hero-actions a:hover { color: var(--ink); text-decoration: none; }
/* Trailing the lede's final sentence rather than sitting on the row below.
   Ink against the muted paragraph, with the arrow doing the work the underline
   used to — between the tonal step and the glyph there is enough signal that a
   rule would only add clutter. Held on one line so neither the phrase nor its
   arrow can break across the wrap. */
.hero .lede .hero-learn {
  color: var(--ink); white-space: nowrap;
  text-decoration: none;
  transition: color 0.2s ease;
}
/* Explicitly none: the global a:hover rule would otherwise put back exactly
   the underline this link is meant not to have. */
.hero .lede .hero-learn:hover { color: var(--muted); text-decoration: none; }

/* ---------------------------------------------------------------------------
   Showcase — the card and the phone, centred as one group.
--------------------------------------------------------------------------- */
/* The band of white between the hero row and this heading is the composition,
   not slack. It started at 220px, matching the Paper artboard's y=698; the hero
   has since lost a line and gained a gap, and the heading was asked further
   down again from there. */
.showcase { position: relative; z-index: 1; padding-top: 300px; }
/* The anchor sits on the heading, not the section: the section carries top
   padding, so targeting it would land with an empty band on screen. Kept tight
   so the heading clears the hero and the phone's top peeks above the fold on
   common laptop heights (the full ~600px demo can't fit any first screen). */
.showcase h2 {
  scroll-margin-top: 40px;
  font-family: var(--text); font-weight: 400;
  font-size: 34px; line-height: 46px; letter-spacing: -0.01em;
  text-align: center; color: var(--ink);
  margin: 0 0 28px;
}
/* Phone and card share the same top line — align-items: flex-start starts both
   at the row top, no per-column offset. */
.showcase-row {
  display: flex; align-items: flex-start; justify-content: center;
  gap: 75px;
}

/* ---------------------------------------------------------------------------
   Synchr card.
--------------------------------------------------------------------------- */
.card {
  flex: 0 0 auto;
  width: 600px; max-width: 100%; padding: 36px;
  background: var(--card-bg);
  border-radius: 20px;
  box-shadow:
    rgba(138, 138, 150, 0.22) 0 -3px 10px,
    rgba(138, 138, 150, 0.22) 0 1px 2px,
    rgba(138, 138, 150, 0.28) 0 14px 32px;
  font-family: var(--mono);
}
/* Fixed 36px box. The 34px line-height would otherwise stack on top of the
   padding and push the rule ~18px further down than the design. */
.card .masthead {
  display: flex; align-items: baseline;
  height: 36px; padding-bottom: 20px;
}
.card .masthead .name {
  flex: 1;
  font-family: var(--text); font-size: 28px;
  letter-spacing: -0.02em; line-height: 34px; color: var(--card-ink);
}
.card .masthead .platform {
  font-size: 12px; letter-spacing: 0.08em; line-height: 34px;
  color: var(--card-muted);
}
.card hr {
  border: 0; height: 1px; width: 100%;
  background: var(--rule);
  margin: 0;
}
.card .row { display: flex; padding-block: 18px; }
.card .row-label {
  flex: 0 0 75px; width: 75px;
  font-size: 11px; font-weight: 500;
  letter-spacing: 0.12em; line-height: 26px;
  color: var(--card-muted);
}
.card .row-body { flex: 1; display: flex; flex-direction: column; }
.card .row-body li { font-size: 15px; line-height: 26px; color: var(--card-ink); }

.card .about { display: flex; flex-direction: column; gap: 6px; padding-block: 18px; }
.card .about .label {
  font-size: 11px; font-weight: 500;
  letter-spacing: 0.12em; line-height: 22px; color: var(--accent);
}
.card .about p { margin: 0; font-size: 15px; line-height: 26px; color: var(--card-ink); }

.card ul { list-style: none; margin: 0; padding: 0; }

/* The steps stagger to the right as they go — the indent carries the sense of
   progression, so the numbers do not have to. */
.card .steps li { display: flex; font-size: 15px; line-height: 26px; color: var(--card-ink); }
.card .steps li .n { flex: 0 0 34px; width: 34px; color: var(--accent); }
.card .steps li:nth-child(2) { padding-left: 34px; }
.card .steps li:nth-child(3) { padding-left: 66px; }
.card .steps li:nth-child(4) { padding-left: 98px; color: var(--accent); }

/* ---------------------------------------------------------------------------
   Phone mock. Sizes derive from the 297px body so it scales as one piece.
--------------------------------------------------------------------------- */
.phone {
  flex: 0 0 auto;
  position: relative;
  width: 297px; height: 609px;
  background: #fff;
  border: 1px solid #bfbfc7;
  border-radius: 53px;
  box-shadow: rgba(150, 150, 160, 0.20) 0 1px 2px, rgba(150, 150, 160, 0.30) 0 16px 38px;
  font-family: var(--app-sans);
}
.phone .screen {
  position: absolute; inset: 12px;
  background: #fafafa; border-radius: 43px;
  padding: 13px 19px 19px;
  display: flex; flex-direction: column; gap: 16px;
  overflow: clip;
}
.phone .island {
  position: absolute; top: 22px; left: 50%; transform: translateX(-50%);
  width: 90px; height: 24px; border-radius: 999px; background: #7c7c86;
}
.phone .home {
  position: absolute; bottom: 14px; left: 50%; transform: translateX(-50%);
  width: 107px; height: 5px; border-radius: 999px; background: var(--muted);
}
.phone .btn-side {
  position: absolute; width: 5px; border-radius: 2px; background: #bfbfc7;
}
.phone .btn-action { left: -4px; top: 79px;  height: 24px; }
.phone .btn-up     { left: -4px; top: 127px; height: 39px; }
.phone .btn-down   { left: -4px; top: 201px; height: 39px; }
.phone .btn-power  { right: -4px; top: 153px; height: 51px; }

/* The interactive demo's controls read as tappable but keep the phone chrome
   flat — reset the native button look everywhere inside the screen. */
.phone button {
  -webkit-appearance: none; appearance: none;
  margin: 0; padding: 0; border: 0; background: none;
  font: inherit; color: inherit; cursor: pointer;
}
.phone .ic-defs { position: absolute; }
.phone svg use { color: inherit; }

/* Cover art: fixed square slot, floating with a soft shadow (`.elevation(.floating)`). */
.phone .art {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  height: 259px; border-radius: 13px; background: #ededef;
  box-shadow: rgba(21, 20, 26, 0.05) 0 1px 2px, rgba(21, 20, 26, 0.10) 0 10px 24px;
}
.phone .art-glyph {
  color: var(--app-muted);
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
/* A live voice session lifts the artwork glyph to make room for the transcript. */
.phone.live .art-glyph { transform: translateY(-30px) scale(0.9); }

/* Episode cover art — fills the square slot; the transcript box overlays it. */
.phone .art-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; border-radius: 13px;
}

/* Live user-transcript box, pinned to the lower strip of the cover square. */
.phone .transcript {
  position: absolute; left: 10px; right: 10px; bottom: 10px;
  height: 66px; padding: 9px 30px 9px 12px;
  border-radius: 12px; background: #fff;
  border: 0.5px solid rgba(60, 60, 67, 0.16);
  box-shadow: rgba(21, 20, 26, 0.06) 0 1px 2px, rgba(21, 20, 26, 0.12) 0 8px 20px;
  text-align: left; overflow: hidden;
  opacity: 0; transform: translateY(8px); pointer-events: none;
  transition: opacity 0.28s ease, transform 0.28s ease;
}
.phone.live .transcript { opacity: 1; transform: translateY(0); }
.phone .transcript p {
  margin: 0; font-size: 13px; line-height: 18px; color: var(--app-ink);
}
.phone .transcript-mic { position: absolute; top: 8px; right: 8px; color: var(--app-indigo); }

/* Title block: left-aligned, show eyebrow over a bold episode title. */
.phone .meta { display: flex; flex-direction: column; gap: 6px; text-align: left; }
.phone .show {
  font-size: 11px; font-weight: 500; letter-spacing: 0.44px;
  line-height: 13px; color: var(--app-muted);
}
.phone .episode {
  font-size: 18px; font-weight: 700; letter-spacing: -0.32px;
  line-height: 22px; color: var(--app-ink);
}
.phone .scrub { display: flex; flex-direction: column; gap: 6px; }
.phone .track {
  position: relative; height: 3px; border-radius: 999px;
  background: rgba(20, 20, 30, 0.1);
  cursor: pointer; touch-action: none;
}
/* Widen only the hit target, not the visual bar. */
.phone .track::before { content: ""; position: absolute; inset: -11px 0; }
.phone .track span {
  position: absolute; inset: 0 auto 0 0; width: 17%;
  border-radius: 999px; background: var(--app-indigo);
}
.phone .track .knob {
  position: absolute; top: 50%; left: 17%;
  width: 11px; height: 11px; margin: -5.5px 0 0 -5.5px;
  border-radius: 50%; background: var(--app-indigo);
  opacity: 0; transition: opacity 0.15s ease;
}
.phone .track:hover .knob,
.phone .track:focus-visible .knob,
.phone.scrubbing .track .knob { opacity: 1; }
.phone .times { display: flex; justify-content: space-between; }
.phone .times span {
  font-family: var(--mono); font-size: 10px; font-weight: 500;
  line-height: 12px; color: var(--app-muted);
}
/* Transport row. Extra top room lets the aux glyphs ride above the play button. */
.phone .transport {
  display: flex; align-items: center; justify-content: space-between;
  padding-top: 22px;
}
.phone .rate {
  width: 40px; text-align: left;
  font-size: 15px; font-weight: 600; line-height: 20px; color: var(--app-muted);
}
.phone .rate.off-default { color: var(--app-indigo); }
.phone .moon { width: 40px; display: flex; justify-content: flex-end; color: var(--app-muted); }
.phone .tbtn { display: flex; align-items: center; justify-content: center; }
.phone .skip { width: 44px; height: 44px; color: var(--app-muted); }
.phone .play { width: 56px; height: 56px; color: var(--app-ink); }
.phone .play-wrap { position: relative; display: flex; align-items: center; justify-content: center; }
/* Mic-mute (left) and audio-out (right) on the hexagon above the play button. */
.phone .aux {
  position: absolute; top: -20px; left: 50%; transform: translateX(-50%);
  width: 92px; display: flex; justify-content: space-between;
}
.phone .aux-btn { color: var(--app-muted); display: flex; }
.phone .aux-btn.muted { color: #d1435b; }
.phone .aux-glyph { display: block; }

/* Voice mic button: neutral surface pill, hairline outline, gradient mic glyph.
   The label/glyph is swapped by JS across the session states. */
.phone .listen {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 44px;
  border: 1px solid rgba(60, 60, 67, 0.18); border-radius: 999px;
  background: #fff;
  transition: border-color 0.2s ease, background 0.2s ease;
}
.phone .listen-stage {
  position: relative; display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%; overflow: hidden;
  font-size: 13px; font-weight: 500; color: var(--app-muted);
}
/* Idle occupant (mic OR a question) — scrolls VERTICALLY on each swap, mirroring
   the app's `.push(from: .bottom)`: occupants are stacked absolutely and the
   .listen-stage clips them, so the incoming rides up from the bottom (.enter) as
   the outgoing rides out the top (.leave). 0.6s ease-in-out matches the app. */
.phone .idle-slide {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center; gap: 7px;
  transition: transform 0.6s ease-in-out, opacity 0.6s ease-in-out;
}
.phone .idle-slide.enter { transform: translateY(100%); opacity: 0; }
.phone .idle-slide.leave { transform: translateY(-100%); opacity: 0; }
.phone .listen-mic { color: transparent; }
.phone .listen-mic use { color: #5a78c5; }        /* approximates the brand ramp */
.phone .listen-label { white-space: nowrap; }
/* Non-idle stage content (bars / dots / spinner / speaker) fills the pill too. */
.phone .listen-stage > .bars,
.phone .listen-stage > .dots,
.phone .listen-stage > .arc,
.phone .listen-stage > .listen-speaker { flex: 0 0 auto; }

/* Level bars (listening) — five bars bouncing on the brand hue. */
.phone .bars { display: flex; align-items: center; gap: 2px; height: 18px; }
.phone .bars i {
  width: 3px; border-radius: 1.5px; background: #5a78c5;
  animation: barPulse 0.9s ease-in-out infinite;
}
.phone .bars i:nth-child(1) { height: 7px;  animation-delay: 0s; }
.phone .bars i:nth-child(2) { height: 12px; animation-delay: 0.12s; }
.phone .bars i:nth-child(3) { height: 18px; animation-delay: 0.24s; }
.phone .bars i:nth-child(4) { height: 12px; animation-delay: 0.36s; }
.phone .bars i:nth-child(5) { height: 7px;  animation-delay: 0.48s; }
@keyframes barPulse { 0%, 100% { transform: scaleY(0.4); } 50% { transform: scaleY(1); } }

/* Thinking dots — three dots, size travelling left→right. */
.phone .dots { display: flex; align-items: center; gap: 5px; }
.phone .dots i {
  width: 6px; height: 6px; border-radius: 50%; background: #5a78c5;
  animation: dotTravel 1.05s ease-in-out infinite;
}
.phone .dots i:nth-child(2) { animation-delay: 0.18s; }
.phone .dots i:nth-child(3) { animation-delay: 0.36s; }
@keyframes dotTravel { 0%, 100% { transform: scale(0.7); } 40% { transform: scale(1.4); } }

/* Loading arc — connecting (forward) / leaving (reverse). */
.phone .arc {
  width: 17px; height: 17px; border-radius: 50%;
  border: 2px solid rgba(90, 120, 197, 0.25); border-top-color: #5a78c5;
  animation: arcSpin 0.8s linear infinite;
}
.phone .arc.reverse { animation-direction: reverse; }
@keyframes arcSpin { to { transform: rotate(360deg); } }

.phone .listen-speaker { color: #5a78c5; }

/* Standing accuracy disclaimer, shown only while a session is live. */
.phone .disclaimer {
  margin: 0; text-align: center;
  font-size: 11px; line-height: 14px; color: var(--app-muted);
  opacity: 0; height: 0; overflow: hidden;
  transition: opacity 0.2s ease;
}
.phone.live .disclaimer { opacity: 1; height: auto; }

@media (prefers-reduced-motion: reduce) {
  .phone .bars i, .phone .dots i, .phone .arc,
  .phone .art-glyph, .phone .transcript { animation: none !important; transition: none !important; }
}

/* ---------------------------------------------------------------------------
   Mission.
--------------------------------------------------------------------------- */
/* Give the introduction breathing room below the hero and keep its text
   above the decorative portal. */
.mission { position: relative; z-index: 1; padding-top: 175px; }
.mission-inner { max-width: 760px; margin-inline: auto; }
/* Both Synchr navigation links land with breathing room above the heading. */
#synchr { scroll-margin-top: 64px; }
.mission h2 {
  font-family: var(--display); font-weight: 400;
  font-size: 30px; line-height: 40px; letter-spacing: -0.02em;
  color: var(--ink); margin: 0 0 30px;
}
.mission h3 {
  font-family: var(--display); font-weight: 400;
  font-size: 24px; line-height: 34px; letter-spacing: -0.01em;
  color: var(--ink); margin: 40px 0 30px;
}
/* Running prose uses one size across the landing page and documents. */
.mission p {
  font-family: var(--text); font-size: 16px; line-height: 27px;
  color: var(--ink); margin: 0 0 30px;
}
.mission p:last-child { margin-bottom: 0; }

/* Links inside running prose stay underlined. An address that only reveals
   itself as clickable on hover isn't discoverable — and on touch there is no
   hover at all. */
.mission p a,
.doc-body p a {
  text-decoration: underline;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1px;
}

/* ---------------------------------------------------------------------------
   Waitlist — the label, an underlined field, and the Turnstile challenge that
   gates it. It lives inside the hero's action row; the standalone section it
   used to have is gone, and so are the rules that dressed it.
--------------------------------------------------------------------------- */
/* Visually hidden but available to screen readers (form label, submit). */
.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;
}

/* One row, two columns: the button, then the address typed beside it — which
   is also the runway the demo addresses fly off into the portal.

       [ join the waitlist ]  your.email.here@gmail.com

   A grid rather than a flex row so the status line underneath can span both
   columns without wrapping tricks.

   The two children not placed below — the screen-reader label and the
   invisible challenge — are both absolutely positioned, so neither claims a
   cell. */
.waitlist-form {
  display: grid;
  grid-template-columns: max-content auto;
  column-gap: 10px;
  align-items: baseline;
  justify-items: start;
}
.waitlist-go { grid-column: 1; grid-row: 1; }
.waitlist-form .field { grid-column: 2; grid-row: 1; }
.waitlist-status { grid-column: 1; grid-row: 2; }
/* The submit. A pill at rest too, so it is legible as a control before you
   have typed anything — that was the whole problem with the version that
   disguised itself as type. Dim while there is nothing to send, lit the moment
   there is. Same size as the lede and the address beside it. */
.waitlist-go {
  appearance: none; -webkit-appearance: none;
  border: 0; margin: 0; padding: 6px 18px;
  font-family: var(--display); font-size: 19px; line-height: 30px;
  border-radius: 999px;
  background: rgba(22, 21, 27, 0.06);
  color: var(--outline);
  cursor: default;
  transition: background 0.3s ease, color 0.3s ease,
              box-shadow 0.3s ease, transform 0.16s ease;
}
/* Lit: there is an address to send. Black fill, and a shadow so it reads as a
   surface above the page rather than a second block of type. */
.waitlist-form.armed .waitlist-go {
  background: var(--ink); color: #fff; cursor: pointer;
  box-shadow: 0 4px 14px rgba(22, 21, 27, 0.22);
}
.waitlist-form.armed .waitlist-go:hover {
  transform: translateY(-1.5px);
  box-shadow: 0 8px 20px rgba(22, 21, 27, 0.26);
}
.waitlist-form.armed .waitlist-go:active { transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
  .waitlist-form.armed .waitlist-go:hover { transform: none; }
}
/* A persistent desktop underline identifies the email field even between
   animated examples. Keep its width stable as the examples cycle. */
.waitlist-form .field {
  display: flex; width: 290px; max-width: 100%;
  padding-bottom: 3px;
  border-bottom: 1px solid rgba(22, 21, 27, 0.18);
}
.waitlist-form input[type="email"] {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--display); font-size: 19px; line-height: 30px;
  color: var(--ink);
  padding: 0; background: none; border: 0;
}
.waitlist-form input[type="email"]::placeholder { color: var(--outline); }
@media (min-width: 621px) {
  .waitlist-form { column-gap: 20px; }
  .waitlist-form .field { padding-bottom: 0; }
  .waitlist-form input[type="email"],
  .waitlist-form .waitlist-ghost { line-height: 24px; }
}
/* The demo line. An example address is typed out here character by character
   with a caret, then thrown into the portal — none of which a placeholder can
   do, since it is one flat string with no elements to animate. It sits over
   the field rather than in it; waitlist.js clears the real placeholder when it
   takes over, so the no-JS page still shows a static example. */
.waitlist-form .field { position: relative; }
.waitlist-ghost {
  position: absolute; left: 0; top: 0;
  font-family: var(--display); font-size: 19px; line-height: 30px;
  color: var(--outline);
  white-space: pre; pointer-events: none;
}
.waitlist-ghost .caret { font-style: normal; animation: caret-blink 1.06s step-end infinite; }
/* While the field has focus the browser draws the real caret, and the ghost's
   would be a second one sitting beside it. The examples keep cycling; only
   their caret stands down. */
.waitlist-form:focus-within .waitlist-ghost .caret { visibility: hidden; }
/* Held steady rather than blinking while keys are actually landing, the way a
   terminal does — a caret flickering under its own typing looks like a fault. */
.waitlist-ghost.caret-solid .caret { animation: none; opacity: 1; }
/* Dark from the moment the address is thrown, through the flight, and for a
   moment after the portal has taken it. Removing the class restarts the
   animation at 0%, so the blink always returns on a full-on caret. */
.waitlist-ghost.caret-gone .caret { animation: none; opacity: 0; }
@keyframes caret-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
/* The global :focus-visible ring would draw exactly the box this field is
   meant not to have — and browsers apply it to text inputs on mouse focus too,
   not just keyboard. The caret carries the focus state instead. */
.waitlist-form input[type="email"]:focus-visible { outline: none; }
.waitlist-go:disabled { opacity: 0.55; cursor: default; }
/* The invisible challenge is taken out of flow entirely: in flow, even a
   zero-height widget contributes its iframe's width to the form's intrinsic
   size, which is what pushed the field onto a line of its own. */
#waitlist-turnstile {
  position: absolute; width: 0; height: 0; overflow: hidden;
}
/* Every message begins at the pill's midpoint and runs rightward from there,
   so a two-word status and a two-line error share the same left edge.

   Percentages are the whole mechanism, because a percentage margin or
   min-width is ignored while the grid works out how wide a column needs to be.
   `margin-left: 50%` therefore puts the start on the middle of the pill, and
   `width: 0; min-width: 250%` gives the text room to run out under the address
   — and neither can push the email field sideways the way a message wider than
   the pill otherwise would. */
.waitlist-status {
  margin: 10px 0 0 50%;
  width: 0; min-width: 250%;
  font-family: var(--display); font-size: 15px; line-height: 22px;
  min-height: 22px; color: var(--muted);
}
/* Empty, it still reserves a line — which padded the hero row out to 84px
   against the design's 48px and pushed everything below it down. Collapsed
   rather than display:none so the live region stays in the accessibility tree
   and the first message is still announced. */
.waitlist-status:empty { min-height: 0; margin: 0; }
.waitlist-status[data-tone="error"] { color: var(--danger, #b3261e); }
.waitlist-status[data-tone="ok"] { color: var(--ink); }
/* Once the controls disappear, the message must size itself independently
   of their grid columns and the in-flight status width. */
.waitlist-form[data-done="true"] { display: block; max-width: 100%; }
.waitlist-form[data-done="true"] .waitlist-status {
  width: auto; min-width: 0; margin: 0;
}
/* On success, collapse the inputs and challenge — the status line stands alone. */
.waitlist-form[data-done="true"] .field,
.waitlist-form[data-done="true"] .waitlist-go { display: none; }

/* ---------------------------------------------------------------------------
   Footer.
--------------------------------------------------------------------------- */
.site-footer { padding-top: 160px; padding-bottom: 67px; }
/* The rule is the same pale blue as the card's hairlines, not a neutral grey. */
.site-footer .inner { border-top: 1px solid var(--rule); padding-top: 28px; }
.site-footer .cols { display: flex; gap: 32px; align-items: flex-start; }
/* The first column is deliberately empty — the wordmark and tagline were cut
   from the design, leaving the link columns weighted to the right. */
.site-footer .spacer { flex: 1.5 1 0; min-width: 0; height: 28px; }
.site-footer .col { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; }
.site-footer h4 {
  margin: 0 0 8px; font-family: var(--display);
  font-size: 14px; font-weight: 500; line-height: 18px; color: var(--muted);
}
.site-footer .col a {
  display: block; padding-bottom: 6px;
  font-family: var(--text); font-size: 15px; line-height: 22px; color: var(--ink);
}
.site-footer .col a:hover { color: var(--muted); text-decoration: none; }
.site-footer .legal-line {
  margin: 0; padding-top: 24px;
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.05em;
  line-height: 14px; color: var(--muted);
}

/* ---------------------------------------------------------------------------
   Documents — legal pages and blog posts. A 680px reading column with a
   section index parked in the left margin.
--------------------------------------------------------------------------- */
/* 232 index + 41 gutter + 680 column = 953, left-packed in the 1226 lane, so
   the index lands in the same lane as the nav wordmark. */
.doc {
  display: grid; grid-template-columns: 232px 680px;
  column-gap: 41px; padding-top: 80px;
}
.doc-index {
  /* Clear of the sticky nav, which is 81px tall — at the old 40px the list
     stuck underneath the bar and lost its first item behind the wordmark. */
  position: sticky; top: 100px; align-self: start;
  display: flex; flex-direction: column; gap: 18px;
}
.doc-index a {
  font-family: var(--display); font-size: 15px; line-height: 22px;
  color: var(--muted);
  transition: color 0.25s ease;
}
/* The section you're reading darkens to ink; the rest sit back in muted.
   Driven by /js/doc.js as you scroll. */
.doc-index a:hover,
.doc-index a[aria-current] { color: var(--ink); text-decoration: none; }

/* Anchor jumps shouldn't tuck the heading under the top of the window — or,
   now, under the sticky nav, which stands 81px tall. */
.doc-body h2, .doc-body { scroll-margin-top: 100px; }
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

.doc-body { min-width: 0; }
.doc-head h1 {
  font-family: var(--display); font-weight: 400;
  font-size: 44px; line-height: 50px; letter-spacing: -0.02em;
  margin: 0;
}

/* Blog post head — rule underneath, eyebrow above the title, byline below. */
.doc-post .doc-head { padding-bottom: 40px; border-bottom: 1px solid var(--rule); }
/* The head takes the body's column in its own row, leaving the contents list to
   begin in row two — level with the first line of the piece rather than with
   the title. The row gap is the 24px the first paragraph used to carry as its
   own top margin, moved onto the grid so both columns start together. */
.doc-post { row-gap: 24px; }
.doc-post > .doc-head { grid-column: 2; grid-row: 1; }
/* 4px down, because matching the boxes is not the same as matching the text.
   The index is Arimo 15/22 and the body is Hedvig 17/28: aligning their tops
   left the two baselines 4px apart (414 against 418), so the contents list read
   as sitting slightly high. This puts the first link on the body's baseline. */
.doc-post > .doc-index { grid-column: 1; grid-row: 2; padding-top: 4px; }
.doc-post > .doc-body { grid-column: 2; grid-row: 2; }
.doc-post > .doc-body > :first-child { margin-top: 0; }
/* Same treatment on the legal pages: the head takes the body's column in row
   one so the contents list can start level with the document instead of with
   the title. Their bodies open on an h2 rather than a paragraph, so the row gap
   is that heading's 34px top margin moved onto the grid. */
.doc-legal { row-gap: 34px; }
.doc-legal > .doc-head { grid-column: 2; grid-row: 1; }
/* 5px, not the blog's 4px: these bodies open on an Arimo 20/28 heading rather
   than a Hedvig 17/28 paragraph, so the half-leading to make up differs. */
.doc-legal > .doc-index { grid-column: 1; grid-row: 2; padding-top: 5px; }
.doc-legal > .doc-body { grid-column: 2; grid-row: 2; }
.doc-legal > .doc-body > :first-child { margin-top: 0; }
.doc-post .doc-head .eyebrow {
  font-family: var(--mono); font-size: 13px; font-weight: 500;
  line-height: 16px; color: var(--muted); margin: 0 0 18px;
}
.doc-post .doc-head .byline { display: flex; align-items: baseline; gap: 8px; margin: 18px 0 0; }
.doc-post .doc-head .byline .who { font-family: var(--display); font-size: 15px; line-height: 18px; color: var(--muted); }
.doc-post .doc-head .byline .read { font-family: var(--mono); font-size: 13px; font-weight: 500; line-height: 16px; color: var(--muted); }

/* Legal head — no rule, and the opening paragraph belongs to the head. */
.doc-legal .doc-head { padding-bottom: 44px; }
.doc-legal .doc-head .updated {
  font-family: var(--mono); font-size: 13px; font-weight: 500;
  letter-spacing: 0.05em; line-height: 16px; color: var(--muted);
  margin: 14px 0 0;
}
.doc-legal .doc-head .standfirst {
  font-family: var(--text); font-size: 16px; line-height: 27px;
  color: var(--ink); margin: 24px 0 0;
}

/* Blog body runs one step larger than the legal body. */
.doc-post .doc-body h2 {
  font-family: var(--display); font-weight: 400;
  font-size: 22px; line-height: 28px; letter-spacing: -0.01em;
  margin: 36px 0 0;
}
.doc-post .doc-body p { font-family: var(--text); font-size: 16px; line-height: 27px; margin: 24px 0 0; }

.doc-legal .doc-body h2 {
  font-family: var(--display); font-weight: 400;
  font-size: 20px; line-height: 28px; letter-spacing: -0.01em;
  margin: 34px 0 0;
}
.doc-legal .doc-body p { font-family: var(--text); font-size: 16px; line-height: 27px; margin: 12px 0 0; }
.doc-legal .doc-body ul { list-style: none; margin: 12px 0 0; padding: 0; }
.doc-legal .doc-body li {
  position: relative; padding-left: 26px;
  font-family: var(--text); font-size: 16px; line-height: 27px; margin-top: 12px;
}
.doc-legal .doc-body li::before { content: "—"; position: absolute; left: 0; color: var(--muted); }

/* No emphasis in document prose. Hedvig ships weight 400 only, so a bold
   would be a synthesised, smeared face — and the earlier workaround of setting
   these in the sans made them stand out more, not less. They read as plain
   text; the sentence carries the emphasis. */
.doc-body strong {
  font-family: inherit; font-weight: inherit; font-size: inherit;
}
/* Sub-headings below a section, if a document needs them. */
.doc-body h3 {
  font-family: var(--display); font-weight: 500;
  font-size: 17px; line-height: 26px; letter-spacing: -0.01em;
  margin: 24px 0 0;
}

/* ---------------------------------------------------------------------------
   FAQ — the support page's questions as real disclosures. Same pale-blue rules
   as the card and the blog table, so the list reads as one of the site's
   tables rather than a stack of boxes.
--------------------------------------------------------------------------- */
/* Support has no section index, so its single reading column sits in the
   middle of the lane. */
.doc-faq {
  grid-template-columns: minmax(0, 680px);
  justify-content: center;
}
.doc-faq > .doc-body { grid-column: 1; }

.faq-title {
  font-family: var(--display); font-weight: 400;
  font-size: 20px; line-height: 28px; letter-spacing: -0.01em;
  color: var(--ink); margin: 0 0 4px;
}

/* Dividers between items, none above the first or below the last: the list's
   own outer edges are already defined by the heading above and the page below,
   so boxing it in would add two rules that do no work. */
.faq { margin-top: 0; }
.faq-item + .faq-item { border-top: 1px solid var(--rule); }

/* The whole question row is the hit target. The default triangle is replaced
   by the +/− drawn below, which sits on the baseline of the question. */
.faq-item > summary {
  display: flex; align-items: baseline; gap: 16px;
  padding: 20px 4px; cursor: pointer;
  list-style: none;
  transition: color 0.2s ease;
}
.faq-item > summary::-webkit-details-marker { display: none; }
.faq-item > summary:hover { color: var(--accent); }
.faq-item > summary:focus-visible { outline-offset: -2px; }
/* The heading carries the outline (and the section index's anchor); it must not
   keep the block-level margins the document styles give an h2. */
.faq-item > summary h3 {
  flex: 1; margin: 0;
  font-family: var(--display); font-weight: 400;
  font-size: 18px; line-height: 27px; letter-spacing: -0.01em;
  color: inherit;
}

/* Plus that becomes a minus — two bars, one of which rotates away. Drawn in
   CSS so it inherits the row's colour on hover without a second asset. */
.faq-item > summary::after {
  content: ""; flex: 0 0 13px;
  width: 13px; height: 13px; margin-top: 7px;
  background:
    linear-gradient(currentColor, currentColor) center / 13px 1px no-repeat,
    linear-gradient(currentColor, currentColor) center / 1px 13px no-repeat;
  opacity: 0.75;
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.faq-item[open] > summary::after {
  /* Only the vertical bar is left visible by the rotation's end. */
  background: linear-gradient(currentColor, currentColor) center / 13px 1px no-repeat;
  transform: rotate(180deg);
  opacity: 1;
}

.faq-answer { padding: 0 40px 24px 4px; }
.faq-answer > :first-child { margin-top: 0; }
.faq-answer p {
  font-family: var(--text); font-size: 16px; line-height: 27px;
  color: var(--ink); margin: 12px 0 0;
}
.faq-answer ul { list-style: none; margin: 12px 0 0; padding: 0; }
.faq-answer li {
  position: relative; padding-left: 26px;
  font-family: var(--text); font-size: 16px; line-height: 27px; margin-top: 12px;
}
.faq-answer li::before { content: "—"; position: absolute; left: 0; color: var(--muted); }
.faq-answer a { text-decoration: underline; text-underline-offset: 0.18em; text-decoration-thickness: 1px; }
/* Emphasis in an answer is a UI label ("Microphone", "Hey Pod"), not stress —
   the mono face marks it as something to look for on screen. Hedvig has no
   bold, so this replaces the synthesised weight the document styles refuse. */
.faq-answer strong {
  font-family: var(--display); font-weight: 500; font-size: 16px;
}
.faq-answer em { font-family: var(--display); font-size: 16px; font-style: normal; color: var(--accent); }

/* Opening an item animates only where the browser supports interpolating to
   `auto`; elsewhere it simply appears, which is the correct fallback. */
@supports (interpolate-size: allow-keywords) {
  .faq-item::details-content {
    block-size: 0; overflow: hidden;
    transition: block-size 0.28s ease, content-visibility 0.28s allow-discrete;
  }
  .faq-item[open]::details-content { block-size: auto; }
}

/* ---------------------------------------------------------------------------
   Blog index — a table, not a card list: filters and search on one row, then
   date / title / author / read-time columns.
--------------------------------------------------------------------------- */
.blog { padding-top: 130px; }
.blog-controls { display: flex; align-items: center; justify-content: space-between; }
.blog-tabs { display: flex; align-items: center; gap: 28px; }
.blog-tabs button {
  border: 0; padding: 0; background: none; cursor: pointer;
  font-family: var(--display); font-size: 17px; line-height: 22px;
  color: var(--muted);
  transition: color 0.2s ease;
}
.blog-tabs button.on, .blog-tabs button:hover { color: var(--ink); }
.blog-search {
  display: flex; align-items: center; gap: 9px; flex-shrink: 0;
  width: 260px; height: 38px; padding-inline: 14px;
  border: 1px solid var(--hair); border-radius: 999px;
  transition: border-color 0.2s ease;
}
.blog-search:focus-within { border-color: var(--outline); }
.blog-search input {
  flex: 1; min-width: 0;
  border: 0; padding: 0; background: none; outline: none;
  font-family: var(--display); font-size: 15px; line-height: 18px; color: var(--ink);
}
.blog-search input::placeholder { color: var(--muted); }
/* Chrome's own clear button doesn't fit the field. */
.blog-search input::-webkit-search-cancel-button { display: none; }

.post-empty {
  padding-block: 28px;
  font-family: var(--display); font-size: 15px; line-height: 22px; color: var(--muted);
}
.post-row[hidden] { display: none; }
.post-table { padding-top: 34px; min-height: 702px; }
/* Content is inset from the row's edges while the rules still run the full
   width, so the date and read-time aren't sitting on the rule's terminus. */
.post-row {
  display: flex; align-items: center;
  padding-block: 18px; padding-inline: 18px;
  border-top: 1px solid var(--rule);
  color: var(--ink);
}
.post-row:last-child { border-bottom: 1px solid var(--rule); }
/* The whole row is the target, so the whole row responds — the 18px inset is
   what gives the highlight something to sit in. */
.post-row:hover {
  background: rgba(22, 21, 27, 0.035);
  text-decoration: none;
}
.post-row .date {
  flex: 0 0 270px; width: 270px;
  font-family: var(--mono); font-size: 13px; font-weight: 500;
  line-height: 16px; color: var(--muted);
}
.post-row .title {
  flex: 1 1 0; min-width: 0;
  font-family: var(--display); font-size: 17px; line-height: 22px; color: var(--ink);
}
.post-row .author {
  flex: 0 0 250px; width: 250px;
  font-family: var(--display); font-size: 15px; line-height: 18px; color: var(--muted);
}
.post-row .read {
  flex: 0 0 56px; width: 56px; text-align: right;
  font-family: var(--mono); font-size: 13px; font-weight: 500;
  line-height: 16px; color: var(--muted);
}

/* ---------------------------------------------------------------------------
   Responsive. The design is drawn at 1440; these are the points where holding
   it literally starts to break the reading experience.
--------------------------------------------------------------------------- */
@media (max-width: 1080px) {
  /* No margin left for the index to sit in, so it becomes a contents block
     above the document. */
  .doc { grid-template-columns: minmax(0, 680px); justify-content: center; padding-top: 80px; }
  .doc-index { position: static; padding-bottom: 48px; }

  /* Stacked: the phone replica leads (right under the heading), the feature card
     follows. `order` reorders only within this column layout — the desktop row
     above keeps its DOM order (card left, phone right). */
  /* The design's 220px band is drawn for 1440. Below that it steps down with
     the rest of the layout rather than holding a fixed slab of white. */
  .showcase { padding-top: 140px; }
  .showcase-row { flex-direction: column; align-items: center; gap: 56px; }
  .showcase-row .phone { order: -1; }
  .portal { left: auto; right: -120px; opacity: 0.7; }
}

/* Tablet / small laptop. The blog table's fixed date and author columns add up
   to 576px of the row on their own, so they give way before the title does. */
@media (max-width: 920px) {
  .hero h1 { font-size: clamp(34px, 6.4vw, 56px); }
  .doc-head h1 { font-size: 38px; line-height: 44px; }
  .post-row .date { flex-basis: 190px; width: 190px; }
  .post-row .author { flex-basis: 170px; width: 170px; }
  .blog { padding-top: 96px; }
  .post-table { min-height: 0; }
}

@media (max-width: 720px) {
  /* Two slots — wordmark and one menu — still fit a 375px row, so the nav no
     longer needs to wrap the way it did with four. */
  .site-nav { padding-top: 24px; }
  .mark { font-size: 19px; line-height: 24px; }
  .site-nav a, .menu-label { font-size: 16px; }
  /* Touch has no hover, so the label's click toggle (initNavMenus) is what
     opens the panel here; it rests closed via the base opacity/visibility and
     the .open class reveals it. The card is absolutely positioned, so it
     overlays rather than reflowing the nav row. */
  .menu-panel { display: block; }
  /* Flush to the label's right edge — at 375px even the -20px overhang would
     put the card's corner past the screen. */
  .contact-slot .menu-panel { right: 0; }
  .menu-card { min-width: 168px; }

  .hero { padding-top: 56px; }
  /* Arimo's headline line measures about 9.09em at this letter spacing.
     Size against the actual content lane so it fills mobile widths safely. */
  .hero-inner { container-type: inline-size; }
  .hero h1 { font-size: 10.9cqw; }
  .hero .lede { font-size: 17px; line-height: 28px; margin-top: 14px; }
  /* The hard break is there to shape the desktop measure; on a phone the line
     it creates is already full, so it only adds a ragged gap. */
  .hero .lede br { display: none; }
  /* The row keeps its shape and wraps only when it actually runs out of width.
     The 18px inset goes, though — flush left is the only sensible edge once
     the lane is this narrow. */
  /* Offset the tighter headline gap so signup controls stay in place. */
  .hero-actions { gap: 16px 24px; padding-inline: 0; margin-top: 28px; }

  .showcase { padding-top: 72px; }
  .showcase h2 { font-size: 26px; line-height: 34px; margin-bottom: 28px; }
  .showcase-row { gap: 44px; }
  .card { width: 100%; padding: 24px; }
  .card .masthead .name { font-size: 24px; }
  .card .row-label { flex-basis: 64px; width: 64px; }
  .card .steps li:nth-child(2) { padding-left: 16px; }
  .card .steps li:nth-child(3) { padding-left: 32px; }
  .card .steps li:nth-child(4) { padding-left: 48px; }

  .mission { padding-top: 120px; }
  .mission h2 { font-size: 26px; line-height: 34px; }
  .mission h3 { font-size: 21px; line-height: 30px; }
  .mission p { font-size: 16px; line-height: 27px; }

  /* Keep all three link groups together; give longer legal labels more room. */
  .site-footer { padding-top: 96px; padding-bottom: 48px; }
  .site-footer .cols {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.6fr);
    gap: 16px;
  }
  .site-footer .spacer { display: none; }

  /* Mobile documents use the full reading lane without a side index. Reset
     desktop column placement so it cannot create an implicit second column. */
  .doc { padding-top: 56px; }
  .doc-index { display: none; }
  .doc-post > .doc-head,
  .doc-post > .doc-body,
  .doc-legal > .doc-head,
  .doc-legal > .doc-body { grid-column: 1; }
  .doc-head h1 { font-size: 30px; line-height: 37px; }
  .doc-post .doc-head { padding-bottom: 28px; }
  .doc-legal .doc-head { padding-bottom: 32px; }
  .doc-body p, .doc-body li, .doc-legal .doc-head .standfirst { font-size: 16px; line-height: 27px; }
  .doc-post .doc-body h2 { font-size: 20px; line-height: 27px; margin-top: 30px; }
  .faq-title { font-size: 18px; line-height: 26px; }
  .faq-item > summary { padding: 17px 2px; gap: 12px; }
  .faq-item > summary h3 { font-size: 17px; line-height: 25px; }
  .faq-answer { padding: 0 8px 20px 2px; }
  .faq-answer p, .faq-answer li { font-size: 16px; line-height: 27px; }

  /* Blog index: controls stack, and each post row becomes title-over-meta
     rather than four columns fighting over 330px. */
  .blog { padding-top: 64px; }
  .blog-controls { flex-direction: column; align-items: stretch; gap: 16px; }
  .blog-tabs { flex-wrap: wrap; gap: 18px; }
  .blog-search { width: 100%; }
  .post-table { padding-top: 22px; }
  .post-row { flex-wrap: wrap; row-gap: 6px; padding-inline: 0; }
  .post-row .title { flex: 1 1 100%; order: -1; font-size: 18px; line-height: 25px; }
  .post-row .date { flex: 1 1 auto; width: auto; }
  .post-row .author { display: none; }
  .post-row .read { flex: 0 0 auto; width: auto; }
  .post-row:hover { background: none; }

  .portal { display: none; }
}

/* Below this the link, the label and a 250px field no longer share a line, so
   the form drops under "learn more" and the field takes the width it is given
   rather than staying a fixed 250px stub. */
@media (max-width: 620px) {
  /* Center the stacked signup controls across the mobile content lane. */
  .hero-actions { padding-inline: 0; }
  .waitlist-form {
    width: 100%; grid-template-columns: minmax(0, 1fr); justify-items: center;
  }
  .waitlist-go { grid-column: 1; grid-row: 2; margin-top: 14px; }
  .waitlist-form .field {
    grid-column: 1; grid-row: 1; width: 100%;
    justify-self: stretch;
    padding: 6px 12px;
    border: 1px solid rgba(22, 21, 27, 0.18);
    border-radius: 999px;
    background: rgba(22, 21, 27, 0.03);
    cursor: text;
  }
  .waitlist-form .field:focus-within {
    border-color: var(--accent);
    outline: 2px solid var(--rule); outline-offset: 2px;
  }
  .waitlist-form input[type="email"] { text-align: center; }
  .waitlist-ghost {
    left: 50%; top: 50%; transform: translate(-50%, -50%);
    max-width: calc(100% - 24px); overflow: hidden;
  }
  .waitlist-status { text-align: center; }
  /* The single column is already the full width here, so half of it is the
     middle of the screen and 250% of it runs off the side. */
  .waitlist-status { grid-row: 3; margin-left: 0; min-width: 100%; }
}

/* Very small phones (≤360px) and older devices. The phone mock is built from
   fixed pixel sizes so it can't reflow — scale the whole piece down and pull
   the following content back up by the height the scale removes. */
@media (max-width: 380px) {
  .phone { transform: scale(0.88); transform-origin: top center; margin-bottom: -73px; }
  .site-nav a, .menu-label { font-size: 15px; }
  .card { padding: 20px; }
  /* Below ~330px of card width the mono list wraps after two or three words
     per line beside its label — the label goes above the list instead. */
  .card .row { flex-direction: column; }
  .card .row-label { flex: 0 0 auto; width: auto; line-height: 20px; margin-bottom: 6px; }
  .card .steps li:nth-child(2) { padding-left: 12px; }
  .card .steps li:nth-child(3) { padding-left: 24px; }
  .card .steps li:nth-child(4) { padding-left: 36px; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}
