/* style.css: the landing site. Concept LEDGER: rows of Inter on black, hairlines the only decoration.
   Tokens in :root; shared components in components.css; fonts in fonts.css. No colour, ever. */

:root{
  --bg:#000000;
  --ink:#ffffff;
  --ink-2:rgba(255,255,255,.6);
  --ink-tab:rgba(255,255,255,.5);
  --ink-off:rgba(255,255,255,.3);
  --rule:rgba(255,255,255,.18);
  --rule-2:rgba(255,255,255,.35);
  --font:Inter,-apple-system,"Helvetica Neue",Arial,sans-serif;
  --s1:.25rem;--s2:.5rem;--s3:.75rem;--s4:1rem;--s5:1.5rem;--s6:2rem;--s7:3rem;--s8:4rem;--s9:6rem;--s10:8rem;
  --display:1.75rem;
  --h2:1.5rem;
  --body:1rem;
  --label:.6875rem;
  --num:2.5rem;
  --gutter:1.5rem;
  --section:4rem;
  --max:2000px;
}
/* THE SCALING RULE (Danny, 5 Sep 2026): text must never re-wrap as the window changes size; the whole
   page scales like an image. The root font size is a share of the viewport width and every size in
   these two stylesheets is in rem (16px = 1rem at the reference width), so widening the window enlarges
   the picture and nothing moves to another line. Three reference widths, one per layout:
   phones 390 (16px root at 390 wide), tablets 600 (16px at 600), desktop 1440 (16px at 1440, 10px at
   900, 21.3px at 1920). Capped at 2000 wide, where .wrap stops growing and sits centred.
   Only hairlines (1px, 2px), the 2000px cap and the breakpoints themselves stay in px.
   Two deliberate exceptions, neither of which moves a line or a width:
   - The desktop manifesto (.ln) is the one block that also answers to the window's height: its size
     is min(width share, 4.7vh) so the eleven lines sit above the fold in a short window. The rem
     share is the ceiling, so it never re-wraps; it only stops growing with the width when the
     window is short. check_scale.py's viewport case checks the fit and the single lines.
   - Every control's hit area is floored at 44px (the brief's minimum) by a transparent
     pseudo-element, so the picture and its alignment do not change; only the clickable area
     stops shrinking.
   There is no floor on any type size (5 Sep review): a px floor on the labels made the pricing
   sub-label and the footer links break at different words across the phone band and widened the
   nav, the buttons and the footer between 900 and 1173, which is exactly what the rule forbids.
   Below the reference widths the small type is simply smaller (labels 10.2px at 360, 7.5px at 900),
   the same as the rest of the picture. check_scale.py's sweep fails on any rem-width drift.
   Known cost of the rule: between breakpoints browser zoom changes the CSS viewport and the root
   with it, so zoom does not enlarge the text (the picture is the same at every zoom). */
html{font-size:calc(100vw / 390 * 16)}
@media (min-width:600px){
  html{font-size:calc(100vw / 600 * 16)}
  :root{--label:.75rem}
}
@media (min-width:900px){
  html{font-size:calc(100vw / 1440 * 16)}
  :root{--h2:2.16rem;--body:1.0625rem;--num:3.6rem;--gutter:4.5rem;--section:8.1rem;--display:4.32rem}
}
@media (min-width:2000px){
  html{font-size:calc(2000 / 1440 * 16px)}
}

*,*::before,*::after{box-sizing:border-box}
html{background:var(--bg);color:var(--ink);font-family:var(--font);font-weight:400;line-height:1.55;font-synthesis:none;-webkit-text-size-adjust:100%;text-size-adjust:100%;scroll-padding-top:4.5rem;overscroll-behavior:none}
/* No rubber band at either end of the page (Danny, 3 Oct 2026, as on El Fin del Cielo: "smooth yet strong and
   locked"). On html and body both, since browsers differ on which one they read it from. */
body{margin:0;min-height:100%;background:var(--bg);color:var(--ink);font-size:var(--body);overflow-x:hidden;overscroll-behavior:none}
/* House rule (Danny, 5 Sep 2026): text on our websites is not highlightable. Form fields stay selectable. */
html{-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}
input,textarea,select{-webkit-user-select:text;user-select:text}
img,svg{max-width:100%}
a{color:inherit}
p{margin:0}
h1,h2,h3{margin:0;font-weight:300}
b,strong{font-weight:500}
.lnk{text-decoration:underline;text-decoration-thickness:1px;text-decoration-color:var(--rule-2);text-underline-offset:.2em}
.lnk:hover{text-decoration-color:var(--ink)}
.wrap{max-width:var(--max);margin:0 auto;padding-left:var(--gutter);padding-right:var(--gutter)}
.skip{display:block;position:absolute;top:-12.5rem;inset-inline-start:var(--gutter);z-index:20;padding:.75rem 1rem;background:var(--bg);border:1px solid var(--ink);font-size:var(--label);line-height:calc(var(--label) * 1.3);font-weight:500;letter-spacing:.12em;text-transform:uppercase;text-decoration:none}
.skip:focus{top:.5rem}

/* Masthead. Phone: a 3.5rem sticky bar (wordmark, globe, currency, Sign in) and a 2.75rem row of section links that scrolls away.
   Desktop: one sticky 4rem row. The header is display:contents so the bar can stick against the page.
   The phone bar was already full in most languages before the currency button came, so on phones the
   globe and the currency are icons and the bar's gap is .5rem (see the currency layer below). */
.mh{display:contents}
.mh-bar{position:sticky;top:0;z-index:10;display:flex;align-items:center;gap:var(--s2);height:3.5rem;padding:0 var(--gutter);background:var(--bg);border-bottom:1px solid var(--rule)}
@media (min-width:600px){.mh-bar{gap:var(--s4)}}
.mh .wm{font-size:1.25rem;margin-inline-end:auto}
.mh-nav{display:flex;align-items:center;gap:var(--s5);height:2.75rem;padding:0 var(--gutter);border-bottom:1px solid var(--rule)}
.mh-nav a{font-size:var(--label);line-height:1;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-2);text-decoration:none;display:inline-flex;align-items:center;min-height:2.75rem}
.mh-nav a:hover,.mh-nav a:focus-visible{color:var(--ink);text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:.35em;text-decoration-color:var(--rule-2)}
@media (min-width:900px){
  .mh{display:flex;align-items:center;position:sticky;top:0;z-index:10;height:4rem;padding:0 max(var(--gutter),calc((100% - var(--max)) / 2 + var(--gutter)));background:var(--bg);border-bottom:1px solid var(--rule)}
  .mh-bar{display:contents}
  .mh .wm{order:0;margin-inline-end:0}
  .mh-nav{order:1;margin-inline-start:auto;margin-inline-end:var(--s6);height:auto;padding:0;border:0;gap:var(--s6)}
  .langbtn{order:2;margin-inline-end:var(--s4)}
  .mh-cta{order:3}
}

