/* =============================================================================
   Dental Deals — v2 · SHARED PAGE BLOCKS
   -----------------------------------------------------------------------------
   Ports to: app/assets/stylesheets/marketing/pages.css

   Blocks used by more than one page. If a pattern appears twice it belongs
   here; if it appears once it belongs in pages/<page>.css.

   .page-hero  .feat-row  .bene  .artifact  .process-step  .page-cta
   .final-cta  .cta-stat  .trust  .legal-page  .qa-grid  .form-status
   ============================================================================= */

/* ═══ PAGE HERO — every non-landing page opens with this ════════════════════
   The pre-v2 hero carried a navy radial wash and a bottom hairline. v2 canvas
   is flat white and cards float against it, so both are gone. */
.page-hero { padding: var(--sp-22) 0 var(--sp-18); background: var(--canvas); }
.page-hero .eyebrow { margin-bottom: var(--sp-5); }
.page-hero h1 { margin: 0 0 var(--sp-5); }
.page-hero .lede-lg, .page-hero .lede { margin-bottom: var(--sp-8); }
.page-hero__ctas { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
/* Muted note under a hero CTA row. Replaces a "text-sm text-base-500 mt-3"
   utility cluster in the brokers view, which resolved to nothing: this
   layout never loads the Tailwind build. */
.page-hero__note { margin-top: var(--sp-3); font-size: var(--e-meta); color: var(--text-tertiary); }

/* ── Section-head rhythm ────────────────────────────────────────────────────
   Eyebrow to heading, heading to lede. This was 41 inline margin-top styles
   spread across 10 views, and they had drifted: .lede-lg carried 20px in three
   places and 16px in two. Normalised to 20px (the majority) and stated once as
   sibling rules, so the rhythm is a property of the components rather than
   something each view re-declares. */
.eyebrow + .display,
.eyebrow + .display-md   { margin-top: var(--sp-4); }
.eyebrow + .display-sm   { margin-top: var(--sp-3); }
.display + .lede,    .display + .lede-lg,
.display-md + .lede, .display-md + .lede-lg,
.display-sm + .lede, .display-sm + .lede-lg { margin-top: var(--sp-5); }

/* A section head that sits directly on its content, with no gap. */
.section-head--flush { margin-bottom: 0; }
.section-head--tight { margin-bottom: var(--sp-8); }

/* ── Artifact internals that had no class ──────────────────────────────────
   The home page's three how-it-works mock cards and the broker pipeline card
   were hand-styled with 20 inline declarations between them. Everything below
   already had a v2 counterpart except these five, which round out the family
   so the markup can drop the style attributes entirely. */
.artifact__row-body   { flex: 1; min-width: 0; }
.artifact__row-title  { font-size: var(--e-micro); font-weight: var(--fw-bold); color: var(--text); }
.artifact__ok         { color: var(--status-success-text); font-weight: var(--fw-bold); }
.artifact__unit       { font-size: var(--e-meta); font-weight: var(--fw-medium); color: var(--text-tertiary); }
.artifact__eyebrow--spaced  { display: block; margin-bottom: var(--sp-4); }
.artifact__progress-label   { display: flex; justify-content: space-between; margin-bottom: var(--sp-2); }
.artifact__stats--spaced    { margin-top: var(--sp-4); }
.btn-row--spaced      { margin-top: var(--sp-8); }

/* Remaining artifact internals, from the brokers and buyers feature-row mocks. */
.artifact__filters    { display: flex; gap: var(--sp-2); flex-wrap: wrap; margin-bottom: var(--sp-3); }
.artifact__chip       { display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px;
                        border-radius: var(--radius-pill); font-size: var(--e-eyebrow);
                        font-weight: var(--fw-semibold); background: var(--gray-50); color: var(--text-secondary); }
.artifact__chip--on   { background: var(--brand-50); color: var(--brand-700); }
.artifact__divider    { border-top: var(--border-hairline) solid var(--hairline); padding-top: var(--sp-4); }
.artifact__kv         { display: flex; justify-content: space-between; padding: 10px 0; font-size: var(--e-meta); }
.artifact__kv + .artifact__kv { border-top: var(--border-hairline) solid var(--hairline); }
.artifact__kv b, .artifact__kv .tnum { font-weight: var(--fw-bold); }
.artifact__tile       { width: 32px; height: 32px; flex: 0 0 auto; border-radius: var(--radius-input);
                        background: var(--surface-ink); color: var(--text-on-ink);
                        display: flex; align-items: center; justify-content: center;
                        font-size: var(--e-eyebrow); font-weight: var(--fw-bold); }
.artifact__name       { font-weight: var(--fw-bold); font-size: var(--e-meta); }
.artifact__value      { font-family: var(--font-display); font-size: var(--e-num-md); font-weight: var(--fw-bold);
                        letter-spacing: var(--tk-tight); color: var(--text); font-variant-numeric: tabular-nums;
                        line-height: var(--lh-flat); }
.artifact__note       { font-size: var(--e-meta); color: var(--text-secondary); margin-bottom: var(--sp-5); }
.artifact--quiet      { background: var(--gray-50); }
.artifact--tight      { padding: var(--sp-5); }
.section-head--wide-sm { max-width: 680px; }
.section-head--wide-md { max-width: 760px; }
.page-hero__note--tight { margin-top: var(--sp-3); }
.artifact__push { margin-left: auto; }
.artifact__kv-v { font-weight: var(--fw-bold); }
.artifact__row--divided { display: flex; align-items: center; gap: var(--sp-3); padding-bottom: var(--sp-4);
                          border-bottom: var(--border-hairline) solid var(--hairline); margin-bottom: var(--sp-4); }
.artifact__meta       { font-size: var(--e-eyebrow); color: var(--text-tertiary); }
.artifact__foot--tight { margin-top: 0; padding-top: var(--sp-4); }
.wrap--narrow-md      { max-width: 820px; }
.u-center             { text-align: center; }
.u-center-row         { display: flex; justify-content: center; margin-top: var(--sp-10); }
.section-head--tight-sm { margin-bottom: var(--sp-6); }
.section-head--tight-xs { margin-bottom: var(--sp-4); }
.feat-row--tight-top  { padding-top: var(--sp-12); }
.split-head           { display: flex; justify-content: space-between; align-items: flex-end; }
.chip-row             { display: flex; gap: var(--sp-3); flex-wrap: wrap; }
.legal-note           { text-align: center; font-size: var(--e-meta); color: var(--text-tertiary); }
.eyebrow--center      { display: inline-flex; justify-content: center; }
.mono-inline          { background: rgba(255, 255, 255, 0.1); color: var(--text-on-ink); }
.comp-wrap            { padding: 0; }
/* Delta cells in the market tables: direction is data, so it is a class, not an
   inline colour computed in the view. */
.n--up   { color: var(--status-success-text); }
.n--down { color: var(--status-danger-text); }
.page-hero__note a { color: var(--text-accent); text-decoration: underline; text-underline-offset: 2px; }
.page-hero__note { margin: var(--sp-4) 0 0; font-size: var(--e-meta); color: var(--text-tertiary); }
.page-hero__note a { text-decoration: underline; text-underline-offset: 2px; }
.page-hero__grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-16); align-items: center; }
.page-hero--center { text-align: center; }
.page-hero--center .lede-lg, .page-hero--center .lede { margin-inline: auto; }
.page-hero--center .page-hero__ctas { justify-content: center; }
.page-hero--narrow { max-width: 860px; }

