:root{
  color-scheme:dark;
  --bg:#0b0f14;        --glow:#16233a;
  --panel:#121821;     --panel-2:#171f2a;
  --line:#233043;      --line-soft:#1a2431;   --line-2:#2a3444;
  --text:#e2ebf7;      --muted:#94a4b9;       --faint:#5b6b80;
  /* Headings are deliberately a touch softer than body text and a good deal
     lighter in weight than they were. Pure white at weight 800 on a near-black
     page glares; a slightly dimmed blue-white at bold reads as calm instead of
     shouty, and the text is still ~14:1 against the background. */
  --head:#d7e3f4;      --head-2:#c6d5ea;
  --accent-d:#3d74e0;  --shadow:0 20px 48px rgba(0,0,0,.44);
  --shadow-sm:0 6px 20px rgba(0,0,0,.3);
  --ring:rgba(79,140,255,.5);
  --faint-2:#4e5c72;   --faint-3:#3f4b5e;
  --chip:#c9d6e6;      --dot:#2b3a4f;
  --inset:#0d131c;     --inset-2:#0e1724;     --track:#111c2b;
  --ico-bg:#141d2a;    --btn-quiet:#0f1620;
  --mock-a:#131b26;    --mock-b:#0e1420;
  --cta-a:#132038;     --cta-b:#101a28;
  --ok-bg:#0f2419;     --ok-line:#1d5c40;     --ok-text:#a7f3d0;
  --bad-bg:#2a1416;    --bad-line:#5b2226;    --bad-text:#ffb3b3;
  --accent:#4f8cff;    --accent-2:#22c98b;    --danger:#ff6b6b;
  --header-bg:rgba(11,15,20,.82);
  --ad-bg:rgba(255,255,255,.014);
  --radius:14px; --maxw:940px;
  /* The reading measure for text pages: about 78 characters of body text.
     Fixed in pixels rather than in ch so that headings, the intro and the
     paragraphs all end on the same edge instead of each landing on its own. */
  --measure:588px;
  /* One spacing value for the whole site.
     Before this the vertical rhythm mixed 14, 24, 26, 30, 34, 40 and 74px, and
     the hero centred itself inside an 800px box so it opened with a ~250px hole
     under the header and closed with another one. Nothing was wrong with any
     single value; the problem was that no two of them were the same, which is
     what makes spacing read as accidental. Every gap between one block of
     content and the next is now this. */
  --gap:32px;
  /* Inside a list, items belong together, so they sit closer than two separate
     blocks do. One value, used in every list on the site, so the difference
     between "next item" and "next block" is a rule rather than an accident. */
  --gap-tight:12px;
}

/* Same layout, inverted surfaces. Every colour above has a counterpart here, so
   nothing has to be special-cased in a component rule. */
[data-theme="light"]{
  color-scheme:light;
  --bg:#f6f8fb;        --glow:#dce7f7;
  --panel:#ffffff;     --panel-2:#f3f6fa;
  --line:#d6dee9;      --line-soft:#e6ebf2;   --line-2:#c3cedb;
  --text:#101c26;      --muted:#59697d;       --faint:#7e8b9c;
  --head:#1b2734;      --head-2:#2b3a4a;
  --accent-d:#1d4ed8;  --shadow:0 20px 48px rgba(13,22,32,.14);
  --shadow-sm:0 6px 20px rgba(13,22,32,.09);
  --ring:rgba(37,99,235,.4);
  --faint-2:#8c98a8;   --faint-3:#9aa5b4;
  --chip:#26313f;      --dot:#c4ceda;
  --inset:#eef2f7;     --inset-2:#e9eef5;     --track:#dde4ec;
  --ico-bg:#eaf0f9;    --btn-quiet:#eef2f7;
  --mock-a:#ffffff;    --mock-b:#f5f8fc;
  --cta-a:#eaf1fb;     --cta-b:#f4f7fc;
  --ok-bg:#e6f7ee;     --ok-line:#a9dfc4;     --ok-text:#0d6640;
  --bad-bg:#fdeceb;    --bad-line:#f0b8b4;    --bad-text:#8f2018;
  --accent:#2563eb;    --accent-2:#0f9d63;    --danger:#c0392b;
  --header-bg:rgba(246,248,251,.85);
  --ad-bg:rgba(13,22,32,.02);
}

*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
  margin:0;background:radial-gradient(1200px 600px at 50% -10%,var(--glow) 0%,var(--bg) 60%);
  color:var(--text);font:16px/1.66 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  min-height:100vh;display:flex;flex-direction:column;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline;text-underline-offset:2px}
/* One visible focus treatment for the whole site. Keyboard users get a ring on
   every control; mouse users never see it. */
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:6px}
::selection{background:var(--accent);color:#fff}
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 20px;width:100%}
header.site{border-bottom:1px solid var(--line);background:var(--header-bg);backdrop-filter:blur(8px);
  position:sticky;top:0;z-index:20}
header.site .wrap{display:flex;align-items:center;justify-content:space-between;height:64px}
/* The wordmark is the whole logo, so it has to carry the header on its own. At
   17px it read as a nav item rather than as the name of the site. */
.brand{font-weight:700;letter-spacing:-.018em;color:var(--head);font-size:20px}
.brand span{color:var(--accent)}
nav.site a{margin-left:18px;font-size:14.5px;color:var(--muted);
  transition:color .18s ease}
nav.site a:hover{color:var(--head)}
main{flex:1;padding:var(--gap) 0 0}
/* Headings: weight 700 rather than 800, a softer colour, and a hair more room
   below. At display sizes the old weight read as shouting. */
h1{font-size:clamp(26px,4.4vw,38px);letter-spacing:-.022em;margin:0 0 var(--gap);line-height:1.18;
  font-weight:700;color:var(--head)}
h2{font-size:21px;margin:0 0 var(--gap);letter-spacing:-.014em;font-weight:650;color:var(--head)}
h3{font-size:16.5px;margin:0 0 var(--gap);font-weight:650;color:var(--head)}
/* Paragraphs take the same gap as everything else. The browser default (1em,
   about 16px) would otherwise be the one place on the site with a rhythm of its
   own, and a run of paragraphs would sit tighter than every other pair of blocks.
   Every block carries its spacing below rather than above, so two neighbours
   never add up to a double gap. */
p,ul,ol{margin:0 0 var(--gap)}
/* A block that opens or closes a container owns no outer margin: the container's
   padding is the gap at that edge, and without this the two would stack. */
.wrap > *:first-child{margin-top:0}
.wrap > *:last-child{margin-bottom:0}
/* A lede fills its container rather than stopping at 70ch.
   Short of the container it left the page looking broken: on a tool page the
   heading and the panel below both ran to 900px while the intro stopped at 641,
   and on the home page two paragraphs ended two-thirds of the way across above a
   table that did not. Text that fails to reach the edge of the box it sits in
   reads as an accident, not as a measure. These intros are two to four lines, so
   the longer line costs very little; long-form pages keep their comfortable
   measure through the .prose rule below. */
