/* The site and the application read from one palette. These values are the
   application's, in /tools/console.css; anything changed here has to be
   changed there, or the two halves of the product drift apart again.

   The faces are self-hosted for the same reason the app self-hosts them: the
   product's claim is that nothing is sent anywhere, and a request to
   fonts.googleapis.com from a page making that claim is a request to
   somewhere. Until the files are in /tools/fonts/ both stacks fall back to the
   system faces and nothing about the layout depends on which one arrives. */
/* The interface faces, self-hosted. Not for speed — the product's claim is
   that nothing is sent anywhere, and a request to fonts.googleapis.com from a
   page making that claim is a request to somewhere.

   These two files were named here for a long time and never existed. Every
   page asked for them, got a 404 twice, and fell back — so the site rendered
   in whatever sans the reader's machine happened to have, which is SF Pro on
   a Mac, Segoe UI on Windows and Roboto on Android. A product that sells
   typographic care cannot be set in three different faces depending on who is
   looking.

   TrueType rather than woff2, which is what the old rules asked for. The
   upstream release is a variable .ttf; turning it into woff2 needs fonttools
   and brotli, and this repository has no dependencies to add one to. Caddy
   compresses them on the wire — `encode zstd gzip` in the Caddyfile — which is
   most of what woff2 would have bought.

   Full faces, not Latin subsets. `font_probe.py` reads both: 735 and 976
   glyphs, Turkish covered in each. A subset that dropped ı, ş and ğ would fail
   in exactly the place this product puts a reader's own words. */
@font-face { font-family:'Space Grotesk'; font-weight:400 700; font-display:swap;
             src:url('/tools/fonts/SpaceGrotesk-Variable.ttf') format('truetype') }
@font-face { font-family:'JetBrains Mono'; font-weight:400 500; font-display:swap;
             src:url('/tools/fonts/JetBrainsMono-Variable.ttf') format('truetype') }

