/*
 * Cashrou, the marketing site.
 *
 * Written from the app's own design tokens rather than from a purchased template. The values below
 * are copied from cashrou-web/src/theme.js -- the same file the browser app reads -- so a card here
 * and a card in the app are the same shape, the same white, and the same distance off the page.
 *
 * Why this file exists at all. The site was built by downloading a Webflow template and substituting
 * our words into its markup, which worked until it did not: the template's palette is a different
 * green and a different navy from the brand (#1AD079 against #10C66E, #091E42 against #031537), so
 * the one surface not wearing Cashrou's colours was Cashrou's website. The logo Jose supplied is
 * drawn in #031537 and #10C66E. It had been sitting on a page painted in somebody else's.
 *
 * The second reason is mechanical. Every mobile fault found this week came from the template
 * fighting content it was not drawn for -- a sign-up field collapsing to twenty pixels, a form grid
 * that kept two columns at 390px because its named areas declared them, a hero image squashed square
 * by a max-height meant for a landscape card, decorations escaping to the page corners, and an
 * interaction engine whose animations never fired. None of that is content we can fix by rewording.
 *
 * MOBILE FIRST, which is the other half of the fix. The template is written desktop-down with phone
 * sizes bolted on in trailing media queries, and that ordering is why a phone kept inheriting a
 * desktop value nobody meant it to have. Everything here starts at the narrow end and widens.
 */

/* ---------------------------------------------------------------- tokens */

:root {
  /* Surfaces. Straight from theme.js LIGHT. */
  --page: #eaf1ed;
  --card: #ffffff;
  --fill: #f6faf8;
  --track: #e4ede8;
  --mint: #e9fff5;
  --line: #d2e1d8;

  /* Ink. Never pure black -- the app does not use it and neither does this. */
  --ink: #071739;
  --muted: #5e6670;
  /* Measured, not chosen by eye. #98a2b3 came out at 2.24:1 on the page background and 2.58:1 on
     white, against the 4.5:1 that text under 18.66px has to clear, and it carries real text: the
     reading times, the group counts, the placeholder note and the quote attributions. This is the
     lightest step down the same hue that clears 4.5 on every background the site uses. */
  --faint: #636d7e;

  /* Brand. The fills are exact; the -text variants are the app's accessible pair, because green on
     a light surface measures about 3:1 as ink and passes only when taken deeper. */
  --green: #10c66e;
  --green-deep: #08ad5d;
  --green-text: #006b3c;
  --navy: #031537;
  --navy-2: #071f4c;
  --lime: #d9ff76;
  --red-text: #c22630;

  /* Shape. theme.js radius: card 20, hero 28, tile 14, chip 999. */
  --r-hero: 28px;
  --r-card: 20px;
  --r-tile: 14px;
  --r-chip: 999px;

  /* Depth. theme.js shadow. */
  --sh-card: 0 1px 2px rgba(7, 23, 57, .04), 0 1px 3px rgba(7, 23, 57, .06);
  --sh-lift: 0 2px 8px rgba(7, 23, 57, .08), 0 1px 3px rgba(7, 23, 57, .06);
  --sh-hero: 0 8px 24px rgba(3, 21, 55, .18);

  /* Rhythm. One scale, used everywhere, so spacing is a choice from a list rather than a number
     somebody typed. */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;
  --s-8: 64px;
  --s-9: 96px;

  --band: 56px;          /* space above and below a section, phone */
  --gutter: 20px;        /* page margin, phone */
  --measure: 68ch;       /* longest comfortable line of body text */

  --font: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif;
}

@media (min-width: 768px) {
  :root { --band: 80px; --gutter: 32px; }
}
@media (min-width: 1100px) {
  :root { --band: 112px; --gutter: 40px; }
}

/* ---------------------------------------------------------------- base */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font-family: var(--font);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, svg { display: block; max-width: 100%; }
img { height: auto; }

a { color: var(--green-text); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { text-decoration-thickness: 2px; }

:focus-visible { outline: 2px solid var(--green-text); outline-offset: 3px; border-radius: 4px; }

.tabular { font-variant-numeric: tabular-nums; }

/* Type. One scale, largest first, and the page title is always the largest thing on the page --
   which the template got wrong on a phone, where its h1 came out smaller than its own h2s. */
/* text-wrap: balance evens the lines of a heading instead of filling the first and orphaning the
   last, which is what left "have." alone on its own line under "Everything you need to plan the
   money you already". Browsers cap it at a few lines, which is exactly the range headings live in.
   pretty does the equivalent for body copy: it only prevents the single-word last line. */
h1, h2, h3, h4 { margin: 0; line-height: 1.15; letter-spacing: -.02em; font-weight: 700; text-wrap: balance; }
p, li, figcaption { text-wrap: pretty; }

/* Four steps, and each one is used at exactly one level. The old site's phone layout had its h1 and
   its h2 land on the same size because both were reading the same class, and a page whose title is
   the same size as its sections has no title. */
.t-display { font-size: clamp(34px, 8vw, 60px); }              /* the homepage, once */
.t-page    { font-size: clamp(29px, 6.4vw, 44px); }            /* an inner page's h1 */
.t-title   { font-size: clamp(26px, 5.6vw, 38px); }            /* a band that leads a page */
.t-section { font-size: clamp(23px, 4.8vw, 31px); letter-spacing: -.015em; }  /* an h2 inside one */
.t-card    { font-size: 19px; line-height: 1.3; letter-spacing: -.01em; }
.t-lede    { font-size: clamp(17px, 2.4vw, 20px); line-height: 1.6; color: var(--muted); font-weight: 400; letter-spacing: 0; }
.t-body    { font-size: 17px; line-height: 1.65; color: var(--muted); }
.t-small   { font-size: 14.5px; line-height: 1.55; color: var(--muted); }
.t-eyebrow {
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--green-text);
}

p { margin: 0 0 var(--s-4); }
p:last-child { margin-bottom: 0; }

/* ---------------------------------------------------------------- layout */

.wrap {
  width: 100%;
  max-width: 1180px;
  margin: 0 auto;
  padding-inline: var(--gutter);
}
.narrow { max-width: 760px; }

section { padding-block: var(--band); }
/* A wash rather than a flat fill, so the edge where one band meets the next is a change of light
   instead of a hard line across the page. The two stops are two steps of the same green-grey. */
