/* ============================================================
   Leafy — editorial landing
   ============================================================ */

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

:root {
  --bg:           #000000;
  /* The page colour at zero alpha. A gradient that ends in plain `transparent`
     fades through transparent *black*, which on a white page is a grey smear. */
  --bg-0:         rgba(0, 0, 0, 0);
  /* Fades out the bottom of a zooming clip (see .library-shot--zoom). This is
     a mask, not a colour, so it has no theme: it takes the picture away rather
     than painting the page colour on top of it. Alphas are the old overlay's
     curve inverted, which keeps its shape: barely anything for the first
     stretch, all of the work in the last quarter, gone at 92% so the clip's
     own last row is already at zero before the box ends. */
  --shot-mask: linear-gradient(to bottom,
    rgb(0, 0, 0) 0%,
    rgba(0, 0, 0, 0.96) 24%,
    rgba(0, 0, 0, 0.89) 40%,
    rgba(0, 0, 0, 0.76) 54%,
    rgba(0, 0, 0, 0.58) 66%,
    rgba(0, 0, 0, 0.38) 76%,
    rgba(0, 0, 0, 0.16) 85%,
    rgba(0, 0, 0, 0) 92%,
    rgba(0, 0, 0, 0) 100%);
  /* The two lengths the bottom of a zooming clip is built from. Both are
     lengths, not percentages: the mask has to end exactly on the clip's last
     row, and a percentage of the figure's width and one of its height cannot
     be added together to find that row. --shot-reserve tracks what 7% of the
     figure used to come to at either end of the range. */
  --shot-reserve: clamp(48px, 7.5vw, 72px);
  /* The clip's outer rows are the window's own border, brighter than its
     panel. Chrome keeps them where Safari averages them away, and they were
     cut for a while on that account; they are the edge of the window, so they
     stay. */
  /* Fixed. A share of the box keeps it in proportion on a wide screen, but the
     curve spends its first stretch near opaque and does its work at the end,
     so a phone's 16px came out as about five pixels of actual gradient, which
     the eye reads as a straight cut. The floor is what makes it a fade there.
     It has to fit inside --shot-reserve, which is where it lives. */
  --shot-fade-len: max(36px, 6%);
  --bg-secondary: #0a0a0a;
  /* The tile: Apple's card grey on black, the redesign's --card. Lighter than
     --bg-secondary, which on a black page is too close to the page to read as
     a card. */
  --card:         #121212;
  /* One step lighter again, for a card that has to stand out from the others
     (the recommended plan). */
  --card-2:       #1c1c1e;
  /* On pure black these have to carry more alpha than they would on a grey
     page: 0.06 white lands on #0f0f0f, which most screens simply do not show.
     These are the values where a hairline reads as a line. */
  --separator:    rgba(255, 255, 255, 0.18);
  --separator-light: rgba(255, 255, 255, 0.12);

  /* Lime green — matches the Leafy app icon; one green, used site-wide */
  --green:        #9fd038;   /* accent text / links / labels */
  --green-fill:   #9fd038;   /* solid green surfaces (callouts, pills, chips) — stays bright in both themes */
  --green-ink:    #9fd038;   /* brush-stroke fill */
  --green-hover:  #b3e840;
  --green-deep:   #7ab02c;   /* darker lime — dry-brush streak / borders */
  --green-on:     #1e2e0a;   /* dark ink, for text on solid green fills */

  --text-primary:   #f5f5f7;
  --text-secondary: #86868b;
  --text-tertiary:  #76767c;   /* small labels on black need this much lift */
  --text-body:      #d4d4d9;   /* readable body copy on black — not muted gray */

  /* 1366, the redesign's container (Procreate's own), not the 1180 the site
     was built on: the page reads bigger, and the clips and cards with it. */
  --page-w:      1366px;
  --nav-h:       52px;
  --gutter:      clamp(22px, 3.6vw, 48px);

  /* Category accents — mirror the Leafy app's folder icon tints */
  --cat-red:    #ff6b6b;
  --cat-orange: #f5a03c;
  --cat-blue:   #4d8bff;
  --cat-yellow: #f6c945;
  --cat-teal:   #3fc4b0;
  --cat-indigo: #7c86ff;
  --cat-purple: #b57cff;
  --cat-gray:   #9aa0a6;

  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display",
          "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-mono: "SF Mono", ui-monospace, Menlo, Monaco, monospace;
  /* Headings used to be set in Fraunces, a literary serif. One face for the
     whole site now (2026-09-20): the system sans the body is in, the way the
     Apple-style redesign has it, so headings and copy read as one voice. The
     variable stays so nothing else has to change. */
  --font-display: var(--font);
}

/* ── Light theme ──────────────────────────────────────────────────────────
   The site is dark by default (the :root values above). js/theme.js stamps
   data-theme on <html> before paint; this block redefines the tokens for the
   light setting. Only tokens change here — everything else reads them. */
:root[data-theme="light"] {
  --bg:           #ffffff;
  --bg-0:         rgba(255, 255, 255, 0);
  /* No light copy of --shot-mask: a mask has no colour to swap. */
  --bg-secondary: #f2f2f4;
  --card:         #f2f2f4;
  --card-2:       #e8e8ec;
  --separator:       rgba(0, 0, 0, 0.14);
  --separator-light: rgba(0, 0, 0, 0.08);

  /* Lime needs to darken for legible text/links on white; the solid green
     button fill is hardcoded elsewhere and stays bright. */
  --green:        #4c7c12;
  --green-ink:    #4c7c12;
  --green-hover:  #3d660c;
  --green-deep:   #3d660c;

  --text-primary:   #1d1d1f;
  --text-secondary: #545458;
  --text-tertiary:  #77777e;
  --text-body:      #33333a;

  /* Faint white film used for hairlines/tints on black → faint ink on white. */
  --film-weak:   rgba(0, 0, 0, 0.05);
  --film-mid:    rgba(0, 0, 0, 0.10);
  --film-strong: rgba(0, 0, 0, 0.16);

  /* The keycap stays pale on white; its face darkens a touch so it separates
     from the page, and the cast shadow lightens. */
  --cta-face: #1d1d1f;
  --cta-ink:  #ffffff;
  --cta-rim:  #000000;

  --key-face-top:    #ededf1;
  --key-face-bottom: #d8d8de;
  --key-rim:         #9a9aa3;
  --key-rim-deep:    #75757e;
  --key-shadow:      rgba(0, 0, 0, 0.18);
}
/* Dark-mode values for the same film tokens (default). */
:root {
  --film-weak:   rgba(255, 255, 255, 0.05);
  --film-mid:    rgba(255, 255, 255, 0.14);
  --film-strong: rgba(255, 255, 255, 0.16);

  /* Keycap (<kbd>): a pale physical key, same one in both themes — a keyboard
     key is light grey in the world. Only the rim and the shadow change. */
  /* Article CTA keycap: ink and page swap places with the theme, so it reads
     as the opposite of the page rather than as a second brand green. */
  --cta-face: #f5f5f7;
  --cta-ink:  #0a0a0a;
  --cta-rim:  #a8a8b0;

  --key-face-top:    #f0f0f3;
  --key-face-bottom: #dcdce1;
  --key-ink:         #2a2a2e;
  --key-gloss:       rgba(255, 255, 255, 0.9);
  --key-rim:         #a3a3ab;
  --key-rim-deep:    #7e7e87;
  --key-shadow:      rgba(0, 0, 0, 0.45);
}

html {
  scroll-behavior: smooth;
  /* <html> is the one scroll container (its scrollbar is styled below), and
     overflow-x:hidden clips full-bleed sections. scrollbar-gutter reserves
     the bar's space even on pages that don't need to scroll (e.g. the
     desktop help two-pane layout), so the nav's right-aligned content
     doesn't shift a few px when navigating between a scrolling page and a
     non-scrolling one. */
  overflow-x: hidden;
  scrollbar-gutter: stable;
}
/* iOS Safari does not reliably clip to overflow-x:hidden on <html> alone: the
   overhanging clips (the drag box, the zooming product windows) widened the
   page, and a page wider than the screen is loaded zoomed out with the fixed
   nav cut off at the right. overflow-x:clip on the body clips without making
   another scroll container, so <html> stays the one scroller. */
body {
  overflow-x: clip;
}
/* Lock the html scroller (not body) when a sheet is open. Removing the
   scrollbar would widen the page and shift everything, so we pad the right
   by exactly the bar's measured width (--sbw, set by JS). scrollbar-gutter
   isn't reliable across browsers when the bar is custom-styled, so we don't
   use it. On overlay-scrollbar devices --sbw is 0 → no padding, no shift. */
html:has(> body.menu-open),
html:has(> body.toc-open) {
  overflow: hidden;
  padding-right: var(--sbw, 0px);
}
/* Slim, unobtrusive scrollbar — a scroll reference, not a jump-scare */
html::-webkit-scrollbar { width: 10px; }
html::-webkit-scrollbar-thumb {
  background: var(--film-mid);
  border-radius: 999px;
  border: 2px solid var(--bg);
}
html::-webkit-scrollbar-thumb:hover { background: var(--film-strong); }

body {
  font-family: var(--font);
  font-size: 1rem;
  line-height: 1.5;
  letter-spacing: -0.02em;
  background: var(--bg);
  color: var(--text-primary);
  -webkit-font-smoothing: antialiased;
}

a { color: var(--green); text-decoration: none; }
a:hover { color: var(--green-hover); }

/* Physical keycap. Every length is in em so the cap keeps its proportions
   wherever it lands — hero line, list item, heading. The two stacked rims are
   the side of the cap; the blurred one under them is its shadow on the page.
   Hover presses it down and swallows the rim. */
kbd {
  font-family: var(--font);
  font-size: 0.84em;
  font-weight: 600;
  letter-spacing: -0.01em;
  display: inline-block;
  padding: 0.22em 0.6em 0.28em;
  border-radius: 0.4em;
  line-height: 1.15;
  vertical-align: 0.04em;
  /* A key is an object sitting in the sentence, not a word in it: it needs
     more air than a word space on either side, or it reads as glued on. */
  margin: 0 0.28em 0 0.1em;
  color: var(--key-ink);
  background: linear-gradient(180deg, var(--key-face-top), var(--key-face-bottom));
  box-shadow:
    inset 0 0.09em 0 var(--key-gloss),
    0 0.22em 0 var(--key-rim),
    0 0.3em 0 var(--key-rim-deep),
    0 0.46em 0.8em var(--key-shadow);
  transition: transform .09s ease, box-shadow .09s ease;
}
/* A keycap is taller than the type around it, so any line that carries one
   needs more leading than plain copy — otherwise it crowds the lines above
   and below and reads as pasted in. */
p:has(kbd), li:has(kbd), dd:has(kbd) { line-height: 1.85; }
h1:has(kbd), h2:has(kbd), h3:has(kbd), h4:has(kbd) { line-height: 1.7; }
/* Same again where a class already sets the line-height and would outrank the
   bare element selector above. */
.features-lead:has(kbd),
.demo-caption:has(kbd),
.frow-text h3:has(kbd) { line-height: 1.7; }

kbd:hover {
  transform: translateY(0.2em);
  box-shadow:
    inset 0 0.09em 0 var(--key-gloss),
    0 0.06em 0 var(--key-rim-deep),
    0 0.18em 0.5em var(--key-shadow);
}

img { display: block; max-width: 100%; height: auto; }

/* ── Scrollbar ── */
.hide-scrollbar {
  scrollbar-width: none;            /* Firefox */
  -ms-overflow-style: none;         /* IE / Edge */
}
.hide-scrollbar::-webkit-scrollbar {
  display: none;                    /* Chrome, Safari, Opera */
}

/* ── Grain ── */
.grain {
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-repeat: repeat;
  background-size: 180px;
}

/* ============================================================
   NAV
   ============================================================ */
#nav {
  position: sticky;
  top: 0;
  /* Translucent, so the page slides under it rather than meeting a solid
     strip. */
  -webkit-backdrop-filter: saturate(180%) blur(18px);
  backdrop-filter: saturate(180%) blur(18px);
  /* Sits between the mobile menu (300) and the help toc sheet (350): always
     above the menu so the X stays tappable AND so the menu never paints over
     the nav while fading out on close (that caused an icon flash). Still below
     the toc sheet, which slides up to meet it. */
  z-index: 320;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  border-bottom: 1px solid var(--separator-light);
  transition: border-color 0.3s ease;
}
#nav.scrolled {
  border-bottom: 1px solid var(--separator);
}
/* Apple's bar, which the redesign follows: the wordmark on the left with the
   section links running straight on from it, and only the one thing the bar is
   asking you to do over on the right. The links used to be bunched against the
   right edge with the button, which made the bar read as one heavy block with
   an empty half. */
.nav-inner {
  width: 100%;
  max-width: var(--page-w);
  margin: 0 auto;
  padding: 0 var(--gutter);
  display: flex;
  align-items: center;
  gap: 30px;
}
.nav-logo {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  text-decoration: none;
}
.nav-logo:hover { color: var(--text-primary); }
/* No mark beside the wordmark: the app icon tile read as a sticker, and the
   hand-drawn LEAFY was a blot at bar height. The word does the job alone. */
.nav-mark { display: none; }
.nav-right {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 22px;
}
/* Everything from the language switcher on sits at the far right; the links
   before it stay next to the wordmark. Written against the switcher and the
   button both, since the switcher is injected by script and is not there on
   the first paint. */
.nav-right > .lang-select,
.nav-right > .lang-select ~ .nav-cta { margin-left: 0; }
.nav-right > .lang-select { margin-left: auto; }
.nav-right > .nav-cta:first-of-type { margin-left: auto; }
.nav-right > .lang-select ~ .nav-cta,
.nav-right > .nav-burger { margin-left: 0; }
.nav-links {
  display: flex;
  list-style: none;
  gap: 30px;
}
.nav-links a {
  font-size: 0.8125rem;
  font-weight: 400;
  color: var(--text-secondary);
  text-decoration: none;
  transition: color 0.2s;
}
.nav-links a:hover,
.nav-links a.is-active { color: var(--text-primary); }
/* Flat pill button — the Help nav CTA */
.nav-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  padding: 7px 16px;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-decoration: none;
  white-space: nowrap;
  border-radius: 980px;
  color: var(--green-on);
  background: var(--green-fill);
  transition: background 0.15s ease;
}
/* Shift the fill (not opacity) so the nav never shows through as a ghost.
   Pin the text colour too: without it the global `a:hover` rule (higher
   specificity) repaints the label green on hover. */
.nav-cta:hover {
  color: var(--green-on);
  background: var(--green-hover);
}
/* The second bar's button: one size down, the way the localnav's type is. */
.nav-cta--sm {
  padding: 6px 14px;
  font-size: 0.75rem;
}

/* ── The home page's second bar ─────────────────────────────────────────────
   Apple's localnav: the site nav scrolls away with the page, and once it is
   gone this one slides down and stays, carrying the page's own sections
   instead of the site's. It is a separate element rather than the same bar
   changing its links, so the swap is a slide rather than a repaint. */
.home #nav { position: relative; }
.localnav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 319;
  height: var(--nav-h);
  transform: translateY(-100%);
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  -webkit-backdrop-filter: saturate(180%) blur(18px);
  backdrop-filter: saturate(180%) blur(18px);
  border-bottom: 1px solid var(--separator-light);
  transition: transform 0.32s cubic-bezier(0.4, 0, 0.6, 1);
}
.localnav.is-shown { transform: none; }
.localnav-inner {
  width: 100%;
  max-width: var(--page-w);
  height: 100%;
  margin: 0 auto;
  padding: 0 var(--gutter);
  display: flex;
  align-items: center;
  gap: 28px;
}
.localnav-spacer { flex: 1; }
.localnav a {
  font-size: 0.8125rem;
  color: var(--text-secondary);
  text-decoration: none;
  white-space: nowrap;
}
.localnav a:hover { color: var(--text-primary); }
/* The button is a button, not one of the bar's links: the rule above is more
   specific than .nav-cta's own colour and was painting its label grey. */
.localnav .nav-cta,
.localnav .nav-cta:hover { color: var(--green-on); }
.localnav-title {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-right: 8px;   /* the site nav's gap from logo to first link */
  font-size: 1.1875rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text-primary) !important;
}
/* Anchors land below the bar, not under it. A section's top padding is the
   seam before it, not part of the section, so the jump scrolls past it and
   stops a small gap above the heading. Without this the bar is cleared but
   the heading starts a third of a screen down, under an empty band. */
.home section[id] {
  --anchor-pad: var(--section-gap);
  scroll-margin-top: calc(var(--nav-h) + 28px - var(--anchor-pad));
}
/* The first section under the hero is already tight against it. */
.home .plain-hero + .plain-section { --anchor-pad: 12px; }

@media (max-width: 720px) {
  /* Only the wordmark and the button: four section links do not fit, and the
     sections are one scroll apart anyway. */
  .localnav a:not(.localnav-title):not(.nav-cta) { display: none; }
}

/* ── Language switcher (injected into the nav by i18n.js) ──
   Minimal: just the language name, like a nav link. */
.lang-select {
  margin: 0;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--text-secondary);
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 400;
  line-height: 1;
  cursor: pointer;
  transition: color 0.2s ease;
}
.lang-select:hover { color: var(--text-primary); }
.lang-select:focus-visible { outline: none; }
.lang-select option { color: #111; background: #fff; }

/* ── Mobile menu: a burger that opens a full-screen sheet (phones only) ── */
.nav-burger {
  display: none;               /* shown only under the mobile breakpoint */
  width: 42px;
  height: 42px;
  margin-right: -8px;
  padding: 0;
  border: none;
  background: transparent;
  cursor: pointer;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
}
.nav-burger span {
  display: block;
  width: 22px;
  height: 2px;
  border-radius: 2px;
  background: var(--text-primary);
  transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease, background 0.2s ease;
}
/* Bars morph into an X when open */
.nav-burger.is-open span:nth-child(1) { transform: translateY(7px) rotate(45deg); background: var(--green); }
.nav-burger.is-open span:nth-child(2) { opacity: 0; }
.nav-burger.is-open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); background: var(--green); }
/* Six links, the Help pill and the language switcher stop fitting long before
   the phone breakpoint: at ~750px the row was running under the logo, which
   read as the bar losing elements. The burger takes over while there is still
   room for it. */
@media (max-width: 900px) {
  .nav-links,
  .nav-cta { display: none; }
  .nav-burger { display: inline-flex; margin-left: auto; }
}

.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.3s ease, visibility 0.3s ease;
}
.mobile-menu.is-open { opacity: 1; visibility: visible; pointer-events: auto; }
.mobile-menu-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  width: 100%;
  padding: 0 var(--gutter);
}
.mobile-menu-inner a {
  font-family: var(--font-display);
  font-size: 1.875rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  padding: 10px 8px;
  transition: color 0.15s ease;
}
.mobile-menu-inner a:hover,
.mobile-menu-inner a:active { color: var(--green); }
.mobile-menu-cta {
  margin-top: 24px;
  padding: 14px 34px 16px;
  background: #9fd038;
  color: var(--green-on) !important;
  border-radius: 980px;
  font-size: 1.25rem !important;
  transition: background 0.2s ease;
}
.mobile-menu-cta:hover,
.mobile-menu-cta:active {
  color: var(--green-on) !important;
  background: var(--green-hover);
}
/* Nav sits above the sheet via its own z-index (see #nav); here we just drop
   its border while the sheet is open, since nav and sheet are both black. */
body.menu-open #nav { border-color: transparent; }

/* ============================================================
   ① PAIN — full-bleed sticky hero
   ============================================================ */

/* Hero is pinned to the viewport; hero-wrap just reserves its height in
   normal flow so .content scrolls up and covers it like a sheet. */
.hero-wrap {
  position: relative;
  height: 100vh;
}

.pain {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  z-index: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 0 0 72px;
  overflow: hidden;
}

/* Full-bleed background image */
.pain-shot {
  position: absolute;
  inset: 0;
  z-index: 0;
}
.pain-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: saturate(0.85) brightness(0.82);
}
.pain-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgba(0,0,0,0.30) 0%,
    rgba(0,0,0,0.38) 35%,
    rgba(0,0,0,0.88) 100%
  );
  pointer-events: none;
}

/* Copy sits at the bottom of the viewport, over the image */
.pain-copy {
  position: relative;
  z-index: 1;
  max-width: var(--page-w);
  width: 100%;
  margin: 0 auto;
  padding: 0 var(--gutter);
}
.pain h1 {
  font-family: var(--font-display);
  font-size: clamp(3rem, 8vw, 6rem);
  font-weight: 900;
  line-height: 0.98;
  letter-spacing: -0.03em;
  max-width: 680px;
  text-shadow: 0 2px 24px rgba(0,0,0,0.4);
}
.pain-sub {
  margin-top: 16px;
  font-size: 1.125rem;
  line-height: 1.55;
  color: rgba(255,255,255,0.75);
  max-width: 480px;
  text-shadow: 0 1px 12px rgba(0,0,0,0.4);
}

/* One solid block slides over the fixed hero — no gaps possible */
.content {
  position: relative;
  z-index: 1;
  background: var(--bg);
}
footer {
  position: relative;
  z-index: 1;
  background: var(--bg);
}

/* ============================================================
   ② PIVOT — the promise
   ============================================================ */
.pivot {
  max-width: var(--page-w);
  margin: 0 auto;
  padding: 120px var(--gutter);
}
.pivot-inner {
  max-width: 580px;
}
.pivot h2 {
  font-size: clamp(2rem, 4.5vw, 3.25rem);
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.04em;
}
.pivot-sub {
  margin-top: 18px;
  font-size: 1.125rem;
  line-height: 1.55;
  color: var(--text-secondary);
  max-width: 480px;
}

/* ============================================================
   ③ DEMO — one-shot reveal: article → select → define.
   Plays once in place when scrolled into view — no pinning, no
   scroll-jacking. JS times a single data-phase attribute on
   .demo; everything else is plain CSS transitions reacting to it.
   ============================================================ */
.demo {
  max-width: none;
  margin: 0 0 120px;
  padding: 110px var(--gutter) 56px;
  position: relative;
}

.demo-sticky { position: relative; }

.demo-stage {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.demo-vignette {
  position: absolute;
  inset: -20% -10%;
  pointer-events: none;
  z-index: 0;
  background: radial-gradient(ellipse 50% 55% at 50% 42%, rgba(159, 208, 56, 0.16), transparent 72%);
}

.demo-scene {
  position: relative;
  z-index: 1;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.demo-page {
  position: relative;
  width: min(680px, calc(100% - 48px));
  margin: 0 auto;
  -webkit-font-smoothing: subpixel-antialiased;
}

.demo-page-inner {
  padding: clamp(32px, 4vw, 48px) clamp(28px, 3.5vw, 40px);
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.045);
  border: 1px solid rgba(255, 255, 255, 0.11);
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.35);
}

.demo-article p {
  font-family: Georgia, "Times New Roman", serif;
  font-size: clamp(1rem, 1.65vw, 1.1875rem);
  line-height: 1.72;
  letter-spacing: -0.01em;
  color: rgba(245, 245, 247, 0.78);
  margin-bottom: 1.15em;
}
.demo-article p:last-child { margin-bottom: 0; }