:root{
  --bg:#F7F8F8; --surface:#fff; --surface-2:#EDF0EF;
  --ink:#15181A; --ink-2:#4F5754; --ink-3:#636C69;
  --line:#E4E7E5; --line-2:#D5D8D6;
  --accent:#0F5C57; --accent-2:#0B4642;
  --ok:#0F5C57; --ok-bg:#EAF5F1;
  --warn:#8A6A1C; --warn-bg:#FBF4E4; --warn-line:#E6D6AE;
  --err:#B4352B; --err-bg:#FBEFEE; --err-line:#E8C8C4;

  /* What is written on a filled control. The fill flips with the scheme, so
     the label on it cannot be a constant white. */
  --on-accent:#fff;

  --sans:'Space Grotesk',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
  --mono:'JetBrains Mono',ui-monospace,SFMono-Regular,'SF Mono',Menlo,monospace;
  /* Not the site's voice any more — the headings are set in the same face as
     the app. This is kept for the one place that is *depicting* a printed
     page: the specimen pages in the widows-and-orphans guide. */
  --serif:ui-serif,"Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif;

  /* One radius, because the console has one. The site had seven — 12px on the
     cards, 10px on the stat grid, 9px on a message, 8px on a field, 7px on the
     signup input, 6px on a small button, 4px on inline code — and nothing
     chose any of them. Seven radii on one page is the surest sign that nobody
     decided; it is also the whole difference between a screen that was drawn
     and a screen that accumulated. */
  --r:6px; --r-lg:8px;

  /* One measure, one column, and both of them as wide as the window.
     ---------------------------------------------------------------------
     Two things were wrong here in turn, and the second one was mine.

     **First**, the column had no width at all and the measure had six: 62ch in
     five inline styles, 64ch in three more, 60ch in two, 70ch on the article
     and the disclaimer, 58ch twice in the console. Measured at 1920, the card
     rows ran the full 1684px while every paragraph stopped at 636 — two things
     on one page disagreeing about how wide the page is.

     **Then** the fix capped the column at 1120px, which made the page agree
     with itself and stop short of the window. On a 1730px screen that is three
     hundred and seventy pixels of nothing down the right-hand side, and the
     owner's answer was the right one: the text should use the window it is in.

     So it does. `--column` is a ceiling rather than a width — high enough that
     no laptop and no ordinary desktop reaches it, there so that a 4K monitor
     does not produce a three-thousand-pixel line — and `--measure` is `100%`,
     which is to say the text fills the column it is in.

     What that costs is worth writing down rather than discovering: on a 1730px
     window a paragraph runs about 140 characters, and the comfortable band for
     continuous reading is nearer 60 to 90. That is a deliberate trade, made by
     the person whose site it is, and it is one number to undo — put a `ch`
     value back on `--measure` and every paragraph on both halves takes it. */
  --measure:100%;
  --column:1800px;

  /* The rail. Dark under both schemes, because it is the thing the page is
     read against rather than part of the page. Same values as the console's,
     which tests/run.py checks rather than trusts. */
  --rail:#15181A; --rail-2:#20262A; --rail-line:#262B2D;
  --rail-ink:#E7EAE9; --rail-ink-2:#A8B1AD; --rail-ink-3:#7E8883;
  --signal:#4FD1A5; --signal-2:#7FE2C4;
  --rail-w:236px;
  /* The rail separates itself by being the dark thing on a light page. When the
     page is dark too it needs an edge, and an edge that is a token keeps every
     difference between the schemes inside the block below. */
  --rail-edge:transparent;

  color-scheme:light;
}
/* Two ways in, because there are two ways of asking. The media query is the
   machine's answer and needs no JavaScript; the attribute is the reader's,
   stamped by /tools/theme.js and by the three lines in each page's <head> that
   run before the first paint. The reader's answer wins, which is why the media
   query stands down for [data-theme=light]. The two copies must stay
   identical: tests/run.py compares them. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme=light]){
    --bg:#121618; --surface:#191E20; --surface-2:#222829;
    --ink:#E8EBEA; --ink-2:#B2BBB7; --ink-3:#8A938F;
    --line:#272E30; --line-2:#39423F;
    --accent:#5FCFB6; --accent-2:#8CDFC9;
    --ok:#5FCFB6; --ok-bg:#122421;
    --warn:#E2C489; --warn-bg:#26200F; --warn-line:#4A3E20;
    --err:#EF9C94; --err-bg:#2A1918; --err-line:#4E2E2B;
    --on-accent:#07211C;
    --rail:#0C0F10; --rail-2:#181E21; --rail-line:#31393C;
    --rail-ink:#E7EAE9; --rail-ink-2:#A8B1AD; --rail-ink-3:#8A938F;
    --signal:#4FD1A5; --signal-2:#7FE2C4;
    --rail-edge:var(--rail-line);
    color-scheme:dark;
  }
}
:root[data-theme=dark]{
    --bg:#121618; --surface:#191E20; --surface-2:#222829;
    --ink:#E8EBEA; --ink-2:#B2BBB7; --ink-3:#8A938F;
    --line:#272E30; --line-2:#39423F;
    --accent:#5FCFB6; --accent-2:#8CDFC9;
    --ok:#5FCFB6; --ok-bg:#122421;
    --warn:#E2C489; --warn-bg:#26200F; --warn-line:#4A3E20;
    --err:#EF9C94; --err-bg:#2A1918; --err-line:#4E2E2B;
    --on-accent:#07211C;
    --rail:#0C0F10; --rail-2:#181E21; --rail-line:#31393C;
    --rail-ink:#E7EAE9; --rail-ink-2:#A8B1AD; --rail-ink-3:#8A938F;
    --signal:#4FD1A5; --signal-2:#7FE2C4;
    --rail-edge:var(--rail-line);
    color-scheme:dark;
}
:root[data-theme=light]{ color-scheme:light }
*,*::before,*::after{box-sizing:border-box}
/* 16px against the console's 15. The console is an instrument read a control
   at a time; these pages are read in paragraphs, and one step down from the
   console is as far as long-form text can follow it. Everything below is in
   rem, so the whole scale moves with this number. */
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--sans);
     font-size:16px;line-height:1.58;-webkit-font-smoothing:antialiased}
.wrap{max-width:820px;margin:0 auto;padding:0 24px}
h1,h2,h3{font-family:var(--sans);font-weight:600;letter-spacing:-.02em;line-height:1.2;margin:0}
h1{font-size:clamp(1.7rem,3.1vw,2.1rem)}
h2{font-size:1.25rem;margin-top:0}
h3{font-size:.95rem;font-family:var(--sans);font-weight:600;letter-spacing:0}
p{margin:0 0 1em}
a{color:var(--accent)}
/* The console's micro-label: mono, tracked, upper. It is the one piece of type
   that says which product this is, and it belongs on both halves of it. */
.eyebrow{font-family:var(--mono);font-size:.66rem;letter-spacing:.12em;
         text-transform:uppercase;color:var(--accent)}
/* The console's screens open on a plate of --surface with a hairline under it.
   The site has no rail to carry the brand, so its masthead is that plate. */