section.on-page { background: linear-gradient(180deg, var(--page) 0%, #eef4f0 100%); }
section.on-card { background: var(--card); }
/* Two page-toned bands in a row would show a seam where the gradient restarts. */
section.on-page + section.on-page { background: #eef4f0; }

/* A stack with one gap, rather than margins that have to be cancelled. */
/* Grid children may not refuse to shrink; min-width defaults to auto, which is how a long word or a
   generous max-width widens the whole page. */
.stack, .stack-lg, .grid, .hero, .split { min-width: 0; }
.stack > *, .stack-lg > *, .grid > *, .hero > *, .split > * { min-width: 0; }
.stack { display: grid; gap: var(--s-4); }
.stack-lg { display: grid; gap: var(--s-6); }

.head { display: grid; gap: var(--s-3); max-width: 740px; }
.head.center { margin-inline: auto; text-align: center; }

/* ---------------------------------------------------------------- controls */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 52px;
  padding: 0 26px;
  border: 1px solid transparent;
  border-radius: var(--r-chip);
  font: inherit;
  font-size: 16px;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: background-color .18s, border-color .18s, color .18s, transform .18s;
}
.btn:active { transform: translateY(1px); }

.btn-primary { background: var(--green); color: var(--navy); }
.btn-primary:hover { background: var(--green-deep); }

.btn-ghost { background: transparent; color: var(--ink); border-color: var(--line); }
.btn-ghost:hover { background: var(--fill); border-color: var(--muted); }

/* On the navy bands, where a bordered button has to hold its own against the dark. */
.navy .btn-ghost { color: #fff; border-color: rgba(255, 255, 255, .28); }
.navy .btn-ghost:hover { background: rgba(255, 255, 255, .08); border-color: rgba(255, 255, 255, .5); }

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 5px 12px;
  border-radius: var(--r-chip);
  background: var(--mint);
  color: var(--green-text);
  font-size: 13px;
  font-weight: 600;
}

/* ---------------------------------------------------------------- surfaces */

.card {
  background: var(--card);
  border-radius: var(--r-card);
  box-shadow: var(--sh-card);
  padding: var(--s-5);
}
.card-lift { box-shadow: var(--sh-lift); }

.navy {
  background: var(--navy);
  color: #fff;
  border-radius: var(--r-hero);
  padding: var(--s-7) var(--s-5);
}
.navy .t-lede,
.navy .t-body,
.navy .t-small { color: rgba(255, 255, 255, .76); }
.navy .t-eyebrow { color: var(--lime); }
/* :not(.btn) matters. `.navy a` is a class plus a type selector, so it outranks `.btn-primary`,
   and without the exclusion the green button on the dark band gets green text on green. */
.navy a:not(.btn):not(.store-badge) { color: var(--green); }

/* ---------------------------------------------------------------- grids
 *
 * auto-fit with a minimum, so the column count follows the space rather than a breakpoint. That is
 * what stops a three-column grid orphaning its last card at one width and crushing it at another.
 */
.grid { display: grid; gap: var(--s-4); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); }

/* ---------------------------------------------------------------- device
 *
 * A screenshot is a flat png with square corners and its own status bar drawn inside it, so the one
 * thing needed to complete a phone is a body around it.
 */
/* figure.shot img as well as .device: markdown writes ![alt](src) and the renderer wraps it in a
   figure, but it cannot know to add a class. Without this the screenshots inside five money tips
   rendered as raw full-width images with no phone around them, which is how they had always been
   and I had not opened one of those five posts. */
.device,
figure.shot img {
  border-radius: 34px;
  box-shadow: 0 0 0 8px var(--navy), 0 0 0 9px rgba(255, 255, 255, .06), var(--sh-hero);
  width: 100%;
  max-width: 300px;
}
@media (min-width: 768px) { .device, figure.shot img { max-width: 330px; border-radius: 40px; } }

/* ---------------------------------------------------------------- header */

.site-head {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(234, 241, 237, .88);
  backdrop-filter: saturate(140%) blur(12px);
  border-bottom: 1px solid transparent;
}
/* Nothing but a hairline once the page moves under it. A bar that is already outlined at rest has
   spent the only signal it has to say "this is floating over your content". */
.site-head.is-stuck { border-bottom-color: var(--line); }

.head-row {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  min-height: 68px;
}
.brand { display: inline-flex; align-items: center; padding-block: 7px; }
.brand img { height: 30px; width: auto; aspect-ratio: 751.56 / 199.17; }

/* The nav sits in the middle of the bar rather than being pushed against the buttons. auto margins
   rather than justify-content: space-between, which centres nothing and only spreads three
   children to the edges. */
.nav { display: none; margin-inline: auto; }
.nav ul { display: flex; align-items: center; gap: 2px; list-style: none; margin: 0; padding: 0; }

/* Each item is a pill, not a word. It gives the hover somewhere to land, makes the current page a
   solid object instead of a colour change you have to already know to look for, and it means the
   pointer target is the whole item rather than the glyphs. */
.nav a {
  display: inline-flex;
  align-items: center;
  height: 38px;
  padding: 0 13px;
  border-radius: 999px;
  color: var(--ink);
  text-decoration: none;
  font-size: 15.5px;
  font-weight: 500;
  white-space: nowrap;
  transition: background-color .16s ease, color .16s ease;
}
.nav a:hover { background: rgba(255, 255, 255, .72); color: var(--green-text); }
.nav a[aria-current="page"] { background: var(--card); color: var(--green-text); font-weight: 600; }

/* The right-hand side is a text link and a button rather than one heavy green lozenge sitting on
   its own. The button is also smaller than a body button: 52px is a call to action in a section,
   and in a 68px bar it is the bar. */
.head-right { display: none; align-items: center; gap: var(--s-4); }
.head-signin {
  display: inline-flex;
  align-items: center;
  height: 38px;
  padding: 0 4px;
  color: var(--ink);
  text-decoration: none;
  font-size: 15.5px;
  font-weight: 500;
  white-space: nowrap;
}
.head-signin:hover { color: var(--green-text); }
.head-cta { min-height: 42px; padding: 0 20px; font-size: 15px; }

.burger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin-left: auto;
  border: 1px solid var(--line);
  border-radius: var(--r-tile);
  background: var(--card);
  cursor: pointer;
}
.burger:hover { border-color: var(--muted); }
.burger span { position: relative; display: block; width: 18px; height: 2px; background: var(--ink); border-radius: 2px; }
.burger span::before,
.burger span::after { content: ""; position: absolute; left: 0; width: 18px; height: 2px; background: var(--ink); border-radius: 2px; }
.burger span::before { top: -6px; }
.burger span::after { top: 6px; }

/* The close button in the panel is the same object with its bars crossed, so the thing you tap to
   shut the menu is visibly the thing you tapped to open it. */
.burger.is-x span { background: transparent; }
.burger.is-x span::before { top: 0; transform: rotate(45deg); }
.burger.is-x span::after { top: 0; transform: rotate(-45deg); }

@media (min-width: 900px) {
  .nav { display: block; }
  .head-right { display: flex; }
  .burger { display: none; }
}

/* ---------------------------------------------------------------- mobile menu */

.nav-panel {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  background: var(--page);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.nav-panel[hidden] { display: none; }
.nav-panel:focus { outline: none; }   /* it takes focus to move the reader into the menu, not to be a control */
/* Opened by [hidden] rather than a class, so the panel is hidden from assistive technology and
   removed from the tab order in the same move that takes it off the screen. */
.menu-open, .menu-open body { overflow: hidden; }

.panel-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 68px;
  border-bottom: 1px solid var(--line);
}
.panel-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding-block: var(--s-5) calc(var(--s-6) + env(safe-area-inset-bottom));
}
.panel-nav, .panel-more { list-style: none; margin: 0; padding: 0; }

/* Big enough to hit without looking. 44px is the floor for a target you are aiming at; a menu is
   read top to bottom with a thumb, and the extra height is what makes that comfortable. */
.panel-nav a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 56px;
  color: var(--ink);
  text-decoration: none;
  font-size: 21px;
  font-weight: 600;
  letter-spacing: -.015em;
  border-bottom: 1px solid var(--line);
}
.panel-nav a[aria-current="page"] { color: var(--green-text); }
/* A dot on the page you are already on, because on a phone there is no other context to tell you. */
.panel-nav a[aria-current="page"]::after {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--green);
}

.panel-more { margin-top: var(--s-5); display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-5); }
.panel-more a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--muted);
  text-decoration: none;
  font-size: 16px;
  font-weight: 500;
}
.panel-more a[aria-current="page"] { color: var(--green-text); }

/* margin-top: auto pins the actions to the bottom of a short menu and lets them sit under the list
   on a small screen where everything scrolls. */
.panel-actions { margin-top: auto; padding-top: var(--s-6); display: grid; gap: var(--s-3); }
.panel-note { margin-top: var(--s-4); text-align: center; font-size: 14px; color: var(--muted); }

@media (min-width: 900px) { .nav-panel { display: none !important; } }

/* ---------------------------------------------------------------- footer */

