/* Homepage chapters. One spacing rhythm: .section from pages.css. */

/* ---------- Hero: one shop, two screens ----------
   Not a scroll stage: the whole product has to read before any scroll, so the
   first viewport is a still composition - the real web dashboard behind, the
   real phone home screen in front, one chip naming the figure both show. The
   only motion is the load-in (reveal) and the chip sliding in last. */
/* overflow: clip, not hidden: hidden makes .hero a scroll container, and the
   hero-stage view timeline (data-lift-scope) would then track .hero instead of
   the page and never progress. clip only clips. */
.hero { background: var(--bg); overflow: hidden; overflow: clip; }
.hero-grid { display: grid; grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr); gap: 40px; align-items: start; align-content: center; min-height: calc(100vh - 74px); min-height: calc(100svh - 74px); padding-top: 96px; padding-bottom: 40px; }
.hero-copy h1 { font-family: var(--display); font-size: clamp(40px, 5vw, 72px); font-weight: 600; line-height: 1.04; letter-spacing: -0.028em; margin: 0 0 24px; color: var(--ink); }
.hero-copy h1 .ink { color: var(--brand); }
.hero-copy .lead { font-size: var(--fs-lead); line-height: 1.45; color: var(--ink-soft); max-width: 540px; margin: 0 0 24px; }
/* The capability row: three short claims, one pill each, no icons. Sits under
   the CTA so the button is the fourth thing read, not the seventh; each pill is
   a chapter further down. */
.caps { list-style: none; margin: 24px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px 8px; max-width: 560px; }
.caps li { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 500; color: var(--ink); padding: 8px 12px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); }
.caps li::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: var(--brand); flex: none; }
.hero-cta { display: flex; align-items: center; gap: 24px; flex-wrap: wrap; }
.hero-note { margin: 24px 0 0; font-size: var(--fs-small); color: var(--ink-faint); }
/* The picture column. The stage is 118% of its grid cell so the browser
   window runs past the container edge (clipped by .hero's overflow at the
   viewport, or simply sitting in the gutter on wide screens): the dashboard
   reads at a real size instead of a thumbnail. Both columns align to the top
   of the row, so the picture hangs from the headline's line and the spare
   height sits under it, never above.
   Browser and phone stand on ONE baseline (both bottom: 0): two objects on a
   desk, the phone in front, covering only the dashboard's side panel. The
   phone is inset from the stage's right edge so it stays inside the
   container while the browser bleeds.
   Each object has a wrapper (.hero-item) that carries the scroll parallax and
   an inner frame that carries the load-in, because an animation and a
   transition cannot share one element's transform. */
.hero-stage { position: relative; width: 118%; aspect-ratio: 100 / 74; container-type: inline-size; margin-top: 8px; }
.hero-item { position: absolute; bottom: 0; }
.hero-item-browser { left: 0; width: 100cqw; z-index: 1; }
.hero-item-phone { right: 6cqw; z-index: 3; --device-w: 30cqw; }
/* The phone stands over the bar's right end, so the hero's pill sits right
   after the lights instead of centred, and stays clear of the phone. */
.hero-browser .browser-bar span { margin-left: 10px; margin-right: auto; flex-basis: 40%; }
.hero-browser img { object-position: top; }
/* Motion, three kinds, all restrained:
   1. Load-in: copy, then the browser lifts in, then the phone, a little
      later and from further down, on the same exponential ease-out.
      Reveal-gated, so no-JS sees the finish.
   2. Depth on scroll: as the hero leaves, the phone travels further than the
      browser (data-lift-far vs data-lift, motion.css), which reads as two
      objects at two depths. Native scroll-driven animation only.
   3. The phone cycles its screen every four seconds: home, a bill in
      progress, the paid checkout. Opacity crossfades only; reduced motion
      holds the home screen. */