/* Sections */
.sec{padding:var(--section) 0}
.sec > .wrap > .mark,.sec-head .mark{margin-bottom:var(--s5)}
.sec-head{margin:0 0 var(--s7)}
.h2{font-size:var(--h2);font-weight:300;line-height:1.2}

/* Manifesto: the hero is the text */
.mf{padding:3rem 0 var(--section)}
.cats{display:flex;flex-direction:column;gap:var(--s2);margin:0 0 var(--s7)}
.cats h1,.cats p{font-size:var(--label);font-weight:500;line-height:calc(var(--label) * 1.3);letter-spacing:.12em;text-transform:uppercase;color:var(--ink-2)}
.cats h1{color:var(--ink)}
.cats h1 b{font-weight:500;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:.25rem;text-decoration-color:var(--rule-2)}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.ln{font-size:calc(var(--display) * var(--hero-fit,1));font-weight:300;line-height:1.15;margin:0 0 .35em}
.ln .wm{font-size:inherit;font-weight:300}
.ln.we{margin-top:.6em;font-size:calc(var(--display) * 1.4 * var(--hero-fit,1))}
/* Phones: every manifesto line on one line (Danny, 4 October 2026: "some of the lines turning into 2
   lines"). The longest English line, "We deploy your SEO strategy", is 380 of 342 wide at the old size;
   at 1.55rem it is 337, and being rem it holds at every phone width. Phones also take a little less
   air between sections. */
@media (max-width:599.98px){
  :root{--section:3rem}
  .sec-head{margin-bottom:var(--s6)}
  .ln{font-size:calc(1.55rem * var(--hero-fit,1))}
  .ln.we{font-size:calc(1.55rem * 1.4 * var(--hero-fit,1))}
}
/* Desktop: the whole hero fits one screen. The line size is the smaller of the width share (the rem
   value, which scales with the page) and a height share, so eleven lines plus the three category
   lines always land above the fold. The width share is the ceiling, so a line never re-wraps. */
@media (min-width:900px){
  :root{--hero:min(4.14rem,4.7vh)}
  .mf{min-height:calc(100vh - 4rem);padding:var(--s6) 0 var(--s7);display:flex}
  .mf > .wrap{display:flex;flex-direction:column;width:100%}
  .cats{gap:var(--s1);margin:0 0 var(--s6)}
  .mf > .wrap > .mark{display:none}
  .mf-lines{margin:auto 0}
  .ln{font-size:calc(var(--hero) * var(--hero-fit,1));letter-spacing:-.01em;line-height:1.1;margin-bottom:.15em}
  .ln.we{margin-top:.5em;font-size:calc(var(--hero) * 1.5 * var(--hero-fit,1))}
}

/* What we do */
.wd-row{border-top:1px solid var(--rule);padding:var(--s5) 0;display:grid;gap:var(--s3)}
.wd-row:last-child{border-bottom:1px solid var(--rule)}
.wd-row h3{font-size:var(--h2);font-weight:300;line-height:1.2}
.wd-row p{color:var(--ink-2);max-width:34em}
/* Desktop: the four services sit side by side, a hairline between each (Danny, 3 Oct 2026: three
   vertical lines). The lines are borders, not a 1px gap over a hairline background: the scaled root
   puts the columns on fractional pixels, and Chrome rounded two of the three gaps away. */
@media (min-width:900px){
  .wd-grid{display:grid;grid-template-columns:repeat(4,1fr);border-top:1px solid var(--rule);border-bottom:1px solid var(--rule)}
  .wd-row{border:0;padding:var(--s6) var(--s6) var(--s7);align-content:start;gap:var(--s4)}
  .wd-row + .wd-row{border-inline-start:1px solid var(--rule)}
  .wd-row:first-child{padding-inline-start:0}
  .wd-row:last-child{border-bottom:0;padding-inline-end:0}
  .wd-row p{max-width:none}
}

/* How it works */
.hw ol{list-style:none;margin:0;padding:0}
.hw li{border-top:1px solid var(--rule);padding:var(--s5) 0;display:grid;gap:var(--s3)}
.hw li:last-child{border-bottom:1px solid var(--rule)}
.hw .num{font-size:var(--num);font-weight:300;line-height:1;font-variant-numeric:tabular-nums}
.hw h3{font-size:var(--h2);font-weight:300;line-height:1.2}
.hw p{color:var(--ink-2);max-width:30em}
/* direction and isolate: a price after right-to-left text has its digits read as Arabic numbers,
   which leaves the pound sign neutral and throws it to the right of the figure (199£). Isolating
   the amount keeps it £199 in every language. Bare prices in the rtl translation files carry the
   same isolate as U+2066 and U+2069, since they have no element to hang this on. */
.fig{font-weight:400;color:var(--ink);font-variant-numeric:tabular-nums;direction:ltr;unicode-bidi:isolate}
@media (min-width:900px){
  .hw ol{display:grid;grid-template-columns:repeat(3,1fr);border-top:1px solid var(--rule);border-bottom:1px solid var(--rule)}
  .hw li{border:0;padding:var(--s7) var(--s6);align-content:start}
  .hw li + li{border-inline-start:1px solid var(--rule)}
  .hw li:first-child{padding-inline-start:0}
  .hw li:last-child{padding-inline-end:0;border-bottom:0}
}