.lede{color:var(--muted);font-size:16px;line-height:1.6;margin:0 0 var(--gap)}
/* The intro and, where there is one, the note about what the tool can read, set
   side by side. Both are read once, before the drop zone, and stacked they were
   pushing the only part of the page that does anything below the fold of an
   ordinary laptop. Below about 820px there is not enough width for two columns
   to be comfortable, so they go back to one. */
.hero-row{display:grid;grid-template-columns:1.3fr 1fr;gap:var(--gap);align-items:start;
  margin:0 0 var(--gap)}
.hero-row .lede,.hero-row .notice{margin:0}
@media (max-width:820px){.hero-row{grid-template-columns:1fr}}
.pill{display:inline-block;background:var(--ok-bg);border:1px solid var(--ok-line);color:var(--ok-text);
  font-size:12.5px;font-weight:600;letter-spacing:.01em;padding:5px 12px;border-radius:999px;margin-bottom:var(--gap)}
.panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:22px;
  margin:0 0 var(--gap);box-shadow:var(--shadow-sm)}
.drop{border:2px dashed var(--line);border-radius:var(--radius);padding:40px 20px;text-align:center;
  cursor:pointer;background:var(--ad-bg);
  transition:border-color .18s ease,background .18s ease,transform .18s ease}
.drop:hover,.drop.over{border-color:var(--accent);background:var(--inset-2)}
.drop.over{transform:scale(1.006)}
.drop strong{display:block;margin-bottom:6px;font-size:16px;color:var(--head);font-weight:650}
.hint{color:var(--muted);font-size:13.5px}
/* Once there is something in it, the box stops being a prompt and becomes the
   frame the pictures sit in: the question shrinks to a line and what is inside
   is laid out from the left, but the dashed edge stays, so it still reads as the
   place things are dropped into rather than as a list that happens to be near
   one. */
.drop.has-files{padding:14px;text-align:left}
.drop.has-files strong{font-size:13px;margin-bottom:2px;color:var(--muted)}
.drop.has-files .hint{font-size:12px}
.drop.has-files .preview{margin-top:14px}
input[type=file]{display:none}
.row{display:flex;flex-wrap:wrap;gap:12px;align-items:center;margin-top:16px;max-width:100%}
label.field{font-size:13.5px;color:var(--muted);display:flex;flex-direction:column;gap:5px;
  min-width:0;max-width:100%}
/* A native <select> sizes itself to its widest <option> — on the compress page
   that pushed the control past a 375px viewport. Bound both to the column. */
select,input[type=text]{background:var(--panel-2);color:var(--text);border:1px solid var(--line);
  border-radius:10px;padding:10px 12px;font-size:14px;width:100%;max-width:100%;box-sizing:border-box}
/* A shallow gradient plus a soft shadow is what separates a button that looks
   hand-drawn from one that looks built. The press state moves it 1px so a click
   is felt as well as seen. */
button{background:linear-gradient(180deg,var(--accent),var(--accent-d));color:#fff;border:0;
  border-radius:10px;padding:12px 22px;font-size:15px;font-weight:640;cursor:pointer;
  box-shadow:0 4px 14px rgba(0,0,0,.28),inset 0 1px 0 rgba(255,255,255,.16);
  transition:transform .12s ease,box-shadow .2s ease,filter .2s ease}
button:hover:not(:disabled){filter:brightness(1.08);box-shadow:0 6px 20px rgba(0,0,0,.34),inset 0 1px 0 rgba(255,255,255,.2)}
button:active:not(:disabled){transform:translateY(1px);box-shadow:0 2px 8px rgba(0,0,0,.3)}
/* A disabled button keeps the shape of the button it will become, but drops the
   accent colour entirely. A dimmed blue still reads as "press me" — a neutral
   surface reads as "not yet". */
button:disabled{cursor:not-allowed;box-shadow:none;background:var(--panel-2);
  color:var(--faint);border:1px solid var(--line);opacity:1}
button.ghost{background:var(--line-2);border:1px solid var(--line);box-shadow:none;color:var(--text)}
button.ghost:hover:not(:disabled){filter:none;border-color:var(--accent)}
button.link{background:none;color:var(--danger);padding:0;font-weight:400;font-size:13.5px;
  box-shadow:none;border:0}
button.link:hover{text-decoration:underline}
.files{margin-top:14px;display:flex;flex-direction:column;gap:6px}
.files .item{background:var(--panel-2);border:1px solid var(--line);border-radius:9px;
  padding:9px 12px;font-size:13.5px;display:flex;justify-content:space-between;gap:10px;
  align-items:center;transition:border-color .18s ease}
.files .item:hover{border-color:var(--line-2)}

/* ------------------------------------------------------- the file workspace --
   For the two tools where the order of the files is the answer. Each row carries
   something you can recognise at a glance — the picture itself for an image, the
   page count for a document — because four invoices that all begin with the same
   word cannot be put in order by reading their names.

   Dragging is the mouse gesture. The arrows beside it are not decoration: HTML5
   drag events never fire on a touch screen, so without them reordering would
   simply not exist on a phone. */
.workspace-list{display:flex;flex-direction:column;gap:8px;margin-top:var(--gap);
  list-style:none;padding:0}
.ws-item{display:flex;align-items:center;gap:11px;background:var(--panel-2);
  border:1px solid var(--line);border-radius:11px;padding:9px 11px;cursor:grab;
  transition:border-color .18s ease,opacity .18s ease,box-shadow .12s ease}
.ws-item:hover{border-color:var(--line-2)}
.ws-item.dragging{opacity:.45;cursor:grabbing}
.ws-item.over-before{box-shadow:inset 0 3px 0 var(--accent)}
.ws-item.over-after{box-shadow:inset 0 -3px 0 var(--accent)}
.ws-num{flex:0 0 22px;height:22px;border-radius:50%;display:grid;place-items:center;
  background:var(--cta-a);border:1px solid var(--line-2);color:var(--accent);
  font-size:11.5px;font-weight:700}
