/* ─────────────────────────────────────────────────────────────────────────────
   Dovilo design language: the stylesheet.

   docs/design-language.md is the spec; this file implements it and nothing
   else. Every page, the generated blog pages included, links this file and
   no other stylesheet, and carries no <style> block. If a page needs
   something this file does not have, the doc gets a new component first.

   Layers, in order:
     1. Font         Inter, self-hosted from /fonts/
     2. Reset
     3. Tokens       colour (two themes), type, space, shape, motion
     4. Elements     body, headings, text, links, code, media, focus
     5. Layout       container, section, split, flow
     6. Components   in the order the doc lists them
     7. Utilities
     8. Motion

   Themes: light tokens sit on :root, dark ones under [data-theme="dark"]. Light is
   the default whatever the OS prefers; the dark theme is a visitor's explicit
   choice (the toggle), which the inline boot script applies before first paint. The
   same selectors work on any element, which is how /styleguide/ renders each
   component in both themes on one page.
   ───────────────────────────────────────────────────────────────────────────── */

/* ─── 1. FONT ─── */
@font-face{
  font-family:'Inter';font-style:normal;font-weight:100 900;font-display:swap;
  src:url('/fonts/inter-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'Inter';font-style:normal;font-weight:100 900;font-display:swap;
  src:url('/fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

/* ─── 2. RESET ─── */
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}

/* ─── 3. TOKENS ───
   Five neutrals per theme and one accent. Every colour on a page is one of
   these seven; there are no alphas to invent. */
:root,[data-theme="light"]{
  color-scheme:light;
  --bg:#FAF8F4;
  --surface:#FFFFFF;
  --border:#DED9D1;
  --text-muted:#6F6B66;
  --text:#1C1B19;
  --toggle-sun:block;
  --toggle-moon:none;
}
:root[data-theme="dark"],[data-theme="dark"]{
  color-scheme:dark;
  --bg:#171717;
  --surface:#1F1E1D;
  --border:#2E2D2B;
  --text-muted:#9A958E;
  --text:#ECEAE6;
  --toggle-sun:none;
  --toggle-moon:block;
}

:root{
  /* The app's amber, identical in both themes. It fills primary buttons and
     the 01-04 numerals and is never used as text colour: on the light
     background it reads at 2.7:1, so it always carries --on-accent text. */
  --accent:#D4880A;
  --on-accent:#1C1B19;

  /* Type */
  --font-sans:'Inter',system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  --font-mono:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation Mono',monospace;
  --text-hero:clamp(2.25rem,1.5rem + 3vw,3.5rem); /* 36-56px; the rem term keeps text zoom working */
  --text-h2:clamp(1.75rem,1.5rem + 1vw,2rem);     /* 28-32px */
  --text-h3:1.25rem;                              /* 20px */
  --text-body:1.0625rem;                          /* 17px */
  --text-small:.875rem;                           /* 14px */
  --text-code:.875rem;                            /* 14px, mono */
  --lh-heading:1.15;
  --lh-body:1.55;
  --measure:64ch;

  /* Space: an 8px grid, nothing between the steps. */
  --space-1:8px;
  --space-2:16px;
  --space-3:24px;
  --space-4:32px;
  --space-5:48px;
  --space-6:64px;
  --space-7:96px;
  --section-y:var(--space-7);
  --container:1080px;
  --gutter:24px;

  /* Shape and chrome */
  --radius:8px;
  --nav-h:64px;

  /* Motion: hover and focus only. */
  --duration:150ms;
}
@media (max-width:767px){
  :root{--section-y:var(--space-6);--gutter:16px}
}

/* ─── 4. ELEMENTS ─── */
html{
  -webkit-text-size-adjust:100%;
  /* Anchors land below the sticky nav, not under it. */
  scroll-padding-top:calc(var(--nav-h) + var(--space-2));
}
body{
  font-family:var(--font-sans);
  font-size:var(--text-body);
  line-height:var(--lh-body);
  background:var(--bg);
  color:var(--text);
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
}
[data-theme]{background:var(--bg);color:var(--text)}

h1,h2,h3,h4{color:var(--text);line-height:var(--lh-heading);font-weight:600;text-wrap:balance}
h1{font-size:var(--text-hero);font-weight:700;letter-spacing:-.025em}
h2{font-size:var(--text-h2);letter-spacing:-.02em}
h3{font-size:var(--text-h3);letter-spacing:-.01em}
h4{font-size:var(--text-body)}

p{max-width:var(--measure);text-wrap:pretty}
strong,b{font-weight:600}
small{font-size:var(--text-small)}
ul,ol{padding-left:1.5em}
li{max-width:var(--measure)}
/* Only blog posts have these today: Markdown's --- and > from Sanity. */
hr{height:0;border:0;border-top:1px solid var(--border)}
blockquote{padding-left:var(--space-2);border-left:1px solid var(--border);color:var(--text-muted)}

a{
  color:inherit;
  text-decoration:underline;
  text-decoration-color:var(--text-muted);
  text-decoration-thickness:1px;
  text-underline-offset:.2em;
  transition:text-decoration-color var(--duration),color var(--duration);
}
a:hover{text-decoration-color:currentColor}

code,kbd,pre{font-family:var(--font-mono);font-size:var(--text-code)}
:not(pre) > code{
  padding:0 .3em;
  background:var(--surface);
  border:1px solid var(--border);
  border-radius:calc(var(--radius) / 2);
  overflow-wrap:anywhere;
}

img,svg,video{display:block;max-width:100%}
img{height:auto}
/* A <picture> adds no box of its own, so grids and figures lay out its <img>
   directly. Its <source> would otherwise become an empty grid item and take
   a cell; the browser picks the image from the markup, not from layout.
   Selectors that count siblings (figure-phones below) must allow for it. */
picture{display:contents}
picture > source{display:none}

:focus-visible{outline:2px solid var(--text);outline-offset:2px}

/* ─── 5. LAYOUT ─── */
.container{
  width:100%;
  max-width:calc(var(--container) + 2 * var(--gutter));
  margin-inline:auto;
  padding-inline:var(--gutter);
}

/* A section makes one claim and shows one proof. */
.section{padding-block:var(--section-y)}
.section + .section,.hero + .section{border-top:1px solid var(--border)}
.section-head{margin-bottom:var(--space-5)}
.section-head p{margin-top:var(--space-2);color:var(--text-muted)}

/* Claim on one side, proof on the other. Stacks below 768px. */
.split{display:grid;gap:var(--space-5);align-items:start}
.split .section-head{margin-bottom:0}
@media (min-width:768px){
  .split{grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--space-6)}
}

/* Components stacked inside one section sit 24px apart. Zero specificity, so
   any component's own margin (and .flow below) wins over it. */
:where(.section > .container) > * + *{margin-top:var(--space-3)}

/* Running text on /faq, /docs/*, /changelog, the legal pages and blog posts. */
.flow > * + *{margin-top:var(--space-2)}
.flow > h2{margin-top:var(--space-6)}
.flow > h3{margin-top:var(--space-4)}
.flow > h4{margin-top:var(--space-3)}
/* A rule stands 48px clear on both sides, the heading after it included. */
.flow > hr,.flow > hr + *{margin-top:var(--space-5)}
.flow > :first-child{margin-top:0}
.flow li + li{margin-top:var(--space-1)}

/* Measure column: a flow held to the measure as a whole, for a document read
   top to bottom (a blog post, /licenses). Its ch resolves at body size, so the
   heading, the cover, a table and a rule all line up with the text. */
.measure{max-width:var(--measure)}

/* Reference layout: a /docs/* article with its table of contents. From 960px
   the contents ride in a rail beside the article and stay in view; below that
   they fold into a collapsible above it. The list ships closed, so a phone
   never sees it fold after load; the page script opens it from 960px, where
   opening it moves nothing else, and marks the section in view with .active. */
.doc{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--space-4)}
.doc > * + *{margin-top:0}
.doc-body > h2{margin-top:var(--space-5);padding-top:var(--space-5);border-top:1px solid var(--border)}
.doc-body > h2:first-child{padding-top:0;border-top:0}