/* The app */
.ap-grid{display:grid;gap:var(--s7);justify-items:center}
.ap-text{width:100%;max-width:34em;display:grid;gap:var(--s5);align-content:start}
.ap-text p{color:var(--ink-2)}
.ap-text .label{margin-bottom:var(--s2)}
.ap-line{color:var(--ink);font-size:var(--h2);font-weight:300;line-height:1.2;margin-top:var(--s3)}
/* Phones and tablets: the words sit right under the phone and turn with it (Danny, 4 October 2026: "the
   text where the app visualisation is isnt close to it"). One caption a screenshot, stacked in one
   grid cell so the height never jumps, each fading in with its screen. main.js marks the grid
   .ap-live only once the phone is actually turning; until then, with scripts off or for anyone who
   asks for reduced motion, the three written descriptions show as before and the captions stay out. */
.ap-caps{display:none}
@media (max-width:899.98px){
  .ap-grid{gap:var(--s6)}
  .ap-live .ap-caps{display:grid;width:100%;max-width:21rem;margin-top:calc(var(--s6) * -.5);text-align:center}
  .ap-caps .cap{grid-area:1 / 1;opacity:0;transition:opacity .35s ease}
  .ap-caps .cap.on{opacity:1}
  .ap-caps .label{margin-bottom:var(--s2);color:var(--ink)}
  .ap-caps p:not(.label){color:var(--ink-2)}
  .ap-live .ap-text > div{display:none}
  .ap-live .ap-text .ap-line{margin-top:0;padding-top:var(--s5);border-top:1px solid var(--rule)}
}
/* Desktop: phone on the left, the three tab descriptions as three ruled columns on the right,
   the closing line spanning all three. */
@media (min-width:900px){
  .ap-grid{grid-template-columns:minmax(18.75rem,1fr) 2fr;gap:var(--s9);align-items:start;justify-items:start}
  .ap-text{max-width:none;padding-top:0;grid-template-columns:repeat(3,1fr);gap:0;border-top:1px solid var(--rule)}
  .ap-text > div{padding:var(--s5);padding-inline-start:0}
  .ap-text > div + div{border-inline-start:1px solid var(--rule);padding-inline-start:var(--s5)}
  /* SITE's paragraph was a pixel short of "uptime and every deployment," on its second line, which left
     "deployment" leading the third (Danny, 25 September 2026: "it looks weird"). 8px more room for
     that column alone, before its divider; the divider itself does not move. */
  .ap-text > div:first-child{padding-inline-end:var(--s4)}
  .ap-text .ap-line{grid-column:1 / -1;border-top:1px solid var(--rule);padding-top:var(--s5);margin-top:0;font-size:var(--display);line-height:1.1}
}

/* Phone frame: a real screenshot of the app, a client's SITE tab on Backend with the site renamed
   Jane's Bakery so it reads as an example (scripts/shoot_app.py), taken in Danny's own app window
   (640 by 1283 CSS px at 2x, 24 September 2026). It replaced a hand-built imitation that had drifted
   from the app; retake it when the app changes. The frame is a phone (Danny: "curve the edges and
   the corners so it feels more like a mobile phone shape"): rounded corners, a black status strip
   with a hairline pill for the notch, so the corners never clip the app's own header, and a hairline
   home bar under the tabs. */
.phone{position:relative;width:16.25rem;padding-top:1.5rem;border:1px solid var(--rule-2);border-radius:2.25rem;background:var(--bg);overflow:hidden}
.phone::before{content:"";position:absolute;top:.5rem;left:50%;width:3.25rem;height:.5rem;margin-left:-1.625rem;border:1px solid var(--rule-2);border-radius:1rem}
.phone::after{content:"";position:absolute;bottom:.3125rem;left:50%;width:4.5rem;margin-left:-2.25rem;border-top:1px solid var(--rule-2)}
.phone .screen{position:relative}
/* One real screenshot per tab (Danny, 25 September 2026: "have one for each of the tabs at the
   bottom"), stacked: the first in flow gives the size, the others sit over it and fade in by
   opacity alone as main.js turns through them. */
.phone .shot{display:block;width:100%;height:auto;transition:opacity .35s ease}
.phone .shot + .shot{position:absolute;top:0;left:0;opacity:0}
.phone .shot.on{opacity:1}
@media (min-width:900px){.phone{width:21.25rem;padding-top:1.75rem;border-radius:2.75rem}.phone::before{top:.625rem}}

/* Pricing */
.pr-setup{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--s2) var(--s5);margin:0 0 var(--s7)}
.pr-setup .num{font-size:var(--display);font-weight:300;line-height:1;font-variant-numeric:tabular-nums}
.pr-setup .h2{color:var(--ink-2)}
.pr-sub{margin:0 0 var(--s4)}
.tiers{list-style:none;margin:0;padding:0}
.tier{border-top:1px solid var(--rule);padding:var(--s5) 0 var(--s6);display:flex;flex-direction:column;gap:var(--s4)}
.tier:last-child{border-bottom:1px solid var(--rule)}
.tier .price{display:flex;flex-direction:column;gap:.375rem}
.tier .num{font-size:var(--num);font-weight:300;line-height:1;font-variant-numeric:tabular-nums}
/* Every line of a plan on one line (Danny, 24 September 2026: "reduce the sizes of this text so that
   they all fit on one line"): the longest English line needs 13.2px in a column at 1440, so
   12.5px, and rem like everything else so it holds at every width. */
.inc{list-style:none;margin:0;padding:0;color:var(--ink-2);font-size:.78125rem;line-height:1.5}
@media (min-width:900px){.pr-setup{display:grid;grid-template-columns:auto 1fr;align-items:baseline;gap:var(--s6);padding:var(--s5) 0;border-top:1px solid var(--rule)}}
.inc li{border-top:1px solid var(--rule);padding:.625rem 0}
.tier .btn{margin-top:auto}
@media (min-width:900px){
  .tiers{display:grid;grid-template-columns:repeat(3,1fr);border-top:1px solid var(--rule);border-bottom:1px solid var(--rule)}
  /* Four equal cells: same padding on every side, price block a fixed height so the lists and the
     buttons line up, buttons pinned to the bottom. */
  .tier{border:0;padding:var(--s7) var(--s6)}
  .tier + .tier{border-inline-start:1px solid var(--rule)}
  .tier:last-child{border-bottom:0}
  .tier .price{min-height:calc(var(--num) + 1.75rem);justify-content:flex-start}
}
.pr-note{margin:var(--s7) 0 0;color:var(--ink-2);max-width:34em}
/* On phones the second line of this note ("... under one account, each") measures exactly the
   wrap's width, so Blink's 1/64px advance rounding decided the break (8/9/3 words at 392, 8/8/4
   at 400). A quarter-rem inside the box gives the break one home at every width; the 34em cap
   still binds from 600 up, so nothing else moves. */
