/* Daili — daili.app
 *
 * Three conventions run through this file, all load-bearing:
 *
 * 1. LOGICAL PROPERTIES, not physical ones. margin-inline-start, not
 *    margin-left. That is what lets the Arabic build mirror itself from a
 *    single stylesheet instead of a generated second copy that drifts.
 *    Transforms have no logical form, so their direction lives in the
 *    --rot-* / --dir custom properties below and is flipped in one place.
 *    The redesign mock is written with physical right:/left:; everything
 *    lifted from it is converted on the way in.
 *
 * 2. EVERY COLOUR IS A TOKEN. Nothing below :root hard-codes a colour, which
 *    is what makes the dark-mode block near the end ~20 lines instead of a
 *    second stylesheet.
 *
 * 3. THE FONTS ARE OURS. Both families are served from /assets/fonts/, never
 *    from Google. See the block immediately below.
 */

/* ---------- brand fonts ----------------------------------------------------
 * Sofia Sans Condensed (display) and Figtree (text): the same two faces the
 * app and the web app use. The woff2 subsets are copied verbatim out of the
 * @fontsource packages (figtree 5.3.0, sofia-sans-condensed 5.3.0) into
 * static/assets/fonts/; static/assets/fonts/LICENSE.txt carries both SIL OFL
 * texts. Self-hosted rather than linked from Google Fonts so that the CSP can
 * stay `font-src 'self'` and no reader's browser is made to announce itself to
 * a third party in order to read this page — the same reason this site has no
 * analytics and no cookie banner.
 *
 * Every face keeps the unicode-range of its subset, copied verbatim from the
 * package's own CSS, so a Russian page fetches the Cyrillic cut of the display
 * face and nothing else. Neither family has Arabic, Thai, Devanagari or CJK
 * glyphs, so /ar/, /th/, /hi/, /ja/, /ko/ and the two Chinese builds match no
 * range, download no font at all, and fall through to the system stack at the
 * end of --display / --sans. That is intended: a system font in those scripts
 * beats a Latin display face that cannot render them.
 * ------------------------------------------------------------------------- */