.ws-thumb{flex:0 0 40px;width:40px;height:40px;object-fit:cover;border-radius:7px;
  background:#fff;border:1px solid var(--line)}
.ws-doc{display:grid;place-items:center;background:var(--panel);color:var(--faint)}
.ws-doc svg{width:19px;height:19px}
.ws-body{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.ws-name{font-size:13.5px;color:var(--text);overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.ws-meta{font-size:12px;color:var(--faint);font-variant-numeric:tabular-nums}
.ws-acts{display:flex;gap:4px;flex:0 0 auto}
.ws-acts button{padding:0 9px;min-height:32px;font-size:14px;line-height:1;color:var(--muted)}
.ws-acts button:disabled{opacity:.3}

@media (max-width:560px){
  /* A finger needs a bigger target than a cursor does. */
  .ws-acts button{min-height:40px;padding:0 11px}
  .ws-item{gap:9px;padding:8px}
  .ws-thumb{flex-basis:34px;width:34px;height:34px}
}
.bar{height:6px;background:var(--panel-2);border-radius:99px;overflow:hidden;margin-top:16px;display:none}
.bar.on{display:block}
.bar i{display:block;height:100%;width:0;background:linear-gradient(90deg,var(--accent),var(--accent-2));transition:width .15s}
.msg{margin-top:16px;padding:12px 14px;border-radius:10px;font-size:14px;display:none;white-space:pre-wrap}
.msg.err{display:block;background:var(--bad-bg);border:1px solid var(--bad-line);color:var(--bad-text)}
.msg.ok{display:block;background:var(--ok-bg);border:1px solid var(--ok-line);color:var(--ok-text)}
.msg.info{display:block;background:var(--track);border:1px solid var(--line);color:var(--muted)}

/* The extracted-text box, used only by the image-to-text tool.
   A textarea rather than a styled div on purpose: the point of the tool is to
   get text out, and a real textarea gives select-all, Ctrl+A, find-on-page and
   the browser's own spellcheck-off behaviour for free — none of which a
   contenteditable pretends to have. */
.out{margin-top:var(--gap);border:1px solid var(--line);border-radius:var(--radius);
  background:var(--panel);overflow:hidden}
.out[hidden]{display:none}
.out-head{display:flex;flex-wrap:wrap;gap:10px 14px;align-items:center;justify-content:space-between;
  padding:12px 14px;border-bottom:1px solid var(--line);background:var(--panel-2)}
#out-meta{font-size:12.5px;color:var(--muted);font-variant-numeric:tabular-nums}
.out-acts{display:flex;gap:8px;flex-wrap:wrap}

/* ------------------------------------------------------------ the text box --
   For the tool that starts from the keyboard. A textarea rather than a rich
   editor: the formatting it understands is two characters wide, and a plain box
   never fights the visitor over what they typed. */
.ta-wrap{margin-top:var(--gap)}
.field-lab{display:block;font-size:12px;font-weight:600;letter-spacing:.06em;
  text-transform:uppercase;color:var(--faint);margin-bottom:8px}
#ta{display:block;width:100%;min-height:240px;resize:vertical;spellcheck:false;
  background:var(--panel-2);border:1px solid var(--line);border-radius:11px;
  color:var(--text);padding:14px;font:13.5px/1.7 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  outline:none;transition:border-color .18s ease}
#ta:focus{border-color:var(--accent)}
#ta::placeholder{color:var(--faint-2)}

/* ------------------------------------------------------------- the options --
   Labelled fields rather than a bare row of selects: "1.45" means nothing and
   "Normal spacing" means something. */
.opts{display:grid;grid-template-columns:repeat(auto-fit,minmax(158px,1fr));
  gap:14px 16px;margin-top:var(--gap);padding-top:var(--gap);border-top:1px solid var(--line)}
.field{display:flex;flex-direction:column;gap:6px;min-width:0}
.field.wide{grid-column:1 / -1}
.field > span{font-size:12px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;
  color:var(--faint)}
.field select,.field input[type=text]{background:var(--panel-2);border:1px solid var(--line);
  border-radius:9px;color:var(--text);padding:9px 11px;font-size:14px;outline:none;width:100%;
  transition:border-color .18s ease}
.field select:focus,.field input[type=text]:focus{border-color:var(--accent)}
.check{display:flex;align-items:center;gap:9px;font-size:14px;color:var(--muted);cursor:pointer;
  grid-column:1 / -1}
.check input{width:16px;height:16px;accent-color:var(--accent);flex:0 0 16px}
@media (min-width:660px){ .check{grid-column:auto} }

/* ------------------------------------------------------ the picture grid ----
   Only on the tool where the orientation of the input decides whether the tool
   works. Each picture carries its own turn buttons, because one sideways page in
   a batch is the usual case rather than the exception — and, once there is more
   than one, its page number and the controls that set the order. */
.preview{display:grid;grid-template-columns:repeat(auto-fill,minmax(132px,1fr));
  gap:12px;margin-top:var(--gap)}
.preview[hidden]{display:none}
.pv{background:var(--panel-2);border:1px solid var(--line);border-radius:11px;padding:9px;
  display:flex;flex-direction:column;gap:8px;align-items:center;min-width:0;position:relative}
.pv img{max-width:100%;height:88px;object-fit:contain;border-radius:6px;background:#fff;
  transition:transform .18s ease}
/* Five controls on a narrow card have to be allowed to fall onto two rows. */
.pv-acts{display:flex;gap:6px;flex-wrap:wrap;justify-content:center}
.pv-acts button{padding:4px 10px;font-size:14px;line-height:1}
.pv-name{font-size:11.5px;color:var(--faint);max-width:100%;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
/* The page number, and the edge that shows where a dragged card will land. Same
   shapes as the list the merge and image-to-PDF tools use, so one pattern is
   learnt rather than two. */
.pv-num{position:absolute;top:8px;left:8px;width:22px;height:22px;border-radius:50%;
  display:grid;place-items:center;background:var(--cta-a);border:1px solid var(--line-2);
  color:var(--accent);font-size:11.5px;font-weight:700}
/* A card that can be moved should look like one, because "you may drag this"
   is not something a visitor should have to discover by trying it. */
.pv[draggable="true"]{cursor:grab}
.pv.dragging{opacity:.45;cursor:grabbing}
.pv.over-before{box-shadow:inset 3px 0 0 var(--accent)}
.pv.over-after{box-shadow:inset -3px 0 0 var(--accent)}
.out-acts button{padding:7px 13px;font-size:13px}
#out-text{display:block;width:100%;min-height:260px;max-height:60vh;resize:vertical;
  border:0;outline:none;background:transparent;color:var(--text);
  padding:14px;font:13.5px/1.7 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  white-space:pre-wrap;overflow:auto}
.grid{display:grid;gap:15px;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));margin:0 0 var(--gap)}
.card{background:linear-gradient(180deg,var(--panel),var(--panel-2));border:1px solid var(--line);
  border-radius:var(--radius);padding:18px;
  transition:border-color .2s ease,transform .2s ease,box-shadow .2s ease}