.pr-note.pr-multi{max-width:min(34em,calc(100% - .25rem))}

/* Control */
.ctl{border-top:1px solid var(--rule-2);border-bottom:1px solid var(--rule-2);padding:var(--s8) 0}
.ctl .h2{max-width:20em}
.ctl-rows{margin:var(--s7) 0 0;max-width:40rem}
/* Desktop: the statement on the left, the ledger of what is yours on the right. */
@media (min-width:900px){
  .ctl{padding:var(--s9) 0}
  .ctl .wrap{display:grid;grid-template-columns:1fr 1fr;column-gap:var(--s9);align-items:start}
  .ctl .mark{grid-column:1 / -1}
  .ctl .h2{grid-column:1;grid-row:2;max-width:none;font-size:var(--display);line-height:1.1}
  .ctl-rows{grid-column:2;grid-row:2;margin:0;max-width:none}
}
.ctl-rows .row .v{color:var(--ink-2)}
.ctl-rows .row .v.on{color:var(--ink)}

/* Contact */
.ct .cta{display:inline-block;font-size:var(--h2);font-weight:300;line-height:1.2;text-decoration:underline;text-decoration-thickness:1px;text-decoration-color:var(--rule-2);text-underline-offset:.15em}
.ct .cta:hover{text-decoration-color:var(--ink)}
.ct-actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s5);margin:var(--s6) 0 0}
.ct .tcg{font-size:.8125rem;color:var(--ink-2)}

/* Tap targets (brief: 44px). The boxes scale with the page, so 2.75rem is 44px only at the reference
   width. A transparent pseudo-element extends every control's hit area to at least 44px each way
   without moving anything: the layout and the picture are untouched, only the clickable area grows.
   The two inline Contact links (the email, The Cairbon Group) and the inline .lnk links in the
   document pages and the 404 get the same treatment. The skip link is not in this list: it is
   already positioned (absolute, line 69) and putting it here made it an in-flow full-width box,
   a black strip above the masthead and a gutter of overflow. */
.btn,.mh-nav a,.ft-links a,a.wm,.ct .tcg .lnk,.ct .cta,.ft .ft-mail,.doc .lnk,.nf .lnk,.langbtn,.curbtn,.su .lnk,.su .edit,.su .choice,.su summary,.su .check{position:relative}
.btn::after,.mh-nav a::after,.ft-links a::after,a.wm::after,.skip::after,.ct .tcg .lnk::after,.ct .cta::after,.ft .ft-mail::after,.doc .lnk::after,.nf .lnk::after,.langbtn::after,.curbtn::after,.su .lnk::after,.su .edit::after,.su .choice::after,.su summary::after,.su .check::after{content:"";position:absolute;left:50%;top:50%;width:max(100%,44px);height:max(100%,44px);transform:translate(-50%,-50%)}
@media (min-width:900px){
  .ct .wrap{display:grid;grid-template-columns:1fr auto;align-items:end;column-gap:var(--s9)}
  .ct .sec-head{grid-column:1 / -1}
  .ct .cta{font-size:var(--display);line-height:1.1}
  .ct-actions{margin:0;justify-self:end;flex-direction:column;align-items:flex-end;gap:var(--s4)}
}

/* Footer */
.ft{border-top:1px solid var(--rule);padding:var(--s7) 0 var(--s8)}
.ft .wm{font-size:.9375rem;font-weight:400}
.ft .co{white-space:pre-line;font-size:.8125rem;line-height:1.6;color:var(--ink-2);margin:var(--s5) 0}
/* The email sits under the company lines at body size, a link like every other on the site. */
.ft .ft-mail{display:inline-block;margin-top:var(--s3);font-size:var(--body);line-height:1.4;color:var(--ink);text-decoration:underline;text-decoration-thickness:1px;text-decoration-color:var(--rule-2);text-underline-offset:.2em}
.ft .ft-mail:hover,.ft .ft-mail:focus-visible{text-decoration-color:var(--ink)}
.ft-links{display:flex;flex-wrap:wrap;gap:var(--s5);align-items:center}
.ft-links a,.ft-links span{font-size:var(--label);line-height:1;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-2);text-decoration:none;display:inline-flex;align-items:center;min-height:2.75rem}
.ft-links a:hover,.ft-links a:focus-visible{color:var(--ink)}
@media (min-width:900px){
  .ft .wrap{display:grid;grid-template-columns:auto 1fr auto;align-items:start;column-gap:var(--s9)}
  .ft .co{margin:0}
  .ft-links{justify-self:end}
}

/* Documents (privacy, terms, 404) */
.doc{padding-top:var(--s7);padding-bottom:var(--s9)}
.doc > *{max-width:45rem}
/* .slack: a paragraph or list item whose line end measured exactly its container's width, so
   Blink's 1/64px advance rounding decided the break at some widths and not others (the sweep in
   check_scale.py names them). A quarter-rem inside the box gives the break one home everywhere.
   Add the class only to an element the sweep flags; it changes nothing else. If the new break
   lands on the edge too, .slack-2 is a measured inset: .8rem leaves every line end of the
   privacy visitor-data paragraph at least .3rem from the edge at every width (see the sweep). */