.demo-target-sentence {
  color: var(--text-primary);
}

.demo-page mark {
  background: none;
  color: inherit;
  font-weight: 600;
}

.demo-highlight-wrap {
  position: relative;
  display: inline-block;
  vertical-align: baseline;
}

.demo-select-box {
  position: absolute;
  inset: -5px -10px;
  border: 1.5px solid rgba(255, 255, 255, 0.72);
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.055);
  pointer-events: none;
  z-index: 2;
  opacity: 0;
  transform: scale(0.9);
  transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1),
              transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
.demo[data-phase="select"] .demo-select-box,
.demo[data-phase="define"] .demo-select-box {
  opacity: 1;
  transform: scale(1);
}

.demo-popup {
  position: fixed;
  width: clamp(300px, 38vw, 460px);
  filter: drop-shadow(0 20px 48px rgba(0,0,0,0.55));
  z-index: 50;
  opacity: 0;
  pointer-events: none;
  transform: translateY(10px) scale(0.97);
  transform-origin: top center;
  transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1),
              transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
.demo[data-phase="define"] .demo-popup {
  opacity: 1;
  transform: translateY(0) scale(1);
}

.demo-captions {
  position: relative;
  margin: 28px auto 0;
  text-align: center;
  width: min(480px, calc(100% - 40px));
  min-height: 1.4em;
  pointer-events: none;
}
.demo-caption {
  position: absolute;
  inset: 0;
  font-size: clamp(0.875rem, 1.5vw, 1rem);
  font-weight: 500;
  letter-spacing: -0.02em;
  color: rgba(245, 245, 247, 0.72);
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.4s ease, transform 0.4s ease;
}
.demo-caption kbd { font-size: 0.9em; }
.demo[data-phase="select"] .demo-caption[data-phase="select"],
.demo[data-phase="define"] .demo-caption[data-phase="define"] {
  opacity: 1;
  transform: translateY(0);
}

/* ============================================================
   FEATURES — visual walkthrough. Each row pairs a real screenshot or
   screen recording with a short caption. Static, alternating sides,
   plain vertical scroll — no pinning, no scrubbing.
   ============================================================ */
.features {
  max-width: var(--page-w);
  margin: 0 auto;
  padding: 88px var(--gutter);
  border-top: 1px solid var(--separator-light);
}
.features-head .topic-label { display: block; margin-bottom: 14px; }
.features-head h2 {
  font-family: var(--font-display);
  font-size: clamp(2.25rem, 5.5vw, 3.75rem);
  font-weight: 900;
  letter-spacing: -0.03em;
  line-height: 1.02;
}
/* The one plain-language definition of Leafy on the page — the hero tells
   the story, this line says what the product literally is. */
.features-lead {
  margin-top: 18px;
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--text-secondary);
  max-width: 620px;
}
.features-lead kbd { font-size: 0.85em; }
.scene-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; }
.scene-chip {
  font-size: 0.8125rem;
  color: var(--text-secondary);
  padding: 6px 13px;
  border-radius: 999px;
  border: 1px solid var(--separator);
  background: rgba(255, 255, 255, 0.02);
}

.frow {
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 0.45fr);
  gap: clamp(24px, 4vw, 52px);
  align-items: center;
  margin-top: clamp(52px, 7vw, 88px);
}
.frow--reverse {
  /* Media sits in column 2 — give it the wide track, not the narrow one */
  grid-template-columns: minmax(0, 0.45fr) minmax(0, 1.55fr);
}
.features-head + .frow {
  margin-top: clamp(24px, 3.5vw, 36px);
}
.frow.demo-row {
  align-items: start;
}
.frow.demo-row .frow-text {
  padding-top: 2px;
}
.frow--reverse .frow-media { order: 2; }
.frow--reverse .frow-text  { order: 1; }

/* No backdrop board — the screenshot/recording stands on its own. */
.frow-media img,
.frow-media video {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 14px;
  box-shadow: 0 30px 70px rgba(0, 0, 0, 0.55);
  opacity: 0;
  transform: translate3d(0, 16px, 0);
  transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.1s,
              transform 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.1s;
}
.reveal.is-visible .frow-media img,
.reveal.is-visible .frow-media video {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}
.feature-vid--scrub {
  cursor: grab;
  touch-action: none;
  user-select: none;
  -webkit-user-drag: none;
}
.feature-vid--scrub.is-dragging {
  cursor: grabbing;
}

/* The floating definition popup just centres, bare. */
.frow-media--pop {
  display: flex;
  align-items: center;
  justify-content: center;
}
.frow-media--pop img { width: min(320px, 82%); }

.frow-step {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--green);
  margin-bottom: 12px;
}
.frow-text h3 {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 3vw, 2.25rem);
  font-weight: 900;
  letter-spacing: -0.03em;
  line-height: 1.08;
  margin-bottom: 14px;
}
.frow-text h3 kbd { font-size: 0.8em; vertical-align: 0.08em; }
.frow-text p {
  font-size: 1rem;
  line-height: 1.6;
  color: var(--text-secondary);
  max-width: 420px;
}

/* Merged scan + look-up demo — a reading passage with the definition
   popup dropped right under the highlighted word. Static, no animation. */
.demo-card {
  position: relative;
  overflow: visible;
}
.demo-card p {
  font-family: Georgia, "Times New Roman", serif;
  font-size: clamp(1rem, 1.6vw, 1.15rem);
  line-height: 1.72;
  color: var(--text-body);
  margin-bottom: 1em;
}
.demo-card p:last-child { margin-bottom: 0; }
.demo-eyebrow {
  font-family: var(--font);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  margin-bottom: 18px;
}
.demo-word {
  color: var(--text-primary);
  font-weight: 600;
}
.demo-pop {
  position: absolute;
  width: 300px;
  max-width: 70vw;
  height: auto;
  border-radius: 14px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.55);
  z-index: 5;
  pointer-events: none;
}

/* ============================================================
   ⑤ DOWNLOAD BUTTON
   ============================================================ */
.dl-wrap {
  margin-top: 32px;
  margin-bottom: 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}
.dl-btn-wrap {
  display: block;
  position: relative;
}
.dl-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 11px 24px;
  background: var(--green-fill);
  color: var(--green-on);
  font-family: var(--font);
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  /* A pill, flat: 980px is the radius Apple's own buttons use, which reads as
     a capsule at any height. The button used to be a tilted keycap standing on
     a rim; the depth was the loudest thing on the page and it belonged to a
     different site. */
  border: 0;
  border-radius: 980px;
  text-decoration: none;
  position: relative;
  z-index: 1;
  cursor: pointer;
  transition: background 0.2s ease;
}
.dl-btn svg { width: 17px; height: 17px; flex: none; }
.dl-btn:hover {
  color: var(--green-on);
  background: var(--green-hover);
}
.dl-meta {
  font-size: 0.9375rem;
  color: var(--text-secondary);
  letter-spacing: 0.01em;
}
.dl-legal {
  margin-top: 6px;
  font-size: 0.875rem;
  color: var(--text-secondary);
  letter-spacing: 0.01em;
}
.dl-legal a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.dl-legal a:hover { color: var(--text-secondary); }

/* ── Download block — flat, no panel / glow / radius ── */
.dl-panel {
  margin-bottom: 44px;
  text-align: center;
}
.dl-icon {
  display: block;
  width: clamp(72px, 12vw, 96px);
  height: clamp(72px, 12vw, 96px);
  margin: 0 auto 24px;
}
.dl-panel h2 {
  font-family: var(--font-display);
  font-size: clamp(2rem, 5.4vw, 3.6rem);
  font-weight: 900;
  line-height: 1.0;
  letter-spacing: -0.03em;
  color: var(--text-primary);
}
.dl-panel .dl-wrap { margin-top: 30px; margin-bottom: 0; }
.dl-panel .dl-meta { color: var(--text-secondary); }

.dl-divider {
  display: flex;
  align-items: center;
  gap: 16px;
  margin: 36px 0 32px;
  color: var(--text-secondary);
  font-family: Georgia, "Times New Roman", serif;
  font-style: italic;
  font-size: 0.9375rem;
}
.dl-divider::before,
.dl-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--separator);
}

/* ============================================================
   ⑤ SIGNUP + TRUST
   ============================================================ */
.signup-section {
  max-width: var(--page-w);
  margin: 0 auto;
  padding: 68px var(--gutter);
  display: flex;
  align-items: center;
  justify-content: center;
}
.signup-inner {
  position: relative;
  width: 100%;
  max-width: 720px;
  text-align: center;
}

.signup-cols {
  display: flex;
  align-items: center;
  margin-top: 0;
  gap: 0;
}
.signup-col-left {
  flex: 1;
  min-width: 0;
  padding-right: 48px;
}
.signup-col-right {
  flex: 0 0 auto;
  padding-left: 48px;
  border-left: 1px solid var(--separator);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 18px;
}
.reach-heading {
  font-family: Georgia, "Times New Roman", serif;
  font-style: italic;
  font-size: 1.0625rem;
  color: var(--text-body);
}
.signup-col-right .reach-links {
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
}
.signup-inner h2 {
  font-size: clamp(2rem, 3.6vw, 2.75rem);
  font-weight: 800;
  letter-spacing: -0.035em;
  line-height: 1.1;
}
.signup-note {
  margin-top: 44px;
  font-size: 0.8125rem;
  letter-spacing: 0.02em;
  color: var(--text-tertiary);
}

/* Languages — ambient gravity text fills the stack behind copy + newsletter */
.lang-stack {
  position: relative;
}
.lang-stack.reveal.is-visible .lang-field {
  opacity: 1;
  transition: opacity 1.1s cubic-bezier(0.16, 1, 0.3, 1) 0.15s;
}
.lang-field {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, rgba(0, 0, 0, 0.5) 10%, #000 22%, #000 100%);
  mask-image: linear-gradient(to bottom, transparent 0%, rgba(0, 0, 0, 0.5) 10%, #000 22%, #000 100%);
}
.info--languages {
  position: relative;
  overflow: visible;
  z-index: 2;
}
/* Double-class selector so this reliably wins over the plain .info padding
   shorthand below regardless of source order (same specificity, and .info's
   `padding: 88px var(--gutter)` shorthand would otherwise silently reset
   padding-bottom back to 88px). The falling-text field now continues behind
   .lang-cta below, so this only needs a normal gap before that panel. */
.info.info--languages {
  padding-bottom: 8px;
}
.lang-copy {
  position: relative;
  z-index: 2;
  max-width: 560px;
}
/* Download panel, replayed over the tail end of the same falling-text
   field instead of a new section — the last thing anyone scrolling
   through the whole page sees before the footer. */
.lang-cta {
  position: relative;
  z-index: 2;
  max-width: var(--page-w);
  margin: 0 auto;
  padding: 0 var(--gutter) 96px;
}
.lang-field__stage {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  pointer-events: auto;
  cursor: default;
}
.lang-tile {
  position: absolute;
  top: 0;
  left: 0;
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  will-change: transform;
  color: rgba(245, 245, 247, 0.14);
}
.feedback-cols {
  display: flex;
  align-items: flex-start;
  gap: 0;
  text-align: left;
}
.feedback-col-main {
  flex: 1;
  min-width: 0;
  padding-right: 48px;
}
.feedback-col-social {
  flex: 0 0 auto;
  padding-left: 48px;
  border-left: 1px solid var(--separator);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 18px;
}
.feedback-col-social .reach-links {
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
}
.feedback-col-social .reach-pill {
  justify-content: center;
}
.feedback-form {
  text-align: left;
}
.feedback-form__body {
  display: grid;
  grid-template-rows: 1fr;
  opacity: 1;
  margin-bottom: 0;
  transition:
    grid-template-rows 0.45s cubic-bezier(0.4, 0, 0.2, 1),
    opacity 0.4s ease,
    margin 0.4s ease;
}
.feedback-form__inner {
  overflow: visible;
  min-height: 0;
}
.feedback-form.is-sent .feedback-form__inner {
  overflow: hidden;
}
.feedback-fields-row {
  display: flex;
  align-items: stretch;
  gap: 18px;
  overflow: visible;
}
.feedback-fields {
  flex: 1;
  min-width: 0;
}
.feedback-line {
  display: block;
  width: 100%;
  max-width: 100%;
}
/* "Send" is a bare text-and-arrow action, not a slab or pill. It rides at
   the bottom of the field row, level with the email line, and the arrow
   nudges to the right on hover. */
.feedback-send-wrap {
  flex: none;
  align-self: flex-end;
  display: flex;
  align-items: flex-end;
  margin: 0 0 4px;
  padding: 0;
}
.feedback-send-wrap::after { display: none; }
.feedback-send-btn {
  position: static;
  inset: auto;
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  gap: 12px;
  width: auto;
  height: auto;
  min-height: 0;
  padding: 6px 2px;
  background: none;
  border: none;
  border-radius: 0;
  color: var(--green);
  font-family: Georgia, "Times New Roman", serif;
  font-weight: 700;
  font-size: clamp(1.125rem, 2.2vw, 1.375rem);
  letter-spacing: -0.01em;
  line-height: 1;
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
  transform: none;
  transition: gap 0.25s cubic-bezier(0.4, 0, 0.2, 1), color 0.18s ease;
}
.feedback-send-btn svg {
  width: 30px;
  height: 18px;
  flex: none;
}
/* No hover treatment — the resting look is the only look. The rule stays so
   it overrides .dl-btn:hover, which would otherwise repaint the pill. */
.feedback-send-btn:hover {
  background: none;
  color: var(--green);
  transform: none;
  gap: 12px;
}
.feedback-send-btn:active {
  background: none;
  transform: none;
  gap: 13px;
}
.feedback-send-btn:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 4px;
  border-radius: 4px;
}
.sign-field--grow {
  flex: 1;
  min-width: 0;
  display: block;
}
.feedback-form .sign-field {
  display: block;
  width: 100%;
}
.feedback-input {
  background: transparent;
  border: none;
  outline: none;
  resize: vertical;
  min-height: 0;
  max-height: 8rem;
  display: block;
  width: 100%;
  box-sizing: border-box;
  font-family: Georgia, "Times New Roman", serif;
  font-size: clamp(1.125rem, 2.6vw, 1.625rem);
  letter-spacing: 0.005em;
  color: var(--text-primary);
  text-align: left;
  padding: 0 0 3px;
  margin: 0;
  caret-color: var(--green);
  line-height: 1.25;
  field-sizing: content;
}
.feedback-input::placeholder {
  color: var(--text-tertiary);
  font-style: italic;
}
.feedback-input:focus {
  outline: none;
}
.feedback-form.is-sent .feedback-form__body {
  grid-template-rows: 0fr;
  opacity: 0;
  margin-bottom: 0;
}
.feedback-form__thanks {
  opacity: 0;
  transform: translateY(8px);
  max-height: 0;
  overflow: hidden;
  margin: 0;
  transition:
    opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.18s,
    transform 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.18s,
    max-height 0.4s ease 0.18s,
    margin 0.4s ease 0.18s;
}
.feedback-form__thanks:not(:empty):not(.ok) {
  opacity: 1;
  transform: translateY(0);
  max-height: 3rem;
  transition: none;
}
.feedback-form.is-sent .feedback-form__thanks.ok {
  opacity: 1;
  transform: translateY(0);
  max-height: 4rem;
  margin-top: 0;
  text-align: center;
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 3.2vw, 2.25rem);
  font-weight: 800;
  font-style: normal;
  letter-spacing: -0.03em;
  line-height: 1.08;
}
.feedback-cols:has(.feedback-form.is-sent) {
  justify-content: center;
  align-items: center;
  gap: 48px;
}
.feedback-cols:has(.feedback-form.is-sent) .feedback-col-main {
  flex: 0 0 auto;
  width: auto;
  padding-right: 0;
}
.feedback-cols:has(.feedback-form.is-sent) .feedback-col-social {
  flex: 0 0 auto;
}
.feedback-email-line { margin-top: 26px; }
.feedback-fields-row .feedback-email-line { margin-top: 22px; }
.sign-prompt--sm { font-size: 0.9375rem; margin-bottom: 14px; }
.feedback-consent { justify-content: flex-start; margin-top: 18px; }

/* ============================================================
   INFO SECTIONS — use cases / features / privacy / languages
   Plain static blocks, no scroll animation beyond a gentle fade.
   ============================================================ */
.info {
  max-width: var(--page-w);
  margin: 0 auto;
  padding: 88px var(--gutter);
  border-top: 1px solid var(--separator-light);
}
.info .topic-label { display: block; margin-bottom: 14px; }
.info h2 {
  font-family: var(--font-display);
  font-size: clamp(2.25rem, 5.5vw, 3.75rem);
  font-weight: 900;
  letter-spacing: -0.03em;
  line-height: 1.02;
  margin-bottom: 14px;
}
.privacy-info {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(24px, 6vw, 72px);
}
.privacy-content { flex: 1 1 auto; min-width: 0; }
.privacy-badge {
  flex: 0 0 auto;
  width: clamp(140px, 16vw, 240px);
  height: clamp(140px, 16vw, 240px);
  will-change: transform;
}
@media (max-width: 780px) {
  .privacy-info { flex-direction: column; align-items: flex-start; }
  .privacy-badge { align-self: center; width: 120px; height: 120px; margin-top: 8px; }
}
.info-lead {
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--text-secondary);
  max-width: 560px;
}
.info-foot { margin-top: 28px; font-size: 0.9375rem; }
.info-foot a { color: var(--green); }
.info-foot a:hover { color: var(--green-hover); }

/* Privacy log — read like a verified audit trail, not a marketing card grid:
   plain rows on hairline rules, a status dot standing in for "checked true",
   and the claim itself set in mono like a system readout. */
.privacy-log {
  list-style: none;
  margin: 40px 0 0;
  padding: 0;
  max-width: 640px;
  border-top: 1px solid var(--separator-light);
}
.privacy-log li {
  display: flex;
  align-items: baseline;
  gap: 16px;
  padding: 22px 0;
  border-bottom: 1px solid var(--separator-light);
}
.privacy-log__dot {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--green);
  transform: translateY(-0.15em);
  box-shadow: 0 0 8px rgba(159, 208, 56, 0.55);
}
.privacy-log__body {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
}
.privacy-log__fact {
  font-family: var(--font-mono);
  font-size: 1.0625rem;
  letter-spacing: -0.01em;
  color: var(--text-primary);
}
.privacy-log__detail {
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--text-secondary);
}
@media (max-width: 700px) {
  .privacy-log li { gap: 12px; padding: 18px 0; }
  .privacy-log__fact { font-size: 0.9375rem; }
}

/* Languages */
.lang-count span { color: var(--green); }

/* ── Feedback bar (below download) ─────────────────────────────────────── */
.signup-row {
  display: flex;
  gap: 10px;
  max-width: 440px;
  margin: 0 auto;
}
.field {
  flex: 1;
  height: 46px;
  padding: 0 14px;
  border-radius: 10px;
  background: rgba(255,255,255,0.06);
  border: 1px solid var(--separator);
  color: var(--text-primary);
  font-family: var(--font);
  font-size: 0.9375rem;
  transition: border-color 0.2s;
}
.field::placeholder { color: var(--text-tertiary); }
.field:focus { outline: none; border-color: var(--green); }

.signup-btn {
  height: 46px;
  padding: 0 22px;
  border-radius: 980px;
  border: none;
  background: #ffffff;
  color: #000000;
  font-family: var(--font);
  font-size: 0.9375rem;
  font-weight: 650;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.2s, transform 0.15s;
}
.signup-btn:hover {
  background: rgba(255, 255, 255, 0.88);
}

.consent {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 8px;
  margin-top: 14px;
  font-size: 0.8125rem;
  color: var(--text-secondary);
  line-height: 1.45;
}
.consent input { margin-top: 2px; accent-color: var(--green); }

.form-msg {
  margin-top: 10px;
  font-size: 0.8125rem;
  min-height: 1.2em;
  color: var(--text-secondary);
  text-align: center;
}
.form-msg.ok { color: var(--green); }
.form-msg.err { color: #ff6961; }

/* ── Manuscript "sign here" line ─────────────────────────────────────────
   No box, no fill — text is written on a hand-painted brush line, like
   signing a page. The stroke paints in (left→right) on focus / once filled.
   Reused by the optional email line under the feedback form. */
.sign-prompt {
  font-family: Georgia, "Times New Roman", serif;
  font-style: italic;
  font-size: 1.0625rem;
  color: var(--text-body);
  margin-bottom: 22px;
}
.sign-line {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 16px;
}
.sign-field {
  position: relative;
  display: inline-block;
}
/* faint resting guide line so the user knows where to write */
.sign-field::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: var(--film-strong);
}
/* the oil-paint swipe, clipped away until focus/fill */
.sign-field::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -3px;
  height: 0.45em;
  pointer-events: none;
  background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 24' preserveAspectRatio='none'><path d='M4,13 C30,8 60,15 95,11 C130,7 165,16 205,10 C222,8 232,11 236,13 C232,19 210,14 182,18 C140,23 100,13 64,18 C36,21 16,15 4,16 Z' fill='%239fd038'/><path d='M16,15 C62,11 120,18 172,13 C202,11 222,15 230,14' fill='none' stroke='%237ab02c' stroke-width='1.6' stroke-linecap='round' opacity='0.45'/></svg>") left center / 100% 100% no-repeat;
  clip-path: inset(0 100% 0 0);
  transition: clip-path 0.55s cubic-bezier(0.4, 0, 0.2, 1);
}
.sign-field:has(.sign-input:focus)::after,
.sign-field:has(.sign-input:not(:placeholder-shown))::after,
.sign-field:has(.feedback-input:focus)::after,
.sign-field:has(.feedback-input:not(:placeholder-shown))::after {
  clip-path: inset(0 0 0 0);
}
.sign-input {
  background: transparent;
  border: none;
  outline: none;
  font-family: Georgia, "Times New Roman", serif;
  font-size: clamp(1.125rem, 2.6vw, 1.625rem);
  letter-spacing: 0.005em;
  color: var(--text-primary);
  text-align: left;
  display: block;
  width: 100%;
  box-sizing: border-box;
  padding: 0 0 3px;
  margin: 0;
  caret-color: var(--green);
  line-height: 1.25;
}
.sign-input::placeholder { color: var(--text-tertiary); font-style: italic; }

/* ── Alternative reach-out — Discord / Reddit, no email needed ────────────
   Quiet secondary option below the signature line. Same manuscript voice:
   an italic serif divider, then two understated pills that warm to green. */
.reach-alt { margin-top: 38px; }
.reach-divider {
  display: flex;
  align-items: center;
  gap: 14px;
  max-width: 360px;
  margin: 0 auto 18px;
  color: var(--text-secondary);
}
.reach-divider::before,
.reach-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--separator);
}
.reach-divider span {
  font-family: Georgia, "Times New Roman", serif;
  font-style: italic;
  font-size: 1.0625rem;
  white-space: nowrap;
}
.reach-links {
  display: flex;
  justify-content: center;
  gap: 14px;
  flex-wrap: wrap;
}
.reach-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 22px;
  border-radius: 980px;
  background: transparent;
  font-size: 0.875rem;
  font-weight: 550;
  letter-spacing: -0.01em;
  text-decoration: none;
  transition: transform 0.1s ease, box-shadow 0.1s ease,
              background 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}
