Floating CTA

A .floating-cta capsule that keeps section-jump links and a call-to-action button always visible on long landing pages. A rounded (--r-xl), translucent bar is fixed to the bottom of the viewport; when the footer comes into view, JS automatically lifts it so it never overlaps the footer.

Shape

A rounded-box bar — border-radius: var(--r-xl) (16px, same family as buttons and cards; pill 9999px is forbidden) — with a translucent background, backdrop-filter: blur(16px), and an --sh-full elevation shadow. Section-jump links are arranged on the left; the global .btn apply button is anchored at the far right. The Preview below is a static mock — it shows the shape only, without the real fixed positioning behavior.

Behavior

In a real page, the capsule is fixed to the bottom of the viewport via position: fixed. The lift logic that raises it above the footer is written as client-side JS directly inside the page component.

Fixed position

  • position: fixed, bottom: var(--cta-gap) — 40px on desktop / 24px on mobile
  • left: 50%, width: min(800px, calc(100% - 32px))
  • z-index: 70 — above content, below modals

Footer avoidance (lift)

To prevent the capsule from overlapping the footer, JS recalculates the lift value on every scroll, resize, and ResizeObserver(body) event using the formula below.

  • lift = max(0, innerHeight - footer.getBoundingClientRect().top)
  • Apply to the capsule: transform: translate(-50%, calc(-1 * var(--cta-lift))) — the capsule bottom always rests --cta-gap above the footer
  • Do not use rAF (requestAnimationFrame) — it pauses in background tabs; update directly in the event handler instead

Parked-position spacing

When the capsule parks above the footer, set the last section's (e.g. FAQ) padding-bottom to roughly capsule height + 2 × gap so the space between the content end and the footer looks balanced. The footer's own padding-bottom should stay at its normal value, independent of the capsule.

Mobile

At viewport widths of 720px and below, the link area switches to horizontal scrolling. The apply button stays pinned to the far right and remains visible regardless of scroll position.

  • Link row: overflow-x: auto with edge mask fades — visually hints that the row is scrollable
  • Apply button: flex: 0 0 auto pins it to the right, independent of link scrolling
  • Capsule width: min(800px, calc(100% - 32px)) — preserves 16px gutters on each side

Rules

  • Radius: var(--r-xl) (16px) — rounded box like buttons/cards; pill 9999px is forbidden
  • Item spacing: row gap = links gap = 36px — links and the apply button stay evenly spaced as one group
  • Background: color-mix(in srgb, var(--card-bg) 66%, transparent) — translucent; automatically follows the dark mode token
  • Elevation: --sh-full family
  • Nav link text: weight 600 — matches header links
  • Apply button: use the global .btn as-is; no custom variants
  • z-index: 70 — below the header layer (100), above regular content
  • No emoji or italic — use inline SVG or pseudo-elements for icons