@media (max-width: 1023px) {
  .page-hero { padding: var(--sp-16) 0 var(--sp-12); }
  .page-hero__grid { grid-template-columns: minmax(0, 1fr); gap: var(--sp-10); }
}
@media (max-width: 640px) {
  .page-hero { padding: var(--sp-10) 0 var(--sp-10); }
  .page-hero .lede-lg, .page-hero .lede { font-size: var(--e-body); margin-bottom: var(--sp-6); }
}

/* ═══ FEATURE ROW ═══════════════════════════════════════════════════════════ */
.feat-row { padding: var(--sp-22) 0; }
.feat-row--quiet { background: var(--surface-quiet); }
.feat-row--tight { padding: var(--sp-14) 0; }
.feat-row__grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-18); align-items: center; }
.feat-row__grid--rev > :first-child { order: 2; }
.feat-row__copy h2 { margin: var(--sp-3) 0 var(--sp-4); font-family: var(--font-display); font-size: var(--e-subsection-size); line-height: var(--e-subsection-lh); letter-spacing: var(--e-subsection-tk); font-weight: var(--fw-bold); color: var(--text); text-wrap: balance; }
.feat-row__copy p { margin: 0 0 var(--sp-6); font-size: var(--e-body); line-height: 1.6; color: var(--text-secondary); max-width: 480px; text-wrap: pretty; }
.feat-row__list { list-style: none; padding: 0; margin: 0 0 var(--sp-7); }
.feat-row__list li { display: flex; align-items: flex-start; gap: var(--sp-3); padding: 13px 0; border-bottom: var(--border-hairline) solid var(--hairline); font-size: var(--e-body-sm); font-weight: var(--fw-medium); color: var(--text); }
.feat-row__list li:last-child { border-bottom: 0; }
.feat-row__list .ico { --ico: 18px; color: var(--brand-500); margin-top: 1px; }

@media (max-width: 1023px) {
  .feat-row { padding: var(--sp-16) 0; }
  .feat-row__grid { grid-template-columns: minmax(0, 1fr); gap: var(--sp-10); }
  .feat-row__grid--rev > :first-child { order: 0; }
}
@media (max-width: 640px) { .feat-row { padding: var(--sp-12) 0; } }

/* ═══ BENEFIT CARDS — 3-up ══════════════════════════════════════════════════
   Was a 1px-gap grid faking hairline dividers over a border-colour background.
   v2 has no dividers: three cards, real gap, floating on the shadow. */
.bene-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-5); margin-top: var(--sp-12); }
.bene { background: var(--surface); border-radius: var(--radius-card); box-shadow: var(--shadow-card); padding: var(--sp-7); }
.bene__icon { display: flex; align-items: center; justify-content: center; width: 42px; height: 42px; border-radius: var(--radius-input); background: var(--brand-50); color: var(--brand-700); margin-bottom: var(--sp-5); }
.bene__icon .ico { --ico: 21px; }
.bene h3 { margin: 0 0 10px; font-family: var(--font-display); font-size: var(--e-card-title); font-weight: var(--fw-bold); letter-spacing: var(--tk-snug); color: var(--text); }
.bene p { margin: 0; font-size: var(--e-body-sm); line-height: 1.6; color: var(--text-secondary); text-wrap: pretty; }
@media (max-width: 1023px) { .bene-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px)  { .bene-grid { grid-template-columns: minmax(0, 1fr); margin-top: var(--sp-8); } .bene { padding: var(--sp-6); } }

/* ═══ ARTIFACT — the in-page product mock ═══════════════════════════════════
   These carry the compact PRODUCT scale, not the editorial one: they are
   pictures of the dashboards, and a 16px body inside one would misrepresent it. */
