/*  Loment — site design system

    Aesthetic direction: the authority of a language's front page — a slab
    serif that carries weight, ink on paper, generous vertical rhythm, a rule
    between every section. Deliberately NOT a copy of any other language's
    site: different faces, different accent, and one motif that comes from
    Loment's own idea rather than from anyone's house style.

    The motif is the boundary. Loment's central distinction is a sealed
    artifact against a hosted one, so the page draws that everywhere: a solid
    hairline encloses what is closed, a dashed one marks what is open. Nothing
    else is decorated.

    Type: Bitter (slab display) for the wordmark and section heads, Public
    Sans for prose, JetBrains Mono for code and for small structural labels —
    that last pair is the one place the page admits it is about a machine.
*/

/* ------------------------------------------------------------- tokens */

:root {
  --ink:      #1d2733;   /* body text, and the slab heads        */
  --ink-2:    #55606f;   /* secondary prose                      */
  --faint:    #8891a0;   /* captions, gutter numbers             */

  --paper:    #ffffff;
  --wash:     #f7f9fc;   /* the one tint, for panels and code    */
  --line:     #e3e8ef;   /* hairlines                            */
  --line-2:   #cbd4e0;   /* a boundary you are meant to notice   */

  --blue:     #2f4bff;   /* the accent, and it is spent sparingly */
  --blue-dk:  #1b34d6;
  --ochre:    #8a5a00;   /* used only to mark an honest boundary  */

  /* The three webfonts carry Latin only. Every stack therefore ends in the
     system CJK faces, so a Chinese or Japanese page sets its Latin in Public
     Sans and its CJK in whatever the reader's platform does best — which is
     also why the site ships no CJK webfont: those are megabytes, and the
     system faces are better than a subset nobody tuned.

     Two things to know before changing these. The CJK end of `--display` is a
     **gothic** face, not a serif one: the serif CJK that a Windows reader
     actually has is SimSun, and SimSun at 6.5rem looks like a blurry
     newspaper. And `--sans` therefore also ends in gothic, so CJK headings and
     CJK body differ by size and weight rather than by family, which is how CJK
     type is normally set anyway. Pinning the face rather than letting `serif`
     resolve is what keeps a Japanese heading looking the same on every
     platform — see the `:lang(ja)` override below for the second half of that. */
  --display: "Bitter", Georgia, "Times New Roman", "PingFang SC",
             "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;
  --sans:    "Public Sans", ui-sans-serif, system-ui, -apple-system,
             "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",
             "Noto Sans CJK SC", "Hiragino Kaku Gothic ProN", "Yu Gothic",
             "Meiryo", sans-serif;
  --mono:    "JetBrains Mono", ui-monospace, "Cascadia Mono", "SF Mono",
             Menlo, Consolas, "Noto Sans Mono CJK SC", monospace;

  --t-hero: clamp(3rem, 9vw, 6.5rem);
  --t-h2:   clamp(1.75rem, 3.6vw, 2.5rem);
  --t-h3:   1.125rem;
  --t-body: 1.0625rem;
  --t-sm:   0.9375rem;
  --t-xs:   0.8125rem;
  --t-code: 0.875rem;

  --page:    1120px;
  --measure: 64ch;

  --s1: 8px;
  --s2: 16px;
  --s3: 24px;
  --s4: 32px;
  --s5: 48px;
  --s6: 64px;
  --s7: 96px;
  --s8: 128px;

  --r: 6px;
}

/* --------------------------------------------------------------- base */

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

html {
  background: var(--paper);
  color-scheme: light;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 var(--t-body) / 1.65 var(--sans);
  text-rendering: optimizeLegibility;
}

a { color: var(--blue-dk); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--ink); }

h1, h2, h3 {
  margin: 0;
  font-family: var(--display);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.015em;
}

p { margin: 0; }
p + p { margin-top: var(--s2); }

code {
  font-family: var(--mono);
  font-size: 0.9em;
  background: var(--wash);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0.1em 0.35em;
}

:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 3px;
  border-radius: 2px;
}

.skip {
  position: absolute; left: -9999px;
  padding: var(--s2) var(--s3);
  background: var(--blue); color: #fff; font-weight: 600;
}
.skip:focus { left: var(--s3); top: var(--s3); z-index: 10; }