/* ── the shell ─────────────────────────────────────────────────────────────
   The site is read inside the same frame as the application: a dark rail on
   the left carrying the name, the promise and the way through, and one column
   of work beside it.

   It used to be a masthead over a centred 820px column, which is the shape of
   every page that has ever been generated for anybody. Nothing in it was
   wrong; it just said nothing about what this is. The rail does — it is the
   product's own furniture, and a reader who arrives on a guide and later opens
   the tool is looking at one thing rather than a site and an app that happen
   to share a colour.

   Every rule below is the console's, adapted in one respect: the console's
   steps are stages of a build and are buttons; these are places and are links.
*/
.app{display:grid;grid-template-columns:var(--rail-w) 1fr;min-height:100vh}

/* Sticky rather than tall. The console's rail wraps a screen that does not
   scroll; these pages do, and a rail stretched to the height of a guide puts
   the way out three thousand pixels below the way in. It stays where it is and
   the work scrolls past it. */
.rail{background:var(--rail);color:var(--rail-ink);display:flex;flex-direction:column;
      padding:24px 0 0;border-right:1px solid var(--rail-edge);
      position:sticky;top:0;align-self:start;height:100vh;overflow:auto}
/* The mark, on a plate that is dark under both schemes. The site draws its two
   leaves out of --line-2 and --accent; neither is legible here, so the quiet
   leaf is the rail's own faintest ink held back to a whisper and the live one
   is --signal — the same green the promise and the current place use. */
