/* Pilebase marketing (for-yards), shared across all its pages.

   The white / green / blue system (2026-08-10) and the radius scale live in /tokens.css.
   As of 2026-09-02 the COMPONENTS this tree drew — the nav and its narrow-screen sidebar,
   buttons, form fields, .page-hero, .kicker, .statement, the dark .band, .step.numbered,
   the card lift and the footer — live in /components.css and are drawn by the buyer
   tree too. Mason looked at both trees, preferred this one, and the
   marketplace adopted it; the reasoning and the date are in the header of tokens.css.

   NOTHING HERE MOVED VISUALLY except in this tree's favour: the narrow-screen hamburger
   went from 40px to the 44px tap-target floor, the contact form's fields went from 15px
   to 16px (below 16px iOS zooms the viewport on focus and never zooms back), and the
   footer opened from 26px to 44px. Everything else is the same rule in a different file.

   WHAT IS LEFT HERE is what only a yard owner reads: this tree's hero and its CSS-drawn
   device illustrations, the module grid, the pricing tiers, the objection block, the
   contact form, the sticky mobile CTA and the motion pass.

   THE LEGACY ALIASES BELOW ARE THIS TREE'S DEBT. --slate/--amber/--red mapped onto the
   new system so older inline references in per-page styles keep resolving. They were
   deliberately NOT copied into /components.css and NOT introduced to the buyer tree.
   Retiring them means rewriting those inline styles onto the real token names, which is
   its own pass with its own screenshots.

   tokens.css and components.css are LINKED from each page's head rather than @imported
   here — see the header of ../style.css for the measurement. An @import costs a serial
   round trip in front of first paint, and the webfont sat one hop beyond that. */
:root {
  /* legacy aliases (old names appear in per-page inline styles) */
  --paper: var(--ink); --muted-d: var(--muted);
  --black-950: var(--white); --black-900: var(--band);
  --black-850: var(--band-2); --black-800: var(--rule);
  --slate-950: var(--white); --slate-900: var(--band);
  --slate-800: var(--band-2); --slate-600: var(--faint);
  --red-700: var(--green-700); --red-500: var(--green-600);
  --red-400: var(--green-700); --red-300: var(--blue-600);
  --oxblood: var(--green-900);
  --amber-500: var(--green-600); --amber-700: var(--green-700);
  --amber-100: var(--green-100);
  --bg: var(--white); --panel: var(--band);
  --text: var(--ink);
}
/* The reset, the body (including `overflow-x: clip`, which must never become `hidden` —
   the reason is written out in /components.css), link colour and ::selection are shared. */

/* reveal-on-scroll: the .js-gated .reveal rules are in /components.css (2026-09-05), since
   the buyer home carries the same hero now, gate and all. reveal-guard.js still sets .js. */

/* nav — the bar, the pills, the current-page fill and the CTA are in /components.css and
   are one shape across both trees now. `.nav nav > a` there reaches this tree's bare
   <nav> and the buyer tree's <nav class="nav-links"> alike; the current page is styled
   off BOTH `.active` (this tree) and `aria-current="page"` (the buyer tree), so neither
   tree had to be re-marked. The brand lockup (.brand, .brand-mark) is there too since
   2026-09-05, when the buyer tree adopted this tree's <img> wordmark; the footer's mark
   is still only this tree's. */
.foot-mark { display: block; margin: 0 auto 14px; height: 36px; width: auto; }
/* The narrow nav — the hamburger, the scrim and the sidebar — is in /components.css.
   One copy now: the two trees carried near-identical forty-line blocks differing only in
   whether the rule named `.nav-links` or `.nav nav`. `data-nav="ready"` is still set by
   site/nav.js, so a browser with no JavaScript keeps the wrapping link row and loses
   nothing. The toggle is 44px there rather than the 40px it was here: 44px is the
   tap-target floor.

   The one thing site/nav.js still has to know about both trees is where the CTA lives —
   the last link INSIDE the list here, a sibling of the list in the buyer tree — which is
   what the containment test in its mirror() is for. Unifying the SHEETS did not unify the
   markup, and it did not need to.

/* hero (home): .hero (the ground and the glow), .hero-grid and its 880px stack, the
   .grad accent word, the ::before aurora and the phone mock are in /components.css since
   2026-09-05, when the buyer home adopted this hero. The type scale below stays here. */
.hero h1 { font-size: clamp(36px, 5.4vw, 62px); line-height: 1.04; letter-spacing: -0.03em; font-weight: 800; margin-bottom: 20px; text-wrap: balance; }
.hero .sub { color: var(--muted); font-size: clamp(16px, 2vw, 19px); max-width: 560px; margin-bottom: 32px; font-weight: 500; }
.buttons { display: flex; gap: 14px; flex-wrap: wrap; }
/* .btn, .btn-primary / .btn-amber and .btn-ghost are in /components.css, at this tree's
   sizing. The hover lift and the deeper primary shadow that used to sit in the motion
   pass at the foot of this file went with them. */
.hero .tiny { margin-top: 20px; font-size: 13.5px; color: var(--faint); }

/* .page-hero, .statement and .kicker are in /components.css. They are three of the four
   things the buyer tree did not have and now does.

   THE FOURTH — `.rows`/`.frow`/`.fcopy`/`.fmock` and the `.fmock` compounds — WAS DELETED
   2026-09-04. Test applied: not "does anything use this class" but "does anything THAT
   LOADS THIS SHEET draw it". Nothing here does. `marketing/style.css` keeps its copy:
   marketing/index.html:152-234 draws all four, so the two sheets have opposite answers.

   AND THE REST OF THE COPY WENT 2026-09-22, same test, run over all thirteen pages that
   link this sheet rather than the nine at its top level — for-yards/legal/ links it too and
   the 09-04 pass could not see that. Gone: the phone home-screen drawing
   (`.aphone`/`.ascreen`/`.astatus`/`.anotif`/`.agrid`/`.aapp`/`.adock`/`.ic`/`.dim` and the
   `anotif` and `aicon` keyframes), the desktop `.window` family, the `.compare` block, the
   `.qualifier` card that marketing.js's own header says is gone, `.hero-art` with the
   `art-float` pair that animated it, `.stat`/`.stat-row`, `.price-foot`, and the
   `.pricing-wrap.two-up`/`.three-up` widths. marketing/style.css keeps every one of them and
   marketing/index.html still draws them, so — again — the two sheets have opposite answers.
   A selector went only when EVERY comma-separated arm of it named at least one class no
   page and no script on the section has ever mentioned; one absent name in a compound is
   enough, because `.pricing-wrap.two-up` needs both on one element. 45,505 bytes to 37,399,
   and nothing that renders moved. */

/* .qcard, its badges, .mockbtn and the .phone (bezel, notch, bar, body) are in
   /components.css: the buyer home holds up the same phone with a different screen. That
   is what these pages draw. The CSS-drawn .window family that used to sit here drew the
   app's desktop screens instead, and its own narrow-ok suppression recorded that no served
   page used it; it went on 2026-09-22 with the rest of the copied-in drawings. */

/* The dark .band and its .cell grid, and the .steps-wrap / .step.numbered set, are in
   /components.css. Two of those selectors are SCOPED there in a way they were not here:
   `.band .grid` and `.steps-wrap .steps`, because the buyer tree's `.grid` is its listing
   grid and its `.steps` is the four-dot order-tracking rail. Both trees already write the
   markup that way, so nothing here changed; a bare `.grid` in a shared sheet would have
   quietly relaid out every yard card on the site.

/* modules page */
.module-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; max-width: 1080px; margin: 0 auto; padding: 70px 24px 100px; }
/* The card lift — 4px, a green border and a soft shadow — is in /components.css, where
   it covers .module here and .card in the buyer tree, because they are the same object. */
.module { background: var(--white); border: 1px solid var(--rule); border-radius: var(--r-card); padding: 26px; }
/* The emoji tile that used to head each module is gone (2026-08-15). The restored
   product page labels each card with the same uppercase .kicker every other for-yards
   section uses, so the page reads as part of this tree rather than as the 2026-08-11
   marketing tree pasted back in. */