.reach-pill svg { width: 18px; height: 18px; flex: none; }

/* Outline at rest, solid brand fill on hover — the button holds still, only
   the fill commits. White text/icon on hover for contrast against the fill. */
.reach-pill--discord {
  border: 1px solid #5865f2;
  color: #5865f2;
}
.reach-pill--discord:hover {
  color: #fff;
  background: #5865f2;
  border-color: #5865f2;
}

/* ============================================================
   FOOTER
   ============================================================ */
footer {
  border-top: 1px solid var(--separator-light);
  padding: 40px 0 32px;
}
.footer-inner {
  max-width: var(--page-w);
  margin: 0 auto;
  padding: 0 var(--gutter);
}
/* Grouped columns rather than one flat row: the search entry pages have no
   home in the nav, so the footer is where they get found. Columns leave room
   to keep adding without the row wrapping into soup. */
.footer-cols {
  display: grid;
  /* Four link columns, then a wider fifth for the sentence people actually
     hesitate over. Prose needs more room than a stack of one-word links. */
  grid-template-columns: repeat(4, minmax(0, 1fr)) minmax(0, 1.5fr);
  gap: 28px 24px;
  margin-bottom: 32px;
}
.footer-col-title {
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--text-primary);
  margin-bottom: 10px;
}
.footer-col ul {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.footer-col a {
  font-size: 0.8125rem;
  color: var(--text-secondary);
  text-decoration: none;
  transition: color 0.2s;
}
.footer-col a:hover { color: var(--text-primary); }
.footer-bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
  padding-top: 20px;
  border-top: 1px solid var(--separator-light);
}
@media (max-width: 720px) {
  .footer-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px 16px; }
  .footer-privacy { grid-column: 1 / -1; }
}
.footer-logo {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--text-primary);
}
.footer-copy {
  font-size: 0.75rem;
  color: var(--text-tertiary);
}
.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 18px;
  list-style: none;
}
.footer-links a {
  font-size: 0.8125rem;
  color: var(--text-secondary);
  text-decoration: none;
  transition: color 0.2s;
}
.footer-links a:hover { color: var(--text-primary); }
/* Footer call-to-action badges. Drawn here rather than using the SVGs Product
   Hunt and GitHub hand out, which are dark-on-white and burn a hole in the
   dark page. Both follow the theme; only the brand mark keeps its colour. */
.footer-badges {
  flex: none;
  display: flex;
  gap: 10px;
}
.footer-badge {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 7px 14px 7px 11px;
  border: 1px solid var(--separator);
  border-radius: 980px;
  text-decoration: none;
  transition: border-color 0.2s, background 0.2s;
}
/* Same move as the Discord button: the badge fills with its own brand colour
   and the label flips to read on it, while the key presses down. The Product
   Hunt mark inverts (white disc, orange letter) so it stays visible on the
   orange fill. */
.footer-badge.is-ph:hover {
  border-color: #ff6154;
  background: #ff6154;
}
.footer-badge.is-ph:hover .footer-badge-kicker,
.footer-badge.is-ph:hover .footer-badge-name { color: #fff; }
.footer-badge.is-ph:hover .footer-badge-mark path:first-child { fill: #fff; }
.footer-badge.is-ph:hover .footer-badge-mark path:last-child { fill: #ff6154; }

.footer-badge.is-dc:hover {
  border-color: #5865f2;
  background: #5865f2;
}
.footer-badge.is-dc:hover .footer-badge-kicker,
.footer-badge.is-dc:hover .footer-badge-name { color: #fff; }
.footer-badge.is-dc:hover .footer-badge-mark { fill: #fff; }
.footer-badge.is-dc .footer-badge-mark { fill: #5865f2; }

.footer-badge.is-gh:hover {
  border-color: var(--text-primary);
  background: var(--text-primary);
}
.footer-badge.is-gh:hover .footer-badge-kicker,
.footer-badge.is-gh:hover .footer-badge-name { color: var(--bg); }
.footer-badge.is-gh:hover .footer-badge-mark { fill: var(--bg); }
.footer-badge-mark {
  transition: fill 0.2s;
  width: 22px;
  height: 22px;
  flex: none;
}
.footer-badge.is-gh .footer-badge-mark { fill: var(--text-primary); }
.footer-badge-text {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
}
.footer-badge-kicker {
  transition: color 0.2s;
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.footer-badge-name {
  transition: color 0.2s;
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--text-primary);
}

/* ============================================================
   ANIMATIONS
   ============================================================ */
.hero-reveal {
  opacity: 0;
  transform: translateY(28px);
  animation: fadeUp 0.7s cubic-bezier(0.16, 1, 0.3, 1) forwards;
  animation-delay: calc(var(--d, 0) * 0.13s + 0.1s);
}
@keyframes fadeUp {
  to { opacity: 1; transform: translateY(0); }
}

.reveal {
  opacity: 0;
  transform: translate3d(0, 32px, 0);
  transition: opacity 0.65s cubic-bezier(0.16, 1, 0.3, 1),
              transform 0.65s cubic-bezier(0.16, 1, 0.3, 1);
  will-change: opacity, transform;
}
.reveal.is-visible {
  opacity: 1;
  transform: translate3d(0, 0, 0);
  will-change: auto;
}

/* ============================================================
   ABOUT — story (for about.html)
   ============================================================ */
.story-hero {
  width: 100%;
  max-width: var(--page-w);
  margin: 0 auto;
  padding: 72px var(--gutter) 56px;
}
.story-hero h1 {
  font-family: var(--font-display);
  font-size: clamp(2.5rem, 5.5vw, 4rem);
  font-weight: 900;
  line-height: 1.06;
  letter-spacing: -0.03em;
  margin: 14px 0 14px;
}
.story-hero-meta {
  font-size: 0.8125rem;
  color: var(--text-secondary);
  margin-bottom: 16px;
}
.story-hero-lead {
  font-size: 1.125rem;
  line-height: 1.55;
  color: var(--text-secondary);
  max-width: none;
}

.story-body {
  width: 100%;
  max-width: var(--page-w);
  margin: 0 auto;
  padding: 0 var(--gutter) 100px;
}

.story-section {
  padding: 56px 0;
  border-top: 1px solid var(--separator-light);
}
.story-section .topic-label {
  display: block;
  margin-bottom: 14px;
}
.story-section h2 {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 3.5vw, 2.25rem);
  font-weight: 900;
  letter-spacing: -0.03em;
  line-height: 1.08;
  margin-bottom: 22px;
}
.story-section p {
  font-size: 1.0625rem;
  line-height: 1.72;
  color: var(--text-secondary);
  margin-bottom: 16px;
}
.story-section p:last-child { margin-bottom: 0; }
.story-section p strong { color: var(--text-primary); }
.story-section p em { color: var(--green); font-style: italic; }
.story-section .btn { margin-top: 8px; }

.story-highlight { color: var(--text-primary) !important; }

/* ============================================================
   ARTICLE PAGES (search-entry guides, .article-page on <body>) —
   full-contrast body copy, same wide page-width layout as about.html
   (no narrower reading column — that read as too cramped next to the
   rest of the site's spread-out storytelling feel).
   --text-secondary is a muted UI-chrome tone (nav, labels, captions);
   --text-body is the design system's actual reading color and was
   never wired up here. */
.article-page .story-section p {
  font-size: 1.125rem;
  line-height: 1.7;
  color: var(--text-body);
}

/* The lead paragraph is the most important sentence on the page after the
   headline — it was rendering dimmer than the body copy below it, which
   reads backwards. It also gets one weight up from body text so the eye
   has a clear order: h1 (black) > lead (medium, brightest) > h2 (bold) >
   body (regular). */
.article-page .story-hero-lead {
  font-size: 1.1875rem;
  font-weight: 500;
  color: var(--text-primary);
}
.article-page .story-section h2 { font-weight: 700; }

/* Comparison / spec tables inside an article. Ruled rows in the same
   quiet separator tone the rest of the page already uses — no card,
   no box, just data lined up. */
.article-page .story-table-wrap { margin: 20px 0 8px; overflow-x: auto; }
.article-page .story-table { width: 100%; border-collapse: collapse; font-size: 0.9375rem; }
.article-page .story-table th,
.article-page .story-table td {
  padding: 10px 16px 10px 0;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--separator-light);
}
.article-page .story-table th {
  font-weight: 700;
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-secondary);
  border-bottom: 1px solid var(--separator);
}
.article-page .story-table td { color: var(--text-body); }
.article-page .story-table td strong { color: var(--text-primary); }
.article-page .story-table tr:last-child td { border-bottom: none; }

/* Quiet way back to the rest of the site from a page someone landed on cold
   from search. Plain text, not a second button competing with Download. */
.story-backlink {
  margin-top: 18px;
  font-size: 0.875rem;
  color: var(--text-secondary);
}
.story-backlink a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.story-backlink a:hover { color: var(--text-primary); }

/* Nominative-use notice on pages that name a third-party product in the title.
   Quieter than the backlink above it: this is legal hygiene, not reading. */
.story-trademark {
  margin-top: 28px;
  padding-top: 14px;
  border-top: 1px solid var(--separator-light);
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--text-secondary);
}

/* Commands and file paths inside a landing page. The legal pages have their
   own inline `code` rule scoped to .story-body.legal; these apply to the
   search pages, which also need a block form that scrolls rather than
   forcing the page sideways on a phone. */
.story-section code {
  padding: 0.1em 0.35em;
  border-radius: 4px;
  background: rgba(127, 127, 127, 0.16);
  font-family: var(--font-mono);
  font-size: 0.9em;
}
.story-section pre.code-block {
  margin: 16px 0;
  padding: 14px 16px;
  border-radius: 10px;
  background: rgba(127, 127, 127, 0.12);
  overflow-x: auto;
}
.story-section pre.code-block code {
  padding: 0;
  background: none;
  font-size: 0.85rem;
  line-height: 1.55;
  white-space: pre;
}

.story-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 32px;
  margin: 24px 0 0;
}
.story-split h3 {
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  margin-bottom: 8px;
}
.story-split p { font-size: 0.9375rem !important; }

.story-blockquote {
  margin: 28px 0 0;
  padding: 28px 0 0;
  border-top: 1px solid var(--separator-light);
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 2.5vw, 1.625rem);
  font-weight: 900;
  line-height: 1.35;
  letter-spacing: -0.03em;
  color: var(--text-primary);
}

.nav-links a.is-active { color: var(--text-primary); }

/* Generic button primitives used across pages */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font);
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  text-decoration: none;
  border: none;
  cursor: pointer;
  border-radius: 980px;
  padding: 10px 22px;
  transition: opacity 0.18s ease, background 0.18s ease;
}

/* Solid white pill — for page-level CTAs on dark backgrounds */
.btn-primary {
  background: #ffffff;
  color: #000000;
}
.btn-primary:hover { background: rgba(255,255,255,0.88); color: #000; }

/* Ghost outline — secondary / inline CTAs */
.btn-ghost {
  background: transparent;
  color: rgba(255,255,255,0.82);
  border: 1px solid rgba(255,255,255,0.28);
}
.btn-ghost:hover {
  border-color: rgba(255,255,255,0.72);
  color: #fff;
  background: rgba(255,255,255,0.06);
}

/* .brush / .brush--bold marked a keyword for emphasis (used to draw a swipe
   behind it, then just coloured it). Neither earned its keep: the word reads
   fine as part of the sentence. The classes stay in the markup harmlessly
   unstyled rather than editing them out of 28 files for a no-op. */

/* ============================================================
   STORY PAGE (about.html, .story-page on <body>) — cut the
   decoration rather than reskin it. First pass added a pill badge
   and a box drawn around a headline word; screenshotted, both read
   as generic component furniture (a status chip, a broken form
   field) rather than as Leafy's own voice. Second pass removes them
   instead of dressing them up: plain mono label, plain coloured
   emphasis, no hairline rule between sections. Brand colour
   untouched throughout.
   ============================================================ */

/* No scroll-triggered fade-ups: the story reads as settled fact. */
.story-page .hero-reveal,
.story-page .reveal {
  opacity: 1;
  transform: none;
  animation: none;
  transition: none;
}

/* No hairline rule between sections — the site's other pattern
   (border-top on every section) is the broadsheet-newspaper default;
   whitespace alone separates the beats of the story. */
.story-page .story-section { border-top: none; padding-top: 8px; }
.story-page .story-section + .story-section { padding-top: 64px; }

/* about.html was still on --text-secondary for body copy, the muted UI-chrome
   tone meant for nav and captions. The guide pages moved to --text-body (see
   .article-page above) and this one never followed, so the story read grey
   next to them. The lead gets the same lift for the same reason it does
   there: a lead dimmer than the paragraphs under it reads backwards.
   Scoped to .story-page, so privacy/terms (.story-body.legal, which sets its
   own colour further down) are untouched. */
.story-page .story-section p { color: var(--text-body); }
.story-page .story-hero-lead {
  font-weight: 500;
  color: var(--text-primary);
}

/* ============================================================
   LEGAL (privacy.html, terms.html) — shares story page shell
   ============================================================ */
.story-hero .back {
  display: inline-block;
  margin-bottom: 28px;
  font-size: 0.9375rem;
  color: var(--green);
}
.story-hero .back:hover { text-decoration: underline; }
.story-hero .updated {
  font-size: 0.8125rem;
  color: var(--text-tertiary);
  margin-top: 8px;
}
.story-body.legal h2 {
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--text-primary);
  margin: 32px 0 8px;
}
.story-body.legal p,
.story-body.legal li {
  font-size: 1rem;
  line-height: 1.6;
  color: var(--text-secondary);
}
.story-body.legal ul {
  padding-left: 1.25em;
  margin: 8px 0 16px;
}
.story-body.legal li { margin-bottom: 6px; }
.story-body.legal strong { color: var(--text-primary); }
.story-body.legal code {
  padding: 0.1em 0.35em;
  border-radius: 4px;
  background: rgba(255,255,255,0.06);
  font-family: var(--font-mono);
  font-size: 0.9em;
}

/* ============================================================
   UPDATE LOG (update.html) — release log
   ============================================================
   Two columns, like every changelog people already know how to read: the
   version and its date on the left, staying put while the notes scroll past;
   the headline and the notes on the right. No timeline rail and no dots: the
   whitespace between releases is the separator, with one hairline so the eye
   can find the seam. */

.update-hero { text-align: center; padding-bottom: 24px; }
.update-hero .story-hero-lead { max-width: 72ch; margin-inline: auto; }
.update-hero .updated { margin-top: 12px; }
.update-page { max-width: 1040px; }
.update-list { margin-top: 8px; }

.update-entry {
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr);
  gap: 0 clamp(24px, 4vw, 56px);
  padding: 44px 0;
  border-top: 1px solid var(--separator-light);
}
.update-list .update-entry:first-child { border-top: none; padding-top: 24px; }

/* The version and its date ride along while the notes scroll: a release's
   notes can run a screen and a half, and the reader should never have to
   scroll back up to see which one they are in. */
.update-meta {
  position: sticky;
  top: calc(var(--nav-h, 64px) + 24px);
  align-self: start;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 10px;
  min-width: 0;
}
/* Apple's release notes do it with type alone: the version set as a small
   bold heading, the date in grey under it, no badge and no colour. */
.update-version {
  font-family: var(--font-display);
  font-size: 1.125rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.3;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}
.update-version::before { content: "Version "; }
.update-entry.is-dev .update-version { color: var(--text-tertiary); }
.update-date {
  flex-basis: 100%;
  font-size: 0.9375rem;
  color: var(--text-tertiary);
}
/* "Current" is a quiet fact beside the version, not a label. */
.update-current-tag {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--text-tertiary);
}
.update-current-tag::before { content: "·"; margin-right: 6px; }

.update-body { min-width: 0; }
.update-title {
  font-family: var(--font-display);
  font-size: clamp(1.375rem, 2.2vw, 1.75rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.25;
  color: var(--text-primary);
  margin: 0 0 16px;
  text-wrap: balance;
}
.update-body p {
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--text-body);
  margin: 0;
  max-width: 68ch;
}
.update-body ul {
  margin: 0;
  padding: 0;
  list-style: none;
  max-width: 68ch;
}
.update-body li {
  position: relative;
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--text-body);
  padding-left: 18px;
  margin-bottom: 12px;
}
.update-body li:last-child { margin-bottom: 0; }
.update-body li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.68em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--text-tertiary);
}
.update-body strong { color: var(--text-primary); font-weight: 600; }
.update-body code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  padding: 1px 5px;
  border-radius: 5px;
  background: var(--film-mid, rgba(128,128,128,0.14));
}
.update-entry.is-dev .update-body p { color: var(--text-tertiary); }

/* Three releases open the page; the rest sit behind one line of text, in
   the voice of the small print, until asked for. */
.update-fold.is-folded > :not(.update-more) { display: none; }
.update-more { display: none; }
.update-fold.is-folded .update-more {
  display: block;
  padding: 28px 0 8px;
  border-top: 1px solid var(--separator-light);
}
.update-more-btn {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 1rem;
  font-weight: 600;
  color: var(--text-secondary);
  cursor: pointer;
  transition: color 0.18s ease;
}
.update-more-btn::after { content: " ↓"; }
.update-more-btn:hover { color: var(--text-primary); }
.update-more-btn:focus-visible { outline: 2px solid var(--text-secondary); outline-offset: 4px; border-radius: 4px; }

.update-footnote {
  margin-top: 40px;
  padding-top: 28px;
  border-top: 1px solid var(--separator-light);
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--text-tertiary);
}

/* The seam between released builds and the internal ones above them: the
   same two columns, with the note in the notes column. */
.update-divider {
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr);
  gap: 0 clamp(24px, 4vw, 56px);
  margin: 0;
  padding: 44px 0 8px;
  border-top: 1px solid var(--separator-light);
}
.update-divider > * { grid-column: 2; }
.update-divider h2 {
  font-family: var(--font-display);
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--text-secondary);
}
.update-divider p {
  margin-top: 6px;
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--text-tertiary);
  max-width: 520px;
}
/* Small print under the release log on translated pages: the notes are
   translated from English and may drift from it. Empty on English (i18n fills
   it), and :empty removes even its margin there. */
.update-disclaimer {
  margin-top: 40px;
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--text-tertiary);
}
.update-disclaimer:empty { display: none; }

@media (max-width: 700px) {
  .update-entry,
  .update-divider { grid-template-columns: minmax(0, 1fr); gap: 14px 0; padding: 32px 0; }
  .update-divider > * { grid-column: 1; }
  .update-meta { position: static; }
  .update-date { flex-basis: auto; }
}

/* ============================================================
   HELP CENTER (help.html) — sidebar + switchable panels
   ============================================================ */
/* Two-pane docs shell: the sidebar and the content scroll independently,
   each filling the viewport below the fixed-height nav. The page itself
   never scrolls — only the panes do. */
.help-page { overflow: hidden; }
.help-body {
  width: 100%;
  max-width: none;
  margin: 0;
  padding: 0;
}
/* Mobile-only controls for the help topic sheet — hidden on desktop */
.help-toc-toggle,
.help-sidebar-close { display: none; }
.help-layout {
  display: grid;
  grid-template-columns: 264px minmax(0, 1fr);
  height: calc(100vh - var(--nav-h));
}
.help-sidebar {
  height: 100%;
  overflow-y: auto;
  padding: 40px 28px 48px var(--gutter);
  background: var(--bg-secondary);
  border-right: 1px solid var(--separator);
}
.help-content {
  height: 100%;
  overflow-y: auto;
  padding: 44px clamp(28px, 5vw, 64px) 96px;
}
.help-content .help-panel { max-width: 760px; }
/* The footer rides inside the scrolling content pane, so it drops the page
   width and gutter it uses elsewhere and lines up with the article instead.
   Four columns is too many at this width, so it wears the same two-column
   arrangement the real footer falls back to on a phone. */
.help-footer {
  margin-top: 64px;
  padding: 32px 0 0;
}
/* The article is capped at 760px, but the footer is not article text: it fills
   the whole pane and starts at the same left edge, so it does not float in the
   middle of a wide column of nothing. */
.help-footer .footer-inner {
  max-width: none;
  margin: 0;
  padding: 0;
}
/* Wide enough for the site's own five-across footer, privacy note included. */
@media (max-width: 1180px) {
  .help-footer .footer-cols {
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  }
  .help-footer .footer-privacy {
    grid-column: 1 / -1;
    max-width: 52ch;
  }
}
/* Slim scrollbars inside each pane, matching the page scrollbar */
.help-sidebar::-webkit-scrollbar,
.help-content::-webkit-scrollbar { width: 10px; }
.help-sidebar::-webkit-scrollbar-thumb,
.help-content::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.14);
  border-radius: 999px;
  border: 2px solid #000;
}
.help-sidebar::-webkit-scrollbar-thumb:hover,
.help-content::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.24); }
/* Visually hidden, still read by screen readers / kept for document outline */
.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;
}
.help-sidebar-title {
  font-family: var(--font);
  font-size: 1.375rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  /* Drop the title so its baseline lines up with the article's h2 title,
     which sits below its green eyebrow in the content column. */
  margin: 38px 0 28px;
}
/* Each group is a slab: a hairline, a header, then its topics. The slabs are
   what the eye lands on first — six of them, one line each — so the long topic
   sentences underneath can afford to stay quiet. */
.help-nav-group {
  display: block;
  padding: 18px 0 20px;
  border-top: 1px solid var(--separator-light);
}
.help-nav-group:first-of-type { padding-top: 0; border-top: none; }
/* Section header — a filing label, deliberately quieter than the topics under
   it. At full contrast it outranked the links and the eye read the headers as
   the clickable rows. Dark text now means "click me", gray means "category". */
.help-nav-group-title {
  display: block;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  margin-bottom: 10px;
}
/* Topic link — a full-width row that fills in behind the text. Nothing at
   rest, no stem, no accent: the block is the whole affordance. */
.help-nav-link {
  position: relative;
  display: block;
  margin: 0 -10px;
  padding: 6px 10px;
  background: none;
  color: var(--text-primary);
  font-size: 0.9375rem;
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: -0.01em;
  border: none;
  border-radius: 7px;
  box-shadow: none;
  transition: background 0.14s ease, color 0.14s ease;
}
/* Two topics in one group had their hover blocks touching, which read as one
   striped slab rather than two rows. */
/* Read by the search panel, never shown: the rail stays one line per topic. */
.help-nav-hint { display: none; }
.help-nav-link + .help-nav-link { margin-top: 6px; }
.help-nav-link:hover {
  color: var(--text-primary);
  background: var(--film-weak);
  box-shadow: none;
  transform: none;
}
/* Active topic: the same block, held on. Same weight as every other label:
   bolding it widens the line and the list reflows as you move between topics. */
.help-nav-link.is-active,
.help-nav-link.is-active:hover {
  color: var(--text-primary);
  background: var(--film-mid);
  border: none;
  box-shadow: none;
  transform: none;
}