.rail .brand{display:flex;align-items:center;gap:.5em;padding:0 20px;
             font-size:1.15rem;font-weight:700;letter-spacing:-.012em;
             text-transform:uppercase;color:#fff;text-decoration:none}
.rail .brand:focus-visible{outline:2px solid var(--signal);outline-offset:3px}
.rail .brand-mark{flex:none;display:block;height:1.25em;width:auto}
.rail .brand-mark rect:first-child{fill:var(--rail-ink-3);opacity:.38}
.rail .brand-mark rect:last-child{fill:var(--signal)}
.rail .local{display:flex;align-items:center;gap:6px;padding:6px 20px 20px;
             font-family:var(--mono);font-size:.66rem;letter-spacing:.08em;color:var(--rail-ink-3)}
.rail .local i{width:6px;height:6px;border-radius:50%;background:var(--signal);flex:none}

/* The language, at the top of the rail on the name's own line and centred
   on it: a globe and the one the page is in, opening onto every other by its
   own name (pages/make.py writes it). A <details>, so it opens and closes with
   no script; core/i18n.js only closes it on Escape or a click elsewhere. Every
   rule the page gives its own details and summary — the FAQ's rule below,
   its weight, its + and – — is undone here. It was pinned to the rail's
   corner by measurement at first, and sat a few pixels above the name. */
.rail-top{display:flex;align-items:center;justify-content:space-between;gap:10px;
          padding:0 14px 0 20px}
.rail .rail-top .brand{padding:0}
.rail .lang-pick{position:relative;z-index:20;flex:none;border:0;padding:0;
                 font-family:var(--mono);font-size:.66rem;letter-spacing:.08em}
.lang-pick summary{display:flex;align-items:center;justify-content:flex-start;gap:5px;
                   font-weight:400;font-size:inherit;line-height:1.4;color:var(--rail-ink-2);
                   padding:4px 9px;border:1px solid var(--rail-line);border-radius:99px}
.lang-pick summary::after,.lang-pick[open] summary::after{content:none}
.lang-pick summary:hover,.lang-pick[open] summary{color:#fff}
.lang-pick summary:focus-visible{outline:2px solid var(--signal);outline-offset:2px}
.lang-pick svg{width:12px;height:12px;flex:none;fill:none;stroke:currentColor;stroke-width:1.2}
.lang-pick ul{position:absolute;top:calc(100% + 6px);right:0;margin:0;padding:4px;list-style:none;
              min-width:100%;background:var(--rail-2);border:1px solid var(--rail-line);
              border-radius:var(--r)}
.lang-pick a{display:block;padding:7px 12px;border-radius:4px;white-space:nowrap;
             font-family:var(--sans);font-size:.85rem;letter-spacing:0;
             color:var(--rail-ink);text-decoration:none}
.lang-pick a:hover{background:var(--rail);color:#fff;text-decoration:none}
.lang-pick a:focus-visible{outline:2px solid var(--signal);outline-offset:-2px}

.steps{padding:0 12px;display:flex;flex-direction:column;gap:1px}
.steps .step{display:flex;align-items:center;gap:10px;padding:9px 10px;border-radius:5px;
             font-size:.88rem;color:var(--rail-ink-2);text-decoration:none}
.steps .step:hover{color:var(--rail-ink);background:var(--rail-2)}
.steps .step:focus-visible{outline:2px solid var(--signal);outline-offset:-2px}
.steps .step .bar{width:2px;height:14px;border-radius:1px;background:transparent;flex:none}
.steps .step .n{font-family:var(--mono);font-size:.62rem;color:var(--rail-ink-3)}
.steps .step .label{flex:1}
.steps .step.on{background:var(--rail-2);color:#fff;font-weight:600}
.steps .step.on .bar{background:var(--signal)}
.steps .step.on .n{color:var(--signal)}

/* The foot of the rail is the way into the product, and it is a button now.
   ---------------------------------------------------------------------------
   It used to be a label — `the product` in small tracked mono — over a plain
   text link. The label was doing nothing: it named a category rather than
   saying anything the link underneath does not, and the narrow layout had
   already decided that, hiding it outright below 860px. What was left was a
   quiet grey link at the bottom of a dark column, which is the least
   conspicuous thing a rail can hold — and it is the one place on all
   twenty-three pages where somebody goes looking for the tool itself.

   So: filled, in the rail's own green, the width of the rail. It is the only
   filled thing in that column, which is what makes it read as the way out of
   the reading and into the work. */
.rail .foot{margin-top:auto;padding:18px 16px 18px;border-top:1px solid var(--rail-line)}
.rail .foot .go{display:block;text-align:center;background:var(--signal);
  color:var(--rail);font-size:.9rem;font-weight:650;letter-spacing:-.005em;
  padding:10px 12px;border-radius:var(--r);text-decoration:none}
.rail .foot .go:hover{background:var(--signal-2);text-decoration:none}
.rail .foot .go:focus-visible{outline:2px solid #fff;outline-offset:2px}
.foot-row{margin-top:14px;display:flex;align-items:center;justify-content:space-between;gap:10px}

/* The switch is the console's, on the rail rather than in a masthead. */
.theme-switch{display:inline-flex;border:1px solid var(--rail-line);
              border-radius:99px;overflow:hidden;flex:none}
.theme-switch button{font-family:var(--mono);font-size:.62rem;letter-spacing:.06em;
                     padding:4px 9px;border:0;background:none;color:var(--rail-ink-3);
                     cursor:pointer;line-height:1.4}
.theme-switch button:hover{color:var(--rail-ink)}
.theme-switch button[aria-pressed=true]{background:var(--rail-2);color:#fff}

/* ── the work column ──────────────────────────────────────────────────────
   No centred measure. The column is the measure, and what has to be read in
   lines caps itself: an article at 70ch, a lead at 62ch, a disclaimer at 70ch.
   A page-wide max-width would put the rail on one side of the screen and the
   text in the middle of the other, which is two layouts arguing. */
.work{min-width:0}
.col{padding:0 0 60px}
.work .wrap{max-width:var(--column);margin:0;padding:22px 34px 0}

/* The head plate: what the console opens every screen with. Full width, on
   --surface, with a hairline under it and the eyebrow above the title. */
/* The head is a plate the page is written under, not a card sitting on it: it
   is the column's own width, outside the wrap that pads everything else. */
.hero{background:var(--surface);border-bottom:1px solid var(--line-2);
      margin:0 0 4px;padding:26px 34px 22px}
/* The plate runs the width of the column the page is in; what is written
   on it keeps the measure. A band that stops short of the screen reads as
   a card that failed to grow. */
.hero>*{max-width:var(--column)}
.hero p{color:var(--ink-2);font-size:1.02rem;max-width:var(--measure);margin:10px 0 0}
.hero .eyebrow{display:block;margin-bottom:9px}
.hero h1{max-width:22ch}

/* The way into the product, in the first screen. The hero used to be about the
   free tools alone, which read as though the paid half were somewhere else —
   or not finished. It is neither: they ship together. */
.hero .hero-cta{display:flex;gap:10px;margin-top:18px;flex-wrap:wrap}
/* Sections are the page's rhythm. The selector follows the shell: they are no
   longer children of <main> but of the work column's wrap. */
.col section{border-top:1px solid var(--line-2);padding-top:30px;margin-top:30px}
.col section:first-of-type{border-top:0;padding-top:0}

/* Narrow: the rail wraps rather than sheds. Hiding it would take the promise
   and the way through off the phone altogether, and the promise is in the rail
   precisely so that it is not seen once and never again. */
@media (max-width:860px){
  .app{grid-template-columns:1fr}
  /* `min-width:0` on both, and it is the whole reason the rail scrolls.
     A grid item and a flex item both take `min-width:auto`, which refuses to
     be narrower than their content — so the row of seven places below set the
     width of the rail, the rail set the width of the page, and every page on
     the site was 795px wide on a 375px screen. The `overflow-x:auto` two rules
     down never engaged, because a box as wide as its contents has nothing to
     scroll. Measured at 375px: 424px of the document hung off the right, the
     headline was cut through, and the trim selector sat off-screen. */
  .rail{flex-direction:row;flex-wrap:wrap;align-items:center;gap:8px;min-width:0;
        padding:10px 16px;border-right:0;border-bottom:1px solid var(--rail-edge);
        position:static;height:auto;overflow:visible}
  .rail .brand{padding:0}
  .rail .local{padding:0}
  /* The name's line opens into the bar, and the picker goes after the
     promise, so on a phone it opens the second line, left of the button. */
  .rail-top{display:contents}
  .rail .brand{order:-2}
  .rail .local{order:-1}
  .lang-pick ul{right:auto;left:0}
  .rail .foot{margin:0 0 0 auto;padding:0;border-top:0;display:flex;align-items:center;gap:10px}
  .rail .foot .go{padding:7px 15px}
  .rail .foot .foot-row{margin-top:0}
  .steps{order:1;flex:1 1 100%;min-width:0;flex-direction:row;padding:0;gap:4px;overflow-x:auto}
  .steps .step{white-space:nowrap;padding:7px 10px}
  .steps .step .n{display:none}
  .work .wrap{padding:18px 18px 0}
  .hero{padding:20px 18px 16px}
}

/* The same sentence the console keeps in its rail, in the same voice: mono,
   small, tracked. It is a promise rather than a headline. */
.privacy{display:inline-flex;align-items:center;gap:8px;font-family:var(--mono);
  font-size:.7rem;letter-spacing:.04em;color:var(--ok);
  background:var(--ok-bg);border:1px solid var(--ok);border-radius:99px;padding:5px 12px;margin-bottom:18px}

/* ── controls ─────────────────────────────────────────────────────────────
   The console's, name for name. A calculator and the tool that does the
   calculating for real should not ask the same question with two different
   controls — and they did: a segmented control with the answer written on it
   here, a bare <select> there.

   The <select> does not go away. It stays as the value, hidden, and the
   segment writes to it: the code that reads the answer is untouched, the
   keyboard still has a real control to land on, and a page whose JavaScript
   never arrives still works. /tools/controls.js builds and wires them. */
.panel{background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r);padding:22px;margin-bottom:22px}
.fields{display:grid;grid-template-columns:2fr 1fr;gap:12px 16px;align-items:start}
@media(max-width:640px){.fields{grid-template-columns:1fr}}
label{display:block;font-size:.9rem;font-weight:600;margin-bottom:8px;color:var(--ink)}

.setting{margin-bottom:20px}
.setting:last-child{margin-bottom:0}
.setting .top{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:baseline;gap:2px 14px;margin-bottom:8px}
.setting .top label{margin:0}
.setting .hint{font-family:var(--mono);font-size:.66rem;color:var(--ink-3);text-align:right}
/* A label and its hint share a line while they fit, and the hint takes the
   next line when they do not. In the narrow column of a pair they did not, and
   the hint squeezed "Innenteil mit Beschnittzugabe" onto two lines instead.
   Two questions side by side that are both answered with buttons put their
   buttons on one line, whichever label ran longer: the headers share a row and
   the buttons share the next. */
.fields.pair{grid-template-rows:auto auto}
.fields.pair > .setting{display:grid;grid-row:span 2;grid-template-rows:subgrid;row-gap:0}
.fields.pair > .setting > .seg{align-self:start}

.seg{display:flex;border:1px solid var(--line-2);border-radius:var(--r);overflow:hidden;background:var(--surface)}
.seg button{flex:1;padding:9px 8px;border:0;border-left:1px solid var(--line-2);background:none;
            font-family:inherit;font-size:.83rem;font-weight:500;color:var(--ink-2);cursor:pointer;line-height:1.25}
.seg button:first-child{border-left:0}
.seg button:hover{background:var(--surface-2)}
.seg button .sub{display:block;font-family:var(--mono);font-size:.62rem;color:var(--ink-3);margin-top:2px}
.seg button[aria-pressed=true]{background:var(--ink);color:var(--bg)}
.seg button[aria-pressed=true] .sub{color:var(--bg);opacity:.62}
.seg button:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}

/* Narrow: the segments stack rather than shrink.
   This only became visible once the page stopped being 795px wide on a 375px
   screen — the sideways scroll was hiding it. Four choices sharing 343px is
   about 75px each, and at 75px "Mass-market feel; common for novellas" breaks
   inside its words: Mass-/market, non-/fiction. A row of eight-character
   fragments is not a control anybody reads.
   Stacked, each choice is a full-width line with room for the sentence under
   it, and a tap target the height of a finger rather than the width of one.
   The label and its hint stack for the same reason: side by side at this width
   the hint took the row and squeezed "Trim size" onto two lines. */
@media (max-width:560px){
  .setting .top{flex-direction:column;align-items:flex-start;gap:3px}
  .setting .hint{text-align:left}
  .seg{flex-direction:column}
  .seg button{border-left:0;border-top:1px solid var(--line-2);
              text-align:left;padding:11px 13px}
  .seg button:first-child{border-top:0}
}
/* And at any width, when the answers do not fit side by side in the column
   they were given — measured by /tools/controls.js, which knows the words. */
.seg.stacked{flex-direction:column}
.seg.stacked button{border-left:0;border-top:1px solid var(--line-2);
                    text-align:left;padding:11px 13px}
.seg.stacked button:first-child{border-top:0}

/* A field is its own label: the name lives inside the box, in the key voice,
   and the box is the control. */
.field{background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r);padding:9px 12px}
.field label{font-family:var(--mono);font-size:.6rem;letter-spacing:.1em;text-transform:uppercase;
             color:var(--ink-3);margin-bottom:3px;font-weight:400}
.field input,.field select{border:0;padding:0;background:transparent;font-size:.95rem;width:100%;
                           color:var(--ink);font-family:var(--sans)}
.field input:focus,.field select:focus{outline:0}
.field:focus-within{border-color:var(--accent)}
.field-note{font-size:.78rem;color:var(--ink-3);margin-top:5px}
.row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
select,input[type=number]{width:100%;padding:9px 12px;font-size:.95rem;font-family:var(--sans);
  border:1px solid var(--line-2);border-radius:var(--r);background:var(--surface);color:var(--ink)}
select:focus,input:focus{outline:2px solid var(--accent);outline-offset:1px}

/* results */
.msg{border-radius:var(--r);padding:12px 15px;margin-bottom:12px;font-size:.93rem}
.msg strong{display:block;margin-bottom:3px}
.msg-err{background:var(--err-bg);border:1px solid var(--err-line);color:var(--err)}
.msg-warn{background:var(--warn-bg);border:1px solid var(--warn-line);color:var(--warn)}
.msg-ok{background:var(--ok-bg);border:1px solid var(--ok);color:var(--ok)}
.msg ul{margin:6px 0 0;padding-left:19px}

table{width:100%;border-collapse:collapse;font-size:.94rem}
th,td{text-align:left;padding:10px 12px;border-bottom:1px solid var(--line)}
th{font-family:var(--mono);font-size:.66rem;text-transform:uppercase;letter-spacing:.1em;
   color:var(--ink-3);font-weight:400}
td.num{font-family:var(--mono);font-size:.9rem;white-space:nowrap}
tbody tr:last-child td{border-bottom:0}
.rec{color:var(--accent);font-weight:650}
.tbl-wrap{overflow-x:auto}
caption{caption-side:top;text-align:left;font-weight:650;font-size:.95rem;padding:0 0 10px;color:var(--ink)}

/* The console's `.facts`, to the pixel. The hairlines are the 1px gap with each
   cell's own outline showing through it rather than the container's background:
   a grid whose last row is short then ends in cells, not in a slab of rule
   where the missing ones would have been. */
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:1px;
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r);
  overflow:hidden;margin-top:18px}
.stat{background:var(--surface);padding:13px 15px;box-shadow:0 0 0 1px var(--line-2)}
.stat .k{font-family:var(--mono);font-size:.6rem;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-3);margin-bottom:5px}
.stat .v{font-size:1.06rem;font-weight:600;color:var(--ink)}
.stat .sub{font-family:var(--mono);font-size:.66rem;color:var(--ink-3);margin-top:3px}
/* A value that is one short word in English can be one long one in another
   language: "wissenschaftlich", "Überschriftenebenen" are wider than a cell at
   every width, and ran under the cell beside them, which painted over their
   ends. Hyphenated where the page's language allows, broken anywhere if it
   must. Measured: English and Turkish cells keep their height. */