.toc{font-size:var(--text-small)}
.toc-title{display:none;margin-bottom:var(--space-2);font-weight:600}
.toc ol{list-style:none;padding:0}
.toc li{max-width:none}
.toc li + li,.toc ol ol{margin-top:var(--space-1)}
.toc a{display:block;color:var(--text-muted);text-decoration:none}
.toc a:hover,.toc a.active{color:var(--text)}
.toc ol ol a{padding-left:var(--space-2)}
@media (min-width:960px){
  .doc{grid-template-columns:240px minmax(0,1fr);gap:var(--space-6);align-items:start}
  .toc{
    position:sticky;top:calc(var(--nav-h) + var(--space-3));
    max-height:calc(100vh - var(--nav-h) - var(--space-5));overflow-y:auto;
  }
  /* In the rail the collapsible loses its box. Open, it also loses its
     summary, so it cannot be closed, and the title stands in; closed (no
     script), the summary is a plain label that still opens it. */
  .toc .collapsible{background:none;border:0;border-radius:0}
  .toc .collapsible > summary{padding:0}
  .toc .collapsible[open] > summary{display:none}
  .toc .collapsible-body{padding:0}
  .toc-title{display:block}
  /* A 1px rule down the rail; the link in view marks it in --text. */
  .toc-title + ol{border-left:1px solid var(--border)}
  .toc a{margin-left:-1px;padding-left:var(--space-2);border-left:1px solid transparent}
  .toc ol ol a{padding-left:var(--space-4)}
  .toc a.active{border-left-color:var(--text)}
}