.site-foot { background: var(--navy); color: #fff; padding-block: var(--s-7) var(--s-6); }
.site-foot a { color: rgba(255, 255, 255, .8); text-decoration: none; }
.site-foot a:hover { color: #fff; text-decoration: underline; }
.foot-grid { display: grid; gap: var(--s-6); }
/* The count comes from the generator, because the footer had repeat(3) baked in and kept an empty
   fourth column when the Compare group was removed, leaving Legal stranded mid-page.
   Not auto-fit: repeat(auto-fit, …) is invalid in the same template as an fr track, so the whole
   declaration is dropped and the footer collapses to a single column. It did exactly that. */
@media (min-width: 700px) {
  .foot-grid { grid-template-columns: 1.4fr repeat(3, 1fr); gap: var(--s-6) var(--s-5); }
  .foot-grid.cols-2 { grid-template-columns: 1.4fr repeat(2, 1fr); }
  .foot-grid.cols-4 { grid-template-columns: 1.4fr repeat(4, 1fr); }
}
.foot-col h2 { font-size: 13px; letter-spacing: .06em; text-transform: uppercase; color: rgba(255, 255, 255, .55); margin-bottom: var(--s-3); }
.foot-col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; font-size: 15px; }
/* Inline text is an 18px tap target. The padding is what makes each one thumb-sized without
   opening the list out. */
/* A mouse does not need 44px, and paying for it here costs real space: at six items the Legal
   column ran 130px past the others and the footer read as three lists and a hole.
   any-pointer, not pointer. `pointer` describes the PRIMARY input, and a touchscreen laptop calls
   its mouse the primary one, so `pointer: fine` would have quietly taken the thumb-sized rows away
   from a device that can still be touched. `any-pointer: coarse` is true if a coarse pointer is
   available at all, which is the side to err on. */
.foot-col li a { display: inline-block; padding-block: 6px; }
@media (any-pointer: coarse) { .foot-col li a { padding-block: 13px; } }  /* 44px with the 18px line */
.foot-brand img { height: 28px; width: auto; }
.foot-note { color: rgba(255, 255, 255, .55); font-size: 14px; margin-top: var(--s-3); max-width: 34ch; }
.foot-mail {
  display: inline-block;
  margin-top: var(--s-3);
  padding-block: 11px;
  font-size: 15px;
  color: var(--lime) !important;   /* beats `.navy a`, which is a class plus a type selector */
}

/* Two paragraphs rather than one long line: the copyright is a fact and the disclaimer is a legal
   notice, and running them together made a 200-character sentence nobody finishes. */
.foot-bottom {
  margin-top: var(--s-7);
  padding-top: var(--s-4);
  border-top: 1px solid rgba(255, 255, 255, .12);
  font-size: 13.5px;
  color: rgba(255, 255, 255, .5);
  display: grid;
  gap: var(--s-2);
}
.foot-bottom p { margin: 0; }
@media (min-width: 860px) {
  .foot-bottom { grid-template-columns: auto minmax(0, 62ch); justify-content: space-between; gap: var(--s-6); }
  .foot-bottom p:last-child { text-align: right; }
}

/* ---------------------------------------------------------------- accordion
 *
 * <details>, so the browser owns the open and closed states. Nothing here hides anything that only
 * a script can bring back.
 */
/* One card with hairline rows, rather than eight cards floating two pixels apart. Eight separate
   surfaces read as eight unrelated things; a list reads as a list. */
.faq {
  display: block;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  box-shadow: var(--sh-card);
  padding-inline: var(--s-5);
  overflow: hidden;
}
.faq details { display: block; border-bottom: 1px solid var(--line); }
.faq details:last-child { border-bottom: 0; }
.faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding-block: var(--s-5);
  font-size: 17px;
  font-weight: 600;
  line-height: 1.35;
  cursor: pointer;
  list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary:hover { color: var(--green-text); }

/* The chevron gets a target of its own, so the affordance reads as a control rather than as a
   decoration sitting at the end of a line. */
.faq summary::after {
  content: "";
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--fill) no-repeat center;
  /* A gradient's stripe runs perpendicular to its axis, so "to bottom left" draws \ and
     "to bottom right" draws /. Closed is \/ pointing down; open rotates it to /\. */
  background-image:
    linear-gradient(to bottom left, transparent 45%, var(--muted) 45%, var(--muted) 55%, transparent 55%),
    linear-gradient(to bottom right, transparent 45%, var(--muted) 45%, var(--muted) 55%, transparent 55%);
  background-size: 9px 9px, 9px 9px;
  background-position: calc(50% - 4px) calc(50% - 1px), calc(50% + 4px) calc(50% - 1px);
  transition: transform .22s, background-color .18s;
}
.faq summary:hover::after { background-color: var(--mint); }
.faq details[open] summary::after { transform: rotate(180deg); }
.faq details[open] summary { padding-bottom: var(--s-3); }
.faq details > :not(summary) { padding-bottom: var(--s-5); }
.faq p { color: var(--muted); font-size: 16px; max-width: 64ch; }

/* The questions on one side, the answers on the other, so the block has a shape instead of being a
   narrow column adrift in a wide band. */
.faq-layout { display: grid; gap: var(--s-6); }
@media (min-width: 940px) {
  .faq-layout { grid-template-columns: .42fr .58fr; gap: var(--s-8); align-items: start; }
  .faq-aside { position: sticky; top: calc(var(--chrome) + var(--s-2)); }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ---------------------------------------------------------------- homepage pieces */

.hero { display: grid; gap: var(--s-7); align-items: center; }
.hero-copy { max-width: 640px; }
.hero-btns { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.hero-btns .btn { flex: 1 1 auto; }
.hero-shot { display: flex; justify-content: center; }
@media (min-width: 900px) {
  .hero { grid-template-columns: 1.05fr .95fr; gap: var(--s-8); }
  .hero-btns .btn { flex: 0 0 auto; }
}

/* ---------------------------------------------------------------- the hero, composed
 *
 * A screenshot alone on a flat field reads as a picture of an app. What makes a product page feel
 * like a product is depth: the light behind the device, and one figure lifted out of the interface
 * at a size you can read from across the room. Both of the lifted pieces below are real content
 * from the screenshot they sit on, shown larger. Nothing here claims anything the app does not.
 */

.hero-band { position: relative; overflow: hidden; }
.hero-band::before {
  content: "";
  position: absolute;
  z-index: 0;
  top: -18%;
  right: -8%;
  width: 62%;
  padding-bottom: 62%;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%, rgba(16, 198, 110, .16), rgba(16, 198, 110, 0) 68%);
  pointer-events: none;
}
.hero-band > .wrap { position: relative; z-index: 1; }

.hero-stage { position: relative; display: inline-block; }
.hero-stage .device { position: relative; z-index: 1; }

/* The figure the whole app exists to produce, at a size the screenshot cannot give it. */
.pull {
  position: absolute;
  z-index: 2;
  border-radius: var(--r-card);
  box-shadow: 0 18px 40px rgba(3, 21, 55, .22), 0 2px 6px rgba(3, 21, 55, .12);
}
.pull-left {
  left: -16%;
  bottom: 6%;
  padding: var(--s-4) var(--s-5);
  background: var(--navy);
  color: #fff;
}
.pull-left .pull-label { font-size: 12px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--lime); }
.pull-left .pull-figure { font-size: 38px; font-weight: 700; letter-spacing: -.03em; line-height: 1.05; margin-top: 2px; }
.pull-left .pull-note { font-size: 12.5px; color: rgba(255, 255, 255, .62); margin-top: 3px; }

.pull-right {
  right: -12%;
  top: 16%;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px var(--s-4);
  background: var(--card);
  color: var(--ink);
  font-size: 14.5px;
  font-weight: 600;
}
/* Navy on green, not white on green. White on this green measures 2.25:1, which at 12px is hard
   to read even though the element is decorative and aria-hidden. Navy on green is the pair the
   buttons already use. */
.pull-right .tick {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--green);
  color: var(--navy);
  font-size: 12px;
  font-weight: 700;
}
.pull-right small { display: block; font-size: 12.5px; font-weight: 400; color: var(--faint); }

/* Below 1100px the phone has no room either side, and a card hanging off it would be clipped by
   the band. The screenshot already contains both figures; the pull-outs are emphasis, not content,
   so they simply go. */
@media (max-width: 1099px) { .pull { display: none; } }

/* ================================================================ how it works
 *
 * The page is one sequence, so it is drawn as one: seven steps numbered straight through, two
 * phase headers saying which of them happen once and which happen every payday, and a rail down
 * the left so the eye can see it is a list rather than a pile of cards.
 */

.hiw-head { display: grid; gap: var(--s-3); max-width: 720px; margin-bottom: var(--s-7); }
.hiw-head .hero-btns { margin-top: var(--s-2); }

.phase { display: grid; grid-template-columns: auto 1fr; gap: var(--s-4); align-items: start; }
.phase > div { display: grid; gap: var(--s-2); max-width: 60ch; }
.phase-n {
  align-self: start;
  padding: 6px 14px;
  border-radius: var(--r-chip);
  background: var(--navy);
  color: #fff;
  font-size: 12.5px;
  font-weight: 650;
  letter-spacing: .04em;
  white-space: nowrap;
}

/* The rail. It stops at the last step's number rather than running past it, which is what the
   ::after mask is for. */
.spine { position: relative; list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-7); }
@media (min-width: 900px) { .spine-lg { gap: var(--s-9); } }
.spine::before {
  content: "";
  position: absolute;
  left: 21px;
  top: 12px;
  bottom: 12px;
  width: 2px;
  background: linear-gradient(180deg, var(--green) 0%, var(--line) 12%, var(--line) 88%, transparent 100%);
}

/* start, not center: the copy belongs beside its own number. Centred against a phone six times
   its height, "Confirm your balances" sat 250px below the 04 it belongs to. */