.card:hover{border-color:var(--accent);transform:translateY(-2px);box-shadow:var(--shadow-sm)}
.card h3{margin:0 0 6px;font-size:15.5px;color:var(--head);font-weight:650}
.card p{margin:0;color:var(--muted);font-size:13.5px;line-height:1.6}
.steps{counter-reset:s;list-style:none;padding:0;margin:0 0 var(--gap)}
.steps li{counter-increment:s;position:relative;padding:0 0 var(--gap-tight) 40px;color:var(--muted)}
.steps li::before{content:counter(s);position:absolute;left:0;top:0;width:26px;height:26px;border-radius:50%;
  background:var(--cta-a);border:1px solid var(--line-2);color:var(--accent);font-size:12.5px;
  display:flex;align-items:center;justify-content:center;font-weight:700}
.steps li strong{color:var(--head);display:block}
/* FAQ. The chevron is drawn in CSS so the markup stays a plain <details>. */
details{border:1px solid var(--line);border-radius:11px;padding:13px 16px;margin:0 0 var(--gap-tight);
  background:var(--panel);transition:border-color .18s ease,background .18s ease}
details:hover{border-color:var(--line-2);background:var(--panel-2)}
details[open]{border-color:var(--line-2)}
details summary{cursor:pointer;font-weight:600;font-size:15px;color:var(--head);
  list-style:none;display:flex;align-items:center;gap:10px}
details summary::-webkit-details-marker{display:none}
details summary::after{content:"";margin-left:auto;flex:0 0 7px;width:7px;height:7px;
  border-right:1.6px solid var(--muted);border-bottom:1.6px solid var(--muted);
  transform:rotate(45deg) translate(-2px,-2px);transition:transform .2s ease}
details[open] summary::after{transform:rotate(-135deg) translate(-2px,-2px);border-color:var(--accent)}
details p{color:var(--muted);margin:10px 0 0;font-size:14.5px;line-height:1.7}
.pro{background:linear-gradient(90deg,var(--cta-a),var(--cta-b));border:1px solid var(--line);border-radius:var(--radius);
  padding:22px;margin:0 0 var(--gap);box-shadow:var(--shadow-sm)}
.pro h2{margin-top:0}
.pro table{width:100%;border-collapse:collapse;font-size:14.5px}
.pro th,.pro td{text-align:left;padding:9px 8px;border-bottom:1px solid var(--line);color:var(--muted)}
.pro th{color:var(--text);font-size:13.5px;text-transform:uppercase;letter-spacing:.04em}
/* Four columns on a wide screen: the promise, then every tool, every guide and
   the pages that carry the site's own information. A flat run of twenty links
   separated by dots was compact, but it read as a wall — nothing distinguished a
   tool from an article, and there was no route to anything that is not a link. */
footer.site{border-top:1px solid var(--line);padding:var(--gap) 0;color:var(--muted);font-size:13.5px;
  background:linear-gradient(180deg,transparent,var(--ad-bg))}
.foot-grid{display:grid;gap:30px 28px;
  grid-template-columns:minmax(0,1.5fr) repeat(3,minmax(0,1fr))}
.foot-logo{margin:0 0 11px;font-size:16px;font-weight:700;letter-spacing:-.014em;color:var(--head)}
.foot-logo span{color:var(--accent)}
.foot-tag{margin:0;max-width:34ch;line-height:1.65}
.foot-col{display:flex;flex-direction:column;gap:9px;min-width:0}
.foot-h{margin:0 0 3px;font-size:11.5px;font-weight:700;letter-spacing:.1em;
  text-transform:uppercase;color:var(--faint)}
footer.site a{color:var(--muted);transition:color .18s ease}
footer.site a:hover{color:var(--head)}
.foot-base{display:flex;flex-wrap:wrap;gap:8px 20px;justify-content:space-between;align-items:center;
  margin-top:32px;padding-top:18px;border-top:1px solid var(--line-soft);
  color:var(--faint);font-size:12.5px}

@media (max-width:880px){
  /* Tools and Guides stay side by side rather than stacking. As one column the
     footer ran to roughly 1000px of scrolling on a phone with the right half
     empty for all of it; two columns halves that, and the link labels are short
     enough to hold a single line at this width. */
  .foot-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:26px 16px}
  /* The brand line reads as a heading for everything below it, so it spans. */
  .foot-brand{grid-column:1 / -1}
  /* Four links in a half-width column would leave the other half empty, so
     these flow across the full width instead. */
  .foot-site{grid-column:1 / -1;flex-direction:row;flex-wrap:wrap;gap:9px 20px}
  .foot-site .foot-h{flex:0 0 100%}
}
@media (max-width:560px){
  footer.site{padding:var(--gap) 0}
  .foot-tag{max-width:none}
}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
/* One row above every subpage heading: a way back on the left, where you are on
   the right. Wraps to two rows on a phone with the button first, which is the
   order the two are useful in. */
.topnav{display:flex;align-items:center;justify-content:space-between;
  gap:10px 12px;flex-wrap:wrap;margin:0 0 var(--gap)}
.topnav .crumbs{margin:0}
/* 44px tall, which is the smallest comfortable tap target — this is the control
   a phone user reaches for first, and the breadcrumb text beside it is not
   tappable at all. The border is --line-2 rather than --line because in the
   light theme the fill alone is nearly invisible and the border is doing all the
   work of saying "this is a button". */
a.back{display:inline-flex;align-items:center;justify-content:center;gap:6px;
  min-height:44px;font-size:13.5px;font-weight:600;
  color:var(--muted);background:var(--panel-2);border:1px solid var(--line-2);
  border-radius:10px;padding:0 15px 0 13px;
  transition:border-color .18s ease,color .18s ease,background .18s ease}
a.back:hover{color:var(--head);border-color:var(--accent);background:var(--inset-2);text-decoration:none}
a.back svg{width:14px;height:14px;flex:0 0 14px}
.crumbs{font-size:13.5px;color:var(--muted);margin:0;display:flex;flex-wrap:wrap;
  gap:7px;align-items:center}
.crumbs a{color:var(--muted);transition:color .18s ease}
.crumbs a:hover{color:var(--text)}
.crumbs .sep{color:var(--faint-3)}
.crumbs span{color:var(--text)}

/* Wide tables scroll inside their own box instead of widening the page. */
.tablewrap{overflow-x:auto;margin:0 0 var(--gap);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow-sm)}
.tablewrap table{min-width:540px}
table.compare,table.simple{width:100%;border-collapse:collapse;font-size:14.5px}
table.compare th,table.compare td,table.simple th,table.simple td{
  text-align:left;padding:12px 13px;border-bottom:1px solid var(--line);vertical-align:top}
table.compare th,table.simple th{color:var(--faint);font-size:11.5px;font-weight:700;
  text-transform:uppercase;letter-spacing:.08em;background:var(--panel-2)}