.artifact { background: var(--surface); border-radius: var(--radius-card); box-shadow: var(--shadow-card); padding: var(--sp-6); }
.artifact--flush { padding: 0; overflow: hidden; }
.artifact--quiet { background: var(--surface-quiet); box-shadow: none; }
.artifact__eyebrow { font-size: var(--text-xs); font-weight: var(--fw-semibold); letter-spacing: var(--tk-eyebrow); text-transform: uppercase; color: var(--text-tertiary); }
.artifact__head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); margin-bottom: var(--sp-4); }
.artifact__title { font-family: var(--font-display); font-size: var(--text-2xl); font-weight: var(--fw-bold); letter-spacing: var(--tk-tight); color: var(--text); }
.artifact__sub { font-size: var(--text-base); color: var(--text-tertiary); margin-top: 3px; }
.artifact__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); margin-top: var(--sp-5); padding-top: var(--sp-4); border-top: var(--border-hairline) solid var(--hairline); font-size: var(--text-sm); color: var(--text-tertiary); }
.artifact__stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-2); }
/* Promoted out of pages/sellers.css: the buyers page renders the same listing
   preview, so the spacing variant belongs beside the component, not on one page. */
.listing-preview__stats { margin: var(--sp-5) 0; }
.artifact__stat { border-radius: var(--radius-input); background: var(--gray-50); padding: 11px 13px; }
.artifact__stat .k { font-size: var(--text-2xs); font-weight: var(--fw-semibold); letter-spacing: var(--tk-wide); text-transform: uppercase; color: var(--text-tertiary); margin-bottom: 4px; }
.artifact__stat .v { font-family: var(--font-display); font-size: var(--text-xl); font-weight: var(--fw-bold); letter-spacing: var(--tk-tight); color: var(--text); font-variant-numeric: tabular-nums; }
.artifact__row { display: flex; align-items: center; gap: 11px; }
.artifact__initials { width: 30px; height: 30px; flex: 0 0 auto; border-radius: var(--radius-pill); background: var(--brand-50); color: var(--brand-700); display: flex; align-items: center; justify-content: center; font-size: var(--text-xs); font-weight: var(--fw-bold); }
.artifact__progress { height: 6px; border-radius: var(--radius-pill); background: var(--gray-200); overflow: hidden; }
.artifact__progress > span { display: block; height: 100%; background: var(--brand-500); }
.artifact__progress span { display: block; height: 100%; border-radius: var(--radius-pill); background: var(--brand-500); }

/* ═══ PROCESS STEPS ═════════════════════════════════════════════════════════ */
.process-steps { list-style: none; padding: 0; margin: 0; }
.process-step { display: flex; gap: var(--sp-5); padding: var(--sp-5) 0; border-top: var(--border-hairline) solid var(--hairline); }
.process-step:last-child { border-bottom: var(--border-hairline) solid var(--hairline); }
.process-step__num { display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; flex: 0 0 auto; border-radius: var(--radius-pill); background: var(--brand-50); color: var(--brand-700); font-family: var(--font-display); font-size: var(--e-meta); font-weight: var(--fw-bold); }
.process-step__title { margin-bottom: 4px; font-family: var(--font-display); font-size: var(--e-card-title-sm); font-weight: var(--fw-semibold); letter-spacing: var(--tk-snug); color: var(--text); }
.process-step__body { font-size: var(--e-body-sm); line-height: 1.6; color: var(--text-secondary); text-wrap: pretty; }

/* ═══ PAGE CTA — ink band, split copy + stats ═══════════════════════════════ */
.page-cta { padding: var(--sp-26) 0; }
.page-cta__inner { display: grid; grid-template-columns: 1.15fr 1fr; gap: var(--sp-12); align-items: center; background: var(--surface-ink); color: var(--text-on-ink); border-radius: var(--sp-5); padding: var(--sp-18) var(--sp-14); }
.page-cta h2 { margin: 0 0 var(--sp-4); font-family: var(--font-display); font-size: var(--e-section-size); line-height: var(--e-section-lh); letter-spacing: var(--e-section-tk); font-weight: var(--fw-bold); color: var(--text-on-ink); text-wrap: balance; }
.page-cta p { margin: 0 0 var(--sp-6); font-size: var(--e-lede); line-height: var(--lh-body); color: var(--text-on-ink-soft); max-width: 520px; text-wrap: pretty; }
.cta-stat-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--sp-3); }
/* Phone: one column. Two columns leave ~160px per card, which the legacy
   .stat card's content overruns. minmax(0, …) above stops `1fr` flooring at
   min-content and pushing the grid past the viewport. */
@media (max-width: 640px) { .cta-stat-grid { grid-template-columns: minmax(0, 1fr); } }
.cta-stat { border-radius: var(--radius-tile); background: rgba(255, 255, 255, 0.05); padding: var(--sp-5); }
.cta-stat__k { font-size: var(--e-eyebrow); font-weight: var(--fw-semibold); letter-spacing: var(--tk-eyebrow); text-transform: uppercase; color: var(--text-on-ink-quiet); margin-bottom: var(--sp-2); }
.cta-stat__v { font-family: var(--font-display); font-size: var(--e-stat-lg); font-weight: var(--fw-bold); letter-spacing: var(--tk-tight); color: var(--text-on-ink); font-variant-numeric: tabular-nums; line-height: var(--lh-tight); }
.cta-stat__d { margin-top: 5px; font-size: var(--e-micro); font-weight: var(--fw-medium); color: var(--text-on-ink-quiet); }
.cta-stat__d--up { color: var(--brand-400); }
.cta-stat__d--down { color: var(--coral-500); }
@media (max-width: 1023px) {
  .page-cta { padding: var(--sp-16) 0; }
  .page-cta__inner { grid-template-columns: minmax(0, 1fr); padding: var(--sp-12) var(--sp-8); gap: var(--sp-8); }
}
@media (max-width: 640px) {
  .page-cta__inner { padding: var(--sp-8) var(--sp-5); border-radius: var(--radius-card); }
  .cta-stat { padding: var(--sp-4); }
  .cta-stat__v { font-size: var(--e-heading-lg); }
}