.step { position: relative; display: grid; gap: var(--s-5); align-items: start; }
@media (min-width: 900px) {
  .step { grid-template-columns: 1fr .78fr; gap: var(--s-8); }
  .step.flip > :first-child { order: 2; }
  .step.no-shot { grid-template-columns: 1fr; }
}
.step-copy { display: grid; gap: var(--s-3); padding-left: 62px; align-content: start; }
.step-copy .t-body p + p { margin-top: var(--s-3); }

.step-num {
  position: absolute;
  left: 0;
  top: -4px;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--card);
  border: 2px solid var(--line);
  color: var(--green-text);
  font-size: 14px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.step:hover .step-num { border-color: var(--green); }

.step-shot { display: flex; justify-content: center; padding-left: 62px; }
@media (min-width: 900px) { .step-shot { padding-left: 0; } }
.step-shot .device { max-width: 270px; }

/* The one step whose subject is a number rather than a screen. */
.step-figure {
  padding: var(--s-6) var(--s-7);
  border-radius: var(--r-card);
  background: var(--navy);
  color: #fff;
  box-shadow: var(--sh-hero);
  text-align: center;
}
.step-figure .pull-label { font-size: 12px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--lime); }
.step-figure .pull-figure { font-size: clamp(40px, 7vw, 56px); font-weight: 700; letter-spacing: -.03em; line-height: 1.05; margin-top: 4px; }
.step-figure .pull-note { font-size: 13px; color: rgba(255, 255, 255, .62); margin-top: 6px; }

/* ---------------------------------------------------------------- month against paydays
 *
 * A diagram, not a screenshot, because the section is about a mismatch between two calendars and
 * there is no screen in the app that shows it.
 */
.cal-compare { display: grid; gap: var(--s-4); align-content: start; }
.cal-row { display: grid; gap: 6px; }
.cal-k { font-size: 13px; font-weight: 650; color: var(--muted); }
.cal-bar { display: flex; gap: 4px; height: 54px; }
.cal-bar span { font-size: 12px; font-weight: 600; }
.cal-bar.month > span {
  display: grid;
  place-items: center;
  width: 100%;
  border-radius: var(--r-tile);
  background: var(--fill);
  border: 1px dashed var(--line);
  color: var(--faint);
}
.cal-bar.rhythm i { flex: 0 0 var(--w); display: grid; place-items: center; border-radius: var(--r-tile); background: var(--mint); color: var(--green-text); font-style: normal; }
.cal-bar.rhythm i:nth-child(even) { background: #dff7ea; }
.cal-note { margin-top: var(--s-2); }

/* ---------------------------------------------------------------- the honest panel
 *
 * Light, not navy. The page already ends on a navy call to action and has a navy figure at step 06,
 * and a third dark panel directly above the CTA read as the same block twice. */
.dont {
  display: grid;
  gap: var(--s-6);
  padding: var(--s-6) var(--s-5);
  background: var(--fill);
  border: 1px solid var(--line);
  border-radius: var(--r-hero);
}
@media (min-width: 900px) { .dont { grid-template-columns: 1.15fr .85fr; gap: var(--s-8); align-items: center; padding: var(--s-8); } }
.marks.on-dark li { color: rgba(255, 255, 255, .9); }
.marks.on-dark li::before { background: rgba(255, 255, 255, .12); color: #fff; }

/* ================================================================ features
 *
 * Four pillars and an index that says there are four. The old page rendered eight H2s as siblings,
 * so the four that are the product and the four that are trimmings looked like the same thing.
 */

.pill-index { display: grid; gap: var(--s-3); margin-top: var(--s-7); }
@media (min-width: 700px) { .pill-index { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .pill-index { grid-template-columns: repeat(4, 1fr); gap: var(--s-4); } }
.pill-chip {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  padding: var(--s-4) var(--s-5);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-tile);
  color: var(--ink);
  text-decoration: none;
  font-size: 16px;
  font-weight: 650;
  line-height: 1.3;
  transition: box-shadow .18s, transform .18s, border-color .18s;
}
.pill-chip:hover { box-shadow: var(--sh-lift); transform: translateY(-2px); border-color: #bcd4c7; }
.pill-n { font-size: 12.5px; font-weight: 700; color: var(--green-text); font-variant-numeric: tabular-nums; }

.pillar { display: grid; gap: var(--s-6); align-items: center; }
@media (min-width: 900px) {
  .pillar { grid-template-columns: 1fr .72fr; gap: var(--s-8); }
  .pillar.flip > :first-child { order: 2; }
  .pillar.flip { grid-template-columns: .72fr 1fr; }
}
.pillar-copy { display: grid; gap: var(--s-3); align-content: center; }
.pillar-copy .points { margin-top: var(--s-2); }
.pillar-n {
  justify-self: start;
  padding: 4px 12px;
  border-radius: var(--r-chip);
  background: var(--mint);
  color: var(--green-text);
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: .06em;
  font-variant-numeric: tabular-nums;
}
.pillar-shot { display: flex; justify-content: center; }
/* A pill, not green bold text: green bold is what a link looks like on this site, and this is a
   note rather than something to click. */
.pillar-note {
  justify-self: start;
  padding: 5px 12px;
  border-radius: var(--r-chip);
  background: var(--mint);
  color: var(--green-text);
  font-weight: 600;
}

/* The bullets lead with bold and then continue the sentence, so they are emphasis rather than
   titles. Six of those as six shadowed cards is a lot of furniture around one sentence each: a
   hairline list carries them better and reads faster. */
.points { list-style: none; margin: 0; padding: 0; display: grid; }
.points li {
  position: relative;
  padding: var(--s-3) 0 var(--s-3) 26px;
  border-top: 1px solid var(--line);
  font-size: 16px;
  line-height: 1.55;
  color: var(--muted);
}
.points li:last-child { border-bottom: 1px solid var(--line); }
.points li strong { color: var(--ink); font-weight: 650; }
.points li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 21px;
  width: 12px;
  height: 6px;
  border-left: 2px solid var(--green);
  border-bottom: 2px solid var(--green);
  transform: rotate(-45deg);
}

/* The smaller things, three columns instead of three more full-width sections. */
.also { display: grid; gap: var(--s-6); }
@media (min-width: 820px) { .also { grid-template-columns: repeat(3, 1fr); gap: var(--s-7); } }
.also-col { display: grid; gap: var(--s-3); align-content: start; }
.also-col .points li { font-size: 15.5px; }

/* ================================================================ pricing */

/* Nine things in three columns rather than one long column, so the list reads as a set rather than
   as a scroll. */
.points.incl { gap: 0; }
@media (min-width: 760px) { .points.incl { grid-template-columns: 1fr 1fr; column-gap: var(--s-7); } }
@media (min-width: 1040px) { .points.incl { grid-template-columns: repeat(3, 1fr); } }

/* The argument for charging, which was folded shut. It gets the dark panel because it is the one
   place on the page making a case rather than listing a fact. */
.argue { display: grid; gap: var(--s-6); }
@media (min-width: 900px) { .argue { grid-template-columns: 1.15fr .85fr; gap: var(--s-8); align-items: center; padding: var(--s-8); } }
.argue-split { display: grid; gap: var(--s-4); margin: 0; }
.argue-split > div {
  padding: var(--s-4) var(--s-5);
  border-radius: var(--r-tile);
  background: rgba(255, 255, 255, .06);
  border: 1px solid rgba(255, 255, 255, .12);
}
.argue-split dt { font-size: 13px; font-weight: 650; letter-spacing: .04em; text-transform: uppercase; color: var(--lime); }
.argue-split dd { margin: 6px 0 0; font-size: 15.5px; line-height: 1.5; color: rgba(255, 255, 255, .82); }
/* The one that is us gets the accent, so the pair is a contrast rather than two grey boxes. */
.argue-split > div:last-child { background: rgba(16, 198, 110, .14); border-color: rgba(16, 198, 110, .38); }

/* "Seven days is about one payday" is the point, so the 1 is the figure, not the 7. */
.trial-figure { align-self: start; }
.trial-figure .pull-note { max-width: 24ch; margin-inline: auto; }

/* ================================================================ about
 *
 * A statement: one sentence at a size that says it is the point rather than a paragraph.
 */
.statement {
  margin: 0;
  font-size: clamp(24px, 3.6vw, 34px);
  font-weight: 650;
  line-height: 1.25;
  letter-spacing: -.02em;
  text-wrap: balance;
}
.navy .statement { color: #fff; }
.navy .statement::before { content: ""; display: block; width: 44px; height: 3px; background: var(--lime); margin-bottom: var(--s-4); }

.statement-lg {
  max-width: 22ch;
  margin-inline: auto;
  text-align: center;
  font-size: clamp(28px, 5vw, 44px);
  color: var(--ink);
}
.statement-lg::after {
  content: "";
  display: block;
  width: 56px;
  height: 3px;
  background: var(--green);
  margin: var(--s-5) auto 0;
}

/* Three sentences that should read as three sentences, not as a section fighting for width. */
.human {
  max-width: 62ch;
  margin-inline: auto;
  display: grid;
  gap: var(--s-4);
  justify-items: center;
  text-align: center;
  padding: var(--s-7) var(--s-5);
  background: var(--fill);
  border: 1px solid var(--line);
  border-radius: var(--r-hero);
}
.human p { font-size: clamp(18px, 2.4vw, 21px); line-height: 1.55; color: var(--ink); }

/* ================================================================ money tips */

/* A group heading with its count on the same line, so the reader can see how much is under each
   one before deciding to read any of it. */
.topic-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-4);
  padding-bottom: var(--s-4);
  border-bottom: 1px solid var(--line);
}
.topic-count {
  flex: 0 0 auto;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--faint);
}