table.compare td,table.simple td{color:var(--muted)}
table.compare td:first-child{color:var(--head);font-weight:600}
table.compare tbody tr:hover td,table.simple tbody tr:hover td{background:var(--panel-2)}
table tr:last-child td{border-bottom:0}

.cta{background:linear-gradient(90deg,var(--cta-a),var(--cta-b));border:1px solid var(--line);
  border-radius:var(--radius);padding:22px;margin:0 0 var(--gap);box-shadow:var(--shadow-sm)}
.cta h2{margin-top:0}
a.button{display:inline-block;background:linear-gradient(180deg,var(--accent),var(--accent-d));
  color:#fff;border-radius:10px;padding:12px 22px;font-weight:640;
  box-shadow:0 4px 14px rgba(0,0,0,.28),inset 0 1px 0 rgba(255,255,255,.16)}
a.button:hover{filter:brightness(1.08);text-decoration:none}

/* =====================================================================
   Home page
   ===================================================================== */

/* A sticky, slightly translucent header with a backdrop blur. Cheap, and it
   does more for perceived polish than anything else on the page. */
header.site{position:sticky;top:0;z-index:20;background:var(--header-bg);
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}

/* --- hero --- */
/* Where two bands meet, each takes HALF the gap and the border falls between
   them. With the whole gap on one side, the divider ended up flush against the
   text on the other: "Pick a tool" was touching the strip's bottom border, and
   the strip's caption was touching the hero's. Half each keeps the total at one
   gap and gives the border air on both sides of it. */
.hero{position:relative;overflow:hidden;border-bottom:1px solid var(--line);
  padding:0 0 calc(var(--gap) / 2)}
main > section + .wrap{padding-top:calc(var(--gap) / 2)}
#hero-canvas{position:absolute;inset:0;pointer-events:none;
  -webkit-mask-image:linear-gradient(to bottom,#000 52%,transparent 100%);
  mask-image:linear-gradient(to bottom,#000 52%,transparent 100%)}
/* The hero shares the one column with the header, the body and the footer.
   It used to break out 70px either side, which meant the headline's first
   letter sat 70px to the left of the logo directly above it and the network
   card's right edge overhung the theme button. Two edges that nearly line up
   look like a mistake in a way that two edges plainly far apart do not — so
   there is one column and everything lands on it. The headline is sized for
   this column rather than the column being widened for the headline. */
.hero .wrap{position:relative;z-index:1;max-width:var(--maxw);display:grid;gap:42px;
  grid-template-columns:minmax(0,1.06fr) minmax(0,.94fr);align-items:center}
.hero h1{font-size:clamp(30px,4.6vw,50px);line-height:1.08;letter-spacing:-.026em;
  font-weight:700;color:var(--head);margin:0 0 var(--gap)}
.hero .sub{font-size:clamp(16px,1.5vw,18px);line-height:1.7;color:var(--muted);
  margin:0 0 var(--gap)}
/* The global .cta rule draws a bordered gradient box. On a landing hero that box
   wrapped two buttons and then ran on for 200px of empty space, which reads as a
   mistake rather than a design. The hero gets the buttons bare. */
.hero .cta{display:flex;gap:12px;flex-wrap:wrap;margin:0 0 var(--gap);
  background:none;border:0;padding:0;border-radius:0}
.hero .checks{list-style:none;padding:0;margin:0;display:grid;gap:var(--gap-tight);
  font-size:14.5px;color:var(--muted)}
.hero .checks li{display:flex;gap:10px;align-items:flex-start}
.hero .checks svg{flex:0 0 15px;margin-top:4px;color:var(--accent-2)}

a.btn{display:inline-block;background:linear-gradient(180deg,var(--accent),var(--accent-d));
  color:#fff;border-radius:11px;padding:13px 24px;font-weight:640;font-size:15px;
  box-shadow:0 6px 20px rgba(31,86,208,.34),inset 0 1px 0 rgba(255,255,255,.18);
  transition:transform .14s ease,box-shadow .2s ease,filter .2s ease}
a.btn.ghost{background:transparent;color:var(--text);border:1px solid var(--line);box-shadow:none}
a.btn:hover{filter:brightness(1.07);text-decoration:none;box-shadow:0 9px 26px rgba(31,86,208,.42),inset 0 1px 0 rgba(255,255,255,.2)}
a.btn:active{transform:translateY(1px)}
a.btn.ghost:hover{border-color:var(--accent);filter:none;box-shadow:none}

/* --- the mock: makes the privacy claim concrete instead of abstract --- */
.mock{background:linear-gradient(180deg,var(--mock-a),var(--mock-b));border:1px solid var(--line-2);
  border-radius:16px;padding:14px 15px 16px;box-shadow:var(--shadow),inset 0 1px 0 rgba(255,255,255,.05)}
/* The inner class names are deliberately NOT bar / head / row.
   They were, and two of them collided with single-word utility rules elsewhere
   in this file: a global `.bar` that draws a 6px skeleton pill (height:6px plus
   overflow:hidden) and a global `.row` with margin-top:16px and flex-wrap. The
   `.mock .bar` rule only overrode the properties it declared, so the pill's
   fixed height and clipping survived — which flattened the top off
   "pdfnest.pages.dev" in the title bar, and gave every log line 16px of
   unintended space and the freedom to wrap on a phone. A compound selector does
   not isolate you from the global namespace; the name has to be unique. */
.mock .titlebar{display:flex;align-items:center;gap:7px;padding-bottom:11px;
  border-bottom:1px solid var(--line);margin-bottom:13px}
.mock .titlebar i{width:9px;height:9px;border-radius:50%;background:var(--dot)}
.mock .titlebar span{margin-left:auto;font:12px ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  color:var(--muted)}
.mock .mline{display:flex;align-items:center;gap:10px;padding:8px 0;
  font:13px ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--muted);
  border-bottom:1px solid var(--line-soft)}
.mock .mline:last-child{border-bottom:0}
.mock .mline b{color:var(--head);font-weight:600}
.mock .mline .ok{margin-left:auto;color:var(--ok-text)}
.mock .caption{display:flex;align-items:center;justify-content:space-between;margin-bottom:4px}
.mock .caption em{font-style:normal;font-size:12px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--faint)}
.mock .pill-ok{background:var(--ok-bg);border:1px solid var(--ok-line);color:var(--ok-text);
  font-size:11.5px;padding:3px 9px;border-radius:999px}

/* --- marquee strip --- */
.strip{border-bottom:1px solid var(--line);padding:calc(var(--gap) / 2) 0;overflow:hidden}
.strip .wrap{max-width:var(--maxw)}
.strip .cap{font-size:12px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--faint);margin:0 0 20px}
/* The last item in a FAQ block ends the list, so it does not carry the
   between-items gap. */