/* Heading anchors: a # after each docs heading, shown on hover, for copying a
   link to that heading. The # is drawn here, so a heading's text is its own
   for crawlers and screen readers; the table of contents carries the same
   links for the keyboard. */
.h-anchor{margin-left:var(--space-1);font-weight:400;color:var(--text-muted);text-decoration:none;opacity:0}
.h-anchor::after{content:"#"}
h2:hover > .h-anchor,h3:hover > .h-anchor{opacity:1}

/* ─── 6. COMPONENTS ─── */

/* Nav */
.nav{
  position:sticky;top:0;z-index:10;
  background:var(--bg);
  border-bottom:1px solid var(--border);
}
.nav-inner{
  position:relative;
  display:flex;align-items:center;gap:var(--space-3);
  min-height:var(--nav-h);
}
.nav-logo{
  display:inline-flex;align-items:center;gap:var(--space-1);
  margin-right:auto;
  font-weight:700;letter-spacing:-.01em;
  text-decoration:none;
}
.nav-logo img{width:32px;height:32px;border-radius:var(--radius)}
.nav-links{display:flex;gap:var(--space-3);list-style:none;padding:0}
.nav-links a{
  display:block;
  font-size:var(--text-small);font-weight:500;
  color:var(--text-muted);text-decoration:none;
}
.nav-links a:hover,.nav-links a[aria-current="page"]{color:var(--text)}
.nav-actions{display:flex;gap:var(--space-1)}
.icon-btn{
  display:inline-flex;align-items:center;justify-content:center;
  width:40px;height:40px;
  background:none;color:var(--text-muted);
  border:1px solid var(--border);border-radius:var(--radius);
  cursor:pointer;
  transition:color var(--duration),border-color var(--duration);
}
.icon-btn:hover{color:var(--text);border-color:var(--text-muted)}
.icon-btn svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}

/* The theme toggle shows the current theme, as on the old pages: a moon in
   dark, a sun in light. The nearest data-theme decides, through the tokens. */
.theme-toggle .icon-sun{display:var(--toggle-sun)}
.theme-toggle .icon-moon{display:var(--toggle-moon)}

/* The nav folds below 900px: the French labels need about 880px to sit on one
   line (a link wrapped to two lines up to 860px; Spanish to 840px, German to
   820px; English and Turkish fit at 800px). Measured in Chrome at 800 to 1000px,
   five languages, 10 Nov 2026. */
.nav-menu{display:none}
@media (max-width:899px){
  .nav-menu{display:inline-flex}
  /* Closed, the menu leaves layout entirely, so nothing parked off-screen can
     widen the page on a phone. Open, it drops below the bar, in the page's
     own colours: no overlay, no shadow. */
  .nav-links{
    display:none;
    position:absolute;top:100%;left:0;right:0;
    flex-direction:column;gap:0;
    padding:var(--space-1) var(--gutter) var(--space-2);
    background:var(--bg);
    border-bottom:1px solid var(--border);
  }
  .nav-links.open{display:flex}
  .nav-links a{padding-block:var(--space-1);font-size:var(--text-body)}
}

/* Language menu: a native <details> in the nav actions, before the theme
   toggle, on a page that exists in more than one language. The summary is an
   .icon-btn with a globe; open, the list drops under it, right-aligned, on
   --surface inside a 1px rule. No script, no shadow. The current language is
   first, marked with aria-current and set in --text. */