.stat .v,.stat .sub{overflow-wrap:break-word;hyphens:auto}

details{border-top:1px solid var(--line);padding:18px 0}
summary{cursor:pointer;font-weight:650;font-size:1rem;list-style:none;display:flex;justify-content:space-between;gap:14px}
summary::-webkit-details-marker{display:none}
summary::after{content:"+";color:var(--ink-3);font-family:var(--mono);font-size:.9em}
details[open] summary::after{content:"–"}
details .body{margin-top:14px;color:var(--ink-2);font-size:.96rem}
section.prose{margin-top:52px}
section.prose h2{margin-bottom:14px}
section.prose p{color:var(--ink-2)}
footer{margin-top:60px;border-top:1px solid var(--line);padding:26px 0 60px;font-size:.85rem;color:var(--ink-3)}
.disclaimer{max-width:var(--measure);line-height:1.6}
code{font-family:var(--mono);font-size:.86em;background:var(--surface-2);padding:1px 5px;border-radius:4px}

/* ── additions shared by the later tools ─────────────────────────────────── */
.fields.three { grid-template-columns: 2fr 1fr 1.4fr; }
@media(max-width:760px){ .fields.three { grid-template-columns:1fr } }
.muted { color: var(--ink-3); }
/* The console has had this since it was written; the site had the class in its
   markup and no rule behind it, so every select a segmented control stands in
   front of was on the page twice. */