/* The last item in a list still owns the FULL gap to whatever follows, because
   that gap is between blocks rather than between items. :last-of-type, not
   :last-child — a heading usually follows the list inside the same container,
   which is exactly the case that made the first attempt silently do nothing. */
details:last-of-type{margin-bottom:var(--gap)}
.steps li:last-child{padding-bottom:0}
/* These were briefly a row of cards, four across at 200px each. It read well as
   a shape and badly as writing: a 200px column holds about thirty characters, so
   every step turned into a narrow strip of broken lines and the longest ran twice
   the height of the shortest. The list is easier to read, and the steps sit at
   the foot of the page where height costs nothing. */
.marquee{position:relative;overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 5%,#000 95%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 5%,#000 95%,transparent)}
/* The track holds the list twice and slides by exactly half its own width, so the
   second copy lands where the first began — a seamless loop with no JavaScript. */
.marquee .track{display:flex;gap:11px;width:max-content;animation:slide 48s linear infinite}
.marquee ul{display:flex;gap:11px;list-style:none;margin:0;padding:0}
.marquee:hover .track{animation-play-state:paused}
.marquee li{background:linear-gradient(180deg,var(--panel),var(--panel-2));border:1px solid var(--line);
  border-radius:11px;padding:10px 17px;font-size:14px;color:var(--chip);white-space:nowrap;
  transition:border-color .2s ease,color .2s ease}
.marquee li:hover{border-color:var(--line-2);color:var(--head)}
@keyframes slide{to{transform:translateX(-50%)}}

/* --- tool + guide cards --- */
.grid .card.with-ico{display:flex;gap:13px;align-items:flex-start;text-align:left}
.card .ico{flex:0 0 38px;height:38px;border-radius:11px;display:grid;place-items:center;
  background:var(--ico-bg);border:1px solid var(--line);color:var(--accent);
  transition:background .2s ease,border-color .2s ease,transform .2s ease}
.card:hover .ico{border-color:var(--accent);transform:translateY(-1px)}
.card .ico svg{width:20px;height:20px;display:block}
.grid .card{transition:transform .2s ease,border-color .2s ease}
@media (hover:hover){
  .grid .card:hover{transform:translateY(-3px)}
}

/* --- reserved advertising space ---
   The height is FIXED rather than automatic. An ad that appears and pushes the
   page down is a layout shift, and Cumulative Layout Shift is one of the three
   Core Web Vitals Google scores — so the slot has to be reserved before the ad
   exists. Delete a slot and you delete the shift protection with it. */
.ad{margin:0 0 var(--gap);border:1px dashed var(--line);border-radius:var(--radius);
  min-height:124px;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:6px;background:var(--ad-bg);
  color:var(--faint-2);font-size:11.5px;letter-spacing:.14em;text-transform:uppercase}
.ad span{font-size:11px;letter-spacing:.04em;text-transform:none;color:var(--faint-3)}

/* --- entrance animation: 5 staggered steps, pure CSS, nothing to load --- */
@keyframes rise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
.rise{animation:rise .8s cubic-bezier(.2,.7,.2,1) both}

/* The headline is the Largest Contentful Paint element, and an element painted at
   opacity 0 is not a contentful paint — animating it in from invisible delays the
   metric Google actually scores. So the H1 moves but never fades. */
@keyframes rise-lcp{from{transform:translateY(12px)}to{transform:none}}
.rise-lcp{animation:rise-lcp .7s cubic-bezier(.2,.7,.2,1) both}
.rise-1{animation-delay:.06s}
.rise-2{animation-delay:.14s}
.rise-3{animation-delay:.2s}
.rise-4{animation-delay:.26s}
.rise-5{animation-delay:.32s}

/* ---- page organiser ---- */
.workspace{display:none;margin-top:18px}
.workspace.on{display:block}
.pagebar{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:10px}
.pagebar .count{font-size:13.5px;color:var(--muted);margin-right:auto}
.pagebar button{background:var(--panel-2);color:var(--text);border:1px solid var(--line);
  border-radius:9px;padding:8px 13px;font-size:13.5px;cursor:pointer;box-shadow:none;font-weight:600}
.pagebar button:hover:not(:disabled){filter:none;background:var(--inset-2)}
.pagebar button:hover:not(:disabled){border-color:var(--accent)}
.pagebar button:disabled{opacity:.4;cursor:default}
.pgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(132px,1fr));gap:12px}
.pcard{position:relative;background:var(--panel-2);border:1px solid var(--line);border-radius:12px;
  padding:9px;display:flex;flex-direction:column;gap:8px}
.pcard.sel{border-color:var(--accent)}
.pcard .chk{display:flex;align-items:center;gap:6px;font-size:12.5px;color:var(--muted);cursor:pointer}
.pcard .chk span{color:var(--text);font-weight:600}
.pcard .thumb{height:150px;display:flex;align-items:center;justify-content:center;
  background:var(--inset);border-radius:8px;overflow:hidden}
.pcard .thumb canvas{max-width:100%;max-height:150px;width:auto;height:auto;display:block}
.pcard .rot{position:absolute;top:34px;right:14px;background:var(--ok-bg);border:1px solid var(--ok-line);
  color:var(--ok-text);font-size:11.5px;padding:2px 7px;border-radius:999px}
.pcard .pacts{display:flex;gap:4px}
.pcard .pacts button{flex:1;background:var(--btn-quiet);color:var(--muted);border:1px solid var(--line);
  border-radius:7px;padding:5px 0;font-size:13px;line-height:1;cursor:pointer;box-shadow:none}
.pcard .pacts button:hover:not(:disabled){color:var(--text);border-color:var(--accent)}
.pcard .pacts button:disabled{opacity:.35;cursor:default}

@media (max-width:560px){
  .pgrid{grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:9px}
  .pcard .thumb{height:118px}
  .pcard .thumb canvas{max-height:118px}
  .pcard .pacts button{font-size:12px}
  .pagebar button{padding:8px 11px}
}

@media (max-width:560px){
  header.site .wrap{height:auto;padding-top:10px;padding-bottom:10px;gap:8px}
  /* Four visible links have to fit beside the logo on a 375px screen, so the
     wordmark takes the smaller step up here. */
  .brand{font-size:18px}
  nav.site a{margin-left:10px;font-size:12.5px}
  nav.site a.opt{display:none}
  main{padding:var(--gap) 0 0}
  /* One control per line, so width:100% resolves against a definite width. */
  label.field{flex:1 1 100%}
  .panel{padding:16px}
  .drop{padding:28px 14px}
}

/* =====================================================================
   Home page — narrow screens
   ===================================================================== */