/* ═══ FINAL CTA — centred ink band ═════════════════════════════════════════ */
.final-cta { padding: var(--sp-26) 0; }
.final-cta__inner { background: var(--surface-ink); color: var(--text-on-ink); border-radius: var(--sp-5); padding: var(--sp-22) var(--sp-14); text-align: center; }
.final-cta h2 { margin: 0 0 var(--sp-4); font-family: var(--font-display); font-size: var(--e-section-size); line-height: var(--e-section-lh); letter-spacing: var(--e-section-tk); font-weight: var(--fw-bold); color: var(--text-on-ink); text-wrap: balance; }
.final-cta p { margin: 0 auto var(--sp-8); max-width: 620px; font-size: var(--e-lede); line-height: var(--lh-body); color: var(--text-on-ink-soft); text-wrap: pretty; }
@media (max-width: 1023px) { .final-cta { padding: var(--sp-16) 0; } .final-cta__inner { padding: var(--sp-14) var(--sp-8); } }
@media (max-width: 640px)  { .final-cta__inner { padding: var(--sp-10) var(--sp-5); border-radius: var(--radius-card); } }

/* ═══ TRUST STRIP ═══════════════════════════════════════════════════════════ */
.trust { padding: var(--sp-12) 0; background: var(--surface-quiet); }
.trust__inner { display: flex; align-items: center; gap: var(--sp-10); flex-wrap: wrap; }
.trust__label { flex: 0 0 auto; font-size: var(--e-eyebrow); font-weight: var(--fw-semibold); letter-spacing: var(--tk-eyebrow); text-transform: uppercase; color: var(--text-tertiary); }
.trust__names { display: flex; flex-wrap: wrap; gap: var(--sp-4) var(--sp-10); }
.trust__names span { font-family: var(--font-display); font-size: var(--e-card-title-sm); font-weight: var(--fw-semibold); letter-spacing: var(--tk-snug); color: var(--text-tertiary); }
@media (max-width: 767px) {
  .trust { padding: var(--sp-8) 0; }
  .trust__inner { flex-direction: column; align-items: flex-start; gap: var(--sp-4); }
  .trust__names { gap: var(--sp-3) var(--sp-6); }
  .trust__names span { font-size: var(--e-body-sm); }
}

/* ═══ Q&A GRID — pricing / contact "fine print" ════════════════════════════ */
.qa-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-6); }
.qa { background: var(--surface); border-radius: var(--radius-tile); box-shadow: var(--shadow-card); padding: var(--sp-6); }
.qa__q { margin-bottom: 8px; font-family: var(--font-display); font-size: var(--e-card-title-sm); font-weight: var(--fw-semibold); letter-spacing: var(--tk-snug); color: var(--text); }
.qa__a { font-size: var(--e-body-sm); line-height: 1.65; color: var(--text-secondary); text-wrap: pretty; }
.qa__a a { text-decoration: underline; text-underline-offset: 2px; }
@media (max-width: 767px) { .qa-grid { grid-template-columns: minmax(0, 1fr); gap: var(--sp-3); } }