.hidden{display:none !important}
.stat .sub { font-size:.8rem; color:var(--ink-3); margin-top:3px; }
.drop {
  border: 1.5px dashed var(--line-2); border-radius: var(--r-lg);
  padding: 44px 24px; text-align: center; cursor: pointer;
  background: var(--surface); transition: border-color .15s, background .15s;
}
.drop:hover, .drop.over { border-color: var(--accent); background: var(--surface-2); }
.drop .big { font-size: 1.05rem; font-weight: 600; margin-bottom: 6px; }
.drop input { display: none; }
.check { display:flex; gap:12px; align-items:flex-start; padding:11px 0; border-bottom:1px solid var(--line); }
.check:last-child { border-bottom:0; }
.check .mark { flex:none; width:1.5em; font-weight:700; font-family:var(--mono); }
.check.pass .mark { color: var(--ok); }
.check.fail .mark { color: var(--err); }
.check.warn .mark { color: var(--warn); }
.check.skip .mark { color: var(--ink-3); }
.check .what { font-weight:600; }
.check .why { font-size:.9rem; color:var(--ink-2); margin-top:2px; }
.verdict { border-radius:var(--r); padding:15px 18px; margin-bottom:16px; }
.verdict h2 { font-size:1.15rem; margin-bottom:4px; }
.verdict.ok { background:var(--ok-bg); border:1px solid var(--ok); color:var(--ok); }
.verdict.bad { background:var(--err-bg); border:1px solid var(--err-line); color:var(--err); }
.verdict.warn { background:var(--warn-bg); border:1px solid var(--warn-line); color:var(--warn); }