.module .kicker { margin-bottom: 8px; }
.module h3 { font-size: 18px; color: var(--ink); margin-bottom: 8px; }
.module p { color: var(--muted); font-size: 14.5px; }
.module ul { margin: 12px 0 0 18px; color: var(--muted); font-size: 13.5px; display: grid; gap: 5px; }
.module li::marker { color: var(--green-600); }

/* contact */
.contact { background: linear-gradient(180deg, var(--green-50), var(--white)); color: var(--ink); padding: 100px 24px 110px; border-top: 1px solid var(--rule); }
.contact-inner { max-width: 560px; margin: 0 auto; text-align: center; }
.contact h2 { font-size: clamp(30px, 4.4vw, 46px); letter-spacing: -0.02em; font-weight: 800; margin-bottom: 12px; }
.contact .sub { color: var(--muted); font-size: 17px; margin-bottom: 40px; }
form { text-align: left; display: grid; gap: 14px; }
form label { font-size: 12.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); display: block; margin-bottom: 6px; }
/* The field itself, its focus ring, .frm-row and .hp are in /components.css. The field
   went from 15px to 16px there: below 16px iOS zooms the viewport on focus and does not
   zoom back. The uppercase label above it is this tree's and stays here. */
/* ---- Pricing ---- */
.pricing-wrap { max-width: 760px; margin: 0 auto; padding: 60px 24px 20px; }
.price-card {
  background: var(--white); border: 1px solid var(--rule); border-radius: var(--r-card);
  padding: 40px 38px; box-shadow: 0 18px 44px rgba(18, 49, 35, 0.08); position: relative;
}
.price-card.featured { border: 2px solid var(--green-600); }
.price-flag {
  position: absolute; top: -14px; left: 38px; background: var(--green-600); color: #fff;
  font-size: 12px; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase;
  padding: 6px 14px; border-radius: var(--r-pill);
}
.price-name { font-size: 15px; font-weight: 700; color: var(--muted); letter-spacing: 0.02em; }
.price-amount { display: flex; align-items: baseline; gap: 8px; margin: 10px 0 4px; }
.price-amount .n { font-size: clamp(44px, 8vw, 62px); font-weight: 800; letter-spacing: -0.03em; color: var(--ink); line-height: 1; font-variant-numeric: tabular-nums; }
.price-amount .per { font-size: 18px; color: var(--muted); font-weight: 600; }
.price-sub { color: var(--muted); font-size: 15px; margin-bottom: 26px; }
.price-features { list-style: none; padding: 0; margin: 0 0 30px; display: grid; gap: 13px; }
.price-features li { position: relative; padding-left: 30px; font-size: 15.5px; line-height: 1.5; color: var(--muted); }
.price-features li::before {
  content: '✓'; position: absolute; left: 0; top: -1px; color: var(--green-600);
  font-weight: 900; font-size: 17px;
}
.price-features li b { color: var(--ink); }
.price-standard { text-align: center; color: var(--muted); font-size: 15px; margin-top: 22px; }
.price-standard b { color: var(--ink); }

/* ---- The side-by-side comparison (pricing.html #compare, 2026-09-23) ----------------
   A real <table>, and the reason is check-narrow.js rather than taste. Its rule 1 refuses
   a two-track grid with no single-column override, and a comparison of two rungs is the
   shape that reaches for `1fr 1fr` first. `table-layout: fixed` with no declared width on
   any column fits whatever viewport it is given, by construction — there is no track
   floor to exceed and nothing to override in a media query, so this survives 375px
   without one. It also brings the row and column headers a screen reader needs for free,
   which a grid of divs would have had to fake.

   NO NEW @keyframes AND NO NEW TRANSITION. check-for-yards-claims.js's motion row requires
   every keyframe name on this sheet to appear in the reduced-motion block; the table uses
   the page's existing `.reveal` entry animation and adds no motion of its own, so there is
   nothing here for that block to stand down. */
.tier-compare { max-width: 860px; margin: 64px auto 0; padding: 0 4px; }
.tier-compare h2 { font-size: clamp(24px, 3.6vw, 32px); letter-spacing: -0.02em; text-align: center; margin-bottom: 10px; }
.tier-compare > p { color: var(--muted); font-size: 15.5px; line-height: 1.6; text-align: center; max-width: 620px; margin: 0 auto 26px; }
.tier-table { width: 100%; table-layout: fixed; border-collapse: collapse; font-size: 14.5px; }
.tier-table caption { caption-side: bottom; color: var(--faint); font-size: 13px; line-height: 1.5; text-align: left; padding-top: 14px; }
.tier-table th, .tier-table td { padding: 12px 10px; border-bottom: 1px solid var(--rule); vertical-align: top; text-align: left; }
.tier-table thead th { color: var(--muted); font-size: 13px; letter-spacing: 0.04em; text-transform: uppercase; border-bottom: 2px solid var(--border); }
.tier-table thead th + th, .tier-table tbody td { text-align: center; }
/* The two rung columns are narrow and equal; the feature column takes the rest. Stated as
   a percentage rather than a px width so check-narrow's rule 3 has nothing to measure and
   the table still fits when the viewport does not. */
.tier-table thead th + th { width: 22%; }
.tier-table tbody th { font-weight: 600; color: var(--ink); line-height: 1.4; }
.tier-table .row-note { display: block; font-weight: 400; color: var(--muted); font-size: 13px; line-height: 1.45; margin-top: 3px; }
/* No .row-note a rule: the cells carry no links. See the note at #compare's connector
   row in pricing.html for why, and check:tap-targets for the floor it would miss. */
.tier-table td.y { color: var(--green-700); font-weight: 700; }
.tier-table td.n { color: var(--faint); font-weight: 600; }
.tier-table tr.sep th, .tier-table tr.sep td { border-top: 2px solid var(--border); }
.tier-table tr.rate td { font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
@media (max-width: 560px) {
  .tier-table { font-size: 13.5px; }
  .tier-table th, .tier-table td { padding: 10px 6px; }
  .tier-table thead th + th { width: 24%; }
  .tier-table .row-note { font-size: 12.5px; }
}

/* ---- Two tiers, side by side ----
   Wider than the single-card wrap, and the cards stack on a phone in source order:
   Storefront first, because the yard that needs it is the one who said "we already have
   software" and stopped reading. */
/* Three layers (decision P, 2026-08-21): Listed, Claimed, Complete. Same cards, one
   more column, and the stack order on a phone is the ladder's own: free first. */
.pricing-wrap.three-up { max-width: 1240px; }
.pricing-wrap.three-up .price-grid { grid-template-columns: 1fr 1fr 1fr; }
@media (max-width: 1020px) {
  .pricing-wrap.three-up .price-grid { grid-template-columns: 1fr; }
}
/* RETIRED 2026-09-22, KEPT RATHER THAN DELETED. .four-up was the 2x2 the pricing page used
   while there were four rungs (Listed, Claimed, Scheduling, Complete) — four columns do not
   fit a laptop at a readable measure. The Scheduling rung was removed, pricing.html is back
   to .three-up above, and no page carries this class today. It stays because the reason it
   exists is the thing worth keeping: if a fourth card ever returns, the answer is a 2x2 and
   not four columns. */
.pricing-wrap.four-up { max-width: 1120px; }
.pricing-wrap.four-up .price-grid { grid-template-columns: 1fr 1fr; }
@media (max-width: 820px) {
  .pricing-wrap.four-up .price-grid { grid-template-columns: 1fr; }
}
.price-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; align-items: start; }
.price-grid .price-card { padding: 34px 30px; }
/* Tier cards carry their own headroom so both columns line up regardless of list length. */
.price-grid .price-features { margin-bottom: 24px; }
.price-tagline { color: var(--muted); font-size: 14.5px; line-height: 1.55; margin: 0 0 22px; }
.price-annual {
  border-top: 1px solid var(--rule); margin-top: 20px; padding-top: 14px;
  color: var(--muted); font-size: 13.5px; line-height: 1.5;
}
.price-annual b { color: var(--ink); }
/* "Everything in Storefront, plus:", the honest way to write the upper tier's list
   without restating the lower tier's features as though they were exclusive. */