.js-reveal .hero-stage:not(.is-in) .hero-browser { opacity: 0; transform: translateY(22px); }
.js-reveal .hero-stage:not(.is-in) .hero-device { opacity: 0; transform: translateY(40px) scale(0.97); }
.js-reveal .hero-stage.is-in .hero-browser { opacity: 1; transform: none; transition: opacity 500ms var(--ease-out), transform 500ms var(--ease-out); transition-delay: 80ms; }
.js-reveal .hero-stage.is-in .hero-device { opacity: 1; transform: none; transition: opacity 400ms var(--ease-out), transform 600ms var(--ease-out); transition-delay: 240ms; }
.hero-device .cycle { opacity: 0; }
.hero-device .cycle-2 { animation: hero-cycle-2 12s linear infinite 3s; }
.hero-device .cycle-3 { animation: hero-cycle-3 12s linear infinite 3s; }
@keyframes hero-cycle-2 { 0%, 30% { opacity: 0; animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } 33%, 63% { opacity: 1; animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } 66%, 100% { opacity: 0; } }
@keyframes hero-cycle-3 { 0%, 63% { opacity: 0; animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } 66%, 97% { opacity: 1; animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .hero-device .cycle { animation: none; } }
@media (max-height: 760px) and (min-width: 901px) { .hero-grid { padding-top: 64px; } }
@media (max-width: 900px) {
  .hero-grid { grid-template-columns: 1fr; gap: 24px; min-height: 0; padding-top: 88px; padding-bottom: 32px; }
  .hero-copy h1 { margin-bottom: 16px; }
  .hero-copy .lead { margin-bottom: 16px; }
  .caps { margin-top: 20px; gap: 8px; }
  .caps li { padding: 6px 10px; }
  .hero-note { margin-top: 12px; }
  /* Stacked: the stage is the content width again; same two objects on one
     baseline, the phone a little larger. */
  .hero-stage { width: 100%; aspect-ratio: 100 / 74; margin-top: 0; }
  .hero-item-browser { width: 92cqw; }
  .hero-item-phone { right: 0; --device-w: 32cqw; }
  /* The phone starts at 68cqw here, so the pill moves left and shrinks its
     type; the shared rule ellipsises anything that still does not fit. */
  .hero-browser .browser-bar span { flex-basis: 46%; font-size: 11px; padding-left: 20px; }
}
@media (max-width: 500px) {
  .hero-grid { padding-top: 72px; gap: 20px; }
  .hero-copy .lead { line-height: 1.4; }
  /* Phones: the pills go (they are the chapters below anyway), and the CTA
     loses its arrow so "Start free for 3 months" and "See it work" share one
     row at 390px; below ~380px the row wraps rather than clips. Together with
     the pills going this lifts the product picture above the fold. */
  .caps { display: none; }
  .hero-cta { gap: 8px 12px; justify-content: space-between; }
  .hero-cta .btn-primary { padding: 0 16px; }
  .hero-cta .btn-primary svg { display: none; }
  .hero-note { font-size: 13px; margin-top: 12px; }
  .hero-item-phone { --device-w: 34cqw; }
}

/* ---------- Hero ground: deep teal (body.hero-dark) ----------
   The hero is the first of the two deep-teal chapters (the counter is the
   other). The header band takes the same colour, with white links, until the
   page scrolls. Kushal's call, 2026-09-08, after a side-by-side with cream. */
body.hero-dark .hero { background: var(--brand-darker); }
body.hero-dark .hero-copy h1 { color: #fff; }
body.hero-dark .hero-copy h1 .ink { color: var(--brand-light); }
body.hero-dark .hero-copy .lead { color: rgba(255,255,255,0.8); }
body.hero-dark .caps li { color: #fff; background: rgba(255,255,255,0.06); border-color: rgba(255,255,255,0.18); }
body.hero-dark .caps li::before { background: var(--brand-light); }
body.hero-dark .hero .btn-primary { background: #fff; color: var(--brand-darker); }
body.hero-dark .hero .btn-primary:hover { background: var(--bg); }
body.hero-dark .hero .btn-link { color: #fff; }
body.hero-dark .hero-note { color: rgba(255,255,255,0.6); }
body.hero-dark .hero-device { box-shadow: inset 0 0 0 1px rgba(255,255,255,0.22), 0 0 0 1px rgba(0,0,0,0.5), 0 18px 30px -18px rgba(0,0,0,0.5), 0 50px 90px -34px rgba(0,0,0,0.6); }
body.hero-dark .hero-browser { box-shadow: 0 0 0 1px rgba(255,255,255,0.12), 0 18px 30px -18px rgba(0,0,0,0.45), 0 50px 90px -34px rgba(0,0,0,0.6); }
/* The header sits above the hero in flow, so its own band has to be the
   chapter colour too; once scrolled it returns to the cream bar. */
body.hero-dark .site-header:not(.scrolled) { background: var(--brand-darker); }
body.hero-dark .site-header:not(.scrolled) .brand { color: #fff; }
body.hero-dark .site-header:not(.scrolled) .nav-links a { color: rgba(255,255,255,0.82); }
body.hero-dark .site-header:not(.scrolled) .nav-links a:hover { color: #fff; background: rgba(255,255,255,0.08); }
body.hero-dark .site-header:not(.scrolled) .nav-login { color: #fff !important; }
body.hero-dark .site-header:not(.scrolled) .nav-cta { background: transparent; color: #fff !important; box-shadow: inset 0 0 0 1px rgba(255,255,255,0.4); }
body.hero-dark .site-header:not(.scrolled) .nav-cta:hover { background: rgba(255,255,255,0.1); }

/* ---------- Scan chapter: stock in one photo ----------
   The invoice, scanline, phone curtain and count chip, scroll-driven. */
.scan { background: var(--bg); }
.scan .stage-pin { align-items: stretch; }
.scan-grid { display: grid; grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr); gap: 40px; align-items: center; width: 100%; min-height: 100%; padding-top: 72px; padding-bottom: 24px; }
.scan-copy h2 .ink { color: var(--brand); }
.scan-copy .lead { max-width: 500px; }
.scan-stage { position: relative; height: min(78vh, 760px); }
.scan-stage .invoice { position: absolute; left: 0; top: 22%; z-index: 1; }
.scan-stage .device { position: absolute; right: 0; top: 0; z-index: 2; --device-w: min(240px, 27vw); }
.scan-stage .count-chip { position: absolute; right: 0; bottom: 6%; z-index: 3; }
@media (max-width: 900px) {
  /* 84px, not 64: the sticky header overlays the top of the pin mid-scroll. */
  .scan-grid { grid-template-columns: 1fr; gap: 20px; padding-top: 84px; align-content: start; }
  .scan-copy h2 { margin-bottom: 14px; }
  .scan-copy .lead { margin-bottom: 18px; }
  /* Short enough that the bottom of the stage - and with it the invoice total -
     stays above the fold at rest; the pinned hero starts 72px down the viewport,
     so a taller stage pushes the total off-screen. */
  .scan-stage { height: 44vh; height: 44svh; min-height: 344px; }
  /* No room to sit beside the phone, and hiding the amounts behind it reads as
     an amputated artefact. The paper moves IN FRONT, bottom-left, and drops the
     Batch column so Qty and Amount both survive the narrower page. */
  /* bottom: 70px, not 44px: the invoice is bottom-anchored with a fixed
     padding-bottom, so its total line's absolute position is set entirely by
     this offset and .invoice's own padding-bottom - content above (rows,
     header) has zero effect on it (verified: even 0px row padding left the
     total's bottom pixel-identical). At 44px the total sat ~22px below the
     stage-pin's bottom on a 360x740 viewport; 70px clears it with margin
     while keeping ≥8px above the hero note at all three required widths. */
  .scan-stage .invoice { position: absolute; left: 0; bottom: 70px; top: auto; z-index: 3; width: 264px; font-size: 11px; padding: 12px 12px 14px; }
  /* The chip is wider than the gap left of the invoice, so it takes the last
     band of the stage on its own rather than printing over the total. */
  .scan-stage .count-chip { bottom: 0; }
  .scan-stage .inv-cols, .scan-stage .inv-row { grid-template-columns: 1fr 30px 72px; }
  /* The letter-spaced header does not fit 230px at 10px; it wrapped to two lines. */
  .scan-stage .inv-head { font-size: 11px; letter-spacing: 0.02em; }
  /* At 360x740 the total line fell ~22px below the pin with the base row
     padding/size; tightened here rather than growing the stage further. */
  .scan-stage .inv-row { padding: 4px 0; font-size: 11px; }
  .scan-stage .inv-cols span:nth-child(2), .scan-stage .inv-row span:nth-child(2) { display: none; }
  .scan-stage .device { --device-w: 46vw; right: 0; top: 0; z-index: 2; }
}

/* Only below ~500px do paper and phone collide (230px + 46vw stops fitting side
   by side), and the paper buried the "Review import" header and first rows.
   Moving it down cannot fix that: .stage-pin is 100svh, overflow: hidden, so
   its bottom CLIPS the total - ~46px of travel against a ~122px shortfall. The
   invoice is bottom-anchored, so shrinking its HEIGHT lowers its top for free:
   three fewer lines takes it 264.5px -> 171.6px, .inv-more carrying the new
   remainder. 210px would wrap the names and make it TALLER, not shorter. */
@media (max-width: 500px) {
  /* At 360x740 the total fell 68px below the fold. The copy block is what
     pushes it there, and tightening it moves the whole assembly up RIGIDLY -
     the phone's top band keeps its clearance, which is only ~18px to spend.
     ("See it work" still wraps at 360: 246px button + 111px link in 320px.) */
  .scan-grid { padding-top: 84px; gap: 16px; }
  .scan-copy .lead { line-height: 1.4; margin-bottom: 14px; }
  .scan-stage { height: 50vh; height: 50svh; min-height: 344px; }
  /* 62px balances 360x740: raising it lifts the total AND the invoice top. */
  .scan-stage .invoice { bottom: 62px; font-size: 11px; }
  .scan-stage .inv-row, .scan-stage .inv-cols { padding: 2px 0; }
  .scan-stage .inv-more { padding: 2px 0; font-size: 11px; }
  .scan-stage .inv-row-extra, .scan-stage .more-full { display: none; }
  .scan-stage .more-lite { display: inline; }
  /* 36vw lowers the bottom of the phone's first rows - the band to clear. */
  .scan-stage .device { --device-w: 36vw; }
}

/* Trust line */
.trust { list-style: none; margin: 0; padding: 16px 0; display: flex; flex-wrap: wrap; gap: 12px 32px; justify-content: center; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); font-size: var(--fs-small); font-weight: 500; color: var(--ink-soft); }
.trust li { display: inline-flex; align-items: center; gap: 8px; }
.trust svg { width: 16px; height: 16px; stroke: var(--brand-deep); fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* ---------- Android install bar ----------
   Homepage only (index.html is the only page that carries the markup, and
   scripts/home.js only renders it on Android), so it lives here rather than in
   the shared pages.css. Moved verbatim out of the old inline <style>. */
.play-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  display: none; align-items: center; gap: 14px;
  background: var(--surface);
  border-top: 1px solid var(--line);
  padding: 12px;
  padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  box-shadow: 0 -8px 24px -8px rgba(15, 26, 23, 0.22);
}
.play-bar-main { display: flex; align-items: center; gap: 12px; flex: 1; min-width: 0; }
/* Uses the shared .brand-mark svg verbatim, so the mark cannot drift from
   the header and footer. Do not restyle its stroke here. */
.play-bar .brand-mark { width: 40px; height: 40px; flex: 0 0 40px; }
/* The text block absorbs the spare width, so Install lands hard against the
   trailing edge, as far from the leading-edge close button as the bar allows.
   Dismissal is sticky for 30 days, so the two must not sit side by side. */
.play-bar-text { flex: 1; min-width: 0; }
.play-bar-title { display: block; font-size: 15px; font-weight: 700; color: var(--ink); line-height: 1.25; }
.play-bar-sub { display: block; font-size: 12px; color: var(--ink-soft); line-height: 1.3; }
.play-bar-cta {
  flex: none; font-size: 13px; font-weight: 700; color: #fff;
  background: var(--brand); padding: 8px 16px; border-radius: 999px;
}
.play-bar-close {
  flex: none; width: 40px; height: 40px; border: 0; padding: 0;
  background: transparent; color: var(--ink-faint); cursor: pointer;
  display: grid; place-items: center; border-radius: 10px;
}
.play-bar-close:hover { background: var(--bg-warm); color: var(--ink-soft); }
.play-bar-close svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 2.4; stroke-linecap: round; }
.play-bar-main:focus-visible, .play-bar-close:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* The bar and the footer gap are driven by the same body flag, so they can
   never disagree. The gap goes on the footer rather than the body so the
   dark green runs right up under the bar instead of leaving a cream strip. */
body.has-play-bar .play-bar { display: flex; }
body.has-play-bar footer { padding-bottom: 106px; }
@media (prefers-reduced-motion: no-preference) {
  .play-bar { animation: play-bar-in 0.28s var(--ease-out) both; }
  @keyframes play-bar-in { from { transform: translateY(100%); } to { transform: translateY(0); } }
}

/* ---------- Counter chapter (committed deep teal) ---------- */
.counter { background: var(--brand-darker); color: #fff; }
.counter-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 48px; align-items: center; width: 100%; padding-top: 84px; padding-bottom: 16px; }
.beats { list-style: none; margin: 24px 0 0; padding: 0; display: grid; gap: 12px; counter-reset: beat; }
/* Each beat is a card, so the lit one reads as a block, not a line: the
   opacity keyframes dim the whole card, and a left accent bar (::after, its
   own keyframes in materials.css) marks the one the phone is showing.
   The dim floor is 0.64, not 0.45: at 0.45 the beat's own white text computes to
   #779590 on the deep teal — 3.66:1, an AA failure axe reports on the resting
   page. 0.64 keeps a clear dim-vs-lit read and takes the .92 span to 4.67:1. */
.beat { position: relative; display: grid; grid-template-columns: 28px 1fr; gap: 12px; padding: 12px 16px; border: 1px solid rgba(255,255,255,0.16); border-radius: 14px; background: rgba(255,255,255,0.06); opacity: 0.64; }
.beat:last-child { opacity: 1; }
.beat::after { content: ""; position: absolute; left: -1px; top: 12px; bottom: 12px; width: 3px; border-radius: 3px; background: var(--brand-light); opacity: 0; }
.beat:last-child::after { opacity: 1; }
.beat::before { counter-increment: beat; content: counter(beat); width: 28px; height: 28px; border-radius: 50%; border: 1px solid rgba(255,255,255,0.45); display: grid; place-items: center; font: 600 13px/1 var(--sans); }
.beat b { display: block; font-size: 17px; letter-spacing: -0.01em; margin-bottom: 4px; }
/* grid-column: 2 is load-bearing. The number is a ::before, so it takes
   column 1 row 1 and <b> takes column 2; without this the <span> falls
   into column 1 of an implicit second row - a 28px-wide column - and the
   description wraps one word per line, 250px tall, pushing the heading
   out of the pinned viewport. */
/* 0.92, not 0.75: the dim opacity multiplies this. 0.92 x 0.64 is 4.67:1
   dimmed (0.92 x 0.6 measured 4.31:1, an AA miss) and still reads softer than the bold. */
.beat span { display: block; grid-column: 2; color: rgba(255,255,255,0.92); font-size: var(--fs-small); line-height: 1.5; }
/* The copy column is taller than the phone: anchored to the top so that on a
   short window it is the note at the bottom that goes, never the heading. */
.counter-copy { align-self: start; }
@media (max-height: 780px) and (min-width: 901px) { .counter-note { display: none; } }
.counter-note { margin: 24px 0 0; color: rgba(255,255,255,0.62); font-size: var(--fs-small); line-height: 1.55; max-width: 460px; }
/* container-type: the three artefacts are sized against THIS column, not the
   viewport. With fixed px widths the card and the receipt kept their size while
   the column shrank, so between 901 and 1139px the card covered 36-52% of the
   phone. cqw ties every artefact to the stage's own width. */
.counter-stage { position: relative; height: min(88vh, 760px); container-type: inline-size; --device-w: min(300px, 58cqw, 38vh); }
/* left: 0, not the 8% the brief drafted: at 8% the phone ran to within 33px of
   the WhatsApp card and the card covered 28% of the screenshot. Flush left, the
   48px grid gap still separates the phone from the copy.
   --device-w lives on the stage, not here, so the receipt can be placed against
   the phone's own width. The 38vh term is the stage-height cap: the phone is
   20/9 of its width, and on a short window a 300px phone would hang below the
   stage (at 80vh it already did on a 800px-tall window - hence 88vh). */
.counter-stage .device { position: absolute; left: 0; top: 3%; }
.counter-stage .wa-card { position: absolute; right: 0; top: 16%; width: min(260px, 50cqw); }
/* The wrapper, not the paper, carries the unroll clip and the placement. Its
   padding opens room for .paper's shadow inside the clip; the equal negative
   margin cancels the padding so the paper lands exactly where it used to.
   left is measured off the phone, not the column: the phone's item amounts sit
   at 86.5-92.5% of the device width (measured off the capture, mapped through
   object-fit: cover), so anchoring the paper at 94% of the device width keeps
   every amount readable at every width while the paper still overlaps the
   phone's edge. A percentage of the stage cannot track this. */
.counter-stage .receipt-wrap { position: absolute; left: calc(var(--device-w) * 0.94); bottom: 0; padding: 0 48px 56px 0; margin: 0 -48px -56px 0; clip-path: inset(0); }
.counter-stage .receipt { width: min(214px, 44cqw); font-size: min(11px, 2.26cqw); }
/* The beats dim and light with the scroll; with motion off they must all read. */
@media (prefers-reduced-motion: reduce) {
  .beat { opacity: 1; }
  .beat::after { opacity: 1; }
  /* .stage-pin gives up its overflow: hidden with motion off, and the wrapper's
     -48px right margin (shadow room, see above) then scrolls the whole page
     sideways — at every width, 360 included. Clip on the stage instead; the
     48px clip margin keeps the paper's drop shadow. */
  .counter-stage { overflow-x: clip; overflow-clip-margin: 48px; }
}
/* The pin is one viewport tall: the beat descriptions and the note do not fit
   beside the phone on a small screen, so they are hidden inside the pin and
   said once below the stage instead. */
.counter-mobile-copy { display: none; }
@media (max-width: 900px) {
  .counter-grid { grid-template-columns: 1fr; gap: 16px; align-content: start; padding-top: 72px; }
  .counter-copy .lead { display: none; }
  .beats { margin-top: 16px; gap: 8px; }
  .beat span { display: none; }
  .beat { grid-template-columns: 24px 1fr; align-items: center; padding: 12px 16px; border-radius: 12px; }
  .beat::after { top: 9px; bottom: 9px; }
  .beat b { margin: 0; }
  .counter-note { display: none; }
  .counter-stage { height: 44vh; height: 44svh; min-height: 300px; --device-w: 44vw; }
  /* z-index: at 375 the three artefacts have to overlap, and the receipt
     comes later in the DOM, so by default it painted over the card's
     message text. The card wins the top of the stack; everything from the
     receipt's items down stays clear of it. */
  .counter-stage .wa-card { width: 200px; right: 0; top: 14px; z-index: 2; }
  .counter-stage .wa-card p { font-size: 13px; }
  /* Both artefacts are pinned in px from the top of the stage, not in % of a
     height that changes with the viewport: the card is 153px tall and the
     receipt's first dashed rule sits 83px below its own top, so top 14px and
     top 84px put the card's bottom edge exactly on that dashed rule at every
     small size. With bottom-anchoring the card's edge sliced an address line
     on a 812px-tall phone and an item row on a 640px-tall one. */
  .counter-stage .receipt-wrap { left: auto; right: 0; top: 84px; bottom: auto; }
  .counter-stage .receipt { width: 170px; font-size: 9.5px; }
  .counter-mobile-copy { display: block; padding-top: 24px; color: var(--ink-soft); font-size: var(--fs-small); line-height: 1.55; }
}

/* Split chapters (khaata, registers, browser) */
.split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 56px; align-items: center; }
.split-wide { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); }
.split .body { font-size: var(--fs-body); line-height: 1.6; color: var(--ink-soft); margin: 16px 0 0; max-width: 520px; }
.split-visual { position: relative; display: flex; justify-content: center; align-items: center; min-height: 520px; }
/* The khaata stage is a size container, so the notebook and the phone are both
   sized from the column they share rather than from the viewport. With a fixed
   300px notebook and a right-pinned phone the composition only worked at the
   two widths it was tuned at: between 901 and 1150 the phone sliced the paper
   (at 1024 through the dates, at 905 through the amounts and the paid tags).
   Scaling both pieces off the column keeps the gap between them proportional
   at every width. */
.khaata .split-visual { container-type: inline-size; }
.khaata .split-visual .notebook { position: absolute; left: 0; top: 10%; z-index: 1; transform: rotate(-3deg); width: min(300px, 55cqw); --nb-fs: min(13px, 2.9cqw); }
.khaata .split-visual .device { position: relative; z-index: 2; margin-left: auto; --device-w: min(216px, 42cqw); }
.registers .split-visual .device { --device-w: min(280px, 26vw); }
/* Below 1150 the column no longer holds a name, an amount and a date at a
   readable size. The date is the column that can go. */
@media (max-width: 1150px) {
  .khaata .split-visual .nb-row { grid-template-columns: 1fr auto; }
  .khaata .split-visual .nb-row span:nth-child(3) { display: none; }
}
@media (max-width: 900px) {
  .split, .split-wide { grid-template-columns: 1fr; gap: 32px; }
  .split-visual { min-height: 0; }
  /* Stacked, the visual is handed the whole content width -- far more than the
     composition wants -- so it is capped at 560px and anchored to the
     container's left edge (a block narrower than that used to sit 17px left of
     the gutter on a bled-out negative margin). The phone deliberately sits on
     the notebook's right gutter, which is widened here so the overlap lands on
     blank paper and never reaches an amount. */
  .khaata .split-visual { max-width: 560px; margin: 0; height: auto; justify-content: flex-start; }
  .khaata .split-visual .notebook { left: 0; top: 4%; width: min(260px, 52cqw); --nb-fs: min(13px, 2.6cqw); padding-right: calc(var(--pitch) * 34 / 30); }
  .khaata .split-visual .device { --device-w: min(216px, 40cqw); margin-left: calc(min(260px, 52cqw) * 0.93); }
  .registers .split-visual .device { --device-w: 220px; }
}
@media (max-width: 430px) {
  /* At 375 the container leaves the stage 335px: a notebook wide enough for a
     name and an amount plus a phone beside it does not fit, so the paper is let
     20px into the left gutter and the phone shrinks. This bleed is scoped to
     the phone band -- at 768 it was what pushed the block outside the gutter. */
  .khaata .split-visual { width: min(calc(100% + 20px), 400px); height: 320px; margin-left: -20px; }
  .khaata .split-visual .notebook { left: 8px; width: 196px; --nb-fs: 12px; padding: 18px 12px 22px 32px; }
  .khaata .split-visual .notebook::before { left: 22px; }
  .khaata .split-visual .device { --device-w: min(150px, 40vw); margin-left: auto; }
  /* The paid tag is the one thing that can push a name out of its track here. */
  .khaata .split-visual .nb-paid em { font-size: 11px; letter-spacing: 0.02em; margin-left: 2px; }
}

/* ---------- Feature index: a pinned stage ----------
   Desktop: the chapter pins for ~2 viewports; every block takes its turn as
   the lit one and the phone swaps to its screen (scripts/home.js drives both
   from the stage's scroll travel, and slides the list so the lit block stays
   mid-pane). Hover still previews a block while the pointer is on the list.
   Below 901px there is no phone and no pin: the list simply reads. */
.index-chapter { background: var(--bg); }
.index-chapter .stage-pin { align-items: stretch; }
.index-stage { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 64px; width: 100%; height: 100%; padding-top: 88px; padding-bottom: 32px; align-items: stretch; }
.index-copy { display: flex; flex-direction: column; min-height: 0; }
/* The pane clips the sliding list; the mask fades the blocks entering and
   leaving so the cut edges never read as a bug. */
.index-pane { flex: 1; min-height: 0; margin-top: 32px; overflow: hidden; -webkit-mask-image: linear-gradient(transparent, #000 10%, #000 90%, transparent); mask-image: linear-gradient(transparent, #000 10%, #000 90%, transparent); }
.index { list-style: none; margin: 0; padding: 8px 4px; counter-reset: idx; display: grid; gap: 12px; transform: translateY(var(--y, 0px)); transition: transform 260ms var(--ease-out); will-change: transform; }
.index li { position: relative; display: grid; grid-template-columns: 32px 1fr; gap: 12px; padding: 16px; border: 1px solid var(--line); border-radius: 12px; background: transparent; cursor: default; transition: background 180ms var(--ease-out), border-color 180ms var(--ease-out), box-shadow 180ms var(--ease-out); }
.index li::before { counter-increment: idx; content: counter(idx, decimal-leading-zero); font-size: var(--fs-label); font-variant-numeric: tabular-nums; color: var(--ink-faint); padding-top: 4px; }
/* The ::before counter is itself a grid item, so without an explicit column
   auto-placement drops the description into the 34px number track. */
.index li b { grid-column: 2; display: block; font-size: 17px; letter-spacing: -0.01em; margin-bottom: 4px; }
.index li span { grid-column: 2; display: block; font-size: var(--fs-small); line-height: 1.5; color: var(--ink-soft); }
@media (min-width: 901px) {
  .index li:hover { background: var(--bg-warm); }
  .index li.is-active { background: var(--brand-soft); border-color: var(--brand-soft); box-shadow: none; }
  .index li.is-active::before { color: var(--brand-deep); }
}
.index-viewer { display: flex; justify-content: center; align-items: center; }
.index-viewer .device { --device-w: min(280px, 34vh); }
@media (max-width: 900px) {
  .index-chapter[data-stage] { height: auto; }
  .index-chapter .stage-pin { position: static; height: auto; overflow: visible; display: block; }
  /* Vertical rhythm only: the container's side padding must survive. */
  .index-stage { grid-template-columns: 1fr; gap: 0; height: auto; padding-top: var(--section-y, clamp(64px, 7vw, 96px)); padding-bottom: var(--section-y, clamp(64px, 7vw, 96px)); }
  .index-pane { overflow: visible; -webkit-mask-image: none; mask-image: none; }
  .index { transform: none; transition: none; }
  .index-viewer { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .index-stage { height: auto; padding-top: var(--section-y, clamp(64px, 7vw, 96px)); padding-bottom: var(--section-y, clamp(64px, 7vw, 96px)); }
  .index-pane { overflow: visible; -webkit-mask-image: none; mask-image: none; }
  .index { transform: none; transition: none; }
  .index-viewer { align-items: flex-start; position: sticky; top: 96px; align-self: start; }
}

/* Browser chapter */
.browser-grid { display: grid; grid-template-columns: 320px minmax(0, 1fr); gap: 48px; align-items: center; margin-top: 48px; }
.browser-visual { overflow: visible; }
@media (max-width: 900px) { .browser-grid { grid-template-columns: 1fr; gap: 32px; } }

/* Install */
.install { border-top: 1px solid var(--line); }
.install-grid { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 48px; align-items: center; }
.install .h2 { font-family: var(--display); font-size: var(--fs-d2); font-weight: 600; line-height: 1.08; letter-spacing: -0.022em; margin: 0 0 16px; }
.install .lead { font-size: var(--fs-lead); color: var(--ink-soft); max-width: 560px; margin: 0 0 24px; }
/* Google's official badge PNG, which carries ~10px of transparent padding of
   its own: the negative margin cancels that so the visible pill aligns with the
   text above it, and 80px renders the pill at ~53px. Never recolour or redraw. */
.play-badge { display: inline-block; margin: 0 -13px; }
.play-badge:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }
.play-badge img { height: 80px; width: auto; display: block; }
.install-qr { margin: 0; text-align: center; }
.install-qr img { display: block; margin: 0 auto 12px; padding: 12px; background: #fff; border: 1px solid var(--line); border-radius: 12px; }
.install-qr figcaption { font-size: 13px; color: var(--ink-faint); max-width: 180px; margin: 0 auto; }
@media (max-width: 720px) { .install-grid { grid-template-columns: 1fr; } .install-qr { display: none; } }

/* Who this is for */
.who-grid { display: grid; grid-template-columns: 1fr 1fr; margin-top: 40px; border-top: 1px solid var(--line); }
.who-grid > div { padding: 24px 32px 24px 0; border-bottom: 1px solid var(--line); }
.who-grid > div:nth-child(odd) { border-right: 1px solid var(--line); }
.who-grid > div:nth-child(even) { padding-left: 32px; }
.who-grid b { display: block; font-size: 17px; margin-bottom: 8px; }
.who-grid p { margin: 0; font-size: var(--fs-small); line-height: 1.55; color: var(--ink-soft); }
@media (max-width: 720px) { .who-grid { grid-template-columns: 1fr; } .who-grid > div { padding: 16px 0; } .who-grid > div:nth-child(odd) { border-right: 0; } .who-grid > div:nth-child(even) { padding-left: 0; } }

/* Close (talk to us) */
.contact-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); gap: 56px; align-items: start; }
.contact-perks { list-style: none; margin: 32px 0 0; padding: 0; display: grid; gap: 12px; font-size: var(--fs-small); color: rgba(255,255,255,0.8); }
.contact-perks li { display: flex; gap: 12px; align-items: center; }
.contact-perks svg { width: 16px; height: 16px; stroke: #fff; fill: none; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; flex: none; }
@media (max-width: 900px) { .contact-grid { grid-template-columns: 1fr; gap: 32px; } }