/* small buttons used by the review screen */
/* The console's button, at the console's proportions. `.btn` is the name it
   uses; `.btn-sm` is what the review screen here already says, and the two are
   one rule so the halves cannot drift. */
.btn, button.btn-sm {
  font-family: var(--sans); font-size: .86rem; font-weight: 600;
  padding: 8px 14px; border-radius: var(--r); cursor: pointer;
  border: 1px solid var(--line-2); background: var(--surface); color: var(--ink);
}
.btn:hover, button.btn-sm:hover { border-color: var(--accent); color: var(--accent); }
.btn.primary, button.btn-sm.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn.primary:hover, button.btn-sm.primary:hover { background: var(--accent-2); border-color: var(--accent-2); color: var(--on-accent); }

/* A `.btn` that is a link rather than a button. An anchor is inline, so the
   padding above draws but makes no room and the underline stays; these two
   declarations are the whole of what makes a link and a button the same
   shape. */
a.btn { display: inline-block; text-decoration: none; }

/* Card grids. Written here rather than in index.html's inline style because
   404.html uses the same markup and was compensating with inline attributes on
   every element — which cannot express :hover, so those three cards were the
   only ones on the site that did not respond to the pointer. */
/* The console has no floating cards. Everything clickable in it — the
   downloads at the end of a build — is a flat plate with a hairline and one
   radius, which stops being a card and starts being part of an instrument. The
   12px corner, the 24px padding and the press-down on :active were the three
   things on this page that belonged to a different product.

   Written here rather than in index.html's inline style because 404.html uses
   the same markup and was compensating with inline attributes on every
   element — which cannot express :hover, so those three cards were the only
   ones on the site that did not respond to the pointer. */
/* One panel of cells rather than a wall of cards. The console's answer to
   "several equal things" is `.facts`: a hairline grid with no gaps, where the
   1px rules are the cells' own outlines showing through a 1px gap. It fills
   evenly however many things there are — four tools, five guides, three
   comparisons — instead of leaving a card stranded on its own row, and it
   reads as one instrument rather than as a page of offers. */
/* Flex rather than grid, because a grid leaves holes. Four tools in three
   columns puts one cell on a row of its own and two outlined blanks beside it,
   and a panel with two empty cells in it looks like a panel that failed to
   load. Flex items grow into the row they land on, so the last row is as full
   as the first however many there are — four tools, five guides, three
   comparisons, one product. */
.tools{display:flex;flex-wrap:wrap;gap:1px;
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r);
  overflow:hidden;margin-top:24px}