.help-panel { display: none; }
.help-panel.is-active { display: block; }
.help-panel > h1 {
  font-family: var(--font-display);
  font-size: clamp(1.375rem, 3vw, 1.75rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
  margin: 0 0 20px;
}
.help-panel h3 {
  font-size: 1rem;
  font-weight: 700;
  color: var(--text-primary);
  margin: 28px 0 8px;
}
.help-panel p { font-size: 1rem; line-height: 1.65; color: var(--text-secondary); margin-bottom: 12px; }
.help-panel p strong { color: var(--text-primary); }
/* The line under the title: the one thing to know before reading the rest.
   It sits unboxed, a size up from body copy, so it reads as part of the
   heading instead of as a warning stuck to the top of the page. */
.help-panel .help-lede {
  font-size: 1.125rem;
  line-height: 1.55;
  color: var(--text-body);
  max-width: 62ch;
  margin: -4px 0 26px;
}
.help-panel .help-lede strong { color: var(--text-primary); font-weight: 600; }

/* GUIDES DIRECTORY (guides.html) — wears the help page's shell and index
   classes; only what the help index has no counterpart for lives here. */
.help-hub-item .guides-item-meta {
  display: block;
  margin-top: 6px;
  font-size: 0.75rem;
  color: var(--text-tertiary);
}
.guides-page .help-hub-group { scroll-margin-top: 20px; }

/* The line to read before the rest. No tint, no box: it earns its place by
   sitting directly under the title at a size the body copy does not use. */
.help-callout {
  position: relative;
  background: none;
  border: none;
  border-radius: 0;
  padding: 0;
  margin: 20px 0 8px;
}
/* Sitting straight under the title it is the page's subtitle, so it hugs the
   heading and carries the extra size a lede gets. */
.help-panel > h1 + .help-callout {
  margin: 0 0 26px;
  padding: 0;
}
.help-panel > h1 + .help-callout p {
  font-size: 1.125rem;
  line-height: 1.55;
  max-width: 62ch;
}
.help-callout p { margin: 0; color: var(--text-body); font-size: 1rem; line-height: 1.6; }
.help-callout p strong { color: var(--text-primary); font-weight: 600; }


.help-steps { list-style: none; counter-reset: help-step; margin: 12px 0 20px; padding: 0; }
.help-steps li {
  position: relative;
  padding-left: 40px;
  margin-bottom: 16px;
  font-size: 1rem;
  line-height: 1.6;
  color: var(--text-secondary);
}
.help-steps li strong { color: var(--text-primary); }
/* The one step people skip, and skipping it leaves the whole setup dead with
   no error to explain why. Red carries the number chip too, so it reads as a
   stop sign in the list rather than a differently coloured sentence. */
.help-steps li.is-critical,
.help-steps li.is-critical strong { color: var(--cat-red); }
.help-steps li.is-critical::before {
  background: rgba(255, 107, 107, 0.18);
  color: var(--cat-red);
}
.help-step-img {
  display: block;
  max-width: min(100%, 420px);
  height: auto;
  margin: 12px 0 4px;
  border-radius: 10px;
  border: 1px solid var(--separator-light);
}
/* Full article width. These clips ARE the instructions: someone following along
   is reading the app's own menus off them, and at 480px those menus were too
   small to read. Portrait captures get their own cap below, or a 3:4 clip at
   full width becomes a metre of scrolling. */
.help-video {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
  margin: 12px 0 4px;
  border-radius: 10px;
  border: 1px solid var(--separator-light);
}
.help-video.is-portrait { max-width: 520px; }
/* A clip holds on its last frame, fades down to black, and comes back up from
   black at the top. The black is the wrapper showing through the clip, so it
   looks the same on a light page as on a dark one. See js/help.js. */
.help-video-wrap {
  position: relative;
  display: block;
  background: #000;
  border-radius: 10px;
}
.help-video-wrap > video { transition: opacity 0.4s ease; }
.help-video-wrap > video.is-dark { opacity: 0; }
.help-steps li::before {
  counter-increment: help-step;
  content: counter(help-step);
  position: absolute;
  left: 0;
  top: -1px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  /* Quiet numbered chip — a soft tinted disc, not a loud green dot. Keeps the
     eye on the instructions, not the counters. */
  background: var(--film-mid);
  border: none;
  color: var(--text-secondary);
  font-size: 0.75rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}

.help-dl-row {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin: 20px 0 8px;
}
.help-dl-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 18px;
  /* Use the bright lime *fill* token, not --green (which darkens to a muddy
     olive in light mode for legible text) — a button surface should stay bright
     in both themes, with dark ink on top. */
  background: var(--green-fill);
  color: var(--green-on);
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-decoration: none;
  border: none;
  border-radius: 980px;
  transition: background 0.2s ease;
}
.help-dl-btn:hover {
  color: var(--green-on);
  background: var(--green-hover);
}
.help-dl-btn svg { width: 17px; height: 17px; flex: none; }
.frow-import-btn { margin-top: 18px; }
.help-dl-ext {
  font-size: 0.75rem;
  font-weight: 600;
  opacity: 0.75;
}

/* A plain bulleted list. The old green bar down the left read like a quote or
   a callout, which these are not: they are just items. The dot is neutral so
   green keeps meaning "link" inside the text. */
.help-cols { list-style: none; margin: 12px 0 20px; padding: 0; }
.help-cols li {
  position: relative;
  padding: 0 0 0 18px;
  margin-bottom: 10px;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--text-secondary);
}
.help-cols li::before {
  content: "";
  position: absolute;
  left: 3px;
  top: 0.62em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--text-tertiary);
}
.help-cols li strong { color: var(--text-primary); }

.help-contact {
  margin-top: 28px;
  padding-top: 20px;
  border-top: 1px solid var(--separator-light);
  font-size: 0.9375rem !important;
}
/* The Discord button carries the same rim as the buttons on the home page, so
   the way to reach a human is a button, not a sentence you skim past. */
.help-contact p {
  font-size: 0.9375rem;
  margin: 0 0 14px;
}
.help-contact .reach-pill { font-size: 0.875rem; }

.help-security-notice {
  max-width: 560px;
  margin-top: 28px;
  padding-top: 20px;
  border-top: 1px solid var(--separator-light);
  font-size: 0.9375rem !important;
  line-height: 1.65;
  color: var(--text-secondary);
}
.help-security-notice em { color: var(--text-primary); font-style: normal; }
.help-security-notice a { white-space: nowrap; }

/* ── Search ──────────────────────────────────────────────────────────────
   Seven articles do not need a search engine, but they do need a way in that
   is not "read the rail". The index is built from the rail itself (label plus
   the hidden one-liner on each row), so it is already translated on a
   /<lang>/ page and there is no second copy of the topic list to keep in step.
   Both triggers are `hidden` in the markup and revealed by js/help.js: with no
   script there is no panel to open. */
.help-search-trigger {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  margin: 0 0 26px;
  padding: 8px 10px;
  background: none;
  border: 1px solid var(--separator-light);
  border-radius: 8px;
  color: var(--text-tertiary);
  font-family: inherit;
  font-size: 0.875rem;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.14s ease, color 0.14s ease;
}
.help-search-trigger:hover { border-color: var(--separator); color: var(--text-secondary); }
.help-search-trigger[hidden] { display: none; }
.help-search-glass { width: 15px; height: 15px; flex: none; opacity: 0.8; }
.help-search-trigger-label { flex: 1; min-width: 0; }
.help-search-key {
  flex: none;
  font-family: inherit;
  font-size: 0.75rem;
  letter-spacing: 0.02em;
  color: var(--text-tertiary);
  opacity: 0.75;
}
/* On a phone the rail is a sheet, so the index page carries its own way in. */
.help-search-trigger--hub { display: none; }

.help-search-scrim {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  padding: 12vh 20px 20px;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.16s ease, visibility 0.16s;
}
.help-search-scrim.is-open { opacity: 1; visibility: visible; }
.help-search-dialog {
  width: 100%;
  max-width: 540px;
  max-height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: 1px solid var(--separator);
  border-radius: 14px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
  overflow: hidden;
  transform: translateY(-6px);
  transition: transform 0.16s ease;
}
.help-search-scrim.is-open .help-search-dialog { transform: none; }
/* The field wears the app's own selection box: LeafyApp is the thing you point
   at a word on screen, and this is where you point at a word in the help. The
   corners only strike when the field has focus, so at rest it is a plain row. */
.help-search-box {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 10px;
  padding: 10px 12px;
}
.help-search-corner {
  position: absolute;
  width: 12px;
  height: 12px;
  border: 2px solid var(--green);
  opacity: 0;
  transform: scale(0.8);
  transition: opacity 0.18s ease, transform 0.18s ease;
  pointer-events: none;
}
.help-search-corner:nth-of-type(1) { top: 0; left: 0; border-right: 0; border-bottom: 0; border-radius: 3px 0 0 0; }
.help-search-corner:nth-of-type(2) { top: 0; right: 0; border-left: 0; border-bottom: 0; border-radius: 0 3px 0 0; }
.help-search-corner:nth-of-type(3) { bottom: 0; right: 0; border-left: 0; border-top: 0; border-radius: 0 0 3px 0; }
.help-search-corner:nth-of-type(4) { bottom: 0; left: 0; border-right: 0; border-top: 0; border-radius: 0 0 0 3px; }
.help-search-box:focus-within .help-search-corner { opacity: 1; transform: none; }
.help-search-box .help-search-glass { width: 17px; height: 17px; color: var(--text-tertiary); }
.help-search-input {
  flex: 1;
  min-width: 0;
  background: none;
  border: none;
  outline: none;
  color: var(--text-primary);
  font-family: inherit;
  font-size: 1rem;
}
.help-search-input::placeholder { color: var(--text-tertiary); }
.help-search-results {
  list-style: none;
  margin: 0;
  padding: 0 10px 10px;
  overflow-y: auto;
}
.help-search-results:empty { display: none; }
.help-search-result {
  display: block;
  padding: 9px 12px;
  border-radius: 8px;
  color: var(--text-primary);
  text-decoration: none;
  font-size: 0.9375rem;
  font-weight: 500;
  line-height: 1.35;
}
.help-search-result span {
  display: block;
  margin-top: 2px;
  color: var(--text-secondary);
  font-size: 0.8125rem;
  font-weight: 400;
}
.help-search-result.is-cursor { background: var(--film-mid); }
.help-search-empty {
  padding: 4px 22px 20px;
  color: var(--text-secondary);
  font-size: 0.9375rem;
}

/* ── The index at /help ──────────────────────────────────────────────────
   Three groups, each an inset list the way Settings draws one: a rounded
   block in the site's card grey, rows divided by a hairline, a chevron on the
   right. A group heading is a real heading rather than a tracked label, since
   on this page the groups are the structure and not a footnote to it. */
.help-content .help-hub { max-width: 960px; }
.help-hub-title {
  font-family: var(--font-display);
  font-size: clamp(1.875rem, 4vw, 2.5rem);
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.08;
  margin: 0 0 10px;
}
.help-hub-lede {
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--text-body);
  max-width: 48ch;
  margin: 0 0 12px;
}
.help-hub-group { padding: 32px 0 0; }
.help-hub-group-title {
  font-family: var(--font-display);
  font-size: 1.375rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.2;
  color: var(--text-primary);
  margin: 0 0 10px;
}
.help-hub-item {
  position: relative;
  display: block;
  padding: 18px 52px 19px 22px;
  background: var(--card);
  text-decoration: none;
  transition: background 0.14s ease;
}
.help-hub-item + .help-hub-item { border-top: 1px solid var(--separator-light); }
/* The block's corners belong to its first and last row, so a one-row group is
   rounded all the way round. */
.help-hub-item:first-of-type { border-radius: 14px 14px 0 0; }
.help-hub-item:last-of-type { border-radius: 0 0 14px 14px; }
.help-hub-item:first-of-type:last-of-type { border-radius: 14px; }
.help-hub-item:hover { background: var(--card-2); }
.help-hub-item::after {
  content: "";
  position: absolute;
  right: 24px;
  top: 50%;
  width: 8px;
  height: 8px;
  border-top: 1.5px solid var(--text-tertiary);
  border-right: 1.5px solid var(--text-tertiary);
  transform: translateY(-50%) rotate(45deg);
}
.help-hub-item-title {
  display: block;
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.3;
  color: var(--text-primary);
}
.help-hub-item-sub {
  display: block;
  margin-top: 3px;
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--text-secondary);
}
.help-hub .help-contact { margin-top: 44px; }

/* ── Article furniture ───────────────────────────────────────────────────
   Built by js/help.js from the rail, so the order lives in one place and a
   new topic needs no second edit here. */
.help-crumb {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0 0 14px;
  font-size: 0.8125rem;
  color: var(--text-tertiary);
}
.help-crumb a { color: var(--text-secondary); text-decoration: none; }
.help-crumb a:hover { color: var(--text-primary); }
.help-crumb svg { width: 12px; height: 12px; opacity: 0.6; }

.help-pager {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
  margin-top: 40px;
  padding-top: 22px;
  border-top: 1px solid var(--separator-light);
}
.help-pager-link { text-decoration: none; }
.help-pager-link--next { grid-column: 2; text-align: right; }
.help-pager-dir {
  display: block;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  margin-bottom: 4px;
}
.help-pager-name {
  display: block;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.35;
}
.help-pager-link:hover .help-pager-name { color: var(--green); }

@media (prefers-reduced-motion: reduce) {
  .help-search-scrim,
  .help-search-dialog,
  .help-search-corner { transition: none; }
}

@media (max-width: 760px) {
  /* Single scrolling column; the topic list opens full-screen from a button. */
  .help-page { overflow: visible; }
  .help-layout { grid-template-columns: 1fr; height: auto; }
  .help-content {
    height: auto;
    overflow: visible;
    padding: 24px var(--gutter) 72px;
  }

  /* The button: a sticky bar under the nav */
  .help-toc-toggle {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    position: sticky;
    top: var(--nav-h);
    z-index: 6;
    width: 100%;
    padding: 14px var(--gutter);
    border: none;
    border-bottom: 1px solid var(--separator);
    background: var(--bg);
    color: var(--text-primary);
    font-family: var(--font);
    font-size: 0.9375rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    cursor: pointer;
  }
  .help-toc-toggle svg { color: var(--green); flex: none; }

  /* Sidebar becomes a sheet of sections that rises from under the nav, with
     the nav left showing above its rounded top edge. */
  .help-sidebar {
    position: fixed;
    inset: var(--nav-h) 0 0;
    z-index: 350;
    border-radius: 20px 20px 0 0;
    box-shadow: 0 -12px 32px var(--key-shadow);
    height: auto;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 2px;
    padding: 24px var(--gutter) 40px;
    overflow-y: auto;
    background: var(--bg);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    border: none;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.3s ease, visibility 0.3s ease;
  }
  .help-sidebar.is-open { opacity: 1; visibility: visible; pointer-events: auto; }
  .help-sidebar-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    position: fixed;
    top: calc(var(--nav-h) + 8px);
    right: calc(var(--gutter) - 8px);
    width: 42px;
    height: 42px;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--green);
    cursor: pointer;
  }
  .help-nav-group { display: block; padding: 0; margin-bottom: 22px; border-top: none; }
  .help-nav-group-title { display: block; margin-bottom: 12px; }
  .help-nav-link {
    display: block;
    margin: 0 0 12px;
    padding: 0;
    font-size: 1.375rem;
    font-weight: 600;
    line-height: 1.3;
    white-space: normal;
    border-radius: 0;
  }
  .help-nav-link + .help-nav-link { margin-top: 0; }
  .help-nav-link:hover { background: none; }
  /* No fill behind the current topic here. The rows are edge to edge and 22px
     tall, so the block became a grey band across the sheet. Green says the
     same thing without drawing a box. */
  .help-nav-link.is-active,
  .help-nav-link.is-active:hover {
    background: none;
    color: var(--green);
  }

  /* The rail is a sheet here, so its search button goes with it. The index
     page carries its own instead, where it is the first thing under the
     lede. */
  .help-sidebar .help-search-trigger { display: none; }
  .help-search-trigger--hub { display: flex; max-width: 340px; }
  .help-search-scrim { padding: 0; align-items: stretch; }
  .help-search-dialog { max-width: none; border: none; border-radius: 0; }

  .help-hub-group { padding-top: 28px; }
  .help-hub-item { padding: 16px 44px 17px 18px; }
  .help-hub-item::after { right: 20px; }
  .help-pager { grid-template-columns: 1fr; gap: 22px; }
  .help-pager-link--next { grid-column: 1; text-align: left; }
}

.pill { display: inline-block; font-size: 0.75rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--green); background: rgba(159, 208, 56, 0.14); border: 1px solid rgba(159, 208, 56, 0.38); padding: 5px 10px; border-radius: 999px; }
.topic-label { font-size: 0.75rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--green); }
.large-title { font-size: 2rem; font-weight: 800; line-height: 1.1; }
.title-1 { font-size: 1.75rem; font-weight: 700; }

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 700px) {
  :root { --gutter: 20px; }

  .hero-wrap { height: auto; }
  .pain { position: relative; height: auto; min-height: auto; padding: 88px 0 48px; }
  .pain h1 { font-size: clamp(2rem, 8vw, 3.5rem); }

  .pivot { padding: 80px var(--gutter); }

  /* ── Demo ── */
  .demo { margin-bottom: 64px; padding: 64px var(--gutter) 40px; }
  .demo-page { max-width: none; }
  .demo-vignette,
  .demo-popup,
  .demo-select-box { display: none !important; }
  .demo-captions {
    position: static;
    width: 100%;
    margin: 28px auto 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
  .demo-caption {
    position: static;
    opacity: 1 !important;
    transform: none !important;
    font-size: 0.8125rem;
    padding: 10px 14px;
    border-radius: 8px;
    background: rgba(255,255,255,0.04);
    border: 1px solid var(--separator);
  }

  /* ── Features: stack the media above the caption ── */
  .features { padding: 64px var(--gutter); }
  .frow,
  .frow--reverse { grid-template-columns: 1fr; gap: 20px; margin-top: 56px; }
  .features-head + .frow { margin-top: 28px; }
  .frow--reverse .frow-media,
  .frow--reverse .frow-text { order: 0; }
  /* Demo row: its "media" is a reading passage, so on one column tell the
     visitor what happens (press ⌥A) before making them read the prose. */
  .frow.demo-row .frow-text { order: -1; }
  .frow-media { border-radius: 10px; }
  .reveal {
    transform: translate3d(0, 24px, 0);
    transition-duration: 0.8s;
  }

  /* ── Signup ── */
  .signup-section { padding: 80px var(--gutter); min-height: 0; }
  /* Flex item default min-width:auto lets a wide child (the headline) push
     .signup-inner past the viewport, dragging the whole download/feedback
     block off the right edge. Let it shrink so children wrap in-bounds. */
  .signup-inner { min-width: 0; max-width: 100%; }
  .sign-input { width: 100%; }
  .signup-cols { flex-direction: column; gap: 40px; }
  .signup-col-left { padding-right: 0; width: 100%; }
  .signup-col-right { padding-left: 0; border-left: none; border-top: 1px solid var(--separator); padding-top: 40px; width: 100%; align-items: center; }
  .signup-col-right .reach-links { flex-direction: row; justify-content: center; gap: 12px; }
  .feedback-cols { flex-direction: column; gap: 40px; text-align: center; }
  .feedback-col-main { padding-right: 0; width: 100%; }
  .feedback-col-social { padding-left: 0; border-left: none; border-top: 1px solid var(--separator); padding-top: 40px; width: 100%; align-items: center; }
  .feedback-col-social .reach-links { flex-direction: row; justify-content: center; gap: 12px; }
  .feedback-fields-row { flex-direction: column; gap: 0; }
  /* Send drops below the email line, right-aligned as a text action with a
     comfortable tap target. */
  .feedback-send-wrap {
    width: auto;
    align-self: flex-end;
    min-height: 0;
    margin: 10px 0 0;
  }
  .feedback-send-btn {
    width: auto;
    min-height: 0;
    flex-direction: row;
    padding: 10px 2px;
    font-size: 1.1875rem;
  }
  .feedback-input { text-align: left; width: 100%; }

  .nav-links { display: none; }
  .nav-cta { display: none; }          /* replaced by the burger on phones */
  .nav-burger { display: inline-flex; }
  .footer-inner { flex-direction: column; align-items: flex-start; gap: 8px; }
  .story-split { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .hero-reveal { animation: none; opacity: 1; transform: none; }
  .reveal { opacity: 1; transform: none; transition: none; }
  .lang-field { opacity: 1; transition: none; }
}

/* ============================================================
   PLAIN INDEX — Apple-utility restyle (body.plain)
   Quiet, centered, system-font sections in the spirit of an
   official macOS tool page. Reuses nav / dl-panel / feedback /
   demo-card components; everything below is layout only.
   ============================================================ */
.plain .content { padding-bottom: 24px; }

.plain-hero {
  max-width: var(--page-w);
  margin: 0 auto;
  padding: calc(var(--nav-h) + 44px) var(--gutter) 20px;
  display: grid;
  grid-template-columns: minmax(0, 520px) minmax(0, 380px);
  justify-content: space-between;
  gap: clamp(40px, 4vw, 72px);
  align-items: start;
  text-align: left;
}
.plain-hero-main {
  max-width: 520px;
  width: 100%;
}
.plain-hero-main,
.plain-hero-main p,
.plain-hero-main .dl-meta {
  text-align: left;
}

/* Right-hand product demo — a line you're reading, the definition popup
   dropping right under the looked-up word, then the auto-save confirmation.
   The popup art carries its beak at top-centre, so it's anchored to the word
   itself (not the column) — that keeps the beak on the word at any wrap. */
/* A real reading passage that keeps flowing; the definition popup floats on top
   of it, covering the text below the word with its beak pointing up at the word
   — exactly how the app looks in place. */
.plain-hero-visual { align-self: start; }
.plain-hero-visual .demo-eyebrow {
  color: var(--text-body);
  font-size: 0.875rem;
  margin-bottom: 16px;
}
.plain-hero-visual .demo-quote {
  font-size: 1.0625rem;
  line-height: 1.72;
  /* Recede the passage to a calm backdrop so the popup reads as the foreground
     (the looked-up word keeps its --text-primary weight below). */
  color: var(--text-secondary);
}
/* The text below the word is the popup's backdrop, so no big reserve is needed —
   just a small cushion under the passage. */
.plain-hero-visual .demo-stage {
  position: relative;
  padding-bottom: 77px;   /* lands the save line just under the popup, not far */
}
/* The looked-up word: a quiet selection highlight, the popup hangs off it. */
.plain-hero-visual .demo-word {
  position: relative;
  display: inline-block;   /* clean box so the popup centres exactly on the word */
  color: var(--text-primary);
  font-weight: 700;
  white-space: nowrap;
}
.plain-hero-visual .demo-popwrap {
  position: absolute;
  top: 100%;
  left: 50%;
  width: 312px;
  margin-top: -12px;   /* nudge the popup up so the beak tucks close to the word */
  margin-left: -156px;   /* half of width, centres the beak on the word */
  pointer-events: none;
  z-index: 5;
  /* Two layers: a tight contact shadow grounds every edge (incl. the top, which
     a single directional shadow would leave floating), plus a soft ambient one
     for depth. Keeps the card from reading as a pasted sticker. */
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.30)) drop-shadow(0 14px 30px rgba(0, 0, 0, 0.20));
}
:root[data-theme="light"] .plain-hero-visual .demo-popwrap {
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.12)) drop-shadow(0 12px 26px rgba(0, 0, 0, 0.11));
}
/* ── The live demo loop: drag box, then the popup springs open ──────────────
   Both are drawn in HTML and CSS and step through the frames of the clips they
   replaced (the timelines further down). They stay hidden until main.js has
   measured them onto the word; with reduced motion the passage simply reads as
   plain text and no popup ever appears. */