@media (max-width:880px){
  .hero{padding:0 0 calc(var(--gap) / 2)}
  .hero .wrap{grid-template-columns:minmax(0,1fr);gap:var(--gap)}
  .hero .sub{max-width:none}
  .mock{order:2}
  .strip{padding:calc(var(--gap) / 2) 0}
}
@media (max-width:560px){
  .ad{min-height:100px;margin:0 0 var(--gap)}
  .marquee li{padding:9px 14px;font-size:13px}
  .hero h1{margin-bottom:var(--gap)}
}

/* =====================================================================
   prefers-reduced-motion
   The hero canvas already refuses to start a loop under this setting, so
   everything here is the CSS half: no entrance animation, no marquee drift.
   ===================================================================== */
@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  .rise,.rise-lcp{animation:none}
  .marquee .track{animation:none;flex-wrap:wrap;width:auto}
  .grid .card{transition:none}
  a.btn{transition:none}
}

/* =====================================================================
   Theme toggle
   The icon shows what a click will switch TO: a sun while dark, a moon while
   light. Both icons live in the button and CSS picks one, so the two themes
   never disagree about which is showing.
   ===================================================================== */
.theme-btn{margin-left:14px;background:transparent;border:1px solid var(--line);
  border-radius:9px;width:34px;height:30px;display:grid;place-items:center;
  color:var(--muted);cursor:pointer;padding:0;box-shadow:none}
.theme-btn:hover{border-color:var(--accent);color:var(--text)}
.theme-btn svg{width:15px;height:15px;display:block}
.theme-btn .i-moon{display:none}
[data-theme="light"] .theme-btn .i-sun{display:none}
[data-theme="light"] .theme-btn .i-moon{display:block}

/* Shadows and glows that would look wrong inverted. */
[data-theme="light"] .mock{box-shadow:0 18px 44px rgba(13,22,32,.1)}
[data-theme="light"] #hero-canvas{opacity:.85}
[data-theme="light"] header.site{box-shadow:0 1px 0 var(--line)}

@media (max-width:560px){
  .theme-btn{margin-left:8px;width:30px;height:28px}
}

/* =====================================================================
   Annotation editor (tool 8)
   The page is drawn to a canvas; everything placed on it is an absolutely
   positioned element inside .ed-overlay, whose box is sized to the canvas, so
   a viewport coordinate is a CSS coordinate and the maths stays in one place.
   ===================================================================== */
.ed{margin-top:16px}
.ed-bar{display:flex;flex-wrap:wrap;gap:8px}
.ed-t{display:flex;flex-direction:column;align-items:center;gap:3px;background:var(--panel-2);
  color:var(--muted);border:1px solid var(--line);border-radius:10px;padding:9px 13px;
  font-size:11.5px;cursor:pointer;min-width:78px;box-shadow:none;font-weight:600;
  transition:border-color .15s,background .15s,color .15s}
.ed-t:hover:not(:disabled){filter:none;background:var(--inset-2)}
.ed-t b{font-size:15px;line-height:1;font-weight:600;color:var(--text)}
.ed-t em{font-style:normal;white-space:nowrap}
.ed-t:hover{border-color:var(--accent);color:var(--text)}
.ed-t.on{border-color:var(--accent);background:var(--cta-a);color:var(--text)}
.ed-t.on b{color:var(--accent)}
.ed-t[hidden]{display:none}

.ed-hint{font-size:13.5px;color:var(--muted);margin:12px 0 0}
.ed-props{margin-top:14px}
.ed-props:empty{display:none}
.ed-ptitle{font-size:12.5px;text-transform:uppercase;letter-spacing:.07em;color:var(--faint);
  margin:0 0 10px;font-weight:600}
.ed-ptitle .hint{text-transform:none;letter-spacing:0;font-weight:400}
.ed-props .row{margin-top:12px}
.ed-props .field{flex:1 1 128px}
.ed-props input[type=color]{width:100%;max-width:74px;height:38px;padding:3px;
  background:var(--panel-2);border:1px solid var(--line);border-radius:10px;cursor:pointer}
.ed-props input[type=range]{width:100%;padding:0;border:0;background:transparent;
  accent-color:var(--accent);height:38px}
.ed-props input[type=text]:disabled{opacity:.6}
.ed-check{display:flex;align-items:center;gap:8px;font-size:13.5px;color:var(--text);
  white-space:nowrap;cursor:pointer}
.ed-check input{accent-color:var(--accent);width:16px;height:16px}

.ed-warn{display:none;margin-top:12px;padding:11px 13px;border-radius:10px;font-size:13.5px;
  background:var(--bad-bg);border:1px solid var(--bad-line);color:var(--bad-text)}
.ed-warn.on{display:block}

.ed-stage{margin-top:14px;overflow:auto;background:var(--inset);border:1px solid var(--line);
  border-radius:12px;padding:12px;max-height:min(78vh,900px);-webkit-overflow-scrolling:touch}
/* The scroll container must not be the positioned ancestor, or the overlay would
   stay put while the page scrolled underneath it. Hence the inner wrapper. */