/* The reading time under a post's lede. */
.head-note { color: var(--faint); }

/* ---------------------------------------------------------------- the fact strip
 *
 * The band under a hero is usually customer logos. There are none yet, and inventing them is the
 * one thing a page like this must not do, so it carries four things that are true instead.
 */
.facts {
  display: grid;
  gap: var(--s-4);
  padding: var(--s-5) 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
/* auto-fit here rather than repeat(4), which is safe because there is no fr track alongside it.
   Hard coded at four, a three-item strip kept an empty column and the last fact ran wide. */
@media (min-width: 720px) { .facts { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 0; } }
/* align-content: start, or the grid rows stretch to the tallest column and the descriptions
   begin at three different heights. Third time this exact fault has appeared: any .stack-like
   grid inside a stretched cell does it. */
.facts div { display: grid; gap: 2px; align-content: start; padding-inline: var(--s-5); }
@media (min-width: 720px) { .facts div + div { border-left: 1px solid var(--line); } }
.facts dt, .facts .fact-k { font-size: 15px; font-weight: 650; color: var(--ink); }
.facts dd, .facts .fact-v { margin: 0; font-size: 13.5px; color: var(--muted); line-height: 1.45; }

.split { display: grid; gap: var(--s-7); align-items: center; }
@media (min-width: 900px) { .split { grid-template-columns: 1fr .8fr; gap: var(--s-8); } }
/* A two-column ticked list inside the narrower half of a split wraps every item onto three lines.
   In a split it is one column. */
.split .checks { grid-template-columns: 1fr; }

/* Centring is right when the two halves are a similar height. Paragraphs beside a full-length
   phone are not: centred, three lines of text float 380px below the heading they belong to. */
.split.top { align-items: start; }

/* Alternate which side the screenshot is on, so a page of four such sections has a rhythm instead
   of being the same shape four times. Order rather than direction, which would also flip the text. */
@media (min-width: 900px) {
  .split.flip > :first-child { order: 2; }
  .split.flip { grid-template-columns: .8fr 1fr; }
}

/* A tall list of points beside one phone: the phone stays with you rather than scrolling away at
   the top of the section. */
@media (min-width: 900px) {
  .hero-shot.sticky { position: sticky; top: calc(var(--chrome) + var(--s-4)); }
}

.steps { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-5); }
.steps li { display: grid; grid-template-columns: 40px 1fr; gap: var(--s-4); align-items: start; }
.step-n {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--r-chip);
  background: var(--mint);
  color: var(--green-text);
  font-weight: 700;
  font-size: 16px;
}

.price-band { display: grid; gap: var(--s-7); }
@media (min-width: 900px) { .price-band { grid-template-columns: 1.2fr .8fr; align-items: center; padding: var(--s-8); } }

.figures { display: grid; gap: var(--s-5); margin: 0; }
@media (min-width: 520px) { .figures { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 900px) { .figures { grid-template-columns: 1fr; gap: var(--s-4); } }
.figures dt { font-size: 34px; font-weight: 700; letter-spacing: -.02em; line-height: 1; }
.figures dd { margin: 6px 0 0; font-size: 14.5px; color: rgba(255, 255, 255, .7); }

/* ---------------------------------------------------------------- the rest of the components
 *
 * Everything the generator emits has a rule here. A class that gets rendered and never styled is the
 * quiet way a design system rots: it looks fine on the page somebody checked and plain on the ones
 * they did not.
 */

/* Reachable by keyboard, invisible until it is wanted. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 12px 18px;
  background: var(--navy);
  color: #fff;
  border-radius: 0 0 var(--r-tile) 0;
  text-decoration: none;
}
.skip:focus { left: 0; }

/* The inner-page header. Shorter than the homepage's hero, because it carries a title and a line
   rather than a product shot and a decision. */
.page-head { padding-block: var(--s-7) var(--s-6); }
@media (min-width: 768px) { .page-head { padding-block: var(--s-8) var(--s-7); } }
.page-head .head { max-width: 760px; }
/* Less air under the hero than a normal band, because the fact strip below it brings its own rule
   and its own padding. The phone is taller than the copy beside it, so the extra was landing as a
   hole in the bottom-left corner. */
.hero-band { padding-block: var(--s-6) var(--s-7); }

.btn-block { width: 100%; }

/* Running text, kept to a measure. */
/* min() rather than a bare ch value: 68ch is about 544px, and as a max-width on a grid child --
   whose min-width is auto -- it let the column push a 390px phone out to 540. */
.prose { max-width: min(var(--measure), 100%); }
.prose > * + * { margin-top: var(--s-4); }
.prose p { color: var(--muted); }
.prose h2 { font-size: clamp(22px, 4.4vw, 28px); margin-top: var(--s-7); color: var(--ink); }
.prose h3 { font-size: 19px; margin-top: var(--s-6); color: var(--ink); }
.prose ul, .prose ol { margin: 0; padding-left: 1.15em; color: var(--muted); display: grid; gap: 8px; }
.prose li::marker { color: var(--green-text); }
.prose strong { color: var(--ink); font-weight: 650; }
.prose blockquote {
  margin: 0;
  padding: var(--s-4) var(--s-5);
  border-left: 3px solid var(--green);
  background: var(--fill);
  border-radius: 0 var(--r-tile) var(--r-tile) 0;
}
.prose code {
  font-size: .92em;
  background: var(--fill);
  padding: 2px 6px;
  border-radius: 6px;
}
.article h2:first-child, .article h3:first-child { margin-top: 0; }

/* A link that is the page's action rather than a mention inside a sentence, so it gets a target
   you can hit with a thumb. */
.link-cta a {
  display: inline-flex;
  align-items: center;
  min-height: 48px;
  padding: 0 22px;
  border: 1px solid var(--line);
  border-radius: var(--r-chip);
  background: var(--card);
  color: var(--green-text);
  font-weight: 600;
  text-decoration: none;
}
.link-cta a:hover { background: var(--fill); border-color: var(--muted); }

/* Legal, which is dense and numbered and wants a tighter rhythm than an article. */
.legal h2 { font-size: 20px; margin-top: var(--s-6); }
.legal p, .legal li { font-size: 16px; }

/* A ticked list, for "what is included" style sections. */
.checks { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-3); }
@media (min-width: 700px) { .checks { grid-template-columns: 1fr 1fr; gap: var(--s-3) var(--s-5); } }
.checks li {
  position: relative;
  padding: 14px 16px 14px 46px;
  background: var(--card);
  border-radius: var(--r-tile);
  box-shadow: var(--sh-card);
  font-size: 16px;
  line-height: 1.5;
}
.checks li::before {
  content: "";
  position: absolute;
  left: 16px;
  top: 18px;
  width: 16px;
  height: 9px;
  border-left: 2.5px solid var(--green-text);
  border-bottom: 2.5px solid var(--green-text);
  transform: rotate(-45deg);
}

/* A card that is itself a link, for the money-tips index. Eleven identical "Money tip" chips on a
   page titled Money tips told the reader nothing and pushed every title down; the affordance they
   were standing in for is a "read it" at the foot of the card. */