.doc .slack{max-width:calc(min(45rem,100%) - var(--slack,.25rem))}
.doc .slack-2{--slack:.8rem}
.doc h1{font-size:var(--display);font-weight:300;line-height:1.15;margin:0 0 var(--s3)}
.doc .meta{margin:0 0 var(--s7)}
.doc h2{font-size:1.375rem;font-weight:300;line-height:1.25;margin:var(--s7) 0 var(--s3);padding-top:var(--s5);border-top:1px solid var(--rule)}
.doc p,.doc li{color:var(--ink-2);margin:0 0 var(--s4)}
.doc ul{margin:0 0 var(--s4);padding-inline-start:1.2em}
.doc li{margin-bottom:var(--s2)}
.doc b{color:var(--ink);font-weight:400}
.doc .lead{color:var(--ink);font-size:1.125rem;margin-bottom:var(--s5)}
.doc dl{margin:0 0 var(--s4)}
.doc dt{color:var(--ink);font-weight:400}
.doc dd{margin:0 0 var(--s3);color:var(--ink-2)}
.doc table{width:100%;border-collapse:collapse;font-size:.9375rem;margin:0 0 var(--s5)}
.doc th,.doc td{text-align:start;padding:.625rem 0;padding-inline-end:.75rem;border-top:1px solid var(--rule);vertical-align:top;color:var(--ink-2);font-weight:400}
.doc th{color:var(--ink)}
.doc tr:last-child td{border-bottom:1px solid var(--rule)}
.doc .tbl{overflow-x:auto}
.nf{min-height:60vh;display:flex;flex-direction:column;justify-content:center;gap:var(--s5)}
.nf .num{font-size:var(--num);font-weight:300;line-height:1;font-variant-numeric:tabular-nums;color:var(--ink)}

/* ---- Sign up (signup.html, js/signup.js) ----
   The four steps and "Account created." on one page, in the document pages' column (45rem, at the
   start of .wrap). Danny, 3 Oct 2026: "make these areas in boxes": every field of the journey is a
   hairline box (text, email, password, textarea, file) and so is every choice, scoped to .su so the
   welcome keeps its underline. Fields are 1.0625rem, the welcome's no-zoom size on an iPhone. One
   section shows at a time; the rest, the error lines and the prototypes carry [hidden], which the
   display of .err, .btn and .row would otherwise override (.su [hidden] outranks each of them; no
   rule of this block that sets a display is ever on a hidden element). All sizes rem: THE SCALING
   RULE. A box's 1px hairline comes out of its padding (calc(.6875rem - 1px)), so every box is an
   exact rem size: step 1 stacks some thirty of them, and hairlines left on top of rem paddings
   drifted the form 14px across the desktop band and moved a choice's line break on phones. */
.su{padding-top:var(--s7);padding-bottom:var(--s9)}
.su > *,.su-step > *{max-width:45rem}
.su [hidden]{display:none}
.su h1{font-size:var(--display);font-weight:300;line-height:1.15;margin:0 0 var(--s3)}
/* the heading and the message slot take focus from the script only, to be read out, not operated */
.su h1:focus,.su-msg:focus{outline:none}
.su-step > .label:first-child{margin:0 0 var(--s3)}
.su .lead{color:var(--ink-2);margin:0 0 var(--s6)}
.su-status,.su-note{color:var(--ink-2);margin:0 0 var(--s5)}
.su-retry{margin:0 0 var(--s5)}
.su-msg{margin:0 0 var(--s5);padding:calc(.75rem - 1px) calc(.875rem - 1px);border:1px solid var(--ink);color:var(--ink);font-size:.9375rem;line-height:1.5}
/* Fields */
.su .field{margin:0 0 var(--s5)}
.su fieldset{min-width:0;margin:0 0 var(--s5);padding:0;border:0}
.su legend{padding:0}
.su .field > .label,.su legend.label{display:block;margin:0 0 .5rem}
.su fieldset:focus-within > legend{color:var(--ink)}
.su .input{min-height:2.75rem;padding:calc(.6875rem - 1px) calc(.875rem - 1px);border:1px solid var(--rule-2);border-radius:0;background:transparent;font-size:1.0625rem;line-height:1.4}
.su .input:hover,.su .input:focus,.su .has-err .input{border-color:var(--ink)}
.su textarea.input{resize:vertical}
.su input[type=file].input{padding:calc(.4375rem - 1px);color:var(--ink-2);font-size:.875rem;cursor:pointer}
.su .input::file-selector-button{margin:0;margin-inline-end:.75rem;padding:calc(.5rem - 1px) calc(.875rem - 1px);border:1px solid var(--ink);border-radius:0;background:transparent;color:var(--ink);font-family:var(--font);font-size:var(--label);font-weight:500;line-height:1;letter-spacing:.12em;text-transform:uppercase;cursor:pointer}
.su .hint{margin:.5rem 0 0;color:var(--ink-2);font-size:.8125rem;line-height:1.5}
/* an error line that runs to two lines keeps its dot on the first */
.su .err{align-items:flex-start;line-height:1.5}
.su .err .dot{margin-top:.359375rem}
/* Choices: a hairline box each, the radio a 1rem hairline circle filled when chosen; the name and
   the price on the first line, the note under them. The radio's column carries the space after it
   and the price its own, so a choice without a price gives its whole line to the words. */
.su .choice{position:relative;display:grid;grid-template-columns:1.75rem 1fr auto;row-gap:.125rem;align-items:start;min-height:2.75rem;padding:calc(.6875rem - 1px) calc(.875rem - 1px);border:1px solid var(--rule-2);cursor:pointer}
.su .choice + .choice{margin-top:.5rem}
.su .choice input{grid-row:1;grid-column:1;width:1rem;height:1rem;margin:.203125rem 0 0;border:1px solid var(--ink);border-radius:50%;background:transparent;cursor:pointer;-webkit-appearance:none;appearance:none}
.su .choice input:checked{background:var(--ink)}
.su .choice-name{grid-row:1;grid-column:2;align-self:baseline;color:var(--ink);font-size:.9375rem;line-height:1.5}
.su .choice-price{grid-row:1;grid-column:3;align-self:baseline;padding-inline-start:.75rem;color:var(--ink-2);font-size:.875rem;line-height:1.5;white-space:nowrap}
.su .choice-note{grid-row:2;grid-column:2 / -1;color:var(--ink-2);font-size:.875rem;line-height:1.5}
.su .choice:hover,.su .choice:focus-within{border-color:var(--ink)}
.su .choice:has(input:checked){border-color:var(--ink)}
.su-choices > .err{margin-top:.75rem}
.su-pair{display:grid;column-gap:var(--s5)}
@media (min-width:600px){.su-pair{grid-template-columns:1fr 1fr}}
/* Stored files, each with its Remove box */
.su-files{margin:.75rem 0 0;border-bottom:1px solid var(--rule)}
.su .su-file-name{overflow-wrap:anywhere}
.su .check{gap:.5rem;color:var(--ink-2);font-size:.8125rem;cursor:pointer}
.su .su-file:has(input:checked) .su-file-name{color:var(--ink-2);text-decoration:line-through;text-decoration-thickness:1px}
/* Advanced: direct references, a disclosure between two hairlines, its marker a hairline chevron */
.su-adv{margin:0 0 var(--s5);border-top:1px solid var(--rule);border-bottom:1px solid var(--rule)}
.su-adv[open]{padding-bottom:var(--s5)}
.su summary{position:relative;display:flex;align-items:center;gap:.75rem;min-height:2.75rem;color:var(--ink);font-size:var(--label);font-weight:500;line-height:calc(var(--label) * 1.3);letter-spacing:.12em;text-transform:uppercase;cursor:pointer;list-style:none}
.su summary::-webkit-details-marker{display:none}
.su summary::before{content:"";flex:none;width:.4375rem;height:.4375rem;border-right:1px solid currentColor;border-bottom:1px solid currentColor;transform:rotate(-45deg)}
.su-adv[open] > summary::before{transform:rotate(45deg)}
.su-adv > .hint{margin:0 0 var(--s4)}
.su-ref{padding-top:var(--s5);border-top:1px solid var(--rule)}
.su-ref .su-ref-n{margin:0 0 var(--s4)}
/* Check it over, Payment, Account created: the app's ledger rows. A value a customer typed can be one
   long word (a link, an address): it breaks anywhere rather than widen the page. */