.tool{flex:1 1 330px;min-width:0;background:var(--surface);padding:16px 18px;
  box-shadow:0 0 0 1px var(--line-2);text-decoration:none;color:inherit;
  transition:background .12s}
.tool:hover{background:var(--surface-2);text-decoration:none}
.tool:hover h3{color:var(--accent)}
/* A card heading is an h3 under a section that has its own h2, and an h2
   where the cards are the page — which is 404, where they are all there is. */
.tool h3,.tool h2{margin-bottom:5px}
.tool h2{font-size:.95rem;font-weight:600;letter-spacing:0}
.tool p{margin:0;font-size:.9rem;line-height:1.5;color:var(--ink-2)}
.tool .free{font-family:var(--mono);font-size:.66rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--accent);margin-bottom:8px}

/* A JavaScript hook that lands mid-sentence: the specification date should not
   break across two lines in the middle of the claim it is dating. */
.spec-version{white-space:nowrap}

/* The way into the product, shown after the answer and never in front of it.
   It replaced an email box in the same slot: the tools and the product ship
   together and finished, so this offers the thing rather than a message about
   it later. The accent edge is the one place on these pages that marks a panel
   as the next step rather than as more reading. */
.cta { margin-top: 26px; border-left: 3px solid var(--accent); }
.cta h3 { margin-bottom: 6px; }
.cta-note { font-size: .94rem; max-width: var(--measure); }
.cta-price { margin: 12px 0 0; font-size: .96rem; }
.cta-price b { font-family: var(--mono); font-size: 1.06rem; letter-spacing: -.01em; }
.cta-row { display: flex; gap: 10px; margin-top: 15px; flex-wrap: wrap; }
.cta-free { font-size: .86rem; margin: 13px 0 0; max-width: var(--measure); }

/* --- long-form pages (guides, comparisons) --------------------------------
   Written once here because there will be more of these than there are tools,
   and a guide that drifts from the tool it links to reads as two sites. */
/* What every paragraph outside an <article> asks for. It used to be written
   into the page as `style="max-width:62ch"`, eleven times, in three different
   numbers — so the site's measure lived in the markup and could not be changed
   anywhere. */
.measure{max-width:var(--measure)}

article{max-width:var(--measure)}
article h2{margin:2.2em 0 .55em}
article h3{margin:1.5em 0 .4em;font-size:1.05rem}
article p{margin:0 0 1.1em}
article ul,article ol{margin:0 0 1.2em;padding-left:1.3em;color:var(--ink-2)}
article li{margin-bottom:.45em}
article table{margin:0 0 1.4em}
.meta{font-family:var(--mono);font-size:.72rem;color:var(--ink-3);margin-bottom:2em}
.callout{border-left:2px solid var(--accent);background:var(--surface-2);
  padding:15px 18px;border-radius:0 var(--r) var(--r) 0;margin:1.7em 0}
.callout p:last-child{margin-bottom:0}
.disclose{border:1px solid var(--warn-line);background:var(--warn-bg);color:var(--warn);
  border-radius:var(--r);padding:13px 16px;margin:0 0 2em;font-size:.92rem}
.disclose p:last-child{margin-bottom:0}
/* The gutter guide's four-line cycle — the one thing on that page that has to
   read as a worked example rather than as another paragraph. */
.cycle{font-family:var(--mono);font-size:.86rem;line-height:1.9;
  background:var(--surface);border:1px solid var(--line);
  border-left:2px solid var(--accent);border-radius:0 var(--r) var(--r) 0;
  padding:15px 18px;margin:1.6em 0;overflow-x:auto}
/* a numbered list of causes, each ending in the one observable that identifies it */
.cause{border-bottom:1px solid var(--line);padding:22px 0}
.cause:last-of-type{border-bottom:0}
.cause h2{margin:0 0 .4em;font-size:1.18rem;display:flex;gap:.6em;align-items:baseline}
.cause h2 .n{font-family:var(--mono);font-size:.72rem;letter-spacing:.08em;color:var(--ink-3)}
.cause .tell{font-size:.92rem;color:var(--ink-3);margin-top:.7em}
.cause .tell b{color:var(--ink-2);font-weight:650}
.related{margin:3em 0 0;padding-top:1.6em;border-top:1px solid var(--line)}
.related h2{font-family:var(--mono);font-size:.66rem;text-transform:uppercase;
  letter-spacing:.12em;color:var(--ink-3);font-weight:400;margin:0 0 .9em}
.related ul{list-style:none;margin:0;padding:0;columns:2;column-gap:34px}
.related li{margin-bottom:.5em;break-inside:avoid;font-size:.93rem}
@media (max-width:620px){.related ul{columns:1}}
