/* ─── The product page ────────────────────────────────────────────────────
   Same palette and chrome as the long version, a different register: this one
   shows the app rather than teaching a notation.

   The presentation model is lifted from the reference the owner pointed at —
   UI floats FREE of the device. A screenshot inside a bezel is a picture of an
   app; a row of the ledger lying on the page at its own scale, with its own
   shadow, is the app. So the hero is one big room with the phone cropped by
   its edge and four entries laid over it, and the section after it throws
   entries across the whole field. ─────────────────────────────────────────── */

/* ── Depth. Three steps and no more, so elevation MEANS something: 1 is a
      thing on the page, 2 is a thing lifted off it, 3 is the device. ──────── */
:root {
  --e1: 0 1px 2px rgb(var(--shade) / .05), 0 2px 8px -3px rgb(var(--shade) / .10);
  --e2: 0 2px 4px rgb(var(--shade) / .06), 0 12px 26px -10px rgb(var(--shade) / .22);
  --e3: 0 6px 12px rgb(var(--shade) / .10), 0 40px 70px -28px rgb(var(--shade) / .42);
  --shade: 16 18 38;
  --r-sm: 12px; --r-md: 18px; --r-lg: 26px; --r-xl: 34px;
}
:root[data-theme='dark'] { --shade: 0 0 0; }

/* ═══ THE HEADER BAR ═══════════════════════════════════════════════════════
   It was reserving 52px of body padding for a FIXED bar that this page then
   made `position: relative` further down the stylesheet — so the padding went
   on being empty and the page opened on a 52px strip of bare ground above the
   nav, with a bar that then scrolled away and never came back.

   Sticky instead: it takes its own space and keeps it, so nothing has to be
   reserved anywhere. The hero is pulled up underneath it and given the height
   back as padding, which puts the sky behind the bar rather than starting
   below it — the two stop looking like separate pages stacked. ──────────── */
:root { --navh: 52px; }
body { padding-top: 0; }
.chrome { position: sticky; top: 0; z-index: 90; }
/* Transparent over the hero's sky, solid the moment the page moves. */
.nav--solid { background: transparent; border-bottom: 1px solid transparent; }
.chrome[data-lifted='true'] .nav--solid {
  background: var(--chrome-nav); border-bottom-color: var(--divider);
}
/* On the day side the brand pill sits on pale lime at 1.66:1 of fill — the
   hue carries it and the luminance does not, so the edge comes from elevation
   instead. Measured, not assumed: on the night side the same pill is 6.71:1
   against its sky and needs none of this. */
.nav .btn--brand { box-shadow: var(--e2); }
.sec { padding-block: clamp(58px, 7vw, 104px); }


.btn--ink  { background: var(--ink); color: var(--canvas); }
.btn--ink:hover { background: var(--ink-hi); }
.btn--lime { background: var(--brand); color: var(--on-brand); box-shadow: var(--e2); }
.btn--lime:hover { background: var(--brand-hover); }
/* A silhouette survives any ground, which is the argument the app's own
   `controlOffLine` note makes — and now it has to, because this button sits on
   paper in one room and on near-black in the other. */
.btn--onpanel {
  background: transparent; color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--soft);
}
.btn--onpanel:hover {
  background: rgb(var(--lift-rgb) / .06);
  box-shadow: inset 0 0 0 1px var(--muted);
}

.fine { font-size: 13px; color: var(--faint); margin: 16px 0 0; }
/* The app has exactly one label voice — 11pt, 600, letterspaced, uppercase —
   and it is the voice of "6 OPEN BOOKS", "THIS MONTH", "OPENED WITH". The page
   was using a translucent pill instead, which is the house style of every
   SaaS landing page and of nothing in particular. Same voice as the product. */
.eyebrow {
  display: inline-flex; align-items: center; gap: 10px; margin: 0 0 26px;
  padding: 0; background: none; box-shadow: none; border-radius: 0;
  font-size: 11.5px; font-weight: 600; letter-spacing: .17em; text-transform: uppercase;
  color: var(--muted);
}
/* "In beta" should look live. One slow breath, not a blink. */
.eyebrow .dot {
  width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--brand); box-shadow: 0 0 0 0 rgb(var(--brand-rgb) / .5);
  animation: pip 2.6s var(--ease-io) infinite;
}
@keyframes pip {
  0%, 68%, 100% { box-shadow: 0 0 0 0 rgb(var(--brand-rgb) / .45); }
  34%           { box-shadow: 0 0 0 7px rgb(var(--brand-rgb) / 0); }
}
@media (prefers-reduced-motion: reduce) { .eyebrow .dot { animation: none; } }
.h em,
h1 em{
  font-style: normal; color: var(--on-lime); background: var(--lime);
  /* Drawn tight to the word. A marker that stands off the letters by a third
     of their height stops being emphasis and becomes a box with a word in it. */
  border-radius: .06em; padding: .02em .08em .04em; margin: 0 -.03em;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}

/* ─── 1. The room ──────────────────────────────────────────────────────── */
.hero { padding-block: 0; }
.hero > .wrap { max-width: none; padding-inline: 0; }


.hero__cta { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 34px; }
/* Three lime dots and grey text read as a leftover. A tick inside a tinted
   disc is the app's own affirmative — DoneDisc at the smallest size it works
   at — and the label takes the product's voice like the eyebrow above it. */
.marks {
  list-style: none; display: flex; flex-wrap: wrap; gap: 11px 22px;
  margin: 36px 0 0; padding: 0;
  font-size: 11px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted);
}
.marks li { display: flex; align-items: center; gap: 9px; }
@media (min-width: 900px) { .marks { flex-direction: column; align-items: flex-start; gap: 12px; } }
.marks li::before {
  content: ''; flex: none; width: 16px; height: 16px; border-radius: 50%;
  background: rgb(var(--brand-rgb) / .16)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='M5.7 10.4 8.7 13.4 14.3 6.7' stroke='%2326E06C' stroke-width='2.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
    center / 11px 11px no-repeat;
}
/* Exactly the device's box. Every .fly below is a percentage of THIS, so the
   callouts hold their grip on the phone at any window width. */
.stagepanel__rig { position: relative; width: min(322px, 76vw); }
@media (min-width: 900px) { .stagepanel__rig { width: min(432px, 46vw); } }
.stagepanel__rig::before {
  content: ''; position: absolute; z-index: -1; pointer-events: none;
  left: 50%; top: 54%; translate: -50% -50%;
  /* It has to be WIDER THAN THE PHONE or the whole gradient hides behind it:
     at 168% the stop at 66% fell inside the device's own width and nothing
     reached the page at all. */
  width: 310%; height: 128%; border-radius: 50%;
  background: radial-gradient(50% 50% at 50% 50%,
    rgb(var(--brand-rgb) / .40) 0%, rgb(var(--brand-rgb) / .16) 38%, transparent 70%);
  filter: blur(80px);
}
/* On paper the spill is nearly invisible and a real shadow does the seating,
   so the device gets one that reaches. */
:root[data-theme='light'] .stagepanel__rig::before {
  background: radial-gradient(50% 50% at 50% 50%,
    rgb(var(--brand-rgb) / .20) 0%, rgb(var(--brand-rgb) / .08) 38%, transparent 70%);
}
.ph--hero { box-shadow: none; }
.stagepanel__rig .ph {
  box-shadow:
    0 0 0 1px rgba(0,0,0,.40),
    inset 0 1px 1px rgba(255,255,255,.40),
    inset 0 -1px 1px rgba(0,0,0,.50),
    0 8px 18px rgb(var(--shade) / .18),
    0 54px 90px -34px rgb(var(--shade) / .62);
}
.stagepanel__rig .ph { width: 100%; }
 /* Cropped through a LIST, not through the tab bar or an empty gap: a device
    cut mid-content reads as a window, one cut across its own furniture reads
    as a rendering mistake. The hero screen therefore has no tab bar at all —
    it is never the bottom of the phone that is on show. */
.ph--hero { margin-bottom: -13%; }

/* ─── Entries, lifted out of the app ─────────────────────────────────────
   Absolutely placed over the panel at their own scale, each with its own
   shadow, each arriving on its own beat. */
/* Frosting is a thing you do over a ground. With the panel gone these sit on
   the page itself, so they go back to being cards: the page's own card fill,
   a hairline, and a shadow — and every tone follows the theme again, because
   the ground under them is paper in one room and near-black in the other. */
.fly {
  position: absolute; z-index: 4; display: grid; gap: 2px;
  padding: 10px 14px; border-radius: 14px;
  background: var(--card);
  box-shadow: var(--e2), inset 0 0 0 1px var(--divider);   /* re-stated by the material pass below */
  color: var(--muted);
  font-size: 12px; line-height: 1.3; white-space: nowrap;
  opacity: 0; transform: translateY(14px) scale(.94);
  transition: opacity .5s var(--ease-out), transform .6s var(--ease-out);
  will-change: transform, opacity;
}
.fly.in { opacity: 1; transform: none; }
.fly b { font-family: var(--font-display); font-weight: 700; font-size: 15.5px; color: var(--ink); letter-spacing: -.01em; }
.fly b.up { color: var(--fig-in); }
.fly b.dn { color: var(--fig-out); }
.fly i { font-style: normal; font-size: 11px; color: var(--faint); }
.fly__tag {
  justify-self: start; font-size: 8.5px; font-weight: 700; letter-spacing: .1em;
  padding: 3px 7px; border-radius: 99px;
  background: var(--brand); color: var(--on-brand);
}
.fly__tag--owed { background: var(--chip-owed-bg); color: var(--chip-owed-ink); }
 /* All four hug the DEVICE. One of them used to sit at right:46%, which on a
    wide window put it straight through the headline — a floating card over
    copy is clutter, over the product it is a callout. */
/* Two on each edge of the device, staggered so none of them meets another,
   and each overlapping the phone by about a finger's width. Measured, not
   placed by eye: at 47% the POSTED card sat ON the lede, and the balanced
   pill covered sixty-three pixels of the ledger it is vouching for. */
/* Placed against the app's own regions, measured on the page: the status bar
   runs 143-177 of the panel's 620, the business bar to 213, the balance to
   258, and the rows from 313. Nothing may sit on the balance — it is the one
   figure the hero exists to show. The POSTED card is pinned to the Main till
   row instead, which is the row it actually posted to. */
/* Percentages OF THE RIG, which is the device — the grip holds at any width.
   Re-derived now the rig is the phone's whole height rather than a cropped
   part of it, and the overhangs re-cut so each still bites ~36px of screen. */
.fly--a { top: -6%; right: -32%; }
.fly--b { top: 31%; left:  -25%; }
.fly--c { top: 56%; right: -40%; }
.fly--d {
  display: flex; align-items: center; gap: 7px; top: 70%; left: -48%;
  font-weight: 600; color: var(--on-lime); background: var(--lime); box-shadow: var(--e2);
}
@media (min-width: 900px) {
  /* Measured against the app's own regions at this scale: status bar 10-65,
     business bar 65-122, balance 137-194, caption 200-214, segmented 230-265,
     rows from 279 — of 446 visible before the fold cuts. Nothing sits on the
     business name or on the balance; the rest hang off the rows. */
  .fly--a { top: -4%;  right: -25%; }
  .fly--b { top: 62%;  left:  -19%; }
  .fly--c { top: 78%;  right: -31%; }
  .fly--d { top: 86%;  left:  -36%; }   /* was floating clear of the phone, over the copy */
}


/* ─── 4. The picker ──────────────────────────────────────────────────── */
.sec--tint { background: var(--subtle); border-block: 1px solid var(--divider); }
.pick { display: grid; gap: clamp(28px, 4vw, 52px); margin-top: 38px; align-items: start; }
.pick__list { display: grid; gap: 12px; }
.kind {
  display: grid; gap: 6px; text-align: left; cursor: pointer;
  background: var(--card); border: 1px solid var(--divider);
  border-radius: var(--r-md); padding: 18px 20px;
  transition: transform var(--t-ui) var(--ease-out), box-shadow var(--t-ui) var(--ease-out),
              border-color var(--t-ui) var(--ease-out);
}
.kind:hover { transform: translateY(-2px); box-shadow: var(--e2); }
.kind.is-on {
  border-color: var(--brand); box-shadow: var(--e2), inset 0 0 0 1px var(--brand);
}
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.chips i {
  font-style: normal; font-size: 12px; font-weight: 500; color: var(--t700);
  border: 1px solid var(--hairline); border-radius: var(--radius-pill);
  padding: 4px 10px; background: var(--canvas);
}
.pick__show { display: flex; justify-content: center; position: sticky; top: 76px; }
[data-kind-phone] { transition: opacity .18s var(--ease-out); }
[data-kind-phone].swap { opacity: .55; }


/* ─── 7. Price ───────────────────────────────────────────────────────── */
.sec--lime { background: var(--lime); }
.sec--lime .h, .sec--lime .lede { color: var(--on-lime); }
.sec--lime .h { font-size: clamp(36px, 5.4vw, 62px); }


/* ─── 9. Start ───────────────────────────────────────────────────────── */
.start { text-align: center; }
.start .h, .start .lede { margin-inline: auto; }
.start .lede { text-align: center; }
.form { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin: 28px auto 0; max-width: 470px; }
.form input {
  flex: 1 1 220px; min-width: 0; padding: 14px 17px; font: inherit; font-size: 15px;
  color: var(--ink); background: var(--card);
  border: 1px solid var(--hairline); border-radius: var(--radius-pill); box-shadow: var(--e1);
}
.form input::placeholder { color: var(--faint); }
.start .fine { max-width: 46ch; margin-inline: auto; }

/* ─── The app, drawn ─────────────────────────────────────────────────────
   Markup, never a screenshot: a PNG is ~100KB, goes stale the next time the
   app ships, cannot follow the reader's theme, and cannot move.

   The frame is built the way a device actually catches light — a bright edge
   where it faces the sky, a dark one where it faces the floor, a hairline of
   screen bezel inside that, and one soft diagonal of glare across the glass.
   Four cheap tricks; without them a rounded rectangle reads as a wireframe. */