.price-inherits {
  font-size: 14px; font-weight: 700; color: var(--green-700);
  background: var(--green-50); border: 1px solid var(--green-100);
  border-radius: var(--r-ctl); padding: 10px 13px; margin-bottom: 16px;
}
@media (max-width: 820px) {
  .price-grid { grid-template-columns: 1fr; }
}

/* ---- "You already have software", the objection, answered on the home page ---- */
.objection { padding: 76px 24px; background: var(--band); border-block: 1px solid var(--rule); }
.objection-inner { max-width: 900px; margin: 0 auto; }
.objection h2 { font-size: clamp(26px, 4.4vw, 38px); letter-spacing: -0.02em; margin-bottom: 12px; }
.objection > .objection-inner > p { color: var(--muted); font-size: 17px; line-height: 1.65; max-width: 660px; }
.objection-split { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin: 28px 0 24px; }
.objection-half {
  background: var(--white); border: 1px solid var(--rule);
  border-radius: var(--r-card); padding: 22px 24px;
}
.objection-half h3 { font-size: 16px; margin-bottom: 10px; color: var(--ink); }
.objection-half ul { list-style: none; padding: 0; margin: 0; display: grid; gap: 9px; }
.objection-half li { font-size: 14.5px; line-height: 1.5; color: var(--muted); padding-left: 20px; position: relative; }
.objection-half li::before { content: '·'; position: absolute; left: 6px; color: var(--green-600); font-weight: 900; }
.objection-half.keep { border-color: var(--green-600); }
@media (max-width: 700px) { .objection-split { grid-template-columns: 1fr; } }