.tip-card { text-decoration: none; color: inherit; transition: box-shadow .18s, transform .18s; }
.tip-card:hover { box-shadow: var(--sh-lift); transform: translateY(-2px); }
.tip-card .t-card { color: var(--ink); }
.tip-more { padding-top: var(--s-2); font-size: 14.5px; font-weight: 600; color: var(--green-text); }
/* margin, not a space inside content(), which collapses. */
.tip-more::after { content: "\2192"; display: inline-block; margin-left: 6px; transition: margin-left .18s; }
.tip-card:hover .tip-more::after { margin-left: 10px; }
/* Title, summary, "read it". The summary row takes the slack, so "read it" sits on the same line
   across a row of cards whatever length the summaries are. */
.grid .tip-card { align-content: stretch; grid-template-rows: auto 1fr auto; }

/* An eyebrow that is also the way back to where this page is listed. */
a.t-eyebrow.back { display: inline-block; padding-block: 12px; text-decoration: none; }
a.t-eyebrow.back::before { content: "\2190"; display: inline-block; margin-right: 7px; }
a.t-eyebrow.back:hover { text-decoration: underline; }

/* The closing band. */
.closing { text-align: center; align-items: center; justify-items: center; }
.closing .t-lede { max-width: 52ch; }
.closing .hero-btns { justify-content: center; }
@media (min-width: 768px) { .closing { padding: var(--s-8); } }

/* A screenshot inside running text. */
figure.shot { margin: var(--s-6) 0; display: grid; gap: var(--s-3); justify-items: center; }
figure.shot figcaption { text-align: center; max-width: 46ch; }

/* A table wider than the column scrolls inside itself rather than widening the page. */
/* Wide enough to breathe, capped so a four-column table does not stretch to 1100px with an inch of
   air in every cell.
   The gradient is the affordance: a table narrower than its content scrolls inside itself, and on a
   phone there is no scrollbar to say so. The fade sits on the right edge and is attached to the
   scroller's own background, so it disappears once you reach the end. */
.scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border-radius: var(--r-tile);
  max-width: 860px;
  background:
    linear-gradient(to right, var(--card) 30%, rgba(255, 255, 255, 0)) left / 32px 100% no-repeat,
    linear-gradient(to left, var(--card) 30%, rgba(255, 255, 255, 0)) right / 32px 100% no-repeat,
    radial-gradient(farthest-side at 0 50%, rgba(7, 23, 57, .12), rgba(7, 23, 57, 0)) left / 14px 100% no-repeat,
    radial-gradient(farthest-side at 100% 50%, rgba(7, 23, 57, .12), rgba(7, 23, 57, 0)) right / 14px 100% no-repeat,
    var(--card);
  background-attachment: local, local, scroll, scroll, local;
}
/* min(520px, max-content), not a flat 520. The flat value forced a two-column table to 520px
   inside a 350px phone and made it scroll sideways when it would have fitted. This scrolls only
   when the table's own content is genuinely wider than the screen. */
.scroll table { border-collapse: collapse; width: 100%; min-width: min(520px, max-content); background: var(--card); }
.scroll th, .scroll td {
  text-align: left;
  padding: 14px 16px;
  border-bottom: 1px solid var(--line);
  font-size: 15.5px;
}
.scroll th { font-weight: 650; color: var(--ink); background: var(--fill); white-space: nowrap; }
.scroll td { color: var(--muted); }
.scroll tr:last-child td { border-bottom: 0; }

/* Cards that carry a number sit the badge above the title rather than beside it, so a two-line
   heading cannot push the number out of alignment with the ones either side of it. */
.card .step-n { margin-bottom: var(--s-1); }

/* Two fixes for the same symptom, cards in a row whose paragraphs all start at a different height.
   Cards in a grid stretch to the tallest, and .stack is a grid, so its rows were stretching too and
   the slack landed inside the title. align-content: start keeps every row at its content height and
   leaves the spare space at the bottom of the card where it belongs. Then the title reserves two
   lines whether it needs them or not, so a one-line and a two-line title still put their bodies on
   the same line. Measured: title heights went 59/49/68 to 49/49/49. */
.grid .card { align-content: start; }
/* Only once the cards are actually beside each other. Stacked on a phone there is nothing to line
   up with, and the reserved line is just a gap. */
/* :has, so the reserved line only exists where there is a paragraph below it to line up with. On
   /404 the cards are a title and a link, and the reserved second line was just a hole between
   them. */
@media (min-width: 640px) { .grid .card:has(.t-body) .t-card { min-height: calc(2 * 1.3em); } }

/* Several screenshots belonging to one section, side by side rather than stacked down the page. */
.shots { display: grid; gap: var(--s-6); justify-items: center; }
@media (min-width: 760px) { .shots { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); } }
.shots figure.shot { margin: 0; }


/* ---------------------------------------------------------------- plans */

.plans { display: grid; gap: var(--s-4); }
@media (min-width: 760px) { .plans { grid-template-columns: 1fr 1fr; gap: var(--s-5); } }

.plan {
  display: grid;
  gap: var(--s-3);
  align-content: start;
  padding: var(--s-6) var(--s-5);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  box-shadow: var(--sh-card);
}
.plan.featured { background: var(--navy); border-color: var(--navy); color: #fff; box-shadow: var(--sh-hero); }
.plan.featured .t-small { color: rgba(255, 255, 255, .68); }

/* The savings chip measures 31.4px, so that is the header height whether a card carries one or not.
   Without it the two prices sat three pixels apart and every row below inherited the offset. */
.plan-top { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); min-height: 31.4px; }
.plan-name { font-weight: 650; font-size: 16px; }
.plan-save { background: var(--green); color: var(--navy); }

.plan-price { display: flex; align-items: baseline; gap: 6px; margin: 0; font-size: 44px; font-weight: 700; letter-spacing: -.03em; }
.plan-per { font-size: 15px; font-weight: 500; color: var(--muted); letter-spacing: 0; }
.plan.featured .plan-per { color: rgba(255, 255, 255, .6); }

.plan-feats { list-style: none; margin: var(--s-2) 0 var(--s-3); padding: 0; display: grid; gap: 10px; font-size: 15.5px; }
.plan-feats li { position: relative; padding-left: 26px; }
.plan-feats li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 8px;
  width: 13px;
  height: 7px;
  border-left: 2px solid var(--green-text);
  border-bottom: 2px solid var(--green-text);
  transform: rotate(-45deg);
}
.plan.featured .plan-feats li::before { border-color: var(--green); }
.plans-note { margin-top: var(--s-5); text-align: center; max-width: 60ch; margin-inline: auto; }

/* A chip is a label, not a bar. Inside a grid stack it would otherwise stretch the full column. */
.chip { justify-self: start; width: fit-content; }

/* A centred section lede reads as a caption at two lines and as a poster at five, so it is capped
   rather than left to run the width of the column. */
.head.center .t-lede { max-width: 54ch; margin-inline: auto; }


/* ================================================================ the landing page
 *
 * The homepage is one long argument rather than a door to other pages, so it gets components the
 * inner pages do not have: an announcement bar, store badges, a before/after contrast, a payday
 * timeline, and an early-access form. Everything below is used on / and nowhere else.
 */

/* ---------------------------------------------------------------- announcement bar */