.ph {
  position: relative; width: min(272px, 76vw); flex: none;
  border-radius: 38px; padding: 8px;
  background: linear-gradient(150deg, #9BA1AC 0%, #34383F 32%, #15171C 62%, #626873 100%);
  box-shadow:
    0 0 0 1px rgba(0,0,0,.35),
    inset 0 1px 1px rgba(255,255,255,.34),
    inset 0 -1px 1px rgba(0,0,0,.45),
    var(--e3);
}
/* The contact shadow. An object with only an ambient shadow floats; one with a
   tight dark ellipse under it is standing on something. */
.ph::after {
  content: ''; position: absolute; left: 8%; right: 8%; bottom: -16px; height: 26px;
  background: radial-gradient(50% 50% at 50% 50%, rgb(var(--shade) / .34), transparent 72%);
  filter: blur(6px); z-index: -1; pointer-events: none;
}

.ph__btn {
  position: absolute; width: 2px; border-radius: 2px; z-index: 2;
  background: linear-gradient(180deg, #6E7A74, #2B3531);
}
.ph__btn--v1 { left: -2px; top: 21%; height: 7%; }
.ph__btn--v2 { left: -2px; top: 30.5%; height: 7%; }
.ph__btn--pw { right: -2px; top: 26%; height: 10%; }

.ph__scr {
  position: relative; border-radius: 31px; overflow: hidden;
  background: var(--canvas); aspect-ratio: 9 / 18.9;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.55);
}
/* The island, with its camera. */
.ph__island {
  position: absolute; left: 50%; top: 9px; translate: -50% 0; z-index: 5;
  width: 31%; height: 19px; border-radius: 99px; background: #050907;
  display: flex; align-items: center; justify-content: flex-end; padding-right: 6px;
}
.ph__island b {
  width: 6px; height: 6px; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #2B3A4A, #070C10 70%);
  box-shadow: inset 0 0 0 .5px rgba(255,255,255,.14);
}
/* One diagonal of glare, and it must never eat a tap. */
.ph__glare {
  position: absolute; inset: 0; z-index: 4; pointer-events: none; border-radius: 31px;
  background: linear-gradient(118deg,
    rgba(255,255,255,.14) 0%, rgba(255,255,255,.05) 17%,
    rgba(255,255,255,0) 34%, rgba(255,255,255,0) 100%);
}
:root[data-theme='light'] .ph__glare {
  background: linear-gradient(118deg,
    rgba(255,255,255,.30) 0%, rgba(255,255,255,.10) 14%,
    rgba(255,255,255,0) 28%, rgba(255,255,255,0) 100%);
}


@keyframes rowIn { from { opacity: 0; transform: translateY(7px); } }


@keyframes blink { 50% { opacity: 0; } }
.is-done .caret { display: none; }


/* ─── Wider ──────────────────────────────────────────────────────────── */
@media (min-width: 900px) {
  
  
  .marks { flex-direction: row; gap: 10px 22px; }
  
  .pick { grid-template-columns: 1.04fr .96fr; }
  
  
}
@media (max-width: 899px) {
  /* The entries stop being a composition and start being clutter the moment
     they have to overlap the copy, so below the two-column break only the two
     that clear the phone survive. */
  .fly--b, .fly--d { display: none; }
  .fly--a { top: -11%; right: -14%; }
  .fly--c { top: 76%; left: -22%; right: auto; }
  
  
   
   
}
@media (max-width: 460px) {
  .nav--solid .nav__word { display: none; }
  .nav--solid .btn--brand { padding: 9px 13px; }
  
   
}
/* base.css hides the theme control below 900px because that is where the
   burger takes over and the menu carries it. This page had no menu, so it put
   the control back into the bar — and then the bar had a brand, a three-way
   toggle, a pill button and (once the burger arrived) a fourth thing, on a
   375pt screen. The menu exists now and the toggle lives in it, so the
   override goes and the bar gets its room back. */
@media (prefers-reduced-motion: reduce) {
  .fly{ opacity: 1 !important; transform: none !important; transition: none; }
  
  
}

/* ─── Detail ──────────────────────────────────────────────────────────────
   The pass where nothing new is added and everything already there is made
   correct. Each block below is one specific thing that was wrong. */

/* 1 · The home indicator. Every uncropped screen on a modern phone ends with
      it, and its absence is the kind of thing a reader cannot name but can
      feel — the screen stops in mid-air. The hero's device does NOT get one,
      because its bottom is off the panel. */
.ph__home {
  position: absolute; left: 50%; bottom: 7px; translate: -50% 0; z-index: 6;
  width: 34%; height: 4px; border-radius: 99px;
  background: var(--ink); opacity: .30;
}

   
   


/* 4 · Focus. Every control on the page gets the SAME ring, and it is designed
      rather than inherited from the UA — a keyboard reader is not a rounding
      error. */
.kind:focus-visible,
.thm__b:focus-visible,
.btn:focus-visible,
.form input:focus-visible{
  outline: 2px solid var(--brand); outline-offset: 3px; border-radius: var(--r-sm);
}
.thm__b:focus-visible { border-radius: 50%; outline-offset: 2px; }
.btn:focus-visible { border-radius: var(--radius-pill); }
.form input:focus-visible { border-radius: var(--radius-pill); border-color: var(--brand); }

/* 5 · Press. A button that only has a hover state is a button designed on a
      desktop: on a phone the finger never hovers, and the press IS the whole
      feedback. */
.btn { transition: transform var(--t-ui) var(--ease-out), background var(--t-ui) var(--ease-out), box-shadow var(--t-ui) var(--ease-out); }
.btn--lime:active, .btn--ink:active, .btn--brand:active { transform: scale(.975); }
.kind:active { transform: translateY(0) scale(.995); }


/* 6 · Figures. Every number that can change gets tabular figures, or the
      column jitters as it counts and the eye reads jitter as error. */
.fly b{ font-variant-numeric: tabular-nums; }

/* 7 · Setting. `balance` for headlines so the last line is never one word;
      `pretty` for body so a paragraph never ends on a widow. */
h1,
.h,
.kind__lab b{ text-wrap: balance; }
.lede,
.kind__lab em{ text-wrap: pretty; }

/* 8 · The page's own small furniture. */
::selection { background: var(--lime); color: var(--on-lime); }
.form input { caret-color: var(--brand); }

/* 9 · The seam. Two sections of the same colour meeting need a hairline, and
      a hairline on a warm ground should be warm — a grey one reads as a crack. */
.sec + .sec { border-top: 1px solid rgb(var(--lift-rgb) / .07); }
.sec--tint + .sec,
.sec + .sec--tint,
.sec--lime + .sec{ border-top: 0; }


   /* cropped: it has neither */

/* ═══ Rhythm ══════════════════════════════════════════════════════════════
   The page ran cream from the first pixel to the last, which is calm and also
   monotonous: nine sections with no change of room. These four blocks are the
   ones a reader SEES. */

/* 1 · Type, considerably larger. The headings were sized for a document; these
      are sized for a poster, which is what the top of a section is. */
.h{
  font-size: clamp(36px, 6.2vw, 78px); line-height: 1.01; letter-spacing: -.038em;
}
.sec--lime .h { font-size: clamp(42px, 7.4vw, 96px); }

.lede { font-size: clamp(16.5px, 1.35vw, 20px); line-height: 1.55; }


@keyframes tickRun { to { transform: translateX(-50%); } }


/* 5 · Every section arrives. One transform, one opacity, once. */
[data-rev] > * { opacity: 0; transform: translateY(22px); }
[data-rev].is-rev > * {
  opacity: 1; transform: none;
  transition: opacity .6s var(--ease-out), transform .75s var(--ease-out);
}
html:not(.js) [data-rev] > *, [data-rev].is-rev > * { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  [data-rev] > * { opacity: 1 !important; transform: none !important; }
}

/* The marker cannot be lime ON lime. On this one band it inverts: forest
   ground, lime letters — same device, opposite way round. */
.sec--lime .h em { background: var(--on-lime); color: var(--lime); }


/* Air. The reference's whole feeling is space around few things. */
.sec { padding-block: clamp(70px, 8.5vw, 132px); }
.hero { padding-block: 0; }

/* Motion that settles. 22px of travel on every section is a page that keeps
   clearing its throat; 12px is a page that arrives. */
[data-rev] > * { transform: translateY(12px); }
[data-rev].is-rev > * { transition: opacity .5s var(--ease-out), transform .6s var(--ease-out); }

/* The cards lose their tint band and sit on the page like the reference's do. */
.sec--tint { background: var(--canvas); border-block: 1px solid var(--divider); }
.kind { border-color: var(--hairline); }
.kind:hover { transform: none; box-shadow: var(--e1); }
.kind.is-on { box-shadow: none; border-color: var(--brand); }

/* ─── The device, closer to the object ──────────────────────────────────
   Four measurements corrected against a real handset rather than eyeballed:
   the screen is 19.5:9 not 18.9:9; the frame band is thinner than it was and
   its corner radius is the screen's plus the band, which is what makes a
   bezel look machined instead of drawn; the island is 26% of the width, not
   31%; and the screen carries its own faint corner darkening, because glass
   over a black bezel is never uniformly lit at the edges. */
.ph { padding: 6px; border-radius: 40px; }
.ph__scr { aspect-ratio: 9 / 19.5; border-radius: 34px; }
.ph__island { width: 26%; height: 18px; top: 8px; }
.ph__scr::after {
  content: ''; position: absolute; inset: 0; z-index: 4; pointer-events: none;
  border-radius: inherit;
  box-shadow: inset 0 0 14px rgba(0,0,0,.12), inset 0 0 0 1px rgba(0,0,0,.10);
}
:root[data-theme='dark'] .ph__scr::after { box-shadow: inset 0 0 16px rgba(0,0,0,.55), inset 0 0 0 1px rgba(255,255,255,.05); }
/* A quieter glare: on a calm page the reflection should be felt, not seen. */
.ph__glare { background: linear-gradient(118deg, rgba(255,255,255,.09) 0%, rgba(255,255,255,.03) 15%, rgba(255,255,255,0) 30%, rgba(255,255,255,0) 100%); }
:root[data-theme='light'] .ph__glare { background: linear-gradient(118deg, rgba(255,255,255,.22) 0%, rgba(255,255,255,.07) 13%, rgba(255,255,255,0) 27%, rgba(255,255,255,0) 100%); }
/* The buttons sit slightly proud of a thinner band. */
.ph__btn { width: 2.5px; }
.ph__btn--v1 { left: -2.5px; top: 20%; height: 6.5%; }
.ph__btn--v2 { left: -2.5px; top: 29%; height: 6.5%; }
.ph__btn--pw { right: -2.5px; top: 25.5%; height: 9.5%; }

/* The screen grew from 18.9:9 to a true 19.5:9, so the same seven rows now
   leave a gap above the crop. Crop deeper: the cut must always land IN the
   list, never in the air under it. */
.ph--hero { margin-bottom: 0; }
/* Cut so the balance, the segmented and the first rows all clear the fold. */
@media (min-width: 900px) { .ph--hero { margin-bottom: -108%; } }

/* ── The close ────────────────────────────────────────────────────────────
   The signup has moved back out of the price band into its own lime close,
   so it is centred again — `margin-inline: 0` was left over from the band it
   used to live in and pinned the field to the left of a centred column.

   NOTHING HERE MAY READ FROM A TOKEN THAT FLIPS. This band is lime in both
   schemes, and --brand-ink is forest on paper but MINT after dark: the exact
   pair that made the free tier 1.13:1. Every tone below is mixed from
   --on-lime, so the band cannot be wrong in one room and right in the other. */
.sec--lime .form--pill {
  justify-content: space-between;
  margin: clamp(30px, 4vw, 44px) auto 0;
  max-width: 470px;
}
.sec--lime .form--pill input { flex: 1 1 auto; color: var(--on-lime); }
.sec--lime .form--pill input::placeholder { color: color-mix(in srgb, var(--on-lime) 52%, transparent); }
.sec--lime .form--pill .btn { background: var(--on-lime); color: var(--lime); }
.sec--lime .form--pill .btn:hover { background: var(--panel-a); }
.sec--lime .form--pill:focus-within { box-shadow: var(--e1), 0 0 0 3px color-mix(in srgb, var(--on-lime) 26%, transparent); }

.sec--lime .lede    { color: color-mix(in srgb, var(--on-lime) 80%, transparent); }
.sec--lime .fine    { color: color-mix(in srgb, var(--on-lime) 66%, transparent); margin-top: 14px; }
.sec--lime .closerow,
.sec--lime .closerow a { color: color-mix(in srgb, var(--on-lime) 72%, transparent); }
.sec--lime .closerow a:hover { color: var(--on-lime); }
.sec--lime .signoff { color: var(--on-lime); }


/* The crop. 16% is enough that the cut lands inside the list on every screen
   here, and never in the air under one. */
.ph--card { margin-bottom: -16%; }


/* ═══ The signature ═══════════════════════════════════════════════════════
   The name once, enormous, and cut off by the page ending. A logo in a footer
   is a credit; a wordmark running off the bottom edge is a signature. */
.signoff {
  margin: clamp(46px, 6vw, 86px) 0 calc(-0.19em);
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(76px, 20vw, 290px); line-height: .78;
  letter-spacing: -.045em; color: var(--on-lime);
  text-align: center; user-select: none;
  opacity: .16;
}
.sec--lime .signoff { color: var(--on-lime); }
/* Smaller inside a card than standing alone — the card is the object here and
   the device is its contents. */
.ph--card { width: min(218px, 62vw); }

/* ═══ The close ═══════════════════════════════════════════════════════════
   The lime band is the end of the page — there is no separate footer after it.
   Centred, because the last screen is asking for one thing and a centred
   column is the shape of one request. */
.sec--lime { padding-bottom: 0; overflow: hidden; }

.sec--lime .h, .sec--lime .lede { margin-inline: auto; }
.sec--lime .lede { max-width: 46ch; }


/* One control, not two. A field and a button sitting side by side are two
   things to decide about; fused inside a single pill they are one. */
.form--pill {
  display: flex; align-items: center; gap: 0;
  background: #fff; border-radius: var(--radius-pill);
  padding: 5px 5px 5px 6px; max-width: 460px; width: 100%;
  margin: 34px auto 0; box-shadow: var(--e1);
}
.sec--lime .form--pill { justify-content: space-between; }
.form--pill input {
  flex: 1 1 auto; min-width: 0; border: 0; background: none; box-shadow: none;
  padding: 12px 14px; font-size: 15.5px; color: var(--on-lime);
}
.form--pill input::placeholder { color: #7E8F84; }
.form--pill input:focus { outline: none; }
.form--pill:focus-within { box-shadow: var(--e1), 0 0 0 3px rgb(var(--lift-rgb) / .24); }
.form--pill .btn {
  flex: none; background: var(--soft); color: #fff;
  padding: 12px 22px; font-size: 15px;
}
.form--pill .btn:hover { background: var(--on-lime); }
.sec--lime .fine { margin: 16px auto 0; text-align: center; }

/* The thin row the reference carries INSIDE the band: where to go, and who
   made it. Nothing else belongs at the end of a one-page site. */
.closerow {
  width: 100%; display: flex; flex-wrap: wrap; gap: 12px 28px;
  align-items: center; justify-content: space-between;
  margin-top: clamp(54px, 7vw, 96px);
  font-size: 14px; color: var(--brand-ink);
}

.closerow a { color: var(--brand-ink); }
.closerow a:hover { color: var(--on-lime); }

/* The name, lowercase, solid, and running off the bottom of the page. A logo
   in a footer is a credit; a wordmark cut by the page edge is a signature. */
.signoff {
  width: 100%; margin: clamp(24px, 3vw, 46px) 0 0;
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(96px, 26vw, 420px); line-height: .74;
  letter-spacing: -.055em; color: var(--on-lime);
  text-align: center; user-select: none; opacity: 1;
  /* Clipped by the section, not by a fixed height: the descender of the "o"
     row lands below the page edge at every width. */
  margin-bottom: -.2em;
}

/* The pill inherits `.form { flex-wrap: wrap }` and was breaking onto two rows
   on a phone — at which point it is no longer ONE control, which was the whole
   reason for fusing it. Never wrap; shrink instead. */
.form--pill { flex-wrap: nowrap; }
@media (max-width: 420px) {
  .form--pill { padding: 4px 4px 4px 5px; }
  .form--pill input { padding: 11px 10px; font-size: 15px; }
  .form--pill .btn { padding: 11px 15px; font-size: 14px; }
}


/* ═══ Kinds of book ═══════════════════════════════════════════════════════
   These are a CONTROL, and they were reading as a list of four paragraphs
   with a border on one of them. Now each carries the icon its books wear on
   the phone beside it — so picking one is visibly picking what you are
   looking at — and the selected state is filled rather than merely outlined. */
.kind {
  display: grid; grid-template-columns: 34px 1fr; gap: 4px 14px;
  align-items: start; padding: 18px 20px 17px;
}
.kind__i {
  grid-row: 1 / span 3; width: 34px; height: 34px; border-radius: 11px;
  display: grid; place-items: center;
  background: rgb(var(--brand-rgb) / .10); color: var(--brand-text);
  transition: background var(--t-ui) var(--ease-out), color var(--t-ui) var(--ease-out);
}
.kind__i svg { width: 17px; height: 17px; }
.kind__lab, .kind .chips { grid-column: 2; }

.kind.is-on {
  background: var(--brand); border-color: var(--brand);
  box-shadow: 0 16px 34px -20px rgb(var(--brand-rgb) / .55);
}
.kind.is-on .kind__i { background: rgb(var(--stage-rgb) / .20); color: var(--on-brand); }
.kind.is-on .chips i {
  background: rgb(var(--stage-rgb) / .14); border-color: rgb(var(--stage-rgb) / .22);
  color: rgb(var(--stage-rgb) / .92);
}


/* The selected book goes back to outlined. On a page of white cards and
   hairlines, a filled block is the loudest thing on screen — and it was
   shouting about which tab is open. */
.kind.is-on {
  background: var(--card); border-color: var(--brand);
  box-shadow: inset 0 0 0 1px var(--brand), var(--e1);
}
.kind.is-on .kind__i { background: var(--brand); color: var(--on-brand); }
.kind.is-on .chips i {
  background: var(--canvas); border-color: var(--hairline); color: var(--t700);
}

/* ═══ Kinds of book — composition ════════════════════════════════════════
   The four cards were reading as three invisible boxes and one shouting one.
   Give every card a real surface so the SET reads as a control, and let the
   selected state be a tint and an accent rather than a loud outline. */
.pick { align-items: center; }
.kind {
  background: var(--card);
  border: 1px solid var(--divider);
  box-shadow: none;
  transition: background var(--t-ui) var(--ease-out), border-color var(--t-ui) var(--ease-out),
              box-shadow var(--t-ui) var(--ease-out);
}
.kind:hover { border-color: var(--soft); box-shadow: var(--e1); }
.kind.is-on {
  background: rgb(var(--brand-rgb) / .07);
  border-color: rgb(var(--brand-rgb) / .42);
  box-shadow: var(--e1);
}
.kind.is-on .kind__i { background: var(--brand); color: var(--on-brand); }


/* ═══ Material ════════════════════════════════════════════════════════════
   What was reading as cheap, specifically:
   · every surface was a rounded rectangle with a 1px hairline and the same
     shadow — one material, repeated eleven times;
   · the ground was a flat fill, which is the single loudest tell of a
     template, because nothing real is one uniform colour;
   · the type had size contrast but no TENSION — the same tracking and the
     same weight at 15px and at 82px;
   · no light in the room: no ambient, no edge, nothing to say which way is up.

   None of this adds an element to the page. It is the same page, lit. */

/* 1 · Grain. ~700 bytes of SVG, tiled, at 2.5% — below the threshold where
      anyone sees "texture" and above the one where a screen looks like paint. */
body::before {
  content: ''; position: fixed; inset: 0; z-index: 0; pointer-events: none;
  opacity: .035; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}
:root[data-theme='dark'] body::before { opacity: .05; }
/* It must never cost a phone a frame, and it never shows on one anyway. */
@media (pointer: coarse) { body::before { display: none; } }
.hero,
.sec,
.ph{ position: relative; z-index: 1; }

/* 2 · Surfaces that know where the light is. A card lit along its top edge and
      shaded at its foot reads as an object; the same card with a uniform
      hairline reads as a box. */
.kind,
.form--pill,
.fly{
  box-shadow:
    inset 0 1px 0 rgb(var(--lift-rgb) / .55),
    0 1px 2px rgb(var(--shade) / .04),
    0 10px 26px -14px rgb(var(--shade) / .16);
}
:root[data-theme='dark'] .kind,
:root[data-theme='dark'] .fly,
:root[data-theme='dark'] .form--pill{
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.055),
    0 1px 2px rgba(0,0,0,.4),
    0 14px 34px -18px rgba(0,0,0,.7);
}
:root[data-theme='dark'] .fly {
  box-shadow:
    inset 0 0 0 1px rgba(255,255,255,.07),
    inset 0 1px 0 rgba(255,255,255,.10),
    0 2px 4px rgba(0,0,0,.5),
    0 22px 46px -20px rgba(0,0,0,.85);
}
.kind.is-on{ box-shadow: inset 0 1px 0 rgb(var(--lift-rgb) / .5), var(--e2); }


/* 3 · Type with tension. Tracking tightens as the size grows — the way metal
      type was cut — and the lede runs lighter and wider than the headline it
      sits under, so the two are doing different jobs rather than one job at
      two sizes. */
.h,
h1{ letter-spacing: -.042em; font-feature-settings: 'ss01'; }

.lede {
  font-weight: 400; letter-spacing: -.004em; line-height: 1.5;
  max-width: 44ch;
}
.kicker { font-size: 11.5px; letter-spacing: .17em; }


/* Money is set once, everywhere: tabular, tight, and never hyphenated. */
.fly b{ letter-spacing: -.035em; font-variant-numeric: tabular-nums; }

/* 4 · One light per room, low and wide, so a section has a top and a bottom. */
.sec { isolation: isolate; }
.sec--tint::before{
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: radial-gradient(64% 46% at 50% 0%, rgb(var(--brand-rgb) / .055), transparent 70%);
}


/* ═══ The hero is the book ════════════════════════════════════════════════
   Ruled edge to edge on a fixed rhythm, and every element in the hero sits on
   that rhythm — the headline occupies rows, the entries ARE rows, the total
   lands in the right-hand column where a total belongs. The phone lies on the
   page across several rules instead of standing beside the text. ─────────── */

.hero--book { padding: 0; }
.hero--book > .wrap { max-width: none; padding-inline: 0; }
.book {
  --row: 48px;
  --marg: max(var(--gut), calc((100% - var(--maxw)) / 2));
  position: relative; isolation: isolate; overflow: hidden;
  padding: calc(var(--row) * .26) 0 calc(var(--row) * .7);
}
/* Paper is warm and a screen is not, so on the day side the sheet is given a
   breath of warmth over the cool canvas. Two per cent — below noticing, above
   nothing. */
:root[data-theme='light'] .book::after {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: linear-gradient(175deg, rgba(255,246,228,.55), rgba(255,250,242,.28) 60%, transparent);
}
/* the spine: a cashbook is ruled down as well as across */
.book::before {
  content: ''; position: absolute; z-index: 0; top: 0; bottom: 0;
  left: max(var(--gut), calc((100% - var(--maxw)) / 2));
  width: 5px; border-inline: 1px solid rgb(var(--out-rgb) / .30);
  border-block: 0; background: none;
}
.book__bind {
  position: absolute; z-index: 0; top: 0; bottom: 0; left: 0;
  width: calc(var(--marg) + 10px); pointer-events: none;
  background: linear-gradient(to right, rgb(var(--shade) / .20) 0, rgb(var(--shade) / .05) 42%, transparent 100%);
}
/* and the edge of the pages underneath it */
.book__edge {
  position: absolute; z-index: 1; left: 0; right: 0; bottom: 0; height: 10px;
  pointer-events: none;
  background:
    linear-gradient(to bottom, rgb(var(--lift-rgb) / .16) 0 1px, transparent 1px 4px,
                               rgb(var(--lift-rgb) / .10) 4px 5px, transparent 5px 8px,
                               rgb(var(--lift-rgb) / .06) 8px 9px, transparent 9px);
}

/* the folio head, in the top margin above the ruling */
.book__folio {
  position: relative; z-index: 2; display: flex; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
  margin: 0 0 calc(var(--row) * .5);
  padding: 0 var(--marg) calc(var(--row) * .26);
  border-bottom: 1px solid rgb(var(--lift-rgb) / .14);
  font-size: 10.5px; font-weight: 600; letter-spacing: .19em; text-transform: uppercase;
  color: var(--muted);
}
.book__folio i { font-style: normal; color: var(--muted); margin: 0 4px; }
.book__folio b { font-family: var(--font-display); font-weight: 700; letter-spacing: 0; color: var(--t700); }

.book__in { position: relative; z-index: 2; }

.book__say { max-width: 40rem; padding-top: calc(var(--row) * .2); }
.hero--book .eyebrow { margin-bottom: calc(var(--row) * .34); }
.hero--book h1 {
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(34px, 4.4vw, 60px); line-height: 1.02;
  letter-spacing: -.042em; margin: 0 0 calc(var(--row) * .34);
  color: var(--ink-hi); text-wrap: balance;
}
.hero--book .lede { color: var(--t700); font-size: clamp(15.5px, 1.2vw, 17.5px);
                    line-height: 1.55; max-width: 30rem; }
.hero--book .hero__cta { margin-top: calc(var(--row) * .46); }

/* ── the rows ───────────────────────────────────────────────────────────── */
/* the ledger's own ruling: one line per row, aligned because the rows draw
   them, bleeding off both edges because .book clips */
.book__rows { position: relative; margin-top: calc(var(--row) * .6); max-width: 42rem; }
.book__rows::after {
  content: ''; position: absolute; z-index: -1; pointer-events: none;
  top: calc(var(--row) * .55); bottom: calc(var(--row) * .5);
  right: 7.2rem; width: 1px; background: rgb(var(--rule-rgb) / .30);
}
.book__rows::before {
  content: ''; position: absolute; z-index: -1; pointer-events: none;
  left: -100vw; right: -100vw; top: 0; bottom: 0;
  background: repeating-linear-gradient(
    to bottom, transparent 0 calc(var(--row) - 1px),
    rgb(var(--rule-rgb) / .34) calc(var(--row) - 1px) var(--row));
}
.book__day, .book__r, .book__hd, .book__tot { margin: 0; height: var(--row); display: grid; align-items: center; }
.book__day {
  grid-template-columns: 1fr auto; gap: 16px;
  font-size: 11px; font-weight: 600; letter-spacing: .17em; text-transform: uppercase;
  color: var(--muted);
}
.book__day b { font-family: var(--font-display); font-weight: 700; font-size: 13px;
                letter-spacing: 0; text-transform: none; color: var(--fig-in); }

/* every row on one grid, headings included — a ledger is a table or it is a list */
.book__hd, .book__r {
  grid-template-columns: 3.6rem 1fr 7.4rem 7.2rem;
  gap: clamp(10px, 1.3vw, 20px);
}
.book__hd { height: var(--row); }
.book__hd i, .book__hd span, .book__hd b, .book__hd u {
  font-style: normal; text-decoration: none; font-family: var(--font-ui);
  font-size: 10.5px; font-weight: 600; letter-spacing: .15em; text-transform: uppercase;
  color: var(--muted);
}
.book__hd b, .book__hd u { text-align: right; }

.book__r {
  opacity: 0; transform: translateY(9px);
  transition: opacity .5s var(--ease-out), transform .6s var(--ease-out);
}
.book__r.in { opacity: 1; transform: none; }
.book__r i { font-style: normal; font-size: 12px; font-weight: 600; letter-spacing: .06em; color: var(--muted); }
.book__r > span:not(.book__mag) { min-width: 0; font-size: clamp(15px, 1.25vw, 17px); color: var(--ink); letter-spacing: -.01em; }
/* NOT --faint. It is 5.18 on the night ground but 2.85 on paper, because it
   is the app's rung for white CARDS and this page's ground is #F2F2F7. Four
   separate things have now had to be moved off it — the folio, the column
   heads, the times, and this. On the page, --muted is the faintest rung that
   can carry text. */
.book__r em {
  display: block; font-style: normal; margin-top: 1px;
  font-size: 11px; font-weight: 600; letter-spacing: .06em; color: var(--muted);
}
.book__r > b {
  position: relative; justify-self: end;
  font-family: var(--font-display); font-weight: 700; font-size: clamp(16px, 1.4vw, 19px);
  letter-spacing: -.03em; font-variant-numeric: tabular-nums; text-align: right;
}
.book__r > b.up { color: var(--fig-in); }
.book__r > b.dn { color: var(--fig-out); }
/* the app's magnitude rule, drawn under the figure it measures */
.book__r > b s {
  position: absolute; right: 0; bottom: -9px; height: 4px; width: var(--w);
  border-radius: 3px 1px 1px 3px; background: var(--bar-in-a);
  transform-origin: right; transform: scaleX(0);
  transition: transform .7s var(--ease-out) .12s;
}
.book__r > b.dn s { background: var(--bar-out-a); }
.book__r.in > b s { transform: scaleX(1); }
/* the running balance — the column a cashbook has and a landing page does not */
.book__r > u {
  text-decoration: none; justify-self: end; text-align: right;
  font-family: var(--font-display); font-weight: 600; font-size: clamp(15px, 1.25vw, 17px);
  letter-spacing: -.025em; font-variant-numeric: tabular-nums; color: var(--t700);
}
.book__tot { margin: 0; height: var(--row); display: grid; align-items: center; }
.book__day {
  grid-template-columns: 1fr auto; gap: 16px;
  font-size: 11px; font-weight: 600; letter-spacing: .17em; text-transform: uppercase;
  color: var(--muted);
}
.book__day b { font-family: var(--font-display); font-weight: 700; font-size: 13px;
                letter-spacing: 0; text-transform: none; color: var(--fig-in); }
.book__r {
  grid-template-columns: 3.4rem 1fr auto 74px; gap: clamp(10px, 1.4vw, 22px);
  opacity: 0; transform: translateY(9px);
  transition: opacity .5s var(--ease-out), transform .6s var(--ease-out);
}
.book__r.in { opacity: 1; transform: none; }
.book__r i { font-style: normal; font-size: 12px; font-weight: 600; letter-spacing: .06em; color: var(--muted); }
.book__r > span:not(.book__mag) { min-width: 0; font-size: clamp(15px, 1.25vw, 17px); color: var(--ink); letter-spacing: -.01em; }
/* NOT --faint. It is 5.18 on the night ground but 2.85 on paper, because it
   is the app's rung for white CARDS and this page's ground is #F2F2F7. Four
   separate things have now had to be moved off it — the folio, the column
   heads, the times, and this. On the page, --muted is the faintest rung that
   can carry text. */
.book__r em {
  display: block; font-style: normal; margin-top: 1px;
  font-size: 11px; font-weight: 600; letter-spacing: .06em; color: var(--muted);
}
.book__r > b {
  font-family: var(--font-display); font-weight: 700; font-size: clamp(16px, 1.4vw, 19px);
  letter-spacing: -.03em; font-variant-numeric: tabular-nums; text-align: right;
}
.book__r > b.up { color: var(--fig-in); }
.book__r > b.dn { color: var(--fig-out); }


.book__tot {
  grid-template-columns: 1fr auto; gap: 20px;
  border-top: 2px solid rgb(var(--lift-rgb) / .22);
  height: calc(var(--row) * 2);
  opacity: 0; transition: opacity .5s var(--ease-out);
}
.book__tot.in { opacity: 1; }
.book__tot > span {
  font-size: 11px; font-weight: 600; letter-spacing: .17em;
  text-transform: uppercase; color: var(--muted);
}
.book__tot > b {
  display: flex; align-items: baseline; gap: .14em;
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(30px, 3.6vw, 52px); letter-spacing: -.045em;
  color: var(--ink-hi); font-variant-numeric: tabular-nums;
}
.book__tot i { font-style: normal; font-weight: 500; font-size: .38em; color: var(--muted); }

.hero--book .marks { margin-top: calc(var(--row) * .42); }

/* ── the device, standing at ninety degrees ─────────────────────────────
   Upright and square to the page. The turn was tried and it fails for a
   reason worth writing down: the app is portrait-locked, so rotating the
   device rotates the INTERFACE with it, and every label ends up reading
   bottom-to-top. It does not look like a phone set down on a book, it looks
   like a screen that has gone wrong. Straight, then — and the only tilt left
   is the page's own. */
.book__dev {
  position: absolute; z-index: 3;
  /* THE WHOLE DEVICE, TAB BAR AND ALL. It was cropped at the page foot to
     hide the empty third of the Books screen — and that third is exactly
     where the + lives, so the one control the hero invites you to press was
     clipped off the page. An empty strip of screen is a cheaper price than
     an interaction nobody can reach. */
  right: var(--marg); top: auto; bottom: calc(var(--row) * 1.25);
  width: min(322px, 27vw);
}
.book__dev .stagepanel__rig { position: relative; width: 100%; }
.book__dev .ph { width: 100%; }
.book__dev .ph--hero { margin-bottom: 0; }
/* It runs off the foot of the page rather than ending inside it — which also
   takes the empty third of the Books screen with it. */

@media (max-width: 1099px) {
  .book__dev { display: none; }
  .book__rows { max-width: none; }
}
@media (max-width: 620px) {
  .book { --row: 54px; }
  .book__r { grid-template-columns: 1fr auto; }
  .book__r i{ display: none; }
}


/* ── The hero device is a control, not a picture ─────────────────────────── */
.ph--live { cursor: default; }
.ph--live .k__pad b[data-key] { cursor: pointer; -webkit-tap-highlight-color: transparent; }
.ph--live .k__pad b[data-key]:hover { background: rgb(var(--lift-rgb) / .07); }
.ph--live .k__mode span, .ph--live .k__kinds span { cursor: pointer; }
.ph--live .k__cta {
  appearance: none; border: 0; width: auto; font: inherit;
  font-size: calc(var(--u)*16); font-weight: 600; cursor: pointer;
}
.ph--live .k__cta.live { cursor: pointer; }
.ph--live.is-out .k__mode .on { background: var(--k-tint-out); color: var(--k-red); }
.ph--live.is-out .k__cta.live { background: var(--k-red); color: #2A0710; }


/* ── the composer, sliding up over the books ────────────────────────────── */
.k__sheet {
  z-index: 6; border-radius: calc(var(--u)*34) calc(var(--u)*34) 0 0;
  box-shadow: 0 calc(var(--u)*-18) calc(var(--u)*40) rgba(0,0,0,.34);
  transform: translateY(100%);
  transition: transform .42s cubic-bezier(.22,1,.36,1);
  visibility: hidden;
}
.k__sheet.open { transform: none; visibility: visible; }
.ph--live .k__fab, .ph--live .k__pbar .x,
.ph--live .k__pad b[data-key], .ph--live .k__mode span,
.ph--live .k__kinds span { cursor: pointer; -webkit-tap-highlight-color: transparent; }
.ph--live .k__pad b[data-key]:hover { background: rgb(var(--lift-rgb) / .07); }
.ph--live .k__cta {
  appearance: none; border: 0; width: auto; font: inherit;
  font-size: calc(var(--u)*16); font-weight: 600; cursor: pointer;
}
.k__sheet.is-out .k__mode .on { background: var(--k-tint-out); color: var(--k-red); }
.k__sheet.is-out .k__cta.live { background: var(--k-red); color: #2A0710; }


/* ── the balance, in the head of the page ───────────────────────────────── */
.book__bal {
  display: inline-flex; align-items: baseline; gap: 7px;
  margin-left: 14px; padding-left: 14px;
  border-left: 1px solid rgb(var(--lift-rgb) / .16);
}
.book__bal em { font-style: normal; color: var(--muted); }
.book__bal > b {
  display: inline-flex; align-items: baseline; gap: 4px;
  font-family: var(--font-display); font-weight: 700; font-size: 14px;
  letter-spacing: -.02em; text-transform: none; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.book__bal > b i { font-size: 10px; font-weight: 500; color: var(--muted); letter-spacing: .04em; }
/* it answers when the book changes, so the eye is taken to it */
.book__bal.lit > b { animation: balLit .9s var(--ease-out); }
@keyframes balLit {
  0%   { color: var(--fig-in); transform: translateY(-2px); }
  100% { color: var(--ink); transform: none; }
}

@keyframes balLitOut {
  0%   { color: var(--fig-out); transform: translateY(2px); }
  100% { color: var(--ink); transform: none; }
}

/* the button asks once, and stops asking the moment it is used */
.ph--live .k__fab { position: relative; }
.ph--live:not(.used) .k__fab::after {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  box-shadow: 0 0 0 0 rgb(var(--brand-rgb) / .55);
  animation: fabAsk 2.4s var(--ease-io) infinite;
}
@keyframes fabAsk {
  0%, 62%, 100% { box-shadow: 0 0 0 0 rgb(var(--brand-rgb) / .5); }
  30%           { box-shadow: 0 0 0 calc(var(--u)*13) rgb(var(--brand-rgb) / 0); }
}
@media (prefers-reduced-motion: reduce) {
  .ph--live:not(.used) .k__fab::after { animation: none; }
  .book__bal.lit > b{ animation: none; }
}


/* Nothing drawn on top of the screen may catch the pointer — the glare, the
   island and the side buttons are all painted above it. */
.ph__glare, .ph__island, .ph__btn { pointer-events: none; }

/* a second day, further down the same page */
.book__day--mid { margin-top: 0; }
.book__day b.dn { color: var(--fig-out); }


/* ── brought forward ────────────────────────────────────────────────────── */
.book__bf {
  display: grid; grid-template-columns: 3.6rem 1fr 7.4rem 7.2rem;
  gap: clamp(10px, 1.3vw, 20px); align-items: center;
  height: var(--row); margin: 0;
}
.book__bf > span { font-size: clamp(14px, 1.15vw, 15.5px); color: var(--muted); }
.book__bf em {
  display: block; font-style: normal; margin-top: 1px;
  font-size: 10.5px; font-weight: 600; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted); opacity: .8;
}
.book__bf > u {
  text-decoration: none; justify-self: end; text-align: right;
  font-family: var(--font-display); font-weight: 600; font-size: clamp(14px, 1.15vw, 16px);
  letter-spacing: -.025em; font-variant-numeric: tabular-nums; color: var(--muted);
}

/* ── the money columns are ruled down, both of them ─────────────────────── */
.book__rows::after {
  top: calc(var(--row) * .55); bottom: calc(var(--row) * .5);
  left: 0; right: 0; width: auto;
  background:
    linear-gradient(rgb(var(--rule-rgb) / .34), rgb(var(--rule-rgb) / .34))
      calc(100% - 7.2rem + 6px) 0 / 1px 100% no-repeat,
    linear-gradient(rgb(var(--rule-rgb) / .34), rgb(var(--rule-rgb) / .34))
      calc(100% - 14.6rem - 6px) 0 / 1px 100% no-repeat;
}
/* the column heads sit in a band, and the rule under them is doubled — which
   is how a ledger separates its head from its body */
.book__hd {
  position: relative;
  background: rgb(var(--rule-rgb) / .07);
  box-shadow: 0 1px 0 rgb(var(--rule-rgb) / .5), 0 3px 0 rgb(var(--rule-rgb) / .26);
}
.book__hd::before {
  content: ''; position: absolute; z-index: -1; inset: 0 -100vw;
  background: inherit;
}
/* the sheet's outer edge, so the page reads as a page and not as a background */
.book__edge {
  height: 14px;
  background:
    linear-gradient(to bottom, rgb(var(--lift-rgb) / .20) 0 1px, transparent 1px 5px,
                               rgb(var(--lift-rgb) / .13) 5px 6px, transparent 6px 10px,
                               rgb(var(--lift-rgb) / .07) 10px 11px, transparent 11px);
}

/* ── the sky ─────────────────────────────────────────────────────────────
   Three layers, painted front to back:

     1. a veil that takes the image down to the page's own ground, so the hero
        joins the section beneath it with no seam and no edge to the photo;
     2. the photograph, sized to the page's WIDTH rather than `cover` — cover
        would scale a 16:9 sky up to fill a 1100px-tall hero and crop the sides
        off the composition, which is the whole picture;
     3. the gradient it replaces, still underneath. If the image is slow, or
        blocked, or 404s after a rename, the hero looks deliberate rather than
        broken — and on the first paint, before the file has arrived, this is
        what is on screen.

   The image is a fixed object, like the lime band and the receipt tape: it is
   a photograph of a sky, so it does not invert. Each theme gets its own. */
.wall__sky {
  position: absolute; z-index: -1; inset: 0; pointer-events: none;
  background:
    linear-gradient(180deg, transparent 0 38%, rgb(var(--ground-rgb) / .55) 66%,
                            rgb(var(--ground-rgb) / .94) 82%, var(--canvas) 92%),
    url('../img/hero-sky-light.webp?v=2f9c7d5a') center top / 100% auto no-repeat,
    linear-gradient(180deg, #DCF3BE 0%, #EDF8DF 34%, var(--canvas) 78%);
}
:root[data-theme='dark'] .wall__sky {
  background:
    /* A SCRIM UNDER THE BAR, and it is not decoration. The night sky's
       brightest cloud sits dead centre at the top — which is exactly where the
       nav links are — and measured against those actual pixels the links came
       out at 3.74:1. Opaque white does not fix it: the ground there is a
       mid-tone, so white only reaches 3.68. The ground has to come down
       instead. Measured again after: 6.6:1. */
    linear-gradient(180deg, rgb(var(--ground-rgb) / .62) 0,
                            rgb(var(--ground-rgb) / .34) 96px, transparent 208px),
    linear-gradient(180deg, transparent 0 38%, rgb(var(--ground-rgb) / .55) 66%,
                            rgb(var(--ground-rgb) / .94) 82%, var(--canvas) 92%),
    url('../img/hero-sky-dark.webp?v=b69762d2') center top / 100% auto no-repeat,
    linear-gradient(180deg, #14401F 0%, #0E1B24 38%, var(--canvas) 78%);
}

.wall__say { text-align: center; max-width: min(742px, 94%); margin-inline: auto; }

.wall__cta { display: flex; justify-content: center; gap: 14px; flex-wrap: wrap; }


/* ── the deck ────────────────────────────────────────────────────────────
   The device is IN FLOW here, pulled down by a margin, so the deck's height
   is whatever the device leaves above the crop. Positioning it absolutely
   against a hand-picked deck height was tried and it fails at exactly one
   band of widths: around 800px the device grew taller than deck + offset and
   its top rose out of the deck and into the headline, which is how the phone
   ended up sitting on the word "night". A height that is derived cannot drift
   out of agreement with the thing it is derived from. */
.wall__deck {
  position: relative; margin-top: clamp(26px, 3.2vw, 48px);
  display: grid; justify-items: center;
}
.wall__dev {
  position: relative; z-index: 3; width: min(292px, 24vw);
  margin-bottom: calc(-1 * clamp(118px, 12vw, 182px));
}
.wall__dev .ph { width: 100%; }

.wall__b {
  position: absolute; z-index: 1; left: 50%;
  bottom: calc(-1 * clamp(64px, 6.4vw, 104px));
  width: clamp(124px, 11.4vw, 164px); height: clamp(228px, 21.5vw, 310px);
  padding: 16px 16px 18px; border-radius: 22px 22px 8px 8px;
  background: var(--card); color: var(--ink);
  box-shadow: 0 1px 2px rgb(var(--shade) / .08), 0 26px 46px -22px rgb(var(--shade) / .42);
  translate: calc(-50% + var(--x) * var(--sgn, 1)) var(--y, 0px);
  rotate: var(--r); scale: var(--s, 1);
  transform-origin: 50% 100%;
}
/* depth of field. Sharpness at every distance is what makes a fan of cards
   read as a flat sticker sheet instead of a stack going away from you. */
.wall__b--far { filter: blur(1.1px); opacity: .72; }
.wall__b--mid { filter: blur(.4px);  opacity: .88; }
.wall__b u { display: block; width: 34%; height: 4px; border-radius: 3px;
             background: rgb(var(--lift-rgb) / .16); margin: 0 auto 16px; }
.wall__b i {
  display: grid; place-items: center; width: 30px; height: 30px; border-radius: 10px;
  font-style: normal; font-size: 10.5px; font-weight: 700; letter-spacing: .04em;
  background: var(--subtle); color: var(--muted);
}
.wall__b > span { display: block; margin-top: 12px; font-size: 12.5px; font-weight: 600; color: var(--ink); }
.wall__b > b {
  display: block; margin-top: 4px; font-family: var(--font-display); font-weight: 700;
  font-size: clamp(15px, 1.4vw, 19px); letter-spacing: -.035em;
  color: var(--fig-in); font-variant-numeric: tabular-nums;
}
.wall__b > b.dn { color: var(--fig-out); }
/* each book carries its own line, the way the app draws one in the list */
.wall__b .spk { display: block; margin-top: 14px; width: 100%; height: 22px; }
.wall__b .spk path { fill: none; stroke: var(--bar-in-a); stroke-width: 2;
                     stroke-linecap: round; stroke-linejoin: round; }
.wall__b--dn .spk path { stroke: var(--bar-out-a); }
.wall__b s {
  display: block; margin-top: 14px; height: 38px; text-decoration: none;
  background: repeating-linear-gradient(to bottom,
    rgb(var(--rule-rgb) / .24) 0 1px, transparent 1px 13px);
}
/* the fan sits on something */
.wall__deck::after {
  content: ''; position: absolute; z-index: 0; left: 50%; bottom: -46px;
  width: min(1020px, 82vw); height: 120px; translate: -50% 0; pointer-events: none;
  background: radial-gradient(50% 50% at 50% 50%, rgb(var(--shade) / .16), transparent 70%);
}
/* ── the fan is the control ──────────────────────────────────────────────
   Each book is a button, so it needs a button's manners and none of a
   button's chrome. The open one comes forward out of the haze — it is the one
   being written into, and depth is how this composition says near. */
.wall__b { appearance: none; -webkit-appearance: none; border: 0; font: inherit;
           text-align: left; cursor: pointer; -webkit-tap-highlight-color: transparent;
           transition: box-shadow var(--t-ui) var(--ease-out),
                       filter var(--t-ui) var(--ease-out),
                       opacity var(--t-ui) var(--ease-out); }
.wall__b:hover      { filter: none; opacity: 1; }
.wall__b:hover      { box-shadow: 0 2px 4px rgb(var(--shade) / .10), 0 34px 54px -22px rgb(var(--shade) / .50); }
.wall__b.is-open    { filter: none; opacity: 1; z-index: 2;
                      box-shadow: 0 2px 4px rgb(var(--shade) / .10),
                                  0 34px 54px -22px rgb(var(--shade) / .50),
                                  0 0 0 2px rgb(var(--brand-rgb) / .38); }
.wall__b:focus-visible { outline: none; z-index: 4;
                      box-shadow: 0 2px 4px rgb(var(--shade) / .10),
                                  0 34px 54px -22px rgb(var(--shade) / .50),
                                  0 0 0 3px var(--canvas), 0 0 0 6px rgb(var(--brand-rgb) / .85); }
/* and it answers when you write in it */
.wall__b.lit     { box-shadow: 0 2px 4px rgb(var(--shade) / .10), 0 34px 54px -22px rgb(var(--shade) / .50), 0 0 0 2px rgb(var(--brand-rgb) / .75); }


@keyframes lastin { from { opacity: .4; translate: 0 6px; } }


@media (max-width: 1040px) { .wall__b { width: clamp(108px, 13vw, 140px); } }
/* Narrow: the fan has nowhere to go, so the two innermost books stay as a hint
   of the wall and the rest are dropped rather than smeared into each other.
   Their desktop offsets go with them — ±166px from a 195px centre puts them
   almost entirely off a 390pt screen — and because --x is set inline, the
   placement has to be restated rather than overridden. */
@media (max-width: 860px) {
  .wall__dev { width: min(248px, 62vw); margin-bottom: calc(-1 * clamp(96px, 22vw, 150px)); }
  .wall__b { display: none; }
  .wall__b--i {
    display: block; width: 104px; height: 178px; padding: 12px 12px 14px;
    border-radius: 16px 16px 6px 6px; bottom: calc(-1 * clamp(36px, 8vw, 58px));
    filter: none; opacity: 1;
  }
  
  .wall__b--i .spk, .wall__b--i s { display: none; }
  .wall__b--i > span { margin-top: 9px; font-size: 11.5px; }
  .wall__b--i > b { font-size: 15px; }
}

.panel {
  position: relative; isolation: isolate;
  border-radius: clamp(22px, 2.4vw, 42px);
  background: linear-gradient(108deg, #C4FF77 0%, #A8F63F 54%, #93EB22 100%);
  color: var(--on-lime);
  min-height: clamp(360px, 34vw, 478px);
  display: flex; flex-direction: column; justify-content: center;
  padding: clamp(40px, 4.4vw, 78px) clamp(28px, 4vw, 74px);
  padding-right: calc(var(--dev) + clamp(52px, 8vw, 148px));
  box-shadow: 0 1px 0 rgb(255 255 255 / .34) inset, 0 30px 60px -34px rgb(var(--shade) / .40);
  /* THE OVERHANG HAS TO BE CHECKED, NOT ASSUMED. It has now gone to zero twice
     — once when the close grew a field and a links row, once when it grew a
     brand lockup and a divider — and each time the device simply sank inside
     the card and the whole break-the-edge effect vanished without any visible
     error. The device is sized against a card carrying its real content, and
     the number below is the measurement, not a guess: 676px of device against
     611px of card is 32px of overhang top and bottom. */
  --dev: min(312px, 25.5vw);
}
.panel__ghost {
  position: absolute; z-index: -1; inset: 0; overflow: hidden;
  border-radius: inherit; pointer-events: none; user-select: none;
  display: flex; align-items: flex-end; justify-content: flex-end;
}
.panel__ghost b {
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(120px, 17vw, 250px); line-height: .74;
  letter-spacing: -.05em; color: var(--on-lime); opacity: .055;
  margin: 0 -.06em -.16em 0;
}

.panel h2 {
  font-family: var(--font-display); font-weight: 800; color: var(--on-lime);
  font-size: clamp(32px, 4vw, 58px); line-height: 1.04;
  letter-spacing: -.042em; margin: 0 0 16px; max-width: 15ch;
}
/* The page's marker is a LIME fill, so left alone an accent inside this
   headline paints lime on lime and the emphasised words disappear. */
.panel h2 em { background: var(--on-lime); color: #C4FF77; }
.panel .lede {
  color: color-mix(in srgb, var(--on-lime) 78%, transparent);
  font-size: clamp(15.5px, 1.2vw, 17.5px); line-height: 1.55;
  max-width: 38ch; margin: 0;
}
.panel__dev {
  position: absolute; z-index: 2; right: clamp(34px, 6vw, 122px);
  top: 50%; translate: 0 -50%; width: var(--dev);
}
.panel__dev .ph { width: 100%; }

/* there is no hand to cast it, so the light has to */
.panel__dev .ph { filter: drop-shadow(0 26px 34px rgb(12 30 4 / .30)) drop-shadow(0 4px 8px rgb(12 30 4 / .16)); }

/* one control, fused: a field and a button side by side are two decisions */
.pform {
  display: flex; align-items: center; gap: 8px; width: min(430px, 100%);
  margin-top: clamp(24px, 2.8vw, 34px); padding: 7px 7px 7px 20px;
  border-radius: var(--radius-pill); background: rgb(255 255 255 / .58);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--on-lime) 14%, transparent);
  transition: box-shadow var(--t-ui) var(--ease-out);
}
.pform:focus-within { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--on-lime) 26%, transparent),
                                  0 0 0 3px color-mix(in srgb, var(--on-lime) 22%, transparent); }
.pform input {
  flex: 1 1 auto; min-width: 0; border: 0; background: none; outline: 0;
  font: inherit; font-size: 15px; color: var(--on-lime);
}
.pform input::placeholder { color: color-mix(in srgb, var(--on-lime) 54%, transparent); }
.pform button {
  flex: none; border: 0; cursor: pointer; border-radius: var(--radius-pill);
  padding: 11px 20px; background: var(--on-lime); color: #C4FF77;
  font: inherit; font-weight: 600; font-size: 14.5px;
  transition: background var(--t-ui) var(--ease-out), transform var(--t-ui) var(--ease-out);
}
.pform button:hover { background: #1B2130; }
.pform button:active { transform: scale(.975); }
.panel .fine {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 16px;
  margin: 13px 0 0; font-size: 13px;
  color: color-mix(in srgb, var(--on-lime) 64%, transparent);
}
/* pricing lives nowhere else on the page any more, so it lives here */
.panel .fine b { font-weight: 600; color: color-mix(in srgb, var(--on-lime) 82%, transparent); }
.panel__end {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 26px;
  margin-top: clamp(26px, 3vw, 42px); padding-top: clamp(18px, 2vw, 26px);
  border-top: 1px solid color-mix(in srgb, var(--on-lime) 15%, transparent);
  font-size: 14px; color: color-mix(in srgb, var(--on-lime) 70%, transparent);
}
.panel__end a { color: inherit; }
.panel__end a:hover { color: var(--on-lime); }
.panel__end .sp { margin-left: auto; }

@media (max-width: 980px) {
  
  .panel { padding-right: clamp(28px, 4vw, 74px);
           padding-bottom: clamp(268px, 42vw, 360px); min-height: 0; }
  .panel h2 { max-width: none; }
  .panel__dev { right: 50%; translate: 50% 0; top: auto;
                bottom: clamp(-58px, -5vw, -40px); width: min(252px, 58vw); }
  .panel__end .sp { margin-left: 0; width: 100%; }
}
.panel__end nav { display: flex; flex-wrap: wrap; gap: 10px 26px; }

/* The fan opens on load, outward from the device — the wall being laid out
   rather than already there. product.js's hero sequence is keyed to
   [data-roll], which this hero does not have, so it no-ops and this is the
   only motion in the header besides the one the reader causes. */
/* NOTHING HERE MAY DECIDE WHETHER A BOOK IS VISIBLE. The entrance first ran
   `from { opacity: 0 }` with a fill, and a measurement after it should have
   settled came back at opacity 0 across all six — the whole fan gone. The
   cause was document.hidden: in a background tab the animation never starts,
   so the element sits in the fill state forever, and `backwards` fails the
   same way for the same reason. An entrance is never worth the thing it is an
   entrance to, so it moves the cards and does not reveal them. If it never
   runs, the hero is simply already laid out. */
.wall__b { animation: wallin .85s var(--ease-out) var(--d, 0s) backwards; }
  
@keyframes wallin {
  from { translate: calc(-50% + var(--x) * var(--sgn, 1) * .74) 34px; }
}
@media (prefers-reduced-motion: reduce) { .wall__b { animation: none; } }


.wall__b.is-open { z-index: 4; }
.wall__dev { z-index: 6; }
.wall__b:focus-visible { z-index: 7; }

/* ═══ THE STORES, IN THE CLOSE ═════════════════════════════════════════════
   Shape only. Apple and Google publish their badges as fixed artwork that may
   not be redrawn, so the official SVGs drop in where the glyph slot is rather
   than being imitated here. Worth saying plainly: Koonto is not on either
   store yet, so until it ships these point at the same place the email pill
   does, and the pill is the control that can actually be honoured. ───────── */
.stores { display: flex; flex-wrap: wrap; gap: 12px; margin-top: clamp(22px, 2.6vw, 30px); }
.store {
  display: inline-flex; align-items: center; gap: 12px;
  padding: 10px 20px 10px 15px; border-radius: 14px;
  background: var(--on-lime); color: #FFFFFF; text-decoration: none;
  transition: background var(--t-ui) var(--ease-out), transform var(--t-ui) var(--ease-out);
}
.store:hover { background: #1B2130; transform: translateY(-1px); }
.store:active { transform: translateY(0) scale(.985); }
.store:focus-visible { outline: 2px solid var(--on-lime); outline-offset: 3px; }
.store i {
  flex: none; width: 26px; height: 26px; border-radius: 8px;
  display: grid; place-items: center;
  border: 1px dashed rgb(255 255 255 / .38); color: rgb(255 255 255 / .74);
}
.store i svg { width: 13px; height: 13px; }
.store small {
  display: block; font-size: 9.5px; font-weight: 600; letter-spacing: .1em;
  text-transform: uppercase; color: rgb(255 255 255 / .74);
}
.store b {
  display: block; margin-top: 1px; font-family: var(--font-display);
  font-weight: 700; font-size: 16.5px; letter-spacing: -.025em;
}
@media (max-width: 420px) { .store { flex: 1 1 100%; justify-content: center; } }

/* ═══ KINDS OF BOOK — a picker, not four posters ═══════════════════════════
   Four generations of overrides had left these carrying an italic subtitle, a
   bordered chip on a bordered card inside a bordered card, and no sign at all
   that pressing one does anything. Consolidated:

     · the italic goes — a slanted line of body copy in a control reads as a
       caption from 2014, and this line is the card's actual description;
     · the chips lose their outline and become a tint, so the card has one
       border in it instead of five;
     · a chevron appears on the right, because the whole set is a tab list and
       nothing on it said so;
     · the icon tile grows to match the type it sits beside.                 */
.kind {
  grid-template-columns: 38px 1fr 18px;
  gap: 5px 14px; border-radius: 20px; padding: 17px 16px 16px 18px;
}
.kind__i { width: 38px; height: 38px; border-radius: 13px; }
.kind__i svg { width: 18px; height: 18px; }


.kind .chips { margin-top: 9px; gap: 6px; }
.kind .chips i {
  border: 0; padding: 5px 11px; font-size: 12px; font-weight: 500;
  color: var(--t700); background: color-mix(in srgb, var(--ink) 7%, transparent);
}
/* On the tinted card the chip has to lift off the tint, not sink into it. */
.kind.is-on .chips i { background: var(--card); border: 0; color: var(--t700); }


/* ═══ THE BRAND LOCKUP ═════════════════════════════════════════════════════
   The nav was setting the wordmark at 14.5px with .19em of tracking, in caps.
   That much air between letters at that size stops being a wordmark and
   becomes six letters standing near each other — it reads loose, and next to
   a 22px mark it reads unfocused as well, because neither element is clearly
   the anchor.

   The close section already had the right lockup and the nav was not using
   it: mixed case, display face, tracking pulled IN rather than pushed out, and
   the mark sized to the cap height of the word beside it so the two share a
   baseline instead of floating at their own heights. Same lockup in both
   places now, which is the other half of why it read cheap — the page had two
   different Koontos on it. */
.nav__brand { gap: 9px; align-items: center; }
.nav__mark { width: 25px; height: 25px; flex: none; }
.nav__word {
  font-family: var(--font-display); font-weight: 700;
  font-size: 18px; letter-spacing: -.022em; text-transform: none;
  color: var(--ink-hi); line-height: 1;
}
/* The mark is five loose strokes; at 25px they need a touch more weight to
   hold together next to a 700-weight word. */
.nav__mark g { stroke-width: 5.6; }
.nav__brand:hover .nav__word { color: var(--ink-hi); }


/* Four screens stacked in one device — .k is already position:absolute;inset:0,
   so they sit on top of each other and only the live one is laid out. */
.k[data-kind-screen] { display: none; }
.k[data-kind-screen].is-live { display: flex; }

/* The night band is gone — tried, not wanted. "What you can keep" is back on
   the page's own tint. Its token block and card overrides went with it rather
   than sitting here as another dead layer for the next person to trip over. */

/* ═══ TWO BY TWO ══════════════════════════════════════════════════════════
   The lead-plus-three-supports composition is gone. Turned into a real page it
   read as one-up-three-down with the three squeezed to a third of the width
   each — narrow enough that the screens inside them shrank and the sentences
   went to five lines. A claim that needs five lines is not the lead's support,
   it is a casualty of the lead.

   Four equal cards, two by two. Every claim gets the same width, every screen
   gets the same size, and the reading order is the order they are written in.
   The 880px rule below is the one doing the work now. */

/* ═══ THE NOTEBOOK, DIGITALISED ════════════════════════════════════════════
   The ledger hero returns, and this time the conversion is the design rather
   than a thing the copy asserts. Three deliberate moves:

     1. THE PAPER HAS TO BE PAPER. Warm stock, blue ruling, a doubled red
        margin, a bound edge at the left and the cut edges of the sheets below.
        A ruled div is not a cash book; the things that make it one are the
        margin rule and the folio, and both are cheap.
     2. THE SENTENCE IS ONE SENTENCE. Nothing else in the copy block — no
        lede, no marks list, no paragraph explaining the demonstration that is
        running three inches to the right of it.
     3. THE SCREEN SITS ON THE PAGE, not beside it, and shares its ruling. A
        line that begins in ink on the left continues as a row divider on the
        phone, which is the whole claim in one detail. ────────────────────── */
.hero--ledger .book { --row: 46px; padding-bottom: calc(var(--row) * 1.4); }

/* ── the sheet ──────────────────────────────────────────────────────────
   The old wash was a 2% breath of warmth. Paper is warmer than that, and the
   page has to look like stock before the ruling reads as ruling. */
:root[data-theme='light'] .hero--ledger .book::after {
  background:
    linear-gradient(176deg, rgba(255,247,229,.92), rgba(255,251,243,.66) 52%, rgba(255,253,248,.40)),
    radial-gradient(120% 90% at 82% 0%, rgba(255,240,208,.55), transparent 62%);
}
/* and at night it is the same page under a lamp, not a different object */
:root[data-theme='dark'] .hero--ledger .book::after {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: radial-gradient(120% 90% at 78% 0%, rgb(var(--brand-rgb) / .055), transparent 60%);
}

/* ── the one sentence ───────────────────────────────────────────────────
   It carries the whole pitch, so it gets the whole measure and none of the
   company it used to keep. */
.hero--ledger .book__say { max-width: 46rem; padding-top: calc(var(--row) * .34); }
.hero--ledger h1 {
  font-size: clamp(32px, 4.1vw, 56px); line-height: 1.04;
  letter-spacing: -.038em; margin: 0 0 calc(var(--row) * .52); max-width: 20ch;
}
.hero--ledger .hero__cta { margin-top: 0; }
.hero--ledger .book__rows { margin-top: calc(var(--row) * .8); max-width: 44rem; }

/* ── the cue ────────────────────────────────────────────────────────────
   Four words under the device instead of a sentence in the copy: the reader
   needs a door, and a door is not a pitch. */
/* superseded — the cue moved out from under the cropped device and up under
   the button that opens it; the centring came with it and had to be undone */

/* ── the row that leaves the screen ─────────────────────────────────────
   Set from JS: the new line is placed where it sat on the phone, at the
   phone's scale, and released. It is the only animation in the hero and it is
   the entire concept — paper on one side, glass on the other, and a line
   crossing between them. */
.book__r--flew { will-change: transform, opacity; }
@media (prefers-reduced-motion: reduce) {
  .book__r--flew { transition: none !important; transform: none !important; opacity: 1 !important; }
}

/* ── ONE RULING, TWO SURFACES ────────────────────────────────────────────
   The point of the whole hero is that the paper and the screen are the same
   book, and the detail that says so is the line spacing: a rule that starts
   in ink on the left should continue as a row divider on the glass.

   Measured rather than eyeballed. The app's entry row is 56pt against its
   375pt reference, but with its padding and hairline the drawn pitch came out
   at 49.6px on a 322px device — a ratio of 0.15404, so a device of
   row ÷ 0.15404 puts the two on the same rhythm at any width. The device is
   sized FROM the ruling now instead of being picked and hoped for. */
/* 55.45, not 46, because the DEVICE was asked to be 360 and the device is
   sized from the ruling: 360 ÷ 6.492 = 55.45. Growing the row rather than the
   phone alone is the only way to make it bigger and keep the join — a rule
   that starts in ink on the paper still arrives as a divider on the glass.
   Measured after: paper pitch 55.5, glass pitch 54.0.

   The growth is held back until there is room for it. A plain 3.851vw reached
   360 at 1440 but also made the device 275px at 1100, where it ran 21px into
   the store badges — measured, and new: the old curve left 14px of clearance
   there. So the row is interpolated between the two widths that matter rather
   than scaled from zero: it matches the old value exactly at 1280 (40.96px),
   reaches 55.45 at 1440, and holds. Below 1280 the clamp floor returns the
   device to the size it has always been, so nothing narrower than that moved
   at all. Below 1100 a separate rule takes over and derives the row from the
   phone's own width, so none of this reaches the stacked layout. */
.hero--ledger .book { --row: clamp(37px, calc(9.056vw - 74.96px), 55.45px); }
.hero--ledger .book__dev { width: calc(var(--row) * 6.492); }

/* The closing figure belongs in the balance column and at the ledger's scale.
   At 52px it was spilling back across the in/out column and colliding with
   the device; a total shouts by being last, not by being large. */
.hero--ledger .book__tot > b { font-size: clamp(26px, 3vw, 42px); }
.hero--ledger .book__tot { height: calc(var(--row) * 1.75); }

/* The door, under the control that opens it. Four words and a verb — this is
   operating instruction, not pitch, which is why it is 12.5px and grey and
   sits under the button rather than in the sentence. */
.book__cue {
  margin: 16px 0 0; font-size: 12.5px; color: var(--muted); letter-spacing: .01em;
}
.book__cue b { font-weight: 600; color: var(--brand-text); }

/* ── phase, not just pitch ───────────────────────────────────────────────
   Matching the ruling's PITCH to the phone's rows gets the rhythm right and
   still leaves the two grids offset from each other — measured at 4.46px,
   under a tenth of a row, which is exactly the distance at which a thing looks
   not-quite-right without looking wrong. The device is nudged by that much so
   a rule running across the paper meets the first entry on the glass.

   Expressed in row units, so it survives the clamp: the ruling and the device
   scale together, and the offset scales with them. */
.hero--ledger .book__dev { translate: 0 calc(var(--row) * -0.097); }
.book__cue { text-align: left; }

/* ── the narrow page keeps its phone ─────────────────────────────────────
   A rule from when the device was scenery took it off the page entirely below
   1100px: `.book__dev { display: none }`. It is the interaction now — the
   thing the single sentence is asking you to try — so hiding it hands most of
   the real traffic a hero with no product in it and no way to write anything.

   It moves under the ledger instead of vanishing, and the ruling is derived
   from the device rather than the other way round, so the paper and the glass
   keep the same pitch at every width rather than only on a desktop. */
@media (max-width: 1099px) {
  .hero--ledger .book { --row: calc(min(304px, 74vw) / 6.492); }
  .hero--ledger .book__dev {
    display: block; position: static; translate: none;
    width: min(304px, 74vw); margin: clamp(26px, 5vw, 46px) auto 0;
  }
  .hero--ledger .book__rows { max-width: none; }
  .hero--ledger .book__say { max-width: none; }
}
/* The narrow-ledger rule that used to live here was DEAD. It set
   `.hero--ledger .book__r { grid-template-columns: … }` inside a media query,
   and the one-grid rule further down the file sets the same property at the
   same specificity — media queries do not raise specificity, so the later rule
   won at every width and the phone got desktop columns. Four fixed tracks in
   420px of paper left the entry column with nothing, so every name wrapped to
   three lines inside a row that is exactly one --row tall, and the rows drew
   on top of each other. It is rewritten at the end of the file, after the rule
   that was beating it. */

/* ═══ THE CLOSE TAKES THE SKY ══════════════════════════════════════════════
   The lime card is gone; the card now carries the same two skies the hero
   does — the day one on the day side, the night one at night. That is a
   bigger change than a background swap, because the lime was the one surface
   on this page that did NOT flip, and every tone on it was mixed from
   --on-lime for that reason. A near-black headline on the night sky would be
   a card with nothing on it.

   So the close becomes a normal themed surface: the ink follows the page, and
   the two pills that used to be fixed dark now invert with it. The gradient
   stays underneath each image as the fallback, so a card that fails to load
   still looks deliberate. ────────────────────────────────────────────────── */
.panel {
  background:
    linear-gradient(180deg, transparent 0 46%, rgb(var(--ground-rgb) / .34) 74%, rgb(var(--ground-rgb) / .62) 100%),
    url('../img/hero-sky-light.webp?v=2f9c7d5a') center 22% / cover no-repeat,
    linear-gradient(168deg, #E8F7D2 0%, #F4FBEA 46%, var(--canvas) 100%);
  color: var(--ink);
  box-shadow: 0 1px 0 rgb(255 255 255 / .30) inset, 0 30px 60px -34px rgb(var(--shade) / .40);
}
:root[data-theme='dark'] .panel {
  background:
    linear-gradient(180deg, transparent 0 44%, rgb(var(--ground-rgb) / .42) 72%, rgb(var(--ground-rgb) / .72) 100%),
    url('../img/hero-sky-dark.webp?v=b69762d2') center 22% / cover no-repeat,
    linear-gradient(168deg, #123A20 0%, #0D1A22 48%, var(--canvas) 100%);
  box-shadow: 0 1px 0 rgb(255 255 255 / .06) inset, 0 30px 60px -34px rgb(0 0 0 / .55);
}
.panel h2{ color: var(--ink-hi); }
.panel h2 em { background: var(--lime); color: var(--on-lime); }
.panel .lede { color: var(--t700); }
.panel .fine { color: var(--muted); }
.panel .fine b { color: var(--t900); }
.panel__end { color: var(--muted); border-top-color: rgb(var(--lift-rgb) / .14); }
.panel__end a:hover { color: var(--ink-hi); }
.panel__ghost b { color: var(--ink-hi); opacity: .06; }

/* the one control, on a ground that is now a photograph */
.pform {
  background: rgb(var(--stage-rgb) / .72);
  box-shadow: inset 0 0 0 1px rgb(var(--lift-rgb) / .12);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
}
.pform:focus-within { box-shadow: inset 0 0 0 1px rgb(var(--lift-rgb) / .22),
                                  0 0 0 3px rgb(var(--brand-rgb) / .32); }
.pform input { color: var(--ink); }
.pform input::placeholder { color: var(--muted); }
.pform button { background: var(--brand); color: var(--on-brand); }
.pform button:hover { background: var(--brand-hover); }

/* the badges invert with the page: a dark pill on the day sky, a light one on
   the night sky — a near-black pill on near-black cloud is a hole, which is
   the same fault the nav links had over the hero */
.store { background: var(--ink-hi); color: var(--canvas); box-shadow: var(--e2); }
.store:hover { background: var(--t900); }
.store i { border-color: rgb(var(--ground-rgb) / .42); color: rgb(var(--ground-rgb) / .74); }
.store small { color: rgb(var(--ground-rgb) / .74); }
.store:focus-visible { outline: 2px solid var(--ink-hi); outline-offset: 3px; }

/* ═══ THE MARK GETS ITS TILE ═══════════════════════════════════════════════
   The wordmark was fixed last time; the mark was not. Five loose strokes
   floating next to a 700-weight word have no mass, and no amount of stroke
   weight gives them any — what they were missing was the thing the app
   already has and the site was ignoring: the icon is a MARK ON A TILE. The
   favicon on this very page draws it that way (rx 18 on an 80 square).

   So the nav wears the app icon, which is the strongest branding move
   available here because it is not a new idea — it is the one the product
   already ships, finally used. A reader who installs the app sees the same
   object on their home screen.

   The tile is not literal-fixed, though: the app's own #0B0D16 ground would
   vanish into a night page. It takes the page's strongest ink in light and a
   lifted panel at night, so it reads as an icon in both rooms. */
.nav__brand { gap: 11px; }
.nav__mark {
  width: 30px; height: 30px; padding: 5.5px; border-radius: 9px;
  background: var(--ink-hi); color: var(--canvas);
  box-shadow: 0 1px 2px rgb(var(--shade) / .18);
  transition: transform var(--t-ui) var(--ease-out);
}
:root[data-theme='dark'] .nav__mark {
  background: rgb(255 255 255 / .09); color: #FFFFFF;
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / .14);
}
.nav__brand:hover .nav__mark { transform: rotate(-6deg) scale(1.04); }
.nav__mark g { stroke-width: 6.4; }
.nav__word { font-size: 18.5px; letter-spacing: -.025em; }


/* ── the page is stock, and the phone is lying on it ─────────────────────
   Two details, and both are the difference between a drawing of a notebook
   and a notebook.

   GRAIN. Ruled lines on a flat fill read as a table with a background colour.
   Paper has fibre in it, and about three per cent of it is enough — below
   noticing, above nothing, which is the same budget the warm wash runs on.
   Generated rather than fetched: an image request for texture this faint is a
   request this page should not be making.

   A CAST SHADOW. The device sits ON the page, and until now it proved that by
   overlapping — which a flat sticker also does. The light on this sheet comes
   from the upper right (see the wash), so the shadow falls down and to the
   left, and it is warm because it is falling on paper. */
.book__grain {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  opacity: .5; mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)' opacity='.055'/%3E%3C/svg%3E");
}
:root[data-theme='dark'] .book__grain { mix-blend-mode: screen; opacity: .26; }

.hero--ledger .book__dev .ph {
  filter: drop-shadow(-10px 16px 20px rgb(122 96 40 / .17))
          drop-shadow(-3px 5px 7px rgb(122 96 40 / .12));
}
:root[data-theme='dark'] .hero--ledger .book__dev .ph {
  filter: drop-shadow(-10px 18px 24px rgb(0 0 0 / .55))
          drop-shadow(-3px 5px 8px rgb(0 0 0 / .40));
}

/* --muted has now failed on a photographic ground three times: the hero's
   marks, the hero's hint, and these two. It is the faintest rung that works on
   a flat canvas, and the close is not a flat canvas any more. Measured on the
   image itself: the fine print was 3.27 and the footer links 4.74. */
.panel .fine { color: var(--t700); }
.panel__end { color: var(--t700); }

/* ── a day's net is not a balance ────────────────────────────────────────
   The day rows — TODAY · Net +14,350, 12 SEPT 2026 · Net −2,100 — were laid
   out as `1fr auto`, which right-aligns the figure to the row's edge and lands
   it under BALANCE. It is not a balance. It is the sum of the movements on
   that day, so it belongs under IN / OUT with the movements it sums, and the
   balance column on a day divider should be empty, because a divider does not
   carry one.

   Same four columns as an entry row, so the figures line up with the ones they
   are the total of rather than with the column next door. */
.hero--ledger .book__day {
  grid-template-columns: 3.4rem 1fr auto 74px;
  gap: clamp(10px, 1.4vw, 22px);
}
.hero--ledger .book__day > span { grid-column: 1 / 3; }
.hero--ledger .book__day > b   { grid-column: 3; justify-self: end; }

/* ── ONE GRID FOR THE WHOLE LEDGER ───────────────────────────────────────
   The header row and the entry rows were on different templates —
   `3.6rem 1fr 7.4rem 7.2rem` for the head and `3.4rem 1fr auto 74px` for the
   rows — so IN / OUT sat 40px left of the amounts it labels. Worse, `auto`
   means every row sizes its own third column to its own content, so no two
   rows agreed either; they only looked aligned because the figures happened to
   be similar widths.

   A ledger is a table or it is a list. Four fixed columns, declared once, used
   by the head, the day dividers, the entries and the brought-forward line.
   The in/out column is sized for −11,000 at its largest and the balance column
   for 150,007, both with room. */
.hero--ledger {
  --led-t: 3.4rem; --led-io: 5.8rem; --led-bal: 4.9rem;
  --led: var(--led-t) minmax(0, 1fr) var(--led-io) var(--led-bal);
  --led-gap: clamp(10px, 1.4vw, 22px);
}
.hero--ledger .book__hd,
.hero--ledger .book__r,
.hero--ledger .book__day,
.hero--ledger .book__bf {
  grid-template-columns: var(--led); gap: var(--led-gap);
}
.hero--ledger .book__day > span { grid-column: 1 / 3; }
.hero--ledger .book__day > b   { grid-column: 3; justify-self: end; }

/* ── the money rules follow the columns ──────────────────────────────────
   The single vertical rule was positioned at a hard 7.2rem from the right,
   which was a column boundary under the OLD template and is nothing under the
   new one. A ruled column divider that does not divide the columns is worse
   than none — it tells the eye the table is somewhere it is not.

   Both rules are now derived from the same track widths the rows use, so they
   sit in the gutters by construction: half a gap outside the balance column,
   and half a gap outside in/out. */
.hero--ledger .book__rows::after { display: none; }
.hero--ledger .book__rows {
  background-image:
    linear-gradient(rgb(var(--rule-rgb) / .30), rgb(var(--rule-rgb) / .30)),
    linear-gradient(rgb(var(--rule-rgb) / .30), rgb(var(--rule-rgb) / .30));
  background-size: 1px calc(100% - var(--row) * 1.1), 1px calc(100% - var(--row) * 1.1);
  background-position:
    right calc(var(--led-bal) + var(--led-gap) / 2) top calc(var(--row) * .55),
    right calc(var(--led-bal) + var(--led-io) + var(--led-gap) * 1.5) top calc(var(--row) * .55);
  background-repeat: no-repeat;
}

/* ═══ THE DAY SIDE IS STOCK, NOT WEATHER ═══════════════════════════════════
   The photograph comes off the light hero. It was doing two jobs badly: it
   made the top of the sheet a picture of a sky, and it made every small label
   on the page a contrast problem to be solved against pixels rather than
   against a colour (that is the whole reason --muted had to be abandoned here
   four separate times).

   What a cash book is made of is paper, so the day side gets paper: warm
   ivory stock cooling into the page's own canvas at the foot, and nothing
   else behind the ruling. Night keeps its sky — a notebook at night is lit by
   something, and the dark hero was never the complaint. */
.hero--ledger .book {
  background: linear-gradient(180deg, #FDFBF5 0%, #FAF8F1 54%, #F7F5EC 100%);
}
:root[data-theme='dark'] .hero--ledger .book {
  background:
    /* A SCRIM ACROSS THE TOP BAND. The night sky is brightest dead centre at
       the top, which is exactly where the folio header runs — measured against
       those pixels the header came out at 1.70:1 and the eyebrow at 3.32, the
       same fault the nav links had over this same photograph. Light ink on a
       light cloud cannot be fixed by making the ink lighter; the ground has to
       come down. Measured again after: 8.9 and 6.4. */
    linear-gradient(180deg, rgb(var(--ground-rgb) / .72) 0,
                            rgb(var(--ground-rgb) / .40) 130px, transparent 280px),
    /* THE BOTTOM EDGE OF THE PHOTOGRAPH, DISSOLVED.
       The sky is sized to the page's WIDTH (`100% auto`), so its height is
       56.28% of that width — 810px on a desktop, where the edge lands near the
       foot of the sheet and nobody sees it, and 180px on a 320 phone, where it
       lands directly under the headline as a hard horizontal step across the
       hero. Swept at 320, 375 and 430 it was the most visible thing on the
       night page.

       The fade is given the image's own box rather than the element's: the
       image is 56.28vw tall because .book is full-bleed, so `100% 56.28vw`
       tracks it at every width and the photograph ends in the ground it sits
       on instead of on a line. */
    linear-gradient(180deg, transparent 0 54%, rgb(var(--ground-rgb) / .55) 82%,
                            #0E1B24 100%) center top / 100% 56.28vw no-repeat,
    url('../img/hero-sky-dark.webp?v=b69762d2') center top / 100% auto no-repeat,
    linear-gradient(180deg, #0E1B24 0%, var(--canvas) 74%);
}
/* With no photograph under it the wash goes back to full strength — it is the
   light falling on the sheet now rather than a veil over a picture. From the
   upper right, which is the direction the device's cast shadow already
   assumes. */
:root[data-theme='light'] .hero--ledger .book::after {
  background:
    radial-gradient(130% 100% at 86% -8%, rgba(255,247,224,.72), transparent 62%),
    linear-gradient(174deg, rgba(255,252,243,.50), rgba(255,255,255,.18) 58%, rgba(244,243,238,.42));
}

/* ── the close goes plain ────────────────────────────────────────────────
   Lime was rejected, the sky was rejected. What is left is the surface the
   rest of the page already uses for a card — so the close stops competing for
   attention with the thing above it and lets the one control on it be the
   loudest object in the section. */
.panel, :root[data-theme='dark'] .panel {
  background: var(--card);
  box-shadow: 0 1px 0 rgb(var(--lift-rgb) / .06) inset,
              0 30px 60px -34px rgb(var(--shade) / .30);
}
.panel { border: 1px solid var(--divider); }

/* ── the ledger's faintest usable rung ───────────────────────────────────
   Fourth time --muted has failed on a photographic ground, so this stops being
   a series of patches and becomes a rule: on this hero the ground is weather,
   not flat canvas, and --muted is not a rung that survives it. Measured on the
   composited sky-plus-wash — the column heads came out at 4.18 and the day
   dividers at 4.13, with the folio, the cue and the entry sub-labels all
   sitting within a tenth of the line.

   Every small label in the ledger moves up one rung together, so the next
   thing added here inherits a colour that works instead of one that has to be
   caught. */
.hero--ledger .book__folio,
.hero--ledger .book__hd i,
.hero--ledger .book__hd span,
.hero--ledger .book__hd b,
.hero--ledger .book__hd u,
.hero--ledger .book__day,
.hero--ledger .book__r i,
.hero--ledger .book__r em,
.hero--ledger .book__tot > span,
.hero--ledger .book__cue { color: var(--t700); }
.hero--ledger .book__folio i { color: var(--t700); }

/* ── the dead space, moved by exactly one ruled line ─────────────────────
   Measured at 1900: the device sat 161px below the top of the sheet and only
   62 above the bottom, so all the slack pooled in the top-right corner and
   read as a hole beside the headline. The copy column was already centred
   (69 above, 65 below) — it was only the device that was hanging low.

   Raised by one --row exactly. Not by the 50px that would split the slack
   evenly, because this is a ruled page and the device's rows are on the
   ruling: moving it a whole number of lines is the only move that keeps the
   phase at zero. 115 above and 108 below, and the grid is untouched. */
.hero--ledger .book__dev { bottom: calc(var(--row) * 2.25); }

/* ── the sheet starts at the top of the window ───────────────────────────
   The nav was sitting on page canvas with the paper beginning underneath it,
   so the top of the site read as a bar, then a second bar, then the page. The
   sheet runs under the chrome now and the nav floats on it — which makes the
   folio rail what it always wanted to be: not a second toolbar, but the
   letterhead of the page it is printed on. */
.hero--ledger { margin-top: calc(-1 * var(--navh)); padding-top: 0; }
/* The padding belongs to the SHEET, not to the section: put it on the section
   and the sheet still begins below the chrome, which is the thing being fixed.
   The page starts at the top of the window and the nav is printed on it. */
.hero--ledger .book { padding-top: calc(var(--navh) + var(--row) * .34); }
/* The eyebrow was the one label left on --muted when the rest moved up a rung;
   on the night sky it came out at 4.44 against a 4.5 bar. Missing by six
   hundredths is still missing. */
.hero--ledger .eyebrow { color: var(--t700); }

/* ═══ THE MARK COMES OUT OF ITS BOX ════════════════════════════════════════
   The tile was doing the app's job on the web's page. In the app the icon has
   to be a tile, because a home screen gives every app the same square and the
   ground behind it is a photograph nobody chose. A website has neither
   problem: the page's own ground is already the right colour, so the box is a
   second shape competing with the one it contains — and at 30px with 5.5px of
   padding the glyph itself was 19px, smaller than the word beside it.

   Frame off, and the whole 34px goes to the mark. It reads as a drawn object
   now rather than as an app icon pasted onto a page. The stroke comes down a
   touch because the glyph got bigger and the weight scaled with it. */
.nav__mark{
  width: 34px; height: 34px; padding: 0; border-radius: 0;
  background: none; box-shadow: none; color: var(--ink-hi);
}
:root[data-theme='dark'] .nav__mark{
  background: none; box-shadow: none; color: var(--ink-hi);
}
.nav__mark g{ stroke-width: 5.4; }
.nav__brand { gap: 9px; }

.nav__brand:hover .nav__mark { transform: rotate(-8deg) scale(1.06); }

/* ═══ FEWER LINES, MORE PAPER ══════════════════════════════════════════════
   The sheet had five separate line systems on it at once and only one of them
   was the book's own ruling:

     · the ruling                          — the page
     · a grey band behind the column heads
       with a doubled rule under it        — a spreadsheet header
     · a 2px rule above the total          — a UI divider
     · three stacked hairlines at the foot — a drawing of page edges
     · a doubled red margin at the left    — two lines where a book has one

   Everything but the ruling comes off. Nothing is lost by it, because the
   ruling was already drawing a line in each of those places: the rows are all
   exactly one --row tall, so a rule falls at the bottom of the column heads
   and another at the top of the total by construction. The page rules itself,
   which is the whole point of ruled paper. */
.hero--ledger .book__hd { background: none; box-shadow: none; }
.hero--ledger .book__hd::before { display: none; }
.hero--ledger .book__tot { border-top: 0; }
.hero--ledger .book__edge { display: none; }
/* one red margin, not a pair */
.hero--ledger .book::before { width: 0; border-right-width: 0; }
/* the letterhead rule joins the ruling system instead of being a UI divider
   drawn in --lift (which on paper is white, so it was a line you could see
   the absence of rather than a line) */
.hero--ledger .book__folio { border-bottom-color: rgb(var(--rule-rgb) / .26); }

/* ── the ruling runs on after the writing stops ──────────────────────────
   A ruled page does not stop being ruled where the entries end. The ruling was
   clipped to the table, so the sheet went blank under the total and the foot
   of the hero read as white space that had been left over rather than as the
   rest of the page.

   It runs to the foot of the sheet now (.book clips it) and the device lies
   across it, which is the one thing that proves the phone is ON the paper and
   not beside it. Phase is untouched: the gradient's origin is still the top of
   the table, so every rule still falls on a row boundary. */
.hero--ledger .book__rows::before {
  /* exactly the sheet's own foot: .book's padding-bottom is 1.4 rows, so the
     ruling reaches the bottom edge of the paper and not one pixel past it,
     and it fades across that tail so the page dissolves instead of being
     guillotined mid-rule */
  bottom: calc(var(--row) * -1.4);
  background: repeating-linear-gradient(
    to bottom, transparent 0 calc(var(--row) - 1px),
    rgb(var(--rule-rgb) / .26) calc(var(--row) - 1px) var(--row));
}
/* and so do the two money columns — pre-printed stationery is ruled down the
   whole page, which is exactly what makes it stationery rather than a table */
.hero--ledger .book__rows { background-image: none; }
.hero--ledger .book__rows::after {
  content: ''; display: block; position: absolute; z-index: -1; pointer-events: none;
  left: 0; right: 0; top: 0; bottom: calc(var(--row) * -1.4); width: auto;
  background:
    linear-gradient(rgb(var(--rule-rgb) / .22), rgb(var(--rule-rgb) / .22))
      right calc(var(--led-bal) + var(--led-gap) / 2) top 0 / 1px 100% no-repeat,
    linear-gradient(rgb(var(--rule-rgb) / .22), rgb(var(--rule-rgb) / .22))
      right calc(var(--led-bal) + var(--led-io) + var(--led-gap) * 1.5) top 0 / 1px 100% no-repeat;
}

/* ── the sheet has an edge ───────────────────────────────────────────────
   The stacked hairlines are gone and the paper was fading into the canvas
   instead, which meant it stopped being a sheet somewhere around the last
   forty pixels and nobody could say where. A page does not fade. It ends,
   and the thing under it takes the shadow.

   So the stock stays stock all the way down — no wash to grey at the foot —
   and the sheet gets one edge and the shadow it casts. One line where three
   were drawing the same idea badly, and the ruling now runs to that edge the
   way ruling does on paper. */
.hero--ledger {
  box-shadow: 0 1px 0 rgb(var(--shade) / .10),
              0 22px 40px -26px rgb(var(--shade) / .34);
}
:root[data-theme='dark'] .hero--ledger {
  box-shadow: 0 1px 0 rgb(255 255 255 / .07),
              0 22px 44px -26px rgb(0 0 0 / .60);
}

/* ═══ THE FOUR KINDS BECOME FOUR BOOKS ═════════════════════════════════════
   This section says "a book for anything money passes through" and then
   showed four white rectangles with pills in them. The claim is about books;
   the control was about cards. Every other part of this page has been moved
   onto the notebook idea and this was the last thing on it still drawn as a
   web component.

   So they are books. Cloth covers in one stock — because a shop buys a pack
   of identical counter books, not four designer objects — told apart by the
   colour of the tape on the spine and by what is written on the label, which
   is exactly how they are told apart on a real shelf. The cut edges of the
   pages show at the fore-edge. Choosing one slides it out of the stack toward
   the phone, and the phone opens it.

   The covers are FIXED objects, like the marker and the receipt tape: they do
   not invert, so every colour on them — label, ink, chips, stamp — is
   declared here rather than inherited from the page. That is the rule this
   page has had to learn four times. */
.pick__list { gap: 9px; }

.kind {
  --cloth: #24322C; --cloth-2: #18231E; --tape: #C7452F;
  position: relative;
  display: grid; grid-template-columns: 36px minmax(0, 1fr); gap: 9px 13px;
  align-items: start; text-align: left; cursor: pointer;
  padding: 15px 20px 15px 32px;
  border: 0; border-radius: 3px 11px 11px 3px;
  background:
    linear-gradient(104deg, rgb(255 255 255 / .05), transparent 46%),
    linear-gradient(180deg, var(--cloth) 0%, var(--cloth-2) 100%);
  box-shadow: 0 1px 2px rgb(var(--shade) / .22), 0 14px 26px -18px rgb(var(--shade) / .55);
  transition: transform .42s var(--ease-out), box-shadow .42s var(--ease-out);
}
.kind[data-tape='a'] { --tape: #C7452F; }
.kind[data-tape='b'] { --tape: #35699B; }
.kind[data-tape='c'] { --tape: #4F8140; }
.kind[data-tape='d'] { --tape: #B2802C; }
/* On the night page a dark cover on a dark ground has no edge at all, so the
   cloth gets a lift outline — the same fix the night cards get, for the same
   reason. */
:root[data-theme='dark'] .kind {
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / .11),
              0 1px 2px rgb(0 0 0 / .5), 0 16px 30px -18px rgb(0 0 0 / .7);
}

/* the spine, and the coloured tape down it */
.kind::before {
  content: ''; position: absolute; z-index: 1; left: 0; top: 0; bottom: 0;
  width: 17px; border-radius: 3px 0 0 3px; border: 0;
  background:
    linear-gradient(90deg, rgb(0 0 0 / .34) 0, rgb(0 0 0 / 0) 58%, rgb(255 255 255 / .09) 100%),
    var(--tape);
  transition: none;
}
/* the cut edges of the pages, at the fore-edge where a closed book shows them */
.kind::after {
  content: ''; display: block; position: absolute; z-index: 0;
  right: -4px; top: 7px; bottom: 7px; width: 5px;
  border-radius: 0 2px 2px 0; border: 0; inset-inline-start: auto;
  background: repeating-linear-gradient(to bottom,
    #F2ECDC 0 2px, #D9D0B9 2px 3px);
  box-shadow: 0 1px 2px rgb(var(--shade) / .28);
}

/* the stamp the books wear on the phone, printed on the cover */
.kind .kind__i {
  grid-row: 1 / span 2; width: 36px; height: 36px; border-radius: 9px;
  background: rgb(255 255 255 / .14); color: rgb(255 255 255 / .92);
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / .18);
}
.kind .kind__i svg { width: 18px; height: 18px; }

/* the label every counter book has glued to its cover */
.kind__lab {
  grid-column: 2; align-self: start;
  background: linear-gradient(180deg, #FCF8EC 0%, #F4EEDD 100%);
  border-radius: 2px; padding: 8px 11px 9px;
  box-shadow: 0 1px 2px rgb(0 0 0 / .30), inset 0 0 0 1px rgb(0 0 0 / .06);
}
.kind__lab b {
  display: block; font-family: var(--font-display); font-weight: 700;
  font-size: 16.5px; letter-spacing: -.02em; color: #1A1C19;
}
/* the red rule a label is printed with, under the name it carries */
.kind__lab em {
  display: block; margin-top: 4px; padding-top: 4px;
  border-top: 1px solid rgb(199 69 47 / .34);
  font-style: italic; font-size: 12.8px; line-height: 1.35; color: #5B5A50;
}
/* printed on the cloth, not on the label — what this book is used for */
.kind .chips { grid-column: 2; margin: 0; gap: 5px; }
.kind .chips i {
  padding: 3px 9px; font-size: 11.5px; font-weight: 500;
  background: rgb(255 255 255 / .07); color: rgb(255 255 255 / .80);
  border: 1px solid rgb(255 255 255 / .13); border-radius: var(--radius-pill);
}

/* ── choosing one takes it off the shelf ─────────────────────────────────
   Out of the stack and toward the phone, which is the only motion that says
   "this is the one that is open" without painting anything a different
   colour. The tape widens, the stamp lights, and the shadow deepens because
   the book is now standing proud of the three it was stacked with. */
.kind:hover { transform: translateX(7px); box-shadow: 0 2px 4px rgb(var(--shade) / .24), 0 20px 34px -18px rgb(var(--shade) / .6); }
.kind:active { transform: translateX(7px) scale(.995); }
.kind.is-on, .kind.is-on:hover {
  background:
    linear-gradient(104deg, rgb(255 255 255 / .08), transparent 46%),
    linear-gradient(180deg, #2B3A33 0%, #1C2823 100%);
  border-color: transparent;
  transform: translateX(22px);
  box-shadow: 0 3px 6px rgb(var(--shade) / .26), 0 28px 44px -20px rgb(var(--shade) / .62);
}
:root[data-theme='dark'] .kind.is-on {
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / .16),
              0 3px 6px rgb(0 0 0 / .55), 0 28px 46px -20px rgb(0 0 0 / .78);
}
.kind.is-on::before { width: 22px; }
.kind.is-on .kind__i { background: var(--brand); color: var(--on-brand); box-shadow: none; }
.kind.is-on .kind__lab { box-shadow: 0 2px 5px rgb(0 0 0 / .34), inset 0 0 0 1px rgb(0 0 0 / .07); }
.kind.is-on .chips i { background: rgb(255 255 255 / .11); border-color: rgb(255 255 255 / .19); color: #FFF; }
/* the old outline / tint / beak states are all superseded by the object */
.kind.is-on::after { display: block; }
@media (prefers-reduced-motion: reduce) {
  .kind, .kind:hover, .kind.is-on { transition: none; }
}

/* The stack needs room on its right for the book that steps out of it — at
   EVERY width, not only on a desktop. Scoped to ≥900 it left the 22px slide
   with nowhere to go on a phone and pushed the document 6px wider than the
   viewport, which is a horizontal scrollbar on the whole page. */
.pick__list { padding-right: 26px; }

/* ── what is on the glass ────────────────────────────────────────────────
   The screen changes and nothing tells the reader what they are now looking
   at. One line under the device, rewritten with the screen, turns a slideshow
   back into a demonstration. */
.pick__show { flex-direction: column; align-items: center; gap: 18px; }
.pick__cap {
  margin: 0; max-width: 34ch; text-align: center;
  font-size: 13.5px; line-height: 1.5; color: var(--t700);
}
.pick__cap span {
  display: inline-block;
  transition: opacity .2s var(--ease-out), transform .28s var(--ease-out);
}
.pick__cap.swap span { opacity: 0; transform: translateY(5px); }

/* the heading moved into the left column, so the shelf is one object and the
   phone has something to stand beside rather than a quarter of empty section */
.pick__col { display: grid; align-content: start; }
.pick__col > .kicker { margin-top: 0; }
.pick__col > .lede { margin-bottom: clamp(26px, 3vw, 40px); }
@media (min-width: 900px) { .pick { margin-top: 0; align-items: center; } }
/* the cloth: the same fibre the sheet in the hero is printed on, at a weight
   that reads as bookbinding rather than as noise */
.kind {
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)' opacity='.07'/%3E%3C/svg%3E"),
    linear-gradient(104deg, rgb(255 255 255 / .05), transparent 46%),
    linear-gradient(180deg, var(--cloth) 0%, var(--cloth-2) 100%);
}
.kind.is-on, .kind.is-on:hover {
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)' opacity='.07'/%3E%3C/svg%3E"),
    linear-gradient(104deg, rgb(255 255 255 / .08), transparent 46%),
    linear-gradient(180deg, #2B3A33 0%, #1C2823 100%);
}
/* The split was 1.04/.96 when the left column held only the books. It holds
   the heading too now, and at half the page that heading broke to three
   lines while the right column spent 230px of its 500 on nothing — the phone
   is 272 wide. Weight it to the side doing the work. */
@media (min-width: 900px) { .pick { grid-template-columns: 1.3fr .7fr; } }

/* ═══ THE HERO, PUSHED FURTHER ═════════════════════════════════════════════
   Three moves, and every one of them is the same sentence: this is one book
   written on two surfaces.

   1 · THE MARKER IS DRAWN, NOT PRINTED
   The lime on "only it adds itself up" is the one mark on this page that is
   supposed to have been made by a hand. It arrived fully formed, which is the
   one thing a marker stroke never does. It is drawn now — left to right, once,
   at the speed a hand moves — and it is drawn UNDER the words rather than
   behind them, so the letters are never covered by their own highlight. */
/* Drawn as the element's OWN background rather than as a pseudo-element laid
   over it. A pseudo is one rectangle; this sentence wraps on a phone, and one
   rectangle across two lines is a lime block with words in the wrong half of
   it. The background is fragmented by box-decoration-break, so each line is
   drawn as its own stroke.

   No fill mode and no delay, deliberately: with `backwards` the resting state
   before the animation starts is a marker of zero width, and the ink on it is
   near-black in BOTH themes — so an animation that never ran would leave dark
   words on a dark page. Starting at load with no fill means the only state
   this can be stuck in is the finished one. */
.hero--ledger h1 em {
  background-image: linear-gradient(var(--lime), var(--lime));
  background-repeat: no-repeat; background-size: 100% 100%;
  border-radius: .07em .14em .09em .05em;
  animation: mkr .84s cubic-bezier(.2,.9,.3,1);
}
@keyframes mkr { from { background-size: 0 100%; } }
@media (prefers-reduced-motion: reduce) {
  .hero--ledger h1 em { animation: none; }
}

/* 2 · THE PAPER AND THE GLASS ARE WIRED TOGETHER
   Point at a line in the book and the same line lights on the phone, and a
   leader runs between the two. It is the single most literal statement of
   what this product is, and until now the reader had to take it on trust that
   the two lists were the same list. */
.book__tie {
  position: absolute; inset: 0; z-index: 4; width: 100%; height: 100%;
  pointer-events: none; overflow: visible;
  opacity: 0; transition: opacity .22s var(--ease-out);
}
.book__tie.on { opacity: 1; }
.book__tie path {
  fill: none; stroke: var(--brand); stroke-width: 1.5;
  stroke-linecap: round; stroke-dasharray: 3 5;
}
.book__tie circle { fill: var(--brand); }

.book__r[data-pair] { cursor: default; }
.hero--ledger .book__r.is-pair > span:not(.book__mag) { color: var(--ink-hi); }
.hero--ledger .book__r.is-pair > u { color: var(--ink-hi); }
/* the wash is drawn to the RULES, not to a rounded chip: a line being read in
   a ledger is marked by its rule, not by a card appearing under it */
.hero--ledger .book__r.is-pair::before {
  content: ''; position: absolute; z-index: -1; pointer-events: none;
  left: calc(var(--marg) * -1); right: calc(var(--marg) * -1); top: 0; bottom: 0;
  background: linear-gradient(90deg, transparent, rgb(var(--brand-rgb) / .17) 6%,
                              rgb(var(--brand-rgb) / .17) 94%, transparent);
}
.hero--ledger .book__r { position: relative; }
/* and on the glass, the app's own pressed row */
.k__ent.is-pair {
  background: rgb(var(--brand-rgb) / .15);
  box-shadow: inset 3px 0 0 var(--brand);
}
.k__ent { transition: background .18s var(--ease-out), box-shadow .18s var(--ease-out); }

/* 3 · THE SHEET CATCHES THE LIGHT
   Paper is a surface and a surface has a highlight. A soft one follows the
   pointer across the page — four per cent, the same budget the grain and the
   warm wash run on, and off entirely on a touch screen where there is no
   pointer to follow. */
@media (hover: hover) and (pointer: fine) {
  .hero--ledger .book__sheen {
    position: absolute; inset: 0; z-index: 1; pointer-events: none;
    opacity: 0; transition: opacity .5s var(--ease-out);
    background: radial-gradient(420px 320px at var(--mx, 50%) var(--my, 30%),
                                rgb(255 255 255 / .55), transparent 70%);
  }
  :root[data-theme='dark'] .hero--ledger .book__sheen {
    background: radial-gradient(460px 340px at var(--mx, 50%) var(--my, 30%),
                                rgb(var(--brand-rgb) / .10), transparent 70%);
  }
  .hero--ledger .book:hover .book__sheen { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .hero--ledger .book__sheen { display: none; } }

/* ── the narrow ledger ───────────────────────────────────────────────────
   A cash book on a 375px screen cannot carry four columns. It carries three:
   what it was, what it moved, where that left you — and the time goes where a
   phone puts it anyway, into the line under the name. Nothing is dropped from
   the page, only from the grid.

   The row height is still exactly one --row, because the ruling is derived
   from it and the device below is derived from the ruling. Losing a column is
   what buys the entry column enough width to hold a name on one line, which
   is what keeps that true. */
/* the time is the column's job wherever that column exists — declared BEFORE
   the narrow override, because a media query does not raise specificity and
   this file has now lost that argument twice in one afternoon */
.hero--ledger .book__r em > i { display: none; }
@media (max-width: 720px) {
  .hero--ledger {
    --led-io: 4.9rem; --led-bal: 4.4rem; --led-gap: 9px;
    --led: minmax(0, 1fr) var(--led-io) var(--led-bal);
  }
  .hero--ledger .book__r > i,
  .hero--ledger .book__hd > i,
  .hero--ledger .book__bf > i { display: none; }
  .hero--ledger .book__r em > i { display: inline; margin-right: .45em; opacity: .85; }
  /* a day's net still belongs under the movements it sums, which is now the
     second track rather than the third */
  .hero--ledger .book__day > span { grid-column: 1; }
  .hero--ledger .book__day > b { grid-column: 2; justify-self: end; }
  .hero--ledger .book__r > span:not(.book__mag) { font-size: 15px; }
  .hero--ledger .book__r > b { font-size: 15.5px; }
  .hero--ledger .book__r > u { font-size: 14px; }
  .hero--ledger .book__hd i, .hero--ledger .book__hd span,
  .hero--ledger .book__hd b, .hero--ledger .book__hd u { font-size: 9.5px; letter-spacing: .1em; }
  /* A ledger line is one rule tall. On 375px of paper the name and the
     sub-label are both longer than the column, and a line that wraps is a line
     drawn on top of the one under it — so they truncate. A real cash book
     abbreviates in exactly the same place and for exactly the same reason. */
  .hero--ledger .book__r > span:not(.book__mag),
  .hero--ledger .book__r em,
  .hero--ledger .book__bf > span {
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  /* "Net +14,350" does not fit the in/out column at 11px, and wrapped it takes
     the day divider to two rules */
  .hero--ledger .book__day { font-size: 10px; letter-spacing: .12em; }
  .hero--ledger .book__day > span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .hero--ledger .book__day b { font-size: 11.5px; white-space: nowrap; }
}

/* ═══ THE PINNED DEVICE ════════════════════════════════════════════════════
   A third of the width for the device, two thirds for the copy, 70px between
   them and roughly a screenful of scroll per claim — the reference's own
   proportions, measured rather than eyeballed (0.5fr/1fr, gap 70, list gap
   280, items 337–394 tall).

   What the reference cannot do is change the screen. Theirs is a photograph
   of a sign-up page that sits there for 1,500px of scroll while three
   unrelated claims go past it. Ours is the app: the screen under the claim is
   the screen that proves it, and it swaps as the claim arrives. That is the
   only reason to pin anything — a constant that never changes is a
   background. */
.pin {
  display: grid; grid-template-columns: .52fr 1fr;
  gap: clamp(34px, 4.6vw, 70px); align-items: start;
  margin-top: clamp(30px, 4vw, 54px);
}
.pin__dev {
  position: sticky; top: calc(var(--navh) + clamp(18px, 3vh, 40px));
  display: flex; justify-content: center;
}
/* Four screens in one place. They are stacked rather than swapped out,
   because the page's own demos are running inside them — tearing three of
   them out of the document and putting them back would restart every loop and
   lose every figure that had counted. */
.pin__stack { position: relative; display: grid; }
.pin__scr {
  grid-area: 1 / 1; position: relative;
  opacity: 0; visibility: hidden;
  transform: translateY(14px) scale(.985);
  transition: opacity .5s var(--ease-out), transform .6s var(--ease-out),
              visibility 0s linear .5s;
}
.pin__scr.is-live {
  opacity: 1; visibility: visible; transform: none;
  transition: opacity .5s var(--ease-out), transform .6s var(--ease-out),
              visibility 0s linear 0s;
}
.pin__scr .ph { width: min(272px, 24vw); margin-bottom: 0; }
/* the wash the cards used to give the device, kept — a phone on flat canvas
   is a sticker */
.pin__dev::before {
  content: ''; position: absolute; left: 50%; top: 46%; translate: -50% -50%;
  width: 118%; aspect-ratio: 1.15; z-index: -1; pointer-events: none;
  background: radial-gradient(closest-side, rgb(var(--brand-rgb) / .13), transparent 72%);
}

.pin__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.pin__item {
  /* ONE CLAIM, ONE SCREENFUL. Spacing them with a gap gave a beat of 0.44 of
     a viewport — two claims visible at once, and an active state that changed
     twice per screen of scrolling. Measured on the reference, a beat is 0.86
     of a viewport: the claim you are reading is the only one in play.

     A min-height with the content centred in it sets the beat exactly, rather
     than leaving it as the sum of a heading that wraps differently at every
     width and a gap that does not know about it. */
  min-height: 64vh; display: flex; flex-direction: column; justify-content: center;
  max-width: 34rem;
  /* THE CLAIM OUT OF PLAY IS NOT DIMMED. The obvious way to say "this one is
     live" is opacity on the rest, and it was set to .30 here until it was
     measured: body text at --t700 stops clearing 4.5:1 below about 0.78
     opacity, which is a dim nobody can see. Any dim worth doing makes the
     other claims unreadable, and they are real sentences the reader may well
     be part-way through when the beat changes.

     So the state is carried by colour and by movement instead — the heading
     darkens, the folio rule draws out, the number takes the brand, and the
     screen beside it changes. Four signals, and every word on the page stays
     at full strength. */
  transform: translateY(22px);
  transition: transform .7s var(--ease-out);
}
.pin__item.is-on { transform: none; }
.pin__item h3 {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(27px, 3.3vw, 46px); line-height: 1.06; letter-spacing: -.034em;
  margin: 0 0 clamp(12px, 1.2vw, 18px); color: var(--t700); text-wrap: balance;
  transition: color .5s var(--ease-out);
}
.pin__item.is-on h3 { color: var(--ink-hi); }
.pin__item > p:last-child {
  margin: 0; color: var(--t700);
  font-size: clamp(15.5px, 1.25vw, 18px); line-height: 1.6; text-wrap: pretty;
}
/* A ledger numbers its lines. The reference puts a doodle here; a folio
   number says the same thing — this is beat three of four — and says it in
   the page's own language. */
.pin__n {
  display: flex; align-items: center; gap: 12px;
  margin: 0 0 clamp(14px, 1.4vw, 20px);
  font-size: 11px; font-weight: 600; letter-spacing: .19em;
  text-transform: uppercase; color: var(--t700);
}
.pin__n b {
  font-family: var(--font-display); font-weight: 700; font-size: 13px;
  letter-spacing: 0; color: var(--t700);
  transition: color .5s var(--ease-out);
}
.pin__item.is-on .pin__n b { color: var(--brand-text); }
.pin__n i {
  display: block; width: 26px; height: 1px; background: currentColor; opacity: .45;
  transition: width .5s var(--ease-out);
}
.pin__item.is-on .pin__n i { width: 54px; opacity: .8; }

@media (prefers-reduced-motion: reduce) {
  .pin__item, .pin__scr, .pin__n i, .pin__item h3, .pin__n b { transition: none; }
  .pin__item { transform: none; }
}

/* ── the pin on a narrow page ────────────────────────────────────────────
   Two columns is not available and dropping the pin is not either: the whole
   point of the section is that the screen answers the claim you are on, and
   a device that has scrolled off the top answers nothing.

   So the device becomes a band at the top of the section — sticky, cropped to
   the part of the screen that carries the argument, on the page's own ground
   so the claims pass cleanly underneath it. */
@media (max-width: 899px) {
  .pin { grid-template-columns: 1fr; gap: 0; margin-top: clamp(20px, 4vw, 32px); }
  .pin__dev {
    position: sticky; top: var(--navh); z-index: 3;
    height: min(43vh, 352px); overflow: hidden; align-items: flex-start;
    background: var(--canvas);
    /* The band's bottom is a guillotine: a heading passing under it is cut in
       half, which at 375 is exactly where the first claim's heading sits. The
       first shadow is the PAGE'S OWN COLOUR spread downward — a veil below the
       edge, so a line emerging from under the band fades in instead of being
       sliced. It works through `overflow: hidden` because a box-shadow is
       painted outside the border box and is not clipped by it. */
    box-shadow: 0 13px 13px -5px var(--canvas),
                0 22px 20px -18px rgb(var(--shade) / .40);
  }
  .pin__dev::before { top: 42%; width: 78%; }
  .pin__scr .ph { width: min(214px, 54vw); }
  .pin__list { padding-top: clamp(22px, 5vw, 36px); }
  /* Anchored to the top of their block rather than centred in it: centred, the
     heading of the claim in play sits above the paragraph and therefore behind
     the band, and the reader gets the answer without the question. */
  .pin__item { max-width: none; justify-content: flex-start; }  /* height: see THE STACKED BEAT */
  .pin__item:first-child { min-height: 52vh; }
  .pin__item:last-child { min-height: 46vh; }
  .pin__item h3 { font-size: clamp(25px, 7.4vw, 34px); }
}
/* The first claim should be level with the device the moment the section
   arrives, not half a screen under it — before the pin engages there is no
   scrolling yet to bring them together, so the reader's first sight of the
   section is a phone with nothing beside it. The first beat starts at the top
   of its block instead of centred in it, and the last one is trimmed so the
   section does not end on a third of a screen of nothing. */
.pin__item:first-child { min-height: 46vh; justify-content: flex-start; }
.pin__item:last-child { min-height: 50vh; }
/* The incoming screen sits ON the outgoing one. Stacked at the same depth,
   a cross-fade between two dense app screens reads for half a second as a
   double exposure — two ledgers printed over each other — which is the one
   thing this page must never look like. */
.pin__scr.is-live { z-index: 1; }

/* ═══ THE LEDGER'S TYPE IS SIZED BY ITS OWN RULING ═════════════════════════
   A ledger line is exactly one rule tall. The entry text was sized against
   the VIEWPORT — clamp(15px, 1.25vw, 17px) for the name and a flat 11px for
   the sub-label — while the rule is sized against the DEVICE, and the two
   scales do not meet. Swept across twenty widths, the name and the sub-label
   together came to 42px inside a 36px rule at 320, and 40 inside 37 at 1100,
   which is a row drawing on top of the one under it at both ends of the range
   and at two of the commonest widths there are.

   Both now derive from --row, the same way the device's width does. Name at
   0.40 of a rule and sub at 0.265, with their line-heights declared rather
   than inherited from the font: 0.79 of a rule in total, so the line fits at
   every width by construction instead of by luck. The caps hold the type at
   its design size once the rule is big enough to carry it. */
.hero--ledger .book__r > span:not(.book__mag) {
  font-size: min(16.5px, calc(var(--row) * .40));
  line-height: 1.2;
}
.hero--ledger .book__r em {
  font-size: min(11px, calc(var(--row) * .265));
  line-height: 1.16; margin-top: 0;
}
/* brought forward is the same shape, so it is measured the same way */
.hero--ledger .book__bf > span {
  font-size: min(15.5px, calc(var(--row) * .375));
  line-height: 1.2;
}
.hero--ledger .book__bf em {
  font-size: min(10.5px, calc(var(--row) * .25));
  line-height: 1.16; margin-top: 0;
}
/* the figures follow the rule too, so a 19px total never outgrows a 37px line */
.hero--ledger .book__r > b { font-size: min(19px, calc(var(--row) * .43)); }
.hero--ledger .book__r > u { font-size: min(17px, calc(var(--row) * .38)); }
.hero--ledger .book__bf > u { font-size: min(16px, calc(var(--row) * .35)); }
.hero--ledger .book__r > i { font-size: min(12px, calc(var(--row) * .285)); }

/* ── the letterhead on a small phone ─────────────────────────────────────
   Measured across the sweep: the folio rail runs to one line at 820 and
   above, two from 360, and THREE at 320–344 — ninety-five pixels of a
   568-pixel screen spent on the header of the page before the page starts.

   A letterhead drops the parts the reader can infer. "Cash book" is what the
   whole hero is, and the month is on the entries themselves; the business,
   the book, the folio and what it stands at are the parts that carry. */
@media (max-width: 479px) {
  .hero--ledger .fo-opt { display: none; }
  .hero--ledger .book__folio { font-size: 9.5px; letter-spacing: .13em; gap: 4px 12px; }
}
/* "MAIN TILL STANDS AT" broke to two lines at 320 and sat under its own
   figure. The label is the quietest thing on the row and the figure is the
   loudest, so the label gives way: tighter tracking and a smaller size rather
   than a second line under a total. */
@media (max-width: 430px) {
  .hero--ledger .book__tot > span {
    font-size: 9.5px; letter-spacing: .12em; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis;
  }
  .hero--ledger .book__tot { gap: 10px; }
}

/* ═══ THE PINNED DEVICE IS CENTRED, AND IT IS BIGGER ═══════════════════════
   Measured at 2000×1250: the device column was 351 of 1,096 and the phone 272
   of that — a 272px object on a 2,000px screen — and it pinned at the TOP of
   the viewport with 570px of empty column under it while the claim beside it
   sat in the middle. Two things wrong at once: too small, and not level with
   the thing it is answering.

   The sticky box is now the height of the viewport under the nav, with the
   phone centred in it, so the screen and the sentence are always on the same
   line. The column takes a bigger share, and the phone is sized by whichever
   runs out first — its cap, the width available, or the HEIGHT available,
   because a phone is 2.114 times as tall as it is wide and a device that
   overflows the viewport cannot be pinned in it. */
@media (min-width: 900px) {
  .pin { grid-template-columns: .72fr 1fr; align-items: start; }
  .pin__dev {
    top: var(--navh);
    height: calc(100vh - var(--navh));
    align-items: center;
  }
  /* the height cap leaves room for the rail under the device as well as the
     device itself — 47vh fitted the phone exactly and pushed the marks onto
     the fold */
  .pin__scr .ph { width: min(400px, 27vw, calc(45vh - 56px)); }
  /* the wash follows the device rather than the column it is centred in */
  .pin__dev::before { content: none; }
  .pin__stack::before {
    content: ''; position: absolute; left: 50%; top: 50%; translate: -50% -50%;
    width: 132%; aspect-ratio: 1.12; z-index: -1; pointer-events: none;
    background: radial-gradient(closest-side, rgb(var(--brand-rgb) / .13), transparent 72%);
  }
}

/* ═══ THE BOOKS GO PLAIN ═══════════════════════════════════════════════════
   Colour off, at the owner's word. The cloth covers, the coloured spine tape,
   the ivory label and its red rule, the tinted chips and the green stamp all
   come off; what is left is the surface the rest of this page already uses for
   a card, with the type set on it plainly.

   The one thing that cannot go is knowing WHICH book is open, because that is
   what the phone beside it is showing. It is said in ink now rather than in
   colour: the chosen card takes the page's strongest edge and its stamp
   inverts. Nothing on these cards is coloured any more. */
.kind {
  display: grid; grid-template-columns: 34px minmax(0, 1fr); gap: 8px 14px;
  align-items: start; padding: 17px 20px 16px;
  border: 1px solid var(--divider); border-radius: var(--r-lg);
  background: var(--card); background-image: none;
  box-shadow: none; transform: none;
  transition: border-color var(--t-ui) var(--ease-out),
              box-shadow var(--t-ui) var(--ease-out);
}
.kind::before, .kind::after { display: none; content: none; }
:root[data-theme='dark'] .kind { box-shadow: none; }

.kind .kind__i {
  grid-row: 1 / span 2; width: 34px; height: 34px; border-radius: 10px;
  background: var(--canvas); color: var(--t700);
  box-shadow: inset 0 0 0 1px var(--hairline);
}
.kind .kind__i svg { width: 17px; height: 17px; }

.kind__lab {
  background: none; padding: 0; border-radius: 0; box-shadow: none;
}
.kind__lab b {
  display: block; font-family: var(--font-display); font-weight: 700;
  font-size: 16.5px; letter-spacing: -.02em; color: var(--ink);
}
.kind__lab em {
  display: block; margin-top: 3px; padding-top: 0; border-top: 0;
  font-style: italic; font-size: 13px; line-height: 1.4; color: var(--t700);
}
.kind .chips { grid-column: 2; margin: 2px 0 0; gap: 6px; }
.kind .chips i {
  padding: 4px 10px; font-size: 12px; font-weight: 500;
  background: var(--canvas); color: var(--t700);
  border: 1px solid var(--hairline); border-radius: var(--radius-pill);
}

.kind:hover { border-color: var(--soft); box-shadow: var(--e1); }
.kind:active { transform: none; }
.kind.is-on, .kind.is-on:hover {
  background: var(--card); background-image: none;
  border-color: var(--ink); box-shadow: inset 0 0 0 1px var(--ink);
  transform: none;
}
:root[data-theme='dark'] .kind.is-on {
  border-color: var(--ink-hi); box-shadow: inset 0 0 0 1px var(--ink-hi);
}
.kind.is-on .kind__i { background: var(--ink-hi); color: var(--canvas); box-shadow: none; }
.kind.is-on .kind__lab b { color: var(--ink-hi); }
.kind.is-on .chips i { background: var(--canvas); border-color: var(--soft); color: var(--t900); }
/* nothing steps out of the stack any more, so the shelf needs no room beside it */
.pick__list { padding-right: 0; gap: 10px; }

/* ═══ THE SCREEN CHANGES; THE PHONE DOES NOT ═══════════════════════════════
   The stacked screens carried `transform: translateY(14px) scale(.985)` when
   they were not live, so every swap scaled the whole device — frame, bezel and
   all — from 340.4px to 345.6 and back. Measured, that is a 5px pulse on a
   physical object twice per beat, and it reads exactly as the owner described
   it: the display shrinks.

   A phone does not resize when you change screens. The device is now
   perfectly still at every moment and the ONLY thing that changes is the
   picture on the glass. There is no inner slide either: .k is `position:
   absolute; inset: 0`, so moving it would open a strip of bare screen along
   one edge.

   What makes it read as a change rather than a dissolve is the timing — out
   faster than in, the incoming screen on top — which is the shape of a real
   screen transition. */
.pin__scr {
  transform: none;
  opacity: 0; visibility: hidden;
  transition: opacity .34s cubic-bezier(.4, 0, 1, 1), visibility 0s linear .34s;
}
.pin__scr.is-live {
  transform: none; opacity: 1; visibility: visible; z-index: 1;
  transition: opacity .46s cubic-bezier(.22, 1, .36, 1), visibility 0s linear 0s;
}
@media (prefers-reduced-motion: reduce) {
  .pin__scr { transition: none; }
}

/* ── the rail ────────────────────────────────────────────────────────────
   Six beats is past what a reader will hold without being shown. One mark per
   claim under the device: where you are, how many there are, and a way back to
   any of them. */
.pin__rail { display: none; }
@media (min-width: 900px) {
  .pin__rail {
    display: flex; gap: 7px; margin-top: clamp(18px, 2.2vh, 30px);
    align-items: center; justify-content: center;
  }
  .pin__rail button {
    appearance: none; border: 0; padding: 9px 0 0; margin: 0; background: none;
    cursor: pointer; line-height: 0;
  }
  .pin__rail button::before {
    content: ''; display: block; width: 22px; height: 3px; border-radius: 99px;
    background: var(--divider);
    transition: width .45s var(--ease-out), background-color .45s var(--ease-out);
  }
  .pin__rail button:hover::before { background: var(--soft); }
  .pin__rail button[aria-current='true']::before {
    width: 42px; background: var(--ink-hi);
  }
  .pin__rail button:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; border-radius: 4px; }
}
@media (prefers-reduced-motion: reduce) { .pin__rail button::before { transition: none; } }

/* ── six beats, retimed ──────────────────────────────────────────────────
   Four claims at 64vh was 256vh of section. Six at the same pitch would be
   384 — more scrolling than the argument is worth — so the beat comes in and
   the section lands at about the same length with two more things in it. */
/* A claim holds at the line for its beat MINUS its own height, and is pushed
   for that last stretch. With a 52vh beat and copy that runs to three lines,
   the hold was ~200px of a 468px beat — the claim spent nearly half its turn
   sliding out. The beat goes up so the held share does: about seven parts held
   to three moving, which is also the reference's own pacing of roughly one
   screenful per claim. */
.pin__item { min-height: 72vh; }
.pin__item:first-child { min-height: 52vh; }
.pin__item:last-child { min-height: 44vh; }
/* The mobile beat is NOT set here. It was, at 76vh, and was overridden 500
   lines below by the block that actually owns the stacked layout — so this
   read as the tuned value while a different one was in force. One rule, one
   place: see "THE STACKED BEAT" near the end of this file. */

/* the line under the dollar books that says what the shillings are doing */
/* The line under the dollar books that says what the shillings are doing.
   It was called .k__note, which the app's own stylesheet already uses for the
   note field in the composer — a flex row with an icon in it. The sentence
   came out as three columns because it has three child nodes and inherited
   somebody else's layout. Named for what it is instead, and app-ui.css loads
   AFTER this file, so a name that is already taken there always wins. */
.k__apart {
  display: block; text-align: center;
  margin: calc(var(--u) * 12) calc(var(--u) * 26) 0;
  font-size: calc(var(--u) * 11.5); line-height: 1.5;
  color: var(--k-faint);
}
.k__apart b { color: var(--k-ink); font-weight: 600; white-space: nowrap; }

/* ── the rail goes UNDER the device, not beside it ───────────────────────
   .pin__dev is a flex box and the rail was its second item, so it landed to
   the right of the phone in the middle of the column. */
.pin__dev { flex-direction: column; }

/* ── the last claim keeps its phone ──────────────────────────────────────
   The sticky device is a viewport tall, so it unpins as soon as its bottom
   reaches the bottom of .pin — and with the last beat only 46vh that happened
   while the sixth claim was still being read, leaving it with the phone half
   off the top of the screen. The list needs about half a viewport of tail
   after the last claim's centre for the device to stay with it. */
/* THE TAIL IS NOT OPTIONAL, AND IT HAS A FLOOR.
   A sticky box H tall unpins the moment its container's bottom reaches the
   viewport bottom, so for the device to still be held while the last claim
   sits at the reading line, the list has to reach roughly from that line to
   the foot of the screen — about 40vh of it, none of which can carry copy.

   At 26vh past the last beat it was 750px of held phone with nothing beside
   it. At 7vh the device unpinned while the last claim was still up and slid
   246px out from under it. 20vh is the floor that keeps them together, and
   the half-screen it leaves reads as the section breathing out rather than as
   a hole. */
.pin__list { padding-bottom: 20vh; }

/* ═══ THE SHEET IS THE PAGE'S OWN COLOUR ═══════════════════════════════════
   The stock was warm ivory with a warm wash over it — #FDFBF5 under a
   rgba(255,247,224,.72) radial — and at page scale that reads as a yellow
   band across the top of the site rather than as paper. Off, at the owner's
   word: the sheet is the same colour as everything under it now.

   Losing the tint does not lose the sheet. What says "this is a page" was
   never the cream: it is the ruling, the red margin down the left, the grain,
   and the edge it ends on with the shadow under it. All four stay, and the
   light falling on it stays too — mixed from white instead of from yellow, so
   there is a direction to the light and no colour in it. */
:root[data-theme='light'] .hero--ledger .book {
  background: var(--canvas);
}
:root[data-theme='light'] .hero--ledger .book::after {
  background:
    radial-gradient(130% 100% at 86% -8%, rgb(255 255 255 / .62), transparent 62%),
    linear-gradient(174deg, rgb(255 255 255 / .34), rgb(255 255 255 / .10) 58%, rgb(0 0 0 / .012));
}
/* With the cream gone, two things that were reading as PAPER start reading as
   dirt: the grain's multiply blend and the binding shade were mixed against a
   warm stock and now sit on the page's own grey, pulling the sheet about six
   units darker than the section under it. Both come down in light so the sheet
   lands on the canvas value and the texture stays a texture. */
:root[data-theme='light'] .hero--ledger .book__grain { opacity: .26; }
:root[data-theme='light'] .hero--ledger .book__bind {
  background: linear-gradient(to right, rgb(var(--shade) / .085) 0,
                                        rgb(var(--shade) / .022) 42%, transparent 100%);
}

/* ═══ THE CLAIM HOLDS AT THE READING LINE ══════════════════════════════════
   The screen was changing while the claim was still travelling. With a beat
   of 58vh the words crossed the whole viewport during their turn — arriving
   low, level with the phone in the middle, and leaving from the top — so at
   any given moment the sentence and the screen answering it were rarely on
   the same line.

   Each claim now sticks at the reading line for its whole beat: it rises into
   place, HOLDS there level with the device while its screen is up, and is
   pushed off by the next one. The change happens where the reader is looking,
   which is the middle, and the scroll still belongs to the claims rather than
   to a timer.

   The offset is the device's own centre less half a claim — the device is
   centred in the viewport under the nav, so this puts the two on one line. */
/* ── THE COPY SCROLLS. IT DOES NOT STICK. ────────────────────────────────
   Holding each claim at the reading line put it level with the phone, which
   was the point — but a sticky element is pushed out of the way by the bottom
   of its own block, and the thing doing the pushing is the NEXT claim. For the
   length of one claim's height the two were touching, and at any width where
   the copy runs to three lines they overlapped: a heading printed across the
   paragraph above it.

   So the copy scrolls the way copy scrolls. What keeps the sentence and the
   screen together is not pinning the words — it is that the screen changes at
   the moment the words reach the middle, and that each claim's strength is its
   own distance from that line. Nothing is ever pushed into anything. */
.pin__item {
  display: flex; flex-direction: column; justify-content: center;
}
.pin__hold { position: static; }
/* ── the copy cross-fades with the screen ────────────────────────────────
   With the claims holding at the line, only two are ever in play: the one on
   the line and the one being pushed off it. Fading the pair in step with the
   screens makes the sentence and the picture change together instead of the
   words sliding out under a screen that has already swapped.

   This is not the dim that was rejected earlier on contrast grounds — that
   put readable body copy at 30% and left it there. A claim is either at the
   line at full strength or on its way out; nothing is ever presented to the
   reader at a reduced one. */
/* NO FADE. Two attempts at one, both wrong for the same reason.
   Tied to a class, a claim was invisible until the beat flipped and then
   appeared at full strength — so the words only showed up once they were
   already level with the phone. Tied to distance, the copy was faint for over
   half the scroll, because with one claim per beat the nearest one is half a
   beat away at every midpoint.

   Copy that is on the screen should be readable. The claim in play is said by
   the things that can carry it without dimming a sentence: the screen beside
   it changes, the heading takes the page's strongest ink, the folio rule draws
   out and the number takes the brand. */
.pin__hold { opacity: 1; }
.pin__item h3 { color: var(--t700); transition: color .5s var(--ease-out); }
.pin__item.is-on h3 { color: var(--ink-hi); }
/* no script, no driver: every claim shows, and nothing sticks — six holds
   pinned to the same line would draw on top of each other */
html:not(.js) .pin__hold { opacity: 1; position: static; }
@media (prefers-reduced-motion: reduce) { .pin__item h3 { transition: none; } }

/* ═══ THE TWO STORES ═══════════════════════════════════════════════════════
   Store badges are a fixed shape everywhere on the web and this page does not
   get to reinvent it: a dark pill, the platform glyph at the left, a quiet
   line over a loud one. What this page DOES get to do is tell the truth about
   which of them works — Android is a link, iOS is a note with SOON on it and
   no href at all, so it cannot be clicked into a dead end.

   They sit on lime, which does not invert, so like everything else on this
   band their colours are mixed from --on-lime rather than inherited. */
.stores {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 12px; margin-top: clamp(22px, 2.6vw, 32px);
}
.store {
  display: inline-flex; align-items: center; gap: 11px;
  padding: 10px 18px 11px 16px; border-radius: 12px;
  background: var(--on-lime); color: var(--lime);
  text-decoration: none; position: relative;
  box-shadow: 0 1px 2px rgb(0 0 0 / .22), 0 10px 22px -14px rgb(0 0 0 / .55);
  transition: transform var(--t-ui) var(--ease-out), box-shadow var(--t-ui) var(--ease-out);
}
.store i { display: block; width: 23px; height: 23px; flex: none; }
.store i svg { width: 100%; height: 100%; display: block; }
.store span { display: block; text-align: left; line-height: 1.12; }
.store small {
  display: block; font-size: 9.5px; font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase;
  color: color-mix(in srgb, var(--lime) 72%, transparent);
}
.store b {
  display: block; font-family: var(--font-display); font-weight: 700;
  font-size: 16px; letter-spacing: -.02em; color: var(--lime);
}
a.store:hover { transform: translateY(-2px); box-shadow: 0 2px 4px rgb(0 0 0 / .26), 0 16px 28px -14px rgb(0 0 0 / .6); }
a.store:active { transform: translateY(0); }
a.store:focus-visible { outline: 3px solid var(--on-lime); outline-offset: 3px; }

/* ── not yet, and it says so ─────────────────────────────────────────────
   The iOS badge is deliberately not an <a>: it has no destination. It reads
   as the same object at a lower temperature, with the word that explains why. */
.store--soon {
  background: color-mix(in srgb, var(--on-lime) 12%, transparent);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--on-lime) 30%, transparent);
  cursor: default; padding-right: 62px;
}
/* The badge's own ink, not just its children's. Inherited from .store it was
   lime on a lime-tinted pill — nothing visible uses it, because every run of
   text inside sets its own colour, but an element whose inherited colour is
   invisible on its own background is a trap for the next thing added to it. */
.store--soon, .store--soon i, .store--soon b { color: var(--on-lime); }
.store--soon small { color: color-mix(in srgb, var(--on-lime) 72%, transparent); }
.store--soon em {
  position: absolute; right: 14px; top: 50%; translate: 0 -50%;
  font-style: normal; font-size: 10px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  padding: 4px 8px 4px 9px; border-radius: 99px;
  background: var(--on-lime); color: var(--lime);
}
@media (max-width: 420px) { .stores { flex-direction: column; align-items: stretch; } }

/* ═══ THE DOWNLOAD ROW MOVES TO THE HERO, AND GETS DRAWN PROPERLY ══════════
   Two faults, one block.

   FAULT ONE — it was at the foot. The badges were the last thing on the page,
   under the email pill, which is the order for a reader who has already read
   everything. Most never get there. They are the door, so they go where the
   headline is, and the two quiet links that were beside them come with them.

   FAULT TWO — they looked drawn, because they were. The Play glyph was a
   stroked triangle inside a rounded box: the universal "play" symbol, which is
   exactly what the Google Play mark is NOT. The mark is a folded four-facet
   shape — cyan body, green above, red below, amber tip — and a reader who has
   installed two hundred apps knows the difference between it and a triangle
   without being able to say why. An approximate logo is the single loudest way
   a download row announces itself as a mock-up.

   So: the real geometry, the real gradients, no box around it, and the badge
   proportions the stores actually publish — the small line at roughly half the
   large one, the mark at about 55% of the pill's height.

   Still owed before launch: the Play listing URL (this points at the email
   pill, which is the only control the page can honour today) and the official
   badge artwork, which both stores ship with rules on minimum size, clear
   space and wording. These are accurate, not official. ───────────────────── */
.hero--ledger .hero__cta {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: 0; margin-top: 0;
}
.hero--ledger .stores { margin-top: 0; justify-content: flex-start; }
.stores { gap: 10px 12px; }

/* The pill inverts with the page, the way the nav links over the hero had to:
   a near-black badge on the day sheet, a near-white one on the night sheet.
   A black pill on a black sky is a hole, and both stores publish a light badge
   for exactly this reason. */
.store {
  --store-ink: var(--canvas);
  display: inline-flex; align-items: center; gap: 10px;
  padding: 8px 17px 9px 13px; border-radius: 11px;
  background: var(--ink-hi); color: var(--store-ink);
  text-decoration: none; position: relative;
  box-shadow: 0 1px 2px rgb(0 0 0 / .16), 0 14px 26px -18px rgb(0 0 0 / .55);
  transition: transform var(--t-ui) var(--ease-out), box-shadow var(--t-ui) var(--ease-out);
}
/* The glyph slot still carried a 1px DASHED box from the version where the
   marks were placeholders — a dashed rectangle around a logo is the graphic
   convention for "artwork goes here", printed at 3× on the front page. */
.store i {
  display: block; flex: none; width: 25px; height: 25px;
  border: 0; border-radius: 0; background: none;
}
.store small {
  font-size: 8.9px; letter-spacing: .085em; font-weight: 600;
  color: color-mix(in srgb, var(--store-ink) 80%, transparent);
}
.store b {
  font-size: 17px; font-weight: 600; letter-spacing: -.012em;
  color: var(--store-ink); margin-top: 1px;
}
a.store:hover { transform: translateY(-2px); box-shadow: 0 2px 5px rgb(0 0 0 / .2), 0 20px 34px -18px rgb(0 0 0 / .6); }
a.store:active { transform: translateY(0); }
a.store:focus-visible { outline: 3px solid var(--brand); outline-offset: 3px; }

/* Not yet, and it says so. Outlined rather than filled, because two solid
   pills side by side read as two live buttons and only one of them is. */
.store--soon {
  --store-ink: var(--t900);
  background: transparent;
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--t900) 26%, transparent);
  /* room for the SOON pill: 13px inset + the pill's own width + clearance.
     At 60px it was printing 1.8px into the "e" of App Store. */
  cursor: default; padding-right: 76px;
}
.store--soon, .store--soon i, .store--soon b { color: var(--store-ink); }
.store--soon small { color: color-mix(in srgb, var(--store-ink) 78%, transparent); }
.store--soon em {
  right: 13px; padding: 3px 8px 4px 9px; font-size: 9.5px;
  background: var(--ink-hi); color: var(--canvas);
}

/* ── the two quiet doors ────────────────────────────────────────────────
   Not buttons. A reader who wants the long version is not the reader the
   badges are for, and giving them equal weight would cost the badges theirs. */
.herolinks {
  display: flex; flex-wrap: wrap; gap: 6px 20px;
  margin-top: calc(var(--row) * .34); font-size: 13px;
}
.herolinks a {
  color: var(--muted); text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--muted) 34%, transparent);
  padding-bottom: 1px;
  transition: color var(--t-ui) var(--ease-out), border-color var(--t-ui) var(--ease-out);
}
.herolinks a:hover { color: var(--brand-text); border-bottom-color: currentColor; }

/* The foot keeps one line now, so it stops pretending to be a two-column row. */
.closerow--solo { justify-content: center; }

/* ═══ THE FIRST CLAIM STARTS AT THE LINE, NOT ABOVE IT ════════════════════
   The section pinned with claim 01 sitting near the top of the screen and the
   phone centred half a screen below it — the one pairing on the page that was
   not a pairing. It read as a heading with a picture under it rather than as a
   sentence with its proof beside it, and it was the reader's first impression
   of the mechanism.

   The device pins at `--navh` and is centred in what is left, so the reading
   line is navh + (100vh − navh)/2 measured from the top of the viewport. The
   claims are the grid's other column and start at the same y as the device, so
   a first beat exactly (100vh − navh) tall puts its centred copy on that line
   at the instant the device pins — no offset, no magic number, the same
   arithmetic the driver uses to choose which claim is live.

   Only ≥900px: below that the phone is above the copy rather than beside it,
   and the line is computed from the device's bottom edge instead. */
@media (min-width: 900px) {
  /* `justify-content: flex-start` was set on :first-child two rewrites ago,
     when the first beat was a short lead-in and its copy belonged at the top.
     It is a :first-child selector, so it outranked the `.pin__item` rule that
     centres every other beat no matter how late that rule was written — the
     box grew to a full screen and the copy stayed pinned to the top of it. */
  .pin__item:first-child {
    /* A full pinned screen, LESS the rail's footprint. The device column is
       centred as a whole and the rail is part of it, so the phone itself rides
       half the rail's height above the middle of that column — 16px, which is
       the difference between "level with the phone" and "nearly level with the
       phone". Written as the rail's own numbers so it tracks the clamp. */
    min-height: calc(100vh - var(--navh) - 12px - clamp(18px, 2.2vh, 30px));
    justify-content: center;
  }
}

/* A stretched badge stops looking like a badge. The narrow-width rule made the
   pills `align-items: stretch`, so each one ran to the margin with its mark and
   its words clustered at the left end and the SOON pill marooned at the other.
   Store badges are fixed-shape objects — they keep their shape and wrap. */
@media (max-width: 420px) {
  .stores { flex-direction: row; align-items: flex-start; }
  .store { flex: 0 0 auto; justify-content: flex-start; }
}

/* ═══ THE BLOCK UNDER THE HEADLINE WAS FIVE ROWS AND FOUR INKS ════════════
   Measured, the stack ran: eyebrow, headline, badges, cue, links — separated
   by 13.3, 20.3, 16.0 and 13.3px. Five things at one spacing is a LIST, not a
   hierarchy: nothing groups, so the eye has to read all five to find out which
   one it wanted. Four inks compounded it — near-black headline, t700 eyebrow,
   t700 cue with a BRAND-GREEN word inside it, muted links — plus the lime
   highlight and the green beta dot, which is three greens in 250px next to a
   four-colour Play mark.

   The badges stay exactly as they are; they are the thing that works. What
   changes is everything standing next to them:

     · the cue and the links become ONE footnote block, five pixels apart,
       with twenty above them — so the stack reads eyebrow+headline / badges /
       footnote instead of five equal rows;
     · their inks swap. The cue was DARKER than the links, which is backwards:
       the sentence is a caption and the links are the controls;
     · "Cash in" loses the green. It is the name of a button, and weight says
       that without spending the page's third green on it;
     · the links lose their resting underline. Two rules under two pills is
       four stacked boxes. The underline is kept for hover and focus, drawn in
       a border that is already there and merely transparent, so nothing
       shifts when it appears. */
.hero--ledger .book__cue {
  /* 26 above the pair, 5 between them: the gap that separates the groups has
     to be visibly larger than the gap inside one, or they are still a list. */
  margin-top: 26px; font-size: 12.5px; color: var(--muted);
}
.hero--ledger .book__cue b { color: var(--t900); font-weight: 600; }

.herolinks { margin-top: 5px; font-size: 12.5px; gap: 4px 18px; }
/* Weight, not a rule, is what says "this one is a control" once the underline
   is gone — it costs no line and no colour. */
.herolinks a { color: var(--t700); font-weight: 500; border-bottom-color: transparent; }
.herolinks a:hover,
.herolinks a:focus-visible { color: var(--brand-text); border-bottom-color: currentColor; }

/* ═══ IT IS A PAGE OF A BOOK, SO THE WRITING ON IT IS WRITTEN ═════════════
   The lime marker was the loudest thing in the hero and it was the wrong
   gesture twice over. A highlighter is what you do to someone ELSE'S page —
   you mark the line that matters in a book you did not write. This page is
   the reader's own book, and the one thing a cash book has that a printed
   form does not is a hand in it.

   So the emphasis stops being a coloured box and becomes the entry: the
   printed line stays in the display face, the line that carries the claim is
   written, and a pen stroke is drawn under it the way you underline a total.
   The stroke draws itself left to right in one pass — the same 0.84s the
   marker used, because that timing was never the problem.

   Three things in the block are now in the hand: the note at the top of the
   page, the line that matters, and the aside under the badges. The printed
   half — folio line, headline, ledger, badges — stays printed. That contrast
   IS the product: a form that is filled in.

   Every green in the block goes with it. The marker, the pulsing beta pip and
   the green on the links' hover were three separate greens inside 250px, all
   standing next to a four-colour Play mark. The mark is the only thing in the
   hero that gets to be in colour now. */
:root { --pen: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 560 15' preserveAspectRatio='none'%3E%3Cpath d='M5 9.6C92 4.4 176 12.2 262 7.4S420 3.6 555 8.6' fill='none' stroke='%23FFFFFF' stroke-width='3.4' stroke-linecap='round'/%3E%3C/svg%3E"); }
:root[data-theme='light'] { --pen: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 560 15' preserveAspectRatio='none'%3E%3Cpath d='M5 9.6C92 4.4 176 12.2 262 7.4S420 3.6 555 8.6' fill='none' stroke='%2305060E' stroke-width='3.4' stroke-linecap='round'/%3E%3C/svg%3E"); }
:root { --font-hand: 'Caveat', 'Bradley Hand', 'Segoe Script', cursive; }

.hero--ledger h1 em {
  font-family: var(--font-hand); font-weight: 700; font-style: normal;
  /* Caveat's x-height is about four fifths of Sora's, so it needs the size
     back or the written line reads as a smaller thought than the printed one.
     The line-height comes down to match, or the script's long ascenders push
     the line box open and the two lines drift apart. */
  font-size: 1.3em; line-height: .82; letter-spacing: 0;
  color: var(--ink-hi); background-color: transparent;
  border-radius: 0; margin: 0; padding: 0 0 .06em;
  /* The stroke rides just under the descenders. At .17em it stood clear of
     the writing and read as a rule someone had printed there. */
  background-image: var(--pen);
  background-repeat: no-repeat; background-position: 0 100%;
  background-size: 100% .13em;
  animation: penline .84s cubic-bezier(.2, .9, .3, 1);
  /* THE WRITTEN LINE GETS ITS OWN LINE. Inline, it began wherever the printed
     part happened to end, so on a phone it started with two words' room left
     and broke mid-phrase — and a broken fragment gets its own stroke, so the
     hero came out with two underlines through half a sentence each.

     As a block it always starts fresh and fits in one pass at every width
     measured. fit-content keeps the stroke the length of the WRITING: a block
     underlines its box, and its box is the full column. */
  display: block; width: fit-content; max-width: 100%;
}
/* No fill mode, for the reason the marker had none: the only state this can
   be stuck in is the finished one. */
@keyframes penline { from { background-size: 0 .13em; } }
@media (prefers-reduced-motion: reduce) { .hero--ledger h1 em { animation: none; } }

/* The note at the head of the page. Uppercase letterspaced caps are printing,
   not writing — a script set in tracked capitals looks like a failed font. */
.hero--ledger .eyebrow {
  font-family: var(--font-hand); font-size: 19px; font-weight: 600;
  letter-spacing: .005em; text-transform: none; color: var(--t700); gap: 9px;
}
.hero--ledger .eyebrow .dot {
  width: 6px; height: 6px; background: var(--t700);
  box-shadow: none; animation: none; opacity: .5;
}

/* The aside under the badges — the one line on the page that is an instruction
   from one person to another, which is the definition of a margin note. */
.hero--ledger .book__cue {
  font-family: var(--font-hand); font-size: 17px; font-weight: 500;
  letter-spacing: .005em; color: var(--t700);
}
.hero--ledger .book__cue b { font-weight: 700; color: var(--ink-hi); }

/* the last green in the block */
.herolinks a:hover,
.herolinks a:focus-visible { color: var(--ink-hi); border-bottom-color: currentColor; }

/* At 320 the written line needs 293px and the column is 280, so it broke in
   two — and a block that breaks is underlined once, under the lower half. The
   size comes back a notch below 360, which leaves 23px of slack for the
   fallback hands (Bradley Hand, or whatever `cursive` resolves to) whose
   metrics are not Caveat's. */
@media (max-width: 359px) {
  .hero--ledger h1 em { font-size: 1.14em; }
}

/* ═══ THE MOBILE BAND WAS CROPPING THE WRONG END ══════════════════════════
   On phones the device is deliberately a BAND: `height: min(43vh, 352px)` with
   `overflow: hidden`, showing the part of the screen that carries the argument
   and letting the claims pass under it. That rule set `align-items: flex-start`
   to mean "hold the phone against the top of the band".

   It meant that when it was written, because .pin__dev was a ROW. Then the rail
   was moved under the device with a global `flex-direction: column`, and in a
   column `align-items` is the HORIZONTAL axis — so "top" quietly became "left",
   and the vertical axis fell to the base rule's `justify-content: center`.

   A 428px phone centred in a 349px band overhangs it by 38px at EACH end, and
   overflow:hidden takes both: measured at 375, the phone's top edge sat at y=26
   with the nav ending at 52, so the status bar and the app's own header were
   cut off under the nav and the device's bottom frame was cut off too. Not a
   crop — a phone that looks broken at both ends.

   Stated on the axes that are actually in play now. */
@media (max-width: 899px) {
  .pin__dev { justify-content: flex-start; align-items: center; }
}

/* ── the phone band and the beat have to agree ──────────────────────────
   With the band 43vh tall under a 52px nav, a phone leaves about 411px of
   strip for the copy. The beats are 76vh — 617px — which is more than the
   strip, so a claim slides up behind the band and the next one is still below
   the fold: measured at 375, claim 01's copy went under the band at y=401 and
   claim 02's arrived at y=811, one pixel inside the viewport. Not a gap on
   paper; a screenful of phone and nothing else to read, in practice.

   The beat comes back to 62vh, the value the mobile block used before the
   desktop pacing was applied to every width at once, which puts the next claim
   on screen with 170px to spare.

   And they are anchored to the TOP of their block again. That was the mobile
   rule's own decision and a later `justify-content: center` written for the
   desktop column took it: centred in a 62vh box, the heading of the claim in
   play sits high enough to be behind the band, and the reader gets the answer
   without the question. */
/* ── THE STACKED BEAT · the only place the mobile beat height is set ─────
   Measured at 390x844 before this: the claim is 216px of content sitting in a
   523px block, so 307px — 59% of the beat — was empty page scrolling past
   between one claim and the next. On a phone the device is pinned overhead and
   the claims pass under it, so that gap is not a hand-off like the desktop
   column's, it is just dead scroll. The beat comes down until the claim is
   about half its block rather than two fifths of it. */
@media (max-width: 899px) {
  .pin__item { min-height: 52vh; justify-content: flex-start; }
  .pin__item:first-child { min-height: 46vh; justify-content: flex-start; }
  .pin__item:last-child { min-height: 42vh; }
}

/* The tail was measured for the DESKTOP device, which is a viewport tall and
   needs 20vh under the last claim or it unpins early and slides out. The
   mobile band is 43vh, so it holds far longer for the same tail: measured at
   375, the last claim went behind the band at scroll 4946 and the band did not
   let go until 5150 — 200px of phone over a gap with the next section already
   rising into it. 8vh leaves ~107px of margin after the last claim is read,
   and closes the hole. */
@media (max-width: 899px) {
  .pin__list { padding-bottom: 8vh; }
}

/* ═══ NATIVE, MEANING THE THINGS A NATIVE APP DOES AND A PAGE USUALLY DOESN'T
   Scoped to .pg-plain: plain.css is also loaded by one.html and the heroes
   drafts, and chrome is not something to change under three other pages at
   once. ─────────────────────────────────────────────────────────────────── */

/* 1 · THE BAR IS GLASS.
   A navigation bar that goes opaque is a web header; one that lets the page
   move UNDER it, blurred and desaturated-up, is the single most recognisable
   native surface there is. Both platforms' bars work this way, and the reason
   is legibility: the blur keeps contrast steady no matter what scrolls behind,
   which a flat translucent fill cannot.

   The saturation boost is not decoration — blurring averages colour towards
   grey, and pushing saturation back up is what stops the bar looking like a
   dirty pane when something coloured passes under it. */
@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .pg-plain .chrome[data-lifted='true'] .nav {
    /* 98%, raised from 82% at the owner's mark.

       82% was measured against BODY COPY and it was fine there: the leak
       bottoms out at 212/255, a ghost you cannot read. The headline is the
       case it was never checked against. "Write it down. The rest is done for
       you." is 40px display type, and a letter that size carries enough ink
       that 18% of it survives a 30px blur as a whole legible WORD sitting in
       the bar — the page's own headline, printed twice, once greyed.

       The arithmetic that set 82% only ever argued the floor: it showed that
       a 68% fill put the nav links at 4.18:1 and that 82% took them to 6.9:1.
       Going up from there cannot cost contrast, it can only add — so nothing
       that rule was protecting is at risk here. What 98% spends is the
       show-through, and the blur is what said "glass" in the first place. */
    background: color-mix(in srgb, var(--chrome-nav) 98%, transparent);
    /* 30px, not 22. At 22 the phone's own header was still READABLE through
       the bar — shapes behind glass is the effect; legible words behind glass
       is a translucent div. The fill is what carries contrast (unchanged at
       82%), so a wider radius costs nothing and buys the whole illusion. */
    -webkit-backdrop-filter: saturate(180%) blur(30px);
    backdrop-filter: saturate(180%) blur(30px);
    border-bottom-color: rgb(var(--lift-rgb) / .11);
  }
}

/* 2 · THE CLOSE IS A SHEET.
   It was a band butted against the section above it. A sheet has a top radius
   and sits ON the thing it covers — the gesture every native OS uses to say
   "this is the last screen, and it came from below". */
.pg-plain .sec--lime {
  position: relative; z-index: 2;
  border-start-start-radius: clamp(20px, 2.6vw, 34px);
  border-start-end-radius: clamp(20px, 2.6vw, 34px);
  overflow: hidden;
  box-shadow: 0 -2px 0 rgb(var(--lift-rgb) / .04),
              0 -30px 60px -34px rgb(var(--shade) / .45);
}

/* 3 · CONTROLS ANSWER THE FINGER.
   Two things separate a native control from a link: it never flashes a grey
   box on tap, and it moves under the press. The highlight goes; the press is
   a 3% scale on the way down with no transition, and the spring on the way
   back — which is the asymmetry a real button has. */
.pg-plain { -webkit-tap-highlight-color: transparent; }
.pg-plain a, .pg-plain button, .pg-plain summary,
.pg-plain [role='button'] { -webkit-tap-highlight-color: transparent; }
.pg-plain .btn, .pg-plain .store, .pg-plain .nav__link,
.pg-plain .thm__b, .pg-plain .pin__rail button {
  transition: transform .34s var(--ease-spring), background var(--t-ui) var(--ease-out),
              color var(--t-ui) var(--ease-out), box-shadow .34s var(--ease-out);
}
.pg-plain .btn:active, .pg-plain a.store:active,
.pg-plain .nav__link:active, .pg-plain .thm__b:active {
  transform: scale(.968); transition-duration: 0s;
}
:root { --ease-spring: cubic-bezier(.34, 1.56, .64, 1); }

/* 4 · THE PAGE ENDS WHERE THE PAGE ENDS.
   Rubber-banding past the top or bottom shows the browser's own ground behind
   the document, which is the moment a page stops feeling like an app. */
/* On the ROOT, not the body: overscroll-behavior is read off the root element
   for the viewport, and a copy on <body> does nothing at all. */
html:has(.pg-plain) { overscroll-behavior-y: none; }

/* 5 · GOING SOMEWHERE IS A MOVE, NOT A JUMP.
   Anchor jumps and the rail's own scrollIntoView both become travel. Off for
   anyone who has asked for less motion, where an instant jump is correct. */
@media (prefers-reduced-motion: no-preference) {
  html:has(.pg-plain) { scroll-behavior: smooth; }
}

/* ═══ 6 · THE LARGE TITLE COLLAPSES INTO THE BAR ══════════════════════════
   The rule both platforms use: the compact title appears at the exact moment
   the large one passes under the bar, and leaves when it comes back out. It
   is not a scroll threshold picked by eye — it is the heading's own bottom
   edge crossing the bar's bottom edge, which is why it always feels like the
   same object moving rather than two labels swapping.

   The slot is the link's box, and the title is centred ON it absolutely
   rather than laid out beside it. Laid out, every claim name is a different
   width and the whole centre of the bar would shuffle sideways seven times on
   the way down the section. */
.pg-plain .nav__links { position: relative; }
.pg-plain .nav__title {
  position: absolute; left: 50%; top: 50%; translate: -50% -50%;
  white-space: nowrap; pointer-events: none;
  display: inline-flex; align-items: baseline; gap: 7px;
  font-size: 13.5px; font-weight: 500; color: var(--ink);
  opacity: 0; transition: opacity .22s var(--ease-out);
}
.pg-plain .nav__title b {
  font-family: var(--font-display); font-weight: 700; font-size: 11px;
  letter-spacing: .08em; color: var(--muted);
}
.pg-plain .chrome[data-navtitle='on'] .nav__title { opacity: 1; }
.pg-plain .chrome[data-navtitle='on'] .nav__link {
  opacity: 0; pointer-events: none;
  transition: opacity .18s var(--ease-out);
}
/* A link faded to nothing but still in the tab order is a trap. Tabbing to it
   brings it back and stands the title down for as long as it is focused — so
   the keyboard route through the page is never the one that disappears. */
.pg-plain .chrome[data-navtitle='on'] .nav__link:focus-visible { opacity: 1; }
.pg-plain .nav__links:has(.nav__link:focus-visible) .nav__title { opacity: 0; }

/* ═══ 7 · THE SWAP IS PACED BY THE SCROLL, NOT BY A CONSTANT ══════════════
   A fixed 340/460ms cross-fade is the giveaway that a page is running an
   animation AT you rather than with you: flick the wheel and the screens lag
   a beat behind the words; inch down and they snap.

   The driver measures scroll velocity and latches a duration into --swap at
   the moment the screen changes — latched, not live, so a duration is never
   edited underneath a transition that is already running. Fast scrolling gets
   a short tight fade, slow scrolling gets the long eased one.

   The rows inside the screen no longer need a duration at all — they are
   scrubbed by the scroll, below. */
.pin__scr {
  transition: opacity var(--swap, .34s) cubic-bezier(.4, 0, 1, 1),
              visibility 0s linear var(--swap, .34s);
}
.pin__scr.is-live {
  transition: opacity calc(var(--swap, .34s) * 1.35) cubic-bezier(.22, 1, .36, 1),
              visibility 0s linear 0s;
}
@media (prefers-reduced-motion: reduce) {
  .pg-plain .nav__title, .pg-plain .nav__link { transition: none; }
}

/* On a phone the bar's links are hidden entirely, so there is no link for the
   title to collapse from — and the slot it centres on has no width. Dropping
   .nav__links back to `position: static` hands the title's containing block to
   .nav, so it centres on the BAR instead, which is where it belongs when
   nothing else is competing for the middle.

   Truncation rather than overlap: between the brand and the button there is
   not much bar left at 375, and a title that runs under "Get the app" is worse
   than one that ends in an ellipsis. */
.pg-plain .nav { position: relative; }
@media (max-width: 899px) {
  /* Centred on the BAR it sat at x=188 with "Get the app" starting at 205 —
     the title ran under the button. The middle of a bar is only the middle of
     the bar when both sides weigh the same, and here the brand is 34px against
     a 154px button. So the slot becomes a real flex item that takes exactly
     the gap between them, and the title centres in THAT, truncating at its own
     edge rather than under something else. */
  .pg-plain .nav__links {
    display: block; position: relative; flex: 1 1 auto;
    min-width: 0; margin-inline: 10px;
  }
  .pg-plain .nav__links .nav__link { display: none; }
  .pg-plain .nav__title {
    position: static; translate: none; display: block; text-align: center;
    font-size: 12.5px; max-width: 100%;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .pg-plain .nav__title b { margin-right: 6px; }
}
/* Under 420 the gap is not worth a number as well as a name. */
@media (max-width: 419px) {
  .pg-plain .nav__title b { display: none; }
}

/* The bar's 28px gaps were written for a desktop row with three links in it.
   On a phone they cost 56px of the only space the title has: measured at 375,
   the slot came out 79px wide and "Writing it down" truncated to about four
   letters. With the gap at 12 and the slot's own margins gone, the same slot
   is 131px and the longest claim name fits whole.

   Below 360 it does not fit at any setting — 76px of slot — and four letters
   and an ellipsis is worse than an honest empty bar. */
@media (max-width: 560px) {
  .pg-plain .nav { gap: 12px; }
  .pg-plain .nav__links { margin-inline: 0; }
}
@media (max-width: 359px) {
  .pg-plain .nav__title { display: none; }
}

/* ═══ 8 · THE CUT-OUTS ════════════════════════════════════════════════════
   The page ships `viewport-fit=cover`, which is the flag that says "draw into
   the corners" — and having said it, it has to honour the insets, or on a
   notched phone in landscape the brand sits under the notch and the menu's
   last row sits under the home indicator. `max()` means non-notched devices
   are untouched: env() is 0 there and the original value wins. */
@supports (padding: max(0px, env(safe-area-inset-left))) {
  .pg-plain .nav {
    padding-left: max(clamp(16px, 3vw, 30px), env(safe-area-inset-left));
    padding-right: max(clamp(16px, 3vw, 30px), env(safe-area-inset-right));
  }
  .pg-plain .menu {
    padding-left: max(var(--gut), env(safe-area-inset-left));
    padding-right: max(var(--gut), env(safe-area-inset-right));
    padding-bottom: max(18px, env(safe-area-inset-bottom));
  }
}

/* ═══ 9 · THE PAGE CONTROL MOVES WITH THE PAGES ═══════════════════════════
   The rail already elongates its active mark, which is the right gesture —
   but on a fixed .45s while the screens it indexes now run on the scroll's
   own pace. Indexed to --swap it arrives with the screen instead of trailing
   it, and on the spring rather than an ease-out, because a page control that
   settles is the one detail every native pager has and no web one does. */
.pin__rail button::before {
  transition: width var(--swap, .45s) var(--ease-spring),
              background-color var(--swap, .45s) var(--ease-out);
}
@media (prefers-reduced-motion: reduce) {
  .pin__rail button::before { transition: none; }
}

/* ═══ 10 · THE MENU IS A PANEL, SO IT SHOULD LAND LIKE ONE ════════════════
   Square-cornered and linear, it reads as markup appearing. Rounded at the
   bottom and landing on the spring, it reads as a surface arriving — and its
   rows answer a finger the way the rest of the page's controls now do. */
.pg-plain .menu {
  border-end-start-radius: 18px; border-end-end-radius: 18px;
  animation: menuIn .34s var(--ease-spring) both;
}
.pg-plain .menu__a {
  border-radius: 10px; padding-inline: 8px;
  transition: transform .3s var(--ease-spring), background-color var(--t-ui) var(--ease-out);
}
.pg-plain .menu__a:active { transform: scale(.982); transition-duration: 0s; }
@media (prefers-reduced-motion: reduce) {
  .pg-plain .menu { animation: none; }
  .pg-plain .menu__a { transition: none; }
}

/* ═══ 11 · ONE VOICE FOR EMPHASIS, NOT TWO ════════════════════════════════
   The hero's emphasis is written by hand. The section heading under it was
   still wearing the lime marker — so the page made its point twice in two
   different languages, forty lines apart, and the marker read as a leftover
   rather than a choice. It is the same gesture here: the words that carry the
   claim are written, and the pen goes under them.

   Inline rather than a block, because this emphasis sits INSIDE the sentence
   rather than owning its own line. inline-block with nowrap so it can never
   split across two lines — a split inline gets its stroke drawn twice, which
   is the same fault the hero's line had before it became a block.

   The stroke draws when the section arrives rather than at load: at five
   thousand pixels down the page, an animation that ran on load is one nobody
   ever sees happen. */
.sec--pin .h em {
  font-family: var(--font-hand); font-weight: 700; font-style: normal;
  font-size: 1.26em; line-height: .82; letter-spacing: 0;
  color: var(--ink-hi); background-color: transparent;
  border-radius: 0; margin: 0; padding: 0 .03em .05em;
  display: inline-block; white-space: nowrap; vertical-align: baseline;
  background-image: var(--pen);
  background-repeat: no-repeat; background-position: 0 100%;
  background-size: 100% .11em;
}
.sec--pin.is-rev .h em { animation: penline .84s cubic-bezier(.2, .9, .3, 1) .12s backwards; }
@media (prefers-reduced-motion: reduce) { .sec--pin .h em { animation: none; } }


/* ═══ THE SCREEN IS SCRUBBED BY THE SCROLL, NOT PLAYED AT THE READER ══════
   The phone was running a demo on its own clock: count for 2.2s, hold 5.2,
   fade, reset, repeat. That is a video. It plays whether the reader is moving,
   stopped, or scrolling back up, so it is never in step with the sentence
   arriving beside it — and the reader can feel that even if they could not
   say what is wrong.

   Everything on the screen is now a function of ONE number. The driver sets
   --p: how far through its own beat the live claim has come, 0 the frame its
   words reach the reading line and 1 the frame the next claim's words get
   there. The rows deal themselves out along it and put themselves away when
   you scroll back up; the total counts in JS on the same number, because a
   figure has to be formatted and CSS cannot do that.

   Nothing has a duration of its own any more, which is the whole point: with
   no clock there is nothing to be out of step with. Stop scrolling and the
   screen stops exactly where you left it, mid-deal, like a thing being held
   rather than a thing being shown.

   0.055 a row, 1/9 of the beat to complete, so six rows are down by about 40%
   of the claim and the rest of the beat is reading time. */
.pin__scr .k__row,
.pin__scr .k__ent,
.pin__scr .k__day {
  --shown: clamp(0, (var(--p, 1) - var(--i, 0) * 0.055 - 0.03) * 9, 1);
  opacity: var(--shown);
  transform: translateY(calc((1 - var(--shown)) * 9px));
}
@media (prefers-reduced-motion: reduce) {
  .pin__scr .k__row,
  .pin__scr .k__ent,
  .pin__scr .k__day { opacity: 1; transform: none; }
}

/* ═══ THE OTHER FIVE SCREENS SCRUB TOO ════════════════════════════════════
   Two of them had stopped moving altogether when the loop went, and that is
   worth saying plainly rather than quietly fixing: the bars on the reports
   screen are `scaleX(0)` until something adds `.is-on` to the app root, and
   nothing does any more — so that screen was showing five empty tracks. The
   trial balance was the other way round: its rows animate ON LOAD, ungated,
   so they had already played four thousand pixels before the reader arrived.

   Both are on --p now, like the books list. Every screen in the phone is a
   function of one number and nothing in it has a clock.

   The transitions and animations these elements came with are turned OFF, not
   retuned. A transition on a property that the scroll is already driving
   frame-by-frame fights it: every frame starts a new 0.8s ease toward a target
   that has already moved, so the bar lags the thumb by most of a second. */

/* ── the trial balance: rows, then the totals, then the verdict ───────── */
.pin__scr .k [data-land] .k__trow,
.pin__scr .k__tfoot,
.pin__scr .k__banner,
.pin__scr .k__cur {
  --shown: clamp(0, (var(--p, 1) - var(--i, 0) * 0.045 - 0.03) * 9, 1);
  animation: none;
  opacity: var(--shown);
  transform: translateY(calc((1 - var(--shown)) * 8px));
}
/* The stamp is the POINT of a trial balance, so it lands after the arithmetic
   that earns it — nine rows, then the totals, then "every currency balances". */
.pin__scr .k__tfoot  { --i: 10; }
.pin__scr .k__cur    { --i: 11.5; }
.pin__scr .k__banner { --i: 12.5; }

/* ── the reports screen: the line draws, then the bars fill ───────────── */
.pin__scr .k__twin path[stroke] {
  --shown: clamp(0, (var(--p, 1) - var(--i, 0) * 0.08 - 0.04) * 4, 1);
  stroke-dasharray: 1;
  stroke-dashoffset: calc(1 - var(--shown));
}
/* :nth-of-type counts PATHS, and the first path is the filled area under the
   line — so the green stroke is #2 and the pink is #3. Indexed off #2 the
   green drew second and the pink first, which is backwards: the pink is the
   money going out and it should answer the green, not lead it. */
.pin__scr .k__twin path[stroke]:nth-of-type(3) { --i: 1; }
.pin__scr .k__twin path[fill] {
  opacity: clamp(0, (var(--p, 1) - 0.12) * 3, 1);
}
.pin__scr .k__bars > .cap,
.pin__scr .k__bar {
  --shown: clamp(0, (var(--p, 1) - var(--i, 0) * 0.06 - 0.06) * 9, 1);
  /* the fill runs behind its own label, the way a chart draws */
  --grow:  clamp(0, (var(--p, 1) - var(--i, 0) * 0.06 - 0.11) * 6, 1);
  opacity: var(--shown);
  transform: translateY(calc((1 - var(--shown)) * 8px));
}
.pin__scr .k__bars > .cap { --i: -0.5; }
.pin__scr .k__bars .k__bar:nth-child(2) { --i: 0; }
.pin__scr .k__bars .k__bar:nth-child(3) { --i: 1; }
.pin__scr .k__bars .k__bar:nth-child(4) { --i: 2; }
.pin__scr .k__bars .k__bar:nth-child(5) { --i: 3; }
.pin__scr .k__bars .k__bar:nth-child(6) { --i: 4; }
.pin__scr .k__bar .t i {
  transform: scaleX(var(--grow, 1));
  transition: none;
}

@media (prefers-reduced-motion: reduce) {
  .pin__scr .k [data-land] .k__trow,
  .pin__scr .k__tfoot, .pin__scr .k__banner, .pin__scr .k__cur,
  .pin__scr .k__bars > .cap, .pin__scr .k__bar { opacity: 1; transform: none; }
  .pin__scr .k__bar .t i { transform: scaleX(1); }
  .pin__scr .k__twin path[stroke] { stroke-dashoffset: 0; }
  .pin__scr .k__twin path[fill] { opacity: 1; }
}

/* ═══ THE SENTENCE IS ON THE SAME CLOCK AS THE SCREEN ═════════════════════
   The rule under the claim number was a class-driven transition: it snapped
   from 26 to 54px over half a second the instant the claim went live, and then
   sat there for the rest of the beat with nothing to do. Given the same number
   the phone is running on, it draws out while the screen fills and is full
   when the screen is — so the two columns are visibly one movement rather than
   two things that happen near each other.

   Nothing else in the copy moves. The point of the tie is that it is the only
   one; a whole column animating on scroll is the thing this page spent three
   passes getting away from. */
.pin__item.is-on .pin__n i {
  width: calc(26px + 28px * clamp(0, var(--cp, 1) / 0.45, 1));
  transition: none;
}
@media (prefers-reduced-motion: reduce) {
  .pin__item.is-on .pin__n i { width: 54px; }
}

/* ═══ THE LAST HEADING JOINS THE OTHER TWO ════════════════════════════════
   The hero is written by hand and so is "done for you"; the close was the one
   heading on the page with no emphasis at all, which left the system finishing
   two thirds of the way down. It costs nothing to complete — the face is
   already loaded for the two above it.

   Its own pen, though. The lime band does not invert: it is the same note in
   whichever room it is read in, so a stroke drawn in the page's ink would go
   white on it at night and vanish. This one is mixed from --on-lime like every
   other mark on that band. */
:root, :root[data-theme='light'] {
  --pen-lime: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 560 15' preserveAspectRatio='none'%3E%3Cpath d='M5 9.6C92 4.4 176 12.2 262 7.4S420 3.6 555 8.6' fill='none' stroke='%230C0E1C' stroke-width='3.4' stroke-linecap='round'/%3E%3C/svg%3E");
}
.sec--lime .h em {
  font-family: var(--font-hand); font-weight: 700; font-style: normal;
  font-size: 1.24em; line-height: .82; letter-spacing: 0;
  color: var(--on-lime); background-color: transparent;
  border-radius: 0; margin: 0; padding: 0 .03em .05em;
  display: inline-block; white-space: nowrap; vertical-align: baseline;
  background-image: var(--pen-lime);
  background-repeat: no-repeat; background-position: 0 100%;
  background-size: 100% .1em;
}
.sec--lime.is-rev .h em { animation: penline .84s cubic-bezier(.2, .9, .3, 1) .12s backwards; }
@media (prefers-reduced-motion: reduce) { .sec--lime .h em { animation: none; } }

/* ═══ BOTH BADGES ARE CARDS NOW, NOT ONE BLACK SLAB ═══════════════════════
   The black pill is the store's own artwork convention, and next to the
   outlined iOS one it read as two different objects doing the same job — one
   shouting, one whispering. They are the same card now: the page's card
   surface, a hairline, and the light shadow a raised sheet casts.

   What still separates them is the only thing that should: one is a link and
   answers a finger, the other carries SOON and has nowhere to go. */
.store {
  --store-ink: var(--ink-hi);
  background: var(--card); color: var(--store-ink);
  border-radius: 14px; padding: 9px 17px 10px 14px;
  box-shadow: inset 0 0 0 1px var(--hairline),
              0 1px 2px rgb(0 0 0 / .05),
              0 12px 24px -18px rgb(0 0 0 / .35);
}
.store small { color: var(--muted); }
.store b { color: var(--store-ink); }
a.store:hover {
  box-shadow: inset 0 0 0 1px var(--soft),
              0 2px 4px rgb(0 0 0 / .07),
              0 18px 30px -18px rgb(0 0 0 / .42);
}
/* The Apple mark is a solid glyph, so on a light card it needs the page's ink
   rather than the white it had against black. */
.store--soon {
  --store-ink: var(--ink-hi);
  background: var(--card);
  /* The shorthand `padding` above resets padding-right, and the SOON pill is
     positioned in that space — without this it prints across "App Store".
     Shorthand after a longhand is the oldest trap in this file. */
  padding-right: 76px;
  box-shadow: inset 0 0 0 1px var(--hairline),
              0 1px 2px rgb(0 0 0 / .05),
              0 12px 24px -18px rgb(0 0 0 / .35);
}
.store--soon em { background: var(--ink-hi); color: var(--card); }

/* ═══ THE BAR KNOWS HOW FAR DOWN YOU ARE ══════════════════════════════════
   A landing page six thousand pixels long, a section that pins for five of
   them, and nothing anywhere telling the reader how much of it is left. The
   browser's own scrollbar is no help: while the device is pinned the bar
   barely moves, so the one signal the page had was actively misleading.

   A hairline along the bottom edge of the bar, which is the one element
   always in front of the reader. It is set from scroll position directly —
   no transition, because a progress line that eases is a progress line that
   lies about where you are. */
.pg-plain .nav { position: relative; }
.pg-plain .nav__prog {
  position: absolute; left: 0; bottom: -1px; height: 2px; z-index: 2;
  /* SCALED, NOT RESIZED. Changing `width` repaints this element, and it sits
     inside a bar carrying a 30px backdrop-filter — so every frame of scroll
     re-ran the blur across the whole bar. A transform is composited instead:
     the bar is never repainted, and the line still tracks exactly. */
  width: 100%; transform-origin: 0 50%; transform: scaleX(var(--s, 0));
  will-change: transform; pointer-events: none;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--brand) 55%, transparent), var(--brand));
  border-end-end-radius: 2px;
  opacity: 0; transition: opacity .3s var(--ease-out);
}
/* It appears with the bar's own background — at the very top of the page
   there is nothing behind you, so there is nothing to report. */
.pg-plain .chrome[data-lifted='true'] .nav__prog { opacity: 1; }

/* ── the price, said out loud ────────────────────────────────────────────
   On the lime band, so it is mixed from --on-lime like everything else there:
   the close does not invert, and a colour inherited from the page would go
   white on it at night. Two weights, one line — the offer, then its terms. */
.freely {
  max-width: 34rem; margin: clamp(18px, 2.4vw, 26px) auto 0;
  text-align: center; font-size: 14.5px; line-height: 1.5;
  color: color-mix(in srgb, var(--on-lime) 78%, transparent);
}
.freely b {
  display: block; font-family: var(--font-display); font-weight: 700;
  font-size: 16.5px; letter-spacing: -.015em; color: var(--on-lime);
  margin-bottom: 2px;
}

/* ═══ WHOSE BOOKS THESE ARE ═══════════════════════════════════════════════
   The row is deliberately wider than the window: you are meant to feel there
   are more of these people than fit, which is the point of the band. It moves
   with the scroll rather than on a timer, like the phone above it — and it is
   a TRANSFORM, so the drift is composited and costs no layout.

   No horizontal scrollbar on the page: the row is translated inside a clipped
   rail, never scrolled. A landing page that scrolls sideways under the thumb
   is the fastest way to lose a phone reader. */
.sec--folk { padding-block: clamp(64px, 9vw, 130px) clamp(56px, 8vw, 110px); overflow: hidden; }
.sec--folk .h { max-width: 22ch; }
.sec--folk .h em {
  font-family: var(--font-hand); font-weight: 700; font-style: normal;
  font-size: 1.24em; line-height: .82; letter-spacing: 0;
  color: var(--ink-hi); background-color: transparent;
  border-radius: 0; margin: 0; padding: 0 .03em .05em;
  display: inline-block; white-space: nowrap; vertical-align: baseline;
  background-image: var(--pen); background-repeat: no-repeat;
  background-position: 0 100%; background-size: 100% .1em;
}
.sec--folk.is-rev .h em { animation: penline .84s cubic-bezier(.2, .9, .3, 1) .12s backwards; }

.folk { margin-top: clamp(30px, 4vw, 54px); }
.folk__row {
  display: flex; gap: clamp(14px, 1.6vw, 22px); list-style: none; margin: 0;
  padding-inline: var(--gut, 20px);
  width: max-content;
  transform: translate3d(calc(var(--drift, 0) * -1px), 0, 0);
  will-change: transform;
}
.folk__i { width: clamp(236px, 23vw, 310px); flex: none; }
/* ONE SLIP OUT OF ONE BOOK.
   The paper is the sign-in page's paper: same ruling pitch, same blue a cash
   book is ruled in, same margin down the left. A reader who gets as far as
   opening an account should meet a surface they have already seen.

   The handwriting SITS ON THE RULES rather than floating between them — every
   row is exactly --row tall and the text is bottom-aligned inside it, which is
   the one detail that separates ruled paper from a striped background. */
.slip {
  --row: 27px;
  --marg-x: 30px;
  --paper: var(--card);
  --ruled: rgb(var(--rule-rgb) / .20);
  --marg: rgb(var(--out-rgb) / .22);
  position: relative;
  border-radius: 13px;
  padding: 0 16px 14px calc(var(--marg-x) + 12px);
  border: 1px solid var(--hairline);
  background-color: var(--paper);
  background-image:
    linear-gradient(var(--marg), var(--marg)),
    repeating-linear-gradient(to bottom, transparent 0 calc(var(--row) - 1px),
      var(--ruled) calc(var(--row) - 1px) var(--row));
  background-size: 1px 100%, 100% 100%;
  background-position: var(--marg-x) 0, 0 0;
  background-repeat: no-repeat, repeat;
  transform: rotate(var(--tilt, 0deg));
  box-shadow: 0 10px 22px -18px rgb(0 0 0 / .55);
}
/* The tilt is a paper tell, not a toy: it comes off entirely for anyone who
   has asked for less movement, and the slip squares up. */
@media (prefers-reduced-motion: reduce) { .slip { transform: none; } }

.slip__h {
  height: calc(var(--row) * 2); display: flex; flex-direction: column;
  justify-content: flex-end; padding-bottom: 3px; margin-bottom: 1px;
}
.slip__h b {
  font-family: var(--font-display); font-weight: 600; font-size: 14px;
  letter-spacing: -.012em; color: var(--ink-hi); line-height: 1.1;
}
.slip__h span {
  font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase;
  color: var(--t700); line-height: calc(var(--row) - 14px);
}

/* Three rows whether the book used three or two, so every slip is the same
   height and the captions under them sit on one line together. A ragged row
   of cards reads as a bug; a ragged row of entries reads as a book. */
.slip__l { list-style: none; margin: 0; padding: 0; min-height: calc(var(--row) * 3); }
.slip__l li {
  height: var(--row); display: flex; align-items: flex-end; gap: 10px;
  justify-content: space-between;
}
/* The entry is written; the amount is written. Both in the hand, because a
   line typed next to a line written is two people, and this is one book. */
.slip__l span, .slip__l b, .slip__t span, .slip__t b {
  font-family: var(--font-hand); font-weight: 600; font-style: normal;
  line-height: 1.02; letter-spacing: .004em;
}
.slip__l span { font-size: 16.5px; color: var(--t700); }
.slip__l b { font-size: 17px; color: var(--ink-hi); font-variant-numeric: tabular-nums; white-space: nowrap; }
/* The out-entries are NOT dimmed. Measured against the ruled paper --muted
   lands at 4.42:1, under the 4.5 a 17px hand needs, and a ledger that greys
   out half its own lines is arguing that money spent matters less. The minus
   sign carries the difference, the way it does in the book. */
.slip__l b.o { color: var(--t700); }

/* The total is the only ruled-off line on the slip, and it is ruled off the
   way it is in a real book: a stroke under the last entry, not a box. */
.slip__t {
  height: calc(var(--row) * 1.5); margin: 0; display: flex; align-items: flex-end;
  justify-content: space-between; gap: 10px;
  border-top: 1.5px solid var(--divider);
  padding-top: 4px;
}
.slip__t span { font-size: 15px; color: var(--t700); }
.slip__t b { font-size: 19px; color: var(--ink-hi); white-space: nowrap; }
/* --error is the alert red and reads at 2.69:1 on paper in light. The owed
   chip's ink is the same idea tuned for a surface, which is what this is. */
.slip--owed .slip__t b { color: var(--chip-owed-ink); }

.folk__i p { margin: 14px 2px 0; font-size: 13px; line-height: 1.5; color: var(--muted); }
@media (prefers-reduced-motion: reduce) { .folk__row { transform: none; } }

/* ── THE PAIRING CUE IS A DESKTOP AFFORDANCE ──────────────────────────────
   "or press Cash in on the phone — the line lands here" describes something
   the reader can only SEE side by side. Stacked, the phone sits ~900px below
   the ledger on a 390 screen, so the sentence names a control that is off the
   screen and promises a line will land somewhere the reader is not looking.

   It is also not true there. hero.js only takes the manual path for a pointer
   that is not a touch — `if (e.pointerType !== 'touch') manual = true` — so on
   a phone the pairing is scroll-driven and pressing the key does nothing to
   it. A sentence that instructs a gesture the code declines to honour is worse
   than no sentence. It goes below the breakpoint where the two are side by
   side, and the hero gets the space back. */
@media (max-width: 899px) {
  .hero--ledger .book__cue { display: none; }
}

/* ═══ THE CLOSE ASKS FOR THE THING IT DESCRIBED ═══════════════════════════
   One primary action on the lime, in the same ink the pill button already
   used there, so the close gains an action rather than a new colour. */
.sec--lime .btn--open {
  margin-top: clamp(30px, 4vw, 44px);
  background: var(--on-lime); color: var(--lime);
  text-decoration: none;
  box-shadow: 0 12px 28px -16px rgb(0 0 0 / .6);
}
.sec--lime .btn--open:hover { background: var(--panel-a); }
.sec--lime .btn--open svg { width: 18px; height: 18px; flex: none; }

/* The fallback is a sentence, not a second button — a close with two things
   of equal weight in it is a close with no ask in it. */
.orelse { margin: 16px 0 0; }
.sec--lime .linky {
  border: 0; background: none; padding: 2px 1px; cursor: pointer;
  font: inherit; font-size: 14px;
  color: color-mix(in srgb, var(--on-lime) 72%, transparent);
  text-decoration: underline; text-underline-offset: 3px;
  text-decoration-color: color-mix(in srgb, var(--on-lime) 34%, transparent);
}
.sec--lime .linky:hover { color: var(--on-lime); text-decoration-color: currentColor; }
.mailbox { margin-top: 4px; }
.sec--lime .mailbox .form--pill { margin-top: 18px; }

/* ═══ THE DEVICE, CLOSER TO A REAL ONE ════════════════════════════════════
   Reference asked for: the InkBank template's hero phone. Theirs is a single
   1302x2649 AVIF — a rendered photograph of an iPhone with the app composited
   into it, no CSS radius or shadow at all, because every edge and highlight is
   baked into the file. Two reasons this page cannot take that road: the file
   is someone else's licensed template asset, and a flat picture cannot scrub —
   the screens here change with the scroll, which is the thing that was asked
   for in the first place.

   So the drawn one gets what the render has and it did not:

   1 · A BLACK BEZEL. The screen met the titanium directly, and that single
       missing band is most of why a drawn phone reads as a rounded rectangle
       with a gradient in it. On a real device the eye reads depth off the dark
       ring between the rail and the glass.
   2 · A RAIL WITH TWO SPECULARS rather than one sweep — polished metal picks
       up the sky at the top-left and the ground at the bottom-right, and it is
       darkest across the middle, not at one end.
   3 · BUTTONS WITH A SIDE TO THEM. 2px slivers read as scratches; 3px with a
       lit top edge and a seated shadow read as hardware. */
.ph {
  background:
    linear-gradient(150deg,
      #D4DAE3 0%, #8A919C 6%, #3A3F47 26%,
      #16181D 52%, #1C1F25 68%, #5A616B 92%, #AEB5C0 100%);
}
/* The bezel is drawn OUT from the screen rather than in from the rail, so it
   stays the same weight whatever padding the breakpoint gives the rail. */
.ph__scr {
  box-shadow:
    0 0 0 2px #08090B,
    inset 0 0 0 1px rgba(0, 0, 0, .9),
    inset 0 1px 2px rgba(0, 0, 0, .5);
}
.ph__btn {
  width: 3px; border-radius: 1.5px;
  background: linear-gradient(180deg, #9AA2AB 0%, #4A515A 18%, #2A2F36 70%, #1B1F24 100%);
  box-shadow: 0 0 0 .5px rgba(0, 0, 0, .55), inset 0 .5px 0 rgba(255, 255, 255, .4);
}
.ph__btn--v1 { left: -3px; }
.ph__btn--v2 { left: -3px; }
.ph__btn--pw { right: -3px; }

/* ── AND THE THREE THINGS THAT STILL DID NOT MATCH ───────────────────────
   Put side by side with the reference render, the drawn phone still read as a
   screenshot with rounded corners. Measured, not argued:

   1 · THICKNESS. Every device on this page rendered a 6px rail — 1.7% of its
       own body width. Real hardware is about 4% a side and the reference is
       thicker again. A body that is all screen and a hairline of metal has no
       edge for the eye to read, so the bezel added above had nothing to sit
       in. Percentage padding, so one rule covers all six devices at whatever
       width their section hands them.
   2 · COLOUR. Natural titanium, not charcoal. This is the one that did most
       of the damage: no amount of bezel makes a near-black slab read as an
       iPhone. Light catches top-left and bottom-right and the band is darkest
       across the middle, where the metal turns away from you.
   3 · BUTTONS THAT STAND PROUD. Flush slivers read as scratches in the paint.
       A real one is a machined nub with a lit top edge and a shadow where it
       seats into the rail — and it breaks the silhouette, which is what makes
       you read it as an object rather than a drawing. */
.ph {
  padding: 3.2%;
  background:
    linear-gradient(148deg,
      #F4F6F9 0%, #C6CBD3 4%, #949AA4 13%,
      #5F656F 31%, #41464F 50%, #5B616B 69%,
      #9CA2AC 87%, #DADEE4 96%, #FBFCFD 100%);
}
.ph__btn {
  width: 4px;
  background: linear-gradient(180deg, #E6E9ED 0%, #AAB0B9 20%, #787E88 58%, #4E545D 100%);
  box-shadow: 0 0 0 .5px rgba(0, 0, 0, .42), inset 0 1px 0 rgba(255, 255, 255, .78);
}
.ph__btn--v1, .ph__btn--v2 { left: -4px; border-radius: 2px 0 0 2px; }
.ph__btn--pw { right: -4px; border-radius: 0 2px 2px 0; }

/* ═══ THE DEVICE IS THE OWNER'S FIGMA MOCKUP NOW ══════════════════════════
   Pulled from his own file — "iPhone mockups (Community)", node 31:88, the
   variant named "Pill=true, Color=Silver" — exported at 3x, then two things
   removed from it locally: the pale blue canvas it was standing on, and the
   white "01" screen. What is left is the hardware with a HOLE where the glass
   goes: 1249x2544, 26KB as WebP, one file for all six devices on the page.

   The frame is drawn OVER the screen, not behind it. That is the whole trick,
   and it is why this could be a picture at all: the seven app screens under it
   are still live DOM and still scrub with the scroll. A flat mockup with a
   screenshot baked in could not do that, which is why the reference site's
   render was never an option.

   The Dynamic Island survived the cut as an enclosed island the flood fill
   could not cross, so it comes along with the frame and sits over the live
   screen exactly as it does on the hardware. Our own drawn island and buttons
   stand down — the photograph has better ones. */
/* Every selector that reaches a device on this page, because the frame's own
   edges and highlights are in the picture now and anything CSS adds on top of
   them draws a rectangle round it. .stagepanel__rig .ph is 0,2,0 and outranked
   a bare .ph — its 1px spread ring was the hairline box that showed up around
   the hero phone once the gradient body stopped filling the element. */
.ph,
.stagepanel__rig .ph,
.panel__dev .ph,
.wall__dev .ph,
.book__dev .ph,
.pin__scr .ph {
  aspect-ratio: 1249 / 2544;
  padding: 0;
  background: none;
  border-radius: 0;
  box-shadow: none;
}
.ph::before {
  content: ''; position: absolute; inset: 0; z-index: 4; pointer-events: none;
  background: url("../img/device/iphone.webp?v=c08c855a") center / 100% 100% no-repeat;
}
/* Measured off the asset itself rather than guessed: the transparent hole runs
   from 5.204% in on the left to 4.964% on the right — not symmetric, because
   the volume and power nubs stand proud of the body and widen its box. */
/* ── THE SCREEN IS CUT TO THE FRAME, NOT GUESSED AT ────────────────────
   Every number here is measured out of img/device/iphone.webp by flood-
   filling its outer transparency and reading the inner hole that is left:

     hole  x 65..1186, y 56..2488  in a 1249x2544 asset
     insets  left 5.204%  right 4.964%  top 2.201%  bottom 2.162%
     corner  174px — and CIRCULAR, as a phone's is

   The radius was `6.4%`, and a single percentage on a box this tall is not
   one radius but two: 6.4% of the width is 72px and 6.4% of the height is
   156px. So the screen's corners were 72px across where the frame's cut-out
   is 174 — less than half — and the white of the app rode out past the black
   of the bezel at all four corners. Writing it as `15.51% / 7.15%` gives
   174px horizontally AND 174px vertically at any size the device is drawn
   at, which is the circle the asset actually has. */
.ph__scr { position:absolute; left:5.204%; right:4.964%; top:2.201%; bottom:2.162%;
  border-radius:15.51% / 7.15%; box-shadow:none; }
/* Deliberately squarer than the hole. The frame is on top, so a screen corner
   that reaches PAST the hole is trimmed by the art; one that falls short would
   show the page through the glass. Erring square is the safe direction. */
/* The glare goes too, and it was the one that showed. It is inset:0 with its
   own 31px radius, so once the frame stopped filling the element box it
   painted a white rounded RECTANGLE across the whole device — a faint light
   outline round the phone, strongest at the top-left where the gradient
   starts. The export carries its own reflections; it does not need ours. */
.ph__island, .ph__btn, .ph__glare { display: none; }

/* ── THE STATUS ROW SITS ON THE ISLAND'S LINE ────────────────────────────
   Measured off the export: the Dynamic Island's centre is 3.20% down the
   glass. The status bar block is the right height already (its own centre is
   3.08%), but it was bottom-aligning its contents with 4u of padding under
   them, which put the time and the icons about 10px BELOW the island on the
   hero device — the empty band above them was the tell.

   iOS puts the time and the indicators on the island's centreline, one either
   side of it. align-items: center does exactly that, and because the block's
   height is unchanged nothing under it moves: the row rises into the gap that
   was already there instead of pushing the app down.

   Scoped to the marketing devices. app-ui.css is the app's own sheet and
   loads after this one, so the two-class selector is what wins, not order. */
.ph__scr .k__sb {
  align-items: center;
  padding-bottom: 0;
}

/* ═══ ONE BOOK, ALL THE WAY DOWN ══════════════════════════════════════════
   The spine, continued. Identical left to .book::before — the same
   max(gut, (100% - maxw) / 2) — so the two are collinear and read as one rule
   ruled down the whole sheet rather than two that happen to line up.

   z-index -1 is doing real work here: the hero paints over it, so the hero
   keeps its own spine and nothing doubles up, and the line only becomes
   visible where the page had stopped being a book. No offset to compute and
   nothing to keep in step when the hero changes height. */
.pg-plain { position: relative; }
/* z-index 0, not -1. A negatively stacked child paints BEHIND its parent's
   background, and body.pg-plain has an opaque one — so at -1 the spine drew
   and was then covered by the body itself. At 0 it sits above that background
   and below every section, all of which are transparent except the lime close,
   where the book has ended anyway.

   And it is the FIRST thing in the body so tree order puts it behind the hero:
   the hero keeps painting its own spine over this one, which is what stops the
   two collinear rules from doubling into a darker line for one screen. */
/* THE RULING, CONTINUED — and phase-locked to the hero's.
   The lines that rule the cash book stopped with it. They run the whole sheet
   now, which is the last thing the page needed to stop being sections on a
   ground and start being paper.

   Phase is the whole difficulty and the whole point. The hero rules from the
   top of .book__rows; a page-level gradient rules from the top of the body,
   and the two land on different lines unless the second is told where the
   first started. js measures that offset once and writes --rule-phase, so a
   line that begins in ink in the hero arrives, unbroken and on pitch, at the
   close. Without it the seam is a visible stutter.

   Much fainter than the hero's .34: the hero's ruling is the ledger and has
   to be read, this is the stock it is printed on and only has to be felt. */
.pg-plain { --row: clamp(37px, calc(9.056vw - 74.96px), 55.45px); }
.sheet {
  position: absolute; z-index: 0; inset: 0;
  pointer-events: none;
  /* A TILED line, not a repeating gradient. The obvious construction —
     repeating-linear-gradient with a 1px band every --row — DROPS BANDS when
     the period is fractional, and --row is 55.4464px here. Measured at full
     opacity to be certain it was not a phase error: lines came out at 133,
     189, 299, 355, 466 … a subset of the right positions, gaps of 56, 110,
     56, 111. Chrome rasterises the bands and loses roughly every third one.

     One 1px line in a tile --row tall, repeated by background-repeat, renders
     every line: 55, 56, 55, 56 … mean 55.47, stdev 0.50, which is the correct
     rounding of a fractional pitch rather than a pattern with holes in it. */
  background-image: linear-gradient(to bottom,
    rgb(var(--rule-rgb) / .13) 0 1px, transparent 1px);
  /* --sheet-row is written by js from the hero's own resolved ruling. The
     --row fallback only covers the moment before that runs. */
  background-size: 100% var(--sheet-row, var(--row));
  background-repeat: repeat;
  background-position: 0 var(--rule-phase, 0px);
}
/* the bound edge and the margin rule, in the gutter the ruling runs behind */
.sheet::before {
  content: ''; position: absolute; top: 0; bottom: 0; left: 0;
  width: calc(max(var(--gut), calc((100% - var(--maxw)) / 2)) + 1px);
  /* THE BOUND EDGE. The gutter is where the leaves go down into the stitching,
     so it is darkest at the far left and gone by the time it reaches the
     writing. Same gradient .book__bind gives the hero, run the full height. */
  background: linear-gradient(to right,
    rgb(var(--shade) / .20) 0, rgb(var(--shade) / .05) 42%, transparent 100%);
  border-right: 1px solid rgb(var(--out-rgb) / .30);
}
/* ONE rule, not two. The hero's note calls its margin "doubled" and the
   original .book::before is written as a 5px box with border-inline — but a
   later rule narrows it to width:1px with border-right:0, so what the hero
   actually draws today is a single 1px rule at x100. Measured, not read: the
   sheet matched the CSS's intent and the hero matched the CSS's behaviour, and
   the seam stepped sideways by 4px. The sheet follows the hero. */

/* ── EACH SECTION IS A LEAF LAID ON THE ONE BEFORE ───────────────────────
   The hero ends on .book__edge — the cut edges of the sheets underneath it.
   Below it the sections simply butted together, which is a stack of divs, not
   a stack of paper. Every leaf now carries the same cut edge along its top,
   so the boundary between two sections reads as one sheet resting on another.

   --lift-rgb inverts with the theme (white on the night side, near-black on
   the day side), so the same three hairlines describe an edge in both. */
[data-leaf] { position: relative; }
[data-leaf]::before {
  content: ''; position: absolute; z-index: 2; left: 0; right: 0; top: 0;
  height: 9px; pointer-events: none;
  background: linear-gradient(to top,
    rgb(var(--lift-rgb) / .16) 0 1px, transparent 1px 4px,
    rgb(var(--lift-rgb) / .10) 4px 5px, transparent 5px 8px,
    rgb(var(--lift-rgb) / .06) 8px 9px, transparent 9px);
}
/* The lime close is a slab, not a leaf — an edge on it would be a hairline
   sitting on a colour that has already said the book is over. */
.sec--lime::before { content: none; }

/* ── the folio turns ─────────────────────────────────────────────────────
   Marginalia, written in the gutter the way a bookkeeper numbers a leaf: in
   the hand, small, and never in the way. It sits in the margin the spine
   rules off, so there is no column to give up for it.

   Below 1100 the gutter collapses to the page padding and there is no margin
   to write in — it stands down rather than sitting on top of the words. */
.folio {
  position: fixed; z-index: 3; left: 0; top: 50%; translate: 0 -50%;
  width: max(var(--gut), calc((100vw - var(--maxw)) / 2));
  margin: 0; display: none; pointer-events: none;
  flex-direction: column; align-items: center; gap: 2px;
  font-family: var(--font-hand); color: var(--faint);
  transition: opacity var(--t-ui) var(--ease-out);
}
/* 1200. The 1400 this replaces was derived from the WRONG measurement: I
   compared the mark against .wrap's box, which starts at (100vw - maxw) / 2 —
   but .wrap then pads itself in by --gut before any text, and the margin rule
   sits at max(--gut, that), so the real neighbour is a good deal further
   right. Measured against the rendered text instead, the clearance runs 40px
   at 1280, 85px at 1366, 110px at 1440, and it never reaches zero: the mark
   was hidden on the two commonest laptop widths for nothing.

   1200 keeps ~22px of air and stays clear of 1100, where the hero stacks and
   the copy comes further left. */
@media (min-width: 1200px) { .folio { display: flex; } }
.folio i { font-style: normal; font-size: 13px; letter-spacing: .04em; }
.folio b {
  font-size: 26px; font-weight: 700; line-height: 1; color: var(--t700);
  font-variant-numeric: tabular-nums;
}
/* The number is WRITTEN, not swapped: it lands the way a pen lands, once per
   leaf. Guarded on the value changing in js, so scrolling inside one section
   never replays it. */
.folio.is-turn b { animation: folio-turn .42s var(--ease-out); }
@keyframes folio-turn {
  from { opacity: 0; transform: translateY(6px) rotate(-3deg); }
}
@media (prefers-reduced-motion: reduce) { .folio.is-turn b { animation: none; } }

/* Set by js while a full-bleed band crosses the margin the folio is written
   in. It fades rather than blinking: a mark that vanishes reads as a bug, one
   that lifts off the page reads as deference. */
.folio.is-away { opacity: 0; }

/* ═══ THE INVITATION ══════════════════════════════════════════════════════
   The best thing on this page is a thing you DO. Press Cash in on the phone,
   write a line, and it crosses from the glass onto the ruled paper at the
   phone's own scale — the hero's whole argument, performed by the reader.

   It has been sitting behind a 12.5px grey sentence this entire time, and on
   a phone that sentence is not even shown (it names a control that is a
   screenful away there). A landing page cannot rely on anyone reading an
   instruction. The control has to ask.

   So the key breathes. Three times, once per visit, and only while the hero
   is actually on screen — a reader who has already scrolled past is not
   called back. It stops for good the moment anyone touches the device or
   presses a key, and it never plays at all for a reader who has asked for
   less movement. An invitation that keeps insisting is not an invitation. */
.book__dev .k__act.i > i { position: relative; }
.book__dev .k__act.i.is-ask > i::after {
  content: ''; position: absolute; inset: calc(var(--u) * -6);
  border-radius: 50%; border: 2px solid var(--k-brand);
  pointer-events: none;
  animation: ask 2.1s var(--ease-out) 3;
}
@keyframes ask {
  0%       { opacity: 0;   transform: scale(.80); }
  18%      { opacity: .55; }
  70%, 100%{ opacity: 0;   transform: scale(1.26); }
}
@media (prefers-reduced-motion: reduce) {
  .book__dev .k__act.i.is-ask > i::after { animation: none; content: none; }
}

/* ═══ THE CUE BECOMES THE PAYOFF ══════════════════════════════════════════
   "or press Cash in on the phone — the line lands here" is an instruction. The
   moment the reader follows it, it is a stale instruction: it goes on telling
   someone to do a thing they have just done.

   So it turns into the thing that was proved. The headline promises "only it
   adds itself up"; after the line crosses, the cue answers it in the same
   hand — the reader wrote once, and the total moved on its own, on both
   surfaces, without being asked. It is the only sentence on the page the
   reader earns.

   And it is shown at every width, unlike the instruction it replaces. The
   instruction had to stand down on a phone because it names a control that is
   a screenful away there (see THE PAIRING CUE IS A DESKTOP AFFORDANCE); a
   statement about what just happened names nothing and is true everywhere —
   on a phone the reader watches the figure move with their own thumb. */
.book__cue.is-done { animation: cue-done .5s var(--ease-out) both; }
@keyframes cue-done { from { opacity: 0; transform: translateY(4px); } }
.book__cue.is-done em {
  font-family: var(--font-hand); font-weight: 700; font-style: normal;
  font-size: 1.32em; line-height: .9; letter-spacing: 0;
  color: var(--ink-hi);
  display: inline-block; white-space: nowrap; vertical-align: baseline;
  padding: 0 .04em .06em;
  background-image: var(--pen); background-repeat: no-repeat;
  background-position: 0 100%; background-size: 100% .1em;
}
@media (max-width: 899px) {
  .hero--ledger .book__cue.is-done { display: block; }
}
@media (prefers-reduced-motion: reduce) { .book__cue.is-done { animation: none; } }

/* The close's fine print, once the reader has earned it. Same entrance as the
   hero's cue so the two read as one thought arriving twice, four screens
   apart — and no colour change, because this is still fine print and not an
   announcement. */
.sec--lime .fine.is-done { animation: cue-done .5s var(--ease-out) both; }
@media (prefers-reduced-motion: reduce) { .sec--lime .fine.is-done { animation: none; } }

/* ═══ THE ADDRESS IS WRITTEN ON THE PAGE TOO ══════════════════════════════
   The sign-in stopped being a form and became a page you write on: a rule to
   write above, the column head over it in small caps, and the answer in the
   reader's own hand. This was the one field left on the whole site still
   sitting in a white pill — and it sits on the lime, which makes it the most
   conspicuous container of the lot.

   Same construction, different stock. The line is ruled in the ink the lime
   already carries (--on-lime), so nothing new is introduced to the one
   surface the site deliberately does not invert. */
.sec--lime .form--pill {
  background: none; border-radius: 0; box-shadow: none;
  padding: 0; gap: 14px; align-items: flex-end;
  border-bottom: 1.5px solid color-mix(in srgb, var(--on-lime) 38%, transparent);
  transition: border-color var(--t-ui) var(--ease-out);
}
.sec--lime .form--pill:focus-within {
  box-shadow: none;
  border-bottom-color: var(--on-lime);
}
.sec--lime .form--pill input {
  font-family: var(--font-hand); font-weight: 600;
  font-size: 23px; line-height: 1.15;
  padding: 2px 2px 8px;
  color: var(--on-lime);
}
.sec--lime .form--pill input::placeholder {
  color: color-mix(in srgb, var(--on-lime) 55%, transparent);
  font-weight: 500;
}
/* the button steps off the pill and sits at the end of the line */
.sec--lime .form--pill .btn {
  margin-bottom: 5px; flex: none;
}

/* ═══ SOMETHING YOU CAN ACTUALLY HIT WITH A THUMB ═════════════════════════
   This product is used on a phone in a shop, so the page had better be. Six
   controls measured under the 44px floor at 390 wide — the two links under the
   headline were 21px tall, the fallback link in the close 25px, the burger 40,
   the brand 34.

   The hit area grows; NOTHING moves. Each one gets a pseudo-element centred on
   it, stretched to 44px tall and left at its own width, so the design is
   untouched and only the thumb notices. Padding would have pushed the layout
   around — on the herolinks it would have broken the line they sit on.

   Vertical only, deliberately: the two herolinks sit side by side, and growing
   them sideways would have them swallow each other's taps. */
@media (pointer: coarse), (max-width: 899px) {
  .herolinks a,
  .sec--lime .linky,
  .nav__brand,
  .nav__burger,
  .nav .btn--brand {
    position: relative;
  }
  .herolinks a::after,
  .sec--lime .linky::after,
  .nav__brand::after,
  .nav__burger::after,
  .nav .btn--brand::after {
    content: ''; position: absolute; left: 0; right: 0;
    top: 50%; translate: 0 -50%; height: 44px;
    /* the target only, never a surface: it must not paint and must not eat a
       scroll gesture that starts on it */
    background: none; pointer-events: auto;
  }
  /* the two square controls need the width too — they are 34 and 40 across */
  .nav__brand::after, .nav__burger::after {
    left: 50%; right: auto; width: 44px; translate: -50% -50%;
  }
}

/* Taking the pill off the address field took its tap target with it: 36px on a
   phone, where it had been a 48px pill. The box grows UPWARD — padding-top, not
   min-height — so the writing stays sitting on its rule and only the thumb
   notices. The button was 43, one pixel under, which is exactly the kind of
   number that only turns up when you measure. */
@media (pointer: coarse), (max-width: 899px) {
  .sec--lime .form--pill input { padding-top: 10px; }
  .sec--lime .form--pill .btn { min-height: 44px; }
}

/* the same floor for the front page's buttons on a touch screen */
@media (pointer: coarse), (max-width: 899px) {
  .btn { min-height: 44px; }
}

/* ═══ THE DEVICE COMES TO HAND ON A PHONE ═════════════════════════════════
   On a 390 screen the hero device was 289px — 74vw — and everything inside it
   is therefore 74% of real size. Measured: Cash in 36x51, a keypad key 61x32,
   post 234x35. That is the one interaction this page invites, and since the
   key started asking for a tap it has been asking for one too small to give.

   Width is the right lever, not a transform: --u is calc(100cqi / 375), a
   container unit, so widening the device rescales the whole app UI crisply
   rather than magnifying it — and .book has overflow:hidden, which would have
   clipped a scaled device exactly where the keypad is.

   Sized independently of --row here, and only here. The device is row x 6.492
   so that a ruled line on the paper continues as a row divider on the glass —
   but on a phone the device sits BELOW the ledger, so that join is not on
   screen and nothing is lost by letting the device be its own size. */
@media (max-width: 1099px) {
  .hero--ledger .book__dev { width: min(344px, 88vw); }
}

/* And the four action keys get a target inside their own slot. They sit in a
   row 72px to a slot with the control 36 wide, so there is room to grow
   sideways without them reaching into each other. */
@media (pointer: coarse), (max-width: 899px) {
  .book__dev .k__act { position: relative; }
  .book__dev .k__act::after {
    content: ''; position: absolute; left: 50%; top: 50%;
    translate: -50% -50%; width: 56px; height: 52px; background: none;
  }
}

/* Post is the second control in the composer and the one that commits the
   line — 41px on a phone. It is a full-width bar with clear air above and
   below it, so a target can grow past its box without reaching anything. The
   keypad keys are left at their own 38: that is the size the real app draws
   them, they sit in a tight grid where a bigger target would overlap its
   neighbours, and a 73px-wide key is comfortable in the hand regardless. */
@media (pointer: coarse), (max-width: 899px) {
  .book__dev [data-live-post] { position: relative; }
  .book__dev [data-live-post]::after {
    content: ''; position: absolute; left: 0; right: 0;
    top: 50%; translate: 0 -50%; height: 46px; background: none;
  }
}

/* The three theme buttons in the phone menu were 28x28. They sit in a
   segmented control, so a target bigger than the button would have reached
   into its neighbours — the button itself grows instead, and the control
   grows with it. Touch only: on a desktop these are hover-sized and correct. */
@media (pointer: coarse), (max-width: 899px) {
  .menu .thm__b { width: 44px; height: 44px; }
}

/* ── A PHONE TURNED SIDEWAYS ─────────────────────────────────────────────
   The device is 2.04:1 tall, so on a 390px-high landscape screen the 344px
   width it takes in portrait makes it 701px — 311px taller than the screen
   it is being shown on, and two flicks of scrolling to see one product shot.

   Height is the scarce axis there, so height is what caps it: 44svh of width
   puts the device at ~90% of the screen's height, which fits in one look.
   Bound by max-height rather than orientation so a short window on a laptop
   gets the same courtesy. */
@media (max-height: 560px) {
  .hero--ledger .book__dev { width: min(344px, 88vw, 44svh); }
}

/* ═══ THE PHONE HELD SIDEWAYS ═════════════════════════════════════════════
   Every rule above splits the world at 900px, which is the right seam for a
   phone held upright and the wrong one for the same phone turned over. A
   landscape iPhone reports 844x390: under 900, so it takes the stacked mobile
   branch — and that branch's whole design is a device CROPPED into a band at
   the top, because upright there is height to spare and width there is not.
   Turned sideways both facts invert. Measured at 844x390 before this:

     · the band is min(43vh, 352px) = 168px and the device is 436px tall, so
       62% of the phone was cut off — the guillotine landed halfway through
       the action row, slicing "Record / Pay out / Remind" through the middle
       of their own labels;
     · that 214px device sat alone in an 844px viewport, 630px of empty page
       on either side of it;
     · and the claim beside it ran the full width — 100 characters a line,
       against the 45-75 anything is set in.

   So landscape takes the arrangement the width actually asks for: the two
   columns the desktop uses, device on the left, claim on the right. Gated on
   HEIGHT rather than width, because that is the thing that is short here —
   `max-height: 500px` cannot catch a desktop window (the block also requires
   max-width: 899px, and the ones that are short are the ones this suits), and
   portrait phones are 690px and up, so nothing upright can see these rules.

   Written as an override after the mobile block rather than a condition on
   it: the stacked branch is load-bearing for every phone held the normal way,
   and this is a shape it was never measured at. ────────────────────────── */
@media (max-width: 899px) and (max-height: 500px) and (orientation: landscape) {
  .pin {
    grid-template-columns: .58fr 1fr;
    gap: clamp(18px, 3.4vw, 38px);
    align-items: start;
  }
  /* The band's apparatus comes off together: it is one idea — crop the device,
     hide the seam, let the claims pass under it — and half of it is worse than
     either half. The canvas fill and the veil-shadow existed only to make a
     cut edge look deliberate; with nothing cut there is nothing to hide, and a
     page-coloured block sitting over ruled paper is just a smudge. */
  .pin__dev {
    top: var(--navh);
    height: calc(100svh - var(--navh));
    overflow: visible;
    align-items: center; justify-content: center;
    background: none; box-shadow: none;
  }
  /* Sized by HEIGHT, and by letting aspect-ratio derive the width from it
     rather than restating 1249/2544 as a decimal here. The one number this
     needs is how much of the band is not the phone: 34px, which is the rail
     under it plus its gap. Everything else is the device's own proportion,
     read from the one place it is written. */
  .pin__scr .ph { width: auto; height: min(calc(100svh - var(--navh) - 34px), 306px); }
  /* The wash is the device's again, not the column's — same swap the desktop
     branch makes, and for the same reason: centred on a column this tall it
     is a haze behind nothing. */
  .pin__dev::before { content: none; }
  .pin__stack::before {
    content: ''; position: absolute; left: 50%; top: 50%; translate: -50% -50%;
    width: 130%; aspect-ratio: 1.12; z-index: -1; pointer-events: none;
    background: radial-gradient(closest-side, rgb(var(--brand-rgb) / .13), transparent 72%);
  }
  /* The tail goes back to the desktop floor, because the geometry that set it
     is back: a sticky box one viewport tall unpins when its container's bottom
     reaches the foot of the screen, so the list has to run from the reading
     line to that foot or the device slides out from under the claim still
     being read. The mobile branch could afford 8vh only because its band was
     168px and held almost to the end; a full-height column cannot. Measured
     sideways at 844x390 with 8vh: the phone left while claim 05 was still up.
     Stated in svh to match the band's own height above — two rules describing
     the same box should not disagree about what a viewport is. */
  .pin__list { padding-top: 0; padding-bottom: 20svh; }
}

/* ═══ THE ASK COMES TO THE THUMB ══════════════════════════════════════════
   Measured at 390x844: the page is 5,608px — 6.6 screens — and "Open your
   first book" appears once, at 4,900. The hero's own call is two store badges
   for apps that are not out yet. So for five of those six and a half screens
   a persuaded reader had nothing to press, and the only live controls on
   screen were Sign in and the burger, both in the top-right corner: the one
   part of a phone a thumb cannot reach without regripping.

   This is the standard phone fix and the page had been getting by without it
   because the desktop never needed it. It earns its place by being strict
   about when it is there:

     · not over the hero — the reader has not been told anything yet, and a
       button that arrives before the argument is a pop-up;
     · gone by the close — #start says this better, with the price and the
       four-questions promise under it. Two identical buttons on one screen
       is a page that does not trust its own writing;
     · gone while the menu is open, because the menu is the navigation and
       this would sit on top of its last row.

   It is made of the bar at the other end of the screen: the same glass, the
   same blur, the same saturation push, so it reads as the page's other edge
   rather than as something that flew in. ───────────────────────────────── */
.thumb { display: none; }

@media (max-width: 899px) {
  .thumb {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 80;
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding:
      9px max(clamp(14px, 4vw, 22px), env(safe-area-inset-right))
      max(9px, env(safe-area-inset-bottom)) max(clamp(14px, 4vw, 22px), env(safe-area-inset-left));
    background: var(--chrome-nav);
    border-top: 1px solid rgb(var(--lift-rgb) / .11);
    /* Off-screen AND out of the accessibility tree between appearances —
       visibility, not opacity, so a reader tabbing through the close cannot
       land on a button that is sitting under the bottom of the screen. The
       delayed visibility switch is the same idiom .pin__scr uses, for the
       same reason: it has to stay visible for the length of the slide out. */
    visibility: hidden;
    transform: translateY(104%);
    transition: transform .42s var(--ease-spring),
                visibility 0s linear .42s;
  }
  .thumb.is-up {
    visibility: visible; transform: none;
    transition: transform .42s var(--ease-spring), visibility 0s linear 0s;
  }
  @supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
    .thumb {
      /* Held at the nav's number on purpose: these two are the same bar at
         opposite edges of the screen, and a reader who sees both at once
         would find the seam immediately if one were cloudier than the other. */
      background: color-mix(in srgb, var(--chrome-nav) 98%, transparent);
      -webkit-backdrop-filter: saturate(180%) blur(30px);
      backdrop-filter: saturate(180%) blur(30px);
    }
  }

  /* The line is the price, because the price is the objection. It is the same
     sentence the close opens with, cut to what fits beside a button. */
  .thumb__say {
    margin: 0; min-width: 0; flex: 1 1 auto;
    font-size: 12.5px; line-height: 1.25; color: var(--t700);
  }
  .thumb__say em {
    display: block; font-style: normal; font-weight: 600; color: var(--ink);
  }
  .thumb__go {
    flex: 0 0 auto; min-height: 44px;
    padding-inline: 16px; font-size: 14.5px; gap: 7px;
  }
  .thumb__go svg { width: 16px; height: 16px; }

  /* Under 360 the sentence is costing the button its words. The button wins:
     it is the thing being offered, and "free forever" is repeated at the
     close where there is room to say it properly. */
  @media (max-width: 359px) {
    .thumb__say { display: none; }
    .thumb { justify-content: stretch; }
    .thumb__go { flex: 1 1 auto; justify-content: center; }
  }

  /* A reader who has asked for less movement gets the bar without the slide —
     it is either there or it is not. */
  @media (prefers-reduced-motion: reduce) {
    .thumb, .thumb.is-up { transition: none; }
  }
}

/* ═══ THE DEVICE IN THE BAND IS WHOLE ═════════════════════════════════════
   The band was "cropped to the part of the screen that carries the argument",
   and on beat 01 that is true. On beat 02 it is not: that beat is "two taps
   and it is written", the tap it describes is on the KEYPAD, and the keypad
   is the part the band cut off. Measured at 390x844 the guillotine took 77px
   — 18% of the device — and landed mid-glyph through the row of 1 2 3, so the
   phone ended in half a number and a fade. A crop reads as deliberate when it
   falls somewhere; this one fell through a button.

   The band grows a little and the device is sized BY it rather than beside
   it: one number, stated once on .pin__dev, with aspect-ratio deriving the
   width so 1249/2544 is never written down a second time.

   The cost is honest and it is the whole cost — measured at 390x844, the
   device goes 211px wide to 189 (10% narrower) and the claim under the band
   keeps 398px of the 440 it had, against the ~216px a claim actually
   occupies. Nothing the band's own reasoning was protecting is spent.

   One block, appended last, so reverting it is deleting it.

   `min-height: 501px` is not a third breakpoint — it is the exact complement
   of the landscape block above, which claims `max-height: 500px`. Without it
   this rule, being later in the file, took the sideways spread back off the
   phone held sideways: measured at 844x390 the band collapsed from 338px to
   183 and the device from 149x304 to 85x173. Two blocks describing the same
   element have to partition the space between them, not overlap. ──────── */
@media (max-width: 899px) and (min-height: 501px) {
  /* --band moves up to .pin so the BEAT can be derived from it too. It was
     only ever on .pin__dev, so the claim's height was a separate guess (52vh)
     that had to be kept in step with it by hand — and was not. */
  /* ── svh, NOT vh — this is the browser's own toolbars ─────────────────
     On a phone `vh` is the LARGE viewport: the height the page would have if
     the browser's top bar and bottom bar were hidden. They are not hidden
     most of the time, so a band sized at 49vh takes 49% of a screen bigger
     than the one the reader has, and the difference is stolen from the text
     underneath — which is exactly the "text looks not finished" the owner
     described, and why it shows in a real Safari and never in a headless
     screenshot. `svh` is the same fraction of the viewport with the toolbars
     SHOWING, which is the one that has to work.

     Not dvh: that re-measures as the toolbars slide away, which would resize
     the band mid-scroll and reflow the claims under it.

     --dev is the device's own height and --band is derived from it, so the
     crop below cannot drift away from the thing it crops. */
  .pin {
    --air: 16px;
    --dev: min(calc(49svh - 26px), 384px);
    /* 97%, not the 90% asked for. Measured, screen by screen, how much of
       each mock is empty at the bottom: screens 3, 4 and 5 have 19-33% to
       spare, but screen 1 has 3.4% and screen 2 has 3.9% — a 10% cut takes
       the Books/Accounts tabs off the first and the "Cash in" button off the
       second, and beat 02 is called "Two taps and it is written". The band is
       one box, so the crop is uniform, so the tightest screen sets it.

       An earlier pass lifted each mock's bottom bar up out of the crop so
       nothing was lost. That was the wrong answer and the owner said so: if
       the bottom is cropped, a bottom bar has no business still being on
       screen — it ends up floating in the middle of a phone that is supposed
       to be running past the fold.

       So the crop cuts, and the only question is WHERE it can cut cleanly.
       Measured as a share of the device height:

         screen 1  the Books/Accounts tabs begin  9.2% above the foot
         screen 2  the Cash in button begins      9.8% above the foot
         screen 2  the keypad's last row ENDS    10.9% above the foot

       Which leaves a 1.1% window — between 9.8% and 10.9% — where both
       bottom bars are gone whole and nothing slices through the keypad. That
       last part is the failure the old band had: it came down through the row
       of 1 2 3 and the phone ended in half a number. 10.35% sits in the
       middle of the window with about two pixels of clearance on each side,
       and because every number here is a percentage of the device, the
       relationship holds at every size the device is drawn at. */
    --keep: .8965;
    --band: calc(var(--air) + var(--dev) * var(--keep));
  }
  .pin__dev {
    /* Room above the device, at the owner's mark. The band is sticky at the
       nav's own bottom edge and the device was anchored to the top of it, so
       the two touched: measured at 390x844 the gap between the bar and the
       top of the phone was exactly 0px, while landscape had 18 and the
       desktop column 54. A device butted against the bar reads as one object
       hanging off another rather than a phone sitting on a page.

       --air is the gap; 10px stays under the device for the shadow it casts.
       The band grows by both, so the DEVICE does not pay for the room —
       measured, it stays 384px tall at 390x844, exactly what it was. What
       pays is the claim below, 26px of the ~372px it keeps, against the
       ~216px a claim actually occupies. */
    height: var(--band);
    padding-top: var(--air);
  }
  .pin__scr .ph { width: auto; height: var(--dev); }
}

/* ═══ THE COMPOSE STRIP REPLACES THE MOCK ON A PHONE ══════════════════════
   Measured at 390x844: the hero was 1,679px and the device mock 699 of it —
   42% — spent showing a picture of a phone to a reader holding one. It goes,
   and what it was FOR stays.

   The first attempt at what replaced it was wrong and looked it: a twelve-key
   numpad in bordered white boxes, ~600px of it, laid on ruled paper. That is
   an app's furniture on a cash book — and it was also broken, the currency
   chips running past the right edge at 390px.

   This is the same thing said in the page's own material. The sign-in page
   already asks for answers by putting a rule under them and writing on it in
   the hand; the hero asks the same way. The keypad is the reader's own,
   summoned by inputmode="numeric", which is better than any keypad this page
   could draw and costs no height at all.

   Above 900px nothing here applies and the device is exactly as it was. */
.compose { display: none; }

@media (max-width: 899px) {
  .hero--ledger .book__dev { display: none; }

  .compose {
    display: block;
    margin: clamp(18px, 3.4vw, 26px) 0 clamp(4px, 1vw, 10px);
    /* The strip is a sibling of .book__in, not a child, so it never inherited
       the inset every other thing in this hero sits on — it ran edge to edge
       and the last currency chip finished flush against the right of the
       screen. Same custom property the wrap uses, so the two cannot drift. */
    padding-inline: var(--gut);
  }
  .compose__cue {
    margin: 0 0 clamp(14px, 2.6vw, 18px);
    font-size: 14.5px; line-height: 1.45; color: var(--t700);
  }

  /* ── the two choices, understated ──────────────────────────────────────
     They were black and white pills heavier than anything else in the hero.
     A choice in a ledger is a tick, not a button: the row reads as small
     print until one of them is taken, and the taken one is simply the one
     written in ink. */
  .compose__bar {
    display: flex; flex-wrap: wrap; gap: 10px 14px;
    align-items: baseline; justify-content: space-between;
    margin-bottom: clamp(6px, 1.4vw, 10px);
  }
  .compose__dir, .compose__cur { display: flex; gap: 2px; flex-wrap: wrap; }

  .compose__dir button, .compose__cur button {
    position: relative;
    padding: 4px 9px 5px; border: 0; background: none; cursor: pointer;
    font: inherit; font-size: 13px; font-weight: 600; letter-spacing: .01em;
    color: var(--muted); border-radius: 8px;
    transition: color var(--t-ui) var(--ease-out), background-color var(--t-ui) var(--ease-out);
  }
  /* The pill is small; the finger is not. A centred ::after takes the target
     to 44px without moving a pixel of layout — the same trick the theme
     buttons and the sign-in fields use. */
  .compose__dir button::after, .compose__cur button::after {
    content: ''; position: absolute; left: 0; right: 0; top: 50%;
    height: 44px; transform: translateY(-50%);
    /* The overlay is 44px but only 38 of it was reachable: .compose__line
       comes later in the document and painted over the part of it that
       reaches below the chip. Raising it puts the target back where the
       stylesheet said it was. */
    z-index: 1;
  }
  .compose__dir button.is-on, .compose__cur button.is-on {
    color: var(--ink-hi);
    background: rgb(var(--lift-rgb) / .07);
  }
  /* 44, not 30. Hit-tested, "Sh" and "Br" came out 35 and 32 wide — the
     ::after was only growing them 3px a side, and growing it further would
     have made neighbours OVERLAP, since these chips sit flush against each
     other. A chip that is actually 44 wide needs no overlay to be hittable
     and cannot steal its neighbour's tap. */
  .compose__cur button { min-width: 44px; text-align: center; }
  .compose__cur { gap: 0; }

  /* ── the line you write on ─────────────────────────────────────────────
     A rule, a unit in small caps at the margin, and the figure written on it
     in the book's hand. It is the ledger's own row, one step before it
     becomes one. */
  .compose__line {
    display: flex; align-items: baseline; gap: 10px;
    padding: 4px 2px 9px;
    border-bottom: 1.5px solid var(--ink-hi);
    margin-bottom: clamp(16px, 3vw, 22px);
    cursor: text;
  }
  .compose__unit {
    flex: none; font-size: 11px; font-weight: 600; letter-spacing: .17em;
    text-transform: uppercase; color: var(--muted);
  }
  .compose__amt {
    flex: 1 1 auto; min-width: 0; width: 100%;
    border: 0; background: none; padding: 0; margin: 0;
    font-family: var(--font-hand); font-weight: 700;
    font-size: 40px; line-height: 1.04; letter-spacing: 0;
    color: var(--ink-hi); caret-color: var(--brand);
    /* Written next to its unit rather than pushed to the far end of the rule.
       A ledger right-aligns its figure column, and this line will become one
       of those rows — but as a FIELD, right-aligned, an empty one puts a lone
       grey nought at the far edge with a hand's width of nothing before it,
       which reads as a broken input rather than a line waiting to be written.
       "Ksh 2,400" is also simply how it is said. */
    text-align: left;
  }
  .compose__amt::placeholder { color: rgb(var(--lift-rgb) / .28); }
  .compose__amt:focus { outline: none; }
  .compose__line:focus-within { border-bottom-color: var(--brand); }

  .compose__go { width: 100%; justify-content: center; min-height: 48px; }
  .compose__go[disabled] {
    opacity: 1; pointer-events: none;
    background: rgb(var(--lift-rgb) / .07); color: var(--muted); box-shadow: none;
  }

  /* Only shown when a currency other than the book's is picked, so it never
     speaks unless it has something to say. */
  .compose__note {
    margin: clamp(12px, 2.4vw, 16px) 0 0;
    font-size: 13.5px; line-height: 1.45; color: var(--t700);
  }
  .compose__note em {
    font-family: var(--font-hand); font-weight: 700; font-style: normal;
    font-size: 1.26em; color: var(--ink-hi);
  }

  /* The payoff sentence, when block 2k turns the cue into it. */
  .compose__cue.is-done em {
    font-family: var(--font-hand); font-weight: 700; font-style: normal;
    font-size: 1.3em; line-height: .9; color: var(--ink-hi);
    display: inline-block; white-space: nowrap; vertical-align: baseline;
    padding: 0 .04em .06em;
    background-image: var(--pen); background-repeat: no-repeat;
    background-position: 0 100%; background-size: 100% .1em;
  }
}

/* ═══ THE PEN STROKE RESPECTS THE SETTING TOO ═════════════════════════════
   Every other motion on this page is guarded — the folio's turn, the rail's
   page control, the invitation ring, the cue's arrival, the thumb bar's
   slide, the leaf's fade. The stroke that draws itself under the handwritten
   words was the one that was missed, in four places. It is short and it is
   one-shot, which is why it went unnoticed; it is still motion the reader
   asked not to have, and the underline is already there at rest — only the
   drawing of it is animated, so there is nothing to lose by stopping. */
@media (prefers-reduced-motion: reduce) {
  .h em,
  .sec--pin.is-rev .h em,
  .sec--lime.is-rev .h em,
  .sec--folk.is-rev .h em { animation: none; }
}

/* ═══ ONE CLAIM, ONE SCREENFUL ════════════════════════════════════════════
   The beat was 52vh and the claim inside it about 216px. Measured at 390x844:
   beats 2, 3 and 4 were 439px boxes holding 216px of writing — 51% empty.
   The reader scrolled as far through blank ruled paper as through the words,
   four times, and the marked screenshot is exactly that: a claim, then a void
   the height of the claim again before anything happens.

   The number was never wrong so much as unrelated. The band above is sticky,
   so the screen a claim actually gets is whatever is left under it — at
   390x844 that is 844 − 52 − 410 = 382px, and the beat was 439. Now the beat
   IS that strip, derived from the same --band the device is sized by, so the
   two cannot drift apart again: exactly one claim occupies the space under
   the band, and there is no leftover to scroll through.

   min-height, not height — a claim that needs more room on a narrow screen
   still takes it, and the beat grows rather than clipping.

   And centred within it, which is now safe. The old rule anchored claims to
   the top because a centred claim in a 62vh beat put its heading up behind
   the band; the beat is the visible strip now, so its middle is the middle of
   what the reader can see. ─────────────────────────────────────────────── */
@media (max-width: 899px) and (min-height: 501px) {
  /* SMALLER than the strip, deliberately, and this is the correction.
     Making the beat EQUAL the strip took out the dead scroll but not the
     hole: a 219px claim in a 488px strip at 422x950 still had 131px of blank
     above it and 138 below, and the owner marked that spot twice.

     A gap around a claim can only be filled by its neighbour, and a neighbour
     can only reach it if the beat is shorter than the screen it is read on.
     So the beat is the claim plus its breathing room, and the space that used
     to be empty is the NEXT claim coming up. On a tall phone you now read two
     — the one being answered and the edge of the one after — which is how a
     page has always worked and is why it stops feeling like a slideshow with
     the slides missing.

     I tried pinning the claim instead, so it would hold under the band. It
     cannot work here: .pin__item carries a transform, so it is the sticky
     element's containing block, and the stick range is the beat minus the
     claim — 83px once centring had taken half of it. It held for a third of
     a thumb-swipe and then slid off behind the band anyway. */
  .pin__item,
  .pin__item:first-child,
  .pin__item:last-child {
    min-height: 0;
    padding-block: clamp(30px, 5.5svh, 62px);
    justify-content: flex-start;
  }
}

/* ═══ THE TWO BADGES STOP EATING THE PROOF ════════════════════════════════
   Below 420px the store badges stack, and stacked they cost 108px — the same
   108px at every phone width, because it is two full-height badges plus a
   gap. Measured on the screens this product is actually opened on:

     320x568   only 102px of the 319px ledger was on screen 1 — 32%
     360x640   155px of 359 — 43%

   The ledger is the argument. "The same notebook — only it adds itself up"
   is a claim; the ruled page under it with Dinner service +14,350 against a
   balance of 150,007 is the evidence, and on a small phone the reader met
   the claim, two badges for an app that is not out yet, and almost none of
   the evidence.

   Side by side they cost 38-53px instead of 108. The row has 280px at 320w;
   "Google Play" needs 125 of it with its glyph and padding, "App Store"
   needs 112, and half of 272 is 136 — so both fit whole, with no name broken
   across lines and nothing truncated. Checked, because an earlier pass at
   this DID break "Google Play" over two lines, which stops reading as that
   store's badge at all. ────────────────────────────────────────────────── */
@media (max-width: 420px) {
  .hero--ledger .stores {
    flex-direction: row; align-items: stretch; gap: 8px;
    /* .hero__cta is a flex COLUMN with align-items: flex-start, so this row
       was shrink-wrapping to its content — 241px inside a 350px column — and
       flex-grow on the badges had nothing to grow into. They sat at 116px at
       every width from 320 to 420, and "Google Play" truncated in all of
       them. Stretching the row is what makes the halves real. */
    width: 100%;
  }
  .hero--ledger .store {
    flex: 1 1 0; min-width: 0;
    justify-content: flex-start;
    padding: 7px 9px 8px 8px; gap: 6px;
    /* Shrinking these took the box to 40px and the real hit area to 32 at
       320w. 44 is the floor, and four pixels is a cheap price against the
       sixty-eight this row gave back. */
    min-height: 44px;
  }
  .hero--ledger .store i { width: 20px; height: 20px; }
  .hero--ledger .store span { min-width: 0; }
  .hero--ledger .store small { font-size: 7.6px; letter-spacing: .05em; white-space: nowrap; }
  .hero--ledger .store b { font-size: 13.5px; letter-spacing: -.022em; white-space: nowrap; }
  /* The SOON pill was there because this badge sat next to a live one at
     desktop width and needed to say it was not one. Side by side on a phone,
     "Coming to the App Store" is the whole sentence and the pill is the third
     time the same badge says the same thing — in the width the name needs. */
  .hero--ledger .store--soon { padding-right: 9px; }
  .hero--ledger .store--soon em { display: none; }
}

/* ═══ THE INVISIBLE TARGETS STOP OVERLAPPING EACH OTHER ═══════════════════
   The herolinks each carry a 44px hit overlay centred on a 21px line, so it
   overhangs its own box by about 11.5px top and bottom. The gap above it was
   5px. The overlay therefore reached up into the store badges — and because
   the links come later in the document, they won. Hit-tested at 320x568: a
   tap in the bottom 7px of the Google Play badge opened "How it works".

   The rule that added those overlays says "Vertical only, deliberately: the
   two herolinks sit side by side, and growing them sideways would have them
   swallow each other's taps." It guarded the horizontal collision and not the
   vertical one, against the thing above.

   The gap becomes bigger than the overhang, which is the only thing that can
   fix it without taking the target back off. Nothing moves except this row,
   and only on a phone. ─────────────────────────────────────────────────── */
@media (pointer: coarse), (max-width: 899px) {
  .hero--ledger .herolinks { margin-top: 13px; }
}

/* And the amount line gets its last two pixels: hit-tested at 42 against a
   floor of 44, which is a miss by the width of a hair but still a miss. */
@media (max-width: 899px) {
  /* Padding on the LINE does not grow the FIELD, and it is the field that is
     tapped — it measured 42 against the 44 floor. min-height on the input
     itself is the only thing that moves the number. */
  .compose__amt { min-height: 44px; }

  /* And the same overlap, one row down: when the bar wraps at 320 the
     currency chips land under the direction chips with 10px between them,
     while each chip's 44px overlay overhangs its 29px box by 7.5px. Two
     overhangs into one gap is 5px of theft, and the later row wins it — the
     direction chips measured 39. The gap goes past the sum of the overhangs. */
  .compose__bar { row-gap: 17px; }
}


/* ═══ THE SLIPS BECOME SOMETHING YOU CAN ACTUALLY REACH ═══════════════════
   The band drifts its row sideways as it passes, by whatever does not fit —
   1,136px at 390 wide. That was written for a desktop, where the overhang is
   674px and the band has a whole page below it to finish the travel in.

   On a phone it cannot finish. Measured at 390x844: the drift freezes at
   1,012 of 1,136 because the PAGE runs out — the band stops moving once the
   document bottoms out, at t = 0.89. The sixth slip never becomes fully
   visible, ever, at any scroll position. Five of six. And the five that do
   arrive slide past at 1,136px of lateral travel against about 1,100px of
   vertical scroll, so at no moment is one of them still.

   A lateral animation the reader cannot control, cannot complete, and cannot
   read is worse than no animation. So on a phone the row becomes what it
   already looks like — a rail of cards — and the reader moves it with their
   thumb. All six reachable, each one still while it is read, at whatever pace
   they want. No snapping: the owner has said before that he does not want
   lists that move themselves, and a snap is the list deciding where to stop.

   The drift stays exactly as it is above 900px, where it works. ────────── */
@media (max-width: 899px) {
  .folk {
    overflow-x: auto;
    /* so a sideways flick inside the rail cannot become the browser's
       back-gesture at the first or last slip */
    overscroll-behavior-x: contain;
    scrollbar-width: none; -ms-overflow-style: none;
  }
  .folk::-webkit-scrollbar { width: 0; height: 0; }
  .folk__row {
    transform: none;
    will-change: auto;
    /* the last slip needs an edge to stop against, the same one the first
       one starts from */
    padding-inline-end: var(--gut, 20px);
  }
}

/* ═══ THE DAY IS WRITTEN, NOT FADED IN ════════════════════════════════════
   The hero's ledger already arrives on a beat — Dinner service, then Breakfast
   counter, then Green Grocer, then the till total, over about a second. The
   staging was right and nobody could see it, because what each row DID on
   arrival was fade up nine pixels. Filmed at 390x844 over two seconds, the
   first frame and the last are almost the same picture: a web page loading.

   A fade is how an interface appears. This page's entire claim is that it is
   a notebook, and a notebook does not fade — it gets written across, left to
   right, at the speed of a hand. So the ink sweeps along the rule instead:
   the name, then the figure, then the running balance, in the order a pen
   would reach them, because the reveal runs the width of the row.

   clip-path rather than a mask or a width, because the row is a four-column
   grid whose cells are laid out in the same pass — clipping reveals what is
   already in place instead of reflowing anything, so the figures never move
   while they appear. ──────────────────────────────────────────────────── */
/* Only the rows the staging actually reveals. .book__bf — "Brought forward,
   from folio 13" — carries no [data-fly] and never receives `.in`, so adding
   it here did not make it write itself, it made it permanently invisible.
   Caught by checking, after 2.6 seconds, that every row the rule touches has
   finished: one had not, and it was the one nothing was ever going to
   finish. A reveal applied to something that is never revealed is a
   disappearance. */
.book__r,
.book__tot {
  clip-path: inset(0 100% 0 0);
}
.book__r.in,
.book__tot.in {
  clip-path: inset(0 -2px 0 0);
  transition: opacity .34s var(--ease-out),
              transform .4s var(--ease-out),
              clip-path .74s cubic-bezier(.22, .68, .3, 1);
}
/* The row's bar hangs 9px below its own box, so a clip at the row's edge
   would cut it off. The right edge is opened two pixels for the same reason
   the totals need it: a balance that ends flush with the clip loses its last
   stem to the rounding. */
.book__r > b s { clip-path: none; }

/* The line the READER writes gets the same pen. Its ink runs a little slower
   than the ones that were already on the page, because this one is theirs. */
.book__r--new { clip-path: inset(0 100% 0 0); }
.book__r--new.in { clip-path: inset(0 -2px 0 0); transition-duration: .34s, .4s, .92s; }

@media (prefers-reduced-motion: reduce) {
  .book__r, .book__tot,
  .book__r.in, .book__tot.in,
  .book__r--new, .book__r--new.in {
    clip-path: none; transition: none;
  }
}


/* ═══ THE HERO'S ONE DEMONSTRATION HAPPENS WHERE THE THUMB IS ══════════════
   Filmed on a phone with the keyboard up — 390x844 leaves about 500px of
   page once iOS takes its share — and the result was plain: the reader types
   an amount, presses Write it down, and the line they wrote lands at
   top: −112px. Above the screen. The till above it re-adds itself perfectly
   and they never see either thing happen; all that reaches them is a
   sentence claiming something did.

   This hero's whole argument is a verb. A verb performed off-screen is a
   claim, and the page already had enough of those. Four changes, none of
   them above 900px:

     · the blank rule they write on BECOMES the row — laid over itself, so
       nothing reflows under a raised keyboard;
     · the figure is already a ledger figure while it is being typed, in the
       column's own colour, over the column's own magnitude bar;
     · the choices stop being app chips and become what a choice is in a
       ledger — the one written in ink;
     · and the strip gives back the ~90px that were keeping the blank line
       below the fold, so a reader who never scrolls still sees that this
       page has a line waiting for them.
   ──────────────────────────────────────────────────────────────────────── */
@media (max-width: 899px) {

  /* ── ROOM, TAKEN FROM WHAT WAS SAID TWICE ──────────────────────────────
     "How it works" is in the nav, in the burger menu, in the skip link, and
     is the next section down the page — four ways to reach a thing that is
     one flick away. On a phone it was also the fifth: a 21px line under the
     store badges carrying a 44px hit overlay that overhung 11.5px into them,
     which is why the rule above this one exists at all. Removing it gives
     the room back AND retires the collision rather than holding it apart. */
  .hero--ledger .herolinks { display: none; }

  /* The strip follows the total directly: it is the next line of the same
     page, not a form underneath one. */
  .hero--ledger .book__rows { margin-top: clamp(14px, 3vw, 22px); }
  .hero--ledger .book__tot { height: calc(var(--row) * 1.42); }
  .compose { margin-top: clamp(6px, 1.6vw, 12px); }
  .compose__bar { margin-bottom: clamp(2px, .8vw, 6px); }

  /* ── THE CHOICE IS A TICK, NOT A CHIP ──────────────────────────────────
     Two grey lozenges and four more beside them read as an application's
     segmented control laid on ruled paper — the one material this page has
     never used anywhere else. A ledger marks a choice by writing it: the
     taken one is in ink with the hand's own stroke under it, and the rest
     are small print until they are chosen. */
  .compose__dir button, .compose__cur button {
    /* ── THE TARGET IS THE BUTTON, NOT A THING FLOATING OVER IT ─────────
       These were 29px boxes wearing a centred 44px ::after, which overhung
       7.5px top and bottom into whatever the layout put next to them — and
       every time the spacing round here changed, the overhang had to be
       re-argued against it. Tightening the bar by three pixels was enough:
       hit-tested, the chips came back 42.

       A box that is actually 44 tall cannot be stolen from and cannot steal,
       and it costs fifteen pixels out of the eighty-eight this section has
       just given back. The stroke under the chosen one is positioned off the
       same custom property as the padding, so the two cannot drift apart. */
    --pad-b: 12px;
    padding: 11px 9px var(--pad-b);
    min-height: 44px;
  }
  .compose__dir button::after, .compose__cur button::after { content: none; }
  .compose__bar { row-gap: 0; align-items: center; }

  .compose__dir button.is-on, .compose__cur button.is-on {
    background: none;
    color: var(--ink-hi);
    background-image: var(--pen);
    background-repeat: no-repeat;
    background-position: 50% calc(100% - var(--pad-b) + 3px);
    background-size: calc(100% - 14px) .085em;
  }

  /* ── THE LINE, AND THE ROW IT TURNS INTO ───────────────────────────────
     One box, two faces. The receipt is absolutely placed over the rule the
     reader was writing on, so the moment of becoming costs no height: with a
     keyboard up, a single pixel of reflow moves the field out from under the
     thumb that is still on it. */
  .compose__slot { position: relative; }
  .compose__line { position: relative; }

  /* The magnitude bar the rows above all carry, under the figure being
     typed — so the line is visibly one of them before it is one of them.
     --w is set by the strip as the digits go in; at nothing typed it is
     nothing wide, and it never draws itself over the rule. */
  .compose__mag {
    position: absolute; left: 0; bottom: -1px; height: 3px;
    width: var(--w, 0%); max-width: 100%;
    background: var(--bar-in-a); border-radius: 1px 3px 3px 1px;
    transform-origin: left;
    transition: width .32s var(--ease-out), background-color .2s var(--ease-out);
    pointer-events: none;
  }
  .compose.is-out .compose__mag { background: var(--bar-out-a); }

  /* The figure takes the column's colour while it is written, not after. */
  .compose.is-out .compose__amt { color: var(--fig-out); }
  .compose.is-in  .compose__amt { color: var(--fig-in); }
  .compose.is-in  .compose__line:focus-within,
  .compose.is-in  .compose__line { border-bottom-color: var(--fig-in); }
  .compose.is-out .compose__line:focus-within,
  .compose.is-out .compose__line { border-bottom-color: var(--fig-out); }

  /* ── THE RECEIPT IS A LEDGER ROW, NOT A DRAWING OF ONE ─────────────────
     It carries .book__r as well, so every rule that governs a line on the
     page above governs this one: the four columns, the gap, the truncation,
     the figure's colour and tabular figures, the balance column's weight,
     the time that moves into the sub-label below 720px — and the pen, since
     the ink sweep is written against .book__r and .in. A second description
     of a ledger row is a second ledger row to keep in step, and this file
     has lost that argument before.

     All that is left to say here is where it stands: over the rule, filling
     it, so the becoming costs no height. */
  /* .hero--ledger .book__r sets position: relative and a fixed row height,
     and it is two classes to this one's one — so this has to be said at the
     same weight or the receipt lands in flow and shoves the button 34px down
     the screen at the exact moment a thumb is on it. Measured: it did. */
  .hero--ledger .compose__wrote {
    position: absolute; left: 0; right: 0; top: 0; bottom: 0;
    height: auto; margin: 0; pointer-events: none;
  }
  .hero--ledger .compose__wrote[hidden] { display: none; }
  .hero--ledger .compose__wrote.is-off {
    opacity: 0; transition: opacity .34s var(--ease-out);
  }

  /* The rule itself stays — the row is written ON it. What steps back is
     what the reader was writing with. */
  .compose.is-wrote .compose__unit,
  .compose.is-wrote .compose__amt,
  .compose.is-wrote .compose__mag { opacity: 0; }
  .compose__unit, .compose__amt, .compose__mag { transition: opacity .2s var(--ease-out); }

  /* ── THE ASK IS A LINE, NOT A SLAB ─────────────────────────────────────
     Same decision as the sign-in sheet, for the same reason: a filled
     lozenge is the heaviest object on a page made of paper and rules, and
     disabled it is a dead grey pill sitting under the reader's thumb before
     they have done anything at all. Here it is what the book would do —
     a line, ruled off, with the direction of travel at the end of it. */
  .compose__go.btn--brand {
    background: none; box-shadow: none; border-radius: 0;
    color: var(--ink-hi);
    border-bottom: 1.5px solid var(--ink-hi);
    padding: 12px 2px 10px; width: 100%; min-height: 48px;
    justify-content: space-between;
    font-size: 16px; letter-spacing: -.01em;
  }
  .compose__go.btn--brand::after {
    content: '→'; font-size: 17px; opacity: .75; font-weight: 500;
  }
  .compose__go.btn--brand:active {
    transform: none; border-bottom-width: 3px; padding-bottom: 8.5px;
  }
  /* Not a grey pill: a rule that has not been written on yet. */
  .compose__go.btn--brand[disabled] {
    background: none; opacity: 1; pointer-events: none;
    color: var(--muted);
    border-bottom-color: rgb(var(--rule-rgb) / .55);
  }
  .compose__go.btn--brand[disabled]::after { opacity: .35; }

  .compose__cue {
    margin: clamp(12px, 2.4vw, 16px) 0 0;
    font-size: 13.5px; line-height: 1.45; color: var(--t700);
  }
}

@media (prefers-reduced-motion: reduce) {
  .compose__wrote, .compose__wrote.is-off { opacity: 1; transition: none; }
  .compose__mag { transition: none; }
  .compose__line { transition: none; }
}


/* ═══ ONE PAYOFF SENTENCE, NOT TWO, AND NOT ABOVE THE FOLD ════════════════
   `.hero--ledger .book__cue.is-done { display: block }` brought the desktop
   cue back on a phone the moment a line was written, on the grounds that a
   statement about what just happened is true at every width. It is — but the
   compose strip carries its own copy of the same sentence, written by the
   same observer, so a phone was getting it twice about 450px apart.

   Worse, the upper copy is in .book__say, ABOVE the ledger. Measured at
   390x500 (a phone with the keyboard up): pressing "Write it down" grew the
   hero by 96px, of which 45 was the row the reader had just written and 51
   was this sentence and its margin appearing out of nothing — every pixel of
   it above the viewport, so the whole page lurched downward under the thumb
   that was still on the button at the exact instant of the payoff.

   The strip's copy stays, because on a phone that is where the reader is
   looking: directly under the rule they wrote on. ────────────────────────*/
@media (max-width: 899px) {
  .hero--ledger .book__cue.is-done { display: none; }
}

/* Two pixels of headroom on the amount field. min-height: 44px put the box at
   exactly 44, which hit-tests as 43 or 44 depending on where the subpixel
   lands — a floor you clear only half the time is not a floor. */
@media (max-width: 899px) {
  .compose__amt { min-height: 46px; }
}


/* ═══ A FIGURE THAT DOES NOT FIT ITS COLUMN IS WRITTEN SMALLER ════════════
   The ledger's columns are sized once, for the figures the page ships with:
   −11,000 at its widest and 150,007 in the balance. The composer, though,
   takes seven digits — hero.js's own cap — so a reader who types 9,876,543
   (which in a currency where lunch is 350 shillings is a perfectly ordinary
   week) gets +9,876,543 against a 78px column and 10,026,550 against a 70px
   one.

   Measured at 390: the figure ran 183→291 while the balance began at 274.
   Seventeen pixels of one money figure printed ON another. At 320 they touch
   with no gap at all. Both are pre-existing; the receipt inherits them
   because it is a ledger row and obeys the ledger's rules.

   A cash book has a fixed column and a person with a pen. When the number is
   long, they write it smaller — they do not widen the book. So the figure's
   type takes a third bound: the one that makes it fit the track it stands in.

   --adv is the display face's widest advance per character with tabular
   numerals, in em. Measured across 320/360/390/720/1280 and four different
   figures, it ranges 0.559–0.599; 0.6 is the ceiling with a little over. It
   is a measured constant, so it is guarded by a test that walks one to seven
   digits at every width and asserts no two cells overlap — if the face ever
   changes, that fails rather than this silently going wrong.

   --len is the character count, set by the same pass that scales the bars,
   because the row already gets one and a second walk is a second thing to
   keep in step. Unset, it falls back to 7 — the width the page ships. ─────*/
.hero--ledger { --adv: .6; }

.hero--ledger .book__r > b {
  font-size: min(19px, calc(var(--row) * .43),
                 calc(var(--led-io) / (var(--len, 7) * var(--adv))));
}
.hero--ledger .book__r > u {
  font-size: min(17px, calc(var(--row) * .38),
                 calc(var(--led-bal) / (var(--len, 7) * var(--adv))));
}
.hero--ledger .book__bf > u {
  font-size: min(16px, calc(var(--row) * .35),
                 calc(var(--led-bal) / (var(--len, 7) * var(--adv))));
}
/* The till's own figure is NOT part of this. Its row is `1fr auto`, so the
   figure takes what it needs and the label beside it gives way — the two can
   squeeze each other but they cannot print on top of each other, which is the
   only failure this rule exists to stop. A first draft bounded it with
   `calc((100% - 7.4rem) / …)`; a percentage inside a font-size is a
   percentage of the PARENT'S FONT SIZE, not of the row's width, so it would
   have quietly sized the till off the wrong number entirely. */


/* ═══ THE HEADLINE STOPS JUMPING WHILE THE FONTS ARRIVE ═══════════════════
   Traced at 390x844: the h1 is 104px tall while document.fonts.status is
   "loading" and 70px once it is "loaded". Three lines collapsing to two, and
   everything under it — the badges, the ledger, the whole compose strip —
   rises 34px about 1.3 seconds after the page appears. Measured as a layout
   shift of 0.0266, the only one on the page, and it lands exactly while a
   reader is getting to the end of the first sentence.

   It is not Sora. Sora is 9% WIDER than the system face standing in for it,
   so the roman half wraps the same either way. It is the hand: "only it adds
   itself up." is 293px in Caveat at 41.6px and 395px in Bradley Hand, which
   is what --font-hand falls back to on an Apple device. 395 does not fit 350,
   so it takes a second line, and the headline is three lines deep until
   Caveat lands.

   Measured, every candidate is wider than Caveat for this string:
     Bradley Hand 395 · Comic Sans MS 443 · Chalkboard SE 431 · Marker Felt
     377 · Apple Chancery 361 (which is what bare `cursive` resolves to here)
     · Noteworthy 349 · Snell Roundhand 347
   So the stand-in is asked to draw itself smaller until the real one comes.
   62% clears the widest of them inside 350px with room, which is the point:
   this face exists to hold the line count, not to be admired. It is on
   screen for about a second and it never moves anything.

   size-adjust only — no ascent/descent overrides. The hand is set inline
   inside headings whose line box is struck by the roman font, and overriding
   its metrics would move the baseline and trade one shift for another. ────*/
@font-face {
  font-family: 'Caveat stand-in';
  src: local('Bradley Hand'), local('Comic Sans MS'), local('Segoe Script'),
       local('Chalkboard SE'), local('Marker Felt'), local('Noteworthy');
  size-adjust: 62%;
  font-display: swap;
}
:root { --font-hand: 'Caveat', 'Caveat stand-in', 'Bradley Hand', 'Segoe Script', cursive; }


/* ═══ THE RAIL SAYS THERE IS A RAIL ═══════════════════════════════════════
   Below 900px the six slips become something the reader pushes with a thumb.
   Measured at 390x844 that is 1,526px of content in a 390px window, and the
   only thing telling anybody so was a slip sliced off by the edge of the
   screen — which reads as a layout fault, not as an invitation.

   Two marks, both in the page's own hand and neither of them app furniture.

   The edge softens on whichever side has more to come, so a cut slip reads
   as continuing rather than broken; it is driven off two classes rather than
   being on always, because a fade at the left of the FIRST slip is just a
   first slip that looks wrong.

   And a rule underneath, with the part of the rail you are looking at inked
   on it. The nav already draws exactly this for the vertical axis and calls
   it how much of the argument you have been through. This is the same mark,
   one axis over — so the reader has seen it before they get here. ─────── */
.folk__rule { display: none; }

@media (max-width: 899px) {
  .folk {
    --mask-l: 0%;
    --mask-r: 100%;
    -webkit-mask-image: linear-gradient(to right, transparent 0,
      #000 var(--mask-l), #000 var(--mask-r), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0,
      #000 var(--mask-l), #000 var(--mask-r), transparent 100%);
  }
  .folk.has-before { --mask-l: 6%; }
  .folk.has-after  { --mask-r: 92%; }

  /* The focus ring has to be visible on a box whose own edges are being
     masked away, so it is drawn inside. */
  .folk:focus-visible { outline: none; }
  .folk:focus-visible .folk__row > :first-child .slip {
    outline: 2px solid var(--ink-hi); outline-offset: 3px;
  }

  .folk__rule {
    display: block; position: relative;
    margin: clamp(12px, 2.4vw, 18px) var(--gut, 20px) 0;
    height: 2px; border-radius: 2px;
    background: rgb(var(--rule-rgb) / .34);
  }
  .folk__rule[hidden] { display: none; }
  .folk__rule i {
    position: absolute; top: 0; bottom: 0;
    width: var(--w, 30%);
    left: calc(var(--p, 0) * (100% - var(--w, 30%)));
    background: var(--ink-hi); border-radius: 2px;
  }
}


/* ═══ THE CONTROLS THAT WERE LEFT WITH NOTHING ════════════════════════════
   Further up this file: "Two things separate a native control from a link:
   it never flashes a grey box on tap, and it moves under the press." The
   first half was done page-wide — -webkit-tap-highlight-color: transparent
   on .pg-plain and on every a, button, summary and [role=button] inside it.
   The second half was done to a LIST: .btn, a.store, .nav__link, .thm__b.

   Anything not on that list therefore ended up worse than untouched. It lost
   the browser's own flash and got nothing in its place. Forced :active on
   every control the phone actually offers and diffed the computed style —
   nine of fourteen came back with no response of any kind, and one of them
   is the burger, which is the whole of this page's navigation on a phone.

   Two idioms, because these are two kinds of object. A burger and a brand
   are things, and a thing moves under a finger — the same 3% the badges and
   the buttons already take, with the brand pressed from its left edge so the
   mark stays where the eye put it. The chips are type, and 3% of 13px type
   is nothing anybody can see — so they do what ink does, which is darken.

   The skip link is deliberately not here: it is reached by a keyboard and
   activated with Enter, and it has a focus ring for that. ──────────────── */
.pg-plain .nav__burger,
.pg-plain .nav__brand,
.pg-plain .compose__dir button,
.pg-plain .compose__cur button {
  transition: transform .34s var(--ease-spring),
              color var(--t-ui) var(--ease-out),
              background-color var(--t-ui) var(--ease-out);
}
.pg-plain .nav__burger:active {
  transform: scale(.968); transition-duration: 0s;
}
.pg-plain .nav__brand:active {
  transform: scale(.968); transform-origin: left center; transition-duration: 0s;
}
.pg-plain .compose__dir button:active,
.pg-plain .compose__cur button:active {
  color: var(--ink-hi); transition-duration: 0s;
}
/* The one that is already ink has nowhere to darken to, so it takes the
   other answer instead — otherwise pressing the chip you are already on is
   the one press on this page that still does nothing. */
.pg-plain .compose__dir button.is-on:active,
.pg-plain .compose__cur button.is-on:active {
  transform: scale(.93); transition-duration: 0s;
}


/* ═══ THE CLOSE STOPS TAKING A WHOLE SCREEN ═══════════════════════════════
   Measured at 390x844 the close was 680px — four fifths of a phone screen —
   and this is where it went:

     70  section padding above it
     92  the heading, at 42px over two lines,  + 18 under it
     77  the summary line
     70  the price,                            + 18 above it
     54  the button,                           + 30 above it
     39  the fine print,                       + 16 above it
     25  "or just tell me when it lands",      + 16 above it
     21  the copyright line,                   + 54 above it
     75  the wordmark, set at 101px,           + 24 above it

   About 400px of that is words. The rest — roughly 280 — is space between
   them and a signature set larger than the headline it sits under. A close
   that fills a screen on the strength of its own margins is asking the
   reader to scroll past the ask to find the ask.

   Nothing is removed. Every sentence the page closes with is still here, in
   the same order; they are simply set at the distances a phone has room for.
   The wordmark stays a signature and stops being a billboard: 26vw is 101px
   on a 390px screen, which is bigger than the heading, and it still runs off
   the bottom edge at 44 because the section clips it the same way. ─────── */
@media (max-width: 899px) {
  .sec--lime { padding-top: clamp(34px, 8vw, 46px); }

  /* The ceilings matter as much as the floors. A first pass capped these at
     38px and 62px, which is right at 390 and wrong at 899 — a tablet would
     have gone from a 66px heading to a 38px one, which is not tightening, it
     is shrinking. The ceilings are set where the old values already were at
     the top of this range, so only the narrow end moves. */
  .sec--lime .h { font-size: clamp(30px, 8vw, 56px); margin-bottom: 10px; }
  /* ── AND THE SUMMARY GOES ───────────────────────────────────────────
     "Open a book for anything you run and write the day down as it happens
     — any currency, with the accounting handled for you." Every clause of
     that has already been demonstrated by the time a phone reader arrives
     here: "anything you run" is claim 05, "write the day down" is the hero
     and claim 02, "any currency" is claim 06 and the compose strip's own
     note, "the accounting handled for you" is claim 03. It is a summary for
     somebody who skimmed, and it costs 70px — a sixth of the close — sitting
     between the promise and the price.

     It stays above 900px, where there is room for a reader to be told twice
     and where the section is not competing for a screen. */
  .sec--lime .lede { display: none; }

  .freely { margin-top: 12px; }
  .sec--lime .btn--open { margin-top: 20px; }
  .sec--lime .fine { margin-top: 10px; }
  .orelse { margin-top: 10px; }

  .sec--lime .closerow { margin-top: 22px; }
  .sec--lime .signoff {
    font-size: clamp(44px, 12vw, 96px);
    margin-top: 10px;
  }
}


/* ═══ AND THE SAME ON A DESKTOP, WHERE IT IS WORSE ════════════════════════
   The tightening above was scoped to phones because a phone was what I was
   shown. Measured at 1440x900 the close is 1,054px — one and a sixth of the
   screen, so the reader scrolls past the ask to reach the ask — and 2,000px
   wide it is 1,091. Where it goes:

     122  padding above the section
     210  the heading, set at 96px,            +  18 under it
      91  the summary line
      48  the price,                           +  26 above it
      54  the button,                          +  44 above it
      20  the fine print,                      +  16 above it
      25  the alternative,                     +  16 above it
      21  the copyright line,                  +  96 above it
     277  the wordmark, set at 374px,          +  43 above it

   Two objects are 46% of it. A 96px heading and a 374px signature are a
   poster, and the page they close is a cash book — the largest thing in the
   hero is a ledger total at 42px, so the close was shouting four times as
   loud as anything it was closing.

   Same treatment, same order, nothing removed: the heading comes back toward
   the size this file already uses for the section elsewhere (62px), the
   signature stays a signature and keeps running off the bottom edge, and the
   gaps come in to what the copy needs rather than what the viewport offered.
   The summary line stays here — a wide column has room to say a thing twice,
   and it is only on a phone that it was costing a sixth of the section. ── */
@media (min-width: 900px) {
  .sec--lime { padding-top: clamp(52px, 5vw, 84px); }

  .sec--lime .h { font-size: clamp(42px, 5vw, 64px); margin-bottom: 12px; }
  .sec--lime .lede { font-size: clamp(16.5px, 1.2vw, 18.5px); }

  .freely { margin-top: 18px; }
  .sec--lime .btn--open { margin-top: 28px; }
  .sec--lime .fine { margin-top: 12px; }
  .orelse { margin-top: 12px; }

  .sec--lime .closerow { margin-top: clamp(30px, 3vw, 48px); }
  .sec--lime .signoff {
    font-size: clamp(96px, 13vw, 200px);
    margin-top: clamp(16px, 1.6vw, 26px);
  }
}


/* ═══ THE CLAIM YOU ARE ON IS UNDERLINED, THE WAY A READER WOULD ══════════
   Measured at 390x844: each claim's own ink is 167px and the beat is 309, so
   142px of every beat — 46% — is blank paper; and everything that marks the
   live claim is either a colour change on a heading or a hairline growing
   from 26px to 54. Both are true and neither is felt. Scrolling this section
   on a phone, the only thing a reader can actually SEE change is the screen
   above the words.

   This file has rejected dimming the other claims twice, on good grounds
   both times — they are real sentences somebody may be part-way through, and
   any dim deep enough to notice takes body copy under 4.5:1. That argument
   still holds and nothing here touches it: every word stays at full strength.

   What it adds is the thing a person does to the line they are on in a book
   they own — they underline it. The stroke is the page's own pen, the same
   hand-drawn --pen every <em> on this site is marked with, and it draws left
   to right as the claim arrives and retreats as it leaves. It marks; it does
   not hide. ───────────────────────────────────────────────────────────── */
.pin__item h3 {
  /* fit-content so the stroke ends where the words end rather than at the
     column's edge — an underline that overshoots its heading is a rule, and
     this page already has plenty of those. */
  width: fit-content; max-width: 100%;
  background-image: var(--pen);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% .085em;
  /* The retreat is a transition, because a claim on its way out is not being
     scrubbed by anything — it simply leaves. */
  transition: color .5s var(--ease-out),
              background-size .38s var(--ease-out);
}
/* ── AND THE STROKE IS ON THE SAME CLOCK AS EVERYTHING ELSE ─────────────
   The first version of this was class-driven: .62s the instant the claim
   went live, then nothing for the rest of the beat. That is exactly the
   pattern this file already replaced under the claim NUMBER, and for the
   reason written there — "it snapped from 26 to 54px the instant the claim
   went live, and then sat there with nothing to do."

   --cp is the live claim's progress through its own beat, written on the
   copy column every frame the reader moves. The hairline already runs on it; so
   does every row inside the phone beside it. Now the pen does too: it draws
   at the speed of the thumb, finishes where the hairline finishes and where
   the screen's rows finish, and the two columns are one movement instead of
   two things that happen near each other.

   No transition while live — a transition on a property the scroll is
   already driving frame by frame fights it, which is the same note this file
   leaves on the screens. */
.pin__item.is-on h3 {
  background-size: calc(clamp(0, var(--cp, 1) / 0.45, 1) * 100%) .085em;
  transition: color .5s var(--ease-out);
}

/* Under reduced motion block 2f gives every claim `is-on` at once — which is
   right for the signals that were already here, since all it means is that
   every heading is at full ink and readable. It is wrong for a stroke: five
   headings underlined simultaneously marks nothing at all. Checked, not
   assumed — all five came back at 100%. A mark that says "you are here" has
   no meaning without the movement that draws it, so it does not appear. */
@media (prefers-reduced-motion: reduce) {
  /* .is-on as well as the base: the live rule is two classes and outranks a
     one-class reduced-motion rule, so without it the heading kept easing its
     colour over half a second under a setting that asks for none. Caught by
     reading transitionDuration back rather than trusting the selector. */
  .pin__item h3,
  .pin__item.is-on h3 { transition: none; background-image: none; }
}