.plain-hero-visual .demo-drag-ui,
.plain-hero-visual .demo-pop-ui {
  position: absolute;
  left: 0;
  top: 0;
  opacity: 0;
  pointer-events: none;
}

/* The drag box, in the 1120 x 582 pixels of the clip it replaced. main.js sets
   left/top and a scale that closes the box around the word. The ink is plain
   white, dark on a light page; the cursor, crosshair and size readout are
   masks cut from the clip, so they take the ink colour too. */
.plain-hero-visual .demo-drag-ui {
  display: block;
  width: 1120px;
  height: 582px;
  transform-origin: 0 0;
  color: #fff;
}
:root[data-theme="light"] .plain-hero-visual .demo-drag-ui { color: #000; }
.demo-card.is-live .demo-drag-ui {
  z-index: 6;
  transition: opacity 0.18s ease;
}
.demo-card.is-live .demo-drag-ui.is-vis { opacity: 1; }
.demo-drag-ui > * { position: absolute; display: block; }
/* A unit square stretched by a transform, with a stroke that does not
   stretch: browsers round border widths and box edges to whole pixels, and the
   clip's line is 1.5 wide on half-pixel centres. */
.demo-drag-ui .dr-rect { left: 0; top: 0; overflow: visible; }
.demo-drag-ui .dr-rect path {
  fill: none; stroke: currentColor; stroke-width: 1.5; vector-effect: non-scaling-stroke;
  transform-origin: 0 0; opacity: 0;
}
.demo-drag-ui .dr-cross, .demo-drag-ui .dr-arrow, .demo-drag-ui .dr-num b {
  background: currentColor;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}
.demo-drag-ui .dr-cross { left: 0; top: 0; width: 29px; height: 29px; opacity: 0; -webkit-mask-image: url(/images/pop/drag-cross.png); mask-image: url(/images/pop/drag-cross.png); }
.demo-drag-ui .dr-arrow { left: 666px; top: 105px; width: 19px; height: 30px; opacity: 0; -webkit-mask-image: url(/images/pop/drag-arrow.png); mask-image: url(/images/pop/drag-arrow.png); }
.demo-drag-ui .dr-num { left: 0; top: 0; width: 72px; height: 14px; opacity: 0; }
.demo-drag-ui .dr-num b { position: absolute; display: block; left: 0; top: 0; width: 9px; height: 14px; -webkit-mask-image: url(/images/pop/drag-digits.png); mask-image: url(/images/pop/drag-digits.png); }
.demo-drag-ui .dr-num b:nth-child(2) { left: 8px; }
.demo-drag-ui .dr-num b:nth-child(3) { left: 16px; }
.demo-drag-ui .dr-num b:nth-child(4) { left: 24px; }
.demo-drag-ui .dr-num b:nth-child(5) { left: 32px; }
.demo-drag-ui .dr-num b:nth-child(6) { left: 40px; }
.demo-drag-ui .dr-num b:nth-child(7) { left: 48px; }
.demo-drag-ui .dr-num b:nth-child(8) { left: 56px; }

/* The video replaces the still, so the wrapper's shadow now has to come off the
   image and onto the clip (a drop-shadow on the wrapper would trace the whole
   transparent frame while the popup is still small). */
.demo-card.is-live .demo-popwrap { filter: none; }
/* The spring is no longer in the clip — the clip is just the popup standing
   still while its answer is on screen. macOS draws an NSPopover's open and
   close outside the app, in the window server, so they can never be filmed:
   the popup's own frame and alpha never move while the server scales and fades
   it. They're replayed here instead, from a curve measured off the window
   server frame by frame (tools/popup-capture/popover-curve.css). Both runs are
   265ms, the scale and the opacity share one easing, and the close is *not* the
   open reversed — its tail is slower, which is why reversing it never matched.
   Keyframes carry the measured values, so the timing function stays linear. */
/* The clip is transparent, so there is no box to put a border on. A zero-offset
   drop-shadow traces the alpha instead, which gives the card a hairline rim
   light: enough to separate its edge from the passage behind it. Stacked twice
   at a tiny blur so it reads as an edge, not a glow. It only ever nudges: the
   edge itself is painted into the clip (tools/popup-capture, RIM_LEVEL),
   because a drop-shadow traces the alpha and fades out around a rounded corner
   exactly where the alpha ramp widens — corners unlit, straight edges fine. */
.demo-card.is-live .demo-pop-ui {
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.30)) drop-shadow(0 14px 30px rgba(0, 0, 0, 0.20));
  transition: none;
  /* The popup hangs below the word, so the beak — and the point the whole
     thing grows out of — is the top centre. */
  transform-origin: top center;
}
.demo-card.is-live .demo-pop-ui.is-on { opacity: 1; }
.demo-card.is-live .demo-pop-ui.is-entering { animation: popover-in 265ms linear both; }
.demo-card.is-live .demo-pop-ui.is-leaving  { animation: popover-out 265ms linear both; }

@keyframes popover-in {
  0%     { transform: scale(0);      opacity: 0; }
  2.7%   { transform: scale(.0068);  opacity: .0020; }
  11.6%  { transform: scale(.0616);  opacity: .0623; }
  17.5%  { transform: scale(.1781);  opacity: .1755; }
  24.0%  { transform: scale(.3082);  opacity: .3107; }
  30.1%  { transform: scale(.4452);  opacity: .4477; }
  36.3%  { transform: scale(.5685);  opacity: .5745; }
  42.6%  { transform: scale(.6712);  opacity: .6848; }
  48.8%  { transform: scale(.7603);  opacity: .7762; }
  55.2%  { transform: scale(.8356);  opacity: .8488; }
  61.4%  { transform: scale(.8904);  opacity: .9043; }
  67.8%  { transform: scale(.9315);  opacity: .9451; }
  74.1%  { transform: scale(.9589);  opacity: .9738; }
  80.3%  { transform: scale(.9726);  opacity: .9930; }
  86.6%  { transform: scale(.9863);  opacity: 1; }
  92.9%  { transform: scale(.9932);  opacity: 1; }
  100%   { transform: scale(1);      opacity: 1; }
}

@keyframes popover-out {
  0%     { transform: scale(1);      opacity: 1; }
  11.2%  { transform: scale(.9296);  opacity: .9191; }
  17.5%  { transform: scale(.8239);  opacity: .8134; }
  23.7%  { transform: scale(.7042);  opacity: .6932; }
  30.0%  { transform: scale(.5775);  opacity: .5726; }
  36.3%  { transform: scale(.4648);  opacity: .4602; }
  42.6%  { transform: scale(.3662);  opacity: .3606; }
  48.9%  { transform: scale(.2817);  opacity: .2756; }
  55.2%  { transform: scale(.2113);  opacity: .2053; }
  61.5%  { transform: scale(.1549);  opacity: .1487; }
  67.8%  { transform: scale(.1056);  opacity: .1043; }
  74.1%  { transform: scale(.0775);  opacity: .0704; }
  80.3%  { transform: scale(.0493);  opacity: .0450; }
  86.6%  { transform: scale(.0282);  opacity: .0267; }
  92.9%  { transform: scale(.0141);  opacity: .0138; }
  100%   { transform: scale(0);      opacity: 0; }
}
/* On a light page the rim has to go the other way: a faint dark hairline, since
   a white one would vanish into the paper. */
:root[data-theme="light"] .demo-card.is-live .demo-pop-ui {
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.12)) drop-shadow(0 12px 26px rgba(0, 0, 0, 0.11));
}
/* ── The popup itself, drawn here instead of filmed ─────────────────────────
   Everything below is laid out in the pixels of the clip it replaces (624 x 660,
   a 2x capture of the app's popup), and .pop-scale halves it into the 312 x 330
   box the clip used to fill. So every number can be checked against a frame of
   _unused/video/popup-still-3.webm directly. Dark in both themes, like the app. */