.announce {
  background: var(--navy);
  color: #fff;
  font-size: 14px;
  line-height: 1.4;
  text-align: center;
}
.announce .wrap { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 4px var(--s-2); padding-block: 10px; }
.announce a { display: inline-block; padding-block: 13px; color: var(--lime); text-decoration: underline; text-underline-offset: 3px; white-space: nowrap; }
.announce a:hover { color: #fff; }
.announce .dot { color: var(--green); }

/* ---------------------------------------------------------------- store badges
 *
 * These are placeholders, not Apple's and Google's artwork. They are <span>s rather than <a>s
 * because the apps are not published: a button that goes nowhere is worse than a label that says
 * so. Swap each for the official badge, wrapped in a real link, when the listings exist.
 */

.badges { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.store-badge {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 9px 18px 9px 14px;
  border-radius: var(--r-tile);
  background: var(--navy);
  color: #fff;
  text-decoration: none;
  border: 1px solid var(--navy);
}
.store-badge svg { flex: 0 0 auto; width: 22px; height: 22px; fill: currentColor; }
.store-badge span { display: block; }
.store-badge .sb-top { font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase; color: rgba(255, 255, 255, .62); }
.store-badge .sb-name { font-size: 15px; font-weight: 650; letter-spacing: -.01em; }
/* Filled, not outlined. An outlined rectangle with small caps inside it reads as a text input
   sitting next to the buttons, which is what these looked like. A store badge is a solid object. */
.store-badge[aria-disabled="true"] {
  background: var(--navy);
  color: #fff;
  border-color: var(--navy);
  cursor: default;
  opacity: .82;
}
.store-badge[aria-disabled="true"] .sb-top { color: var(--lime); }
.navy .store-badge[aria-disabled="true"],
.on-navy .store-badge[aria-disabled="true"] { color: #fff; border-color: rgba(255, 255, 255, .3); }
.navy .store-badge[aria-disabled="true"] .sb-top { color: var(--lime); }

/* ---------------------------------------------------------------- contrast, without / with */

.contrast { display: grid; gap: var(--s-4); align-items: stretch; }
@media (min-width: 820px) { .contrast { grid-template-columns: 1fr 1fr; gap: var(--s-5); } }

.col-was, .col-now { display: grid; gap: var(--s-4); align-content: start; padding: var(--s-6) var(--s-5); border-radius: var(--r-card); }
.col-was { background: var(--fill); border: 1px solid var(--line); }
.col-now { background: var(--navy); color: #fff; box-shadow: var(--sh-hero); }
.col-now .t-card { color: #fff; }

.marks { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-3); font-size: 16px; }
.marks li { position: relative; padding-left: 32px; line-height: 1.45; }
.marks li::before {
  position: absolute;
  left: 0;
  top: -1px;
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: var(--r-chip);
  font-size: 13px;
  font-weight: 700;
}
.marks.was li { color: var(--muted); }
.marks.was li::before { content: "\00d7"; background: #f0e4e4; color: var(--red-text); }
.marks.now li { color: rgba(255, 255, 255, .88); }
.marks.now li::before { content: "\2713"; background: var(--green); color: var(--navy); font-size: 12px; }

/* ---------------------------------------------------------------- three connected screens */

.trio { display: grid; gap: var(--s-6); }
@media (min-width: 860px) { .trio { grid-template-columns: repeat(3, 1fr); gap: var(--s-5); align-items: start; } }
.trio-item { display: grid; gap: var(--s-4); justify-items: center; text-align: center; }
.trio-item .device { max-width: 260px; }
.trio-item .stack { justify-items: center; }

/* The rail that connects them, drawn behind the phones on wide screens only. */
.trio-wrap { position: relative; }
@media (min-width: 860px) {
  .trio-wrap::before {
    content: "";
    position: absolute;
    left: 16%;
    right: 16%;
    top: 20px;
    height: 2px;
    background: repeating-linear-gradient(to right, var(--line) 0 8px, transparent 8px 16px);
  }
  .trio { position: relative; }
}

/* ---------------------------------------------------------------- feature cards */

.feats { display: grid; gap: var(--s-4); }
@media (min-width: 640px) { .feats { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1000px) { .feats { grid-template-columns: repeat(4, 1fr); } }

/* A hairline so the card has an edge on white as well as a shadow, and a lift on hover. Eight flat
   rectangles with no border disappear into a white band; the border is what makes them objects. */
.feat {
  display: grid;
  gap: var(--s-3);
  align-content: start;
  padding: var(--s-5);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  box-shadow: var(--sh-card);
  transition: box-shadow .2s, transform .2s, border-color .2s;
}
.feat:hover { box-shadow: var(--sh-lift); transform: translateY(-2px); border-color: #bcd4c7; }
.feat:hover .feat-ico { background: var(--green); color: #fff; }
.feat-ico { transition: background-color .2s, color .2s; }
.feat-ico {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border-radius: var(--r-tile);
  background: var(--mint);
  color: var(--green-text);
}
.feat-ico svg { width: 21px; height: 21px; stroke: currentColor; fill: none; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

/* ---------------------------------------------------------------- spreadsheet swap */

.swap { display: grid; gap: var(--s-5); align-items: center; }
@media (min-width: 900px) { .swap { grid-template-columns: 1fr auto 1fr; gap: var(--s-6); } }

.swap-arrow { display: grid; place-items: center; color: var(--faint); }
.swap-arrow svg { width: 34px; height: 34px; stroke: currentColor; fill: none; stroke-width: 1.6; }
@media (max-width: 899px) { .swap-arrow svg { transform: rotate(90deg); } }

/* A spreadsheet, drawn rather than photographed, because a screenshot of somebody's real budget is
   both a privacy problem and a licensing one. */
.sheet {
  border-radius: var(--r-tile);
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--card);
  box-shadow: var(--sh-card);
  font-size: 11px;
  line-height: 1.3;
  font-variant-numeric: tabular-nums;
}
.sheet-bar { display: flex; gap: 5px; padding: 9px 12px; background: var(--fill); border-bottom: 1px solid var(--line); }
.sheet-bar i { width: 9px; height: 9px; border-radius: 50%; background: var(--line); }
.sheet-grid { display: grid; grid-template-columns: 26px repeat(4, 1fr); }
.sheet-grid > * { padding: 7px 8px; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); }
.sheet-grid > .hd { background: var(--fill); color: var(--faint); font-weight: 600; text-align: center; }
.sheet-grid > .rn { background: var(--fill); color: var(--faint); text-align: center; }
.sheet-grid .fx { color: var(--green-text); }
.sheet-grid .err { color: var(--red-text); background: #fbeeee; font-weight: 600; }
.sheet-cap { padding: 8px 12px; color: var(--faint); font-size: 11px; border-top: 1px solid var(--line); }

.swap-app { display: flex; justify-content: center; }
.swap-app .device { max-width: 250px; }

/* ---------------------------------------------------------------- payday timeline */

.timeline { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; }
.timeline li {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--s-3);
  align-items: baseline;
  padding: var(--s-4) 0 var(--s-4) var(--s-6);
  border-left: 2px solid var(--line);
}
.timeline li::before {
  content: "";
  position: absolute;
  left: -7px;
  top: 22px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--card);
  border: 2px solid var(--line);
}
.timeline li.mark::before { background: var(--green); border-color: var(--green); }
/* The rows the screenshot shows already ticked, so the list and the phone agree. */
.timeline li.done::before { background: var(--green); border-color: var(--green); }
.timeline li.done .tl-what { color: var(--muted); }
.timeline li.done .tl-amt { color: var(--faint); text-decoration: line-through; }
.timeline li.end { border-left-color: transparent; }
.timeline li.end::before { background: var(--navy); border-color: var(--navy); }
.timeline .tl-what { font-weight: 600; font-size: 16px; }
.timeline .tl-when { display: block; font-size: 13.5px; font-weight: 400; color: var(--muted); }
.timeline .tl-amt { font-size: 16px; font-weight: 650; font-variant-numeric: tabular-nums; white-space: nowrap; }
.timeline li.mark .tl-amt { color: var(--green-text); }
.timeline .tl-safe { background: var(--mint); border-radius: var(--r-tile); }

/* ---------------------------------------------------------------- product preview */

.preview { display: grid; gap: var(--s-7); align-items: center; }
@media (min-width: 900px) { .preview { grid-template-columns: .9fr 1.1fr; gap: var(--s-8); } }
.readout { display: grid; gap: var(--s-3); }
.readout div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-4);
  padding: 13px var(--s-4);
  background: var(--card);
  border-radius: var(--r-tile);
  box-shadow: var(--sh-card);
  font-size: 15.5px;
}
.readout dt { color: var(--muted); }
.readout dd { margin: 0; font-weight: 650; font-variant-numeric: tabular-nums; }
.readout div.hi { background: var(--navy); }
.readout div.hi dt { color: rgba(255, 255, 255, .7); }
.readout div.hi dd { color: var(--lime); font-size: 19px; }

/* ---------------------------------------------------------------- testimonials
 *
 * Placeholder copy, and marked as such on the page as well as in the markup. Nothing here is a
 * real person, a real rating or a real result, and it must not be dressed up as one.
 */

.quotes { display: grid; gap: var(--s-4); }
@media (min-width: 820px) { .quotes { grid-template-columns: repeat(3, 1fr); gap: var(--s-5); } }

/* Three rows: the mark, the quote, the attribution. The quote row takes the slack, so however
   uneven the three quotes are, the three faces still line up across the row. */
.quote {
  position: relative;
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: var(--s-4);
  padding: var(--s-6) var(--s-5) var(--s-5);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  box-shadow: var(--sh-card);
}
.quote-mark { height: 22px; font-size: 46px; line-height: .78; font-weight: 700; color: var(--green); }
.quote blockquote { margin: 0; font-size: 17.5px; line-height: 1.55; color: var(--ink); }

.quote-who {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding-top: var(--s-4);
  border-top: 1px solid var(--line);
}
.quote-face {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--mint);
  color: var(--green-text);
}
.quote-face svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 1.8; }
/* Both are spans inside a span, so they need telling: without this the name and the pay schedule
   run together into "First namePaid biweekly". */
.quote-name, .quote-meta { display: block; }
.quote-name { font-size: 15px; font-weight: 650; line-height: 1.3; }
.quote-meta { font-size: 13.5px; color: var(--faint); line-height: 1.3; }

/* Labelled on the card itself and not only in a note underneath, so the label survives somebody
   screenshotting one card. */
.quote-tag {
  position: absolute;
  top: var(--s-4);
  right: var(--s-4);
  padding: 3px 9px;
  border-radius: var(--r-chip);
  background: var(--fill);
  border: 1px solid var(--line);
  color: var(--faint);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
}
.placeholder-note {
  margin: var(--s-5) auto 0;
  max-width: 60ch;
  font-size: 13.5px;
  color: var(--faint);
  text-align: center;
}

/* ---------------------------------------------------------------- trust block
 *
 * This was a .split, which centred a short left column against a tall right one and left a hole
 * down the middle with the disclaimer stranded under it. Centred head, one grid of points, one
 * row of buttons.
 */

.badge-lg {
  display: grid;
  place-items: center;
  justify-self: center;
  width: 56px;
  height: 56px;
  margin-bottom: var(--s-2);
  border-radius: 18px;
  background: var(--mint);
  color: var(--green-text);
}
.badge-lg svg { width: 26px; height: 26px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

.trust { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
@media (min-width: 620px) { .trust { grid-template-columns: 1fr 1fr; column-gap: var(--s-7); } }
@media (min-width: 980px) { .trust { grid-template-columns: repeat(3, 1fr); } }
.trust li {
  position: relative;
  padding: var(--s-4) 0 var(--s-4) 32px;
  border-top: 1px solid var(--line);
  font-size: 16px;
  line-height: 1.45;
  color: var(--ink);
}
.trust li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 24px;
  width: 15px;
  height: 8px;
  border-left: 2.5px solid var(--green-text);
  border-bottom: 2.5px solid var(--green-text);
  transform: rotate(-45deg);
}
.disclaimer { max-width: 74ch; margin-inline: auto; text-align: center; }

/* ---------------------------------------------------------------- early access
 *
 * The form used to be a white card on a white band, which meant it read as six loose fields rather
 * than a panel, with a two-line column of copy beside it and a hole under that. It sits on the page
 * tone now so the card is a card, the copy column carries what joining actually means, and the two
 * halves are the same height.
 */
.signup { display: grid; gap: var(--s-6); align-items: center; }
@media (min-width: 900px) { .signup { grid-template-columns: 1fr 1fr; gap: var(--s-8); } }
.signup-copy { display: grid; gap: var(--s-4); align-content: center; max-width: 46ch; }
/* Its own tick style rather than .marks.now, which paints its text white because it was written
   for the dark column in the contrast section. Reused here on a pale background it produced white
   text on pale green, which measured fine in every automated check and was invisible on screen. */
.signup-marks { margin-top: var(--s-2); }
.signup-marks li { color: var(--ink); font-weight: 500; }
.signup-marks li::before { content: "\2713"; background: var(--green); color: var(--navy); font-size: 12px; }

/* A shadow and a real border, because on the page tone this is the object the eye should land on. */
.signup .form.card {
  padding: var(--s-6);
  border: 1px solid var(--line);
  box-shadow: var(--sh-lift);
}
@media (min-width: 700px) { .signup .form.card { padding: var(--s-7); } }

/* ---------------------------------------------------------------- early access form */

.form { display: grid; gap: var(--s-4); }
.fields { display: grid; gap: var(--s-4); }
@media (min-width: 620px) { .fields { grid-template-columns: 1fr 1fr; } }
.field { display: grid; gap: 6px; min-width: 0; }
.field > label { font-size: 14px; font-weight: 600; }
.field .hint { font-size: 13px; color: var(--muted); }

/* min-height rather than height: a flex or grid child can be told to be 20px tall by its container,
   and it cannot be told to be shorter than a min-height. That fault shipped once already. */
.field input, .field select, .field textarea {
  width: 100%;
  min-width: 0;
  min-height: 52px;
  padding: 0 16px;
  border: 1px solid var(--line);
  border-radius: var(--r-tile);
  background: var(--card);
  color: var(--ink);
  font: inherit;
  font-size: 16px;               /* 16px or iOS zooms the page on focus */
  appearance: none;
}
.field select {
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 21px) 23px, calc(100% - 15px) 23px;
  background-size: 6px 6px;
  background-repeat: no-repeat;
  padding-right: 42px;
}
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--green-text); }
/* A textarea is the one field with a real height, so it opts out of the single-line min-height and
   gets its own padding and a resize handle that can only grow downwards. */