.lang-menu{position:relative}
.lang-menu > summary{list-style:none}
.lang-menu > summary::-webkit-details-marker{display:none}
.lang-menu[open] > summary{color:var(--text);border-color:var(--text-muted)}
.lang-menu-list{
  position:absolute;top:calc(100% + var(--space-1));right:0;z-index:1;
  min-width:max-content;
  padding:var(--space-1) 0;list-style:none;
  background:var(--surface);
  border:1px solid var(--border);border-radius:var(--radius);
}
.lang-menu-list a{
  display:block;padding:var(--space-1) var(--space-2);
  font-size:var(--text-small);font-weight:500;
  color:var(--text-muted);text-decoration:none;
}
.lang-menu-list a:hover{color:var(--text)}
.lang-menu-list a[aria-current="page"]{color:var(--text);font-weight:600}

/* Buttons */
.actions{display:flex;flex-wrap:wrap;gap:var(--space-2)}
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--space-1);
  min-height:44px;padding:var(--space-1) var(--space-3);
  font:inherit;font-size:var(--text-small);font-weight:600;line-height:1.3;text-align:center;
  color:var(--text);text-decoration:none;
  background:none;
  border:1px solid var(--border);border-radius:var(--radius);
  cursor:pointer;
  transition:background-color var(--duration),border-color var(--duration),color var(--duration);
}
.btn-primary{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
/* Hover adds no colour: the outline darkens to an existing token. */
.btn-primary:hover{border-color:var(--text)}
.btn-secondary:hover{border-color:var(--text-muted)}

/* Store badges: the App Store and Google Play links where a page sends the
   reader to the stores (the home hero and closing) in place of the two
   buttons. The inverted fill reads as a store badge rather than a second
   primary button, and stays on the neutrals in both themes. */
.store-badges{display:flex;flex-wrap:wrap;gap:var(--space-2)}
.store-badge{
  display:inline-flex;align-items:center;gap:var(--space-1);
  min-height:48px;padding:var(--space-1) var(--space-2);
  line-height:1.15;text-align:left;text-decoration:none;
  background:var(--text);color:var(--bg);
  border:1px solid var(--text);border-radius:var(--radius);
  transition:border-color var(--duration);
}
.store-badge:hover{border-color:var(--text-muted)}
.store-badge svg{flex:none;width:24px;height:24px;fill:currentColor}
.store-badge small,.store-badge strong{display:block}

/* Links that stand on their own line, outside running text. */
.link{display:block;width:fit-content;font-weight:600;text-decoration:none}
.link:hover{text-decoration:underline}
* + .link{margin-top:var(--space-2)}

/* Hero */
.hero{padding-block:var(--space-7) var(--space-6)}
.hero h1{max-width:18ch}
.hero-sub{margin-top:var(--space-3);font-size:var(--text-h3);color:var(--text-muted);max-width:48ch}
.hero .actions,.hero .store-badges{margin-top:var(--space-4)}
.hero .figure{margin-top:var(--space-6)}
@media (max-width:767px){
  .hero{padding-block:var(--space-6) var(--space-5)}
  .hero .figure{margin-top:var(--space-5)}
}
/* Hero with a phone pair: the text and the phones side by side from 960px,
   the phones under the text below that. */
@media (min-width:960px){
  .hero-split{
    display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
    gap:var(--space-6);align-items:center;
  }
  .hero-split .figure{margin-top:0;justify-self:end}
}

/* Steps 01-04 */
.steps{
  list-style:none;padding:0;
  display:grid;gap:var(--space-3) var(--space-4);
}
.step{
  max-width:none;
  display:grid;grid-template-columns:auto minmax(0,1fr);gap:var(--space-1) var(--space-2);
  align-items:baseline;
  padding-top:var(--space-2);
  border-top:1px solid var(--border);
}
.step-num{
  grid-row:span 2;
  display:inline-flex;align-items:center;justify-content:center;
  min-width:40px;height:32px;padding-inline:var(--space-1);
  font-family:var(--font-mono);font-size:var(--text-small);font-weight:600;
  font-variant-numeric:tabular-nums;
  background:var(--accent);color:var(--on-accent);
  border-radius:var(--radius);
}
.step-label{font-weight:600}
.step-detail{font-size:var(--text-small);color:var(--text-muted)}
@media (min-width:600px){
  .steps{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (min-width:960px){
  .steps{grid-template-columns:repeat(4,minmax(0,1fr))}
  .step{grid-template-columns:minmax(0,1fr);padding-top:var(--space-3)}
  .step-num{grid-row:auto;justify-self:start;margin-bottom:var(--space-1)}
}

/* Steps with screens: each step carries its phone screen under the detail.
   From 960px the detail row takes the slack, so the screens share a bottom
   edge however many lines a label wraps to. */
.step-shot{
  grid-column:1 / -1;
  width:100%;max-width:288px;
  margin-top:var(--space-2);
  background:var(--surface);
  border:1px solid var(--border);border-radius:var(--radius);
}
@media (min-width:960px){
  .steps-shots .step{grid-template-rows:auto auto 1fr auto}
}

/* Feature rows */
.features{border-top:1px solid var(--border)}
.feature{
  display:grid;gap:var(--space-1) var(--space-4);
  padding-block:var(--space-3);
  border-bottom:1px solid var(--border);
}
.feature p{color:var(--text-muted)}
@media (min-width:768px){
  .feature{grid-template-columns:minmax(0,1fr) minmax(0,2fr)}
}

/* Line lists: facts, integrations, trust, recently shipped, pricing summary.
   One look, five content rules (see the doc). Price lines, with the pricing
   columns below, share the look. */
.facts,.integrations,.trust,.shipped,.pricing-summary{list-style:none;padding:0}
.facts li,.integrations li,.trust li,.shipped li,.pricing-summary li{
  max-width:none;
  padding-block:var(--space-2);
  border-top:1px solid var(--border);
}
.integrations li,.shipped li,.pricing-summary li{color:var(--text-muted)}
.integrations strong,.trust strong,.pricing-summary strong{color:var(--text)}
.integrations,.trust,.shipped,.pricing-summary{border-bottom:1px solid var(--border)}

/* Link list: the same look for links into other pages, one per line. The
   whole line is the link, so the padding sits on the <a>. */
.link-list{list-style:none;padding:0;border-bottom:1px solid var(--border)}
.link-list li{max-width:none;border-top:1px solid var(--border)}
.link-list a{display:block;padding-block:var(--space-2);font-weight:600;text-decoration:none}
.link-list a:hover{text-decoration:underline}

.facts{display:grid;column-gap:var(--space-4)}
.facts li{font-weight:500}
@media (min-width:600px){.facts{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (min-width:960px){.facts{grid-template-columns:repeat(3,minmax(0,1fr))}}

/* Facts boxes: two facts lists side by side, each under its name and in one
   column, so a URL keeps a whole line. Pricing columns, download boxes and
   link cards are the same two boxes with other contents. */
.facts-boxes,.plans,.download-boxes,.cards{display:grid;gap:var(--space-3)}
@media (min-width:768px){.facts-boxes,.plans,.download-boxes,.cards{grid-template-columns:repeat(2,minmax(0,1fr))}}
.facts-box,.plan,.download-box,.card{
  padding:var(--space-3);
  background:var(--surface);
  border:1px solid var(--border);border-radius:var(--radius);
}
.facts-box h3{margin-bottom:var(--space-1)}
.facts-box .facts{grid-template-columns:minmax(0,1fr)}

/* Pricing columns: one box per plan, both alike, no badge and no plan
   singled out. The plan's name is an h2 for the outline, set at the h3 size. */
.plan h2,.download-box h2{font-size:var(--text-h3);letter-spacing:-.01em}
.plan-prices{list-style:none;padding:0;margin-top:var(--space-2)}
.plan-prices li{font-size:var(--text-h3);font-weight:600;line-height:var(--lh-heading)}
.plan-prices li + li{margin-top:var(--space-1)}
.plan-lead{margin-top:var(--space-3);font-weight:600}
/* Price lines: the line-list look in one column, for what a plan or a
   one-time purchase includes. */
.price-lines{list-style:none;padding:0;border-bottom:1px solid var(--border)}
.price-lines li{max-width:none;padding-block:var(--space-1);border-top:1px solid var(--border)}
.plan .price-lines{margin-top:var(--space-3)}
.plan-lead + .price-lines{margin-top:var(--space-1)}

/* Download boxes: phones and desktop, each with its badges and a line or two.
   The page script marks the visitor's platform with a ring, no colour. */
.download-box > * + *{margin-top:var(--space-3)}
.download-box p{color:var(--text-muted)}
.download-box p a{color:var(--text)}
/* Not while focused: the focus ring must stay the one a keyboard user sees. */
.store-badge.highlighted:not(:focus-visible){outline:1px solid var(--text);outline-offset:3px}

/* Link cards: two places to go next, each box one link with a title and a
   line. Hover adds no colour: the outline darkens, as on a secondary button. */
.card{
  display:flex;flex-direction:column;gap:var(--space-1);
  text-decoration:none;
  transition:border-color var(--duration);
}
.card:hover{border-color:var(--text-muted)}
.card-kind{font-size:var(--text-small);color:var(--text-muted)}
.card-title{font-size:var(--text-h3);font-weight:600;line-height:var(--lh-heading);letter-spacing:-.01em}
.card p{color:var(--text-muted)}

.shipped li,.pricing-summary li{display:grid;gap:0 var(--space-3)}
.shipped time{font-size:var(--text-small);font-variant-numeric:tabular-nums}
.shipped a{color:var(--text)}
@media (min-width:600px){
  .shipped li,.pricing-summary li{grid-template-columns:128px minmax(0,1fr)}
  .shipped time{padding-top:2px}
}
.shipped + .link,.pricing-summary + .link{margin-top:var(--space-3)}

/* Figure */
.figure img{
  width:100%;
  background:var(--surface);
  border:1px solid var(--border);border-radius:var(--radius);
}
.figure figcaption{margin-top:var(--space-2);font-size:var(--text-small);color:var(--text-muted)}
.figure-phone{max-width:288px}

/* Figure pair: the desktop screen with one phone screen in front of its top
   right corner, rising above it into the hero's empty right side (the h1 and
   sub keep to the left, the buttons end well short of it). Both share one
   grid row; the phone's negative margin lifts it without moving the desktop,
   and a phone taller than the desktop simply grows the row. The ring in --bg
   parts it from the screen behind; outlines are ink overflow, so the ring
   never widens the page. */
.figure-pair{
  display:grid;
  grid-template-columns:minmax(0,64fr) minmax(0,36fr);
}
.figure-pair img{align-self:start}
.figure-pair .pair-desktop{grid-column:1 / -1;grid-row:1}
.figure-pair .pair-phone{
  grid-column:2;grid-row:1;
  margin-top:calc(-1 * var(--space-3));
  position:relative;z-index:1;
  outline:var(--space-1) solid var(--bg);
}
.figure-pair figcaption{grid-column:1 / -1;grid-row:2}
@media (min-width:768px){
  .figure-pair{grid-template-columns:minmax(0,76fr) minmax(0,10fr) minmax(0,14fr)}
  .figure-pair .pair-desktop{grid-column:1 / 3}
  .figure-pair .pair-phone{grid-column:2 / 4;margin-top:calc(-1 * var(--space-7))}
}

/* Phone pair: two phone screens of one flow side by side, the second a step
   lower, under one caption. They never overlap, so they need no ring. */
.figure-phones{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:0 var(--space-2);
  max-width:480px;
}
.figure-phones img{align-self:start}
.figure-phones > img:nth-child(2),
.figure-phones > picture:nth-child(2) > img{margin-top:var(--space-5)}
.figure-phones figcaption{grid-column:1 / -1}
@media (min-width:600px){
  .figure-phones{column-gap:var(--space-3)}
  .figure-phones > img:nth-child(2),
  .figure-phones > picture:nth-child(2) > img{margin-top:var(--space-6)}
}

/* Figure row: two figures side by side, each with its own caption, as the
   proof in a split. */
.figure-row{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:var(--space-2);align-items:start;
  max-width:480px;
}
@media (min-width:600px){.figure-row{gap:var(--space-3)}}

/* Stage: the captures sit on a --surface panel with the figure's own border
   and radius, padded --space-3 (--space-2 below 600px); each capture keeps
   its 1px border inside it. No shadow, no gradient, no device outline. The
   panel is the figure's ::before in its first grid row, so the markup needs no
   wrapper and the caption stays under the panel. A figure-phone or a figure
   in a figure-row holds one capture; a figure-phones holds both phones on one
   panel, the second keeping its step down inside it. Never on figure-pair or
   step-shot. The max-widths add the padding and the border to the screens'
   widths, so the screens keep their size where the column allows. */
.figure-stage{display:grid;grid-template-columns:minmax(0,1fr);align-items:start}
.figure-stage::before{
  content:"";grid-column:1 / -1;grid-row:1;align-self:stretch;
  background:var(--surface);
  border:1px solid var(--border);border-radius:var(--radius);
}
.figure-stage > img,.figure-stage > picture > img{
  grid-row:1;grid-column:1;
  width:calc(100% - 2 * var(--space-2) - 2px);
  margin:calc(var(--space-2) + 1px);
}
.figure-stage figcaption{grid-row:2}
.figure-phone.figure-stage{max-width:calc(288px + 2 * var(--space-3) + 2px)}
.figure-row:has(> .figure-stage){max-width:calc(480px + 4 * var(--space-3) + 4px)}
.figure-phones.figure-stage{
  grid-template-columns:repeat(2,minmax(0,1fr));
  max-width:calc(480px + 2 * var(--space-3) + 2px);
}
.figure-phones.figure-stage > img:nth-child(1),
.figure-phones.figure-stage > picture:nth-child(1) > img{grid-column:1;margin-right:0;width:calc(100% - var(--space-2) - 1px)}
.figure-phones.figure-stage > img:nth-child(2),
.figure-phones.figure-stage > picture:nth-child(2) > img{
  grid-column:2;margin-left:0;width:calc(100% - var(--space-2) - 1px);
  margin-top:calc(var(--space-5) + var(--space-2) + 1px);
}
@media (min-width:600px){
  .figure-stage > img,.figure-stage > picture > img{
    width:calc(100% - 2 * var(--space-3) - 2px);
    margin:calc(var(--space-3) + 1px);
  }
  .figure-phones.figure-stage > img:nth-child(1),
  .figure-phones.figure-stage > picture:nth-child(1) > img{width:calc(100% - var(--space-3) - 1px)}
  .figure-phones.figure-stage > img:nth-child(2),
  .figure-phones.figure-stage > picture:nth-child(2) > img{
    width:calc(100% - var(--space-3) - 1px);
    margin-top:calc(var(--space-6) + var(--space-3) + 1px);
  }
}

/* Code block. Themed like the page; no highlighting library. */
.code{
  background:var(--surface);
  border:1px solid var(--border);border-radius:var(--radius);
  overflow:hidden;
}
.code-head{
  display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);
  min-height:40px;padding-inline:var(--space-2) var(--space-1);
  border-bottom:1px solid var(--border);
  font-size:var(--text-small);color:var(--text-muted);
}
.code pre{margin:0;padding:var(--space-2);overflow-x:auto;line-height:1.6;color:var(--text)}
.code pre code{white-space:pre}
/* A long block the reader copies rather than reads scrolls inside a fixed
   height; the copy button still copies all of it. */
.code-long pre{max-height:480px;overflow:auto}
.copy-btn{
  min-height:32px;padding-inline:var(--space-1);
  font:inherit;font-size:var(--text-small);font-weight:500;
  color:var(--text-muted);background:none;
  border:1px solid transparent;border-radius:var(--radius);
  cursor:pointer;
  transition:color var(--duration),border-color var(--duration);
}
.copy-btn:hover{color:var(--text);border-color:var(--border)}
.copy-btn.copied{color:var(--text)}

/* Collapsible and FAQ accordion: native <details>, no script. */
.collapsible{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius)}
.collapsible > summary,
.faq-accordion > details > summary{
  display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);
  cursor:pointer;list-style:none;
}
.collapsible > summary{padding:var(--space-2) var(--space-3);font-weight:600}
.collapsible > summary::-webkit-details-marker,
.faq-accordion > details > summary::-webkit-details-marker{display:none}
.collapsible > summary::after,
.faq-accordion > details > summary::after{
  content:"";flex:none;
  width:8px;height:8px;margin-right:4px;
  border-right:2px solid var(--text-muted);border-bottom:2px solid var(--text-muted);
  transform:translateY(-2px) rotate(45deg);
}
.collapsible[open] > summary::after,
.faq-accordion > details[open] > summary::after{transform:translateY(2px) rotate(-135deg)}
.collapsible-body{padding:0 var(--space-3) var(--space-3)}

.faq-accordion{border-top:1px solid var(--border)}
.faq-accordion > details{border-bottom:1px solid var(--border)}
.faq-accordion > details > summary{padding-block:var(--space-3)}
.faq-accordion > details > summary h3{font-size:var(--text-body);font-weight:600;letter-spacing:0}
.faq-answer{padding-bottom:var(--space-3);color:var(--text-muted)}
/* An answer that runs to more than one paragraph (/faq). */
.faq-answer > * + *{margin-top:var(--space-2)}

/* Release entries: one per build on /changelog, newest first, each under a
   1px rule. The .rel* names are a contract with the changelog's release
   feed, which writes an entry of the same markup for a newer desktop build.
   Latest is a label in a 1px outline: no fill, no colour. */
.rel{padding-top:var(--space-3);border-top:1px solid var(--border)}
.rel + .rel{margin-top:var(--space-5)}
.rel-head{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--space-1) var(--space-2)}
.rel-date{font-size:var(--text-small);color:var(--text-muted);font-variant-numeric:tabular-nums}
.rel-latest{
  padding-inline:var(--space-1);
  font-size:var(--text-small);font-weight:600;
  border:1px solid var(--border);border-radius:calc(var(--radius) / 2);
}
.rel-notes{margin-top:var(--space-2)}
.rel-notes li + li{margin-top:var(--space-1)}
.rel-text{margin-top:var(--space-2);color:var(--text-muted)}
.rel-foot{font-size:var(--text-small);color:var(--text-muted)}
* + .rel-foot{margin-top:var(--space-3)}

/* Table. On a page with a word budget, a table longer than six rows goes
   inside a .collapsible; the pages that carry the depth keep it open. */
.table-wrap{overflow-x:auto}
.table{width:100%;border-collapse:collapse;font-size:var(--text-small)}
.table th,.table td{padding:var(--space-1) var(--space-2) var(--space-1) 0;text-align:left;vertical-align:top;border-bottom:1px solid var(--border)}
.table th{font-weight:600}
.table td{color:var(--text-muted)}
/* Identifiers and URLs never break mid-word in a table: the wrapper scrolls. */
.table code{color:var(--text);white-space:nowrap;overflow-wrap:normal}
/* On a phone a table of three or more columns would squeeze its sentence
   column to its longest word; a minimum width gives that column room and the
   wrapper scrolls instead. Two-column tables still fit the screen. */
@media (max-width:767px){
  .table:has(th:nth-child(3)){min-width:48em}
}

/* Meta line: the date and counts under a title (a post's date, reading time
   and author; a legal page's Last updated). */
.meta{font-size:var(--text-small);color:var(--text-muted)}

/* Post list: the blog index, newest first, one post per entry under a 1px
   rule. The title is the link: an h2 for the outline, set at the h3 size, as
   a plan's name is. */
.posts{list-style:none;padding:0;border-bottom:1px solid var(--border)}
.posts > li{max-width:none;padding-block:var(--space-3);border-top:1px solid var(--border)}
.posts > li > * + *{margin-top:var(--space-1)}
.posts h2{font-size:var(--text-h3);letter-spacing:-.01em}
.posts h2 a{text-decoration:none}
.posts h2 a:hover{text-decoration:underline}
.posts p{color:var(--text-muted)}

/* Note: one line, said plainly. */
.note{
  padding:var(--space-2);
  font-size:var(--text-small);color:var(--text-muted);
  background:var(--surface);
  border:1px solid var(--border);border-radius:var(--radius);
}
.note strong,.note a{color:var(--text)}

/* Closing CTA */
.closing{text-align:center}
.closing h2{max-width:24ch;margin-inline:auto}
.closing p{margin:var(--space-2) auto 0;color:var(--text-muted)}
.closing .actions,.closing .store-badges{justify-content:center;margin-top:var(--space-4)}
.closing .link{margin-inline:auto}
/* A closing that is the badges alone (/pricing) starts at the section's edge. */
.closing .container > :first-child{margin-top:0}

/* Footer */
.footer{
  padding-block:var(--space-6) var(--space-5);
  border-top:1px solid var(--border);
  font-size:var(--text-small);color:var(--text-muted);
}
.footer-cols{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--space-4)}
@media (min-width:768px){.footer-cols{grid-template-columns:repeat(4,minmax(0,1fr))}}
.footer-title{margin-bottom:var(--space-1);font-weight:600;color:var(--text)}
.footer ul{list-style:none;padding:0}
.footer li + li{margin-top:var(--space-1)}
.footer a{text-decoration:none}
.footer a:hover{color:var(--text)}
.footer-meta{
  display:flex;flex-wrap:wrap;justify-content:space-between;gap:var(--space-1) var(--space-3);
  margin-top:var(--space-5);padding-top:var(--space-3);
  border-top:1px solid var(--border);
}

/* ─── 7. UTILITIES ─── */
.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;
}
.skip-link{
  position:absolute;left:var(--space-2);top:-100px;z-index:20;
  padding:var(--space-1) var(--space-2);
  background:var(--text);color:var(--bg);
  border-radius:var(--radius);
  font-weight:600;text-decoration:none;
}
.skip-link:focus{top:var(--space-1)}
/* Keeps a hyphenated compound ("to-do") on one line in a heading. */
.nowrap{white-space:nowrap}

/* ─── 8. MOTION ─── */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{transition-duration:.001ms !important}
}