.wrap {
  width: 100%;
  max-width: var(--page);
  margin-inline: auto;
  padding-inline: clamp(20px, 5vw, 48px);
}

/* --------------------------------------------------------------- bar */

.bar {
  position: sticky; top: 0; z-index: 5;
  background: color-mix(in srgb, var(--paper) 90%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}
.bar .wrap {
  display: flex; align-items: center; gap: var(--s3);
  min-height: 64px;
}
.brand {
  display: inline-flex; align-items: center; gap: 10px;
  margin-right: auto;
  color: var(--ink); text-decoration: none;
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.25rem;
  letter-spacing: -0.01em;
}
.brand svg { display: block; border-radius: 6px; }
.brand:hover { color: var(--ink); }

.bar nav { display: flex; align-items: center; gap: var(--s3); }
.bar nav a {
  color: var(--ink-2); text-decoration: none;
  font-size: var(--t-sm); font-weight: 500;
}
.bar nav a:hover { color: var(--ink); }
.bar nav a[aria-current="page"] { color: var(--ink); }

/* ------------------------------------------------------------- language

   A native <details>, so switching language works with no JavaScript and
   degrades to a plain disclosure. It sits last in the bar and the menu hangs
   off its right edge, because that is the edge nearest the window. */

.lang { position: relative; }
.lang > summary {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 10px;
  border: 1px solid var(--line-2);
  border-radius: 4px;
  cursor: pointer;
  list-style: none;
  font: 600 var(--t-xs) / 1 var(--mono);
  letter-spacing: 0.06em;
  color: var(--ink-2);
}
.lang > summary::-webkit-details-marker { display: none; }
.lang > summary::after {
  content: ""; width: 5px; height: 5px;
  border-right: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  transform: rotate(45deg) translate(-1px, -1px);
}
.lang > summary:hover { color: var(--ink); border-color: var(--ink); }
.lang[open] > summary { color: var(--ink); border-color: var(--ink); }

.lang ul {
  position: absolute; right: 0; top: calc(100% + 6px);
  z-index: 20;
  margin: 0; padding: 4px;
  list-style: none;
  min-width: 12rem;
  background: var(--paper);
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  box-shadow: 0 12px 28px rgb(29 39 51 / 14%);
}
.lang li a {
  display: block;
  padding: 7px 10px;
  border-radius: 4px;
  color: var(--ink-2); text-decoration: none;
  font-size: var(--t-sm);
}
.lang li a:hover { background: var(--wash); color: var(--ink); }
.lang li a[aria-current="true"] { color: var(--ink); font-weight: 600; }
.lang li a[aria-current="true"]::after {
  content: ""; display: inline-block;
  width: 7px; height: 7px; margin-left: 9px;
  border: 2px solid var(--blue); border-radius: 50%;
  vertical-align: middle;
}

/* -------------------------------------------------------- nav groups

   A disclosure inside the nav, for the same reason the language switcher is
   one: it works with no JavaScript. Deliberately its own class rather than
   folded into `.lang` — `.lang`'s contents are generated by
   tools/i18n_check.py, and the nav is hand-written once per language, so
   sharing a class would mean a tool that rewrites half of it.

   The bar is nine destinations wide without this, which is past what fits;
   grouping the four systems and the two version pages brings it back to six. */

.navmenu { position: relative; }
.navmenu > summary {
  display: inline-flex; align-items: center; gap: 6px;
  cursor: pointer;
  list-style: none;
  color: var(--ink-2);
  font-size: var(--t-sm); font-weight: 500;
}
.navmenu > summary::-webkit-details-marker { display: none; }
.navmenu > summary::after {
  content: ""; width: 4px; height: 4px;
  border-right: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  transform: rotate(45deg) translate(-1px, -1px);
}
.navmenu > summary:hover,
.navmenu > summary[aria-current="page"] { color: var(--ink); }
.navmenu[open] > summary { color: var(--ink); }

.navmenu ul {
  position: absolute; left: 0; top: calc(100% + 11px);
  z-index: 20;
  margin: 0; padding: 4px;
  list-style: none;
  min-width: 12rem;
  background: var(--paper);
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  box-shadow: 0 12px 28px rgb(29 39 51 / 14%);
}
.navmenu li a {
  display: block;
  padding: 7px 10px;
  border-radius: 4px;
  color: var(--ink-2); text-decoration: none;
  font-size: var(--t-sm);
  white-space: nowrap;
}
.navmenu li a:hover { background: var(--wash); color: var(--ink); }
.navmenu li a[aria-current="page"] { color: var(--ink); font-weight: 600; }

/* --------------------------------------------------------------- hero */

/* The homepage is one statement and two buttons, so the hero *is* the page.
   It holds the first screen instead of leaving the footer floating in the
   middle of it. `70vh` and not `100vh` on purpose: the bar is 64px on a wide
   window and up to ~110px once its nav wraps, and a hero measured to the exact
   viewport would be taller than the screen by exactly that much on a phone.
   Column + `justify-content` rather than `align-items`, so `.wrap` keeps
   behaving like the block it is everywhere else. */
.hero {
  padding-block: clamp(var(--s6), 8vw, var(--s8)) var(--s7);
  min-height: 70vh;
  display: flex; flex-direction: column; justify-content: center;
}

.eyebrow {
  font: 500 var(--t-xs)/1 var(--mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: var(--s3);
}

.hero h1 {
  font-size: var(--t-hero);
  letter-spacing: -0.03em;
}

.head { padding-block: clamp(var(--s5), 6vw, var(--s7)) var(--s5); }
.head h1 { font-size: var(--t-h2); }

.hero .lede, .head .lede {
  max-width: var(--measure);
  margin-top: var(--s3);
  font-size: clamp(1.125rem, 1.9vw, 1.375rem);
  line-height: 1.55;
  color: var(--ink-2);
}
.head .lede { font-size: var(--t-body); }

.actions {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s2) var(--s3);
  margin-top: var(--s4);
}
.btn {
  display: inline-flex; align-items: center;
  padding: 12px 26px;
  border-radius: 4px;
  font: 700 var(--t-sm)/1 var(--sans);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  text-decoration: none;
  border: 1px solid transparent;
}
.btn-solid { background: var(--blue); color: #fff; }
.btn-solid:hover { background: var(--blue-dk); color: #fff; }
.btn-quiet { color: var(--ink); border-color: var(--line-2); }
.btn-quiet:hover { border-color: var(--ink); color: var(--ink); }

/* --------------------------------------------------------- the split

   Sealed against hosted, drawn as the page's one motif: a closed pane has a
   solid edge, an open one has a dashed edge. Nothing else on the page is
   allowed to use a dashed border, so the idea stays readable. */

.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
  margin-top: var(--s5);
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  overflow: hidden;
}
.pane { padding: var(--s4) var(--s4) var(--s4); }
.pane + .pane { border-left: 1px solid var(--line-2); }
.pane-sealed { background: var(--paper); }
.pane-hosted { background: var(--wash); }

.pane-tag {
  font: 600 var(--t-sm)/1.4 var(--mono);
  color: var(--ink);
  margin: 0 0 var(--s3);
  padding-bottom: var(--s2);
  border-bottom: 1px solid var(--line-2);
}
.pane-sealed .pane-tag::before {
  content: ""; display: inline-block;
  width: 9px; height: 9px; margin-right: 8px;
  border: 2px solid var(--blue);         /* closed  */
  vertical-align: baseline;
}
.pane-hosted .pane-tag::before {
  content: ""; display: inline-block;
  width: 9px; height: 9px; margin-right: 8px;
  border: 2px dashed var(--ink-2);       /* open    */
  vertical-align: baseline;
}

.pane dl { margin: 0; }
.pane dt {
  font: 600 var(--t-xs)/1.5 var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--faint);
  margin-top: var(--s3);
}
.pane dt:first-child { margin-top: 0; }
.pane dd { margin: 4px 0 0; color: var(--ink-2); font-size: var(--t-sm); }
.pane dd code { background: none; border: none; padding: 0; color: var(--ink); }

@media (max-width: 760px) {
  .split { grid-template-columns: 1fr; }
  .pane + .pane { border-left: none; border-top: 1px solid var(--line-2); }
}

/* ------------------------------------------------------- numbered list

   For release notes, where the numbers are the point: a release note is
   quoted, and "the third thing in 0.1.4.5" has to mean one thing. The marker
   sits in a mono gutter — the same device the code gutters use — and the
   items are separated by the one hairline the rest of the page uses. */

ol.notes {
  counter-reset: note;
  list-style: none;
  margin: var(--s4) 0 0;
  padding: 0;
  max-width: var(--measure);
}
ol.notes > li {
  position: relative;
  padding: var(--s3) 0 var(--s3) 3.25rem;
  border-top: 1px solid var(--line);
  color: var(--ink-2);
}
ol.notes > li::before {
  counter-increment: note;
  content: counter(note);
  position: absolute; left: 0; top: var(--s3);
  font: 600 var(--t-xs) / 1.7 var(--mono);
  color: var(--faint);
}
ol.notes > li strong { color: var(--ink); font-weight: 600; }
ol.notes > li code { font-size: 0.95em; }

/* A sub-heading inside a section, sitting above one of those lists. The
   section's own h2 carries the weight; this only has to separate, and h3 has
   no margin of its own here. */
.sec > .wrap > h3 {
  margin-top: var(--s5);
  font-size: var(--t-h3);
  color: var(--ink);
}

/* ---------------------------------------------------------------- code */

.codewrap {
  margin-top: var(--s4);
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  background: var(--wash);
  overflow: hidden;
}

pre.code {
  margin: 0;
  padding: var(--s3) 0;
  overflow-x: auto;
  font: 400 var(--t-code) / 1.75 var(--mono);
  tab-size: 4;
}
pre.code code {
  display: block;
  background: none; border: none; padding: 0;
  font-size: inherit;
}

.ln {                     /* one line: real gutter numbers, no faked ones */
  display: block;
  padding-inline: var(--s3);
  white-space: pre;
}
.ln::before {
  content: attr(data-n);
  display: inline-block;
  width: 2ch; margin-right: var(--s3);
  color: var(--faint);
  text-align: right;
  user-select: none;
}

/* The one emphasis: a guard line is marked by a rule down its left edge. */
.ln-mark {
  border-left: 2px solid var(--blue);
  padding-left: calc(var(--s3) - 2px);
  background: color-mix(in srgb, var(--blue) 5%, transparent);
}
.ln-mark::before { margin-left: 0; }

.k   { color: var(--blue-dk); }     /* keyword  */
.s   { color: #1d6b3a; }            /* string   */
.n   { color: #8a5a00; }            /* number   */
.c   { color: var(--faint); font-style: italic; }
.t   { color: #0b6a75; }            /* type     */
.rng { color: var(--blue-dk); font-weight: 600; }   /* a declared range */

.out {
  border-top: 1px solid var(--line-2);
  padding: var(--s2) var(--s3);
  background: var(--paper);
  font: 400 var(--t-code) / 1.7 var(--mono);
  color: var(--ink-2);
}
.out b { color: var(--ink); font-weight: 500; }
.out .prompt { color: var(--faint); }

/* ------------------------------------------------------------ sections */

.sec { padding-block: var(--s7); border-top: 1px solid var(--line); }
.sec h2 { font-size: var(--t-h2); max-width: 30ch; }
.sec .body { max-width: var(--measure); margin-top: var(--s3); color: var(--ink-2); }
.sec .body strong { color: var(--ink); font-weight: 600; }

/* Rows, not cards: a hairline between items and nothing else. */
.rows { margin-top: var(--s5); border-top: 1px solid var(--line); }
.row {
  display: grid;
  grid-template-columns: minmax(0, 16rem) minmax(0, 1fr);
  gap: var(--s2) var(--s4);
  padding: var(--s4) 0;
  border-bottom: 1px solid var(--line);
}
.row h3 { font-size: var(--t-h3); }
.row p { color: var(--ink-2); max-width: var(--measure); }
.row p + p { margin-top: var(--s2); }

@media (max-width: 720px) {
  .row { grid-template-columns: 1fr; gap: var(--s2); }
}

/* --------------------------------------------------------------- misc */

.doclink {
  display: grid;
  grid-template-columns: minmax(0, 16rem) minmax(0, 1fr);
  gap: var(--s2) var(--s4);
  padding: var(--s3) 0;
  border-bottom: 1px solid var(--line);
  text-decoration: none;
  color: inherit;
}
.doclink:hover { background: var(--wash); }
.doclink .who {
  font: 600 var(--t-sm)/1.5 var(--mono);
  color: var(--blue-dk);
}
.doclink .what { color: var(--ink-2); font-size: var(--t-sm); }
@media (max-width: 720px) { .doclink { grid-template-columns: 1fr; gap: 4px; } }

footer {
  border-top: 1px solid var(--line-2);
  padding-block: var(--s5);
  color: var(--faint);
  font-size: var(--t-sm);
}
footer .wrap {
  display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4);
  align-items: baseline; justify-content: space-between;
}
footer a { color: var(--ink-2); text-decoration: none; }
footer a:hover { color: var(--ink); text-decoration: underline; }
footer nav { display: flex; flex-wrap: wrap; gap: var(--s3); }

/* The Mastodon link carries a mark, so it needs to line up with the plain text
   links around it. `currentColor` is deliberate: the icon takes the footer
   link's own colour, so it reads as one of them rather than as a brand badge —
   and it follows the hover with everything else. Nothing here is Mastodon's
   purple; that would be the only saturated pixel in the footer. */
footer nav a.mast { display: inline-flex; align-items: center; gap: 0.45em; }
footer nav a.mast svg { width: 1.15em; height: 1.15em; flex: none; fill: currentColor; }

/* The honest-boundary marker. One job: flag a limit the page is stating about
   itself, so a reader can never mistake a promise for a fact. */
.limit {
  margin-top: var(--s4);
  padding: var(--s3) var(--s3) var(--s3) var(--s4);
  border-left: 3px solid var(--ochre);
  background: color-mix(in srgb, #b8860b 6%, var(--paper));
  border-radius: 0 var(--r) var(--r) 0;
  color: var(--ink-2);
  font-size: var(--t-sm);
  max-width: var(--measure);
}
.limit strong { color: var(--ochre); }

.note {
  margin-top: var(--s4);
  padding-left: var(--s3);
  border-left: 2px solid var(--line-2);
  color: var(--faint);
  font-size: var(--t-sm);
  max-width: var(--measure);
}

/* A line of prose that belongs to the block above it. No rule of its own. */
.caption {
  margin-top: var(--s2);
  color: var(--faint);
  font-size: var(--t-sm);
  max-width: var(--measure);
}
.caption code { font-size: inherit; background: none; border: none; padding: 0; color: var(--ink-2); }

/* -------------------------------------------------------- per language

   Four things in the Latin design carry over badly, and each is switched off
   per language rather than globally so the English pages keep them.

   Uppercase does nothing to a Han glyph, and the 0.14em tracking that reads
   as "small structural label" in Latin reads as broken spacing on a wide one.
   Negative tracking is a Latin display habit that only crowds a Han glyph.
   A CJK line wants to be looser and the box wider. And Japanese must not fall
   through to a Chinese face: macOS ships Songti SC, so without the override a
   Japanese page would set its kanji in Chinese glyph variants. */

html:lang(zh-Hans), html:lang(ja) { --measure: 78ch; }

:lang(zh-Hans) body, :lang(ja) body { line-height: 1.85; }
:lang(zh-Hans) .hero .lede, :lang(zh-Hans) .head .lede,
:lang(ja) .hero .lede, :lang(ja) .head .lede { line-height: 1.75; }

:lang(zh-Hans) h1, :lang(zh-Hans) h2, :lang(zh-Hans) h3,
:lang(ja) h1, :lang(ja) h2, :lang(ja) h3,
:lang(zh-Hans) .hero h1, :lang(ja) .hero h1 { letter-spacing: 0; }

:lang(zh-Hans) .sec h2, :lang(ja) .sec h2 { max-width: 46ch; }

:lang(zh-Hans) .eyebrow, :lang(ja) .eyebrow {
  text-transform: none; letter-spacing: 0.08em;
}
:lang(zh-Hans) .btn, :lang(ja) .btn {
  text-transform: none; letter-spacing: 0.02em;
}
:lang(zh-Hans) .pane-tag, :lang(ja) .pane-tag { text-transform: none; }

:lang(ja) {
  --display: "Bitter", Georgia, "Hiragino Kaku Gothic ProN", "Hiragino Sans",
             "Yu Gothic", "Meiryo", "Noto Sans CJK JP", sans-serif;
  --sans:    "Public Sans", ui-sans-serif, system-ui, -apple-system,
             "Hiragino Kaku Gothic ProN", "Hiragino Sans", "Yu Gothic",
             "Meiryo", "Noto Sans CJK JP", sans-serif;
}

/* Russian runs long. Let the browser hyphenate it rather than push a word
   past the measure. */
html:lang(ru) { hyphens: auto; }

/* ------------------------------------------------------------- entrance

   A short settle when a page arrives: the bar fades, then the blocks below it
   rise a few pixels into place, staggered so the page assembles instead of
   appearing all at once. Only `opacity` and `translateY`, so it stays on the
   compositor and never lays anything out twice.

   `backwards`, never `both`. With `both` the element keeps the keyframe's
   `from` state — `opacity: 0` — for as long as it takes the animation to
   start, so a browser that declines to run it, or a stylesheet that arrives
   after the content, leaves the page invisible. `backwards` hides the element
   only for its own delay, and the element's ordinary state is the visible one.

   `prefers-reduced-motion` turns all of this off; the block at the end of the
   file also zeroes `animation-delay`, which it has to — a 300ms delay with a
   0.01ms duration is 300ms of invisible text. */

@keyframes settle {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
@keyframes settle-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* The bar gets opacity alone: it is `position: sticky`, and a transform on it
   would make it a containing block while the animation runs. */
.bar { animation: settle-fade 300ms ease-out backwards; }

.hero > .wrap, .head > .wrap {
  animation: settle 420ms cubic-bezier(0.22, 0.61, 0.36, 1) 60ms backwards;
}

/* One delay for every section rather than a stagger down the page. The only
   thing a reader sees arrive is the bar, the head and at most the first
   section — everything after that is below the fold and has finished before
   it is looked at — so a per-section stagger would be numbering that buys
   nothing and breaks the day a page starts with a `.sec` instead of a `.head`. */
main > .sec { animation: settle 460ms cubic-bezier(0.22, 0.61, 0.36, 1) 180ms backwards; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-delay: 0ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Six items do not fit one line on a tablet, so the bar is allowed to wrap
   before the links start colliding. Below that it shrinks instead of wrapping
   two lines' worth of chrome into the top of every page. */
@media (max-width: 860px) {
  .bar .wrap { flex-wrap: wrap; padding-block: 10px; row-gap: var(--s1); min-height: 0; }
  /* The language chip belongs on the first line, against the right edge, and the nav
     belongs on its own line below — so the nav is ordered after it. Without this the
     bar wraps into three lines as soon as the nav stops fitting beside the brand.
     Absolute positioning would do the same job and need a magic offset that drifts
     with the padding. */
  .bar nav { order: 2; flex-wrap: wrap; }
  /* A menu hanging off a group's left edge would run past the window here: the nav
     has its own line and the groups sit near its right end. */
  .navmenu ul { left: auto; right: 0; }
}

/* Narrow phones: the nav is the first thing to break, and a wrapped two-line
   "Get / started" in a 64px bar is worse than a smaller one. */
@media (max-width: 560px) {
  .bar .wrap { padding-inline: 16px; gap: var(--s2); }
  .bar nav { gap: var(--s2); }
  .bar nav a { font-size: 0.8125rem; white-space: nowrap; }
  .brand { font-size: 1.0625rem; }

  .ln { padding-inline: var(--s2); }
  .ln::before { margin-right: var(--s2); }
  .ln-mark { padding-left: calc(var(--s2) - 2px); }
  .out { padding-inline: var(--s2); }
}

/* The paper page renders the PDF in a frame. 880px is the shortest height that still shows a
   full page of the two-column layout on a laptop; the download button covers phones, where
   browsers either use their own viewer or show nothing. */
.paperframe { display: block; width: 100%; height: 880px; border: 1px solid var(--line-2); border-radius: 8px; }