.field textarea {
  min-height: 0;
  padding: 14px 16px;
  line-height: 1.55;
  resize: vertical;
  font-family: inherit;
}
.field input[aria-invalid="true"], .field select[aria-invalid="true"], .field textarea[aria-invalid="true"] { border-color: var(--red-text); background: #fdf6f6; }
.field .err { font-size: 13px; color: var(--red-text); min-height: 0; }
.field .err:empty { display: none; }

.form .btn[disabled] { opacity: .6; cursor: progress; }
.form-msg { padding: var(--s-4) var(--s-5); border-radius: var(--r-tile); font-size: 15.5px; }
.form-msg[hidden] { display: none; }
.form-msg.ok { background: var(--mint); color: var(--green-text); }
.form-msg.bad { background: #fbeeee; color: var(--red-text); }

/* The honeypot. Off-screen rather than display:none, because some bots skip anything hidden and
   fill everything else, which is the opposite of what we want. aria-hidden and tabindex -1 keep it
   away from anybody using a screen reader or a keyboard. */
.trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* The spinner in the submit button while the request is in flight. */
.spin {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 2px solid rgba(3, 21, 55, .3);
  border-top-color: var(--navy);
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------------------------------------------------------------- reveal on scroll
 *
 * Opt-in, tiny, and it must fail open: the class is only added by script once the observer is
 * running, so with no JavaScript, or with reduced motion asked for, everything is simply visible.
 */
.js-reveal .reveal { opacity: 0; transform: translateY(14px); transition: opacity .5s ease, transform .5s ease; }
.js-reveal .reveal.in { opacity: 1; transform: none; }
/* A row of cards arriving together is a slab moving; arriving 60ms apart it is a row of cards.
   Six steps only, because past that the last card is late enough to look broken. */
.js-reveal .reveal.in > :nth-child(1) { transition-delay: 0ms; }
.js-reveal .reveal.in > :nth-child(2) { transition-delay: 60ms; }
.js-reveal .reveal.in > :nth-child(3) { transition-delay: 120ms; }
.js-reveal .reveal.in > :nth-child(4) { transition-delay: 180ms; }
.js-reveal .reveal.in > :nth-child(5) { transition-delay: 240ms; }
.js-reveal .reveal.in > :nth-child(n+6) { transition-delay: 300ms; }
.js-reveal .reveal > * { transition: opacity .5s ease, transform .5s ease; }
.js-reveal .reveal:not(.in) > * { opacity: 0; transform: translateY(10px); }
@media (prefers-reduced-motion: reduce) {
  .js-reveal .reveal { opacity: 1; transform: none; }
}

/* The sticky chrome is the announcement bar plus the header, and it measures 109px at desktop and
   more on a phone, where the announcement wraps to two lines. 90px was not enough and anchored
   headings landed underneath it. Over-scrolling a little is harmless; under-scrolling hides the
   thing you clicked to see. */
:root { --chrome: 132px; }
@media (min-width: 560px) { :root { --chrome: 120px; } }

html { scroll-behavior: smooth; scroll-padding-top: var(--chrome); }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

[id] { scroll-margin-top: var(--chrome); }