.su-head{display:flex;align-items:center;justify-content:space-between;gap:var(--s4);margin:var(--s6) 0 var(--s2)}
.su .edit{position:relative;display:inline-flex;align-items:center;min-height:2.75rem;color:var(--ink);font-size:var(--label);font-weight:500;line-height:1;letter-spacing:.12em;text-transform:uppercase;text-decoration:underline;text-decoration-thickness:1px;text-decoration-color:var(--rule-2);text-underline-offset:.35em}
.su .edit:hover{text-decoration-color:var(--ink)}
.su .row .k{overflow-wrap:anywhere}
.su .row .v{min-width:0;text-align:end;overflow-wrap:anywhere}
.su .row .sub{grid-column:1 / -1;color:var(--ink-2);white-space:pre-line;overflow-wrap:anywhere}
.su-actions{display:flex;flex-wrap:wrap;gap:var(--s4);margin:var(--s6) 0 0}
.su form > .hint:last-child,#su-check-next{margin-top:var(--s4)}
.su-alt{margin:var(--s5) 0 0;color:var(--ink-2);font-size:.9375rem}
.su-process{margin:var(--s5) 0 0;color:var(--ink-2);font-size:.9375rem}
.su-progress{margin:var(--s4) 0 0;color:var(--ink);font-size:.875rem}
.su-progress:empty{margin:0}
.su-pay-note{margin:var(--s5) 0 0;color:var(--ink-2);font-size:.9375rem}
.su-pay-mode{margin-top:var(--s6)}
.su-pay-mode > .su-actions{margin-top:0}
.su-pay-mode > .label + .su-actions{margin-top:var(--s3)}
.su-pay-mode > .hint{margin-top:var(--s4)}
.su-sub{margin:var(--s7) 0 var(--s3)}
.su-p{margin:0 0 var(--s3)}
.su-quiet{color:var(--ink-2)}

/* Motion: opacity only. The inline head script adds html.js (before first paint), then main.js reveals lines and sections as they come into view. */
.js .ln,.js .rv{opacity:0;transition:opacity .3s ease}
.js .ln{transition-delay:calc(var(--i,0) * 120ms)}
.js .ln.in,.js .rv.in{opacity:1}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{transition:none !important;animation:none !important}
  .js .ln,.js .rv{opacity:1}
}

/* Logo beside the wordmark in the masthead (Danny, 5 Sep). Footer wordmark stays text only. */
.mh-bar .wm{display:inline-flex;align-items:center;gap:.625rem;text-decoration:none}
.wm-logo{width:2.5rem;height:2.5rem;display:block;flex:none}
@media (min-width:900px){.wm-logo{width:3rem;height:3rem}}

/* Anchor jumps (Pricing, What we do, The app): scroll smoothly and land with the section's label
   1.5rem under the header rather than a full section-padding below it. Negative scroll-margin
   cancels the section's own top padding. */