.plain-hero-visual .demo-pop-ui {
  display: block;
  width: 312px;
  height: 330px;
  text-align: left;
  font-style: normal;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  white-space: nowrap;
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, "Segoe UI", sans-serif;
  -webkit-font-smoothing: auto;
}
.demo-pop-ui .pop-scale {
  position: absolute;
  left: 0;
  top: 0;
  width: 624px;
  height: 660px;
  transform: scale(0.5);
  transform-origin: 0 0;
}
.demo-pop-ui .pop-beak { position: absolute; left: 0; top: 0; z-index: 1; overflow: visible; fill: #0f0f0f; stroke: #373737; stroke-width: 2; }
.demo-pop-ui .pop-card {
  position: absolute;
  left: 4.5px;
  top: 20px;
  width: 616px;
  height: 636px;
  overflow: hidden;
  border-radius: 30px;
  background: #0f0f0f;
  box-shadow: inset 0 0 0 2px #373737;
}
/* children are placed in clip coordinates, not card ones */
.demo-pop-ui .pop-in { position: absolute; left: -4.5px; top: -20px; width: 624px; height: 660px; }
.demo-pop-ui .pop-in > *, .demo-pop-ui .pop-bar > * { position: absolute; display: block; font-style: normal; }
/* The tool bar keeps the app's own point geometry, scaled onto the clip. */
.demo-pop-ui .pop-bar {
  left: 16.5px;
  top: 31.5px;
  width: 384px;
  height: 34.4px;
  transform: scale(1.5417);
  transform-origin: 0 0;
  border-radius: 17.2px;
  background: #111;
  box-shadow: inset 0 0 0 1.3px #232323;
}
.demo-pop-ui .pop-sym {
  background: currentColor;
  -webkit-mask: var(--m) center / 100% 100% no-repeat;
  mask: var(--m) center / 100% 100% no-repeat;
}
.demo-pop-ui .pop-lbl { top: 10.3px; font: 600 12.2px/16px -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif; color: #cdcdcd; }
/* The loading bar under the tool bar. Its colour along its length is taken
   from every frame of the clip, one pixel row a frame (images/pop/bar.png,
   145 rows, the last repeated so the run can end on it), and stepped through;
   the soft top and bottom edge are a mask. */
.demo-pop-ui .pop-load {
  left: 5px; top: 93px; width: 615px; height: 4px;
  background: url(/images/pop/bar.png) 0 0 / 615px 580px no-repeat;
  -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.3) 25%, #000 25% 75%, rgba(0, 0, 0, 0.55) 75%);
  mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.3) 25%, #000 25% 75%, rgba(0, 0, 0, 0.55) 75%);
}
.demo-pop-ui .pop-t { color: #e2e2e2; }
.demo-pop-ui .pop-head { left: 23px;  top: 103px; font-size: 36.2px; font-weight: 600; line-height: 44px; color: #fff; }
.demo-pop-ui .pop-phon { left: 35px;  top: 188px; font-size: 31.9px; line-height: 38px; color: #b1b1b1; }
.demo-pop-ui .pop-h2   { font-size: 34.2px; font-weight: 800; line-height: 40px; color: #ceee6d; }
.demo-pop-ui .pop-def  { left: 36px; top: 278px; }
.demo-pop-ui .pop-chip { left: 34.5px; top: 330.5px; width: 133.5px; height: 33px; border-radius: 4px; background: #262b17; }
.demo-pop-ui .pop-chipt { left: 42px; top: 329px; font-size: 27.9px; font-weight: 700; line-height: 36px; color: #ceee6d; }
.demo-pop-ui .pop-body { font-size: 31.55px; line-height: 39px; }
.demo-pop-ui .pop-l1   { left: 36px; top: 370px; }
.demo-pop-ui .pop-l2   { left: 36px; top: 409px; }
.demo-pop-ui .pop-gl   { left: 36px; top: 453px; }
.demo-pop-ui .pop-gl u { position: absolute; left: 0; top: 0; text-decoration: none; opacity: 0; }
.demo-pop-ui .pop-more {
  left: 35px; top: 465px; font-size: 29.75px; font-weight: 600; line-height: 36px; color: #ddd;
  text-decoration: underline; text-decoration-color: #7a7a7a; text-decoration-thickness: 2.6px; text-underline-offset: 3.5px;
}
.demo-pop-ui .pop-foot { left: 36px; top: 560px; font-size: 27.3px; line-height: 31px; color: #7b7b7b; }
.demo-pop-ui .pop-ctx  { left: 36px; top: 559px; }
.demo-pop-ui .pop-cb1  { left: 35px; top: 607px; }
.demo-pop-ui .pop-cb2  { left: 35px; top: 646px; }
.demo-pop-ui .pop-thumb { left: 598px; top: 169.5px; width: 17.5px; height: 443px; border-radius: 8.75px; background: #9b9b9b; }
/* Text that is still streaming in: opaque up to --xs, gone by --xe. */
@property --xs { syntax: "<length>"; inherits: false; initial-value: 2000px; }
@property --xe { syntax: "<length>"; inherits: false; initial-value: 2100px; }
.demo-pop-ui .pop-l1, .demo-pop-ui .pop-l2, .demo-pop-ui .pop-gl u, .demo-pop-ui .pop-more,
.demo-pop-ui .pop-ctx, .demo-pop-ui .pop-cb1 {
  -webkit-mask-image: linear-gradient(90deg, #000 var(--xs), transparent var(--xe));
  mask-image: linear-gradient(90deg, #000 var(--xs), transparent var(--xe));
}
/* @popup-timeline: generated from the clip, one keyframe per changed frame */
@keyframes pop-l1 {
  0% { opacity: 1.0; --xs: 70px; --xe: 246px; }
  0.6944% { opacity: 1.0; --xs: 248px; --xe: 420px; }
  1.3889% { opacity: 1.0; --xs: 410px; --xe: 590px; }
  2.0833% { opacity: 1.0; --xs: 516px; --xe: 772px; }
  2.7778% { opacity: 1.0; --xs: 2000px; --xe: 2100px; }
  100% { opacity: 1.0; --xs: 2000px; --xe: 2100px; }
}
.demo-pop-ui.is-on .pop-l1 { animation: pop-l1 2.4s step-end both; }
@keyframes pop-l2 {
  0% { opacity: 0; }
  1.3889% { opacity: 0.234; --xs: 8px; --xe: 52px; }
  2.0833% { opacity: 1.0; --xs: 34px; --xe: 190px; }
  2.7778% { opacity: 1.0; --xs: 332px; --xe: 524px; }
  3.4722% { opacity: 1.0; --xs: 2000px; --xe: 2100px; }
  100% { opacity: 1.0; --xs: 2000px; --xe: 2100px; }
}
.demo-pop-ui.is-on .pop-l2 { animation: pop-l2 2.4s step-end both; }
@keyframes pop-more {
  0% { opacity: 0; }
  2.7778% { opacity: 1.0; --xs: 70px; --xe: 74px; }
  3.4722% { opacity: 1.0; --xs: 2000px; --xe: 2100px; }
  36.8056% { opacity: 0; --xs: 2000px; --xe: 2100px; }
  68.75% { opacity: 1.0; --xs: 178px; --xe: 182px; }
  69.4444% { opacity: 1.0; --xs: 2000px; --xe: 2100px; }
  77.0833% { opacity: 0; --xs: 2000px; --xe: 2100px; }
  77.7778% { opacity: 1; --xs: 2000px; --xe: 2100px; }
  100% { opacity: 1; --xs: 2000px; --xe: 2100px; }
}
.demo-pop-ui.is-on .pop-more { animation: pop-more 2.4s step-end both; }
@keyframes pop-foot {
  0% { opacity: 1; transform: translateY(-121px); }
  1.3889% { opacity: 1; transform: translateY(-83px); }
  2.7778% { opacity: 1; transform: translateY(-22px); }
  3.4722% { opacity: 1; transform: translateY(0px); }
  36.8056% { opacity: 1; transform: translateY(-62px); }
  57.6389% { opacity: 1; transform: translateY(-39px); }
  68.75% { opacity: 1; transform: translateY(-22px); }
  69.4444% { opacity: 1; transform: translateY(70px); }
  70.1389% { opacity: 0; transform: translateY(70px); }
  100% { opacity: 0; transform: translateY(70px); }
}
.demo-pop-ui.is-on .pop-foot { animation: pop-foot 2.4s step-end both; }
@keyframes pop-gl0 {
  0% { opacity: 0; }
  57.6389% { opacity: 0.064; --xs: 22px; --xe: 26px; }
  58.3333% { opacity: 0.234; --xs: 12px; --xe: 64px; }
  59.0278% { opacity: 0.401; --xs: 14px; --xe: 86px; }
  59.7222% { opacity: 1.0; --xs: 112px; --xe: 116px; }
  60.4167% { opacity: 0; --xs: 2000px; --xe: 2100px; }
  100% { opacity: 0; --xs: 2000px; --xe: 2100px; }
}
.demo-pop-ui.is-on .pop-gl u:nth-child(1) { animation: pop-gl0 2.4s step-end both; }
@keyframes pop-gl1 {
  0% { opacity: 0; }
  60.4167% { opacity: 1.0; --xs: 19px; --xe: 23px; }
  61.1111% { opacity: 0; --xs: 2000px; --xe: 2100px; }
  100% { opacity: 0; --xs: 2000px; --xe: 2100px; }
}
.demo-pop-ui.is-on .pop-gl u:nth-child(2) { animation: pop-gl1 2.4s step-end both; }
@keyframes pop-gl2 {
  0% { opacity: 0; }
  61.1111% { opacity: 0.236; --xs: 6px; --xe: 54px; }
  61.8056% { opacity: 0; --xs: 2000px; --xe: 2100px; }
  100% { opacity: 0; --xs: 2000px; --xe: 2100px; }
}
.demo-pop-ui.is-on .pop-gl u:nth-child(3) { animation: pop-gl2 2.4s step-end both; }
@keyframes pop-gl3 {
  0% { opacity: 0; }
  61.8056% { opacity: 0.416; --xs: 7px; --xe: 91px; }
  62.5% { opacity: 0; --xs: 2000px; --xe: 2100px; }
  100% { opacity: 0; --xs: 2000px; --xe: 2100px; }
}
.demo-pop-ui.is-on .pop-gl u:nth-child(4) { animation: pop-gl3 2.4s step-end both; }
@keyframes pop-gl4 {
  0% { opacity: 0; }
  62.5% { opacity: 1.0; --xs: 36px; --xe: 40px; }
  63.1944% { opacity: 1.0; --xs: 36px; --xe: 40px; }
  63.8889% { opacity: 0; --xs: 2000px; --xe: 2100px; }
  100% { opacity: 0; --xs: 2000px; --xe: 2100px; }
}
.demo-pop-ui.is-on .pop-gl u:nth-child(5) { animation: pop-gl4 2.4s step-end both; }
@keyframes pop-gl5 {
  0% { opacity: 0; }
  63.8889% { opacity: 0.233; --xs: 6px; --xe: 54px; }
  64.5833% { opacity: 0; --xs: 2000px; --xe: 2100px; }
  100% { opacity: 0; --xs: 2000px; --xe: 2100px; }
}
.demo-pop-ui.is-on .pop-gl u:nth-child(6) { animation: pop-gl5 2.4s step-end both; }
@keyframes pop-gl6 {
  0% { opacity: 0; }
  64.5833% { opacity: 0.316; --xs: 10px; --xe: 58px; }
  65.2778% { opacity: 0; --xs: 2000px; --xe: 2100px; }
  100% { opacity: 0; --xs: 2000px; --xe: 2100px; }
}
.demo-pop-ui.is-on .pop-gl u:nth-child(7) { animation: pop-gl6 2.4s step-end both; }
@keyframes pop-gl7 {
  0% { opacity: 0; }
  65.2778% { opacity: 1.0; --xs: 4px; --xe: 8px; }
  65.9722% { opacity: 0; --xs: 2000px; --xe: 2100px; }
  100% { opacity: 0; --xs: 2000px; --xe: 2100px; }
}
.demo-pop-ui.is-on .pop-gl u:nth-child(8) { animation: pop-gl7 2.4s step-end both; }
@keyframes pop-gl8 {
  0% { opacity: 0; }
  65.9722% { opacity: 0.16; --xs: 8px; --xe: 20px; }
  66.6667% { opacity: 0; --xs: 2000px; --xe: 2100px; }
  100% { opacity: 0; --xs: 2000px; --xe: 2100px; }
}
.demo-pop-ui.is-on .pop-gl u:nth-child(9) { animation: pop-gl8 2.4s step-end both; }
@keyframes pop-gl9 {
  0% { opacity: 0; }
  66.6667% { opacity: 1.0; --xs: 105px; --xe: 109px; }
  67.3611% { opacity: 0; --xs: 2000px; --xe: 2100px; }
  100% { opacity: 0; --xs: 2000px; --xe: 2100px; }
}
.demo-pop-ui.is-on .pop-gl u:nth-child(10) { animation: pop-gl9 2.4s step-end both; }
@keyframes pop-gl10 {
  0% { opacity: 0; }
  67.3611% { opacity: 1.0; --xs: 19px; --xe: 23px; }
  68.0556% { opacity: 0; --xs: 2000px; --xe: 2100px; }
  100% { opacity: 0; --xs: 2000px; --xe: 2100px; }
}
.demo-pop-ui.is-on .pop-gl u:nth-child(11) { animation: pop-gl10 2.4s step-end both; }
@keyframes pop-gl11 {
  0% { opacity: 0; }
  68.0556% { opacity: 1.0; --xs: 11px; --xe: 19px; }
  68.75% { opacity: 0; --xs: 2000px; --xe: 2100px; }
  100% { opacity: 0; --xs: 2000px; --xe: 2100px; }
}
.demo-pop-ui.is-on .pop-gl u:nth-child(12) { animation: pop-gl11 2.4s step-end both; }
@keyframes pop-ctx {
  0% { opacity: 0; }
  69.4444% { opacity: 1.0; transform: translateY(0px); --xs: 79px; --xe: 327px; }
  70.1389% { opacity: 1.0; transform: translateY(0px); --xs: 321px; --xe: 577px; }
  70.8333% { opacity: 1.0; transform: translateY(0px); --xs: 2000px; --xe: 2100px; }
  77.0833% { opacity: 1.0; transform: translateY(-61px); --xs: 2000px; --xe: 2100px; }
  77.7778% { opacity: 1.0; transform: translateY(0px); --xs: 2000px; --xe: 2100px; }
  100% { opacity: 1.0; transform: translateY(0px); --xs: 2000px; --xe: 2100px; }
}
.demo-pop-ui.is-on .pop-ctx { animation: pop-ctx 2.4s step-end both; }
@keyframes pop-cb1 {
  0% { opacity: 0; }
  70.1389% { opacity: 1.0; transform: translateY(0px); --xs: -30px; --xe: 118px; }
  70.8333% { opacity: 1.0; transform: translateY(0px); --xs: 104px; --xe: 300px; }
  71.5278% { opacity: 1.0; transform: translateY(0px); --xs: 288px; --xe: 428px; }
  72.2222% { opacity: 1.0; transform: translateY(0px); --xs: 422px; --xe: 614px; }
  72.9167% { opacity: 1.0; transform: translateY(0px); --xs: 2000px; --xe: 2100px; }
  77.0833% { opacity: 1.0; transform: translateY(-61px); --xs: 2000px; --xe: 2100px; }
  77.7778% { opacity: 1.0; transform: translateY(0px); --xs: 2000px; --xe: 2100px; }
  100% { opacity: 1.0; transform: translateY(0px); --xs: 2000px; --xe: 2100px; }
}
.demo-pop-ui.is-on .pop-cb1 { animation: pop-cb1 2.4s step-end both; }
@keyframes pop-cb2 {
  0% { opacity: 0; }
  72.2222% { opacity: 1; transform: translateY(0px); }
  77.0833% { opacity: 1; transform: translateY(-61px); }
  77.7778% { opacity: 1; transform: translateY(0px); }
  100% { opacity: 1; transform: translateY(0px); }
}
.demo-pop-ui.is-on .pop-cb2 { animation: pop-cb2 2.4s step-end both; }
@keyframes pop-thumb {
  0% { opacity: 0; }
  2.7778% { opacity: 1; height: 462.5px; }
  3.4722% { opacity: 1; height: 443.5px; }
  36.8056% { opacity: 0; height: 443.5px; }
  57.6389% { opacity: 1; height: 477.5px; }
  68.75% { opacity: 1; height: 462.5px; }
  69.4444% { opacity: 1; height: 391.5px; }
  70.1389% { opacity: 1; height: 363.5px; }
  72.2222% { opacity: 1; height: 344.5px; }
  77.0833% { opacity: 1; height: 318.5px; }
  77.7778% { opacity: 1; height: 294.5px; }
  100% { opacity: 1; height: 294.5px; }
}
.demo-pop-ui.is-on .pop-thumb { animation: pop-thumb 2.4s step-end both; }
.demo-pop-ui.is-on .pop-load { animation: pop-bar 2.4s steps(144, end) both; }
@keyframes pop-bar { from { background-position: 0 0; } to { background-position: 0 -576px; } }
/* @end-popup-timeline */
/* @drag-timeline: generated from video/drag3-alpha.webm, one keyframe per changed frame */
@keyframes dr-rect {
  0% { opacity: 0; }
  6.4935% { opacity: 1; transform: translate(436.25px,30.5px) scale(35.00,4.94); }
  7.7922% { opacity: 1; transform: translate(436.25px,30.5px) scale(44.00,9.57); }
  9.0909% { opacity: 1; transform: translate(436.25px,30.5px) scale(54.54,13.61); }
  10.3896% { opacity: 1; transform: translate(436.25px,30.5px) scale(64.62,17.53); }
  11.6883% { opacity: 1; transform: translate(436.25px,30.5px) scale(75.62,21.01); }
  12.987% { opacity: 1; transform: translate(436.25px,30.5px) scale(86.36,24.43); }
  14.2857% { opacity: 1; transform: translate(436.25px,30.5px) scale(97.01,28.30); }
  15.5844% { opacity: 1; transform: translate(436.25px,30.5px) scale(106.82,31.47); }
  16.8831% { opacity: 1; transform: translate(436.25px,30.5px) scale(114.88,34.40); }
  18.1818% { opacity: 1; transform: translate(436.25px,30.5px) scale(122.05,37.30); }
  19.4805% { opacity: 1; transform: translate(436.25px,30.5px) scale(128.29,39.40); }
  20.7792% { opacity: 1; transform: translate(436.25px,30.5px) scale(133.66,41.39); }
  22.0779% { opacity: 1; transform: translate(436.25px,30.5px) scale(138.68,43.39); }
  23.3766% { opacity: 1; transform: translate(436.25px,30.5px) scale(143.74,45.41); }
  24.6753% { opacity: 1; transform: translate(436.25px,30.5px) scale(149.58,47.43); }
  25.974% { opacity: 1; transform: translate(436.25px,30.5px) scale(155.34,49.46); }
  27.2727% { opacity: 1; transform: translate(436.25px,30.5px) scale(159.68,51.40); }
  28.5714% { opacity: 1; transform: translate(436.25px,30.5px) scale(163.85,52.65); }
  29.8701% { opacity: 1; transform: translate(436.25px,30.5px) scale(168.24,54.35); }
  31.1688% { opacity: 1; transform: translate(436.25px,30.5px) scale(172.66,55.52); }
  32.4675% { opacity: 1; transform: translate(436.25px,30.5px) scale(177.71,56.98); }
  33.7662% { opacity: 1; transform: translate(436.25px,30.5px) scale(183.59,59.27); }
  35.0649% { opacity: 1; transform: translate(436.25px,30.5px) scale(189.50,60.62); }
  36.3636% { opacity: 1; transform: translate(436.25px,30.5px) scale(194.59,62.19); }
  37.6623% { opacity: 1; transform: translate(436.25px,30.5px) scale(198.63,63.71); }
  38.961% { opacity: 1; transform: translate(436.25px,30.5px) scale(201.85,65.13); }
  40.2597% { opacity: 1; transform: translate(436.25px,30.5px) scale(205.56,65.91); }
  41.5584% { opacity: 1; transform: translate(436.25px,30.5px) scale(208.72,66.57); }
  42.8571% { opacity: 1; transform: translate(436.25px,30.5px) scale(211.53,67.47); }
  45.4545% { opacity: 1; transform: translate(436.25px,30.5px) scale(213.61,68.35); }
  46.7532% { opacity: 1; transform: translate(436.25px,30.5px) scale(215.39,68.67); }
  48.0519% { opacity: 1; transform: translate(436.25px,30.5px) scale(217.21,69.40); }
  49.3506% { opacity: 1; transform: translate(436.25px,30.5px) scale(218.88,69.73); }
  50.6494% { opacity: 1; transform: translate(436.25px,30.5px) scale(220.62,70.44); }
  51.9481% { opacity: 1; transform: translate(436.25px,30.5px) scale(222.32,70.85); }
  53.2468% { opacity: 1; transform: translate(436.25px,30.5px) scale(223.96,71.47); }
  54.5455% { opacity: 1; transform: translate(436.25px,30.5px) scale(225.52,71.76); }
  55.8442% { opacity: 1; transform: translate(436.25px,30.5px) scale(226.67,72.33); }
  57.1429% { opacity: 1; transform: translate(436.25px,30.5px) scale(227.34,72.44); }
  58.4416% { opacity: 1; transform: translate(436.25px,30.5px) scale(227.73,72.44); }
  59.7403% { opacity: 1; transform: translate(436.25px,30.5px) scale(228.13,72.60); }
  61.039% { opacity: 1; transform: translate(436.25px,30.5px) scale(228.73,72.80); }
  62.3377% { opacity: 1; transform: translate(436.25px,30.5px) scale(229.48,73.23); }
  63.6364% { opacity: 1; transform: translate(436.25px,30.5px) scale(229.86,73.48); }
  66.2338% { opacity: 1; transform: translate(436.25px,30.5px) scale(230.47,73.48); }
  67.5325% { opacity: 1; transform: translate(436.25px,30.5px) scale(230.65,73.66); }
  68.8312% { opacity: 1; transform: translate(436.25px,30.5px) scale(230.83,74.09); }
  70.1299% { opacity: 1; transform: translate(436.25px,30.5px) scale(230.94,74.38); }
  71.4286% { opacity: 1; transform: translate(436.25px,30.5px) scale(231.47,74.55); }
  74.026% { opacity: 1; transform: translate(436.25px,30.5px) scale(231.47,74.54); }
  75.3247% { opacity: 1; transform: translate(436.25px,30.5px) scale(231.85,74.62); }
  76.6234% { opacity: 1; transform: translate(436.25px,30.5px) scale(231.85,74.63); }
  77.9221% { opacity: 1; transform: translate(436.25px,30.5px) scale(232.25,74.86); }
  79.2208% { opacity: 1; transform: translate(436.25px,30.5px) scale(232.61,75.19); }
  81.8182% { opacity: 1; transform: translate(436.25px,30.5px) scale(232.77,75.46); }
  85.7143% { opacity: 1; transform: translate(436.25px,30.5px) scale(233.36,75.49); }
  98.7013% { opacity: 0.69; transform: translate(436.25px,30.5px) scale(233.41,75.48); }
  100% { opacity: 0.69; transform: translate(436.25px,30.5px) scale(233.41,75.48); }
}
.demo-drag-ui.is-on .dr-rect path { animation: dr-rect 1.2833s step-end both; }
@keyframes dr-cross {
  0% { opacity: 1; transform: translate(423px,16px); }
  1.2987% { opacity: 1; transform: translate(426px,16px); }
  2.5974% { opacity: 1; transform: translate(431px,16px); }
  3.8961% { opacity: 1; transform: translate(437px,16px); }
  5.1948% { opacity: 1; transform: translate(445px,19px); }
  6.4935% { opacity: 1; transform: translate(455px,22px); }
  7.7922% { opacity: 1; transform: translate(465px,27px); }
  9.0909% { opacity: 1; transform: translate(476px,31px); }
  10.3896% { opacity: 1; transform: translate(488px,35px); }
  11.6883% { opacity: 1; transform: translate(498px,39px); }
  12.987% { opacity: 1; transform: translate(509px,42px); }
  14.2857% { opacity: 1; transform: translate(520px,45px); }
  15.5844% { opacity: 1; transform: translate(530px,49px); }
  16.8831% { opacity: 1; transform: translate(538px,52px); }
  18.1818% { opacity: 1; transform: translate(545px,55px); }
  19.4805% { opacity: 1; transform: translate(551px,57px); }
  20.7792% { opacity: 1; transform: translate(556px,58px); }
  22.0779% { opacity: 1; transform: translate(561px,60px); }
  23.3766% { opacity: 1; transform: translate(566px,63px); }
  24.6753% { opacity: 1; transform: translate(572px,65px); }
  25.974% { opacity: 1; transform: translate(577px,66px); }
  27.2727% { opacity: 1; transform: translate(582px,68px); }
  28.5714% { opacity: 1; transform: translate(587px,70px); }
  29.8701% { opacity: 1; transform: translate(590px,71px); }
  31.1688% { opacity: 1; transform: translate(595px,73px); }
  32.4675% { opacity: 1; transform: translate(600px,75px); }
  33.7662% { opacity: 1; transform: translate(607px,76px); }
  35.0649% { opacity: 1; transform: translate(612px,78px); }
  36.3636% { opacity: 1; transform: translate(616px,80px); }
  37.6623% { opacity: 1; transform: translate(621px,81px); }
  38.961% { opacity: 1; transform: translate(625px,83px); }
  40.2597% { opacity: 1; transform: translate(628px,83px); }
  41.5584% { opacity: 1; transform: translate(631px,84px); }
  42.8571% { opacity: 1; transform: translate(634px,84px); }
  45.4545% { opacity: 1; transform: translate(636px,86px); }
  46.7532% { opacity: 1; transform: translate(638px,86px); }
  48.0519% { opacity: 1; transform: translate(642px,88px); }
  50.6494% { opacity: 1; transform: translate(646px,88px); }
  51.9481% { opacity: 1; transform: translate(647px,89px); }
  54.5455% { opacity: 1; transform: translate(649px,89px); }
  55.8442% { opacity: 1; transform: translate(651px,89px); }
  62.3377% { opacity: 1; transform: translate(652px,91px); }
  67.5325% { opacity: 1; transform: translate(654px,91px); }
  75.3247% { opacity: 1; transform: translate(654px,93px); }
  77.9221% { opacity: 1; transform: translate(655px,93px); }
  90.9091% { opacity: 0; transform: translate(655px,93px); }
  100% { opacity: 0; transform: translate(655px,93px); }
}
.demo-drag-ui.is-on .dr-cross { animation: dr-cross 1.2833s step-end both; }
@keyframes dr-num {
  0% { opacity: 1; transform: translate(446px,40px); }
  1.2987% { opacity: 1; transform: translate(449px,40px); }
  2.5974% { opacity: 1; transform: translate(454px,40px); }
  3.8961% { opacity: 1; transform: translate(460px,40px); }
  5.1948% { opacity: 1; transform: translate(468px,44px); }
  6.4935% { opacity: 1; transform: translate(478px,47px); }
  7.7922% { opacity: 1; transform: translate(488px,52px); }
  9.0909% { opacity: 1; transform: translate(499px,55px); }
  10.3896% { opacity: 1; transform: translate(511px,60px); }
  11.6883% { opacity: 1; transform: translate(520px,63px); }
  12.987% { opacity: 1; transform: translate(532px,66px); }
  14.2857% { opacity: 1; transform: translate(543px,70px); }
  15.5844% { opacity: 1; transform: translate(553px,73px); }
  16.8831% { opacity: 1; transform: translate(561px,76px); }
  18.1818% { opacity: 1; transform: translate(568px,79px); }
  19.4805% { opacity: 1; transform: translate(574px,81px); }
  20.7792% { opacity: 1; transform: translate(579px,83px); }
  22.0779% { opacity: 1; transform: translate(584px,84px); }
  23.3766% { opacity: 1; transform: translate(589px,88px); }
  24.6753% { opacity: 1; transform: translate(595px,89px); }
  25.974% { opacity: 1; transform: translate(600px,91px); }
  27.2727% { opacity: 1; transform: translate(605px,92px); }
  28.5714% { opacity: 1; transform: translate(610px,94px); }
  29.8701% { opacity: 1; transform: translate(613px,96px); }
  31.1688% { opacity: 1; transform: translate(618px,97px); }
  32.4675% { opacity: 1; transform: translate(623px,99px); }
  33.7662% { opacity: 1; transform: translate(630px,101px); }
  35.0649% { opacity: 1; transform: translate(635px,102px); }
  36.3636% { opacity: 1; transform: translate(639px,104px); }
  37.6623% { opacity: 1; transform: translate(644px,105px); }
  38.961% { opacity: 1; transform: translate(648px,107px); }
  40.2597% { opacity: 1; transform: translate(651px,107px); }
  41.5584% { opacity: 1; transform: translate(654px,109px); }
  42.8571% { opacity: 1; transform: translate(657px,109px); }
  45.4545% { opacity: 1; transform: translate(659px,110px); }
  46.7532% { opacity: 1; transform: translate(661px,110px); }
  48.0519% { opacity: 1; transform: translate(666px,112px); }
  50.6494% { opacity: 1; transform: translate(669px,112px); }
  51.9481% { opacity: 1; transform: translate(670px,114px); }
  54.5455% { opacity: 1; transform: translate(672px,114px); }
  55.8442% { opacity: 1; transform: translate(674px,114px); }
  62.3377% { opacity: 1; transform: translate(675px,115px); }
  67.5325% { opacity: 1; transform: translate(677px,115px); }
  75.3247% { opacity: 1; transform: translate(677px,117px); }
  77.9221% { opacity: 1; transform: translate(679px,117px); }
  90.9091% { opacity: 0; transform: translate(679px,117px); }
  100% { opacity: 0; transform: translate(679px,117px); }
}
.demo-drag-ui.is-on .dr-num { animation: dr-num 1.2833s step-end both; }
@keyframes dr-c0 {
  0% { opacity: 1; -webkit-mask-position: -9px 0; mask-position: -9px 0; }
  1.2987% { opacity: 1; -webkit-mask-position: -45px 0; mask-position: -45px 0; }
  2.5974% { opacity: 1; -webkit-mask-position: -9px 0; mask-position: -9px 0; }
  5.1948% { opacity: 1; -webkit-mask-position: -18px 0; mask-position: -18px 0; }
  6.4935% { opacity: 1; -webkit-mask-position: -36px 0; mask-position: -36px 0; }
  7.7922% { opacity: 1; -webkit-mask-position: -45px 0; mask-position: -45px 0; }
  9.0909% { opacity: 1; -webkit-mask-position: -54px 0; mask-position: -54px 0; }
  10.3896% { opacity: 1; -webkit-mask-position: -72px 0; mask-position: -72px 0; }
  11.6883% { opacity: 1; -webkit-mask-position: -81px 0; mask-position: -81px 0; }
  12.987% { opacity: 1; -webkit-mask-position: -9px 0; mask-position: -9px 0; }
  28.5714% { opacity: 1; -webkit-mask-position: -18px 0; mask-position: -18px 0; }
  100% { opacity: 1; -webkit-mask-position: -18px 0; mask-position: -18px 0; }
}
.demo-drag-ui.is-on .dr-num b:nth-child(1) { animation: dr-c0 1.2833s step-end both; }
@keyframes dr-c1 {
  0% { opacity: 0; }
  2.5974% { opacity: 1; -webkit-mask-position: -18px 0; mask-position: -18px 0; }
  3.8961% { opacity: 1; -webkit-mask-position: -81px 0; mask-position: -81px 0; }
  6.4935% { opacity: 1; -webkit-mask-position: -9px 0; mask-position: -9px 0; }
  7.7922% { opacity: 1; -webkit-mask-position: -36px 0; mask-position: -36px 0; }
  9.0909% { opacity: 1; -webkit-mask-position: -63px 0; mask-position: -63px 0; }
  10.3896% { opacity: 1; -webkit-mask-position: -9px 0; mask-position: -9px 0; }
  11.6883% { opacity: 1; -webkit-mask-position: -36px 0; mask-position: -36px 0; }
  12.987% { opacity: 1; -webkit-mask-position: 0px 0; mask-position: 0px 0; }
  14.2857% { opacity: 1; -webkit-mask-position: -18px 0; mask-position: -18px 0; }
  15.5844% { opacity: 1; -webkit-mask-position: -27px 0; mask-position: -27px 0; }
  16.8831% { opacity: 1; -webkit-mask-position: -36px 0; mask-position: -36px 0; }
  18.1818% { opacity: 1; -webkit-mask-position: -45px 0; mask-position: -45px 0; }
  20.7792% { opacity: 1; -webkit-mask-position: -54px 0; mask-position: -54px 0; }
  22.0779% { opacity: 1; -webkit-mask-position: -63px 0; mask-position: -63px 0; }
  24.6753% { opacity: 1; -webkit-mask-position: -72px 0; mask-position: -72px 0; }
  25.974% { opacity: 1; -webkit-mask-position: -81px 0; mask-position: -81px 0; }
  28.5714% { opacity: 1; -webkit-mask-position: 0px 0; mask-position: 0px 0; }
  31.1688% { opacity: 1; -webkit-mask-position: -9px 0; mask-position: -9px 0; }
  32.4675% { opacity: 1; -webkit-mask-position: -18px 0; mask-position: -18px 0; }
  35.0649% { opacity: 1; -webkit-mask-position: -27px 0; mask-position: -27px 0; }
  36.3636% { opacity: 1; -webkit-mask-position: -36px 0; mask-position: -36px 0; }
  38.961% { opacity: 1; -webkit-mask-position: -45px 0; mask-position: -45px 0; }
  42.8571% { opacity: 1; -webkit-mask-position: -54px 0; mask-position: -54px 0; }
  49.3506% { opacity: 1; -webkit-mask-position: -63px 0; mask-position: -63px 0; }
  55.8442% { opacity: 1; -webkit-mask-position: -72px 0; mask-position: -72px 0; }
  100% { opacity: 1; -webkit-mask-position: -72px 0; mask-position: -72px 0; }
}
.demo-drag-ui.is-on .dr-num b:nth-child(2) { animation: dr-c1 1.2833s step-end both; }
@keyframes dr-c2 {
  0% { opacity: 1; -webkit-mask-position: -90px 0; mask-position: -90px 0; }
  2.5974% { opacity: 0; -webkit-mask-position: -90px 0; mask-position: -90px 0; }
  12.987% { opacity: 1; -webkit-mask-position: -63px 0; mask-position: -63px 0; }
  14.2857% { opacity: 1; -webkit-mask-position: -9px 0; mask-position: -9px 0; }
  15.5844% { opacity: 1; -webkit-mask-position: -27px 0; mask-position: -27px 0; }
  18.1818% { opacity: 1; -webkit-mask-position: -18px 0; mask-position: -18px 0; }
  19.4805% { opacity: 1; -webkit-mask-position: -81px 0; mask-position: -81px 0; }
  20.7792% { opacity: 1; -webkit-mask-position: -54px 0; mask-position: -54px 0; }
  22.0779% { opacity: 1; -webkit-mask-position: -18px 0; mask-position: -18px 0; }
  23.3766% { opacity: 1; -webkit-mask-position: -72px 0; mask-position: -72px 0; }
  24.6753% { opacity: 1; -webkit-mask-position: -45px 0; mask-position: -45px 0; }
  25.974% { opacity: 1; -webkit-mask-position: -18px 0; mask-position: -18px 0; }
  27.2727% { opacity: 1; -webkit-mask-position: -72px 0; mask-position: -72px 0; }
  28.5714% { opacity: 1; -webkit-mask-position: -27px 0; mask-position: -27px 0; }
  29.8701% { opacity: 1; -webkit-mask-position: -72px 0; mask-position: -72px 0; }
  31.1688% { opacity: 1; -webkit-mask-position: -27px 0; mask-position: -27px 0; }
  32.4675% { opacity: 1; -webkit-mask-position: 0px 0; mask-position: 0px 0; }
  33.7662% { opacity: 1; -webkit-mask-position: -63px 0; mask-position: -63px 0; }
  35.0649% { opacity: 1; -webkit-mask-position: -36px 0; mask-position: -36px 0; }
  36.3636% { opacity: 1; -webkit-mask-position: 0px 0; mask-position: 0px 0; }
  37.6623% { opacity: 1; -webkit-mask-position: -45px 0; mask-position: -45px 0; }
  38.961% { opacity: 1; -webkit-mask-position: 0px 0; mask-position: 0px 0; }
  40.2597% { opacity: 1; -webkit-mask-position: -36px 0; mask-position: -36px 0; }
  41.5584% { opacity: 1; -webkit-mask-position: -72px 0; mask-position: -72px 0; }
  42.8571% { opacity: 1; -webkit-mask-position: -9px 0; mask-position: -9px 0; }
  45.4545% { opacity: 1; -webkit-mask-position: -36px 0; mask-position: -36px 0; }
  46.7532% { opacity: 1; -webkit-mask-position: -54px 0; mask-position: -54px 0; }
  48.0519% { opacity: 1; -webkit-mask-position: -72px 0; mask-position: -72px 0; }
  49.3506% { opacity: 1; -webkit-mask-position: -9px 0; mask-position: -9px 0; }
  50.6494% { opacity: 1; -webkit-mask-position: -18px 0; mask-position: -18px 0; }
  51.9481% { opacity: 1; -webkit-mask-position: -45px 0; mask-position: -45px 0; }
  53.2468% { opacity: 1; -webkit-mask-position: -63px 0; mask-position: -63px 0; }
  54.5455% { opacity: 1; -webkit-mask-position: -72px 0; mask-position: -72px 0; }
  55.8442% { opacity: 1; -webkit-mask-position: 0px 0; mask-position: 0px 0; }
  57.1429% { opacity: 1; -webkit-mask-position: -9px 0; mask-position: -9px 0; }
  59.7403% { opacity: 1; -webkit-mask-position: -18px 0; mask-position: -18px 0; }
  62.3377% { opacity: 1; -webkit-mask-position: -27px 0; mask-position: -27px 0; }
  63.6364% { opacity: 1; -webkit-mask-position: -36px 0; mask-position: -36px 0; }
  67.5325% { opacity: 1; -webkit-mask-position: -45px 0; mask-position: -45px 0; }
  71.4286% { opacity: 1; -webkit-mask-position: -54px 0; mask-position: -54px 0; }
  77.9221% { opacity: 1; -webkit-mask-position: -63px 0; mask-position: -63px 0; }
  85.7143% { opacity: 1; -webkit-mask-position: -72px 0; mask-position: -72px 0; }
  100% { opacity: 1; -webkit-mask-position: -72px 0; mask-position: -72px 0; }
}
.demo-drag-ui.is-on .dr-num b:nth-child(3) { animation: dr-c2 1.2833s step-end both; }
@keyframes dr-c3 {
  0% { opacity: 0; }
  2.5974% { opacity: 1; -webkit-mask-position: -90px 0; mask-position: -90px 0; }
  12.987% { opacity: 0; -webkit-mask-position: -90px 0; mask-position: -90px 0; }
  100% { opacity: 0; -webkit-mask-position: -90px 0; mask-position: -90px 0; }
}
.demo-drag-ui.is-on .dr-num b:nth-child(4) { animation: dr-c3 1.2833s step-end both; }
@keyframes dr-c4 {
  0% { opacity: 1; -webkit-mask-position: 0px 0; mask-position: 0px 0; }
  2.5974% { opacity: 0; -webkit-mask-position: 0px 0; mask-position: 0px 0; }
  12.987% { opacity: 1; -webkit-mask-position: -90px 0; mask-position: -90px 0; }
  100% { opacity: 1; -webkit-mask-position: -90px 0; mask-position: -90px 0; }
}
.demo-drag-ui.is-on .dr-num b:nth-child(5) { animation: dr-c4 1.2833s step-end both; }
@keyframes dr-c5 {
  0% { opacity: 0; }
  2.5974% { opacity: 1; -webkit-mask-position: 0px 0; mask-position: 0px 0; }
  3.8961% { opacity: 1; -webkit-mask-position: -9px 0; mask-position: -9px 0; }
  5.1948% { opacity: 1; -webkit-mask-position: -36px 0; mask-position: -36px 0; }
  6.4935% { opacity: 1; -webkit-mask-position: -72px 0; mask-position: -72px 0; }
  7.7922% { opacity: 1; -webkit-mask-position: -9px 0; mask-position: -9px 0; }
  10.3896% { opacity: 1; -webkit-mask-position: -18px 0; mask-position: -18px 0; }
  12.987% { opacity: 0; -webkit-mask-position: -18px 0; mask-position: -18px 0; }
  100% { opacity: 0; -webkit-mask-position: -18px 0; mask-position: -18px 0; }
}
.demo-drag-ui.is-on .dr-num b:nth-child(6) { animation: dr-c5 1.2833s step-end both; }
@keyframes dr-c6 {
  0% { opacity: 0; }
  7.7922% { opacity: 1; -webkit-mask-position: -27px 0; mask-position: -27px 0; }
  9.0909% { opacity: 1; -webkit-mask-position: -72px 0; mask-position: -72px 0; }
  10.3896% { opacity: 1; -webkit-mask-position: -27px 0; mask-position: -27px 0; }
  11.6883% { opacity: 1; -webkit-mask-position: -63px 0; mask-position: -63px 0; }
  12.987% { opacity: 1; -webkit-mask-position: -27px 0; mask-position: -27px 0; }
  15.5844% { opacity: 1; -webkit-mask-position: -36px 0; mask-position: -36px 0; }
  19.4805% { opacity: 1; -webkit-mask-position: -45px 0; mask-position: -45px 0; }
  24.6753% { opacity: 1; -webkit-mask-position: -54px 0; mask-position: -54px 0; }
  31.1688% { opacity: 1; -webkit-mask-position: -63px 0; mask-position: -63px 0; }
  37.6623% { opacity: 1; -webkit-mask-position: -72px 0; mask-position: -72px 0; }
  54.5455% { opacity: 1; -webkit-mask-position: -81px 0; mask-position: -81px 0; }
  100% { opacity: 1; -webkit-mask-position: -81px 0; mask-position: -81px 0; }
}
.demo-drag-ui.is-on .dr-num b:nth-child(7) { animation: dr-c6 1.2833s step-end both; }
@keyframes dr-c7 {
  0% { opacity: 0; }
  12.987% { opacity: 1; -webkit-mask-position: -9px 0; mask-position: -9px 0; }
  14.2857% { opacity: 1; -webkit-mask-position: -54px 0; mask-position: -54px 0; }
  15.5844% { opacity: 1; -webkit-mask-position: 0px 0; mask-position: 0px 0; }
  16.8831% { opacity: 1; -webkit-mask-position: -36px 0; mask-position: -36px 0; }
  18.1818% { opacity: 1; -webkit-mask-position: -63px 0; mask-position: -63px 0; }
  19.4805% { opacity: 1; -webkit-mask-position: 0px 0; mask-position: 0px 0; }
  20.7792% { opacity: 1; -webkit-mask-position: -18px 0; mask-position: -18px 0; }
  22.0779% { opacity: 1; -webkit-mask-position: -45px 0; mask-position: -45px 0; }
  23.3766% { opacity: 1; -webkit-mask-position: -63px 0; mask-position: -63px 0; }
  24.6753% { opacity: 1; -webkit-mask-position: 0px 0; mask-position: 0px 0; }
  25.974% { opacity: 1; -webkit-mask-position: -18px 0; mask-position: -18px 0; }
  27.2727% { opacity: 1; -webkit-mask-position: -45px 0; mask-position: -45px 0; }
  28.5714% { opacity: 1; -webkit-mask-position: -54px 0; mask-position: -54px 0; }
  29.8701% { opacity: 1; -webkit-mask-position: -72px 0; mask-position: -72px 0; }
  31.1688% { opacity: 1; -webkit-mask-position: 0px 0; mask-position: 0px 0; }
  32.4675% { opacity: 1; -webkit-mask-position: -9px 0; mask-position: -9px 0; }
  33.7662% { opacity: 1; -webkit-mask-position: -36px 0; mask-position: -36px 0; }
  35.0649% { opacity: 1; -webkit-mask-position: -54px 0; mask-position: -54px 0; }
  36.3636% { opacity: 1; -webkit-mask-position: -72px 0; mask-position: -72px 0; }
  37.6623% { opacity: 1; -webkit-mask-position: 0px 0; mask-position: 0px 0; }
  38.961% { opacity: 1; -webkit-mask-position: -9px 0; mask-position: -9px 0; }
  40.2597% { opacity: 1; -webkit-mask-position: -18px 0; mask-position: -18px 0; }
  41.5584% { opacity: 1; -webkit-mask-position: -27px 0; mask-position: -27px 0; }
  42.8571% { opacity: 1; -webkit-mask-position: -36px 0; mask-position: -36px 0; }
  45.4545% { opacity: 1; -webkit-mask-position: -45px 0; mask-position: -45px 0; }
  46.7532% { opacity: 1; -webkit-mask-position: -54px 0; mask-position: -54px 0; }
  48.0519% { opacity: 1; -webkit-mask-position: -63px 0; mask-position: -63px 0; }
  50.6494% { opacity: 1; -webkit-mask-position: -72px 0; mask-position: -72px 0; }
  53.2468% { opacity: 1; -webkit-mask-position: -81px 0; mask-position: -81px 0; }
  54.5455% { opacity: 1; -webkit-mask-position: 0px 0; mask-position: 0px 0; }
  59.7403% { opacity: 1; -webkit-mask-position: -9px 0; mask-position: -9px 0; }
  63.6364% { opacity: 1; -webkit-mask-position: -18px 0; mask-position: -18px 0; }
  70.1299% { opacity: 1; -webkit-mask-position: -27px 0; mask-position: -27px 0; }
  79.2208% { opacity: 1; -webkit-mask-position: -36px 0; mask-position: -36px 0; }
  100% { opacity: 1; -webkit-mask-position: -36px 0; mask-position: -36px 0; }
}
.demo-drag-ui.is-on .dr-num b:nth-child(8) { animation: dr-c7 1.2833s step-end both; }
@keyframes dr-arrow {
  0% { opacity: 0; }
  90.9091% { opacity: 1; }
  100% { opacity: 1; }
}
.demo-drag-ui.is-on .dr-arrow { animation: dr-arrow 1.2833s step-end both; }
/* @end-drag-timeline */
.plain-icon { margin: 0 0 18px; }
/* The wordmark carries the display type, but it is not the page's heading:
   the tagline is. Nobody searches for a brand they haven't heard of, so the
   <h1> goes on the line that says what Leafy actually does. Class selector,
   not `h1`, so the two can swap tags without the styling following them. */
.plain-hero .plain-wordmark {
  font-family: var(--font-display);
  font-size: clamp(2.75rem, 5vw, 4.375rem);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.05;
}
/* Two type levels only: the Leafy title, then one supporting-text style shared
   by the tagline and the paragraph below it. */
.plain-tagline,
.plain-sub {
  font-size: 1.25rem;
  font-weight: 500;
  /* Roomy enough that the keycap's rim and shadow clear the next line. */
  line-height: 1.85;
  letter-spacing: -0.01em;
  color: var(--text-primary);
}
.plain-tagline { margin-top: 14px; }
.plain-sub { margin: 16px 0 0; max-width: 560px; }
.plain-cta {
  margin-top: 30px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
}
/* Community invite: visible the moment the page opens, but a clear second
   tier to the green Download button — outlined pills, smaller than their
   .reach-pill default, sitting below the download meta line. */
.hero-community { margin-top: 26px; }
.hero-community-label {
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-primary);
  margin: 0 0 10px;
}
.hero-community .reach-links {
  justify-content: flex-start;
  gap: 10px;
}
.hero-community .reach-pill {
  padding: 7px 16px;
  font-size: 0.8125rem;
}
.hero-community .reach-pill svg { width: 16px; height: 16px; }

/* The gap from one section to the next. One value, read from the section
   above it, so every seam on the page is the same size: about a fifth of a
   desktop viewport, the way Apple's product pages breathe, and a third less
   on a phone where the same gap would read as an empty screen. Inside a
   section the gaps stay as they are (34px to a clip, 20px between cards), so
   the ladder holds: tight inside, loose between. */
:root {
  --section-gap: clamp(80px, 12.5vw, 180px);
  /* Heading to the thing it introduces, inside a section. */
  --head-gap: 64px;
}
@media (max-width: 720px) {
  :root { --head-gap: 40px; }
}
.plain-section {
  max-width: var(--page-w);
  margin: 0 auto;
  padding: var(--section-gap) var(--gutter) 0;
  text-align: left;
}
/* Tighten the gap between the hero and the first section below it. */
.plain-hero + .plain-section { padding-top: 12px; }
/* Set large and loosely tracked, the way the redesign (and Procreate's own
   pages) have it: 60px at -.005em, not a tight 36. */
.plain-section h2 {
  font-family: var(--font-display);
  font-size: clamp(2.125rem, 4.4vw, 3.75rem);
  font-weight: 600;
  letter-spacing: -0.005em;
  line-height: 1.1;
  max-width: 20ch;
  text-wrap: balance;
}
/* One-line section lead, shared voice with the hero sub. */
.section-lead {
  max-width: 44ch;
  margin: 22px 0 0;
  font-size: 1.25rem;
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: -0.01em;
  color: var(--text-body);
}
/* The library screenshot ships on its own black backdrop, so it floats
   straight on the page — no wrapper, border, or card. */
.library-shot {
  margin: 34px 0 0;
  max-width: 980px;
}
.library-shot img,
.library-shot video {
  display: block;
  width: 100%;
  height: auto;
}
/* A clip that carries a zoom track scales and slides while it plays. Sideways
   the push-in is meant to run off the edge of the browser and is not clipped
   here at all (the viewport does that, see body's overflow-x). Downwards it has
   to stop at this box, or a 1.4x window would drop ~290px over the section
   below, and reserving that room instead leaves the same ~290px standing empty
   every moment the clip is not at full push. So it stops here, and the mask
   below takes the edge off. */
/* The box is stretched sideways past the viewport on both sides, and pulled
   back the same distance, so it sits where it did while its border box is
   wider than the screen. That box is what the mask below is clipped to (a mask
   paints inside the border box and nowhere else), and a mask clipped to a
   980px box would shave the sideways push-in off at the figure's edges. The
   content box, and so the clip itself, stays 980px: content-box sizing keeps
   max-width off the padding. */
.library-shot--zoom {
  /* Where the picture ends: the reserve below it plus the trimmed rows. */
  --shot-cut: var(--shot-reserve, 56px);
  position: relative;
  box-sizing: content-box;
  padding-left: 100vw;
  padding-right: 100vw;
  margin-left: -100vw;
  margin-right: -100vw;
  clip-path: inset(0);
}
/* Room for the push-in to run into below the clip. */
.library-shot--zoom::after {
  content: "";
  display: block;
  padding-top: var(--shot-reserve, 68px);
}
/* The bottom edge handed over to the page instead of cut, by taking the
   picture away rather than by covering it.
   A band of page colour laid over the clip was the obvious way and the wrong
   one: it is only invisible while the thing behind it really is the page
   colour. Over the clip's own last rows it reads as a veil, it has to be
   redefined for every theme, and it fails outright the moment anything but
   flat page sits underneath. A mask has none of that. There is no layer: the
   figure's alpha ramps to zero over its bottom --shot-mask-h, so the clip
   dissolves into whatever the page puts there, in either theme, with nothing
   to see through.
   Two layers because a mask paints only where its image is: the ramp, and
   plain opaque above it, with the union of the two being the figure. The ramp
   runs out on the clip's own last row, not on the box's: below that row is the
   reserve, which the mask leaves out entirely, so whatever the push-in drives
   down there is already gone by the time it gets there.
   Both layers are 300vw wide and centred, so the sideways slack the push-in
   runs into is still there; a mask sized to the box would have cropped it back
   to the figure's 980px. */
html.nomask .library-shot--zoom {
  -webkit-mask-image: none !important;
  mask-image: none !important;
}
.library-shot--zoom {
  -webkit-mask-image: var(--shot-mask), linear-gradient(#000, #000);
  -webkit-mask-size: 300vw var(--shot-fade-len, 6%),
                     300vw calc(100% - var(--shot-cut));
  -webkit-mask-position:
    50% calc(100% - var(--shot-cut) + var(--shot-fade-len, 6%)), 50% 0;
  -webkit-mask-repeat: no-repeat, no-repeat;
  mask-image: var(--shot-mask), linear-gradient(#000, #000);
  mask-size: 300vw var(--shot-fade-len, 6%),
             300vw calc(100% - var(--shot-cut));
  mask-position:
    50% calc(100% - var(--shot-cut) + var(--shot-fade-len, 6%)), 50% 0;
  mask-repeat: no-repeat, no-repeat;
}
.library-shot--zoom video {
  transform-origin: 0 0;
}
/* Only while the clip is actually moving. will-change holds a layer the size
   of the frame open for as long as it is set, and these frames are large. */
.library-shot--zoom video.is-playing {
  will-change: transform;
}
/* The still that stands in for the clip until it can paint itself. It has to
   sit over the video rather than behind it: the clip is transparent outside
   the window it shows, and anything left behind it would show through as a
   second set of window edges once the push-in moves. Pinned to the content
   box, which the figure's 100vw of padding on either side puts inside its
   padding box. */
.library-shot--zoom .shot-poster {
  position: absolute;
  top: 0;
  left: 100vw;
  right: 100vw;
  width: auto;
}
/* The clip is opaque now (see index.html), so this is what cuts the window's
   rounded corners out of it, and the still has to be cut the same way. The
   radius is the window's own, as a share of each axis of the 1920x1400
   source. It also takes off the outermost row of the window's border, which
   is the brightest pixel in the frame and reads as a light dot at the corner
   once a phone draws the clip at half size. */
.library-shot--zoom video,
.library-shot--zoom .shot-poster { border-radius: 2.4% / 3.2%; }
/* Theme-swapped product screenshots (hero popup + word list). Default is
   dark; the light set shows only under data-theme="light". */
.shot-light { display: none; }
.shot-dark { display: block; }
:root[data-theme="light"] .shot-light { display: block; }
:root[data-theme="light"] .shot-dark { display: none; }

/* ── The library/review pager ───────────────────────────────────────────────
   Two product clips on one horizontal rail. The heading and the arrows sit in
   the column; the rail is full-bleed so a page can run to the edge of the
   window and the next one waits just past it. --pad is the distance from the
   window's edge to the column, so the first page lines up with the heading
   above it. --sbw is the vertical scrollbar's width (js/rail.js): 100vw
   counts it and the column does not, and without it the rail sits half a bar
   off. */
.rail-head {
  display: flex;
  align-items: flex-end;
  gap: 24px;
}
.rail-head .rail-intro {
  flex: 1;
  min-width: 0;
}
.rail-nav {
  display: flex;
  gap: 10px;
  flex: none;
  padding-bottom: 4px;
}
.rail-nav button {
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--bg-secondary);
  color: var(--text-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 0.2s, opacity 0.2s;
}
.rail-nav button:hover { background: var(--separator-light); }
.rail-nav button[disabled] {
  opacity: 0.32;
  cursor: default;
  background: var(--bg-secondary);
}
.rail-nav svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
}
.rail {
  --pad: max(var(--gutter), calc((100vw - var(--sbw, 0px) - var(--page-w)) / 2 + var(--gutter)));
  margin-top: var(--head-gap);
  overflow-x: scroll;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  /* No snapping on the card rails: a drag coasts to a stop wherever its
     speed runs out, the way superwhisper's card row does, and nothing pulls
     it onto a card afterwards. scroll-padding is still what tells the arrows
     where the column edge is. */
  scroll-padding-inline: var(--pad);
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.rail::-webkit-scrollbar { display: none; }
/* Draggable with a mouse (js/rail.js): the hand says so, and text or an
   image inside must not start a selection or a native drag instead. */
.rail { cursor: grab; }
.rail.is-dragging { cursor: grabbing; user-select: none; -webkit-user-select: none; }
/* Snapping off while the hand is on the rail and while it is settling
   afterwards (js/rail.js runs that animation itself); otherwise the browser
   cuts the ease short with a jump of its own. */
.rail.is-dragging,
.rail.is-settling { scroll-snap-type: none; }
.rail.is-dragging * { cursor: grabbing; }
.rail:focus-visible { outline: 2px solid var(--green); outline-offset: -2px; }
.rail-track {
  display: flex;
  gap: var(--gutter);
  padding-inline: var(--pad);
  width: fit-content;
}
.rail-track > * { scroll-snap-align: start; }
/* Apple's caption tile: the picture is the card, the words sit under it on the
   page. Sized off the window so the third card is always cut, which is what
   says there is more to the right. */
.tile {
  flex: 0 0 auto;
  width: min(44vw, 640px);
  display: flex;
  flex-direction: column;
}
.tile-media {
  position: relative;
  flex: none;
  aspect-ratio: 4 / 3;
  background: var(--card);
  border-radius: 26px;
  overflow: hidden;
}
.tile-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.tile-note {
  margin: 22px 0 0;
  font-size: 1.0625rem;
  line-height: 1.45;
  letter-spacing: -0.01em;
  color: var(--text-secondary);
}
.tile-note b {
  font-weight: 600;
  color: var(--text-primary);
}
/* The rail carries its own gap to the heading; the section above it holds only
   the heading and the arrows. */
#where { padding-bottom: 0; }
/* A page is the whole window wide, with the column's side gap as padding
   inside it, so its box runs from one edge of the window to the other. That
   box is what cuts the push-in (overflow: clip): the clip inside runs off the
   window's edges, the way it did before it lived on a rail, and stops there,
   so nothing it does adds to the rail's scrollable width. The next page
   starts right at the window's edge, out of sight until the arrow is pressed. */
.pager .rail-track {
  gap: 0;
  padding-inline: 0;
}
/* The pager is two whole screens, not a row of cards: half of one is never a
   resting place, so it alone snaps. */
.pager { scroll-snap-type: x mandatory; }
.pager .page {
  flex: 0 0 auto;
  box-sizing: border-box;
  width: calc(100vw - var(--sbw, 0px));
  padding-left: var(--pad);
  overflow: clip;
}
/* The clip, sized by its own ratio so the still and the clip stack on the
   same rectangle and nothing reflows when one hands over to the other. The
   bottom is handed over to the page by a mask that takes the picture away
   (no colour to get wrong, so it works in either theme): near opaque for the
   first stretch, all of its work in the bottom two fifths, which is the strip
   the three notes sit on. */
/* The notes are out of flow, so the faded strip of clip below them is the
   only thing holding the rail open at the bottom. The rail is a scroll box and
   cuts at its own edge, so nothing inside it is pulled up: the whole rail is
   pulled back instead, by a share of the clip's width, and the next section
   sits over the strip where the picture has already gone. Nothing gets cut. */
.pager {
  --shot-w: min(calc(var(--page-w) - var(--gutter) * 2),
                calc(100vw - var(--sbw, 0px) - var(--pad)));
  margin-bottom: calc(var(--shot-w) * -0.24);
}
.pager-shot {
  /* 40px in from the column on either side, the way the redesign has it (there
     it is the browser's own figure margin, never reset): the clip sits inside
     the heading's column rather than flush with it, and the notes below keep
     the column's full width. */
  margin: 0 40px;
  position: relative;
  /* The masked-off strip at the bottom is still a box, and it sits over the
     next section's heading once the rail is pulled back. Nothing in the clip
     is clickable, so let the heading be. A sideways drag still reaches the
     rail from the page underneath. */
  pointer-events: none;
  aspect-ratio: 1920 / 1400;
  overflow: hidden;
  border-radius: 2.4% / 3.2%;
  background: var(--bg-secondary);
  -webkit-mask-image: var(--pager-mask);
  mask-image: var(--pager-mask);
}
:root {
  --pager-mask: linear-gradient(to bottom,
    rgb(0, 0, 0) 18%,
    rgba(0, 0, 0, 0.86) 28%,
    rgba(0, 0, 0, 0.66) 36%,
    rgba(0, 0, 0, 0.44) 44%,
    rgba(0, 0, 0, 0.24) 51%,
    rgba(0, 0, 0, 0.10) 57%,
    rgba(0, 0, 0, 0.02) 62%,
    rgba(0, 0, 0, 0) 67%,
    rgba(0, 0, 0, 0) 100%);
}
.pager-shot video,
.pager-shot .shot-poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.shot-wrap {
  position: relative;
  max-width: calc(var(--page-w) - var(--gutter) * 2);
}
/* The pointer layer over the folder clip. Positioned in source pixels scaled
   to the box (js/rail.js), so it lands on the same spot the take had it,
   whatever width the clip is. */
.clip-cursor {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  pointer-events: none;
  will-change: transform;
  z-index: 2;
}
/* Sat on the faded strip rather than hung above it: pinned to the bottom of
   the clip, so the last line of the notes lands where the picture has already
   gone. Three-up: a glyph in the accent, then one paragraph where the title
   runs straight into the body, bold into grey. No boxes. */
.notes-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 32%;
  z-index: 3;
}
.notes-row > div { text-align: left; }
/* One tint per note. Its own three, not the step tiles' purple, orange and
   cyan, so the rail is not wearing the section above's colours. */
.notes-row > div:nth-child(1) { --tint: #f5c542; }
.notes-row > div:nth-child(2) { --tint: #ff9500; }
.notes-row > div:nth-child(3) { --tint: #5b8cff; }
.note-icon {
  display: block;
  width: 32px;
  height: 32px;
  color: var(--tint, var(--green));
  margin-bottom: 18px;
}
.note-icon svg {
  width: 100%;
  height: 100%;
  display: block;
}
.notes-row p {
  margin: 0;
  font-size: 1.0625rem;
  line-height: 1.45;
  letter-spacing: -0.01em;
  color: var(--text-secondary);
}
.notes-row b {
  font-weight: 600;
  color: var(--text-primary);
}
/* ── Reviews ────────────────────────────────────────────────────────────────
   One account carries the section and the other three sit under it. The lead
   is a third-year medical student on clinical rotations, and her review is
   printed whole rather than clamped, because the detail is the argument: the
   abbreviations, the five days, the Anki deck. The line that lands hardest is
   pulled out beside it in display type and stays put while the story scrolls
   past, so the section has one thing to read and one thing to remember.
   Nothing here is a tile. The three underneath are quotes on the page, set
   off by a hairline and their own column, the way a pull quote sits in print;
   the rest of each opens in a dialog (js/rail.js). Grey prose with the lines
   that matter picked out in full white, the way a long quote is read, rather
   than every word at the same weight. */
/* The one heading on the page set in the middle. The pulled line under it is
   big type on the left edge, and a heading on that same edge read as more of
   the same; from the centre it is plainly the section's name, and the quote
   plainly the first thing in it. */
#reviews > h2 {
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}
.review-lead {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: 64px;
  margin-top: var(--head-gap);
}
/* The quote holds while the story moves. 108px clears the pinned local nav. */
.lead-aside {
  position: sticky;
  top: 108px;
  align-self: start;
}
.lead-pull {
  margin: 0;
  font-size: clamp(1.75rem, 3vw, 2.5rem);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  text-wrap: balance;
}
.lead-who {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 28px 0 0;
}
.lead-who .review-avatar { width: 44px; height: 44px; }
/* A measure that stays readable: the story is long, so the line length is the
   thing that decides whether anyone finishes it. */
.lead-story { max-width: 62ch; }
.lead-story p {
  margin: 0;
  font-size: 1rem;
  line-height: 1.62;
  color: var(--text-secondary);
}
.lead-story p + p { margin-top: 18px; }
/* Desktop reads the whole story beside the held quote. The way in is a phone
   thing only. */
.lead-more { display: none; }
/* The three shorter ones, as tiles under the lead. Three across, all the same
   height whatever the quote's length, so the row reads as one block rather
   than a ragged edge. */
.reviews {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
  margin-top: 88px;
}
.review {
  display: flex;
  flex-direction: column;
  padding: 30px 28px 24px;
  border-radius: 22px;
  background: var(--card);
  transition: transform 0.28s cubic-bezier(0.2, 0.7, 0.3, 1);
}
/* The line worth carrying off the tile, across the top. It is the strongest
   sentence in the review rather than whatever the poster typed in the title
   field, and the body no longer repeats it, so nothing is said twice. */
.review h3 {
  margin: 0 0 14px;
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.005em;
  line-height: 1.32;
  text-wrap: balance;
}
.review-body {
  flex: 1;
  min-height: 0;
  overflow: hidden;
}
.review-body p {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--text-secondary);
}
.review-body p + p { margin-top: 12px; }
/* The column shows the opening paragraph whole; the rest is in the dialog. */
.review-body > p:nth-child(n+2) { display: none; }
.lead-story b,
.review b,
.review-modal-body b {
  color: var(--text-primary);
  font-weight: 600;
}
/* Who said it, and the way in. One row along the bottom of every column: the
   attribution on the left, the weak action on the right, plain text rather
   than a boxed button. */
.review-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: auto;
  padding-top: 22px;
}
.review-who {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.review-avatar {
  flex: none;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--film-mid);
  object-fit: cover;
}
.review-name {
  display: flex;
  flex-direction: column;
  min-width: 0;
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.25;
  color: var(--text-primary);
}
.review-src {
  font-size: 0.8125rem;
  font-weight: 400;
  color: var(--text-secondary);
}
.review-more {
  flex: none;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 0.9375rem;
  color: var(--text-secondary);
  cursor: pointer;
  transition: color 0.18s ease;
}
.review-more::after { content: " \2026"; }
.review-more:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 3px;
  border-radius: 3px;
}
/* Hover lifts the tile, and the way in brightens with it. Transform only:
   changing width or height would reflow the row and shove the others about.
   Only a tile with more to show lifts or takes a click; written both ways so
   the pointer is right before the script has run. */
.review.is-clickable,
.review:has([data-review-more]) { cursor: pointer; }
.review.is-clickable:hover,
.review:has([data-review-more]):hover { transform: scale(1.03); }
.review-more:hover,
.review.is-clickable:hover .review-more,
.review:has([data-review-more]):hover .review-more { color: var(--text-primary); }
@media (prefers-reduced-motion: reduce) {
  .review { transition: none; }
  .review:hover { transform: none; }
  .review-more { transition: none; }
}

/* The full account, over the page. A native <dialog>, so Escape and the
   backdrop come for free. Nothing in it is cut: the clamp belongs to the
   card. */
.review-modal {
  /* The page resets margins, and margin: auto is what centres a dialog. */
  margin: auto;
  width: min(760px, calc(100vw - var(--gutter) * 2));
  max-height: min(82vh, 900px);
  padding: 0;
  border: 1px solid var(--separator-light);
  border-radius: 22px;
  background: var(--card);
  color: var(--text-primary);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.45);
  overflow: hidden;
}
.review-modal::backdrop {
  background: rgba(0, 0, 0, 0.55);
  -webkit-backdrop-filter: saturate(140%) blur(20px);
  backdrop-filter: saturate(140%) blur(20px);
}
.review-modal-body {
  max-height: min(82vh, 900px);
  overflow-y: auto;
  padding: 56px 56px 52px;
  scrollbar-width: thin;
  scrollbar-color: var(--film-mid) transparent;
}
/* The same slim bar the page has, sitting on the card colour rather than the
   page colour, so it is a scroll reference and not a white stripe. */
.review-modal-body::-webkit-scrollbar { width: 10px; }
.review-modal-body::-webkit-scrollbar-track { background: transparent; }
.review-modal-body::-webkit-scrollbar-thumb {
  background: var(--film-mid);
  border-radius: 999px;
  border: 2px solid var(--card);
}
.review-modal-body::-webkit-scrollbar-thumb:hover { background: var(--film-strong); }
.review-modal-body h3 {
  margin: 0 0 20px;
  font-size: 1.75rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.2;
}
.review-modal-body .review-body {
  overflow: visible;
}
.review-modal-body .review-body > p {
  display: block;
  font-size: 1.0625rem;
  line-height: 1.55;
}
.review-modal-body p + p { margin-top: 16px; }
.review-modal-body .review-foot { padding-top: 28px; }
.review-modal-body .review-more { display: none; }
/* The lead opens with its quote on top; nothing holds inside a scrolling box. */
.review-modal-body .lead-aside { position: static; }
.review-modal-body .lead-story { max-width: none; }
.review-close {
  position: absolute;
  top: 18px;
  right: 18px;
  z-index: 2;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--film-mid);
  color: var(--text-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.review-close:hover { background: var(--separator); }
.review-close svg {
  width: 15px;
  height: 15px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
}
@media (max-width: 900px) {
  /* Two columns of three would leave one quote hanging on its own row, so the
     three go under each other, and the pulled line goes above the story it
     came out of instead of beside it. Nothing sticks on a short screen. */
  /* Nothing holds beside the story on a phone, so the two stack and the
     pulled line becomes the opening rather than a margin note. Block, not a
     one-column grid: these gaps are not the column gap. */
  .review-lead { display: block; cursor: pointer; }
  .lead-aside { position: static; }
  .lead-pull { font-size: clamp(1.75rem, 7.4vw, 2.25rem); }
  .lead-who { margin-top: 22px; }
  .lead-story { max-width: none; margin-top: 30px; }
  /* Seven paragraphs in a phone's width read as an essay. About ten lines of
     it, taken away at the bottom rather than cut off square, so the edge
     itself says there is more; the whole account is in the dialog the quotes
     below use. */
  .review-lead .lead-story {
    max-height: 16.2em;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(to bottom, rgb(0, 0, 0) 56%, rgba(0, 0, 0, 0) 100%);
    mask-image: linear-gradient(to bottom, rgb(0, 0, 0) 56%, rgba(0, 0, 0, 0) 100%);
  }
  .review-lead .lead-more { display: block; margin-top: 16px; }
  /* Three grey tiles stacked is the shape of every other phone page. Plain
     quotes on the black page instead, held apart by the space between them,
     so nothing is in a box and no rule is drawn beside them. */
  .reviews {
    grid-template-columns: 1fr;
    gap: 0;
    margin-top: 52px;
  }
  .review {
    background: none;
    border-radius: 0;
    padding: 0;
  }
  .review + .review { margin-top: 42px; }
  /* A phone has no pointer, but a touch leaves :hover stuck on the tile, and
     there is no tile left to lift. Written both ways to out-weigh the lift. */
  .review.is-clickable:hover,
  .review:has([data-review-more]):hover { transform: none; }
  .review-foot { padding-top: 16px; }
  .review-modal-body { padding: 44px 26px 34px; }
}

@media (max-width: 720px) {
  /* The arrows stay on a phone: nothing of the next page shows, so without
     them there would be no sign there is one. A drag works too. */
  .rail { margin-top: 40px; }
  .rail-nav { padding-bottom: 0; }
  .tile { width: 78vw; }
  .tile-note { font-size: 1rem; margin-top: 16px; }
  /* Three columns in a phone's width, over a fading clip, is unreadable.
     Below the clip, one under the other, and the clip keeps its bottom. */
  .pager-shot { -webkit-mask-image: none; mask-image: none; margin: 0; }
  .pager { margin-bottom: 0; }
  /* A page carries the side gap on the left only, so the next one starts at
     the window's edge. On a phone nothing of the next page shows anyway, and
     without the matching gap on the right the clip sits off to one side. */
  .pager .page { padding-right: var(--pad); }
  .notes-row {
    grid-template-columns: 1fr;
    position: static;
    margin-top: 28px;
    gap: 24px;
  }
}

/* Apple's feature cards: a tinted glyph up top, then one bold line where the
   action carries the tint and the rest stays white. One tint per card.
   Measured off Apple's own tiles at 2x: 1.5:1, 26px corners, 36/32 padding,
   24/28 type, a 36px glyph 30px above the line, 20px between tiles. */
.steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  margin-top: var(--head-gap);
  text-align: left;
}
.step {
  background: var(--card);
  border-radius: 26px;
  padding: 36px 32px;
  aspect-ratio: 1.5;
}
.step--a { --tint: #9d85ff; }
.step--b { --tint: #ff8a3d; }
.step--c { --tint: #4ac4dc; }
.step-icon {
  display: block;
  width: 36px;
  height: 36px;
  color: var(--tint);
  margin-bottom: 30px;
}
.step-icon svg {
  width: 100%;
  height: 100%;
  display: block;
}
.step-line {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 600;
  line-height: 1.17;
  letter-spacing: -0.012em;
  color: var(--text-primary);
}
.step-line b {
  font-weight: inherit;
  color: var(--tint);
}
/* The cap sits on a 24px line set tight (1.17), so it has to be smaller and
   sit lower than a keycap in body copy, or its side and shadow drop onto the
   line underneath. */
.step-line kbd {
  font-size: 0.66em;
  vertical-align: 0.12em;
  padding: 0.2em 0.5em 0.24em;
  box-shadow:
    inset 0 0.09em 0 var(--key-gloss),
    0 0.16em 0 var(--key-rim),
    0 0.22em 0 var(--key-rim-deep),
    0 0.3em 0.55em var(--key-shadow);
}
.step-line kbd:hover {
  box-shadow:
    inset 0 0.09em 0 var(--key-gloss),
    0 0.05em 0 var(--key-rim-deep),
    0 0.14em 0.4em var(--key-shadow);
}

/* Spec sheet: questions on the left, the answer on the right as a grid of
   short items. Click, not hover, and all four answers stacked in one grid cell
   so the block keeps the height of the longest. */
.spec-switch {
  display: grid;
  grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
  column-gap: clamp(32px, 4vw, 64px);
  align-items: start;
  margin: 40px 0 0;
  text-align: left;
}
.spec-index {
  position: relative;
  border-left: 1px solid var(--separator-light);
}
.spec-tab {
  display: flex;
  align-items: baseline;
  gap: 8px;
  width: 100%;
  appearance: none;
  background: none;
  border: 0;
  padding: 15px 0 15px clamp(16px, 1.8vw, 22px);
  font: inherit;
  font-family: var(--font-display);
  font-size: clamp(1.0625rem, 1.4vw, 1.25rem);
  font-weight: 400;
  line-height: 1.3;
  letter-spacing: -0.015em;
  text-align: left;
  color: var(--text-secondary);
  cursor: pointer;
  transition: color 0.18s ease;
}
/* The chevron points at the column the answer lands in, and is the part that
   says "clickable" — the row is otherwise just a line of text. */
.spec-tab::after {
  content: "›";
  font-size: 1.15em;
  line-height: 1;
  opacity: 0.4;
  transition: transform 0.18s ease, opacity 0.18s ease, color 0.18s ease;
}
.spec-tab:hover { color: var(--text-primary); }
.spec-tab:hover::after { opacity: 0.8; transform: translateX(3px); }
.spec-tab.is-active { color: var(--text-primary); font-weight: 700; }
.spec-tab.is-active::after { opacity: 1; color: var(--green); transform: translateX(3px); }
.spec-tab:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 2px;
  border-radius: 3px;
}

.spec-panels {
  display: grid;
  align-self: center;
}
.spec-panel {
  grid-area: 1 / 1;
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition: opacity 0.24s ease, transform 0.24s ease, visibility 0.24s;
}
.spec-panel.is-active {
  opacity: 1;
  visibility: visible;
  transform: none;
}
/* The answer itself: short items on a grid, so they line up in columns down
   the block instead of running on as a sentence. */
.spec-facts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: clamp(20px, 2.4vw, 36px);
  row-gap: 10px;
  max-width: 40em;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 1.0625rem;
  line-height: 1.5;
  color: var(--text-body);
}
/* Sentences rather than single words: two columns, or they wrap to shreds. */
.spec-facts--wide { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* A link in one of these lists sits among plain facts, so it is set in the
   same colour as them: one green item in a list of eleven language names
   reads as a highlight on that language rather than as a way through. It
   picks up its colour under the pointer instead. */
.spec-facts a {
  color: inherit;
  text-decoration: none;
  transition: color 0.18s ease;
}
.spec-facts a:hover { color: var(--green); }

@media (max-width: 760px) {
  .spec-switch {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 22px;
  }
  .spec-index {
    display: flex;
    flex-wrap: wrap;
    column-gap: clamp(16px, 4vw, 28px);
    border-left: 0;
    border-bottom: 1px solid var(--separator-light);
  }
  .spec-tab { width: auto; font-size: 1.0625rem; padding: 0 0 12px; }
  .spec-tab::after { display: none; }
  .spec-panels { align-self: start; }
  .spec-facts, .spec-facts--wide {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    max-width: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .spec-panel { transition: opacity 0.01s; transform: none; }
}

.pricing-more {
  margin-top: 30px;
  font-size: 1.0625rem;
  color: var(--text-body);
}
.pricing-more + .pricing-more { margin-top: 10px; }
.pricing-more--quiet {
  font-size: 0.9375rem;
  color: var(--text-secondary);
}
.pricing-more a {
  color: var(--green);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.pricing-more a:hover { border-bottom-color: currentColor; }

/* Headings use the Fraunces serif everywhere (matching the live site); body
   and UI stay on the system font. */
.plain .dl-panel h2 {
  font-family: var(--font-display);
  letter-spacing: -0.02em;
}
.plain .dl-btn,
.plain .sign-prompt,
.plain .dl-divider span,
.plain .reach-heading,
.plain .feedback-input,
.plain .sign-input {
  font-family: var(--font);
  font-style: normal;
}
.plain .feedback-input::placeholder,
.plain .sign-input::placeholder {
  font-style: normal;
}

/* Flat green pill for the download / send buttons on plain pages, the size
   the rest of the page is set in. */
.plain .dl-btn,
.story-cta .dl-btn {
  padding: 11px 24px;
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  border: none;
  border-radius: 980px;
  transform: none;
  color: var(--green-on);
  background: var(--green-fill);
  box-shadow: none;
  transition: background 0.2s ease;
}
.plain .dl-btn:hover,
.story-cta .dl-btn:hover {
  color: var(--green-on);
  background: var(--green-hover);
}
.plain .dl-btn svg,
.story-cta .dl-btn svg { width: 17px; height: 17px; }
/* Feedback "Send" on the plain page: the Download keycap, one size down.
   It is the form's action, so it should read as the same kind of object as
   the page's main action; as bare green text it read as a link that happened
   to sit at the end of the row. Everything but size comes from
   .plain .dl-btn above — the rules here exist only to stop the base
   .feedback-send-btn from undoing it. */
.plain .feedback-send-wrap {
  align-self: flex-end;
  width: auto;
  min-height: 0;
  margin: 0 0 4px;
}
.plain .feedback-send-wrap::after { display: none; }
.plain .feedback-send-btn {
  position: static;
  inset: auto;
  width: auto;
  height: auto;
  min-height: 0;
  flex-direction: row;
  gap: 9px;
  padding: 11px 24px 13px;
  font-family: var(--font);
  font-weight: 600;
  font-size: 0.9375rem;
  letter-spacing: -0.01em;
}
/* The gap no longer opens on hover: the key going down is the feedback now,
   and two things moving at once reads as a glitch. */
.plain .feedback-send-btn:hover,
.plain .feedback-send-btn:active { gap: 9px; }
.plain .feedback-send-btn svg { width: 22px; height: 13px; }
.plain .feedback-send-btn:focus-visible { border-radius: 980px; outline-offset: 3px; }
.plain .story-hero { padding: 56px var(--gutter) 32px; }
.plain .story-hero h1 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2rem, 4vw, 2.75rem);
  letter-spacing: -0.02em;
}

@media (max-width: 860px) {
  .plain-hero {
    grid-template-columns: minmax(0, 1fr);
    gap: 40px;
  }
  .plain-hero-visual { max-width: 420px; }
}
@media (max-width: 700px) {
  .plain-hero {
    padding-top: calc(var(--nav-h) + 40px);
    text-align: center;
  }
  .plain-hero-main {
    margin: 0 auto;
    text-align: center;
  }
  .plain-hero-visual { max-width: 380px; margin: 0 auto; }
  .plain-hero-visual .demo-card { text-align: left; }
  .plain-sub { margin: 14px auto 0; }
  .plain-hero-main p,
  .plain-hero-main h1,
  .plain-hero-main .dl-meta {
    text-align: center;
  }
  .plain-icon { margin: 0 auto 18px; }
  .plain-cta { align-items: center; }
  .hero-community { align-self: center; }
  .hero-community .reach-links { justify-content: center; }
  .steps { grid-template-columns: 1fr; }
  .step { aspect-ratio: auto; }
  /* The cards grow to their text here, so the line carrying the keycap can
     have the leading it needs instead of the cap crowding the line below. */
  .step-line { line-height: 1.38; }
}


/* ============================================================
   DOWNLOAD PAGE (download.html)
   Reuses the homepage download panel for the hero, so the icon,
   button and meta line are the ones people already saw there.
   ============================================================ */
.download-hero { padding-bottom: 8px; }
.download-hero .dl-panel { margin-bottom: 0; }
.download-hero h1 { margin-top: 0; }
.download-hero .story-hero-lead {
  max-width: 48ch;
  margin: 0 auto;
}
.download-page .story-section:first-child { border-top: none; }
/* Same body colour about.html uses. The default .story-section grey is meant
   for a long read; this page is three short answers and needs to be readable
   at a glance. */
/* Article CTAs used .btn-primary, which is hard coded white on black and so
   went invisible in light mode. They use the real download button now; this
   just gives it room and keeps the tilted shadow from clipping. */
.story-cta {
  margin: 32px 0 0;
  padding-bottom: 10px;
}
.story-cta .dl-btn-wrap { display: inline-block; }
.story-cta .dl-btn {
  color: var(--cta-ink);
  background: var(--cta-face);
}
.story-cta .dl-btn:hover {
  color: var(--cta-ink);
  background: var(--cta-face);
}

.download-page .story-section p { color: var(--text-body); }
/* ⌥ and A are different widths, so padding alone makes two keycaps of
   different sizes sitting next to each other. Give both a floor and centre
   the glyph, the way a real keyboard has one key size. */
/* The one thing people hesitate over, on every page rather than only the one
   that explains it. Sits above the logo row, quiet but not hidden. */
.footer-note {
  font-size: 0.8125rem;
  line-height: 1.6;
  color: var(--text-secondary);
  margin: 0;
}

.download-page h2 kbd {
  /* min-width only sets a floor, so the wider glyph still won the argument.
     Fixed box, glyph centred inside it, padding out of the way. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 1.95em;
  height: 1.7em;
  padding: 0;
  line-height: 1;
}

/* ── Help reads in black and white ──────────────────────────────────────
   No accent anywhere inside an article: a link is marked by the rule under
   it, the current item by weight, and a hover by an underline. The site's
   lime is still on the nav CTA, which belongs to the site rather than to
   the page. */
.help-panel p a,
.help-panel li a,
.help-callout a,
.help-security-notice a {
  color: var(--text-primary);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: var(--separator);
  transition: text-decoration-color 0.14s ease;
}
.help-panel p a:hover,
.help-panel li a:hover,
.help-callout a:hover,
.help-security-notice a:hover { text-decoration-color: currentColor; }

/* Template download: a solid button, in ink rather than lime. */
.help-dl-btn {
  background: var(--text-primary);
  color: var(--bg);
}
.help-dl-btn:hover {
  background: var(--text-primary);
  color: var(--bg);
  opacity: 0.85;
}

/* Hover on the pager: the name underlines. Colour is no longer available to
   say "this one", and bolding it would reflow the line. */
.help-pager-link:hover .help-pager-name {
  color: var(--text-primary);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.help-search-corner { border-color: var(--text-primary); }

@media (max-width: 760px) {
  /* In the sheet the rows are 22px tall and the current one used to be lime.
     Every row goes quiet instead, and the one you are on stays full strength. */
  .help-toc-toggle svg { color: var(--text-primary); }
  .help-sidebar-close { color: var(--text-primary); }
  .help-nav-link { color: var(--text-secondary); }
  .help-nav-link.is-active,
  .help-nav-link.is-active:hover { color: var(--text-primary); }
}