.submit { background: var(--green-600); color: #fff; border: none; border-radius: var(--r-pill); padding: 15px 32px; font-size: 16.5px; font-weight: 700; cursor: pointer; font-family: inherit; margin-top: 6px; box-shadow: 0 8px 20px rgba(46, 125, 79, 0.22); }
.submit:hover { background: var(--green-700); }
.submit:disabled { opacity: 0.6; cursor: wait; }
.form-note { font-size: 13px; color: var(--faint); margin-top: 14px; text-align: center; }
.form-note a { color: var(--blue-600); }
/* Booking CTA. Hidden until marketing.js confirms a scheduler URL is configured,
   that way an unconfigured site simply shows the form, never a dead button. */
.booking { display: none; margin-bottom: 8px; }
/* .ready forces full visibility outright, it must never inherit the reveal
   animation's opacity:0 start state, or the button stays invisible when the
   scroll observer can't track a display:none element. */
.booking.ready { display: block; opacity: 1; transform: none; }
.booking-note { color: var(--muted); font-size: 14.5px; margin-top: 14px; line-height: 1.6; }
/* ON THE DARK BAND the muted grey is all but invisible — the booking block had only
   ever sat on the light .contact section until start.html put one inside a .band (2026-09-02).
   Same value the band already uses for its own sub-line, so there is one light-on-dark grey
   rather than a second one. */
.band .booking-note { color: #c2d3c8; }
.or-divider {
  display: flex; align-items: center; gap: 14px;
  color: var(--faint); font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase;
  margin: 36px 0 30px;
}
.or-divider::before, .or-divider::after {
  content: ''; flex: 1; height: 1px; background: var(--rule);
}

/* The form outcome chip and its two states are in /components.css.
/* Same chip, but it appears mid-block under the booking button rather than atop a form. */
.booking-thanks { margin: 16px auto 0; max-width: 460px; text-align: left; font-weight: 600; }

/* The footer and its policy row are in /components.css. This tree's copy was two shades
   and 18px of padding away from the buyer tree's for no reason anybody wrote down. */

/* ============================================================
   Voice + motion pass. A display face with some character for
   the headlines, and motion that rewards attention without
   demanding it. All of it sits at the end of the sheet so it
   wins the cascade, and all of it stands down for
   prefers-reduced-motion.
   ============================================================ */
/* h1/h2/h3/.brand take the display face in /components.css, which covers .statement h2.
   (It names .fcopy h3 too; that selector is dead in THIS sheet as of 2026-09-04, but
   /components.css is shared with the buyer tree and is not this pass's to edit.) */

/* The breathing headline gradient (.hero h1 .grad) and the aurora (.hero::before) are in
   /components.css with the rest of the hero, and stand down for prefers-reduced-motion
   there. */


/* ---- Sticky mobile CTA ------------------------------------------------------
   The phone viewport loses the nav's "Book a demo" the moment you scroll, and the
   next CTA can be four screens away. This keeps one within thumb reach without
   covering anything: it only exists under 760px, it waits until the hero is behind
   you, and it retracts when the contact form is on screen, so a prospect is never
   looking at two competing asks stacked on top of each other.
   Desktop is unaffected, the element is display:none there, not merely hidden. */
.mobile-cta { display: none; }

@media (max-width: 760px) {
  .mobile-cta {
    display: flex; gap: 10px;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 80;
    padding: 10px 14px calc(10px + env(safe-area-inset-bottom));
    background: rgba(255, 255, 255, 0.94);
    backdrop-filter: saturate(1.2) blur(14px);
    -webkit-backdrop-filter: saturate(1.2) blur(14px);
    border-top: 1px solid var(--rule);
    transform: translateY(110%);
    transition: transform 0.28s cubic-bezier(0.16, 1, 0.3, 1);
  }
  .mobile-cta.show { transform: none; }
    .mobile-cta a {
    flex: 1; text-align: center; text-decoration: none;
    font-weight: 700; font-size: 14.5px; padding: 12px 10px; border-radius: var(--r-pill);
    white-space: nowrap;
  }
  .mobile-cta .m-primary { background: var(--green-600); color: #fff; }
  .mobile-cta .m-secondary { color: var(--blue-600); border: 1.5px solid var(--border); background: var(--white); }
  /* Clear the bar so it never sits on top of the last line of the footer. */
  footer { padding-bottom: calc(76px + env(safe-area-inset-bottom)); }
}

@media (prefers-reduced-motion: reduce) {
      .btn, .mobile-cta { transition: none; }
}

/* ---- the signup form (start.html, 2026-09-02) --------------------------------------
   Two controls the rest of the site has never needed: a text field with a fixed suffix
   glued to it, and a checkbox on a line of prose. Both live here rather than inline on
   the page, because tools/check-narrow.js reads the SHEETS for shapes that cannot fit a
   375px phone, and a width declared in a style attribute is a width it cannot see. */
.addr-row { display: flex; align-items: stretch; flex-wrap: wrap; gap: 0; }
.addr-row input { flex: 1 1 140px; min-width: 0; border-top-right-radius: 0; border-bottom-right-radius: 0; }
.addr-suffix {
  display: flex; align-items: center; white-space: nowrap;
  padding: 0 13px; font-size: 14.5px; font-weight: 600; color: var(--muted);
  background: var(--band); border: 1.5px solid var(--border); border-left: 0;
  border-top-right-radius: var(--r-ctl); border-bottom-right-radius: var(--r-ctl);
}
/* Under about 360px the suffix wraps to its own line rather than squeezing the field it
   annotates down to nothing; `flex-wrap: wrap` above is what allows it, and these two
   rules put the corners back so the wrapped pair still reads as one control. */
@media (max-width: 380px) {
  .addr-row input { border-radius: var(--r-ctl); }
  .addr-suffix { border-left: 1.5px solid var(--border); border-radius: var(--r-ctl); margin-top: 6px; }
}
.check-row {
  /* `flex-wrap: wrap` because tools/check-narrow.js reads the SHEET for shapes that cannot
     fit a 375px phone, and a flex row with no wrap is one of them by declaration whatever
     it happens to render as today. It renders the same either way — the sentence wraps
     inside its own box long before the row would need to break — and the declaration is
     now honest about what it would do if it did not. */
  display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px;
  margin: 16px 0 4px; cursor: pointer;
  /* `form label` is uppercase 12.5px, which is right for a field name above a box and
     wrong for a sentence somebody reads. Reset it rather than inventing a new element. */
  text-transform: none; letter-spacing: 0; font-size: 14.5px; font-weight: 500;
  color: var(--muted); line-height: 1.55;
}
.check-row input[type="checkbox"] { width: 18px; height: 18px; flex: 0 0 auto; margin-top: 2px; padding: 0; }

/* The signup's phone-code step (2026-09-16): "text it again" and "call me" read as links, and are
   buttons because they post rather than navigate. */
.link-btn { background: none; border: 0; padding: 0 4px; min-height: 44px; font: inherit; color: var(--blue-600); text-decoration: underline; cursor: pointer; }
#start-verify input#y-code { max-width: 180px; letter-spacing: 0.2em; }

/* THE FOR-YARDS PITCH: THE FOUR TIME-SAVERS, THEIR DRAWN FIGURES, AND THE MOTION
   PASS THAT CARRIES THEM (2026-09-19).

   Mason: the buyer marketplace stays restrained, "for yards" pages "definitely can
   be more animated and flowy". The one section of the site where rich motion is
   licensed, and the licence has four conditions that every rule below obeys.

     1. LEGIBLE AT REST, ON FIRST PAINT. Every figure ships DRAWN FINISHED in the
        markup; the rules below add an ENTRY effect over a complete drawing. No
        JavaScript, a dead observer or a half-loaded sheet each leave a reader
        looking at the whole diagram, not a blank box. That is the opposite of the
        usual scroll-reveal deal, on purpose.
     2. NOTHING REFLOWS. Only transform, opacity and stroke-dashoffset animate.
     3. NOTHING BLOCKS READING. No motion under body copy, nothing over 1.1s on
        entry, and the single loop lives inside a drawn device.
     4. prefers-reduced-motion REMOVES it rather than shortening it — one block at
        the foot of this section, because a per-effect rule is one the next effect
        forgets. check-for-yards-claims.js holds all four.

   NO LIBRARY, NO BUILD: CSS, one inline <svg> per figure, and ~40 lines in
   marketing.js that add `.in` on scroll — the reveals' own observer, second target
   list.

   WHY DRAWN AND NOT PHOTOGRAPHED: a screenshot is a JPEG re-cut every time the
   app's chrome moves, pre-sized because Pilebase cannot resize at runtime, and
   real bytes on a phone. These weigh what the HTML weighs, scale to any width,
   read on both grounds, and carry `data-image-source` plus a visible caption so
   nobody has to guess whether they are a photograph of something real. */

/* ---- The section that holds a time-saver: copy on one side, figure on the
        other, alternating, one column under 900px with the figure FIRST in
        the stack (a drawing explains a heading better than it follows it). */
.savers { max-width: 1120px; margin: 0 auto; padding: 20px 24px 90px; display: grid; gap: 92px; }
.saver { display: grid; grid-template-columns: 1fr 1.08fr; gap: 54px; align-items: center; }
.saver.flip .saver-copy { order: 2; }
.saver.flip .saver-fig { order: 1; }
.saver-copy h3 { font-size: clamp(24px, 3.3vw, 34px); letter-spacing: -0.02em; line-height: 1.12; color: var(--ink); margin-bottom: 14px; }
.saver-copy p { color: var(--muted); font-size: 16.5px; line-height: 1.65; }
.saver-copy p + p { margin-top: 12px; }
/* The line under each claim that names the part of the product it comes from.
   A pitch that says "dispatch ranks your trucks" and does not say what the
   ranking is made of is the sentence a yard owner has heard from four vendors
   already. */
.saver-basis {
  margin-top: 16px; font-size: 13.5px; line-height: 1.6; color: var(--faint);
  border-left: 3px solid var(--rule); padding-left: 13px;
}
.saver-basis b { color: var(--muted); }
@media (max-width: 900px) {
  .saver, .saver.flip { grid-template-columns: 1fr; gap: 26px; }
  .saver .saver-copy, .saver.flip .saver-copy { order: 2; }
  .saver .saver-fig, .saver.flip .saver-fig { order: 1; }
}

/* ---- A drawn figure, and the line underneath that says what it is. THE CAPTION
        IS NOT DECORATION: every figure here is a drawing we made, and it says so
        in words a reader sees rather than only in an attribute a checker reads. A
        placeholder that reads as real is the failure it exists to prevent. */
.fig { margin: 0; position: relative; }
.fig svg { display: block; width: 100%; height: auto; }
.fig-note {
  margin-top: 10px; font-size: 11.5px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--faint); text-align: center;
}
/* On the dark band the muted grey is unreadable; this is the same light grey
   the band already uses for its own sub-line, not a second one. */
.band .fig-note { color: #c2d3c8; }

/* The figure's own frame: the drawings depict the app's screens, so they sit
   on a card the way a window does, with a visible edge rather than a glow. */
.fig-frame {
  background: var(--white); border: 1px solid var(--rule); border-radius: var(--r-card);
  box-shadow: 0 18px 44px rgba(18, 49, 35, 0.1); overflow: hidden;
  transition: transform 0.4s cubic-bezier(0.2, 0.6, 0.2, 1), box-shadow 0.4s ease;
}
/* Hover with some life in it. The lift is a TRANSFORM and a SHADOW, never a
   hue on its own: a state that exists only as a colour tells a reader who
   cannot separate those colours nothing at all. */
.fig-frame:hover { transform: translateY(-5px); box-shadow: 0 26px 60px rgba(18, 49, 35, 0.16); }
.saver-fig { position: relative; }

/* ---- THE ENTRY ANIMATIONS, every one gated on `.js` AND `.in`. reveal-guard.js
        sets `.js` before first paint and takes it off again after two seconds if
        marketing.js never arrives; the observer adds `.in`. So no JavaScript,
        failed JavaScript and not-yet-scrolled all land on the FINISHED state.
        The animation is the exception, never the default. */

/* A drawn line that draws itself. The path ships with its full stroke; only
   the .js-gated rule hides it, and only until .in arrives. */
.js .fig .draw { stroke-dasharray: var(--len, 400); stroke-dashoffset: var(--len, 400); }
.js .fig.in .draw { animation: fig-draw 1.05s cubic-bezier(0.22, 1, 0.36, 1) forwards; }
.js .fig.in .draw.late { animation-delay: 0.3s; }
@keyframes fig-draw { to { stroke-dashoffset: 0; } }

/* A stroke that must KEEP ITS DASH PATTERN cannot self-draw: stroke-dasharray is
   both how a line draws itself and how it is dashed, and the CSS one beats the
   presentation attribute. .draw on the far delivery route erased the dashes it is
   identified BY, leaving the two routes told apart by colour alone. So it fades. */
.js .fig .fade { opacity: 0; }
.js .fig.in .fade { animation: fig-fade 0.75s ease-out 0.45s forwards; }
@keyframes fig-fade { to { opacity: 1; } }

/* A part of the drawing that slides up into place: a line item landing on a
   ticket, a truck chip settling onto an order, a row arriving in the ledger. */
.js .fig .lift { opacity: 0; transform: translateY(14px); }
.js .fig.in .lift { animation: fig-lift 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
.js .fig.in .lift.s1 { animation-delay: 0.14s; }
.js .fig.in .lift.s2 { animation-delay: 0.28s; }
.js .fig.in .lift.s3 { animation-delay: 0.42s; }
.js .fig.in .lift.s4 { animation-delay: 0.56s; }
@keyframes fig-lift { to { opacity: 1; transform: none; } }

/* Arrives in place without moving. `transform-box: fill-box` so the origin is the
   shape's own box rather than the svg's. */
.js .fig .pop { opacity: 0; transform: scale(0.9); transform-box: fill-box; transform-origin: center; }
.js .fig.in .pop { animation: fig-pop 0.42s cubic-bezier(0.34, 1.4, 0.5, 1) forwards; }
.js .fig.in .pop.s2 { animation-delay: 0.34s; }
.js .fig.in .pop.s3 { animation-delay: 0.52s; }
.js .fig.in .pop.s4 { animation-delay: 0.7s; }
@keyframes fig-pop { to { opacity: 1; transform: scale(1); } }

/* ---- The one loop, inside a drawn device only: one pulse every few seconds on
        the single element the figure is about, with no text under it. */
.js .fig.in .pulse { animation: fig-pulse 3.6s ease-in-out 1.2s infinite; transform-box: fill-box; transform-origin: center; }
@keyframes fig-pulse {
  0%, 82%, 100% { opacity: 1; transform: scale(1); }
  88% { opacity: 0.82; transform: scale(1.06); }
}

/* ---- The yard rail: the whole pitch in a line. Four stops, one ticket, which
        travels once on entry and stops at the books, where a finished order
        belongs. A transform on a <g>, so it moves nothing around it. */
.rail-wrap { max-width: 1000px; margin: 0 auto; padding: 0 24px 10px; }
/* An svg's text scales with the drawing, which is the one thing on a page that a width
   measurement cannot see going wrong: at 400px this rail's labels came out around six
   pixels while scrollWidth said the page was clean. The narrow pair is drawn larger and
   named shorter; see the comment on the labels themselves. 760px is where the rail's
   column stops being wide enough to hold the long labels at a readable size. */
.rail-narrow { display: none; }
@media (max-width: 760px) {
  .rail-wide { display: none; }
  .rail-narrow { display: block; }
}

/* The sentence a free pitch usually leaves out: no uptime promise. Its own class rather
   than borrowing .rail-wrap, which it did in the first draft — and which made it vanish
   the moment the rail was hidden to photograph the page below it. A shared class between
   two things that are not the same thing is a bug waiting for its first reader. */
.fine-print { max-width: 1000px; margin: 0 auto; padding: 0 24px 70px; }
.fine-print p { max-width: 720px; margin: 0 auto; text-align: center; color: var(--muted); font-size: 15.5px; line-height: 1.65; }
.js .fig .rail-ticket { opacity: 0; }
.js .fig.in .rail-ticket { animation: rail-run 2.6s cubic-bezier(0.5, 0, 0.2, 1) 0.25s forwards; }
@keyframes rail-run {
  0% { opacity: 0; transform: translateX(0); }
  8% { opacity: 1; transform: translateX(0); }
  30%, 40% { transform: translateX(200px); }
  58%, 68% { transform: translateX(400px); }
  92%, 100% { opacity: 1; transform: translateX(600px); }
}

/* ---- The proof block: three claims, each naming the document that backs it,
        because "free" with nothing behind it is a page asking to be trusted. */
.proof { max-width: 1040px; margin: 0 auto; padding: 0 24px 96px; display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 18px; }
.proof-card {
  background: var(--white); border: 1px solid var(--rule); border-radius: var(--r-card);
  padding: 24px 24px 20px; transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}
.proof-card:hover { transform: translateY(-4px); border-color: var(--green-600); box-shadow: 0 16px 36px rgba(18, 49, 35, 0.1); }
.proof-card .big {
  font-family: var(--font-display); font-size: 40px; font-weight: 800; letter-spacing: -0.03em;
  color: var(--green-700); line-height: 1; font-variant-numeric: tabular-nums;
}
.proof-card h3 { font-size: 16.5px; margin: 10px 0 8px; color: var(--ink); }
.proof-card p { color: var(--muted); font-size: 14.5px; line-height: 1.6; }
/* The link to the document that backs the claim. `display: flex` with a 44px minimum
   because it IS the tap target — tools/check-tap-targets.js reads the cascade rather than
   the render, and a 12.5px line of text is a 17px-tall thing to hit with a thumb whatever
   it looks like. The padding is on the top so the text keeps sitting under the paragraph
   rather than floating in the middle of its own box. */
.proof-card .src {
  display: flex; align-items: flex-end; min-height: 44px; padding-top: 14px;
  font-size: 12.5px; color: var(--blue-600);
}

/* ---- The two-address comparison: two houses a zone sheet bills identically,
        nine and eighteen road miles out. NOT DISTINGUISHED BY COLOUR ALONE —
        each carries its mileage and its own label, and in the drawing the long
        route is dashed as well as darker. */
.mileage { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 18px; }
.mileage-cell { border: 1px solid var(--rule); border-radius: var(--r-card); padding: 14px 16px; background: var(--band); }
.mileage-cell .n { font-family: var(--font-display); font-size: 26px; font-weight: 800; letter-spacing: -0.02em; color: var(--ink); font-variant-numeric: tabular-nums; }
.mileage-cell .l { font-size: 13px; color: var(--muted); margin-top: 3px; line-height: 1.45; }
@media (max-width: 560px) { .mileage { grid-template-columns: 1fr; } }

/* ---- THE PROVISIONING SWITCH.
        Two versions of the closing ask ship in every copy of this page. Which
        one a reader sees is decided by PILEBASE_PROVISION_LIVE on the server,
        read once through /api/public/form-token and applied by marketing.js as
        a class on <html>. Nothing here is a rewrite: turning the switch on
        changes which of the two is displayed, and turning it off changes it
        back.

        THE DEFAULT IS OFF, AND OFF IS THE HONEST STATE TODAY. `.flag-on` is
        display:none until the class arrives, so a reader with no JavaScript, a
        failed fetch, a 404 from a Caddy block that has not learned the path, or
        an older server that does not send the flag at all sees "talk to us" --
        which is what is true. Absent is not permission; the same rule
        marketplace/server/wireFlag.js is written around.

        AND THE SWAP CANNOT SHIFT THE PAGE, STRUCTURALLY RATHER THAN BY
        ARITHMETIC. The first draft gave .flag-slot a min-height per breakpoint
        and that was a guess, measured and wrong: at 1440px the pitch page's two
        versions differed by 25px and start.html's by 121px, so flipping the
        switch moved everything below the fold. A reserved height can only ever
        be right at the widths somebody checked.

        So BOTH versions are always in the layout, stacked in ONE grid cell, and
        the hidden one is hidden with `visibility` rather than `display`. A
        visibility:hidden element still occupies its cell, so the cell is always
        as tall as the TALLER of the two, at every width, with no number to keep
        up to date — and it is still out of the accessibility tree and still
        unclickable, which is what `display: none` was there for.

        THE DEFAULT IS STILL OFF: `.flag-on` is hidden until `.pb-selfserve`
        arrives on <html>, and nothing but the server can put it there. */
.flag-slot { display: grid; }
.flag-slot > .flag-off, .flag-slot > .flag-on { grid-area: 1 / 1; align-self: center; }
/* A switched pair of BUTTONS rather than blocks. Grid blockifies its items and stretches
   them to the cell, so without this the visible button would silently grow to the width of
   whichever label is longer — a layout change caused by a sentence nobody can see. Start
   both at their own width; the cell is still as wide as the wider of the two, so flipping
   the switch still moves nothing. */
.flag-slot-inline { justify-items: start; }
.flag-on { visibility: hidden; }
.pb-selfserve .flag-on { visibility: visible; }
.pb-selfserve .flag-off { visibility: hidden; }

/* ---- Reduced motion: everything above, REMOVED rather than shortened, with
        every animated property pinned at the value it would have finished on.
        Last in the sheet, and ONE block, because a per-effect rule is one the
        next effect forgets. check-for-yards-claims.js holds the membership. */
@media (prefers-reduced-motion: reduce) {
  .js .fig .draw, .js .fig.in .draw,
  .js .fig .fade, .js .fig.in .fade,
  .js .fig .lift, .js .fig.in .lift,
  .js .fig .pop, .js .fig.in .pop,
  .js .fig.in .pulse,
  .js .fig .rail-ticket {
    animation: none;
    opacity: 1;
    transform: none;
    stroke-dashoffset: 0;
  }
  /* The ticket's finished position is the fourth stop, not the first: pinning
     it at `none` would park it on the counter and read as a different claim. */
  .js .fig.in .rail-ticket { animation: none; opacity: 1; transform: translateX(600px); }
  .fig-frame, .fig-frame:hover, .proof-card, .proof-card:hover { transition: none; transform: none; }
}

/* ---- "What is this costing you" (index.html #costing, cost-calc.js), 2026-09-24 ----
   LAYOUT ONLY, AND TAKEN FROM EXISTING TOKENS. The Design session owns this tree's look and
   agreed to this on the understanding that it adds no colour, no image and no motion, so it
   can be restyled wholesale when the for-yards treatment lands. The heading and sub-line
   come from .steps-wrap; the labels and fields come from the tree's own `form` rules and
   /components.css. The result line is tabular figures so a sum reads down cleanly. */
.costcalc form { max-width: 760px; margin: 0 auto; gap: 18px; }
.costcalc fieldset { border: 1px solid var(--rule); border-radius: var(--r-card); padding: 18px 20px 16px; margin: 0; min-width: 0; }
.costcalc legend { font-weight: 800; color: var(--ink); padding: 0 6px; font-size: 17px; }
.costcalc-inputs { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; align-items: end; }
.costcalc-inputs input { width: 100%; }
.costcalc-out { margin-top: 12px; color: var(--muted); font-size: 15px; line-height: 1.55; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.costcalc-out.done { color: var(--ink); font-weight: 600; }
.costcalc-fix { margin-top: 6px; color: var(--muted); font-size: 14.5px; line-height: 1.55; }
.costcalc-total { margin-top: 6px; color: var(--muted); font-size: 17px; line-height: 1.55; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.costcalc-total.done { color: var(--ink); font-weight: 800; }
.costcalc-note { color: var(--muted); font-size: 14.5px; line-height: 1.55; }

/* ---- The story: one yard, one day (2026-09-24) -----------------------------------------------
   Mason: "The scroll story becomes the whole for-yards home page ... One continuous yard the camera
   travels through ... STOPS along the way pin a short card about the software, then release."
   The content spec is docs/for-yards/SCROLL-STORY-2026-09-24.md; the camera is /for-yards/load.js.

   AT REST (and under reduced motion, on a phone under 720px, and with scripts off) the story is an
   ordered list: the hero with the whole yard, then each stop's drawing, clock, headline and facts.
   load.js adds .st-live: the stage (one canvas) is pinned behind everything, each stop becomes a
   tall stretch of scroll whose card pins in view and then releases, and each stop's own <img> is
   shrunk to a point inside its stop so the browser still lazy-loads it as the stop nears while
   the canvas paints it. Nothing here animates on its own, so there is no @keyframes to stand down:
   every movement is the reader's scroll. */
.st { position: relative; background: var(--night); color: var(--ink); }
.st-stage { display: none; }
.st .ld-src { font-size: 12px; color: var(--faint); margin: 0; }
.ld-when { font-size: 12.5px; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); }
.st-name { margin: 0; font-size: 13px; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; color: var(--orange-2); }
.ld-copy h3 { color: var(--ink); font-size: clamp(22px, 2.4vw, 30px); line-height: 1.1; letter-spacing: -0.015em; }
.st-facts { margin: 0; padding: 0 0 0 18px; display: grid; gap: 7px; color: #c9dccf; font-size: 15px; line-height: 1.5; }
.st-facts li::marker { color: var(--orange); }
.st-more { justify-self: start; display: inline-flex; align-items: center; min-height: 44px; color: var(--leaf); font-weight: 700; font-size: 14.5px; text-underline-offset: 3px; }
.st-more::after { content: " →"; }
.ld-pic { position: relative; border-radius: var(--r-card); overflow: hidden; border: 1px solid var(--line); background: #000; aspect-ratio: 16 / 9; }
.ld-pic picture, .ld-pic img { display: block; width: 100%; height: 100%; }
.ld-pic img { object-fit: cover; }
/* The Truck 6 tag on the whole-yard drawing, placed in percent of the drawing (a unitless custom
   property: a bare percentage in an inline style reads as a fee to check:price-surfaces). */
.ld-tag, .st-tag {
  position: absolute; background: var(--orange); color: var(--orange-ink); font-size: 12px; font-weight: 800;
  letter-spacing: 0.04em; padding: 4px 9px; border-radius: calc(var(--r-ctl) - 4px); white-space: nowrap; pointer-events: none;
}
.ld-tag { left: calc(var(--x) * 1%); top: calc(var(--y) * 1%); transform: translate(-50%, -135%); }
.ld-tag::after, .st-tag::after { content: ""; position: absolute; left: 50%; top: 100%; transform: translateX(-50%); border: 6px solid transparent; border-top-color: var(--orange); }
.ld-card { margin: 4px 0 0; max-width: 400px; background: var(--night-2); border: 1px solid var(--line); border-radius: var(--r-card); overflow: hidden; font-size: 13px; }
.ld-sh { background: var(--loam); color: var(--ink); padding: 9px 12px; font-size: 12px; font-weight: 800; letter-spacing: 0.08em; display: flex; justify-content: space-between; gap: 12px; }
.ld-sb { padding: 10px 12px; display: grid; gap: 8px; }
.ld-r { background: var(--loam); border: 1px solid var(--line); border-radius: calc(var(--r-ctl) - 2px); padding: 8px 10px; color: var(--ink); }
.ld-r small { display: block; color: var(--muted); font-size: 11.5px; }
.ld-r.pick { border-color: var(--orange); box-shadow: inset 3px 0 0 var(--orange); }
.ld-card .fig-note { color: var(--faint); font-size: 11px; padding: 0 12px 10px; margin: 0; }

/* The list, at rest. */
.st .st-hero.hero { background: none; }
.st .st-hero.hero::before { display: none; }
.st-hero { display: grid; grid-template-columns: 1fr 1.1fr; gap: 40px; align-items: center; max-width: 1240px; margin: 0 auto; padding: 72px 24px 48px; background: none; }
.st-hero-copy { position: relative; z-index: 1; }
.st-open { margin: 0; display: grid; gap: 8px; }
.st-stops { display: flex; gap: 8px; justify-content: center; padding: 8px 24px 24px; }
.st-stops a { display: grid; place-items: center; width: 44px; height: 44px; border-radius: var(--r-pill); border: 1px solid var(--line); color: var(--muted); font-weight: 800; text-decoration: none; }
.st-stops a:hover, .st-stops a.on { border-color: var(--orange); color: var(--ink); background: var(--loam); }
.st-list { list-style: none; margin: 0 auto; padding: 24px 24px 80px; max-width: 1180px; display: grid; gap: 64px; }
.st-list > .ld-scene { display: grid; grid-template-columns: 1.3fr 1fr; gap: 32px; align-items: start; }
.st-list > .ld-scene:not(:has(.ld-pic)) { grid-template-columns: 1fr; max-width: 720px; }
.st-list > .ld-scene .ld-copy { display: grid; gap: 12px; }
@media (max-width: 880px) {
  .st-hero, .st-list > .ld-scene { grid-template-columns: 1fr; gap: 20px; }
  .st-hero { padding-top: 40px; }
}

/* The camera, only where load.js has run. The stage is pinned behind the words for the whole
   story; the hero and every stop are laid over it. */
.st.st-live .st-stage { display: block; position: sticky; top: 0; height: 100vh; margin-bottom: -100vh; z-index: 0; overflow: hidden; }
.st.st-live .st-canvas { display: block; width: 100%; height: 100%; }
.st.st-live .st-tag { left: 0; top: 0; will-change: transform; }
/* The shade keeps words legible over any part of the drawing: dark behind the cards (left on a
   wide screen, low on a narrow one), and a lid under the bar. */
.st.st-live .st-shade { position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(90deg, rgba(7, 12, 10, 0.92) 0%, rgba(7, 12, 10, 0.78) 34%, rgba(7, 12, 10, 0) 58%),
    linear-gradient(180deg, rgba(7, 12, 10, 0.75) 0%, rgba(7, 12, 10, 0) 16%, rgba(7, 12, 10, 0) 80%, rgba(7, 12, 10, 0.7) 100%); }
.st.st-live .st-hero { position: relative; z-index: 1; min-height: 100vh; grid-template-columns: minmax(0, 560px) 1fr; padding-top: 96px; padding-bottom: 96px; max-width: none; padding-left: max(24px, calc((100vw - 1180px) / 2)); }
.st.st-live .st-hero .hero-grid { display: block; }
/* Each drawing stays in its stop as a point: still laid out, so the browser's lazy loading fetches
   it as the stop nears, and never seen, because the canvas paints it. */
.st.st-live .ld-pic, .st.st-live .st-open { position: absolute; left: 0; top: 0; width: 2px; height: 2px; opacity: 0; overflow: hidden; pointer-events: none; border: 0; aspect-ratio: auto; }
.st.st-live .st-open .ld-src { display: none; }
.st.st-live .st-stops { position: sticky; top: calc(50vh - 190px); z-index: 2; height: 0; padding: 0; display: block; }
.st.st-live .st-stops::before { content: ""; }
.st.st-live .st-stops a { position: relative; left: calc(100vw - 72px); margin-bottom: 10px; background: rgba(7, 12, 10, 0.7); }
.st.st-live .st-list { max-width: none; padding: 0; gap: 0; display: block; }
.st.st-live .st-list > .ld-scene { position: relative; z-index: 1; display: block; min-height: 165vh; padding: 0 max(24px, calc((100vw - 1180px) / 2)); }
.st.st-live .st-list > .ld-scene .ld-copy {
  position: sticky; top: max(88px, calc(50vh - 300px)); width: min(460px, calc(100vw - 120px));
  background: rgba(7, 12, 10, 0.8); border: 1px solid var(--line); border-radius: var(--r-card); padding: 24px 24px 22px;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
.st.st-live .st-list > .ld-scene + .st-list > .ld-scene { margin-top: 0; }
@media (max-width: 999px) {
  .st.st-live .st-shade { background: linear-gradient(180deg, rgba(7, 12, 10, 0.75) 0%, rgba(7, 12, 10, 0) 14%, rgba(7, 12, 10, 0) 38%, rgba(7, 12, 10, 0.92) 62%); }
  .st.st-live .st-hero { grid-template-columns: 1fr; align-content: end; }
  /* The offset is the STOP's padding, not the card's margin: a margin collapses through the stop
     and leaves the card above the screen. */
  .st.st-live .st-list > .ld-scene { padding-top: 95vh; }
  .st.st-live .st-list > .ld-scene .ld-copy { top: auto; bottom: 16px; width: auto; }
  .st.st-live .ld-card { display: none; }
  .st.st-live .st-stops a { left: calc(100vw - 64px); }
}

/* ==========================================================================================
   THE DARK THEME (Mason, 2026-09-24): "Replace all the white with the theme we built for the
   for-yards prototype: near-black page with loam-green panels, the dark engraved drawing style,
   Truck 6 ORANGE as the single accent."

   HOW IT IS DONE, AND WHY HERE. Every page on this side links /tokens.css, then
   /components.css, then this sheet, in that order (for-yards/*.html and for-yards/legal/*.html
   alike). So the whole side turns dark by giving the shared token NAMES dark VALUES in this
   sheet: --white becomes the page's night, --ink the pale text, the tints their dark
   counterparts. /tokens.css and /components.css are not touched (check:for-yards pins both,
   and the buyer tree reads them), and the buyer marketplace stays exactly as it was.
   /claim/ and /portal/ are not in this tree; they get the same values from theme-yard.css.

   THE NAMES NOW LIE ABOUT THEIR VALUES on this side (--white is near-black). That is the price
   of not forking the component sheet, and it is written here so nobody "fixes" it by reading a
   name. A token used BOTH as a text colour and as a fill cannot be both at once on a dark
   ground; each such use is overridden by selector below, under THE DOUBLE-DUTY TOKENS.

   GREEN LEADS, ORANGE IS THE SECOND COLOUR (owner, 2026-09-29: "dark, green, hints of orange and
   lighter stuff", then "I still want orange to be apart of it, dont just completely remove it").
   Every action is --go, a bright green with near-black type (8.23:1). Orange marks what to look at:
   the kickers, the stressed words in a headline, the current stop and its facts, the featured
   price card, and Truck 6 (its tag on the pictures, its dot on the rail, its row in the drawn app
   card). --orange-2 as text on the night is 8.4:1 (6.0:1 on the loam panels). Lighter text is --leaf. The blue of the light
   theme is retired on this side.
   Every text/ground pair was measured to WCAG AA (4.5:1 text, 3:1 large text and controls);
   the table is in docs/for-yards/SCROLL-STORY-2026-09-24.md.
   ========================================================================================== */
:root {
  color-scheme: dark;
  --night: #070c0a; --night-2: #0c1511; --loam: #123123; --loam-2: #173a29; --line: #2e5240;
  --leaf: #8fd0a9; --orange: #f2711c; --orange-2: #ff8a3d; --orange-ink: #14181a;
  --go: #4cbf7a; --go-2: #6fd395; --go-ink: #06120b;

  --white: var(--night);
  --band: var(--night-2);
  --band-2: var(--loam);
  --ink: #eef3ef;
  --muted: #b3c6bb;
  --faint: #93a99c;
  --border: #61826f;
  --rule: #2e5240;
  --rule-soft: #18291f;

  --green-900: #123123;
  --green-800: #173a29;
  --green-700: var(--leaf);
  --green-600: #2e7d4f;
  --green-500: #8fd0a9;
  --green-100: #1d3f2c;
  --green-50: #0f1d16;

  --blue-800: var(--leaf);
  --blue-600: var(--leaf);
  --blue-400: var(--leaf);
  --blue-100: #1d3f2c;
  --blue-50: #0f1d16;

  --amber-600: #f5b25b;
  --amber-100: #2e2210;
  --red-600: #ff9d8f;
  --red-100: #2d1411;
}
body { background: var(--night); color: var(--ink); }
::selection { background: var(--go); color: var(--go-ink); }

/* The bar: a dark glass over the drawings instead of a white one. */
.nav { background: rgba(7, 12, 10, 0.86); border-bottom-color: var(--line); }
.nav nav > a { color: var(--muted); }
.nav nav > a:hover { color: var(--ink); background: var(--green-50); }
.nav nav > a.active, .nav nav > a[aria-current] { color: var(--ink); background: var(--loam-2); }
.nav .cta, .nav .cta:hover { background: var(--go); color: var(--go-ink); }
.nav .cta:hover { background: var(--go-2); }
.nav-toggle { border-color: var(--line); }
.nav-sidebar { background: var(--night-2); }
.nav-sidebar a { color: var(--ink); border-bottom-color: var(--line); }
.nav-sidebar a.cta { color: var(--leaf); }
/* The wordmark is drawn for a white ground; on the night it is lifted to the page's text
   colour. One filter over the whole lockup, so the brand's own file is not forked. */
.brand-mark, .foot-mark { filter: brightness(0) invert(0.94); }

/* THE DOUBLE-DUTY TOKENS. --green-700 is link-and-hover TEXT in the component sheet and a hover
   FILL under white text; on the night the text reading wins (leaf), and each fill is set here. */
.btn-primary, .btn-amber { background: var(--go); color: var(--go-ink); box-shadow: 0 8px 22px rgba(76, 191, 122, 0.22); }
.btn-primary:hover, .btn-amber:hover { background: var(--go-2); color: var(--go-ink); box-shadow: 0 12px 30px rgba(76, 191, 122, 0.3); }
.btn-ghost { background: transparent; color: var(--ink); border-color: var(--border); }
.btn-ghost:hover { background: var(--green-50); border-color: var(--leaf); color: var(--ink); }
.submit { background: var(--go); color: var(--go-ink); }
.submit:hover { background: var(--go-2); }
.pb-pile-go { color: var(--go-ink); background: var(--go); }
.pb-pile-go:hover { background: var(--go-2); }
input, select, textarea { color: var(--ink); background: var(--night-2); border-color: var(--border); }
input::placeholder, textarea::placeholder { color: var(--faint); opacity: 1; }
.form-result.ok { background: var(--loam); border-color: var(--leaf); color: var(--ink); }
.form-result.err { background: var(--red-100); border-color: #7a3027; color: var(--red-600); }
.kicker, .ld-eyebrow, .band .kicker { color: var(--orange-2); }

/* The light theme's washes (green-50 into white, a blue glow) become the night with a loam glow:
   the same shape of light, on the dark. */
.hero { background: radial-gradient(1100px 560px at 76% -14%, rgba(46, 125, 79, 0.22), transparent 62%), var(--night); }
.page-hero { background: radial-gradient(900px 420px at 50% -20%, rgba(46, 125, 79, 0.24), transparent 70%), var(--night); color: var(--ink); }
.contact { background: linear-gradient(180deg, var(--night-2), var(--night)); border-top-color: var(--line); }
.band { background: linear-gradient(180deg, var(--loam), #0d2419); }
.hero h1 .grad, .grad { background: none; -webkit-background-clip: border-box; background-clip: border-box; color: var(--orange-2); animation: none; }
.statement h2 em { color: var(--orange-2); }

/* Panels: loam on the night, lines one step lighter, so a card reads as a surface. */
.module, .step, .proof-card, .price-card, .objection-half, .qcard, .fig-frame, .costcalc fieldset {
  background: var(--loam); border-color: var(--line);
}
.price-card.featured { border-color: var(--orange); box-shadow: 0 0 0 1px var(--orange) inset; }
.price-flag { background: var(--orange); color: var(--orange-ink); }
.module li::marker { color: var(--leaf); }
.card:hover, .module:hover, .step:hover, .proof-card:hover { border-color: var(--leaf); box-shadow: 0 14px 34px rgba(0, 0, 0, 0.4); }
.qcard { border-left-color: var(--go); }
.qcard .badge, .mockbtn { background: var(--go); color: var(--go-ink); }
.qcard .badge.navy { background: var(--leaf); color: var(--orange-ink); }
.qcard .badge.done { background: var(--night); color: var(--ink); }
.phone { border-color: var(--line); background: var(--night-2); box-shadow: 0 18px 44px rgba(0, 0, 0, 0.5); }
.tier-table th, .tier-table td { border-color: var(--line); }
.tier-table thead th { background: var(--loam); color: var(--ink); }
footer { background: #050907; color: var(--muted); border-top: 1px solid var(--line); }
footer a, footer .legal-links a { color: var(--ink); }
footer .fine, footer .privacy-line, footer .affil-line, footer .legal-links { color: var(--faint); }

/* The drawn SVG figures carry their light-theme colours as presentation attributes; a CSS rule
   outranks an attribute, so the drawings are recoloured here without touching their markup. */
.rail-wrap svg line { stroke: var(--line); }
.rail-wrap svg circle { fill: var(--orange); }
.rail-wrap svg text { fill: var(--muted); }
.rail-ticket rect:first-child { fill: var(--loam); stroke: var(--leaf); }
.rail-ticket rect:not(:first-child) { fill: var(--leaf); }
.rail-ticket path { stroke: var(--leaf); }
.fig svg [fill="#ffffff"], .fig svg [fill="#fff"] { fill: var(--loam); }
.fig svg [fill="#14181a"], .fig svg [fill="#55625b"] { fill: var(--muted); }
.fig svg [stroke="#dce3de"], .fig svg [stroke="#e8edea"] { stroke: var(--line); }
/* The connector page's code blocks fill with --ink in its own <style>, which follows this sheet;
   one class of extra weight puts them on a well darker than the page instead of the pale ink. */
body .doc pre { background: #030605; border: 1px solid var(--line); }
/* The current page's pill must not repaint the one green button (start.html is its own CTA). */
.nav .cta.active, .nav .cta[aria-current] { background: var(--go); color: var(--go-ink); }
/* On a wide screen the drawn app card leaves the text column and sits over the scene, to the
   right of the words, the way the app itself would sit on a counter in that yard. */
@media (min-width: 1000px) {
  .st.st-live .st-list > .ld-scene .ld-card { position: absolute; left: calc(100% + 28px); bottom: 0; width: 340px; margin: 0; box-shadow: 0 18px 40px rgba(0, 0, 0, 0.55); }
}
/* The phone's sticky buttons: the dark glass of the bar, and the one green action. */
.mobile-cta { background: rgba(7, 12, 10, 0.9); border-top-color: var(--line); }
.mobile-cta .m-primary { background: var(--go); color: var(--go-ink); }
.mobile-cta .m-secondary { color: var(--ink); background: transparent; border-color: var(--border); }
/* The four legal pages share a <style> block that still carries the legal index's "live" tag
   (.legal-index .tag.live: --green-800 on --green-100, 1.1:1 on the dark side). No for-yards
   legal page draws it today; it is repainted here so it cannot come back unreadable, and the
   legal pages' own files stay untouched (CSS only, as the brief requires).
body .legal-index .tag.live { background: #1d3f2c; color: var(--leaf); }
/* A form field sits one step lighter than the page: components.css's `form input` outranks a bare
   `input`, so the well is set at the same weight here. */
form input, form select, form textarea { background: var(--night-2); }
/* The calculator's legends wrap at phone width, and a wrapped legend rides above its fieldset's
   border instead of on it; there it is taken out of the border and set as the box's first line. */
@media (max-width: 600px) {
  .costcalc legend { float: left; width: 100%; padding: 0 0 10px; font-size: 16px; line-height: 1.3; }
  .costcalc legend + * { clear: both; }
}
/* The FAQ's expand glyph is the only cue a row opens: leaf, not the fill green (3.9:1 there). */
body .faq-wrap summary::after { color: var(--leaf); }
/* The hero's aurora still carried the light theme's blue; on the dark side it is the loam glow only. */
.hero::before { background: radial-gradient(560px 320px at 22% 30%, rgba(46, 125, 79, 0.12), transparent 65%); }
/* THE SCREENS, RUNNING (product.html, 2026-09-29): five real captures of the yard app on the night.
   A light screen on the dark page, framed like a monitor bezel; the caption says it is a screenshot
   with demo data (check:for-yards-claims holds the words), and nothing moves. */
.shots { max-width: 1240px; margin: 0 auto; padding: 8px 24px 56px; }
.shots h2 { color: var(--ink); font-size: clamp(26px, 3vw, 36px); letter-spacing: -0.02em; margin: 0 0 6px; }
.shots-sub { color: var(--muted); margin: 0 0 22px; max-width: 62ch; }
.shots-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px; align-items: start; }
.screen-card { margin: 0; display: grid; gap: 10px; }
.screen-card img { display: block; width: 100%; height: auto; border-radius: var(--r-card); border: 1px solid var(--line); background: #f4f6f3; box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45); }
.screen-card figcaption { color: var(--muted); font-size: 14px; line-height: 1.5; }
.screen-card .shot-note { color: var(--faint); font-size: 12.5px; }
.screen-card a { display: inline-flex; align-items: center; min-height: 44px; color: var(--leaf); font-weight: 700; white-space: nowrap; }
.shot-wide { grid-column: 1 / -1; }
.shot-phone { justify-self: center; max-width: 360px; }
@media (max-width: 880px) {
  .shots-grid { grid-template-columns: 1fr; }
  .shot-phone { max-width: 70vw; }
}