html{scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
.sec{scroll-margin-top:calc(1.5rem - var(--section))}
.ctl{scroll-margin-top:calc(1.5rem - var(--s8))}
@media (min-width:900px){.ctl{scroll-margin-top:calc(1.5rem - var(--s9))}}

/* ---- The language layer: one site, every tongue (js/i18n.js, i18n/<code>.json) ----
   The globe in the masthead opens the LANGUAGE overlay: the 37 native names in a hairline grid,
   the current one filled. With JS off the globe is a plain link to #lang (the overlay shows via
   :target) and each name is a plain link to ?lang=<code>. All sizes in rem: THE SCALING RULE. */
.langbtn{display:inline-flex;align-items:center;gap:.5rem;min-height:2.75rem;padding:0 .25rem;color:var(--ink-2);text-decoration:none;font-size:var(--label);font-weight:500;line-height:1;letter-spacing:.12em;text-transform:uppercase}
.langbtn svg{width:1.25rem;height:1.25rem;display:block;flex:none;fill:none;stroke:currentColor;stroke-width:1;vector-effect:non-scaling-stroke}
.langbtn svg *{vector-effect:non-scaling-stroke}
.langbtn:hover,.langbtn:focus-visible,.langbtn[aria-expanded="true"]{color:var(--ink)}
.lang{display:none;position:fixed;inset:0;z-index:40;background:var(--bg);color:var(--ink);overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain}
.lang:target,.lang.open{display:block}
html.lang-open{overflow:hidden}
.js .lang.open{animation:langfade .25s ease}
@keyframes langfade{from{opacity:0}to{opacity:1}}
.lang-in{padding-top:var(--s5);padding-bottom:var(--s8)}
.lang-top{display:flex;align-items:center;justify-content:space-between;gap:var(--s4);min-height:3.5rem;margin:0 0 var(--s6);border-bottom:1px solid var(--rule)}
@media (min-width:900px){.lang-top{min-height:4rem}}
.lgrid{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(2,1fr);gap:.5rem}
@media (min-width:600px){.lgrid{grid-template-columns:repeat(3,1fr)}}
@media (min-width:900px){.lgrid{grid-template-columns:repeat(6,1fr);gap:.625rem}}
.lgrid a{display:flex;align-items:center;justify-content:center;min-height:2.75rem;padding:.75rem .5rem;border:1px solid var(--ink);color:var(--ink);font-size:.8125rem;font-weight:400;line-height:1.3;letter-spacing:.04em;text-align:center;text-decoration:none;transition:opacity .2s}
.lgrid a[data-ns]{letter-spacing:0}
.lgrid a:hover,.lgrid a:focus-visible{opacity:.7}
.lgrid a.cur{background:var(--ink);color:var(--bg);opacity:1}
.hero-measure .ln{white-space:nowrap}

/* ---- The currency layer (js/currency.js) ----
   Prices can be read in 29 currencies; every charge stays in pounds. The currency button sits beside
   the globe and is drawn as the globe is: same height, padding, gap, label type, --ink-2 at rest and
   --ink on hover, focus and while its box is open. Its symbol sits in a fixed 1.25rem box, the
   globe's own, so the button is the same width whatever the currency (1.75rem on phones, 4.375rem
   from 600). It shows only when JavaScript runs (html.scripted, set by the head script): without it
   the prices are the pounds as written and there is nothing to switch.
   The box is the language box (full screen, the hairline grid, the current one filled) built as a
   native modal dialog; the first-visit welcome is a black panel with a hairline on an 80% black
   scrim, the site's own mark, heading, ledger fields and primary button. All sizes in rem: THE
   SCALING RULE. */
.langbtn .lc{display:none}
@media (min-width:600px){.langbtn .lc{display:block}}
.curbtn{display:none;align-items:center;gap:.5rem;min-height:2.75rem;margin:0;padding:0 .25rem;border:0;border-radius:0;background:transparent;color:var(--ink-2);font-family:var(--font);font-size:var(--label);font-weight:500;line-height:1;letter-spacing:.12em;text-transform:uppercase;cursor:pointer;-webkit-appearance:none;appearance:none}
.scripted .curbtn{display:inline-flex}
.curbtn[hidden]{display:none}
.curbtn:hover,.curbtn:focus-visible,.curbtn[aria-expanded="true"]{color:var(--ink)}
/* The symbol (measured at 2x, 390 and 1440, every currency): a one-character symbol at 1.25rem 300
   inks .91 to .97rem tall against the globe's 1rem circle, its centre within .06rem of the circle's
   (the shekel, which sits on the baseline, .11rem low), widest the won at 1.19rem; two and three
   letters at .8125rem 400, widest RM at 1.16rem. All inside the 1.25rem box, so no nudge. */
.curbtn .cs{display:block;flex:none;width:1.25rem;font-size:1.25rem;font-weight:300;line-height:1;letter-spacing:0;text-align:center;text-transform:none;white-space:nowrap}
.curbtn[data-len="2"] .cs,.curbtn[data-len="3"] .cs{font-size:.8125rem;font-weight:400}
.curbtn .cc{display:none}
@media (min-width:600px){.curbtn .cc{display:block;min-width:2.125rem;text-align:start}}
@media (min-width:900px){.curbtn{order:2;margin-inline-end:var(--s4)}}

/* The head script hides the body until a remembered or asked-for currency is in (3 seconds at most);
   html's own background is already black. While a window is open the page behind does not scroll,
   and nothing behind the welcome's scrim moves:
   - body overflow:visible: once html has an overflow of its own, body's overflow-x:hidden no longer
     passes to the window and makes body a scroll box of its own, and the sticky masthead would let
     go of the top of the screen (measured: it jumped off screen behind the scrim);
   - --bar: overflow:hidden takes a classic scrollbar away, which would widen the page; the script
     sets --bar to that scrollbar's width (only when there was one) and body gets it back on the
     right, where Chrome, Edge and Safari keep the page's scrollbar in both directions. It is a
     measured px width of the browser's own scrollbar, not a design size. scrollbar-gutter is no use
     here: Chrome takes a stable gutter out of 100vw, and every size on this site is drawn from 100vw
     (measured: the root went from 16px to 15.83px at 1440 and stayed there after closing). */
html.i18n-wait body,html.cur-wait body{visibility:hidden}
html.cur-open,html.welcome-open{overflow:hidden}
html.cur-open body,html.welcome-open body{overflow:visible;padding-right:var(--bar,0px)}

/* A converted price reads left to right as one piece in every language (469 €, never € 469 in one
   place and 469 € in another). In left to right pages this changes nothing. */
.money{direction:ltr;unicode-bidi:isolate}

/* The box */
.curbox{position:fixed;inset:0;z-index:40;width:100%;height:100%;max-width:none;max-height:none;margin:0;padding:0;border:0;background:var(--bg);color:var(--ink);overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain}
.curbox::backdrop,.welcome::backdrop{background:transparent}
/* An open modal dialog makes its text selectable again (Chrome's own sheet sets user-select:text on
   it): the house rule says no, and the selects stay selectable through the rule at the top. */
.curbox,.welcome{-webkit-user-select:none;user-select:none}
.js .curbox[open]{animation:langfade .25s ease}
.cgrid li{display:flex}
.cgrid button{flex:1;display:grid;grid-template-columns:auto auto;justify-content:center;align-content:center;column-gap:.375rem;row-gap:.125rem;min-height:2.75rem;margin:0;padding:.75rem .5rem;border:1px solid var(--ink);border-radius:0;background:transparent;color:var(--ink);font-family:var(--font);font-size:.8125rem;font-weight:400;line-height:1.3;letter-spacing:.04em;text-align:center;cursor:pointer;-webkit-appearance:none;appearance:none;transition:opacity .2s}
/* the name spans both columns and widens them: the symbol and the code meet in the middle */
.cg-sym{justify-self:end}
.cg-code{justify-self:start}
.cg-name{grid-column:1 / -1;font-size:var(--label);color:var(--ink-2);overflow-wrap:break-word}
html.nls .cgrid button{letter-spacing:0}
.cgrid button:hover,.cgrid button:focus-visible{opacity:.7}
.cgrid button[aria-current="true"]{background:var(--ink);color:var(--bg);opacity:1}
.cgrid button[aria-current="true"] .cg-name{color:var(--bg)}
.cur-note{max-width:34em;margin:var(--s6) 0 0;color:var(--ink-2);font-size:.8125rem;line-height:1.55}
.cur-note + .cur-note{margin-top:var(--s2)}

/* The welcome. The selects are 1.0625rem, not the field's 1rem: iPhone Safari zooms the page when a
   field under 16px is focused, and 1.0625rem is 16px or more from 368px wide up. .input has no
   native arrow (appearance:none), so the chevron is drawn in hairlines: its ink is .3125rem tall
   and centred .3125rem above the text's baseline (measured at 2x, 390 and 1440: the capitals run
   .84 to 1.66rem down the 2.75rem select, the chevron 1.16 to 1.47). */
:root{--scrim:rgba(0,0,0,.8)}
.welcome{position:fixed;inset:0;z-index:50;width:100%;height:100%;max-width:none;max-height:none;margin:0;padding:var(--gutter);border:0;background:var(--scrim);color:var(--ink);overflow:hidden}
.welcome[open]{display:flex;align-items:center;justify-content:center}
.js .welcome[open]{animation:langfade .25s ease}
.welcome-in{width:100%;max-width:30rem;max-height:100%;overflow-y:auto;overscroll-behavior:contain;padding:var(--s6) var(--s5) var(--s5);background:var(--bg);border:1px solid var(--rule-2)}
@media (min-width:600px){.welcome-in{padding:var(--s7) var(--s6) var(--s6)}}
.welcome-in .h2{margin:0 0 var(--s3)}
.welcome-p{margin:0 0 var(--s6);color:var(--ink-2)}
.welcome .field{position:relative}
.welcome .field[hidden]{display:none}
.welcome .field::after{content:"";position:absolute;inset-inline-end:.375rem;bottom:1.3125rem;width:.4375rem;height:.4375rem;border-right:1px solid var(--ink-2);border-bottom:1px solid var(--ink-2);transform:rotate(45deg);pointer-events:none}
.welcome .field:focus-within::after{border-color:var(--ink)}
.welcome select.input{font-size:1.0625rem;padding-inline-end:1.75rem;cursor:pointer;text-overflow:ellipsis}
.welcome .btn{margin-top:var(--s2)}

/* Right-to-left pages (ar, he, fa, ur): the masthead, grids, hairline lists, footer and forms
   mirror through the logical properties above (inline-start/end); the centred marks (the 44px
   hit areas) are symmetric and need nothing, and the app screenshot is the app, left to right. The company block is
   Latin and stays left-to-right, set against the mirrored edge. */
html[dir=rtl] .ln{letter-spacing:0}
html[dir=rtl] .ft .co{direction:ltr;text-align:end}
/* the closed disclosure's chevron points along the line, to the left in these languages */
html[dir=rtl] .su summary::before{transform:rotate(135deg)}
html[dir=rtl] .su-adv[open] > summary::before{transform:rotate(45deg)}
/* The wordmark is Latin and reads left to right everywhere; as a link it is a flex box whose
   three runs (site&, seo, nd) would otherwise reverse under dir=rtl. */
.wm{direction:ltr;unicode-bidi:isolate}

/* Scripts that are never letter-spaced or force-uppercased (Arabic, Hebrew, Persian, Urdu,
   Devanagari, Bengali, Tamil, Thai, CJK, Hangul): the label style loses its tracking and its
   uppercase, the hero its negative tracking. The two-letter code in the globe button stays Latin. */
html.nls .label,html.nls .btn,html.nls .mh-nav a,html.nls .ft-links a,html.nls .ft-links span,html.nls .skip,html.nls .cats h1,html.nls .cats p,html.nls .tab,html.nls .wm{letter-spacing:0;text-transform:none}
html.nls .ln{letter-spacing:0}
html.nls .su summary,html.nls .su .edit{letter-spacing:0;text-transform:none}
html.nls .langbtn .lc,html.nls .curbtn .cc{letter-spacing:.12em;text-transform:uppercase}
html.nls .doc h2,html.nls .h2,html.nls .wd-row h3,html.nls .hw h3{line-height:1.35}

/* Fallback faces for the scripts Inter does not carry: Inter stays first so Latin letters,
   figures and the wordmark keep the house face, and each script drops to a clean system face. */
html.nls{--font:Inter,-apple-system,"Segoe UI","Noto Sans",Arial,sans-serif}
html[lang=ja]{--font:Inter,-apple-system,"Hiragino Sans","Hiragino Kaku Gothic ProN","Yu Gothic UI","Meiryo","Noto Sans JP",sans-serif}
html[lang=ko]{--font:Inter,-apple-system,"Apple SD Gothic Neo","Malgun Gothic","Noto Sans KR",sans-serif}
html[lang=zh-Hans]{--font:Inter,-apple-system,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","Noto Sans SC",sans-serif}
html[lang=zh-Hant],html[lang=yue]{--font:Inter,-apple-system,"PingFang TC","PingFang HK","Hiragino Sans CNS","Microsoft JhengHei","Noto Sans TC",sans-serif}
html[lang=th]{--font:Inter,-apple-system,"Thonburi","Leelawadee UI","Noto Sans Thai",sans-serif}
html[lang=ar],html[lang=fa],html[lang=ur]{--font:Inter,-apple-system,"Geeza Pro","Segoe UI","Noto Naskh Arabic","Noto Sans Arabic",Arial,sans-serif}
html[lang=he]{--font:Inter,-apple-system,"Arial Hebrew","Segoe UI","Noto Sans Hebrew",Arial,sans-serif}
html[lang=hi]{--font:Inter,-apple-system,"Kohinoor Devanagari","Devanagari Sangam MN","Nirmala UI","Noto Sans Devanagari",sans-serif}
html[lang=bn]{--font:Inter,-apple-system,"Kohinoor Bangla","Bangla Sangam MN","Nirmala UI","Noto Sans Bengali",sans-serif}
html[lang=ta]{--font:Inter,-apple-system,"Tamil Sangam MN","Nirmala UI","Noto Sans Tamil",sans-serif}