/* Figtree — body text */
@font-face{font-family:Figtree;font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/figtree-latin-400-normal.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:Figtree;font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/figtree-latin-ext-400-normal.woff2) format("woff2");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:Figtree;font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/figtree-latin-500-normal.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:Figtree;font-style:normal;font-weight:600;font-display:swap;src:url(/assets/fonts/figtree-latin-600-normal.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:Figtree;font-style:normal;font-weight:700;font-display:swap;src:url(/assets/fonts/figtree-latin-700-normal.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:Figtree;font-style:normal;font-weight:700;font-display:swap;src:url(/assets/fonts/figtree-latin-ext-700-normal.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}

/* Sofia Sans Condensed — headlines, the wordmark, the language codes.
   500/600 are never asked for; 700 and 800 are the only weights the design
   uses, in four scripts (Latin runs the whole site, Cyrillic is /ru/, Greek is
   here because the family ships it and a stray ° or Ω in any locale would
   otherwise fall out of the face mid-word). */
@font-face{font-family:"Sofia Sans Condensed";font-style:normal;font-weight:700;font-display:swap;src:url(/assets/fonts/sofia-sans-condensed-latin-700-normal.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:"Sofia Sans Condensed";font-style:normal;font-weight:700;font-display:swap;src:url(/assets/fonts/sofia-sans-condensed-latin-ext-700-normal.woff2) format("woff2");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:"Sofia Sans Condensed";font-style:normal;font-weight:700;font-display:swap;src:url(/assets/fonts/sofia-sans-condensed-cyrillic-700-normal.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:"Sofia Sans Condensed";font-style:normal;font-weight:700;font-display:swap;src:url(/assets/fonts/sofia-sans-condensed-greek-700-normal.woff2) format("woff2");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}
@font-face{font-family:"Sofia Sans Condensed";font-style:normal;font-weight:800;font-display:swap;src:url(/assets/fonts/sofia-sans-condensed-latin-800-normal.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:"Sofia Sans Condensed";font-style:normal;font-weight:800;font-display:swap;src:url(/assets/fonts/sofia-sans-condensed-latin-ext-800-normal.woff2) format("woff2");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:"Sofia Sans Condensed";font-style:normal;font-weight:800;font-display:swap;src:url(/assets/fonts/sofia-sans-condensed-cyrillic-800-normal.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:"Sofia Sans Condensed";font-style:normal;font-weight:800;font-display:swap;src:url(/assets/fonts/sofia-sans-condensed-greek-800-normal.woff2) format("woff2");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}

:root{
  /* The redesign palette, lifted from claude-prompts/2026-09-05/redesign-mock.html */
  --paper:#FBF9F2; --card:#FFFFFF; --ink:#10201A; --body:#2B3A32; --muted:#5E6D64;
  --forest:#04613C; --forest-deep:#0B4A30; --on-forest:#FFFFFF;
  --mint:#DCEFDF; --mint-strong:#A9D9B8; --mint-soft:#EEF6EF;
  --amber:#E9A93B; --amber-soft:#FBF0D6;
  /* The amber tile's icon chip. Its own pair rather than --amber/--mint,
     because the chip has to sit on --amber-soft in both modes. */
  --amber-ic:#F3DFB4; --amber-ink:#8A5A00;
  --line:#E4DFD1;

  /* The app's five event/member colours. They label the dots in the hero
     kicker and the avatars on the family tile — the only place on the page
     where colour IS the content, which is why they do not flip in dark mode:
     these are the same five colours the app itself draws. */
  --cat-green:#1F8A55; --cat-amber:#D48B12; --cat-plum:#B85C9E;
  --cat-sky:#3B82C4; --cat-red:#D4553F;

  /* The "also on your computer" block is dark in BOTH modes (its ground is
     --frame), so its accent cannot be --mint-strong: that token darkens in
     dark mode and would put a near-black tick on a near-black card. */
  --web-mint:#A9D9B8; --web-mint-ink:#10201A;
  --frame:#10201A;                 /* the phone bezel — must stay dark in dark mode */
  --shadow:16,29,22;
  --display:"Sofia Sans Condensed","Arial Narrow",Impact,sans-serif;
  --sans:Figtree,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --ease:cubic-bezier(.22,.8,.3,1);
  color-scheme:light;

  /* Blog and legal furniture: the cream ground behind a post hero, a
     blockquote and a table head, and the radius they share. */
  --cream:#F4EDDE; --radius:22px;

  /* Direction-dependent geometry. Transforms cannot be expressed logically, so
     the sign lives here and [dir="rtl"] flips it exactly once. */
  --dir:1;
  --rot-laptop:1.2deg; --rot-phone:-3deg;
  --rot-shot:-4deg; --rot-shot-cal:3deg; --rot-shot-shop:-6deg;
  --rot-brand-hover:-8deg;
}

[dir="rtl"]{
  --dir:-1;
  --rot-laptop:-1.2deg; --rot-phone:3deg;
  --rot-shot:4deg; --rot-shot-cal:-3deg; --rot-shot-shop:6deg;
  --rot-brand-hover:8deg;
}

*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth}
/* clip, not hidden: `overflow-x:hidden` on an ancestor silently kills
   position:sticky, which is what holds the header up. */
html,body{overflow-x:clip}
body{font-family:var(--sans);background:var(--paper);color:var(--ink);line-height:1.6;font-size:16px;-webkit-font-smoothing:antialiased}
/* height:auto is required now that every <img> carries width/height attributes:
   without it the attribute height wins over the CSS width and images squash. */
img{max-width:100%;height:auto;display:block}
a{color:var(--forest)}
p{color:var(--body)}
h1,h2,h3{font-family:var(--display);font-weight:800;line-height:1.02;letter-spacing:-.01em;text-wrap:balance}
h3{font-weight:700}
.wrap{max-width:1120px;margin:0 auto;padding-inline:22px}

/* 1.02 is the mock's leading, and it is right for display type at 3rem and up.
   Everything set smaller — the legal pages, a step or tile heading at 1.35rem —
   needs air or two lines touch, and most of these wrap in German. */
.legal :is(h1,h2,h3),.tile h3,.step h3{line-height:1.2}
/* Arabic and Thai carry marks above and below the line, which .98 clips. */
:is([lang="ar"],[lang="th"]) .hero h1{line-height:1.2}

/* Arabic and Thai need more leading than Latin at the same size. */
[lang="ar"] body,[lang="th"] body,body[lang="ar"],body[lang="th"]{line-height:1.85}
:is([lang="ar"],[lang="th"]) body{line-height:1.85}

/* Negative tracking makes CJK glyphs collide; unspaced strings must still wrap. */
:is([lang="ja"],[lang="ko"],[lang^="zh"]) h1,
:is([lang="ja"],[lang="ko"],[lang^="zh"]) h2,
:is([lang="ja"],[lang="ko"],[lang^="zh"]) h3{letter-spacing:0}
h1,h2,h3,p,li,summary,figcaption{overflow-wrap:anywhere}

/* Visible focus. There was none at all before, which made the whole site
   unusable by keyboard. :focus-visible so it never shows on a mouse click. */
:focus-visible{outline:3px solid var(--forest);outline-offset:3px;border-radius:6px}

.skip{position:absolute;inset-inline-start:-9999px;top:0;z-index:100;background:var(--forest);color:var(--on-forest);padding:10px 18px;border-radius:0 0 12px 0;text-decoration:none}
.skip:focus{inset-inline-start:0}

/* ---------- header ----------------------------------------------------------
 * Layout note, because the DOM order and the visual order differ on purpose.
 *
 * The design wants, left to right: brand · the five links · the language chip ·
 * Download. On a phone it wants: brand · the language chip · a burger, with the
 * links AND Download dropping into a panel the burger opens.
 *
 * So the chip cannot live inside the panel, and Download must. .menu is that
 * panel — and on desktop it is `display:contents`, which dissolves it: .links,
 * .langpicker and .dl all become direct flex items of .nav and `order` puts the
 * chip between the links and the pill. Below 860px .menu becomes a real box
 * again and the chip, which was never inside it, simply stays in the header row.
 *
 * One DOM, one language picker, at every width — the mock reaches the same
 * result by shipping a second, abbreviated picker for phones.
 * ------------------------------------------------------------------------- */
header{position:sticky;top:0;z-index:30;background:color-mix(in srgb,var(--paper) 86%,transparent);backdrop-filter:blur(12px);border-bottom:1px solid transparent;transition:border-color .3s,box-shadow .3s}
header.scrolled{border-bottom-color:var(--line);box-shadow:0 6px 24px rgba(var(--shadow),.06)}
.nav{display:flex;align-items:center;gap:22px;height:66px}
.brand{display:flex;align-items:center;gap:10px;text-decoration:none;color:var(--ink);font-family:var(--display);font-weight:800;font-size:1.5rem;letter-spacing:0}
.brand img{width:36px;height:36px;border-radius:10px;transition:transform .5s var(--ease)}
.brand:hover img{transform:rotate(var(--rot-brand-hover)) scale(1.06)}

.menu{display:contents}
.links{order:1;margin-inline-start:auto;display:flex;align-items:center;gap:6px}
.links a.nl{text-decoration:none;color:var(--ink);font-weight:500;font-size:.95rem;padding:8px 12px;border-radius:999px;transition:background .2s,color .2s}
.links a.nl:hover{background:var(--mint-soft);color:var(--forest)}
.links a.nl-web{color:var(--forest);font-weight:600}
.langpicker{order:2;margin-inline-start:4px}
.dl{order:3}
.burger{order:4}

/* The two-locale pill. Dead at 23 locales, kept because LOCALES is a list
   anyone may shorten. */
.lang{border:1.5px solid var(--line);border-radius:999px;padding:6px 12px;font-size:.85rem;background:var(--card);text-decoration:none;color:var(--ink);transition:background .3s,border-color .3s}
.lang:hover{background:var(--mint-soft);border-color:var(--mint-strong)}

/* ---------- language picker ---------- */
.langpicker{position:relative}
.langpicker summary{list-style:none;cursor:pointer;display:inline-flex;align-items:center;gap:6px;border:1.5px solid var(--line);border-radius:999px;padding-block:6px;padding-inline:9px 10px;font-size:.8rem;font-weight:700;letter-spacing:.06em;background:var(--card);color:var(--ink);transition:background .3s,border-color .3s}
.langpicker summary::-webkit-details-marker{display:none}
.langpicker summary:hover{background:var(--mint-soft);border-color:var(--mint-strong)}
.langpicker summary svg{width:15px;height:15px;stroke:var(--muted);fill:none;stroke-width:1.8;flex:none}
.langpicker summary .chev{width:10px;height:10px;stroke:var(--muted);opacity:.8}
.langpicker-menu{position:absolute;inset-inline-end:0;top:calc(100% + 10px);background:var(--card);border:1px solid var(--line);border-radius:16px;padding:12px;box-shadow:0 22px 48px rgba(var(--shadow),.16);z-index:40;width:min(560px,88vw)}
.langpicker-menu ul{list-style:none;display:grid;grid-template-columns:repeat(3,1fr);gap:2px 10px}
.langpicker-menu a{display:flex;align-items:center;gap:10px;padding:7px 10px;border-radius:9px;text-decoration:none;color:var(--ink);font-size:.9rem;white-space:nowrap;overflow:hidden}
/* The code is set in the display face, which is condensed: 2.6em of it holds
   every two-letter code at this size without the endonyms losing their column. */
.langpicker-menu a small{font-family:var(--display);font-weight:700;letter-spacing:.06em;font-size:.72rem;color:var(--muted);width:2.6em;flex:none}
.langpicker-menu a:hover{background:var(--mint-soft)}
.langpicker-menu a[aria-current]{background:var(--mint);font-weight:600}

/* ---------- burger ---------- */
/* display:none until 860px, and `align-items` is set here rather than in the
   media query so the flex centring survives the display flip. */
.burger{display:none;align-items:center;justify-content:center;margin-inline-start:6px;width:40px;height:40px;flex:none;border:1.5px solid var(--line);background:var(--card);border-radius:999px;cursor:pointer;transition:background .3s,border-color .3s}
.burger:hover{background:var(--mint-soft);border-color:var(--mint-strong)}
.burger svg{width:20px;height:20px;stroke:var(--ink);fill:none;stroke-width:2}

@media(max-width:860px){
  .nav{gap:10px}
  .menu{display:none}
  /* .links carries the auto margin that pushes everything right, and .links is
     inside the panel — so on a phone the chip takes it over. */
  .langpicker{margin-inline-start:auto}
  .burger{display:inline-flex}
  /* Absolute inside the sticky <header>, so the panel is full-bleed, needs no
     hard-coded header height, and overlays the page instead of shoving it
     down. In RTL `inset-inline` covers both edges, so nothing to mirror. */
  body.menu-open .menu{display:flex;flex-direction:column;align-items:stretch;
    position:absolute;top:100%;inset-inline:0;
    background:var(--paper);border-bottom:1px solid var(--line);
    padding:14px 22px 22px;gap:10px;
    box-shadow:0 30px 50px rgba(var(--shadow),.14)}
  body.menu-open .links{flex-direction:column;align-items:stretch;gap:4px;margin-inline-start:0}
  body.menu-open .links a.nl{font-size:1.05rem;padding:12px 10px}
  body.menu-open .dl{align-self:start}
  .langpicker-menu ul{grid-template-columns:repeat(2,1fr)}
}

/* download dropdown */
.dl{position:relative}
.dl-btn{display:inline-flex;align-items:center;gap:8px;background:var(--forest);color:var(--on-forest);border:1.5px solid transparent;border-radius:999px;padding:11px 22px;font-family:var(--sans);font-size:.98rem;font-weight:600;cursor:pointer;text-decoration:none;transition:background .25s,transform .25s var(--ease),box-shadow .25s}
.dl-btn:hover{background:var(--forest-deep);transform:translateY(-1px);box-shadow:0 10px 24px rgba(4,97,60,.22);color:var(--on-forest)}
.dl-menu{position:absolute;inset-inline-end:0;top:calc(100% + 12px);background:var(--card);border:1px solid var(--line);border-radius:18px;padding:16px;box-shadow:0 22px 48px rgba(var(--shadow),.16);min-width:250px;display:none;flex-direction:column;gap:10px;z-index:40}
.dl.open .dl-menu{display:flex;animation:menuIn .3s var(--ease)}
@keyframes menuIn{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}
.dl-menu .store-badge{width:100%;justify-content:start}
.dl-menu small{color:var(--muted);font-size:.78rem;line-height:1.4}

/* ---------- hero ------------------------------------------------------------
 * Two columns: the words, then the device stack. The old hero was one centred
 * column with two tilted phones under it, which pushed everything else below
 * the fold; this puts the headline and the download buttons next to the product
 * instead of above it.
 *
 * The mint glow is one radial-gradient on ::before. It replaces two blurred,
 * keyframed "blobs" — 60px of blur animated forever is a repaint per frame on
 * every phone that loads the page, and the gradient is indistinguishable. */
.hero{position:relative;display:grid;grid-template-columns:1.05fr 1fr;gap:40px;align-items:center;padding-block:56px 40px}
.hero::before{content:"";position:absolute;inset-block:auto -80px;inset-inline:auto -10%;width:520px;height:520px;border-radius:50%;background:radial-gradient(closest-side,var(--mint) 0%,transparent 72%);z-index:-1;opacity:.9}
.hero .kicker{display:inline-flex;align-items:center;gap:8px;font-size:.85rem;font-weight:600;color:var(--forest);background:var(--mint-soft);border:1px solid var(--mint);border-radius:999px;padding-block:6px;padding-inline:8px 12px}
.hero .kicker .dots{display:inline-flex;gap:3px;flex:none}
.hero .kicker .dots i{display:block;width:8px;height:8px;border-radius:50%}
.hero .kicker .dots i:nth-child(1){background:var(--cat-green)}
.hero .kicker .dots i:nth-child(2){background:var(--cat-amber)}
.hero .kicker .dots i:nth-child(3){background:var(--cat-plum)}
.hero .kicker .dots i:nth-child(4){background:var(--cat-sky)}
.hero h1{font-size:clamp(2.7rem,6.4vw,5.4rem);margin-block:18px 16px;line-height:.98}

/* The highlighted half of the headline.
 *
 * This used to be white-space:nowrap with an absolutely-positioned SVG
 * underline stretched across it. That worked in English (16 characters) and
 * would have pushed the headline off-screen in German, Finnish, Turkish and
 * Polish. The underline is now a background gradient on the inline box, so it
 * wraps with the text; box-decoration-break:clone draws it under every line. */
.hero h1 em{color:var(--forest);font-style:normal;
  background-image:linear-gradient(var(--mint-strong),var(--mint-strong));
  background-repeat:no-repeat;background-size:100% .18em;background-position:0 88%;
  -webkit-box-decoration-break:clone;box-decoration-break:clone;
  padding-bottom:.05em}
.hero .lead{font-size:1.18rem;color:var(--muted);max-width:34rem}
.hero .ctas{margin-block-start:26px}
.hero .note{display:flex;gap:14px;flex-wrap:wrap;margin-block-start:18px;font-size:.9rem;color:var(--muted)}
.hero .note span::before{content:"✓";color:var(--forest);font-weight:700;margin-inline-end:6px}

/* ---------- device stack: the phone in front, the browser window behind ---- */
.stack{position:relative;height:560px}
.laptop{position:absolute;top:20px;inset-inline-end:0;width:88%;border:1px solid var(--line);border-radius:14px;background:var(--card);box-shadow:0 30px 70px rgba(var(--shadow),.16);overflow:hidden;transform:rotate(var(--rot-laptop))}
.laptop .bar{display:flex;align-items:center;gap:6px;padding-block:9px;padding-inline:12px;border-bottom:1px solid var(--line);background:var(--mint-soft)}
.laptop .bar i{display:block;width:9px;height:9px;flex:none;border-radius:50%;background:var(--line)}
/* A URL is LTR even in /ar/, and it is ellipsised rather than wrapped: this is
   a drawing of a browser, and browsers do not wrap their address bar. */
.laptop .bar .url{flex:1;min-width:0;margin-inline-start:10px;padding-block:3px;padding-inline:8px;border:1px solid var(--line);border-radius:6px;background:var(--card);color:var(--muted);font-size:.7rem;direction:ltr;text-align:start;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.phone{position:absolute;bottom:0;inset-inline-start:0;width:44%;padding:9px;border-radius:36px;background:var(--frame);box-shadow:0 34px 70px rgba(var(--shadow),.26);transform:rotate(var(--rot-phone))}
.phone img{width:100%;border-radius:28px}

/* ---------- buttons ---------- */
.btn{display:inline-flex;align-items:center;gap:8px;border:1.5px solid transparent;border-radius:999px;padding-block:12px;padding-inline:22px;font-size:.98rem;font-weight:600;text-decoration:none;transition:transform .25s var(--ease),background .25s,box-shadow .25s}
.btn-primary{background:var(--forest);color:var(--on-forest)}
.btn-primary:hover{background:var(--forest-deep);color:var(--on-forest);transform:translateY(-1px);box-shadow:0 10px 24px rgba(4,97,60,.22)}
.web-link{display:inline-flex;align-items:center;gap:8px;padding-block:10px;padding-inline:4px;color:var(--forest);font-weight:600;text-decoration:none}
.web-link svg{width:18px;height:18px;flex:none;stroke:currentColor;fill:none;stroke-width:2}
.web-link:hover{text-decoration:underline;text-underline-offset:4px}

/* ---------- store badges ---------- */
/* .ctas is the row; .badges is what script.js looks for when it promotes the
   store the visitor is actually on. Start-aligned, not centred: the hero copy
   is a left column now, and the price block's badges sit under a left column
   too — there is nowhere left on the page where a centred row is right. */
.ctas{display:flex;gap:12px;flex-wrap:wrap;align-items:center}
.store-badge{display:inline-flex;align-items:center;gap:10px;background:var(--frame);color:#fff;border-radius:13px;padding-block:9px;padding-inline:13px 16px;text-decoration:none;line-height:1.15;cursor:pointer;transition:opacity .15s,transform .3s var(--ease)}
.store-badge:hover{opacity:.85;color:#fff;transform:translateY(-1px)}
.store-badge svg{width:22px;height:22px;fill:#fff;flex:none}
.store-badge small{display:block;font-size:.65rem;font-weight:400;opacity:.8;color:#fff}
.store-badge span{font-weight:600;font-size:.98rem}
/* The platform the visitor is actually on gets the emphasis; the other stays
   available. Applied by script.js — with no JS both simply render equal. */
.store-badge.primary{outline:2px solid var(--mint-strong);outline-offset:2px}
.store-badge.secondary{opacity:.72}
.store-badge.secondary:hover{opacity:1}
/* A store that is not live yet: a <button>, not a link. Same box as a real
   badge so the row does not shift when the flag flips, visibly dimmed so it
   never reads as the thing to tap, and no hover lift — there is nowhere to
   go. script.js never adds .primary to it; the reset below is the belt to
   that braces, so a stray class can't outline a dead store. */
button.store-badge{border:0;margin:0;font-family:inherit;font-size:inherit;text-align:inherit;-webkit-appearance:none;appearance:none}
.store-badge.soon{opacity:.6}
.store-badge.soon:hover{opacity:.6;transform:none}
.store-badge.soon.primary{outline:none}
/* Full-width under the badge row in .ctas (a wrapping flex row) and a normal
   block in the nav's .dl-menu column. Logical properties only — this sits
   under an RTL badge row in /ar/ too. */
.store-soon{flex-basis:100%;width:100%;margin-block-start:10px;font-size:.85rem;line-height:1.4;color:var(--muted);text-align:start}

/* The same paragraph, promoted to a popover anchored to the chip.
 *
 * Everything below hangs off .soon-pop / .soon-anchor, which ONLY script.js
 * adds. With JavaScript off not one of these rules matches and the sentence
 * stays the plain line above — which is the whole reason the note ships
 * visible in the HTML rather than being built here.
 *
 * .soon-anchor is the chip's container (.badges). script.js applies it only
 * where the container is still position:static: .dl-menu is already absolute
 * and is its own containing block, and turning it relative would drop the nav
 * dropdown into the flow. The z-index keeps the card above the ✓ line that
 * follows it in the hero. */
.soon-anchor{position:relative;z-index:35}
.store-soon.soon-pop{
  position:absolute;z-index:35;                      /* under .sticky-cta (40) */
  inset-block-start:0;inset-inline-start:0;          /* both set by script.js */
  /* At 360px in English the badge row wraps and the card lands on top of the
     Google Play badge — the one thing on the page that is actually tappable.
     pointer-events:none means the tap goes straight through to Play and the
     document handler dismisses the card on the way, instead of the card
     swallowing it. Purely informational, so it gives up nothing but the
     ability to select the sentence. */
  pointer-events:none;
  width:max-content;max-width:min(280px,calc(100vw - 32px));
  margin-block-start:0;padding:11px 14px;
  background:var(--card);color:var(--body);
  border:1px solid var(--line);border-radius:14px;
  box-shadow:0 14px 34px rgba(var(--shadow),.18);
  font-size:.85rem;line-height:1.45;text-align:start;
  animation:soonIn .18s var(--ease)}
@keyframes soonIn{from{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:none}}
/* The arrow: a 12px square rotated onto its corner, half of it tucked under
   the card so its own background hides the card's top border.
   Its borders are PHYSICAL on purpose. rotate() does not mirror in RTL (see
   the --rot-* note at the top of this file), so top+left is what puts the tip
   at the top in both directions; border-inline-start would point it sideways
   in /ar/. Only the offset along the row is logical, and script.js overrides
   it so the tip keeps tracking the chip when the card has been clamped. */
.store-soon.soon-pop::before{
  content:"";position:absolute;
  top:-6px;inset-inline-start:var(--soon-arrow,16px);
  width:12px;height:12px;
  background:var(--card);
  border-top:1px solid var(--line);border-left:1px solid var(--line);
  transform:rotate(45deg)}

/* ---------- section furniture ---------- */
section.block{padding-block:84px 20px}
.sec-head{display:flex;align-items:end;justify-content:space-between;gap:24px;flex-wrap:wrap;margin-block-end:34px}
.sec-head>div{max-width:36rem}
.sec-head h2{margin-block-start:10px}
/* The display size lives on these three selectors rather than on h2, so the
   legal pages, the blog and the release notes keep the sizes they set. */
.sec-head h2,.web h2,.price h2{font-size:clamp(1.9rem,4.4vw,3.1rem);line-height:1.06}
.eyebrow{display:inline-flex;align-items:center;gap:8px;font-family:var(--display);font-weight:700;text-transform:uppercase;letter-spacing:.12em;font-size:.82rem;color:var(--forest)}
.eyebrow::before{content:"";width:22px;height:2px;flex:none;border-radius:2px;background:var(--forest)}
.section-lead{font-size:1.12rem;color:var(--muted);max-width:38rem;margin-block-start:12px}
/* Second column of a two-part section head, so it wraps under at 760px. */
.sec-head>.section-lead{max-width:26rem;margin:0}

/* ---------- trust strip ---------- */
/* Three claims in a row between two rules. Not three bordered cards inside a
   bordered, tinted panel, which is what this replaced. */
.trust{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;padding-block:28px 10px;margin-block-start:24px;border-block:1px solid var(--line)}
.trust>div{display:flex;gap:12px;align-items:flex-start;padding-block:12px}
.trust svg{width:26px;height:26px;flex:none;margin-block-start:2px;stroke:var(--forest);fill:none;stroke-width:1.8}
.trust strong{display:block;font-family:var(--display);font-size:1.2rem;font-weight:700}
.trust p{font-size:.9rem;color:var(--muted)}
@media(max-width:760px){.trust{grid-template-columns:1fr;gap:0}}

/* ---------- feature grid ---------- */
/* Seven tiles in one bento, replacing seven full-width alternating blocks and a
   six-screen gallery strip. Two tiles carry a screenshot that leans out of the
   bottom corner; the rest are words, which is why the grid is readable at all. */
.bento{display:grid;grid-template-columns:repeat(6,1fr);grid-auto-rows:minmax(150px,auto);gap:16px}
.tile{position:relative;overflow:hidden;display:flex;flex-direction:column;gap:10px;padding:26px;border:1px solid var(--line);border-radius:22px;background:var(--card);transition:transform .3s var(--ease),box-shadow .3s}
.tile:hover{transform:translateY(-3px);box-shadow:0 22px 50px rgba(var(--shadow),.12)}
.tile .copy{display:flex;flex-direction:column;gap:10px;min-width:0}
.tile h3{font-size:1.45rem;margin-block-start:2px}
.tile p{font-size:.95rem;color:var(--muted);max-width:30rem}
.tile ul{list-style:none;display:flex;flex-direction:column;gap:6px;margin-block-start:4px;font-size:.92rem}
.tile ul li::before{content:"✓";color:var(--forest);font-weight:700;margin-inline-end:8px}
.tile .ic{width:40px;height:40px;flex:none;border-radius:12px;background:var(--mint);display:grid;place-items:center}
.tile .ic svg{width:20px;height:20px;stroke:var(--forest);fill:none;stroke-width:2}
.tile .shot{position:absolute;bottom:-38%;inset-inline-end:-8%;width:56%;padding:6px;border-radius:22px;background:var(--frame);box-shadow:0 20px 40px rgba(var(--shadow),.25);transform:rotate(var(--rot-shot))}
.tile .shot img{width:100%;border-radius:17px}
.t-cal{grid-column:span 4;grid-row:span 2;min-height:420px}
.t-cal .copy{max-width:52%}
.t-cal .shot{width:31%;bottom:-20%;inset-inline-end:5%;transform:rotate(var(--rot-shot-cal))}
.t-shop{grid-column:span 2;grid-row:span 2}
/* The narrowest tile that carries both a screenshot and bullets, so it is the
   one place the two can collide: the copy stops at 56% and the phone starts at
   about 68% of the tile. The mock's 62%/56% left the last two bullets running
   under the bezel. */
.t-shop .copy{max-width:56%}
.t-shop .shot{width:52%;bottom:-52%;inset-inline-end:-20%;transform:rotate(var(--rot-shot-shop))}
.t-todo,.t-meal,.t-bday{grid-column:span 2}
.t-vault{grid-column:span 3;background:var(--mint-soft)}
.t-fam{grid-column:span 3;background:var(--amber-soft)}
.t-fam .ic{background:var(--amber-ic)}
.t-fam .ic svg{stroke:var(--amber-ink)}
/* Five overlapping avatars, and deliberately no initials in them: a letter per
   dot would be five more strings in 23 content files for decoration. */
.t-fam .avs{display:flex;margin-block-start:auto}
.t-fam .avs i{width:38px;height:38px;border-radius:50%;border:3px solid var(--card);margin-inline-start:-10px}
.t-fam .avs i:first-child{margin-inline-start:0}
.t-fam .avs i:nth-child(1){background:var(--cat-green)}
.t-fam .avs i:nth-child(2){background:var(--cat-amber)}
.t-fam .avs i:nth-child(3){background:var(--cat-plum)}
.t-fam .avs i:nth-child(4){background:var(--cat-sky)}
.t-fam .avs i:nth-child(5){background:var(--cat-red)}
@media(max-width:960px){
  .bento{grid-template-columns:repeat(2,1fr)}
  .tile{grid-column:span 2;grid-row:auto}
  .t-cal{min-height:380px}
  .t-cal .copy{max-width:60%}
  .t-shop{min-height:300px}
  .t-shop .copy{max-width:70%}
}
@media(max-width:600px){
  .bento{grid-template-columns:1fr}
  .tile{grid-column:span 1}
  .t-cal,.t-shop{min-height:0}
  .t-cal .copy,.t-shop .copy{max-width:100%}
  .t-cal .shot,.t-shop .shot{display:none}
}

/* ---------- also on your computer ---------- */
.web{display:grid;grid-template-columns:1fr 1.15fr;gap:48px;align-items:center;margin-block-start:20px;padding:48px;border-radius:28px;background:var(--frame);color:#fff}
.web h2,.web p,.web li{color:#fff}
.web p{opacity:.85}
.web .eyebrow{color:var(--web-mint)}
.web .eyebrow::before{background:var(--web-mint)}
.web ul{list-style:none;display:flex;flex-direction:column;gap:10px;margin-block:18px 26px;font-size:1rem}
.web ul li{display:flex;gap:10px;align-items:flex-start}
.web ul li::before{content:"";width:18px;height:18px;flex:none;margin-block-start:3px;border-radius:50%;background:var(--web-mint);mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20 6L9 17l-5-5' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/70% no-repeat;-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20 6L9 17l-5-5' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/70% no-repeat}
.web .btn-primary{background:var(--web-mint);color:var(--web-mint-ink)}
.web .btn-primary:hover{background:#fff;color:var(--web-mint-ink)}
.web .muted{font-size:.85rem;opacity:.65;margin-block-start:12px}
/* The same browser frame as the hero, but in the flow rather than tilted
   behind a phone. */
.web .laptop{position:static;width:100%;transform:none;box-shadow:0 30px 60px rgba(0,0,0,.35)}
@media(max-width:900px){.web{grid-template-columns:1fr;gap:32px;padding-block:32px;padding-inline:24px}}

/* ---------- steps ---------- */
.steps{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;counter-reset:step}
.step{position:relative;padding-block:26px;padding-inline:24px;border:1px solid var(--line);border-radius:20px;background:var(--card)}
.step::before{counter-increment:step;content:counter(step);display:block;margin-block-end:10px;font-family:var(--display);font-weight:800;font-size:2.6rem;line-height:1;color:var(--mint-strong)}
.step h3{font-size:1.35rem}
.step p{font-size:.95rem;color:var(--muted);margin-block-start:6px}
@media(max-width:760px){.steps{grid-template-columns:1fr}}

/* ---------- comparison ---------- */
.table-scroll{overflow-x:auto}
/* min-width, so at 390px the table scrolls inside .table-scroll instead of
   squeezing four columns into 346px. */
.cmp{width:100%;min-width:520px;border-collapse:collapse;margin-block-start:8px;font-size:.98rem}
.cmp thead th{padding-block:12px;padding-inline:10px;font-family:var(--display);font-weight:700;letter-spacing:.06em;text-transform:uppercase;font-size:.8rem;color:var(--muted);text-align:center;vertical-align:bottom}
.cmp thead th:first-child{text-align:start}
.cmp td{padding-block:14px;padding-inline:10px;border-top:1px solid var(--line);text-align:center;font-weight:600}
.cmp tbody th{padding-block:14px;padding-inline:10px;border-top:1px solid var(--line);font-family:var(--sans);font-size:.98rem;font-weight:500;letter-spacing:normal;text-transform:none;text-align:start;color:var(--body)}
.cmp td.y{color:var(--forest)}
.cmp td.n,.cmp td.p{color:var(--muted);font-weight:400}
/* The tinted Daili column. `.d` is on every cell of it (build.mjs) because
   Firefox ESR does not support :has(), and a comparison table where the answer
   column is not marked is a table with no answer in it. The :has() rule is the
   mock's and is kept as the progressive half. */
.cmp td.d,.cmp thead th.d{background:var(--mint-soft)}
.cmp tr:has(td) td:nth-child(2){background:var(--mint-soft)}
.cmp thead th.d{color:var(--forest);border-radius:14px 14px 0 0}
.cmp tbody tr:last-child td.d{border-radius:0 0 14px 14px}
.legend{font-size:.85rem;color:var(--muted);margin-block-start:10px}

/* ---------- pricing ---------- */
.price{display:grid;grid-template-columns:1.1fr .9fr;gap:40px;align-items:center;padding:48px;border:1px solid var(--mint);border-radius:28px;background:var(--mint-soft)}
.price .big{font-family:var(--display);font-size:clamp(4rem,10vw,7rem);font-weight:800;line-height:.9;color:var(--forest)}
.price .big small{display:block;margin-block-start:8px;font-family:var(--sans);font-size:1.1rem;font-weight:600;line-height:1.4;letter-spacing:0;color:var(--muted)}
.price ul{list-style:none;display:flex;flex-direction:column;gap:10px;margin-block:18px 24px}
.price ul li::before{content:"✓";color:var(--forest);font-weight:700;margin-inline-end:10px}
.price .promise{padding-block:14px;padding-inline:16px;border:1px solid var(--mint-strong);border-radius:16px;background:var(--card);font-size:.95rem;color:var(--body)}
.price .promise strong{display:block;margin-block-end:4px;font-family:var(--display);letter-spacing:.06em;text-transform:uppercase;font-size:.8rem;color:var(--forest)}
.price .ctas{margin-block-start:22px}
@media(max-width:860px){.price{grid-template-columns:1fr;gap:28px;padding-block:32px;padding-inline:24px}}

/* ---------- faq ---------- */
.faq{max-width:760px}
.faq details{border-top:1px solid var(--line);padding-block:6px}
.faq details:last-child{border-bottom:1px solid var(--line)}
.faq summary{list-style:none;cursor:pointer;display:flex;justify-content:space-between;align-items:center;gap:16px;padding-block:14px;padding-inline:4px;font-weight:600;font-size:1.05rem}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:"+";font-family:var(--display);font-size:1.6rem;font-weight:700;line-height:1;flex:none;color:var(--forest);transition:transform .25s}
.faq details[open] summary::after{transform:rotate(45deg)}
.faq details p{padding-block-end:18px;padding-inline:4px;color:var(--body);max-width:62ch}

/* ---------- sticky mobile CTA ---------- */
.sticky-cta{position:fixed;inset-inline:0;bottom:0;z-index:40;display:none;align-items:center;gap:10px;justify-content:center;padding:12px 16px calc(12px + env(safe-area-inset-bottom));background:color-mix(in srgb,var(--paper) 94%,transparent);backdrop-filter:blur(10px);border-top:1px solid var(--line)}
.sticky-cta[hidden]{display:none}
.sticky-cta .dl-btn{flex:1;max-width:22rem;text-align:center}
/* Not a link and not styled as one: on iOS there is nothing to link to. */
.sticky-soon{flex:1;max-width:22rem;text-align:center;color:var(--body);font-size:.92rem;line-height:1.35}
.sticky-close{background:none;border:none;font-size:1.5rem;line-height:1;color:var(--muted);cursor:pointer;padding:4px 8px}

/* ---------- footer ---------- */
footer{border-top:1px solid var(--line);margin-top:90px;padding:38px 0 44px;font-size:.9rem;color:var(--muted)}
footer .cols{display:flex;gap:20px;flex-wrap:wrap;align-items:center;justify-content:space-between}
/* The links are a row with its own gaps rather than a sentence with separators:
   at 390px the row wraps to three lines and a trailing "·" at the end of a line
   reads as a typo. */
footer .flinks{display:flex;gap:6px 18px;flex-wrap:wrap}
footer a{color:var(--muted);text-decoration:none;transition:color .25s}
footer a:hover{color:var(--forest);text-decoration:underline;text-underline-offset:3px}

/* ---------- legal pages ---------- */
.legal{max-width:760px;margin:0 auto;padding:48px 20px}
.legal h1{font-size:2rem;margin-bottom:6px}
.legal .updated{color:var(--muted);font-size:.9rem;margin-bottom:26px}
.legal h2{font-size:1.25rem;margin:28px 0 8px}
.legal h3{font-size:1.08rem;margin:22px 0 8px}
.legal p,.legal li{color:var(--body);font-size:.98rem}
.legal ul{margin-block:8px;margin-inline-start:22px}
/* The binding-language note at the top of every translated legal body. Muted
   and small on purpose: it is a disclaimer above the title, not a headline. */
.legal .translated{color:var(--muted);font-size:.88rem;margin-bottom:18px}
.notice{background:var(--mint);border:1px solid var(--mint-strong);border-radius:14px;padding:12px 16px;margin:18px 0;font-size:.92rem}

/* ---------- responsive ----------
   The grid's own breakpoints live with the grid (feature tiles at 960/600, the
   web block at 900, the price at 860). What is left here is everything that
   crosses sections. */
@media(max-width:960px){
  .hero{grid-template-columns:1fr;gap:24px;padding-block-start:36px}
  /* The glow is 520px wide and hangs 10% past the hero's end edge, which at
     360px is 10px past the viewport. Pulled back to the edge once the hero is
     one column, so nothing but overflow-x:clip stands between the page and a
     horizontal scrollbar. */
  .hero::before{inset-inline-end:0;width:min(520px,100%)}
  .stack{height:auto;aspect-ratio:1/.95;width:100%;max-width:560px;margin-block-start:10px;margin-inline:auto}
  section.block{padding-block:64px 16px}
}
@media(max-width:820px){
  .langpicker-menu ul{grid-template-columns:repeat(2,1fr)}
  /* Only shown on small screens, and only after script.js unhides it. */
  .sticky-cta:not([hidden]){display:flex}
  body{padding-bottom:76px}
}
@media(max-width:600px){
  /* One column, phone centred, no browser window behind it: at 390px the frame
     is 340px wide and the screenshot inside it is unreadable anyway. */
  .stack{aspect-ratio:auto;height:auto;padding-block-start:10px}
  .stack .laptop{display:none}
  .phone{position:relative;bottom:auto;width:62%;margin-inline:auto;transform:none}
  .web{padding:26px 18px}
  .price{padding:26px 18px}
}

/* ---------- dark mode ---------- */
/* Only the tokens are redefined. Everything above inherits, which is the whole
   point of having tokenised the palette. */
@media(prefers-color-scheme:dark){
  :root{
    --paper:#0E1712; --card:#16211A; --ink:#EDF2EC; --body:#CBD6CE; --muted:#93A498;
    --forest:#5EC08F; --forest-deep:#7BD3A6; --on-forest:#08160F;
    --mint:#1D3327; --mint-strong:#2E4F3C; --mint-soft:#152219;
    --amber:#F0BC5E; --amber-soft:#2E2718;
    --line:#263A2E;
    /* --frame stays dark: a phone bezel that inverts to white reads as a
       rendering bug, and the store badges sit on it with white text. */
    --frame:#0A110D;
    --shadow:0,0,0;
    color-scheme:dark;
    --amber-ic:#3E3320; --amber-ink:#F0BC5E;
    /* The dark block's accent goes lighter, not darker: see --web-mint above. */
    --web-mint:#7BD3A6; --web-mint-ink:#08160F;
    --cream:#141D18;
  }
  .store-badge{background:#000;border:1px solid var(--line)}
  /* The border above comes from a shorthand the <button> reset zeroes out. */
  button.store-badge{border:1px solid var(--line)}
  .store-badge.soon{opacity:.62}
  .store-badge.soon:hover{opacity:.62}
  /* The popover's fill, text, border and arrow are all tokens that flip above,
     so --card on --paper keeps the card a shade lighter than the page here
     too. The shadow is the one thing that does not carry over: --shadow turns
     into 0,0,0 and .18 of black on a near-black page is invisible, which would
     leave the card floating with only its 1px border. */
  .store-soon.soon-pop{box-shadow:0 16px 40px rgba(var(--shadow),.55)}
  /* A dash at .5 opacity of --muted disappears on a near-black ground; this is
     the one mark in the table that needs its own dark value. */
  .cmp td.n{color:#5A6B61}
  .phone{box-shadow:0 34px 70px rgba(0,0,0,.5)}
  .laptop{box-shadow:0 30px 70px rgba(0,0,0,.45)}
}

/* ---------- reduced motion ---------- */
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation:none!important;transition:none!important}
  /* The hover lifts are the only motion left on the page that a transition
     rule does not already stop — a transform with no transition still jumps. */
  .tile:hover,.btn-primary:hover,.store-badge:hover,.dl-btn:hover{transform:none}
  html{scroll-behavior:auto}
}

/* ---------- blog post ---------- */
/* Every rule here is built on the tokens redefined in the dark-mode block
   above, so the post gets dark mode for free — no colour literal below. */
.post{max-width:68ch;margin:0 auto;padding:40px 20px 64px}
.post-hero{margin-bottom:26px;border-radius:var(--radius);overflow:hidden;background:var(--cream)}
.post-hero img{width:100%}
.post h1{font-size:clamp(1.7rem,4.6vw,2.3rem);line-height:1.2;letter-spacing:-.02em;margin-bottom:10px}
.post-byline{color:var(--muted);font-size:.9rem;margin-bottom:28px;padding-bottom:18px;border-bottom:1px solid var(--line)}
.post h2{font-size:1.38rem;line-height:1.3;letter-spacing:-.01em;margin:38px 0 12px}
.post h3{font-size:1.06rem;margin:24px 0 6px}
.post p,.post li{color:var(--body);font-size:1.02rem}
.post p{margin-bottom:16px}
.post ul,.post ol{margin-bottom:18px;padding-inline-start:24px}
.post li{margin-bottom:9px}
.post strong{color:var(--ink)}
.post a{text-underline-offset:3px}
/* Post 2 names files inline (`emma-vaccinations`, not IMG_4471). Without a
   rule the UA's monospace default shrinks them mid-sentence. Monospace alone is
   enough to mark a filename in prose — a boxed chip reads as developer docs and
   its padding leaves the following full stop floating away from the word. */
.post code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.9em;color:var(--ink)}

/* Post 11 is the first post to carry a blockquote, and it is not a quotation —
   it is a letter template the reader is meant to copy or print. So it is marked
   as a block the eye can lift off the page rather than as an aside: the cream
   ground and the rule down the leading edge say "this part is the document".
   The UA's own blockquote margin is dropped; padding does that job here, and
   40px of it would push the ruled edge away from the measure on a phone.
   `padding-inline-start` and a border on the inline edge, not left, so the RTL
   locales the rest of this file is careful about would flip it correctly if the
   blog ever gains one. */
.post blockquote{margin:0 0 22px;padding:20px 22px;background:var(--cream);border-inline-start:3px solid var(--line);border-radius:calc(var(--radius) - 8px)}
.post blockquote p:last-child{margin-bottom:0}

/* The one table in the post is wider than a phone. It scrolls inside its own
   box; the page itself must never scroll sideways. */
.table-wrap{overflow-x:auto;margin-bottom:22px;border:1px solid var(--line);border-radius:14px}
.post-table{border-collapse:collapse;width:100%;min-width:500px;font-size:.93rem}
.post-table th,.post-table td{border-bottom:1px solid var(--line);padding:11px 14px;text-align:start;color:var(--body)}
.post-table thead th{background:var(--cream);font-size:.76rem;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);font-weight:700}
.post-table tbody th{font-weight:600;color:var(--ink)}
.post-table tbody tr:last-child>*{border-bottom:0}

/* The FAQ is the last section of the article, not a widget bolted on after it:
   same type scale as the prose above, one rule to mark the seam. */
.post-faq{margin-top:42px;padding-top:24px;border-top:1px solid var(--line)}

.post-cta{margin-top:42px;background:var(--mint);border:1px solid var(--mint-strong);border-radius:calc(var(--radius) - 4px);padding:26px 24px}
.post-cta h2{margin-top:0;font-size:1.18rem;color:var(--forest-deep)}
.post-cta p:last-child{margin-bottom:0}
.post-cta-link a{font-weight:600}

@media(max-width:560px){
  .post{padding:22px 18px 48px}
  .post-hero{border-radius:16px}
  .post-cta{padding:22px 18px}
}

/* ---------- printing a post ---------- */
/* Post 3 is a 27-item checklist, and a checklist is printed and stuck to the
   inside of a cupboard door. That is the whole reason the post exists — people
   link to what prints well — so the print sheet is part of the article, not a
   nicety.

   What goes: the header (and with it the language picker), the footer, the skip
   link, the hero photo and the CTA. None of them are the checklist, and the
   hero alone is half a sheet of ink. What stays: the h1, the byline, the prose
   and every numbered item.

   The ☐ is scoped to .doc-checklist rather than to .post ol, so a future post
   with an ordinary numbered list does not print as a to-do list. The list
   marker still supplies the number; the box sits after it. */
@page{margin:16mm}

@media print{
  /* Black on white whatever the screen was doing. The dark-mode block above is
     a media query, not a class, so a laptop set to dark would otherwise print
     the checklist as pale grey on near-black — 27 items of it. Every rule in
     this file reads a token, so putting the tokens back here is the whole fix;
     this block comes later in the file, so it wins. */
  :root{
    --paper:#fff; --card:#fff; --cream:#fff;
    --ink:#000; --body:#1a1a1a; --muted:#3f3f3f; --line:#b9b9b9;
  }
  html,body{background:#fff}

  header,footer,.skip,.post-hero,.post-cta{display:none}

  .post{max-width:none;margin:0;padding:0}
  .post h1{font-size:19pt}
  .post h2{font-size:13pt;margin:18pt 0 6pt}
  .post h3{font-size:11.5pt}
  .post p,.post li{font-size:10.5pt}
  .post-byline{margin-bottom:14pt;padding-bottom:8pt}

  /* A printed link cannot be clicked, and blue underlines all down a checklist
     read as noise. The href is not printed after the text either: three of them
     are internal and one is a 130-character German URL. */
  .post a{color:inherit;text-decoration:none}

  .doc-checklist li{margin-bottom:6pt;break-inside:avoid}
  .doc-checklist li::before{content:"\2610\00a0\00a0"}

  /* A heading stranded at the foot of a page takes its section's first items
     with it and the printed list stops looking like one list. */
  .post h2{break-after:avoid}

  /* Post 11's consent letter is the one block on the blog people print in order
     to fill it in, so it gets the two rules that matter on paper. It must not
     split across a page break — half a letter on each sheet is not a letter —
     and it needs a real box: the tokens above turn every ground white, which
     would leave the cream panel invisible and the template indistinguishable
     from the prose around it. */
  .post blockquote{background:none;border:1px solid var(--line);padding:11pt 13pt;break-inside:avoid}
}

/* ---------- what's new (release notes) ---------- */
/* Built on .legal, so measure, headings and dark mode all come for free; these
   are only the rules that separate one release block from the next. */
.release+.release{margin-top:30px;padding-top:26px;border-top:1px solid var(--line)}
.release h2{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px}
.release h2 small{font-size:.82rem;font-weight:500;color:var(--muted)}
.release ul{margin-block:10px}

/* The nav no longer hides the Blog link below 560px. That rule existed because
   the old header wrapped: six items ran off the right edge in Arabic and grew a
   third row in five other locales. Under 860px the links are now a panel, one
   per line, so width is no longer what decides whether the link fits — and the
   Blog link is reachable in every locale at every size, which the footer alone
   used to have to cover. */

/* ---------- blog index ---------- */
.blogindex{max-width:820px;padding-block:44px 64px}
.blogindex h1{font-size:clamp(1.8rem,5vw,2.4rem);letter-spacing:-.02em;margin-bottom:8px}
.blogindex-intro{color:var(--muted);font-size:1.02rem;max-width:60ch;margin-bottom:34px}
.postlist{list-style:none}
.postlist-item{display:grid;grid-template-columns:220px 1fr;gap:22px;align-items:start;padding:24px 0;border-top:1px solid var(--line)}
.postlist-art{display:block;border-radius:14px;overflow:hidden;background:var(--cream)}
.postlist-art img{width:100%}
.postlist-txt h2{font-size:1.22rem;line-height:1.32;letter-spacing:-.01em;margin-bottom:8px}
.postlist-txt h2 a{color:var(--ink);text-decoration:none}
.postlist-txt h2 a:hover{color:var(--forest);text-decoration:underline;text-underline-offset:3px}
.postlist-txt p{color:var(--body);font-size:.97rem;margin-bottom:10px}
.postlist-txt time{color:var(--muted);font-size:.86rem}
@media(max-width:640px){
  .postlist-item{grid-template-columns:1fr;gap:14px}
  .postlist-art{max-width:100%}
}