.ed-inner{position:relative;margin:0 auto;background:#fff;box-shadow:0 6px 22px rgba(0,0,0,.3)}
[data-theme="light"] .ed-inner{box-shadow:0 4px 16px rgba(13,22,32,.12)}
#ed-canvas{display:block}
.ed-overlay{position:absolute;inset:0}
.ed-overlay.placing{cursor:crosshair}

.ed-el{position:absolute;box-sizing:border-box;outline:1px dashed transparent;outline-offset:2px;
  touch-action:none}
.ed-el.ed-text{white-space:pre;cursor:move}
.ed-el.ed-image,.ed-el.ed-rect{cursor:move}
.ed-el.ed-image img{width:100%;height:100%;display:block;object-fit:fill;pointer-events:none}
.ed-el.ed-rect{background:#fff}
@media (hover:hover){
  .ed-el:hover{outline-color:var(--faint-3)}
}
.ed-el.sel{outline:1px solid var(--accent)}
.ed-ghost{background:rgba(79,140,255,.18);border:1px dashed var(--accent);pointer-events:none}
.ed-h{position:absolute;right:-6px;bottom:-6px;width:14px;height:14px;border-radius:4px;
  background:var(--accent);border:2px solid var(--bg);cursor:nwse-resize;touch-action:none}
.ed-wm{pointer-events:none;white-space:nowrap;font-weight:700;letter-spacing:.03em}
.ed-pn{pointer-events:none;font-family:Helvetica,Arial,sans-serif;line-height:1}

.ed-items{margin-top:14px}
.ed-row{display:flex;align-items:center;gap:10px;background:var(--panel-2);border:1px solid var(--line);
  border-radius:9px;padding:8px 12px;font-size:13.5px;margin-bottom:6px}
.ed-row.sel{border-color:var(--accent)}
.ed-row .ed-k{color:var(--faint);text-transform:uppercase;font-size:11px;letter-spacing:.06em;
  min-width:46px}
.ed-row .ed-d{color:var(--text);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.ed-row .link{background:none;border:0;color:var(--accent);padding:0;font-weight:400;
  font-size:13px;cursor:pointer;white-space:nowrap}
.ed-row .link:hover{text-decoration:underline}

.ed-nav{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-top:12px}
.ed-nav button.ghost{padding:8px 13px;font-size:13px}
.ed-nav #ed-pageno{font-size:13.5px;color:var(--text);font-weight:600}
.ed-zoom{font-size:12px;max-width:132px}
.ed-zoom select{padding:7px 9px;font-size:13px}
.ed-nav .hint{margin-left:auto}

@media (max-width:700px){
  .ed-bar .ed-t{flex:1 1 calc(50% - 4px);min-width:0}
  .ed-bar{gap:6px}
  .ed-stage{padding:8px;max-height:62vh}
  .ed-h{width:20px;height:20px;right:-10px;bottom:-10px}
  .ed-nav .hint{margin-left:0;flex:1 1 100%}
  .ed-row{flex-wrap:wrap}
}

/* =====================================================================
   Finishing pass
   The things that are felt rather than noticed: a scrollbar that belongs to
   the theme, anchors that do not hide under the sticky header, and the light
   theme's own shadows (a shadow tuned for a near-black page looks like dirt
   on a white one).
   ===================================================================== */

/* The sticky header is 58px tall, so a jump link would land underneath it. */
[id]{scroll-margin-top:82px}

/* Reading measure on the text pages.
   A guide was setting ~118 characters to the line in a 900px column — well past
   the 45–75 the eye tracks comfortably, and the reason a long paragraph felt
   like work. Narrowing only the paragraphs fixed the reading but left the
   headings and boxes at full width, which made the narrow text look like a
   mistake rather than a choice; so everything on a text page takes the one
   measure and the column reads as deliberate. A card grid that kept the full
   column was the one element still overshooting — every edge on the page now
   lands on the same line. Tables are the exception: they genuinely need the
   room, and a table that scrolls sideways inside its own box is normal. */
/* ...and the column is centred rather than pinned to the left of a wider one.
   Pinned left, a 588px column inside a 900px box reads as text that stopped
   two-thirds of the way across, because the gap all lands on one side. Centred,
   the same column reads as a measure that was chosen. */
.prose{max-width:calc(var(--measure) + 40px);margin-inline:auto}
.prose > *:not(.tablewrap){max-width:var(--measure)}
/* Two tidy columns rather than three cramped ones, at this measure. */
.prose > .grid{grid-template-columns:repeat(auto-fill,minmax(240px,1fr))}

/* The browser draws the scrollbar itself, so it is the one piece of chrome
   left at the default — and the most obvious tell on an otherwise finished
   dark page. */
*{scrollbar-width:thin;scrollbar-color:var(--line-2) transparent}
::-webkit-scrollbar{width:11px;height:11px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--line-2);border-radius:99px;
  border:3px solid transparent;background-clip:content-box}
::-webkit-scrollbar-thumb:hover{background:var(--faint);background-clip:content-box}

/* A coloured left edge says "this worked" or "this failed" before the sentence
   is read. */
.msg{border-radius:11px;line-height:1.6;border-left:4px solid transparent}
.msg.err{border-left-color:var(--bad-line)}
.msg.ok{border-left-color:var(--ok-line)}
.msg.info{border-left-color:var(--line-2)}

[data-theme="light"] .mock{box-shadow:var(--shadow),inset 0 1px 0 rgba(255,255,255,.7)}
[data-theme="light"] button{box-shadow:0 3px 10px rgba(29,78,216,.22),inset 0 1px 0 rgba(255,255,255,.22)}
[data-theme="light"] button.ghost{box-shadow:none}
[data-theme="light"] a.btn{box-shadow:0 5px 16px rgba(29,78,216,.24),inset 0 1px 0 rgba(255,255,255,.22)}
[data-theme="light"] a.btn.ghost{box-shadow:none}
[data-theme="light"] a.button{box-shadow:0 3px 10px rgba(29,78,216,.22),inset 0 1px 0 rgba(255,255,255,.22)}
[data-theme="light"] .ad{border-color:var(--line-2)}

/* =====================================================================
   The two hero buttons on a phone
   ===================================================================== */

/* Side by side instead of stacked. "Browse the tools" and "How is that
   possible?" at 15px with 24px of side padding need 378px together; a phone
   offers 350, so they used to fall onto separate rows. Two equal columns and a
   smaller size fit them, and the pair reads as one choice rather than as two
   unrelated actions. */
@media (max-width:560px){
  .hero .cta{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:10px}
  .hero .cta .btn{font-size:13px;padding:12px 8px;text-align:center}
  /* The demo panel's rows are fixed-width log lines, so they cannot reflow. At
     a phone's width the longest rows pushed "done" onto a second line, which
     made a log look like a paragraph. Drop the reading direction instead and
     keep the file, its size and its result — the three things that carry the
     meaning — on one line each. */
  .mock .rdesc{display:none}
  /* The log lines must never wrap: a wrapped line stops being a log line. The
     global .row rule used to allow it; this name does not, and nowrap makes the
     intent explicit rather than relying on the text being short enough. */
  .mock .mline{flex-wrap:nowrap}
}

/* Narrower than this, the longer label genuinely cannot sit beside the shorter
   one without shrinking past legibility, so they stack again. */
@media (max-width:340px){
  .hero .cta{grid-template-columns:minmax(0,1fr)}
}

/* A note above the drop zone, on the tools whose input decides whether they can
   work at all. It is not an error, so it does not wear the error colours: a
   calm panel with a rule down the left edge, and the two cases set as labelled
   lines rather than a paragraph, because it has one job -- to be read at a
   glance by someone who is about to hand it a photograph of handwriting.
   Every colour here is a variable, so the light theme needs no override. */
.notice{
  border:1px solid var(--line);
  border-left:3px solid var(--accent);
  border-radius:10px;
  background:var(--inset-2);
  padding:14px 16px;
  margin:0 0 18px;
}
.notice p{margin:0;}
.notice p.notice-lead{font-weight:700;color:var(--head);font-size:14.5px;line-height:1.5;}
.notice p.notice-yes,
.notice p.notice-no{margin-top:9px;font-size:13px;line-height:1.5;color:var(--muted);}
.notice p.notice-yes span,
.notice p.notice-no span{
  display:block;font-size:11px;font-weight:600;letter-spacing:.07em;
  text-transform:uppercase;color:var(--faint);margin-bottom:1px;
}
@media (max-width:560px){
  .notice{padding:12px 13px;}
  .notice p.notice-lead{font-size:14.5px;}
  .notice p.notice-yes,
  .notice p.notice-no{font-size:13.5px;}
}