/* ═══ FORM STATUS — submitting / success, shared by contact + newsletter ════ */
.form-status { display: none; }
.form-status.is-on { display: flex; }
.spinner { width: 16px; height: 16px; flex: 0 0 auto; border-radius: var(--radius-pill); border: 2px solid currentColor; border-top-color: transparent; animation: spin 700ms linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
form.is-submitting .btn[type="submit"] { pointer-events: none; opacity: 0.7; }

/* ═══ LEGAL / DOCUMENT ARCHETYPE ═══════════════════════════════════════════
   Terms, Privacy, the broker confidentiality agreement, 404 and 500.
   The same partials render inside a compact modal elsewhere in the app, so the
   heading hierarchy has to stay sane at both sizes: h2/h3 are sized in rem-free
   role tokens and never depend on the page width. */
/* Rebranded 2026-09-04. A reading DOCUMENT, not a band stack: /terms, /privacy
   and the broker confidentiality agreement are read top to bottom in one
   column, and alternating bone and ink behind a legal text would make it harder
   to read, not more on-brand. So these take the design's type, ink and
   hairlines and none of its band rhythm. The page's own .da-sec supplies the
   bone ground and the vertical padding. */
/* The block owns the MEASURE, and the header and the body both read it. Set
   here rather than on each so a page can narrow or widen its own document with
   one override, and so the two cannot drift.

   They did drift: the header carried no max-width at all, so its bottom
   hairline ran the full 924px of .da-wrap--narrow while the text under it was
   68ch. Measured at 227px of overshoot on /privacy, which reads as a rule
   belonging to the page rather than to the document. */
/* A LENGTH, not var(--measure-legal). That token is 68ch, and `ch` resolves
   against the USING element's own font-size, so one custom property handed to
   two elements at different sizes produces two different widths: the header
   (whose type is the h1's) came out 686px against the body's 697px, an 11px
   step that is exactly visible on a hairline. A custom property is substituted
   as a token and resolved at each use, so it cannot carry a shared ch measure.

   43.5rem is 696px, which is what 68ch resolves to at the body's 15px Satoshi.
   The intent is unchanged (about 68 characters of body text); it is just
   resolved once here instead of twice, differently, downstream. */
.legal-page { --legal-measure: 43.5rem; }

.legal-page__header {
  max-width: var(--legal-measure);
  padding-bottom: var(--sp-7);
  margin-bottom: var(--sp-10);
  border-bottom: var(--border-hairline) solid var(--da-sec-hairline);
}
.legal-page__header h1 { margin: 0; }
.legal-page__updated {
  margin: var(--sp-4) 0 0;
  font-family: var(--font-brand);
  font-size: var(--e-meta);
  color: var(--da-sec-muted);
}
.legal-page__intro { margin: var(--sp-6) 0 0; padding: var(--sp-5) var(--sp-6); border-radius: var(--radius-tile); background: var(--surface-tint); color: var(--brand-700); font-size: var(--e-body-sm); line-height: 1.65; max-width: var(--measure-legal); text-wrap: pretty; }
.legal-page__intro strong { font-weight: var(--fw-semibold); }

.legal-toc { margin-bottom: var(--sp-12); padding: var(--sp-6); border-radius: var(--radius-tile); background: var(--gray-50); }
.legal-toc h2 { margin: 0 0 var(--sp-4); font-family: var(--font-sans); font-size: var(--e-eyebrow); font-weight: var(--fw-semibold); letter-spacing: var(--tk-eyebrow); text-transform: uppercase; color: var(--text-tertiary); }
.legal-toc ol { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 7px var(--sp-6); counter-reset: toc; }
.legal-toc li { counter-increment: toc; font-size: var(--e-meta); }
.legal-toc a { display: flex; gap: 9px; color: var(--text-secondary); }
.legal-toc a::before { content: counter(toc) "."; color: var(--text-quaternary); font-variant-numeric: tabular-nums; min-width: 16px; }
.legal-toc a:hover { color: var(--brand-700); }
@media (max-width: 640px) { .legal-toc ol { grid-template-columns: 1fr; } }

.legal-body { max-width: var(--measure-legal); font-size: var(--e-body-sm); line-height: 1.7; color: var(--text-secondary); }
.legal-body > * + * { margin-top: var(--sp-4); }
.legal-body h2 { margin: var(--sp-12) 0 var(--sp-4); font-family: var(--font-display); font-size: var(--e-heading-lg); font-weight: var(--fw-bold); letter-spacing: var(--tk-tight); line-height: var(--lh-snug); color: var(--text); scroll-margin-top: 96px; }
.legal-body h2:first-child { margin-top: 0; }
.legal-body h3 { margin: var(--sp-8) 0 var(--sp-3); font-family: var(--font-display); font-size: var(--e-card-title-sm); font-weight: var(--fw-semibold); letter-spacing: var(--tk-snug); color: var(--text); }
.legal-body p { margin: 0; text-wrap: pretty; }
.legal-body ul, .legal-body ol { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 7px; }
.legal-body li::marker { color: var(--text-quaternary); }
.legal-body a { color: var(--brand-700); text-decoration: underline; text-underline-offset: 2px; }
.legal-body strong { color: var(--text); font-weight: var(--fw-semibold); }
.legal-body .note { margin-top: var(--sp-6); }
.legal-body__meta { margin-top: var(--sp-12); padding-top: var(--sp-6); border-top: var(--border-hairline) solid var(--hairline); font-size: var(--e-micro); color: var(--text-tertiary); }
@media (max-width: 640px) {
  .legal-page__header { margin-bottom: var(--sp-7); }
  .legal-body h2 { margin-top: var(--sp-10); font-size: var(--e-heading-md); }
}

/* ═══ COMPARISON TABLE — tables become cards on phones ═════════════════════ */
.dtable-wrap { border-radius: var(--radius-card); background: var(--surface); box-shadow: var(--shadow-card); overflow: hidden; }
.dtable { width: 100%; border-collapse: collapse; font-size: var(--e-body-sm); }
.dtable th, .dtable td { padding: 15px var(--sp-5); text-align: left; }
.dtable thead th { font-family: var(--font-sans); font-size: var(--e-eyebrow); font-weight: var(--fw-semibold); letter-spacing: var(--tk-eyebrow); text-transform: uppercase; color: var(--text-tertiary); white-space: nowrap; }
.dtable tbody tr { border-top: var(--border-hairline) solid var(--hairline); }
.dtable tbody td:first-child { font-weight: var(--fw-medium); color: var(--text); }
.dtable .n { text-align: right; font-variant-numeric: tabular-nums; color: var(--text-secondary); white-space: nowrap; }
.dtable th.n { text-align: right; }
.dtable .up { color: var(--status-success-text); font-weight: var(--fw-semibold); }
.dtable .down { color: var(--status-danger-text); font-weight: var(--fw-semibold); }
.dtable tbody tr:hover { background: var(--row-hover); }

/* ═══ SCROLL REVEAL ════════════════════════════════════════════════════════
   Content arrives as it is scrolled to. marketing/site.js adds .is-in the first
   time an element crosses into view, once, then stops observing it.

   ── THE GATE, and why the old failsafe had to go ──────────────────────────
   Nothing here hides anything unless `.da-motion` is on <html>. That class is
   added by an inline script in the marketing layout's head, before first paint,
   and REMOVED again if marketing/site.js has not reported in within 2.5s.

   The previous design instead gave every hidden element a CSS animation with a
   1600ms delay, as insurance against site.js dying and leaving a blank page. It
   worked as insurance and destroyed the feature: a blanket timer un-hides the
   WHOLE PAGE 1.6s after load, scrolled to or not. Measured on /sellers: 27 bands
   below the fold, all 27 still hidden at 1200ms, all 27 visible at 2000ms with no
   scrolling at all. So a reader who looked at the hero for two seconds before
   scrolling saw no motion anywhere, which is exactly what was reported.

   A class gate has the same insurance with none of that cost:

     JS disabled          the class is never added, nothing is ever hidden
     site.js fails        the class is removed at 2.5s, everything renders
     site.js alive        the class stays, and reveals keep working all the way
                          down the page for as long as the reader scrolls

   Release by REMOVING A ROOT CLASS, never by a per-element timer.

   ── TWO LEVELS ───────────────────────────────────────────────────────────
   A band taller than the viewport cannot usefully animate as one object: its
   top crosses the trigger line while its last screenful is still 1000px away,
   so by the time that content is read it has long since finished moving. site.js
   therefore SPLITS a band into its own blocks and observes each one, and each
   pops as it crosses. `.da-reveal-split` marks the container (which then stops
   hiding itself) and `.da-reveal-item` marks the blocks.

   A `.da-stagger` group is deliberately NOT split: those are tight rows of four
   or five cells where the point is that they cascade together.

   Intensity lives in --reveal-rise / --dur-reveal / --reveal-blur / --ease-reveal
   and the --dgm-* set, all in the token file. Change it there, not here.

   ONE curve, and it does not overshoot. An earlier version bounced by 2.1px on
   the way in; a bounce is the clearest tell of a template, so premium means a
   pure expo-out that leaves fast and settles for a long time.
   --reveal-delay defaults to 0ms; only .da-stagger sets it. */

.da-motion [data-reveal]:not(.da-reveal-split),
.da-motion .da-reveal-item {
  opacity: 0;
  transform: translateY(var(--reveal-rise));
  filter: blur(var(--reveal-blur));
  transition: opacity var(--dur-reveal) var(--ease-reveal) var(--reveal-delay, 0ms),
              transform var(--dur-reveal) var(--ease-reveal) var(--reveal-delay, 0ms),
              filter var(--dur-reveal) var(--ease-reveal) var(--reveal-delay, 0ms);
}
.da-motion [data-reveal].is-in,
.da-motion .da-reveal-item.is-in { opacity: 1; transform: none; filter: none; }

/* The amber rule draws from its leading edge rather than rising. scaleX on a
   148x3 bar is cheaper than animating width and does not reflow. It wins over
   the generic translateY above by being declared after it. */
.da-motion .da-reveal-item.da-rule {
  transform: scaleX(0); transform-origin: left center;
}
.da-motion .da-reveal-item.da-rule.is-in { transform: none; }

/* ── Figure choreography ────────────────────────────────────────────────────
   A diagram assembles itself: every spoke DRAWS from its origin, and each node
   appears as its line arrives. This replaced a single scale on the whole svg,
   which moved the figure without saying anything about it.

   The svg itself no longer hides. Its PARTS do, so the figure holds its space
   from the start and nothing reflows as it assembles.

   The sequence is free. Each helper appends its parts in drawing order (a spoke,
   then the node it points at, then the next spoke), so a flat :nth-child ramp
   over `.da-dgm > *` produces hub-outward or left-to-right order in every one of
   the eleven figures without a single index passed from Ruby.

   Capped at 24 parts: the criteria funnel has about fifty, and 50 x 35ms is two
   seconds of assembling, which stops reading as choreography and starts reading
   as a slow page. */
.da-motion .da-dgm > * { --dgm-i: 0; }
.da-motion .da-dgm > *:nth-child(2)  { --dgm-i: 1; }
.da-motion .da-dgm > *:nth-child(3)  { --dgm-i: 2; }
.da-motion .da-dgm > *:nth-child(4)  { --dgm-i: 3; }
.da-motion .da-dgm > *:nth-child(5)  { --dgm-i: 4; }
.da-motion .da-dgm > *:nth-child(6)  { --dgm-i: 5; }
.da-motion .da-dgm > *:nth-child(7)  { --dgm-i: 6; }
.da-motion .da-dgm > *:nth-child(8)  { --dgm-i: 7; }
.da-motion .da-dgm > *:nth-child(9)  { --dgm-i: 8; }
.da-motion .da-dgm > *:nth-child(10) { --dgm-i: 9; }
.da-motion .da-dgm > *:nth-child(11) { --dgm-i: 10; }
.da-motion .da-dgm > *:nth-child(12) { --dgm-i: 11; }
.da-motion .da-dgm > *:nth-child(13) { --dgm-i: 12; }
.da-motion .da-dgm > *:nth-child(14) { --dgm-i: 13; }
.da-motion .da-dgm > *:nth-child(15) { --dgm-i: 14; }
.da-motion .da-dgm > *:nth-child(16) { --dgm-i: 15; }
.da-motion .da-dgm > *:nth-child(17) { --dgm-i: 16; }
.da-motion .da-dgm > *:nth-child(18) { --dgm-i: 17; }
.da-motion .da-dgm > *:nth-child(19) { --dgm-i: 18; }
.da-motion .da-dgm > *:nth-child(20) { --dgm-i: 19; }
.da-motion .da-dgm > *:nth-child(21) { --dgm-i: 20; }
.da-motion .da-dgm > *:nth-child(22) { --dgm-i: 21; }
.da-motion .da-dgm > *:nth-child(23) { --dgm-i: 22; }
.da-motion .da-dgm > *:nth-child(n+24) { --dgm-i: 23; }

/* A line drawing itself. pathLength="1" on the element (set by
   MarketingDiagramsHelper#da_diagram_edge) normalises every line's length to 1,
   so this one dasharray draws all of them whatever their geometry. */
.da-motion .da-reveal-item .da-dgm__edge,
.da-motion [data-reveal]:not(.da-reveal-split) .da-dgm__edge {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset var(--dur-draw) var(--ease-reveal)
              calc(var(--dgm-i, 0) * var(--dgm-step));
}
.da-motion .da-reveal-item.is-in .da-dgm__edge,
.da-motion [data-reveal].is-in .da-dgm__edge { stroke-dashoffset: 0; }

/* A dashed edge cannot draw: animating its dashoffset marches the dashes along
   it instead of revealing it. Those fade. */
.da-motion .da-reveal-item .da-dgm__edge--dashed,
.da-motion [data-reveal]:not(.da-reveal-split) .da-dgm__edge--dashed {
  stroke-dashoffset: 0;
  opacity: 0;
  transition: opacity var(--dur-draw) var(--ease-reveal)
              calc(var(--dgm-i, 0) * var(--dgm-step));
}
.da-motion .da-reveal-item.is-in .da-dgm__edge--dashed,
.da-motion [data-reveal].is-in .da-dgm__edge--dashed { opacity: 1; }

/* A node lands as its line reaches it: its own delay plus a little over half the
   draw. transform-box: fill-box is required, or an svg group scales about the
   viewBox origin and flies in from the top left corner. */
.da-motion .da-reveal-item .da-dgm__node,
.da-motion [data-reveal]:not(.da-reveal-split) .da-dgm__node {
  opacity: 0;
  transform: scale(var(--node-scale));
  transform-box: fill-box;
  transform-origin: center;
  transition: opacity var(--dur-node) var(--ease-reveal)
                calc(var(--dgm-i, 0) * var(--dgm-step) + var(--dur-draw) * 0.55),
              transform var(--dur-node) var(--ease-reveal)
                calc(var(--dgm-i, 0) * var(--dgm-step) + var(--dur-draw) * 0.55);
}
.da-motion .da-reveal-item.is-in .da-dgm__node,
.da-motion [data-reveal].is-in .da-dgm__node { opacity: 1; transform: none; }

/* Loose dots: a comp cloud, the amber spine's joints, a dot field. They arrive
   as a cloud rather than drawing, since a dot has no length to draw. */
.da-motion .da-reveal-item .da-dgm__dot,
.da-motion [data-reveal]:not(.da-reveal-split) .da-dgm__dot {
  opacity: 0;
  transition: opacity var(--dur-node) var(--ease-reveal)
              calc(var(--dgm-i, 0) * var(--dgm-step));
}
.da-motion .da-reveal-item.is-in .da-dgm__dot,
.da-motion [data-reveal].is-in .da-dgm__dot { opacity: 1; }

/* ── Stagger ────────────────────────────────────────────────────────────────
   A container that walks its children in as one cascade: the hero assurance
   strips, the KPI rows, the buyer-type rows, the product rows, the Q&A lists.
   Capped at six, because past that the last item waits long enough to read as
   broken rather than as motion.

   .da-stagger[data-reveal], never a bare .da-stagger. The hidden state must be
   keyed to the attribute that gets the element observed, or a container can hide
   its children with nothing on the page able to reveal them. That is exactly
   what happened once: MarketingHelper#reveal emits nothing in Hotwire Native, so
   a native render kept `da-stagger`, dropped `data-reveal`, and 24 elements sat
   at opacity 0. Keying on the attribute makes the two impossible to separate. */
.da-motion .da-stagger[data-reveal] > * {
  opacity: 0; transform: translateY(var(--reveal-rise));
  transition: opacity var(--dur-reveal) var(--ease-reveal) var(--reveal-delay, 0ms),
              transform var(--dur-reveal) var(--ease-reveal) var(--reveal-delay, 0ms);
}
.da-motion .da-stagger[data-reveal].is-in > * { opacity: 1; transform: none; }

/* Explicitly 0ms, so a --reveal-delay set on the CONTAINER by the load-time
   entrance cannot inherit down and give the first cell a later start than the
   second. Every other cell has its own value below. */
.da-motion .da-stagger[data-reveal] > *:first-child      { --reveal-delay:   0ms; }
.da-motion .da-stagger[data-reveal] > *:nth-child(2)   { --reveal-delay:  70ms; }
.da-motion .da-stagger[data-reveal] > *:nth-child(3)   { --reveal-delay: 140ms; }
.da-motion .da-stagger[data-reveal] > *:nth-child(4)   { --reveal-delay: 210ms; }
.da-motion .da-stagger[data-reveal] > *:nth-child(5)   { --reveal-delay: 280ms; }
.da-motion .da-stagger[data-reveal] > *:nth-child(n+6) { --reveal-delay: 350ms; }

/* ── Reduce Motion: a QUIET reveal, not no reveal ────────────────────────────
   The gate script adds .da-motion--quiet when `prefers-reduced-motion: reduce`
   matches. Content still arrives as it is scrolled to; it just fades, with no
   travel, no scale and no overshoot. Nothing vestibular, which is what the
   setting actually asks for.

   The first version withheld the gate entirely so that NOTHING animated. That is
   a defensible reading of the setting and it was wrong here: the requester runs
   macOS Reduce Motion on, so the whole feature was invisible to the person who
   asked for it, twice.

   !important is load-bearing, not laziness. site.css:923 carries the standard
   global reset,

     *, *::before, *::after { transition-duration: 0.01ms !important; ... }

   so without it this fade is overridden to instant and the reveal becomes a pop
   into existence. This is a deliberate, narrow exception to that reset: opacity
   only, on the reveal selectors alone.

   A root CLASS rather than an @media block, because two attempts at an @media
   override both lost a specificity fight that is easy to lose and hard to see
   (15 stagger children hidden on /buyers the first time; 8 to 12 rules and hero
   strips the second). One more class on the root cannot lose that fight. */
.da-motion--quiet [data-reveal]:not(.da-reveal-split),
.da-motion--quiet .da-reveal-item,
.da-motion--quiet .da-reveal-item.da-rule,
.da-motion--quiet .da-stagger[data-reveal] > *,
.da-motion--quiet .da-dgm__node {
  transform: none !important;
  transition: opacity 360ms linear var(--reveal-delay, 0ms),
              filter 360ms linear var(--reveal-delay, 0ms) !important;
}

/* The blur SURVIVES Reduce Motion, and the draw does too. Neither displaces
   anything: the blur resolves in place, and a line is static geometry
   progressively revealed, like a wipe. Between them they are most of what makes
   the arrival feel considered rather than abrupt, which matters more here than
   anywhere because this project's reviewer has the setting on.

   Judgement call on the draw. The conservative alternative is to add
   .da-dgm__edge to the block above and let the lines fade instead. */
.da-motion--quiet .da-dgm__edge {
  transition: stroke-dashoffset var(--dur-draw) linear
              calc(var(--dgm-i, 0) * var(--dgm-step)) !important;
}

/* ═══ IN-PAGE NAVIGATION ═══════════════════════════════════════════════════
   The anchor links between sections ("See the Transaction Ecosystem" ->
   #ecosystem, the hero's "See What Practices Are Asking" -> #valuation, "See How
   It Works" -> #process) did two things wrong: they jumped instantly, and they
   landed with the target's heading UNDERNEATH the 90px sticky nav.

   scroll-margin-top fixes the landing for EVERY route into an anchor at once: a
   click, a pasted URL, a reload on a hash, the back button, and any scrollIntoView
   from script. A JS scroll offset would only have fixed the click.

   scroll-behavior belongs on the scrolling box, which for the document is <html>
   -- and .da-motion is on <html>, so the gate scopes it for free: no JS means no
   class means an instant jump, which is the correct fallback.

   :not(.da-motion--quiet) is deliberate. A smooth scroll across several thousand
   pixels is exactly the large-field movement Reduce Motion exists to prevent, so
   that setting keeps the instant jump. It still gets the corrected landing. */
.da-motion:not(.da-motion--quiet) { scroll-behavior: smooth; }

/* The companion `.da-sec[id] { scroll-margin-top }` rule moved to
   marketing/da_system.css: it belongs to the page system rather than to motion,
   and /explore loads that file without this one. Left here, an anchored band on
   explore would land under the sticky nav. scroll-behavior stays, because it IS
   motion and is gated on .da-motion, which explore does not carry. */

/* ═══════════════════════════════════════════════════════════════════════════
   LEGACY SHARED BLOCKS — pre-v2 rules for views not yet rewritten.

   🚨 Retire an entry only when its consumer has been ported.

   The v2 pages.css drops 62 class names. 12 are still used by a view on one of
   the four layouts loading this bundle and are not re-homed anywhere else in the
   v2 bundle, so their rules are carried over verbatim. Every selector is scoped
   by a class v2 does not style.

   All of these belong to the /sellers valuation panel (.val-card__*), the
   home-page artifact cards, and the feature-row alternation, so they retire
   together when those pages are ported in the marketing-pages stage.

   `.btns` is not here: its only rule was ever `.cta-block .btns`, which lives in
   site.css's legacy block. See the note there.
   ═══════════════════════════════════════════════════════════════════════════ */
.feat-row--alt { background: #fafaf9; }

.feat-row__grid.rev > :first-child { order: 2; }

/* .artifact-card is retired. It was the pre-v2 card: a 1px border plus a
   0 24px 48px -24px shadow, both off-system now that cards are borderless and sit
   on --shadow-card. Its seven usages moved to .artifact, which the same views were
   already using alongside it. */

@media (max-width: 900px) {
  /* minmax(0, 1fr), not 1fr: plain `1fr` floors at min-content, so a long
     heading pushed this column past the viewport at 375. */
  .page-hero__grid, .feat-row__grid { grid-template-columns: minmax(0, 1fr) !important; gap: 40px; }
  .feat-row__grid.rev > :first-child { order: 0; }
  .bene-grid { grid-template-columns: 1fr; }
  .page-hero { padding: 64px 0 48px; }
}

@media (max-width: 760px) {
  .feat-row { padding: 64px 0; }
  .feat-row__grid { gap: 28px; }
  .feat-row__grid h2 { font-size: var(--text-3xl); }
  .feat-row__grid p { font-size: var(--text-lg); }
  .bene { padding: 28px 24px; }
  .page-hero .lede-lg { font-size: var(--text-lg); margin-bottom: 24px; }
  .final-cta { padding: 64px 0; }
}

.onb-input[type="number"]::-webkit-inner-spin-button,
.onb-input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.onb-input[type="number"] { -moz-appearance: textfield; appearance: textfield; }

.page-cta__stats { position: relative; z-index: 1; }

.val-card__submit__hint {
  margin: 12px 0 0; text-align: center;
  font-size: var(--text-sm); color: var(--base-text-tertiary);
}

.val-card__right--info .val-card__submit { padding: 0; }

@media (max-width: 960px) {
  .val-card { grid-template-columns: 1fr; }
  .val-card__heading { font-size: var(--text-3xl); }
  .val-card__fields,
  .val-card__result,
  .val-card__submit { padding-left: 28px; padding-right: 28px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE DENTAL ACQUISITIONS PAGE SYSTEM MOVED OUT
   ---------------------------------------------------------------------------
   .da-sec, .da-wrap, .da-h1/2/3, .da-body, .da-btn, .da-rows, .da-cols and the
   rest of the vocabulary now live in marketing/da_system.css, and every layout
   that links this file links that one FIRST.

   It moved because /explore needed the vocabulary and cannot load this file:
   the legacy blocks above would collide with explore.css. See the banner in
   da_system.css.

   Nothing was edited in the move. Do not re-add .da-* rules here.
   ═══════════════════════════════════════════════════════════════════════════ */
