/*  public/main.css
 *
 *  The complete, SELF-CONTAINED stylesheet for the public (non-app) surfaces.
 *  It holds TWO layers, deliberately in one file:
 *
 *    AUTH LAYER  - scoped to `.auth-page`. Login, signup, password reset,
 *                  email verify, approve/reject, team accept. All rendered by
 *                  Levito::HtmlTools::render_auth_page.
 *    SITE LAYER  - scoped to `.site-page`. The six marketing pages (home,
 *                  pricing, for agencies, FAQs, contact, legal), rendered by
 *                  Levito::Public::Layout.
 *
 *  Neither layer may use bare element selectors - they would cross into the
 *  other layer. Only the tiny Base block below is shared. This is the one
 *  invariant to preserve when editing this file.
 *
 *  This file loads ZERO app CSS and the app loads ZERO of this - the public
 *  and app zones are deliberately separate design systems that happen to share
 *  a repo. A few primitives (tokens, toast, field validation) are intentionally
 *  duplicated from the app's stylesheets rather than shared; keep them in sync
 *  by intent, not by cross-loading. See render_public_head_assets() in
 *  Levito::HtmlTools for the loader.
 *
 *  (Merged from the former base.css + public.css.)
 */

/* ---- Geist - self-hosted (variable font), the public typeface. One woff2 per
 * subset covers the whole 100-900 weight axis. Files in /assets/fonts/geist/;
 * the latin file is preloaded in render_public_head_assets. Duplicated by intent
 * from the app zone (app/tokens.css), per the no-cross-loading rule. ---- */
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(/assets/fonts/geist/geist-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(/assets/fonts/geist/geist-latinext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ---- STIX Two Text - self-hosted, the marketing site's DISPLAY face (the
 * serif headlines, roman + italic). Variable font, 400-700 on one axis, so one
 * woff2 per subset per style. Used ONLY by the site layer; the auth pages and
 * the app never load a serif. Files in /assets/fonts/stix-two-text/. ---- */
@font-face {
  font-family: 'STIX Two Text';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(/assets/fonts/stix-two-text/stix-two-text-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'STIX Two Text';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(/assets/fonts/stix-two-text/stix-two-text-latinext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'STIX Two Text';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url(/assets/fonts/stix-two-text/stix-two-text-latin-italic.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'STIX Two Text';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url(/assets/fonts/stix-two-text/stix-two-text-latinext-italic.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ============================================================
 * Design tokens (CSS custom properties) - the public palette.
 * ============================================================ */

:root {
    /* Brand */
    --lv-brand:           #0C7BC0;
    --lv-brand-hover:     #0A69A4;
    --lv-brand-tint:      #e3f2fd;

    /* Neutrals */
    --lv-text:            #222;
    --lv-text-dark:       #1a1a1a;
    --lv-text-strong:     #333;
    --lv-text-medium:     #555;
    --lv-text-soft:       #666;
    --lv-text-muted:      #888;
    --lv-text-faint:      #999;

    --lv-bg-page:         #fff;
    --lv-bg-soft:         #fafafa;
    --lv-bg-soft-2:       #f7f8f7;
    --lv-bg-muted:        #f5f5f5;
    --lv-bg-card:         #fafafa;
    --lv-border:          #eee;
    --lv-border-mid:      #ddd;
    --lv-border-strong:   #ccc;

    /* Status colors */
    --lv-error:           #c0392b;
    --lv-error-dark:      #762014;
    --lv-error-bg:        #fdf2f1;
    --lv-warn:            #a87420;
    --lv-warn-bg:         #fff4d6;
    --lv-success:         #5fac5a;
    --lv-success-strong:  #2b8a3e;
    --lv-success-dark:    #245820;
    --lv-success-bg:      #f1f8f0;
    --lv-info:            #3498db;
    --lv-info-dark:       #1b4a6b;
    --lv-info-bg:         #eff7fc;

    /* Typography - Geist self-hosted via @font-face above (woff2 in /assets/fonts/geist/). */
    --lv-font: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI",
               system-ui, sans-serif;
    --lv-font-mono: ui-monospace, Menlo, Consolas, monospace;

    /* Radius */
    --lv-radius:          6px;
    --lv-radius-lg:       8px;
    --lv-radius-pill:     12px;

    /* ---- SITE LAYER tokens (marketing pages only) ----
     * Extracted from Figma `VoSqlP0heUKjGYLydeAZwx`, page "Round 4".
     *
     * FONT NOTE: the comps bind body text to a shared-library variable
     * `family/body` whose value is Inter. That token is STALE - it was never
     * updated when Levito moved to Geist, and Levito uses Inter nowhere. Build
     * body text in Geist; the Figma file showing Inter is the file being wrong,
     * not a design decision. (Ruled 2026-08-07.)
     *
     * TRACKING NOTE: the designer sets letter-spacing as a PERCENTAGE, so the
     * px values Figma reports are derived (104px -> -3.12px, 60px -> -1.8px,
     * 16px -> -0.48px are all -3%). These are `em` so they scale; never
     * transcribe the px value.
     */
    --lv-display:         "STIX Two Text", Georgia, "Times New Roman", serif;

    --lv-site-ink:        #000;
    --lv-site-ink-soft:   rgba(0, 0, 0, 0.48);
    --lv-site-ink-btn:    #191919;
    --lv-site-line:       rgba(0, 0, 0, 0.08);
    --lv-site-line-soft:  rgba(0, 0, 0, 0.06);
    --lv-site-card-blue:  #347ed0;
    --lv-site-glass:      rgba(255, 255, 255, 0.2);
    --lv-site-edge:       rgba(255, 255, 255, 0.32);

    /* The sheen that rides on every SOLID primary button. */
    --lv-site-sheen: linear-gradient(180deg, rgba(255,255,255,.12) 0%, rgba(255,255,255,0) 100%);

    --lv-site-track:      -0.03em;   /* -3%  - display + most body */
    --lv-site-track-1:    -0.01em;   /* -1%  - footer link columns */

    --lv-site-gutter:     8px;       /* white margin around every section card */

    /* HORIZONTAL GUTTERS - continuous, NOT stepped.
     *
     * The comps exist at 1440 and 375 only, and .site-shell is capped at 1440,
     * so between those two widths the gutter is ours to choose. It used to STEP
     * to 40px at the top of the laptop tier (1439), which with the shell already
     * capped meant content got WIDER as the window got NARROWER - one pixel of
     * window bought back 80px of gutter. On pricing that jumped the fee card
     * from 1280 to 1359 and, through the fee column's reflow, blew the receipt
     * row gaps from 7.5px to 67.5px (found + fixed 2026-08-11).
     *
     * Each gutter now interpolates from its comp value at 1440 down to 40px at
     * 1024 and clamps flat outside that range, so >=1440 is EXACTLY the comp and
     * <=1024 is exactly what the iPad tiers already assumed - neither end moved.
     * Three values because the comp draws three: 80 on plain sections, 128 on
     * the big rounded cards, 72 on the two inset ones.
     *
     * 100vw, not 100%: a percentage resolves against each element's own
     * containing block, and those differ (the hero sits inside an 8px-gutter
     * band), so the cap would land short of the comp value on some of them.
     * 100vw counts the classic scrollbar where one exists, which shifts the
     * curve by the scrollbar width but keeps it continuous and monotonic. */
    --lv-site-pad-x:      clamp(40px, calc(40px + (100vw - 1024px) * 40 / 416), 80px);
    --lv-site-pad-xl:     clamp(40px, calc(40px + (100vw - 1024px) * 88 / 416), 128px);
    --lv-site-pad-xm:     clamp(40px, calc(40px + (100vw - 1024px) * 32 / 416), 72px);

    /* Horizontal GAPS follow the same curve for the same reason: stepping one to
     * 40px at 1439 hands the whole difference to whatever sits beside it, so the
     * neighbour gets wider on a narrower window exactly as the gutters did. Two
     * values because the comp draws two - 64 between section columns, 80 inside
     * a section head and across the contact grid. -gap-xl is deliberately an
     * alias rather than a second copy of the same numbers. */
    --lv-site-gap-x:      clamp(40px, calc(40px + (100vw - 1024px) * 24 / 416), 64px);
    --lv-site-gap-xl:     var(--lv-site-pad-x);
    /* HALF the gap between two sections, since both sides contribute it. The
     * comp drew 160, which put 320px of white between every section; the
     * agreed rhythm is 180 total, matching the hero-to-first-section gap. */
    --lv-site-pad-y:      90px;
    --lv-site-gap:        64px;
    --lv-site-radius:     24px;
    --lv-site-radius-lg:  32px;

    /* Only the last two layers of the designer's 5-layer shadows render - the
     * first three are alpha 0. These are the real ones. */
    --lv-site-shadow-btn:  0 1px 1px rgba(0, 0, 0, 0.05);
    --lv-site-shadow-card: 0 52px 52px rgba(0, 0, 0, 0.01), 0 13px 29px rgba(0, 0, 0, 0.01);
    --lv-site-shadow-nav:  0 52px 26px rgba(0, 0, 0, 0.01), 0 13px 14.5px rgba(0, 0, 0, 0.01);

    /* White-on-sky text separation. THESE TWO LINES ARE THE DIAL - every white
     * run on the sky photo reads from them, so tuning happens here and nowhere
     * else. See the "White-on-sky text shadow" block for what uses them.
     *
     * BLUR is the lever that matters, not darkness. With little or no blur
     * there is no halo between the glyph edge and the sky, so raising alpha
     * alone just prints a hard dark edge - at 104px that reads as a
     * registration error rather than depth. Raise blur and alpha together.
     *
     * -lg is in em so it tracks .site-display from 104px down to 32px on its
     * own; -sm is px because those runs only span 16-20px. */
    /* Nav pill width. The notch offsets derive from this, so the pill and the
       two corner notches can never drift apart again. */
    --lv-nav-w:           800px;

    --lv-sky-text-shadow-lg: 0 0.02em 0.05em rgba(0, 0, 0, 0.30);
    --lv-sky-text-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.24);
}

/* ============================================================
 * Base - the only rules that apply to BOTH layers. Everything else is
 * scoped to .auth-page or .site-page so the two can't bleed into each
 * other. Keep this block minimal for that reason.
 * ============================================================ */

body {
    font-family: var(--lv-font);
    color: var(--lv-text);
    background: var(--lv-bg-page);
    margin: 0;
}

/* ============================================================
 * AUTH LAYER - login / signup / password reset / verify / approve /
 * reject / team-accept. Every one of these is rendered by
 * Levito::HtmlTools::render_auth_page, which always emits
 * <body class="auth-page">, so scoping on that class covers all of them
 * and nothing else. Do not unscope these rules: unprefixed element
 * selectors here would land on the marketing pages below.
 *
 * (The former narrow 420px centered-body layout lived here and was
 * DEAD - body.auth-page cancelled its max-width/margin/padding, and
 * every page that loaded this file set that class. Removed with its
 * unused body.card-centered companion rather than carried forward.)
 * ============================================================ */

.auth-page { font-size: 18px; }

.auth-page a { color: var(--lv-brand); text-decoration: none; }
.auth-page a:hover { text-decoration: none; }

.auth-page h1 { font-size: 24px; margin: 0 0 30px; font-weight: 700; }
.auth-page h2 { font-size: 22px; margin: 28px 0 8px; font-weight: 700; }
.auth-page h3 { font-size: 19px; margin: 24px 0 8px; font-weight: 700; }

.auth-page p       { font-size: 18px; line-height: 1.55; }
.auth-page p.lead  { color: var(--lv-text-soft); margin: 0 0 16px; }

.auth-page code {
    background: #f7f7f7;
    padding: 1px 6px;
    border-radius: 3px;
    font-family: var(--lv-font-mono);
    font-size: 90%;
}

/* ---- Auth forms (scoped - see the AUTH LAYER note above) ---- */

.auth-page label {
    display: block;
    font-weight: 600;
    margin-top: 16px;
    margin-bottom: 6px;
    font-size: 15px;
}

.auth-page input[type=text],
.auth-page input[type=email],
.auth-page input[type=password] {
    width: 100%;
    padding: 10px 12px;
    font-size: 15px;
    border: 1px solid var(--lv-border-strong);
    border-radius: var(--lv-radius);
    box-sizing: border-box;
    font-family: inherit;
}
.auth-page input[type=text]:focus,
.auth-page input[type=email]:focus,
.auth-page input[type=password]:focus {
    border-color: var(--lv-brand);
    outline: 0;
}

.auth-page .help {
    font-size: 15px;
    color: var(--lv-text-muted);
    margin-top: 4px;
}

.auth-page button[type=submit] {
    margin-top: 24px;
    width: 100%;
    padding: 12px;
    font-size: 15px;
    font-weight: 600;
    background: var(--lv-brand);
    color: #fff;
    border: 0;
    border-radius: var(--lv-radius);
    cursor: pointer;
    font-family: inherit;
}
.auth-page button[type=submit]:hover { background: var(--lv-brand-hover); }

/* Double-submit guard: dim and lock the submit button while a submission is in
   flight (main.js sets data-lv-submitting on the form). The form-level guard
   does the real blocking; this is the visual cue. Left UNSCOPED on purpose -
   main.js drives it and the marketing contact form uses the same guard. */
form[data-lv-submitting] button[type=submit],
form[data-lv-submitting] input[type=submit],
form[data-lv-submitting] button:not([type]) {
  opacity: 0.6;
  pointer-events: none;
}

/* Side-by-side first/last name fields on signup + team-accept signup. */
.name-row { display: flex; gap: 12px; }
.name-row > div { flex: 1; }

/* ============================================================
 * Auth split layout (login / signup / password / join team / status) - a
 * vertically-centered form column on the left, the brand sky panel on the
 * right. Matches the Figma "missing screens" frames. Built by
 * Levito::HtmlTools::render_auth_page, which sets <body class="auth-page">.
 * ============================================================ */

.auth-split { display: flex; align-items: stretch; min-height: 100vh; }

.auth-form-col {
    flex: 1 1 50%; min-width: 0; box-sizing: border-box;
    display: flex; align-items: center; justify-content: center;
    padding: 48px 40px;
}
.auth-col-inner {
    width: 100%; max-width: 400px;
    display: flex; flex-direction: column; gap: 40px;
}
.auth-col-inner > form { display: flex; flex-direction: column; gap: 40px; }

/* Selector is deliberately doubled: `.auth-page a` above sets brand blue on
   inline auth links, and at 0-1-1 it outranks a bare `.auth-logo` at 0-1-0 - so
   the wordmark SVG (fill: currentColor) rendered blue instead of near-black.
   Don't "simplify" this back to `.auth-logo`. */
.auth-page .auth-logo { align-self: flex-start; display: inline-flex; align-items: center; gap: 12px; color: var(--lv-text-dark); text-decoration: none; }
.auth-logo-mark { display: block; width: 32px; height: 32px; }
.auth-logo-word { display: block; height: 22px; width: auto; }

.auth-intro { display: flex; flex-direction: column; gap: 8px; }
.auth-title { margin: 0; font-size: 30px; line-height: 40px; font-weight: 600; letter-spacing: -0.2px; color: #3f3f46; white-space: nowrap; }
/* Longer headlines (approve/reject/verify landings) must wrap - the login
   title is the only one held to a single line. */
.auth-title-wrap { white-space: normal; }
.auth-sub { margin: 0; font-size: 15px; line-height: 20px; color: #71717a; }
.auth-sub a { color: #3f3f46; font-weight: 500; text-decoration: underline; }

.auth-fields { display: flex; flex-direction: column; gap: 32px; }
.auth-row { display: flex; gap: 12px; }
.auth-row > .auth-field { flex: 1 1 0; min-width: 0; }
.auth-field { display: flex; flex-direction: column; gap: 8px; }
.auth-field > label { margin: 0; font-size: 15px; line-height: 20px; font-weight: 500; color: #18181b; }
.auth-field .field-error { margin: 0; }
.auth-label-row { display: flex; align-items: flex-start; gap: 8px; }
.auth-label-row > label { flex: 1 1 0; min-width: 0; margin: 0; font-size: 15px; line-height: 20px; font-weight: 500; color: #18181b; }
.auth-forgot { font-size: 15px; line-height: 20px; font-weight: 500; color: var(--lv-brand); text-decoration: none; white-space: nowrap; }
.auth-forgot:hover { text-decoration: none; }

/* Figma-matched inputs + button, scoped to the auth form column so the other
   (not-yet-converted) public pages keep their current styling. */
.auth-field input[type=text],
.auth-field input[type=email],
.auth-field input[type=password] {
    width: 100%; box-sizing: border-box;
    height: 36px; padding: 0 8px;
    font-size: 15px; font-weight: 500; line-height: 20px;
    color: #18181b; background: #fff;
    border: 1px solid rgba(0, 0, 0, 0.08);
    border-radius: 8px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.auth-field input[type=text]:hover:not(:focus):not(:disabled),
.auth-field input[type=email]:hover:not(:focus):not(:disabled),
.auth-field input[type=password]:hover:not(:focus):not(:disabled) {
    border-color: rgba(0, 0, 0, 0.16);
}
.auth-field input[type=text]:focus,
.auth-field input[type=email]:focus,
.auth-field input[type=password]:focus {
    border-color: #18181b; outline: none;
    box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.16);
}

.auth-form-col button[type=submit],
.auth-form-col .auth-btn {
    margin-top: 0;
    display: flex; align-items: center; justify-content: center;
    width: 100%; height: 40px; box-sizing: border-box;
    padding: 8px 16px;
    font-size: 16px; font-weight: 600; line-height: 24px; letter-spacing: -0.4px;
    color: #fff; background: var(--lv-brand);
    border: 1px solid rgba(255, 255, 255, 0.32);
    border-radius: 10px;
    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
    cursor: pointer;
    text-decoration: none;
}
.auth-form-col button[type=submit]:hover,
.auth-form-col .auth-btn:hover { background: var(--lv-brand-hover); }

/* Secondary link under the primary action (e.g. "View in dashboard" on the
   approve/reject confirm pages). */
.auth-altlink { align-self: center; font-size: 15px; line-height: 20px; font-weight: 500; color: #71717a; text-decoration: none; }
.auth-altlink:hover { color: var(--lv-brand); text-decoration: underline; }

.auth-sky { flex: 1 1 50%; min-width: 0; box-sizing: border-box; padding: 8px 8px 8px 0; }
.auth-sky-inner {
    height: 100%; position: relative; overflow: hidden;
    border: 1px solid rgba(0, 0, 0, 0.04);
    border-radius: 16px; background: #cfe6f7;
    display: flex; align-items: center; justify-content: center;
}
.auth-sky-inner .bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.auth-sky-inner .mark { position: relative; width: 160px; height: 160px; }

/* iPad LANDSCAPE (1024x768): tighten the vertical rhythm on the auth forms.
   The signup form is the tallest one - five fields plus the 70px Turnstile
   widget - and at 768px of height the default 48px padding and two 40px gap
   stacks pushed it to 818px, putting the "Create account" button below the
   fold. Login and password reset fit comfortably either way; this keeps all
   three consistent rather than special-casing one page.

   Width-tiered, not height-tiered, per the house convention (desktop-first,
   two max-width tiers). Be honest about what that leaves: this fixes the
   TARGET profiles on a clean load (1024x768 goes 818 -> 745 content, CTA 770
   -> 713). It does NOT make every case fit. A shorter landscape iPad, and the
   bounce-back state where a flash error plus five inline field errors add
   ~90px, still run past the fold. Those scroll - the window is the scroller
   here, so the button is always reachable, just not always visible without
   scrolling, which is ordinary behaviour for a form this tall. Making it fit
   in every case means scrolling the column internally, which is a structural
   change to a layout shared by seven auth surfaces and not worth it for a
   transient error state. */
@media (max-width: 1024px) {
    .auth-form-col  { padding: 32px 40px; }
    .auth-col-inner,
    .auth-col-inner > form { gap: 24px; }
}

/* iPad portrait + narrow screens: drop the sky panel, let the centered form fill.
   834 (not 800) so ALL iPad portrait widths (768/810/820/834) collapse to the
   clean centered form instead of cramming it into a 50/50 split; landscape iPad
   (1024) keeps the comfortable split. Matches the app's portrait breakpoint
   (reference_levito_ui_conventions §Responsive - iPad). */
@media (max-width: 834px) {
    .auth-sky { display: none; }
    .auth-form-col { padding: 32px 24px; }
}

/* ============================================================
 * Password-reset modal + "use reset link" dialog - overlaid on the login page
 * (per Figma). Shown/hidden by the inline script on /login/.
 * ============================================================ */
.pw-overlay {
    position: fixed; inset: 0; z-index: 9000;
    display: flex; align-items: center; justify-content: center; padding: 24px;
    background: rgba(0, 0, 0, 0.48);
    -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
}
.pw-overlay[hidden] { display: none; }

/* "Reset your password" modal (480px) */
.pw-modal {
    box-sizing: border-box; width: 480px; max-width: 100%;
    background: #fff; border: 1px solid rgba(0, 0, 0, 0.08); border-radius: 20px;
    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
}
.pw-modal-header { display: flex; gap: 24px; align-items: flex-start; padding: 32px; }
.pw-modal-head-text { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.pw-modal-title { margin: 0; font-size: 18px; line-height: 28px; font-weight: 600; letter-spacing: -0.2px; color: #18181b; }
.pw-modal-sub { margin: 0; font-size: 15px; line-height: 20px; color: #71717a; }
.pw-modal-close {
    flex: 0 0 auto; width: 32px; height: 32px; padding: 0; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    background: none; border: 0; border-radius: 8px; color: #71717a;
}
.pw-modal-close:hover { background: #f4f4f5; }
.pw-modal-close svg { width: 16px; height: 16px; display: block; }
.pw-modal-body { padding: 0 32px 32px; }
.pw-modal-actions { display: flex; gap: 10px; justify-content: flex-end; padding: 0 32px 32px; }

/* "Use reset link" dialog (400px) */
.pw-dialog {
    box-sizing: border-box; width: 400px; max-width: 100%; overflow: hidden;
    background: #fff; border: 1px solid rgba(0, 0, 0, 0.08); border-radius: 20px;
    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
}
.pw-dialog-header { display: flex; flex-direction: column; align-items: center; gap: 24px; padding: 24px 32px 32px; }
.pw-dialog-icon {
    width: 48px; height: 48px; flex: 0 0 auto; color: var(--lv-brand);
    display: flex; align-items: center; justify-content: center;
    background: #fff; border: 1px solid rgba(0, 0, 0, 0.08); border-radius: 999px;
    box-shadow: 0 1px 0.5px rgba(0, 0, 0, 0.05);
}
.pw-dialog-icon svg { width: 20px; height: 20px; display: block; }
.pw-dialog-text { display: flex; flex-direction: column; gap: 6px; text-align: center; }
.pw-dialog-title { margin: 0; font-size: 18px; line-height: 28px; font-weight: 600; letter-spacing: -0.2px; color: #18181b; }
.pw-dialog-body { margin: 0; font-size: 15px; line-height: 20px; color: #71717a; }
.pw-dialog-actions { display: flex; gap: 10px; justify-content: flex-end; padding: 0 32px 32px; }

/* small modal buttons (content-width, 8px radius). Scoped under .pw-overlay so
   they beat the public-zone global `button[type=submit]` rule (which forces
   width:100% + margin-top:24px on submit buttons - that would blow the primary
   action to full modal width and shove the outline button out of the card). */
.pw-overlay .pw-btn-outline, .pw-overlay .pw-btn-primary {
    box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center;
    flex: 0 0 auto; width: auto; margin: 0; white-space: nowrap;
    padding: 6px 12px; border-radius: 8px; font-size: 14px; line-height: 20px;
    font-family: inherit; cursor: pointer; text-decoration: none; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
}
.pw-overlay .pw-btn-outline { background: #fff; border: 1px solid rgba(0, 0, 0, 0.08); color: #3f3f46; font-weight: 500; }
.pw-overlay .pw-btn-outline:hover { background: var(--lv-bg-soft); }
.pw-overlay .pw-btn-primary { background: var(--lv-brand); border: 1px solid rgba(255, 255, 255, 0.32); color: #fff; font-weight: 600; letter-spacing: -0.4px; }
.pw-overlay .pw-btn-primary:hover { background: var(--lv-brand-hover); }
.pw-overlay .pw-btn-full { flex: 1 1 0; width: 100%; }

/* ============================================================
 * Footer / helper links
 * ============================================================ */

.footer {
    margin-top: 24px;
    font-size: 15px;
    color: var(--lv-text-soft);
    text-align: center;
}
.footer a { color: var(--lv-brand); text-decoration: none; }
.footer a:hover { text-decoration: none; }

.helplink {
    display: block;
    text-align: center;
    margin-top: 12px;
    font-size: 15px;
    color: var(--lv-brand);
    text-decoration: none;
}
.helplink:hover { text-decoration: none; }

/* ============================================================
 * Flash banner (rendered by Levito::Flash for the error type - shown on auth
 * pages after a failed login redirect, etc.)
 * ============================================================ */

.lv-banner {
    border-radius: var(--lv-radius-lg);
    padding: 14px 18px;
    margin: 0 0 18px;
    font-size: 16px;
    line-height: 1.5;
    display: flex;
    align-items: flex-start;
    gap: 12px;
    border-left: 4px solid #888;
    background: var(--lv-bg-soft-2);
    color: var(--lv-text);
}
.lv-banner-error {
    border-left-color: var(--lv-error);
    background: var(--lv-error-bg);
    color: var(--lv-error-dark);
}
.lv-banner-message { flex: 1; }
.lv-banner-message strong { font-weight: 700; }
.lv-banner-message ul { margin: 8px 0 0; padding-left: 18px; }
.lv-banner-message li { margin-bottom: 4px; }
.lv-banner-message a { color: inherit; font-weight: 600; text-decoration: none; }

.lv-banner-close {
    background: none;
    border: 0;
    color: inherit;
    opacity: 0.6;
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    padding: 2px 4px;
    border-radius: 4px;
    font-family: inherit;
}
.lv-banner-close:hover { opacity: 1; background: rgba(0, 0, 0, 0.05); }

/* ============================================================
 * Inline form validation (lv-ui primitive - driven by public/main.js).
 * Markup: <input ... required><div class="field-error" id="x-error"></div>
 * ============================================================ */

.field-error {
    color: var(--lv-error);
    font-size: 15px;
    line-height: 1.4;
    margin: 4px 0 0;
    display: none;
}
.field-error.is-shown { display: block; }

input.input-invalid,
input[type].input-invalid,
select.input-invalid,
textarea.input-invalid {
    border-color: var(--lv-error);
    color: var(--lv-error);
}
input.input-invalid:focus,
input[type].input-invalid:focus,
select.input-invalid:focus,
textarea.input-invalid:focus {
    border-color: var(--lv-error);
    outline: none;
    box-shadow: 0 0 0 3px rgba(192, 57, 43, 0.32);
}

/* Placeholder color - clearly lighter than real input text so it isn't
 * mistaken for a filled value. opacity:1 overrides Firefox's default fade. */
::placeholder { color: #b0b0b0; opacity: 1; }
::-webkit-input-placeholder { color: #b0b0b0; opacity: 1; }
::-moz-placeholder          { color: #b0b0b0; opacity: 1; }
:-ms-input-placeholder      { color: #b0b0b0; }

/* Inline Lucide icons (Levito::HtmlTools::lucide_icon). */
.lv-icon { display: inline-block; vertical-align: -3px; }

/* ============================================================
 * Toast (top-right stack, auto-dismiss) - flash success/info messages.
 * ============================================================ */

.lv-toast-region {
    position: fixed;
    top: 20px;
    right: 20px;
    z-index: 9500;
    display: flex;
    flex-direction: column;
    gap: 10px;
    pointer-events: none;
    max-width: calc(100vw - 40px);
}
.lv-toast {
    background: #fff;
    border-radius: var(--lv-radius-lg);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
    padding: 14px 16px 14px 18px;
    font-size: 16px;
    font-weight: 500;
    color: var(--lv-text);
    font-family: var(--lv-font);
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 260px;
    max-width: 380px;
    border-left: 4px solid #888;
    pointer-events: auto;
    transform: translateX(20px);
    opacity: 0;
    transition: transform 200ms cubic-bezier(0.2, 0.9, 0.3, 1.1), opacity 200ms ease-out;
}
.lv-toast.is-open    { transform: translateX(0);  opacity: 1; }
.lv-toast.is-closing { transform: translateX(20px); opacity: 0; }

.lv-toast-success { border-left-color: var(--lv-success); }
.lv-toast-error   { border-left-color: var(--lv-error); }
.lv-toast-info    { border-left-color: var(--lv-info); }

.lv-toast-message { flex: 1; line-height: 1.4; }

/* Leading type icon (the toast markup emits a .lv-toast-icon span). */
.lv-toast-icon { flex: 0 0 auto; display: flex; margin-top: 1px; color: var(--lv-text-soft); }
.lv-toast-success .lv-toast-icon { color: var(--lv-success); }
.lv-toast-error   .lv-toast-icon { color: var(--lv-error); }
.lv-toast-info    .lv-toast-icon { color: var(--lv-info); }

.lv-toast-close {
    background: none;
    border: 0;
    color: var(--lv-text-muted);
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    padding: 2px 4px;
    margin: -2px -4px;
    border-radius: 4px;
    font-family: inherit;
}
.lv-toast-close:hover { color: var(--lv-text-strong); background: var(--lv-bg-soft); }

/* ============================================================
 * ============================================================
 *  SITE LAYER - the six marketing pages. Scoped to `.site-page`.
 *
 *  Source of truth: Figma `VoSqlP0heUKjGYLydeAZwx`, page "Round 4".
 *  Rule for this layer: reproduce the comps, don't normalize. Where a value
 *  repeats across pages it became a token above; where it is unique it is
 *  written literally here rather than snapped to the nearest token.
 *
 *  NO bare element selectors below this line - they would land on the auth
 *  pages sharing this stylesheet.
 * ============================================================
 * ============================================================ */

.site-page {
    /* The marketing site is white end to end; the few bands that are not (the
     * grey FAQ band, the dark cards) set their own. Stating it here lets a band
     * be TRANSPARENT where the comp needs something behind it to show through,
     * instead of every band having to paint white defensively. */
    background: #fff;
    font-size: 16px;
    line-height: 24px;
    letter-spacing: var(--lv-site-track);
    color: var(--lv-site-ink);
    -webkit-font-smoothing: antialiased;
}
.site-page *,
.site-page *::before,
.site-page *::after { box-sizing: border-box; }

.site-page img { max-width: 100%; }

/* ---- Type primitives ----------------------------------------------------
 * Display sizes are the three the comps use: hero 104, section head 60,
 * footer CTA 56. Line-height is per-size (1.0 / 60 / 52) - it is NOT a
 * constant ratio in the comps, so it isn't expressed as one here.
 * The italic half of a headline is a <em> inside the same element, which is
 * how the designer built them (mixed roman + italic on one line). */
.site-display,
.site-display-lg,
.site-display-md {
    font-family: var(--lv-display);
    font-weight: 400;
    letter-spacing: var(--lv-site-track);
    margin: 0;
}
.site-display    { font-size: 104px; line-height: 1; }
.site-display-lg { font-size: 60px;  line-height: 60px; }
.site-display-md { font-size: 56px;  line-height: 52px; }
.site-display em,
.site-display-lg em,
.site-display-md em { font-style: italic; }

.site-lede {
    margin: 0;
    font-size: 20px;
    line-height: 28px;
    font-weight: 500;
    letter-spacing: var(--lv-site-track);
    color: var(--lv-site-ink-soft);
}
.site-body {
    margin: 0;
    font-size: 16px;
    line-height: 24px;
    font-weight: 500;
    letter-spacing: var(--lv-site-track);
    color: var(--lv-site-ink-soft);
}

/* ---- Buttons ------------------------------------------------------------
 * Three sizes (36 / 48 / 56) and one shape (fully round). The PRIMARY is
 * always maximum contrast against whatever it sits on - brand blue on the
 * white nav, black on the white footer, white on the dark hero. That is the
 * rule the comps follow, so the colour is a modifier, not a size property. */
.site-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    height: 36px;
    padding: 0 16px;
    border: 1px solid transparent;
    border-radius: 999px;
    font-family: inherit;
    font-size: 16px;
    line-height: 24px;
    font-weight: 500;
    letter-spacing: -0.025em;      /* tracking/tight, the app DS token */
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    box-shadow: var(--lv-site-shadow-btn);
    transition: background-color .15s, border-color .15s, color .15s, opacity .15s;
}
.site-btn-md { height: 48px; padding: 0 24px; }
.site-btn-lg { height: 56px; padding: 0 26px; font-size: 20px; line-height: 28px; gap: 12px; }

/* The trailing arrow is sized in `em`, so it always keeps the same proportion
 * to its label. The comps set it per button - 14.588 on 16px text, 18 on 20px -
 * which is a constant 0.91 ratio; a fixed px size made it read noticeably
 * larger on the small buttons than the big ones. 0.912em / 0.974em reproduces
 * both comp values to within half a pixel. */
.site-btn-arrow { flex: 0 0 auto; width: 0.912em; height: 0.974em; }

/* Solid primaries. Each carries the sheen overlay plus its own flat colour -
 * two backgrounds, exactly as the comps layer them. */
/* Solid primaries sit at 500, not the comp's semibold. The comp binds this text
 * to the shared `family/body` token - the stale Inter one - and Geist carries
 * visibly more weight than Inter at the same number, so transcribing 600
 * literally rendered heavier than the comp looks. Patrick 2026-08-07. */
.site-btn-primary,
.site-btn-dark {
    color: #fff;
    font-weight: 500;
    border-color: var(--lv-site-edge);
}
.site-btn-primary { background: var(--lv-site-sheen), var(--lv-brand); }
.site-btn-dark    { background: var(--lv-site-sheen), var(--lv-site-ink); }
.site-btn-primary { padding-inline: 16px; }
.site-btn-dark    { padding-inline: 26px; }

/* White button - the secondary on light ground, and the PRIMARY on the dark
 * hero (where white is the high-contrast choice). */
.site-btn-white {
    background: #fff;
    color: var(--lv-site-ink-btn);
    border-color: var(--lv-site-line);
}
.site-btn-white.site-btn-lg { color: var(--lv-site-ink); border-color: transparent; border-width: 2px; }

/* Ghost pill on dark ground - the hero eyebrow.
 *
 * The comp applies FIGMA'S GLASS EFFECT here: Light -45 deg at 80%, Refraction
 * 80, Depth 20, Dispersion 50, Frost 4, Splay 0. That is a simulated refractive
 * material - the bright rim is the BACKDROP BENT through a glass edge, not a
 * stroke - and the browser has no equivalent. This is an APPROXIMATION.
 *
 *   Frost 4        -> backdrop-filter: blur(). The one native equivalent.
 *   Light -45 deg  -> the inset shadows below.
 *   Dispersion 50  -> a small saturate() on the backdrop. A gesture at the
 *                     chromatic split, not a reproduction of it.
 *   Refraction/Depth -> NOT REPRODUCIBLE. Nothing in CSS bends a backdrop.
 *
 * WHY INSET SHADOWS, AND WHY NO BLUR ON THEM. An inset shadow's band width at
 * any point is -(offset . normal), so an offset pointing down-right is widest
 * at the top-left cap (x sqrt 2), tapers along the top and left edges, and is
 * EXACTLY ZERO at the top-right and bottom-left caps. That is the comp's
 * pattern, and it falls out of the geometry rather than being dialled in. Blur
 * destroys it: at blur 2px the top-right cap measured +68 where the comp has 0.
 * So the four shadows are hard-edged, and they are ordered bright-then-dark
 * because an earlier shadow paints OVER a later one - the darks are wider than
 * the brights on purpose and show only on the caps the brights cannot reach.
 *
 * MEASURED against a 2x in-context export of the comp (red channel, delta over
 * the pill's own fill; device px). Peak AND width both matter - matching peak
 * alone produced a rim 2-8x too thick, which reads as a fat halo rather than
 * the comp's hairline, and no peak-only metric could see it:
 *
 *              comp peak/width      this rule
 *   top-left        74 / 2.5px      geometry: 0.75px x sqrt 2
 *   top            +79 / 1.5px      primary bright, offset 0.75px
 *   left           +79 / 1.5px      primary bright
 *   top-right        0 / 0          both brights are zero here by construction
 *   bottom-left      0 / 0          both brights are zero here by construction
 *   right          +51 / 0.5px      secondary bright, offset 0.25px
 *   bottom         +48 / 0.5px      secondary bright
 *   off-axis caps  -20 (dark)       the two black shadows
 *
 * The right/bottom alphas look far too high next to the top/left one. They are
 * not: those bands are a QUARTER of a CSS pixel, so the compositor antialiases
 * them down to a fraction of their nominal value (at 0.28 the band measured +20
 * against the comp's +51). The alpha buys back what the coverage takes away.
 *
 * If an exact match is ever needed, the pill has to be baked into the hero
 * image with live text over it - which pins its width to one string, so it is
 * only safe once the copy is final. */
.site-btn-ghost {
    position: relative;
    background: rgba(255, 255, 255, 0.1);
    border: 0;
    color: #fff;
    /* 18, up from the 16 every other button inherits (Patrick, 2026-08-12).
       This is read as a line of copy rather than a control, so it takes the
       size of one. The phone tier below stops at 16, not 17: at 17 the home
       and FAQs eyebrows hit the 335px screen and wrap to a two-line block. */
    font-size: 18px;
    line-height: 28px;
    letter-spacing: var(--lv-site-track);
    -webkit-backdrop-filter: blur(4px) saturate(1.15);
            backdrop-filter: blur(4px) saturate(1.15);
    box-shadow:
        inset 0.75px 0.75px 0 rgba(255, 255, 255, 0.49),
        inset -0.25px -0.25px 0 rgba(255, 255, 255, 0.56),
        inset -0.4px 0.4px 0 rgba(0, 0, 0, 0.31),
        inset 0.4px -0.4px 0 rgba(0, 0, 0, 0.31);
}

/* Interaction states. NOT in the comps - lifted from the app zone's treatment
 * per Patrick 2026-08-07 and duplicated here rather than cross-loaded. Focus
 * is the app's DS ring; hover is a slight darken; active a slight press. */
.site-btn:hover  { opacity: .92; }
.site-btn:active { transform: translateY(1px); }
/* ...except the eyebrow, which is a SPAN, not a control - it takes .site-btn
 * only for the pill shape and typography. :hover still fires on a span, so
 * without this the whole pill (rim included) dimmed under a passing cursor and
 * implied it was clickable. Nothing to press, so nothing to feed back. */
.site-btn-ghost:hover  { opacity: 1; }
.site-btn-ghost:active { transform: none; }
.site-btn-primary:hover { background: var(--lv-site-sheen), var(--lv-brand-hover); opacity: 1; }
.site-btn-white:hover   { background: #fafafa; opacity: 1; }
.site-btn:focus-visible {
    outline: 0;
    border-color: #18181b;
    box-shadow: 0 0 0 3px rgba(0, 0, 0, .16);
}

/* ---- Page shell ---------------------------------------------------------
 * position:relative is STRUCTURAL - the nav pill and its two notches are
 * absolutely positioned and must resolve against the shell, not the initial
 * containing block. Without it they escape and drag document height with them
 * (the same trap app-main-inner guards against in the app zone). */
/* Capped at the comp width and centred. The comps exist only at 1440 and 375,
 * so above-1440 behaviour is undefined by the design; capping keeps every
 * measurement the designer chose (128px hero padding, the 800px nav pill, the
 * 480px lede, 280px footer columns) rather than stretching them apart on a wide
 * monitor. It also fixes the retina maths: the hero card is then always 1424px,
 * so the 2x asset is exactly 2848px and never upscales. (Patrick, 2026-08-07.) */
.site-shell { position: relative; background: #fff; max-width: 1440px; margin: 0 auto; }

/* <main> is a landmark only - it must not introduce a box that changes layout,
   so it stays display:contents and the sections inside lay out exactly as they
   did when they were direct children of the shell. */
#site-main { display: contents; }

/* ---- Hero band ----------------------------------------------------------
 * ONLY the hero is inset in a rounded card - content sections below it are
 * plain full-width white. The 8px gutter is the white margin the card floats
 * in, and it is what the nav notches align to. */
/* The gutter is the card's white margin on three sides; the BOTTOM is its own
 * value, deliberately NOT the --lv-site-pad-y half-gap the other boundaries use.
 * 40 + the next section's 90 = 130px card-edge to first heading, where the comp
 * draws 168 and a plain pad-y would give 180. Two reasons to run tighter than
 * the comp here (Patrick, 2026-08-14):
 *   1. Every OTHER boundary on this site was already tightened against the comp -
 *      section-to-section is 180 where the comp draws 248-320. The hero was the
 *      one gap still at comp spacing, which made it the loosest seam on the page.
 *   2. The hero is a rounded CARD with a photographic fill, so its bottom edge
 *      already separates. Whitespace after a full-bleed hero has to create that
 *      break; here it pays twice for one the card gives free. The card's own
 *      121px of sky below the CTA stacks on top, so the read-to-read void was
 *      301px - about a third of a laptop viewport.
 * Pricing and contact override this below and are unaffected; home, agencies and
 * faqs are the three pages this governs. */
.site-hero-band {
    background: #fff;
    padding: var(--lv-site-gutter) var(--lv-site-gutter) 40px;
}
/* Pricing is the exception: its hero card carries margin-bottom:-160px so the
 * plan table overlaps up into it, and that overlap is measured from the card,
 * so this band keeps the plain gutter. */
.site-pricing-band { padding-bottom: var(--lv-site-gutter); }

/* Same join as the pricing CTA band, one level up: where the grey FAQ band comes
 * next, this band fades into it and keeps only the gutter below the card. The
 * grey then runs up behind the card's rounded bottom corners instead of leaving
 * a white gap between the hero and the band. Comp confirms it - the contact
 * band's gutter runs 254 at the top to 249 at the bottom, and 249 is the FAQ
 * band's own colour. Contact is the only page whose hero is followed directly
 * by the band; keyed on that rather than on a page class. */
.site-hero-band:has(+ .site-faq) {
    padding-bottom: var(--lv-site-gutter);
    background: linear-gradient(180deg, #fff 0%, #f9f9f9 100%);
}

/* Every full-bleed background image is an <img> inside a <picture>, and only
 * the IMG is absolutely positioned. The <picture> wrapper still generates its
 * own box, so inside a flex card it counts as a flex item and consumes a whole
 * gap despite being zero-height - that was +40px on every CTA band, +70px on
 * the agencies CTA and +4px across the agencies steps row. display:contents
 * takes the wrapper out of flow so no gap is charged for it.
 *
 * It has to be position, NOT display:contents - that removes the picture's own
 * box but promotes its children into the flex container, and Chromium computes
 * <source> to display:block, so the zero-height phantom simply reappears one
 * level down and the gap is still spent. Absolute positioning removes the
 * whole subtree from flow instead.
 *
 * inset:0 sizes the wrapper to the card's padding box, which is the containing
 * block the images already resolved against, so the art does not move.
 * Keyed on the image class rather than on each card so a new background
 * cannot reintroduce the bug by being added to a card nobody listed here. */
picture:has(> .site-hero-bg),
picture:has(> .site-cta-bg),
picture:has(> .site-steps-bg),
picture:has(> .site-statement-bg),
picture:has(> .site-agcta-bg) { position: absolute; inset: 0; }

.site-hero-card {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 160px var(--lv-site-pad-xl) 120px;
    border: 1px solid var(--lv-site-line);
    border-radius: var(--lv-site-radius);
    overflow: hidden;
}
/* ---- SKY SHIFT: every sky photo sits 30px LEFT of centre ----------------
 * (Patrick, 2026-08-12.) Applied identically to .site-hero-bg here and to
 * .site-cta-bg / .site-agcta-bg below, so all three compose alike.
 *
 * `object-position` CANNOT do this. At >=1440 the chosen asset is 1424px wide
 * in a 1422px box, so cover scales it to fit the width exactly and there is no
 * horizontal slack to move within - slack only appears below ~1200, where the
 * box gets shorter. So the element carries the overscan itself: spanning
 * -65..W+5 puts the image's centre at W/2 - 30 while still covering the right
 * edge, with 5px to spare against subpixel rounding. General form, for shift S
 * and right margin m: width = W + 2m + 2S, left = -(m + 2S).
 * Every card involved is overflow:hidden, so the excess is clipped and the
 * rounded corners are untouched. Costs ~5% of upscale, invisible on cloud.
 *
 * `img.`, not a bare class: `.site-page img { max-width: 100% }` is (0,1,1)
 * and outranks a lone (0,1,0) class, so it clamps the calc straight back to
 * the card's width - the shift lands and the overscan silently does not,
 * leaving a bare strip of card down the right edge. Qualifying by the element
 * ties the specificity, and these rules sit later in the file. */
img.site-hero-bg {
    position: absolute;
    top: 0;
    bottom: 0;
    left: -65px;
    width: calc(100% + 70px);
    max-width: none;
    height: 100%;
    object-fit: cover;
    border-radius: var(--lv-site-radius);
    pointer-events: none;
}
.site-hero-inner {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 48px;
    width: 100%;
}
.site-hero-head {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 24px;
    width: 100%;
    text-align: center;
    color: #fff;
}
.site-hero-head .site-display { color: #fff; }

/* The HOME headline is long - "Conversion optimization that runs itself" - so
 * it carries its own size ladder instead of the shared 104. Each step is the
 * largest size that still breaks it onto the intended TWO lines at that width:
 * the line costs ~9.8px of width per px of type (measured: 84 needs 819px, 72
 * needs 705, 64 needs 626), and the hero's text box is 926 at 1024 but only
 * 736 at 834 and 670 at 768. Below 700 two lines are impossible at any
 * readable size; 48 is the largest that still keeps "optimization" from
 * overflowing a 320px screen, where 56 and 64 both run off the edge.
 * TWO CLASSES, so this also outranks the bare `.site-display` in the phone
 * tier - hence the phone value has to be restated here rather than inherited.
 * Same trap documented on .site-pricing-hero below. */
.site-home-hero .site-display { font-size: 84px; line-height: 84px; }
@media (min-width: 835px) and (max-width: 1024px) {
  .site-home-hero .site-display { font-size: 72px; line-height: 72px; }
}
@media (min-width: 701px) and (max-width: 834px) {
  .site-home-hero .site-display { font-size: 64px; line-height: 64px; }
}
@media (max-width: 700px) {
  .site-home-hero .site-display { font-size: 48px; line-height: 48px; }
}
.site-hero-sub {
    margin: 0;
    max-width: 610px;
    /* 22/30, not the 20/28 the rest of the site's lede type uses - the hero
       sub is the one place it carries a whole paragraph on its own, and the
       column is 40px narrower than the rest of the hero (Patrick,
       2026-08-12). 30 keeps the original 1.4 leading ratio at the larger
       size. The phone tier keeps its own 18/24 below. */
    font-size: 22px;
    line-height: 30px;
    font-weight: 500;
    letter-spacing: var(--lv-site-track);
    color: #fff;
}
.site-hero-cta { display: flex; flex-direction: column; align-items: center; gap: 16px; }
.site-hero-cta .site-btn-lg { width: 240px; }
.site-hero-note { margin: 0; color: #fff; }

/* ---- White-on-sky text shadow -------------------------------------------
 *
 * Every white text run that sits DIRECTLY on the sky photo gets a shadow so it
 * separates from the lighter parts of the image. All eight background files
 * (hero-bg, cta-bg, ag-cta and the five *-hero images) are byte-identical - the
 * same sky - so the hero bands and both CTA bands are one problem, not two.
 *
 * SCOPED ON PURPOSE - never put this on a bare .site-display / .site-display-md.
 * Those same classes are used on the white section backgrounds, where a shadow
 * would be grime. Every selector below names its sky container.
 *
 * Also never put it on the CONTAINERS (.site-hero-head, .site-contact-intro,
 * .site-cta-text): text-shadow inherits, and those wrap the CTA buttons and the
 * glass eyebrow pill, which have their own backgrounds and must stay clean.
 *
 * TWO TIERS, both read from the two --lv-sky-text-shadow-* variables in :root
 * (tune there, not here):
 *   body-size runs use Patrick's value as given (2026-08-09);
 *   display-size runs use em units so the shadow tracks the font size by
 *   itself. That matters here - .site-display is 104px on desktop and steps
 *   down to 64/56/48/44 across the breakpoints, and .site-display-md to 32px.
 *   In em there is one declaration and nothing to keep in sync; in px it would
 *   need an override inside eight media queries and would silently drift.
 * At 104px this resolves to ~2.1px/3.1px, at 44px to ~0.9px/1.3px. */
.site-hero-head .site-display,
.site-cta-text .site-display,
.site-contact-lead .site-display,
.site-contact-help .site-display-md {
    text-shadow: var(--lv-sky-text-shadow-lg);
}
/* NOT .site-cta-eyebrow - that eyebrow is the glass pill now, and per the note
 * above a pill carries its own background and must stay shadow-free. */
.site-hero-sub,
.site-hero-note,
.site-cta-note,
.site-contact-sub,
.site-agcta-sub {
    text-shadow: var(--lv-sky-text-shadow-sm);
}

/* ---- Section ------------------------------------------------------------ */
.site-section {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--lv-site-gap);
    padding: var(--lv-site-pad-y) var(--lv-site-pad-x);
    background: #fff;
}
/* Header row: display headline left, lede right, baselines aligned at the
 * bottom (align-items:end in the comps, not center). */
.site-section-head {
    display: flex;
    gap: var(--lv-site-gap-xl);
    align-items: flex-end;
    width: 100%;
}
.site-section-head .site-display-lg { flex: 1 0 0; min-width: 0; }
.site-section-head .site-lede { width: 480px; flex: 0 0 auto; padding: 8px 0; }

/* The scroll sentinel: a 1px marker at the top of the shell. When it leaves the
   viewport the nav becomes its "stuck" pill (see site.js). Zero-height so it
   cannot affect layout. */
.site-nav-sentinel { position: absolute; top: 0; left: 0; width: 1px; height: 1px; pointer-events: none; }

/* ---- Nav ----------------------------------------------------------------
 * An 800px pill that hangs from the top edge of the hero card: square on top,
 * 32px rounded on the bottom. The two 12px SVG notches that join it to the
 * edge are emitted by the layout, not drawn here. */
.site-nav {
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    z-index: 10;
    width: var(--lv-nav-w);
    background: #fff;
    border-radius: 0 0 32px 32px;
    box-shadow: var(--lv-site-shadow-nav);
}
.site-nav-bar {
    display: flex;
    align-items: center;
    gap: 24px;
    height: 64px;
    padding: 0 14px 0 24px;
    list-style: none;               /* kill the native disclosure marker */
    cursor: default;
}
.site-nav-bar::-webkit-details-marker { display: none; }
.site-nav-left,
.site-nav-right { display: flex; flex: 1 0 0; min-width: 0; align-items: center; }
.site-nav-left  { gap: 32px; }
.site-nav-right { gap: 10px; justify-content: flex-end; }

/* Desktop: no hamburger, no panel. The disclosure exists in the DOM at every
   width so the mobile rules below need no separate markup, but above 834px it
   is inert - and `pointer-events: none` on the summary means a stray click on
   the bar can't open a panel the customer can't see. */
.site-nav-toggle, .site-mnav { display: none; }
.site-nav-bar { pointer-events: none; }
.site-nav-bar a, .site-nav-bar button { pointer-events: auto; }

/* ---- Nav: scrolled state (`73:10591`) -----------------------------------
 * Comp `home-scroll`. The nav detaches from the top edge and becomes a fully
 * rounded pill floating 10px down, with a border and a deeper shadow. Only
 * the radius, border and shadow can transition - position cannot - so the
 * slide is done with transform, which is compositor-only and costs no layout.
 * Desktop only: the 375 comps show no scrolled state, so the phone nav is
 * left as the comps have it. */
@media (min-width: 701px) {
    /* The notches join the HANGING pill to the card's top edge. Once the nav
       detaches and floats they have nothing to join, and for the first ~20px of
       scroll they sit just above the floating pill as two white divots (they
       stay at their document position, top 8, while the nav is fixed at 10).
       They are following siblings of the nav, so the stuck class can reach them. */
    .site-nav.is-stuck ~ .site-notch { display: none; }

    .site-nav.is-stuck {
        position: fixed;
        top: 10px;
        border: 1px solid var(--lv-site-line-soft);
        border-radius: 999px;
        box-shadow: 0 63px 19px rgba(0,0,0,.01), 0 28px 14px rgba(0,0,0,.02), 0 7px 7.5px rgba(0,0,0,.03);
    }
    @media (prefers-reduced-motion: no-preference) {
        /* NO transition on border-radius. `position` cannot transition, so the
           nav teleports between hanging and floating either way - animating
           only its corners meant that on the way BACK the notches reappeared
           instantly (they are display-toggled) while the pill spent 180ms
           morphing from 999px to a square top. The notches are cut to meet a
           square top, so for that 180ms they did not fit what they were drawn
           against. Shape now changes on the same frame as the notches.
           The drop-in below still plays when the nav detaches; it is a
           transform, so it animates the element as a whole rather than
           reshaping it mid-flight. */
        .site-nav.is-stuck { animation: site-nav-drop .18s ease both; }
        @keyframes site-nav-drop {
            from { transform: translateX(-50%) translateY(-12px); }
            to   { transform: translateX(-50%) translateY(0); }
        }
    }
}

.site-nav-home { display: block; line-height: 0; }
.site-nav-logo { display: block; width: 85.155px; height: 20px; }

/* The concave joins where the pill meets the card's top edge. They sit at the
 * card's top (8px = the shell gutter), flush against each side of the 800px
 * pill: 720px - 12px on the left, 720px on the right of centre. */
.site-notch {
    position: absolute;
    top: var(--lv-site-gutter);
    z-index: 9;
    display: block;
}
.site-notch-l { left: 50%; margin-left: calc(var(--lv-nav-w) / -2 - 12px); }
.site-notch-r { left: 50%; margin-left: calc(var(--lv-nav-w) / 2); }

.site-nav-link {
    font-size: 16px;
    line-height: 24px;
    font-weight: 500;
    letter-spacing: -0.025em;
    color: var(--lv-site-ink);
    text-decoration: none;
    opacity: .7;
    transition: opacity .15s;
}
.site-nav-link:hover { opacity: 1; }
.site-nav-link:focus-visible {
    outline: 0;
    opacity: 1;
    border-radius: 4px;
    box-shadow: 0 0 0 3px rgba(0, 0, 0, .16);
}

/* ---- Footer -------------------------------------------------------------
 * Same on all six pages: a CTA block on the left, two link columns, then a
 * hairline and the bottom row. */
.site-footer {
    display: flex;
    flex-direction: column;
    gap: var(--lv-site-gap);
    padding: 80px var(--lv-site-pad-x) 24px;
    background: #fff;
}
.site-footer-top { display: flex; gap: var(--lv-site-gap); align-items: flex-start; width: 100%; }

.site-footer-cta {
    display: flex;
    flex: 1 0 0;
    min-width: 0;
    flex-direction: column;
    justify-content: flex-end;
    gap: 32px;
    align-items: flex-start;
}
.site-footer-mark { display: block; width: 48px; height: 48px; }
/* No width cap. These were pinned at 360/320 inside a container that is ~900px
   wide, which forced the headline to three lines and the body to two and made
   the CTA a tall narrow strip beside two short link columns. Left to size
   themselves each sits on ONE line wherever there is room, which uses the width
   the footer already has and brings the block's height into line with the
   links. They still wrap normally once the container is narrower than the text. */
.site-footer-cta .site-display-md,
.site-footer-cta .site-body { width: auto; }
/* The container's flex `gap` applies between EVERY child, so 32px sat between
   the headline and its own subline as well as around the mark and the buttons.
   Tighten the gap to pull those two lines together, then give the mark and the
   button row their 32 back - they are separate blocks and want the air. */
.site-footer-cta { gap: 12px; }
.site-footer-cta .site-display-md { margin-top: 20px; }
.site-footer-actions { margin-top: 20px; }
.site-footer-actions { display: flex; gap: 12px; align-items: flex-start; }

/* Natural width, never flex-grow. A fixed/growing column makes a box far wider
   than its links, and because the links are left-aligned inside it the dead
   space lands BETWEEN the two columns - which reads as "Product is scrunched
   against the CTA and then there is a hole". Sizing to content and letting the
   row gap do the spacing puts the gap where it is intended and keeps it equal
   at every width. .site-footer-cta keeps flex:1 0 0, so it absorbs the slack
   and the two columns stay together at the right. */
.site-footer-col { display: flex; flex-direction: column; gap: 8px; width: auto; flex: 0 0 auto; }
/* +20 BETWEEN the link columns only (Patrick, 2026-08-10). It has to be a
   margin on the adjacent siblings, not a bigger column-gap on .site-footer-top:
   that gap also sets the CTA-to-Product distance, which stays at 64. Reset in
   the phone tier below, where the columns wrap and a leading margin would
   indent whichever column starts a new row. */
.site-footer-col + .site-footer-col { margin-left: 20px; }
.site-footer-col-head {
    margin: 0;
    font-size: 18px;
    line-height: 28px;
    font-weight: 500;
    color: var(--lv-site-ink);
}
.site-footer-link {
    font-size: 16px;
    line-height: 24px;
    font-weight: 500;
    letter-spacing: var(--lv-site-track-1);
    color: var(--lv-site-ink-soft);
    text-decoration: none;
    transition: color .15s;
}
.site-footer-link:hover { color: var(--lv-site-ink); }
.site-footer-link:focus-visible {
    outline: 0;
    color: var(--lv-site-ink);
    border-radius: 4px;
    box-shadow: 0 0 0 3px rgba(0, 0, 0, .16);
}

.site-footer-bottom { display: flex; flex-direction: column; gap: 24px; width: 100%; }
.site-footer-rule { height: 1px; width: 100%; background: var(--lv-site-line); }
.site-footer-meta { display: flex; gap: var(--lv-site-gap); align-items: flex-start; width: 100%; }
.site-footer-meta-left { display: flex; flex: 1 0 0; min-width: 0; gap: 12px; align-items: center; }
.site-footer-meta-mark { display: block; width: 18px; height: 18px; }
.site-footer-tagline { width: 320px; text-align: right; }

/* ---- Stage row (home: explore / refine / maintain) ----------------------
 * Three equal columns. Each is a 400px-tall blue card with a glass panel over
 * a texture, then a caption below it. The card's inner product mock ships as
 * an exported image (Patrick, 2026-08-07) rather than being rebuilt in HTML. */
.site-stage-row { display: flex; gap: 32px; align-items: flex-start; width: 100%; }
.site-stage { display: flex; flex: 1 0 0; min-width: 0; flex-direction: column; gap: 28px; }

.site-stage-card {
    display: flex;
    flex-direction: column;
    justify-content: center;
    height: 400px;
    padding: 40px;
    border: 1px solid var(--lv-site-line-soft);
    background: var(--lv-site-card-blue);
    overflow: hidden;
    box-shadow: var(--lv-site-shadow-card);
}

.site-stage-caption { display: flex; flex-direction: column; gap: 8px; padding-right: 24px; }
.site-stage-name {
    margin: 0;
    font-size: 20px;
    line-height: 28px;
    font-weight: 500;
    letter-spacing: var(--lv-site-track);
    color: var(--lv-site-ink);
}
.site-stage-caption .site-body { font-size: 20px; line-height: 28px; }

/* ---- Bento grid (home: "Built by the team behind ClickMagick") ----------
 * A 520px band of 8px-gapped tiles: two stat tiles stacked beside a tall image
 * tile on the left half; a stat + image row above a wide customers tile on the
 * right.
 *
 * The comp gave this section pt160/pb88 against a py160 default. Both of those
 * are now handled by the uniform 180 rhythm instead: the top comes from the
 * hero-adjacent rule and the bottom is the standard half-gap, which lands
 * within 2px of the comp's 88 anyway. The class survives only for the phone
 * override below. */

/* RATIO, not a fixed 520px height. The comp's grid is 1280x520, and every tile
 * inside it is an exported image sized to land pixel-exact at that one width.
 * A hardcoded height kept all 520 of those pixels while the width shrank with
 * the viewport, so `object-fit: cover` ate the sides: measured 27% of every
 * image cropped at 1024, 42% at 834, 47% at 768 - iPad in both orientations,
 * where the images simply stopped resizing. Scaling the whole grid by ratio
 * keeps each tile's box proportional, so nothing crops. At >=1440 the shell
 * caps at 1280 and this still resolves to exactly 520. */
.site-bento { display: flex; gap: 8px; aspect-ratio: 1280 / 520; width: 100%; }
.site-bento-half { display: flex; flex: 1 0 0; min-width: 0; gap: 8px; }
.site-bento-half:nth-child(2) { flex-direction: column; }
.site-bento-col { display: flex; flex: 1 0 0; min-width: 0; flex-direction: column; gap: 8px; }
/* GRID, not flex, and deliberately so: this row pairs a PADDED stat tile with an
 * unpadded image tile. `flex: 1 0 0` distributes free space to CONTENT boxes, so
 * the stat's 64px of padding lands on top of its share and the two tiles come out
 * 346/282 instead of 314/314. Grid tracks size the border box, so 1fr 1fr is
 * actually equal. Same reason the halves below use grid. */
.site-bento-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; flex: 1 0 0; min-height: 0; }
/* The customers tile is one exported image and the "150,000+ / Customers
 * worldwide" wording is baked into it, top-left, exactly as the comp has it.
 * There used to be an absolutely positioned HTML copy of that text over the
 * bottom-left corner as well, so the figure appeared twice in the same tile.
 * The stat lives in the image's alt text instead. Nothing here is positioned. */
.site-bento-customers { display: flex; flex: 1 0 0; min-height: 0; }
.site-bento-customers .site-tile { flex: 1 0 0; }

.site-stat {
    display: flex;
    flex: 1 0 0;
    min-width: 0;
    min-height: 0;
    flex-direction: column;
    justify-content: space-between;
    /* Scales with the grid. The bento is now a RATIO, so a stat tile is 256px
     * tall at 1440 and 122 at 701 - the comp's 32/44/18 clips inside the short
     * one, and .site-stat's overflow:hidden means it clips SILENTLY. These
     * interpolate over the same 701-1440 span the gutters use, continuous
     * rather than stepped for the reason documented at --lv-site-pad-x, and
     * they resolve to exactly the comp's values at 1440 and above. Below 701
     * the phone block's flat values win (later in the file, same specificity). */
    padding: clamp(16px, calc(16px + (100vw - 701px) * 16 / 739), 32px);
    background: #e9f3fd;
    overflow: hidden;
}
.site-stat-icon { display: block; line-height: 0; color: var(--lv-site-ink); }
.site-stat-body { display: flex; flex-direction: column; gap: 6px; }
.site-stat-num {
    margin: 0;
    font-size: clamp(24px, calc(24px + (100vw - 701px) * 20 / 739), 44px);
    line-height: clamp(28px, calc(28px + (100vw - 701px) * 20 / 739), 48px);
    font-weight: 500;
    letter-spacing: var(--lv-site-track);
    color: var(--lv-site-ink);
}
.site-stat-label {
    margin: 0;
    font-size: clamp(13px, calc(13px + (100vw - 701px) * 5 / 739), 18px);
    line-height: clamp(18px, calc(18px + (100vw - 701px) * 10 / 739), 28px);
    font-weight: 400;                 /* Geist Regular - the one place the site uses 400 */
    letter-spacing: var(--lv-site-track);
    color: var(--lv-site-ink-soft);
}

.site-tile { flex: 1 0 0; min-width: 0; min-height: 0; overflow: hidden; }
.site-tile picture,
.site-tile img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* ---- CTA band ("Start free today") --------------------------------------
 * Shared: home, pricing, FAQs, legal. Same inset-card treatment as the hero
 * but radius 32 (not 24) and NO top padding - it butts against the section
 * above, so only the sides and bottom carry the 8px gutter. */
/* Comp values, and they are deliberate: NO top padding, so the card butts flush
 * against whatever is above it, and only the 8px gutter below. On pricing that
 * flush join is what puts the card directly against the FAQ band with no white
 * between them. Do not put the section half-gap on this band. */
.site-cta-band { background: #fff; padding: 0 var(--lv-site-gutter) var(--lv-site-gutter); }

/* When the grey FAQ band comes next (pricing is the only page that orders the
 * blocks cta-then-faq), this band fades into it, so the grey reaches up behind
 * the card and its rounded bottom corners sit ON grey. Flat white leaves the
 * card ending on a hard white-to-grey seam a few pixels below its own corners.
 *
 * Conditional because the comp genuinely differs per page: the pricing band's
 * gutter runs 255 -> 249 top to bottom, while home's - which is followed by the
 * white footer - stays flat 255 the whole way down. Keyed on what actually
 * follows rather than on a page class, so reordering the blocks cannot leave a
 * gradient fading into the wrong thing. Same treatment .site-agcta already has
 * on agencies, where the FAQ band likewise comes next. */
.site-cta-band:has(+ .site-faq) { background: linear-gradient(180deg, #fff 0%, #f9f9f9 100%); }

/* The mirror, for home, which orders the blocks faq-then-cta: the grey carries
 * DOWN behind the card's rounded top corners instead of leaving a white notch
 * at each one against the grey band directly above.
 *
 * NOTE this one is a deliberate departure from the comp, on Patrick's call
 * (2026-08-08). Home's comp is flat grey to the FAQ band's last row and flat
 * white from the CTA band's first, so the hard edge is in the design; pricing
 * is the page where the comp actually fades. Treating both joins the same way
 * was the explicit ask. */
.site-faq + .site-cta-band { background: linear-gradient(180deg, #f9f9f9 0%, #fff 100%); }

.site-cta-card {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 40px;
    padding: 120px var(--lv-site-pad-xl);
    border: 1px solid var(--lv-site-line);
    border-radius: var(--lv-site-radius-lg);
    overflow: hidden;
}
img.site-cta-bg {
    position: absolute;
    top: 0;
    bottom: 0;
    left: -65px;
    width: calc(100% + 70px);
    max-width: none;
    height: 100%;
    object-fit: cover;
    border-radius: var(--lv-site-radius-lg);
    pointer-events: none;
}
.site-cta-inner {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 48px;
    width: 100%;
}
.site-cta-mark { display: block; width: 48px; height: 48px; }
/* gap 24, matching .site-hero-head. The eyebrow here used to be plain 20px text
 * at a 12px gap; it is the glass pill now (same markup as every hero's), and a
 * pill needs the hero's breathing room rather than a text line's. */
.site-cta-text { display: flex; flex-direction: column; align-items: center; gap: 24px; width: 100%; text-align: center; color: #fff; }
/* 72px, DOWN from the shared .site-display 104 (Patrick, 2026-08-10). Scoped to
 * the CTA bands - the heroes keep 104. Phone overrides this to 48 below, and
 * the agencies CTA to 44, both already in the max-width:700 block. */
.site-cta-text .site-display { color: #fff; font-size: 72px; line-height: 72px; }
.site-cta-actions { position: relative; display: flex; flex-direction: column; align-items: center; gap: 16px; }
.site-cta-actions .site-btn-lg { width: 240px; }
.site-cta-note {
    margin: 0;
    font-size: 16px;
    line-height: 24px;
    font-weight: 500;
    letter-spacing: var(--lv-site-track);
    color: #fff;
}

/* ---- FAQ teaser ("Any questions?") --------------------------------------
 * Shared: home, agencies, pricing, contact. Note the section background is
 * #f9f9f9, the only non-white section band on the site, and padding is py 120
 * (not the 160 default). Native <details>, so no JS: the plus/minus swap is
 * CSS off [open]. */
.site-faq {
    display: flex;
    /* -gap-x, not -gap: this one is HORIZONTAL, and the laptop tier used to step
       it to 40. --lv-site-gap stays the vertical token. */
    gap: var(--lv-site-gap-x);
    align-items: flex-start;
    justify-content: center;
    /* 120 is the BAND's own inset, not the section rhythm - this is the one
     * block here with a background of its own, so its top edge is the boundary
     * and the white above it is paid entirely by the section before. Comp
     * value; leave it alone when the section half-gap changes. */
    padding: 120px var(--lv-site-pad-x);
    background: #f9f9f9;
}
/* 40/60 with a CAP, rather than a fixed 720 that a media query later swaps for
   a percentage. At and above 1440 the cap binds and the split is exactly the
   comp's 496/720; below it the card shrinks continuously. The swap-at-1439
   version stepped the card 720 -> 744, i.e. wider on a narrower window. */
.site-faq-intro { display: flex; flex: 1 1 40%; min-width: 0; flex-direction: column; justify-content: flex-end; gap: 24px; }
.site-faq-intro .site-lede { width: auto; max-width: 480px; }
/* Two links share this row - "All FAQs" and "Ask a question" - so they sit
   side by side rather than inheriting the intro column's 24px stack gap, which
   read as two unrelated items. Wraps rather than squeezing on a phone. */
.site-faq-links { display: flex; flex-wrap: wrap; gap: 28px; }
.site-faq-link {
    width: auto;
    max-width: 480px;
    font-size: 20px;
    line-height: 28px;
    font-weight: 500;
    letter-spacing: var(--lv-site-track);
    color: var(--lv-site-ink);
    text-decoration: underline;
    text-underline-position: from-font;
}
.site-faq-link:hover { color: var(--lv-brand); }

/* Chrome ONLY - no sizing. The same card is reused on /faqs/ inside
   .site-faqcat, where it fills its category and must NOT pick up the two-column
   block's 720 cap (putting the cap on the bare class shrank all five accordions
   there from 784 to 720). Sizing lives on the child selector below. */
.site-faq-card {
    background: #fff;
    border: 1px solid var(--lv-site-line);
    border-radius: var(--lv-site-radius);
    box-shadow: var(--lv-site-shadow-nav);
    overflow: hidden;
}
.site-faq > .site-faq-card { flex: 1 1 60%; min-width: 0; max-width: 720px; }
.site-faq-row { border-bottom: 1px solid var(--lv-site-line); }
.site-faq-row:last-child { border-bottom: 0; }

.site-faq-q {
    display: flex;
    gap: 24px;
    align-items: flex-start;
    padding: 24px;
    cursor: pointer;
    list-style: none;                    /* kill the native disclosure marker */
    font-size: 20px;
    line-height: 32px;
    font-weight: 500;
    letter-spacing: var(--lv-site-track);
    color: var(--lv-site-ink);
}
.site-faq-q::-webkit-details-marker { display: none; }
.site-faq-qt { flex: 1 0 0; min-width: 0; }
.site-faq-q:focus-visible { outline: 0; box-shadow: inset 0 0 0 3px rgba(0, 0, 0, .16); }

/* An open row sets its own question-to-answer gap. The answer carries no top
 * padding, so without this the question's full 24px bottom padding becomes the
 * gap where the comp has 8 - 16px per open row, which compounds on /faqs/
 * where one row per category starts open. */
.site-faq-row[open] .site-faq-q { padding-bottom: 8px; }

.site-faq-toggle {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    /* Nudged down to optically centre on the 32px line. This is a RELATIVE
     * offset, not margin-top: under align-items:flex-start a 4px top margin
     * made the 32px toggle the tallest thing in the row, so every question row
     * measured 36px against the comp's 32 and each one leaked 4px. */
    position: relative;
    top: 4px;
    border-radius: 999px;
    background: var(--lv-site-line);
    color: rgba(0, 0, 0, 0.56);
}
/* Closed shows plus, open shows minus - one glyph visible at a time. */
.site-faq-toggle .lv-icon:nth-child(2) { display: none; }
.site-faq-row[open] .site-faq-toggle .lv-icon:nth-child(1) { display: none; }
.site-faq-row[open] .site-faq-toggle .lv-icon:nth-child(2) { display: inline; }

/* A <div>, not a <p>: the reused in-app answers carry their own <p> tags. */
.site-faq-a p { margin: 0 0 12px; }
.site-faq-a p:last-child { margin-bottom: 0; }
.site-faq-a {
    margin: 0;
    padding: 0 80px 24px 24px;
    font-size: 18px;
    line-height: 24px;
    font-weight: 500;
    letter-spacing: var(--lv-site-track-1);   /* -1% here, not the -3% default */
    color: var(--lv-site-ink-soft);
}

/* Stage card art fills the card. The exported render already carries the blue
 * texture, so the card's own background colour is only a load-time fallback. */
.site-stage-card { padding: 0; }
.site-stage-card img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* ---- Split row: a 720px shot beside a 480px column of points -----------
 * SHARED, and named for the shape rather than for either section that uses
 * it: "Nothing goes live until you approve it" and "You get the wins."
 * both render this exact row, so a change to one is a change to both. That is
 * the point - the two sit one above the other on the home page and any drift
 * between them is visible. Only the shot and the copy differ.
 * Header row is a fixed 120px tall in the comp (two lines of display-lg). */
.site-section-start { align-items: flex-start; }
/* 60/40 with caps and an explicit gap, not two fixed widths pushed apart by
   space-between. At 1440 the container is 1280, the gap is 80 and both caps
   bind, so the row is exactly the comp's 720 + 80 + 480; below that everything
   shrinks together. The old shape swapped to percentages at 1439 and stepped
   the shot 720 -> 743.6 - wider on a narrower window. */
.site-split-row { display: flex; align-items: center; gap: var(--lv-site-gap-xl); width: 100%; }

.site-split-card {
    flex: 1 1 60%;
    min-width: 0;
    max-width: 720px;
    height: auto;
    /* 720x480 IS 3:2, so this is the comp's own ratio, not a substitute for it. */
    aspect-ratio: 3 / 2;
    border: 1px solid var(--lv-site-line-soft);
    background: var(--lv-site-card-blue);
    overflow: hidden;
    box-shadow: var(--lv-site-shadow-card);
}
.site-split-card img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* The "You get the wins" shot is the DASHBOARD, which is 2:1 where the card draws
   3:2, so `cover` crops 320 source px of width. Anchor it RIGHT: that takes
   the crop out of the app's left nav rail and keeps the entire result column,
   which is what the section is about. The ratio itself stays shared. */
.site-split-card-dash img { object-position: right; }

.site-split-points { display: flex; flex: 1 1 40%; min-width: 0; flex-direction: column; gap: 48px; max-width: 480px; }
.site-split-point { display: flex; gap: 16px; align-items: flex-start; }
.site-split-ico {
    flex: 0 0 24px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 32px;
    color: rgba(0, 0, 0, 0.4);
}
.site-split-text { display: flex; flex: 1 0 0; min-width: 0; flex-direction: column; gap: 8px; padding-right: 32px; }
.site-split-title {
    margin: 0;
    font-size: 20px;
    line-height: 28px;
    font-weight: 500;
    letter-spacing: var(--lv-site-track);
    color: var(--lv-site-ink);
}
.site-split-text .site-body { font-size: 20px; line-height: 28px; }

/* ---- Testimonial row ---------------------------------------------------
 * Sits at the foot of the ClickMagick bento section, as its last child, so it
 * picks up that section's gap above it and its padding below rather than
 * carrying any spacing of its own. */
.site-quotes { display: flex; gap: 32px; align-items: flex-start; width: 100%; padding-left: 4px; }
.site-quote { display: flex; flex: 1 0 0; min-width: 0; flex-direction: column; gap: 16px; margin: 0; padding-right: 24px; }
.site-quote-text {
    margin: 0;
    font-size: 20px;
    line-height: 28px;
    font-weight: 500;
    letter-spacing: var(--lv-site-track);
    color: var(--lv-site-ink);
}
/* Attribution: circular headshot + a two-line name/business block. The two
 * lines are 24px each, so at the intended two lines the 48px headshot is
 * exactly as tall as the text and flex-start reads identically to centre.
 * flex-start is for the case that DOES differ: a business name long enough
 * to wrap to a third line (measured: below a 238px column, which is the
 * narrow columns at 768 and 701). Centre floats the photo down against the
 * middle of that block; flex-start keeps it level with the name. */
.site-quote-by {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    font-size: 16px;
    line-height: 24px;
    font-weight: 500;
    letter-spacing: var(--lv-site-track);
    color: var(--lv-site-ink-soft);
}
/* The <picture> is the flex item, not the <img> - size both (same as .site-tile). */
.site-quote-by picture,
.site-quote-face { display: block; flex: 0 0 auto; width: 48px; height: 48px; }
.site-quote-face { border-radius: 50%; object-fit: cover; }
.site-quote-who { display: flex; min-width: 0; flex-direction: column; }
.site-quote-name { color: var(--lv-site-ink); }

/* ============================================================
 * SITE - PHONE (<= 700px)
 *
 * The marketing site DOES support phones (unlike /app/ - see CLAUDE.md).
 * Source: the designer's 375-wide comps. Desktop is untouched: every rule
 * below lives inside this query.
 *
 * WAS 834, WHICH IS AN iPAD (2026-08-09). 834 is iPad portrait, so the whole
 * phone layout - burger nav, stacked footer columns, phone padding - was being
 * served to a tablet. The old bound was not chosen from the content: it came
 * from the desktop nav pill being a hard-coded 800px, which needs ~832 of
 * viewport. The pill is now fluid in the tablet tier, so nothing forces the
 * boundary up there any more.
 *
 * 700 because it sits below iPad mini portrait (744) and above every phone
 * portrait width (<= 440), so tablets get the tablet layout and phones get the
 * phone one. Phones in LANDSCAPE (844-932) now get the tablet layout too,
 * which is the better fit for that aspect ratio.
 * ============================================================ */
@media (max-width: 700px) {

  /* ---- Chrome: the pill becomes a rounded bar with a hamburger ---- */
  .site-nav {
      top: 16px;
      left: 16px;
      right: 16px;
      transform: none;
      width: auto;
      border: 1px solid var(--lv-site-line-soft);
      border-radius: 32px;
      box-shadow: 0 63px 19px rgba(0,0,0,.01), 0 28px 14px rgba(0,0,0,.02), 0 7px 7.5px rgba(0,0,0,.03);
  }
  .site-nav-bar { height: auto; gap: 24px; padding: 8px; pointer-events: auto; cursor: pointer; }
  .site-nav-left { gap: 8px; }
  .site-nav-right { flex: 0 0 auto; gap: 6px; }

  /* The desktop link row collapses into the panel. */
  .site-nav-bar .site-nav-link { display: none; }

  .site-nav-toggle {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 32px;
      height: 32px;
      flex: 0 0 32px;
      color: var(--lv-site-ink);
  }
  .site-nav-toggle-close { display: none; }
  .site-nav[open] .site-nav-toggle-open  { display: none; }
  .site-nav[open] .site-nav-toggle-close { display: inline-flex; }

  .site-nav-logo { width: 76.639px; height: 18px; }

  /* Buttons shrink to the comp's 32px bar height, and the primary drops its
     arrow - the comp has no icon on the mobile nav button. */
  .site-nav-bar .site-btn { height: 32px; padding: 0 12px; font-size: 14px; line-height: 20px; }
  .site-nav-bar .site-btn-arrow { display: none; }

  /* ---- The expanded panel ---- */
  .site-nav[open] .site-nav-right { display: none; }
  /* Open: the chevron and the logo sit at opposite ends of the panel header
     (the comp spreads them), and the panel picks up the soft sky wash. */
  .site-nav[open] .site-nav-left { flex: 1 1 auto; justify-content: space-between; }
  .site-nav[open] {
      background: linear-gradient(180deg, #e9f3fd 0%, #fff 45%);
      border-radius: 24px;
  }
  .site-nav[open] .site-nav-bar { padding: 12px 16px; }
  /* Scope the panel to [open]. An explicit `display` on a <details> child
     overrides the UA rule that hides it while closed, so without this the panel
     is visible in the collapsed state. */
  .site-nav[open] .site-mnav {
      display: flex;
      flex-direction: column;
      gap: 32px;
      padding: 24px 16px 16px;
  }
  .site-mnav-links { display: flex; flex-direction: column; align-items: center; gap: 8px; }
  .site-mnav-link {
      padding: 8px 12px;
      font-size: 24px;
      line-height: 32px;
      font-weight: 500;
      letter-spacing: var(--lv-site-track);
      color: var(--lv-site-ink);
      text-decoration: none;
  }
  .site-mnav-link.is-current { color: var(--lv-brand); }
  .site-mnav-actions { display: flex; flex-direction: column; gap: 8px; }
  .site-mnav-actions .site-btn { width: 100%; }

  /* The notches only make sense under the hanging desktop pill. */
  .site-notch { display: none; }
  /* No scrolled state on phone: the 375 comps do not show one, and the stuck
     rules are scoped to min-width 835 so nothing needs neutralising here. */

  /* ---- Hero: full-bleed, no gutter, bottom corners only ---- */
  .site-hero-band { padding: 0; }
  /* The :has() rule above out-specifies the shorthand, so undo its gutter here. */
  .site-hero-band:has(+ .site-faq) { padding-bottom: 0; }
  .site-hero-card {
      padding: 120px 20px 64px;
      border: 0;
      border-radius: 0 0 24px 24px;
  }
  .site-hero-bg { border-radius: 0 0 24px 24px; }
  .site-hero-inner { gap: 40px; }
  .site-hero-head { gap: 24px; }
  .site-hero-cta { width: 100%; }

  .site-display    { font-size: 64px; line-height: 64px; }
  .site-display-lg { font-size: 40px; line-height: 44px; }
  .site-display-md { font-size: 40px; line-height: 44px; width: auto; }

  .site-btn-ghost { font-size: 16px; line-height: 24px; height: auto; min-height: 36px; padding: 6px 16px; white-space: normal; }
  .site-hero-sub  { font-size: 18px; line-height: 24px; }

  /* ---- Sections stack ---- */
  .site-section { padding: 80px 20px; gap: 40px; align-items: stretch; }
  .site-section-tight { padding-bottom: 56px; }
  .site-section-head { flex-direction: column; align-items: flex-start; gap: 16px; }
  .site-section-head .site-lede { width: auto; padding: 0; }
}

/* ---- Phone: section internals stack --------------------------------------
 * Everything below 834 is a single column. These rules exist to kill the
 * desktop fixed widths (720 cards, 800 nav, 480/280 columns) that would
 * otherwise force a horizontal scrollbar on a phone. Per-section fidelity
 * against the 375 comps is refined as each mobile section is built. */
@media (max-width: 700px) {
  /* Bento */
  /* aspect-ratio OFF as well as height:auto - stacked, the grid's height is
     the sum of its tiles, and leaving the 1280/520 ratio on would size it from
     the width again and crush the whole stack into a 136px box. */
  .site-bento { flex-direction: column; height: auto; aspect-ratio: auto; }
  .site-bento-half { flex-direction: column; }
  .site-bento-half:nth-child(2) { flex-direction: column; }
  .site-bento-col { gap: 8px; }
  .site-bento-row { grid-template-columns: 1fr; }
  /* Size to content once stacked. The desktop bento is a FIXED 520px box, so
     its halves and rows are `flex: 1 0 0` (with min-height: 0 on two of them)
     to DIVIDE that height between themselves. Here the container is
     height:auto, so there is no height to divide: basis-0 items resolve to
     zero, and with the automatic minimum defeated by that min-height: 0 they
     collapse outright and their tiles paint over whatever section follows.
     Measured before this rule: the bento box was 368px tall holding 536px of
     tiles - 168px of spill at every width from 700 down. */
  .site-bento-half,
  .site-bento-col,
  .site-bento-row,
  .site-bento-customers,
  .site-bento .site-tile { flex: 0 0 auto; min-height: auto; }
  .site-stat { min-height: 168px; padding: 24px; }
  .site-stat-num { font-size: 32px; line-height: 40px; }
  .site-stat-label { font-size: 16px; line-height: 24px; }
  .site-tile { width: 100%; }

  /* Stage row */
  .site-stage-row { flex-direction: column; gap: 32px; }
  .site-stage-caption { padding-right: 0; }
  .site-stage-caption .site-body,
  .site-stage-name { font-size: 18px; line-height: 26px; }

  /* Approval */
  .site-split-row { flex-direction: column; gap: 40px; }
  /* Stacked, so the 60/40 basis would be measured against HEIGHT - reset both. */
  .site-split-card { flex: 0 0 auto; width: 100%; max-width: none; height: auto; aspect-ratio: 3 / 2; }
  .site-split-points { flex: 0 0 auto; width: 100%; max-width: none; gap: 32px; }
  .site-split-text { padding-right: 0; }
  .site-split-title,
  .site-split-text .site-body { font-size: 18px; line-height: 26px; }

  /* Quotes */
  .site-quotes { flex-direction: column; gap: 32px; padding-left: 0; }
  .site-quote { padding-right: 0; }

  /* FAQ */
  .site-faq { flex-direction: column; gap: 32px; padding: 80px 20px; }
  /* max-width too, not just width: the base caps these at 480 with max-width
     now (it used to be a flat `width: 480px`), and a bare `width: auto` no
     longer releases it - which pinned this column to 480 inside a 660 phone. */
  .site-faq-intro .site-lede,
  .site-faq-link { width: auto; max-width: none; }
  .site-faq-card { width: 100%; }
  .site-faq > .site-faq-card { flex: 0 0 auto; max-width: none; }
  .site-faq-q { padding: 20px; gap: 16px; font-size: 18px; line-height: 26px; }
  .site-faq-a { padding: 0 20px 20px; font-size: 16px; line-height: 24px; }

  /* CTA band */
  .site-cta-band { padding: 0; }
  .site-cta-card { padding: 80px 20px; border: 0; border-radius: 24px 24px 0 0; gap: 32px; }
  .site-cta-bg { border-radius: 24px 24px 0 0; }
  .site-cta-inner { gap: 32px; }
  .site-cta-text .site-display { font-size: 48px; line-height: 52px; }

  /* Footer */
  .site-footer { padding: 64px 20px 24px; gap: 40px; }
  /* The CTA takes a full row of its own; the link columns then WRAP onto the
     next row rather than each becoming its own 100%-wide block. */
  .site-footer-top { flex-direction: row; flex-wrap: wrap; gap: 40px 64px; }
  /* No inter-column margin here - see the base rule. Once the columns wrap,
     a leading margin indents whichever one starts a row. */
  .site-footer-col + .site-footer-col { margin-left: 0; }
  .site-footer-cta { flex: 1 1 100%; gap: 24px; }
  .site-footer-cta .site-display-md,
  .site-footer-cta .site-body { width: auto; }
  .site-footer-meta { flex-direction: column; gap: 16px; }
  .site-footer-tagline { width: auto; text-align: left; }
}

/* ---- Between the phone layer and the comp width (835 - 1439) ------------
 * The desktop composition is built from fixed columns (a 720px card beside a
 * 480px one, plus 80px section padding) which together need the full 1440. On
 * anything narrower those columns overflow the viewport. This tier makes them
 * proportional so no width can produce a horizontal scrollbar; the iPad pass
 * refines the proportions, but nothing here is ever broken in the meantime.
 * Desktop at >= 1440 is untouched. */
@media (min-width: 701px) and (max-width: 1439px) {
  /* Horizontal gutters and gaps are NOT stepped here any more - the
     --lv-site-pad-x* / --lv-site-gap-x* variables interpolate them across this
     whole tier, and the two-column blocks carry a 60/40 basis with a cap on
     their BASE rules instead of swapping to percentages at this boundary.
     Re-adding a flat 40px to any of them, or a fixed width that a rule here
     then overrides, reintroduces the content-gets-wider-as-the-window-shrinks
     jump at 1439. */
  .site-section-head .site-lede { width: auto; max-width: 480px; flex: 1 1 40%; }


  /* The pill was a hard 800px, which is what forced the phone tier to start at
     834. Set on the VARIABLE so the notches (which derive from it) follow.
     Content measures 609px intrinsic, so 640 is the floor with a little slack.

     The 84px side allowance is not arbitrary and is not a step. With the old
     32px the clamp only engaged below 832, so 834 - the top of the portrait-iPad
     tier, and a real device width - still got the full 800px pill in an 834px
     window: 17px of air per side, which reads as pinned to the edges. 84 gives
     42 per side there, and because it is a continuous allowance rather than a
     breakpoint the whole 701-884 band lands on the same 42 instead of stepping
     from 17 to 16 at 832. Nothing moves at 884 and above, so desktop and
     landscape iPad are byte-identical.

     clamp, not min: at the very bottom of the tier 100vw - 84 would drop the
     pill under the content width and start crowding the links, so the 640 floor
     takes over and the gap narrows instead - still wider than the 16px it had
     before. Verified by measurement across 1440/1024/900/884/834/820/810/768/701. */
  :root { --lv-nav-w: clamp(640px, calc(100vw - 84px), 800px); }

  /* Row gap 40, COLUMN gap 64: the column gap is the visible space between the
     CTA and the link columns, and 40 reads as cramped once the columns are only
     as wide as their links. (The link columns add 20 on top - see the base
     .site-footer-col + .site-footer-col rule.) */
  .site-footer-top { gap: 40px 64px; }
}

/* ---- Pricing page ------------------------------------------------------
 * The hero card is a fixed 640 tall and carries a -160px bottom margin so the
 * plan table overlaps up into it - that overlap is the design, not a spacing
 * accident, so it is reproduced as a negative margin rather than by nudging
 * the card. */
.site-pricing-hero { height: 640px; margin-bottom: -160px; }
.site-pricing-hero .site-hero-inner { gap: 0; }
/* 72, not the 104 the home hero uses - same as the FAQs and agencies heroes.
   This hero is the ONLY one with a card pulled up 160px into it, so its usable
   height is 480px, not 640. At 104 a three-line headline pushed the subhead
   underneath that card and buried it (153px at 1024). The size is the fix, not
   the overlap: the overlap is the comp's design.

   The full ladder is 72 -> 56 -> 44, and the 56 rung is SHARED with the FAQs
   and agencies heroes - see the 701-1439 block in the agencies section. This
   hero was missing that rung entirely, so it held 72 across every iPad width
   while the other two dropped to 56 (found 2026-08-12, measured at 1366/1194/
   1024/834/768). Line-height is 72 rather than the 76 it carried before for
   the same reason - the three heroes are ONE ladder, so a value that differs
   is drift, not intent. */
.site-pricing-hero .site-display { font-size: 72px; line-height: 72px; }
/* This hero used to carry two extra overrides below 1025 - a forced <br> in the
   headline and a 130px top pad (down from the shared 160) to pull the wrapped
   text back up out of the overlapping plan card. Both existed only because the
   headline was stuck at 72px there. On the shared ladder it is 56px, which fits
   "First you win, then you pay" on ONE line at every width down to 701, so the
   break never fires and the pull-back has nothing to correct. Removing them put
   the headline at the same 221px offset as the agencies and FAQs heroes and
   IMPROVED the gap to the plan card, 63px -> 93px (measured 2026-08-12).
   If new copy ever overflows here, size it on the shared ladder first - do not
   reintroduce a page-specific break. */

/* NO background here. The hero card overflows its band by 160px so the pricing
 * card can sit up inside the sky, and an opaque background on this wrapper
 * paints straight over that overflow - which is what flattened the overlap and
 * cut the sky off in a hard line at the card's top edge. The page itself
 * supplies the white; only .site-pricecard below is a white card. z-index keeps
 * that card painting ABOVE the sky, which is the whole point of the overlap. */
.site-pricing {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0 var(--lv-site-pad-x) var(--lv-site-pad-y);
}
/* The white card that overlaps up into the sky. Was three traffic-tier plan
 * columns; now the calculator on the left and the fee on the right. The card
 * chrome (radius 40, hairline, nav shadow, overflow hidden) is unchanged from
 * the comp - only what sits inside it changed. The 12px padding is the comp's:
 * it is what insets the grey calculator panel from the card's edge, and the
 * panel's 28 radius is this 12 subtracted from the card's 40. */
.site-pricecard {
    display: flex;
    align-items: stretch;
    width: 100%;
    padding: 12px;
    background: #fff;
    border: 1px solid var(--lv-site-line);
    border-radius: 40px;
    box-shadow: var(--lv-site-shadow-nav);
    overflow: hidden;
}
/* Calculator left, fee panel right at a FIXED 400 (the comp's width, padding
 * included) - the calculator takes whatever is left, because it is the half
 * with a wrapping sentence in it and the fee list is a fixed set of short
 * lines. */
.site-pricecalc {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 40px;
    padding: 48px;
    background: #f5f5f5;
    border-radius: 28px;
}
/* Interpolated 400 -> 340 across the same 1440..1024 curve as the gutters,
   rather than dropped to 340 the moment the laptop tier opened. The step is
   what wrapped three of the six feature lines all at once and, back when the
   receipt still absorbed the columns' height difference, blew the receipt row
   gaps to 67.5px. Sliding it means the wrap arrives one line at a time and
   costs nothing but a slightly taller fee column when it does.

   The curve is still anchored at 1024 but is only ever WALKED down to 1181 -
   below that the card stacks and this basis is overridden (see the 1180 block
   further down), so the narrowest this panel is actually seen at is ~367, not
   the 340 floor. Left anchored at 1024 so the curve keeps matching the gutters'. */
.site-priceplan { flex: 0 0 clamp(340px, calc(340px + (100vw - 1024px) * 60 / 348), 400px); }

/* Kept from the plan-table build - the fee panel reuses this chrome verbatim.
 * The old `.site-plan + .site-plan` border rule went with the extra columns,
 * and the single divider went with THIS comp: the grey panel beside it is the
 * separation now, so a hairline as well read as a double edge. */
.site-plan {
    display: flex;
    min-width: 0;
    flex-direction: column;
    gap: 40px;
    padding: 48px;
}
/* The icon and the text as ONE box, so that when the card stacks the pair can
 * shrink to the width of the longest line and be centred over the button as a
 * unit (see the stacked block further down). Side by side it is inert: a column
 * flex item that stretches to the panel width, with `gap: inherit` reproducing
 * the icon-to-text spacing .site-plan would have applied itself, at every tier
 * (40 base, 32 at 701-1371, 28 on phone). Verified pixel-identical on desktop. */
.site-plan-lead { display: flex; flex-direction: column; gap: inherit; min-width: 0; }
.site-plan-icon { display: block; width: 48px; height: 48px; }
.site-plan-body { display: flex; flex-direction: column; gap: 20px; }
.site-plan-head { display: flex; flex-direction: column; gap: 8px; }
.site-plan-name {
    margin: 0;
    font-size: 20px; line-height: 28px; font-weight: 500;
    letter-spacing: var(--lv-site-track);
    color: var(--lv-site-ink-soft);
}
.site-plan-price {
    margin: 0;
    display: flex; align-items: baseline; gap: 4px; flex-wrap: wrap;
    font-size: 28px; line-height: 36px; font-weight: 500;
    letter-spacing: var(--lv-site-track);
    color: var(--lv-site-ink);
}
.site-plan-rule { height: 1px; width: 100%; background: var(--lv-site-line); }
.site-plan-feats { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 12px; }
.site-plan-feat {
    display: flex; gap: 10px; align-items: flex-start;
    font-size: 18px; line-height: 24px; font-weight: 500;
    letter-spacing: var(--lv-site-track-1);
    color: var(--lv-site-ink-soft);
}
.site-plan-feat .lv-icon { flex: 0 0 18px; margin-top: 3px; color: #111; }

/* Reuses the .site-display-md face (STIX, 400, -3%) at the comp's 48/48. */
.site-pricecalc-h { font-size: 48px; line-height: 48px; color: var(--lv-site-ink); }

/* The sentence is TWO flex rows, not a flowing paragraph with a <br>. The
 * selects are 48px tall and the text is 32, so as inline content each line box
 * has to be ~62px and the half-leading then pushes the first control 14px
 * below where the comp puts it. Rows of centred flex items sit exactly on the
 * control height, and they still wrap - each word-group is its own item, so a
 * narrow card breaks between the groups instead of overflowing. */
.site-pricecalc-sentence {
    margin: 0;
    display: flex; flex-direction: column; gap: 14px;
    font-size: 24px; line-height: 32px; font-weight: 500;
    letter-spacing: var(--lv-site-track);
    color: rgba(0, 0, 0, 0.88);
}
.site-pricecalc-line {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
    min-height: 48px;
}
/* CONTROLLED BREAK POINT. The cost select and the words that finish the clause
 * are ONE flex item, so a narrow row can never stop between them and strand
 * "in costs." on a line of its own - which is exactly what the plain five-item
 * row did (Patrick, 2026-08-12). At 375 it now breaks as
 *
 *     My site makes [$500k]
 *     a month, with [70%] in costs.
 *
 * It is a nested flex row, not a nowrap text span, because the select sitting
 * between the words has to stay vertically centred on them exactly as it does
 * in the outer row.
 *
 * IT GROUPS THE SELECT, NOT THE WHOLE CLAUSE. Grouping "with [x%] in costs."
 * and splitting "a month, with" in two was tried first and is wrong twice over:
 * it turns the space between "a month," and "with" from a TEXT space into a
 * flex gap, which is ~5px wider and reads as a double space at the base 10px
 * (visible at 1440), and the extra gap costs ~3px of row width, which was
 * enough to put 768 back over the edge. Keeping the phrase whole adds no gap at
 * all, and measured across all 56 revenue x cost combinations it breaks the
 * same or better at every width.
 *
 * `gap: inherit` - gap is not an inherited property, so this explicitly takes
 * the parent's computed value, and the space between the select and "in costs."
 * tracks the outer row's at every tier (10px base, 8px stacked).
 *
 * Above the phone tier the sentence fits on one row and this changes nothing:
 * same items, same gaps, identical measured width. Verified 375 = 2 rows with
 * zero overflow, degrading to 3 tidy rows below 360 rather than overflowing. */
.site-pricecalc-grp { display: flex; align-items: center; gap: inherit; }

/* ---- The receipt --------------------------------------------------------
 * The three results sit on a white panel with a torn-off bottom edge, low in the
 * grey panel (the fee column beside it is the taller of the two, so there is
 * always slack). It used to be pinned hard to the bottom - see the note on
 * .site-pricecalc-tail below for why the slack is now split instead.
 *
 * The panel used to GROW into that slack with the rows distributed by
 * space-between, which read correctly at 1440 because the two columns are
 * within ~15px of each other there. It was a trap everywhere else: the receipt
 * was absorbing the entire height mismatch between the two columns, and the
 * laptop tier makes that mismatch enormous - it shrinks the calculator's type
 * and padding (heading, dt, figures, both panel paddings) while the fee column
 * keeps its own height, freeing ~96px, and the shrunken rows give back another
 * 24px. All 120px landed in two gaps: 60px each, so the three rows drifted from
 * 7.5px apart to 67.5px apart on a 1px change of window (2026-08-11).
 *
 * So the panel now sits at its natural height and the rows keep a constant 8px
 * rhythm (the comp's own spacing at 1440, where this measured 7.5). Slack shows
 * as a little more of the grey panel above the receipt, which is the
 * calculator's own background and reads as breathing room rather than as three
 * rows that have drifted apart. Measured constant from 1441 down to the phone
 * tier, which sets its own 12px.
 *
 * #ebebeb, not --lv-site-line: the line token is 8% black, which resolves to
 * #ebebeb over WHITE but to #e1e1e1 over the grey panel this edge sits on.
 * The comp draws one continuous colour around the whole panel, so it is the
 * flat value here. Inside the panel the background IS white, so the row rules
 * use the token and land on the same colour. */
/* THE SLACK IS SPLIT, NOT PARKED (Patrick, 2026-08-12). The tail used to be
 * `flex: 1 1 auto` with `justify-content: flex-end`, which pinned the receipt to
 * the panel's bottom edge and therefore put the ENTIRE height mismatch between
 * the two columns into one gap - the one between the sentence and the receipt.
 * At 1440 that is invisible because the columns are the same height and the
 * slack is ~0, but the laptop tier shrinks the calculator's type and padding
 * while the fee column keeps its height, and the leftover all landed in the
 * middle: measured 40px at 1440 against 75px at 1366 and 89px at 1025, so the
 * three lines hugged the top and the receipt hugged the bottom with a hole
 * between them.
 *
 * Now the tail sizes to its content (`flex: 0 1 auto`) and the free space is
 * divided by AUTO MARGINS on the first and last children - half above the
 * heading, half below the receipt - so the group sits centred in the column and
 * the middle gap stays at the panel's own 28/40.
 *
 * Auto margins, NOT `justify-content: center`: centring an overflowing flex
 * column clips the top UNREACHABLY (the same trap that hid the onboarding logo,
 * see the app-shell notes in tokens.css). Auto margins resolve to 0 when there
 * is no free space, so at 1440 - where there is none - this changes nothing.
 * The receipt's own row rhythm is untouched; that is a separate mechanism and
 * the reason it is fixed rather than distributed is written above. */
.site-pricecalc-tail { flex: 0 1 auto; display: flex; flex-direction: column; min-height: 0; }
.site-pricecalc-h    { margin-top: auto; }
.site-pricecalc-tail { margin-bottom: auto; }
.site-pricecalc-receipt {
    flex: 0 1 auto;
    display: flex; flex-direction: column;
    padding: 28px 32px;
    background: #fff;
    border: 1px solid #ebebeb;
    border-bottom: 0;
    border-radius: 16px 16px 0 0;
}
/* Scalloped tear-off. One 24x9 tile repeated with `round`, so a whole number
 * of notches always fits no matter how wide the card is - no half-notch at the
 * right edge. The tile is 9 tall for an 8px strip and is pulled up 1px, giving
 * the hairline over each notch's apex room to draw at full weight instead of
 * being clipped by the strip's top edge. The notch itself is transparent: the
 * grey of .site-pricecalc shows through it. */
.site-pricecalc-edge {
    height: 9px;
    margin-top: -1px;
    border-left: 1px solid #ebebeb;
    border-right: 1px solid #ebebeb;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='9' viewBox='0 0 24 9'%3E%3Cpath d='M0 0H24V9H20A8 8 0 0 0 4 9H0Z' fill='%23fff'/%3E%3Cpath d='M0 8.5H4M20 8.5H24M4 9A8 8 0 0 1 20 9' fill='none' stroke='%23ebebeb'/%3E%3C/svg%3E");
    background-size: 24px 9px;
    background-position: left bottom;
    background-repeat: round no-repeat;
}

.site-pricecalc-out {
    margin: 0;
    flex: 0 0 auto;
    display: flex; flex-direction: column; gap: 8px;
}
.site-pricecalc-row {
    display: flex; align-items: center; justify-content: space-between; gap: 16px;
    padding-bottom: 11px;
    border-bottom: 1px solid var(--lv-site-line);
    letter-spacing: -0.02em;
}
.site-pricecalc-row dt {
    font-size: 20px; line-height: 28px; font-weight: 500;
    color: rgba(0, 0, 0, 0.56);
}
/* "one-time" is the whole offer in two words - the fee does not recur - so it
   is the one thing in the receipt's labels that carries emphasis. Geist is a
   variable face (100-900), so 700 here is a real weight and not a synthesised
   one. Full ink as well as the weight: at 56% black the weight change alone is
   too quiet to register at a glance. The phrase lands three times on this card
   on purpose (Patrick, 2026-08-11) - the heading italicises it, this bolds it,
   and the fee column names it - so don't dedupe them. */
.site-pricecalc-row dt strong { font-weight: 700; color: var(--lv-site-ink); }
/* The three figures are the point of the block, so they get the display face at
 * a size nothing else in the card competes with. */
.site-pricecalc-fig {
    margin: 0;
    font-family: var(--lv-display);
    font-size: 40px; line-height: 48px; font-weight: 400;
    color: var(--lv-site-ink);
    text-align: right;
}
.site-pricecalc-foot {
    margin: 0;
    padding-top: 11px;
    font-size: 14px; line-height: 20px; font-weight: 500;
    letter-spacing: var(--lv-site-track);
    color: var(--lv-site-ink-soft);
}

/* ---- The raised dollar sign --------------------------------------------
 * PRICING PAGE ONLY, and only on figures that are being READ as a price - the
 * receipt's three results and the fee itself. Deliberately NOT on the revenue
 * <select>: an <option>'s text cannot carry markup, and a value inside a form
 * control is being picked rather than displayed, so it keeps the ordinary
 * full-height sign.
 *
 * `position: relative`, NOT `vertical-align: super`. Super raises by a
 * font-derived amount and its box counts toward the line, which would push the
 * receipt rows apart and change the height the card lays out at; a relative
 * offset paints the glyph somewhere else and leaves every box where it was.
 *
 * The offset is in em so one rule serves both sizes it appears at (the 40px
 * STIX figures and the 28px Geist fee) and neither breaks at a breakpoint.
 *
 * `line-height: 0` is load-bearing, and not for the reason it looks like. A
 * SMALLER font-size on a baseline-aligned inline box does not shrink its line
 * box - it inherits the parent's line-height as a length and gets a much
 * bigger half-leading, so the box grows ABOVE the parent's strut and takes the
 * line with it. Measured: it added 3px to every dropdown row carrying a
 * dollar sign and 4px to the two receipt rows, which is why those rows were
 * taller than the "6 days" row beside them. Zeroing it makes the span
 * contribute nothing to the line; painting is unaffected, since line-height
 * never clips a glyph. */
.site-cur {
    font-size: 0.56em;
    line-height: 0;
    position: relative;
    top: -0.42em;
    margin-right: 0.03em;
}

/* Holds the sign and its digits together as ONE flex item, so the 4px gap in
   .site-plan-price falls between the amount and the qualifier - not inside
   the amount. */
.site-plan-amt { white-space: nowrap; }

/* "$100" and its qualifier share a line; the qualifier is not display-sized. */
.site-plan-per {
    font-size: 18px; line-height: 26px; font-weight: 500;
    color: var(--lv-site-ink);
}

/* ---- Inline select ------------------------------------------------------
 *
 * DUPLICATED from the app's Tom Select skin (css/app/components.css
 * .ts-wrapper.single .ts-control + .ts-dropdown), NOT imported - the two asset
 * zones are self-contained and may never cross-load. The app's design tokens do
 * not exist here either, so the values are inlined at the numbers the app
 * resolves to: stroke rgba(0,0,0,.08), radius 8, shadow 0 1px 1px rgba(0,0,0,.05),
 * hover stroke rgba(0,0,0,.16), focus #18181b + a 3px rgba(0,0,0,.16) ring,
 * dropdown shadow 0 8px 24px rgba(0,0,0,.12), active row #f4f4f5.
 * If the app's control is restyled, this does NOT follow automatically.
 *
 * The real <select> stays in the DOM and keeps working with scripting off; it
 * is only visually replaced once site.js swaps in .site-sel-btn. */
.site-sel { position: relative; display: inline-block; vertical-align: middle; }
.site-sel select,
.site-sel-btn {
    appearance: none; -webkit-appearance: none;
    height: 48px; box-sizing: border-box;
    display: inline-flex; align-items: center; gap: 10px;
    margin: 0; padding: 0 42px 0 16px;
    border: 1px solid rgba(0, 0, 0, 0.08);
    border-radius: 12px;
    background: #fff;
    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
    font-family: inherit;
    font-size: 24px; line-height: 1; font-weight: 500;
    letter-spacing: var(--lv-site-track);
    color: var(--lv-site-ink);
    cursor: pointer;
    text-align: left;
}
.site-sel select:hover,
.site-sel-btn:hover { border-color: rgba(0, 0, 0, 0.16); }
.site-sel select:focus-visible,
.site-sel-btn:focus-visible {
    outline: none;
    border-color: #18181b;
    box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.16);
}
/* The chevron. On the enhanced button it is a real element so it can rotate;
 * on the bare select it is drawn by the wrapper so no pseudo-element is needed
 * on a replaced element (which cannot carry one). */
.site-sel::after {
    content: "";
    position: absolute; right: 16px; top: 50%;
    width: 9px; height: 9px; margin-top: -6px;
    border-right: 2px solid #71717a;
    border-bottom: 2px solid #71717a;
    transform: rotate(45deg);
    pointer-events: none;
}
.site-sel.is-open::after { margin-top: -2px; transform: rotate(225deg); }

.site-sel-list {
    position: absolute; z-index: 20;
    top: calc(100% + 6px); left: 0; min-width: 100%;
    margin: 0; padding: 4px; list-style: none;
    /* Tall enough for the LONGEST of the three ladders (costs, 8 rows at 44).
       At 320 the last option of the two longer lists sat below the fold of the
       dropdown - so the top of the revenue range and the highest cost figure,
       the two the visitor is most likely to be hunting for, were the ones you
       had to scroll to find. Bounded by .site-pricecard's `overflow: hidden`:
       the tightest case (the LIFT list, lowest on the card, at 1440) clears
       the card's bottom edge by 68px, so nothing is clipped. Re-measure if a
       ladder grows - there is no flip-up behaviour to save it. */
    max-height: 368px; overflow-y: auto;
    background: #fff;
    border: 1px solid rgba(0, 0, 0, 0.08);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.site-sel-list[hidden] { display: none; }
.site-sel-opt {
    padding: 10px 12px;
    border-radius: 6px;
    font-size: 18px; line-height: 24px; font-weight: 500;
    letter-spacing: var(--lv-site-track-1);
    color: var(--lv-site-ink);
    cursor: pointer;
    white-space: nowrap;
}
.site-sel-opt[aria-selected="true"],
.site-sel-opt.is-active { background: #f4f4f5; }
/* Once site.js has swapped in the button, the real select is hidden but stays
   in the DOM as the value of record. */
.site-sel.is-enhanced select { display: none; }

/* The CTA is the last thing in the fee column, and `margin-top: auto` pins it
 * to the bottom of the card: the two columns are near enough the same height
 * that the comp shows them ending together, and if the calculator ever grows
 * taller this keeps the button on the card's baseline rather than leaving it
 * floating with dead space under it. */
.site-priceplan-cta { display: flex; flex-direction: column; align-items: center; gap: 12px; margin-top: auto; }
.site-priceplan-cta .site-btn { width: 100%; }
.site-pricing-note {
    margin: 0;
    font-size: 16px; line-height: 24px; font-weight: 500;
    letter-spacing: var(--lv-site-track-1);
    color: var(--lv-site-ink-soft);
    text-align: center;
}

/* 1371, NOT 1439. These are the card's COMPENSATIONS for running out of width,
   and with the gutter interpolating rather than stepping they are not needed
   until far below the tier the rest of the site uses. Starting them at 1439
   applied a ~1100px-wide layout to a 1420px window, which is what narrowed the
   fee column to 340, wrapped three of its six feature lines, and (through
   .site-pricecalc-out) drove the receipt row gaps to 67.5px.

   1371 is measured, not chosen: stepping the real page 1px at a time, the
   calculator sentence's first line wraps at exactly vw 1371 (the grey panel
   crossing ~798px). So the comp layout is intact down to 1372 and compact mode
   takes over on the very pixel it would otherwise have gone to three lines.
   Do not nudge this without re-measuring - an earlier pass put it at 1359 off a
   probe that had the wrong select padding, and the 12px of daylight that left
   showed up as a 42px bulge in the card between 1371 and 1360. */
@media (min-width: 701px) and (max-width: 1371px) {
  .site-pricecalc { padding: 32px; gap: 28px; border-radius: 20px; }
  .site-pricecalc-h { font-size: 40px; line-height: 40px; }
  .site-pricecalc-sentence { font-size: 22px; }
  .site-pricecalc-receipt { padding: 24px; }
  .site-pricecalc-row dt { font-size: 18px; line-height: 24px; }
  .site-pricecalc-fig { font-size: 32px; line-height: 40px; }
  /* The fee column's own 400 -> 340 give-back is on the base rule now, as a
     clamp; below 900 or so the receipt labels wrap to two lines, which is the
     intended graceful failure - nothing is set to nowrap. */
  .site-plan { padding: 32px; gap: 32px; }
  .site-sel select, .site-sel-btn { font-size: 22px; height: 44px; padding: 0 38px 0 14px; }
}

@media (max-width: 700px) {
  /* Mobile pricing is NOT a stacked desktop table. The comp (`73:7182`) uses a
     segmented tier selector and shows ONE plan at a time - the visitor picks
     their traffic band and sees the plan for it. Driven by three radios that
     sit before the cards in the DOM, so plain sibling selectors do the work
     and no JavaScript (and no :has()) is involved.

     The hero keeps its overlap here too, just deeper: pb 280 / mb -250, so the
     selector's white label sits over the bottom of the hero image. */
  .site-pricing-hero { height: auto; margin-bottom: -250px; padding: 120px 20px 280px; }
  /* The base rule is `.site-pricing-hero .site-display` - two classes, so it
     outranks the bare `.site-display { font-size: 64px }` in this tier no
     matter that this one is inside a media query. Repeat the two-class
     selector, same as the FAQs and agencies heroes do. */
  .site-pricing-hero .site-display { font-size: 44px; line-height: 48px; }
  /* The bottom padding used to be paid by .site-pricing-cta, which is now
     inside the card - so it moves here. */
  .site-pricing { padding: 8px 8px 64px; }

  /* The tier selector and the one-plan-at-a-time radios went with the traffic
     tiers. What is left stacks: calculator above, fee panel below. The full
     iPad + phone pass is still to come, so this is a sane stack, not the final
     mobile design. */
  /* 24px here rather than the 32 the wider stacked tier uses - same reasoning,
     matched to THIS tier's 24px calculator padding. */
  .site-pricecard { flex-direction: column; gap: 24px; padding: 8px; border-radius: 32px; box-shadow: var(--lv-site-shadow-card); }
  .site-pricecalc { padding: 24px; gap: 24px; border-radius: 24px; }
  .site-pricecalc-h { font-size: 32px; line-height: 34px; }
  .site-pricecalc-sentence { font-size: 18px; line-height: 28px; gap: 10px; }
  .site-pricecalc-receipt { padding: 20px; }
  /* Stacked, so the figure gets the full width and "Under a day" - the longest
     value any of the three can take - has somewhere to go. */
  .site-pricecalc-row { flex-direction: column; align-items: flex-start; gap: 2px; padding-bottom: 10px; }
  .site-pricecalc-row dt { font-size: 16px; line-height: 22px; }
  .site-pricecalc-fig { font-size: 30px; line-height: 36px; text-align: left; }
  .site-pricecalc-out { gap: 12px; }
  .site-pricecalc { flex: 0 0 auto; }
  .site-priceplan { flex: 0 0 auto; }
  .site-plan { padding: 24px; gap: 28px; }
  .site-plan-icon { width: 36px; height: 36px; }
  .site-plan-body { gap: 16px; }
  .site-plan-head { gap: 4px; }
  .site-plan-name { font-size: 18px; line-height: 24px; }
  .site-plan-price { font-size: 24px; line-height: 32px; }
  .site-plan-per { font-size: 16px; line-height: 22px; }
  .site-plan-feats { gap: 8px; }
  .site-plan-feat { font-size: 16px; line-height: 24px; letter-spacing: var(--lv-site-track-1); }
  .site-sel select, .site-sel-btn { font-size: 18px; height: 40px; padding: 0 34px 0 12px; border-radius: 10px; }
  .site-sel::after { right: 13px; }

  .site-pricing-note { font-size: 14px; line-height: 20px; letter-spacing: var(--lv-site-track-1); }
}

/* Stacked fee panel: CENTRE the text block on the button, lines still left-aligned.
 *
 * Once the card stacks, the panel runs the full width of the card while its CTA
 * stays capped at 400 and centred - so the icon, the fee and all six feature
 * lines hung ~170px to the LEFT of their own button (Patrick, 2026-08-12).
 *
 * The first fix squeezed the panel's CONTENT into a 400 column with symmetric
 * padding, which put every line on the button's LEFT EDGE. That is not the same
 * thing: the longest line is ~280 against the button's 400, so the block was
 * flush left under a much wider button rather than centred on it. What is wanted
 * is the block centred on the button's centre line, with the lines inside it
 * still ragged-right (Patrick, same day).
 *
 * So the pair shrink-wraps (`width: fit-content` on the wrapper) and centres
 * itself with auto margins, while the button centres independently inside the
 * panel's normal padding. Two centred boxes in one column share a centre line,
 * so they line up whatever either one measures - no shared magic number, and
 * nothing to keep in sync if the copy or the button width changes.
 *
 * fit-content, not max-content: it resolves to min(max-content, available), so
 * on a narrow phone the block simply fills the panel and the lines wrap as they
 * did before instead of overflowing.
 *
 * The upper bound MUST match the stacking width in the block below - this only
 * makes sense while the card is stacked. Side by side the panel is already a
 * 340-400 column and the block fills it.
 *
 * NOT ON PHONE, hence the 701 floor. Down there the button loses its 400 cap and
 * goes full width, so centring a 253px block inside a 293px button just indents
 * the copy ~20px from both edges - which reads as a mistake rather than as a
 * centred block, and full-bleed left is the conventional mobile treatment
 * anyway (Patrick, 2026-08-12). Below 701 the wrapper stretches as it did
 * before and the text sits flush with the button's left edge. */
@media (min-width: 701px) and (max-width: 1180px) {
  .site-plan-lead { width: fit-content; margin-inline: auto; }
}

/* ---- THE PRICE CARD STACKS AT 1180, not at the 1024 the rest of the iPad
 * pass uses. ------------------------------------------------------------
 *
 * Side by side, the fee panel takes a fixed 340-400 out of the row and the
 * calculator gets the rest, so the calculator column keeps narrowing as the
 * window does - 626px at 1200, 515px at 1025. Below ~1168 that is too narrow
 * for the sentence's first line and "in costs." orphans onto a second row.
 *
 * MEASURED across all 56 revenue x cost combinations (2026-08-12): 1167 is the
 * last width that wraps, 1168 the first that does not - but only by 1px, so the
 * boundary is set at 1180, which has 9px of slack at 1181 and leaves iPad Pro
 * 11" landscape (1194, 17px of slack) side by side, where it renders correctly.
 * Do not lower this without re-running that sweep; a single value combination
 * wrapping is the whole defect, and the DEFAULT $50k / 0% is one of the roomiest
 * pairs, so eyeballing the page as it first loads will not show it.
 *
 * BOTH flex bases have to be reset. Stacked, the container's main axis is
 * vertical, so the tiers above (`flex: 1 1 0` / `flex: 0 0 clamp(340..400)`)
 * stop meaning column WIDTH and start meaning HEIGHT - the fee panel would be
 * pinned to 340px tall with its content spilling out. */
@media (min-width: 701px) and (max-width: 1180px) {
  /* Side by side the two panels are separated by the gap BETWEEN columns, which
     the row's own layout provides; stacked they had nothing between them and the
     grey calculator's bottom edge sat directly against the fee panel above its
     icon (Patrick, 2026-08-12). 32px, matching the calculator panel's own inner
     padding at this tier so the seam reads on the same rhythm as everything
     else in the card. */
  .site-pricecard { flex-direction: column; gap: 32px; }
  .site-pricecalc { flex: 0 0 auto; }
  .site-priceplan { flex: 0 0 auto; }
  /* Nothing is beside the button now, so full width would stretch it to the
     whole card. 400 is the width it has on the desktop comp. */
  .site-priceplan-cta .site-btn { max-width: 400px; }

  /* 8px, down from the base 10. The sentence's first row is five items, so this
     buys 8px - which is what stops "in costs." orphaning onto its own line at
     768 once the labels are abbreviated and the verb is "makes" (see the
     _money_short comment in pricing/index.cgi). All three are needed: measured
     across all 56 revenue x cost combinations, 10px still wrapped in 20 of them
     at 768, 9px in 12, and 8px in none. Only 768 and below ever needed it; it
     costs nothing visible at the wider stacked widths. */
  .site-pricecalc-line { gap: 8px; }
}

/* ---- 701-767: the calculator takes the PHONE tier's type -----------------
 * A dead zone of the responsive pass, and the last non-phone width where the
 * sentence still broke. The phone tier ends at 700 and the narrowest real iPad
 * portrait is 768, so 701-767 was getting iPad-sized type (22px sentence, 44px
 * selects) at sub-iPad width - too big for the row no matter which values are
 * selected. The card already stacks here, so width is not the lever; type is.
 *
 * Borrowing the phone tier's sizes clears all 56 revenue x cost combinations
 * with 28px to spare at the worst point (701) - measured 2026-08-12. The calc
 * panel's PADDING is deliberately left at the 32px it has above: it is not
 * needed (the 28px of slack is with 32px padding in place) and dropping it too
 * would visibly shrink the panel a whole tier early.
 *
 * Real devices here are older phones in landscape (iPhone 8 Plus is 736). This
 * duplicates four declarations from the phone block rather than widening that
 * block to 767, because the phone block also carries the one-plan-at-a-time
 * selector, the stacked receipt rows and the small plan panel - none of which
 * should arrive 67px early. */
@media (min-width: 701px) and (max-width: 767px) {
  .site-pricecalc-sentence { font-size: 18px; line-height: 28px; gap: 10px; }
  .site-sel select, .site-sel-btn { font-size: 18px; height: 40px; padding: 0 34px 0 12px; border-radius: 10px; }
  .site-sel::after { right: 13px; }
}

/* ---- Contact page ------------------------------------------------------
 * Split hero: intro column left, a real form card right. The form's fields
 * are scoped to `.site-form` - the auth layer's input styles are behind
 * `.auth-page` and never reach here, so these are the site's own. */
/* Two-column grid rather than a flex row, because the left column holds TWO
   blocks (the intro and the Help Center) while the right holds one (the form)
   spanning both rows. Flex cannot express that from a flat DOM; grid can, and
   the same DOM then re-stacks on phone with `order`. */
.site-contact-card {
    display: grid;
    grid-template-columns: 1fr 720px;
    grid-template-areas: "top form" "help form";
    /* The form spans BOTH rows, so with two auto rows any height it has beyond
     * the left column gets split between them - which silently pushed the help
     * block down by half the overhang. That overhang is real: the comp has no
     * Turnstile and ours does, making the form ~112px taller than the design
     * allows for. Giving row 2 the flexible track sends the slack there
     * instead, so the help block stays put under the rule no matter how tall
     * the form grows, and lands on the comp's y=540. */
    grid-template-rows: auto 1fr;
    align-items: start;
    gap: 48px var(--lv-site-gap-xl);
    padding: 160px var(--lv-site-pad-xm) 120px;
}
.site-contact-top  { grid-area: top; }
.site-contact-help { grid-area: help; }
.site-contact-card .site-form { grid-area: form; }

/* The background is a CSS image, not an <img>: on desktop it sits behind the
   whole card (form included), on phone behind the hero portion only, and one
   element cannot be in two places. image-set gives the WebP/JPEG choice and the
   2x variant that a <picture> would. */
.site-contact-card {
    background-image: -webkit-image-set(var(--contact-bg-webp) type("image/webp") 1x, var(--contact-bg-webp2) type("image/webp") 2x, var(--contact-bg-jpg) 1x);
    background-image: image-set(var(--contact-bg-webp) type("image/webp") 1x, var(--contact-bg-webp2) type("image/webp") 2x, var(--contact-bg-jpg) 1x);
    background-size: cover;
    background-position: center;
}
.site-contact-intro { position: relative; display: flex; flex: 1 1 0; min-width: 0; flex-direction: column; gap: 48px; color: #fff; }
.site-contact-lead { display: flex; flex-direction: column; align-items: flex-start; gap: 24px; padding-right: 40px; }
.site-contact-lead .site-display { color: #fff; text-align: left; }
.site-contact-sub {
    margin: 0; max-width: 640px;
    font-size: 20px; line-height: 28px; font-weight: 500;
    letter-spacing: var(--lv-site-track); color: #fff;
}
.site-contact-sub a { color: #fff; text-decoration: underline; text-underline-position: from-font; }
.site-contact-rule { height: 1px; width: 100%; background: rgba(255,255,255,0.32); }
.site-contact-help { display: flex; flex-direction: column; align-items: flex-start; gap: 40px; padding-right: 40px; }
.site-contact-help .site-display-md { color: #fff; }
.site-contact-login { width: 160px; }

.site-form {
    position: relative;
    display: flex; flex: 0 0 auto; flex-direction: column; gap: 32px;
    /* Padding interpolates 48 -> 32 rather than stepping at 1439: nothing else
       about this card changes at that boundary, so a step there was a bare 32px
       jump in every field's width on a 1px narrower window. */
    width: 720px; padding: clamp(32px, calc(32px + (100vw - 1024px) * 16 / 416), 48px);
    background: #fff;
    border: 1px solid var(--lv-site-line);
    border-radius: var(--lv-site-radius-lg);
    box-shadow: var(--lv-site-shadow-nav);
}
.site-field { display: flex; flex-direction: column; gap: 8px; }
.site-field-grow { flex: 1 0 auto; }
.site-label {
    font-size: 16px; line-height: 24px; font-weight: 500;
    letter-spacing: -0.0125em; color: #18181b;
}
.site-input {
    width: 100%; padding: 16px;
    font-family: inherit; font-size: 16px; line-height: 24px; font-weight: 500;
    letter-spacing: -0.0125em; color: #18181b;
    background: #fff;
    border: 1px solid var(--lv-site-line);
    border-radius: 12px;
    box-shadow: var(--lv-site-shadow-btn);
    box-sizing: border-box;
}
.site-input::placeholder { color: rgba(0,0,0,0.4); opacity: 1; }
.site-input:focus-visible { outline: 0; border-color: #18181b; box-shadow: 0 0 0 3px rgba(0,0,0,.16); }
.site-textarea { min-height: 173px; resize: vertical; }
.site-form-submit { width: 100%; }
.site-form-flash { margin: 0; font-size: 15px; line-height: 22px; font-weight: 500; }
.site-form-flash.is-error { color: var(--lv-error); }
.site-form-flash.is-ok    { color: var(--lv-success-strong); }

@media (min-width: 701px) and (max-width: 1439px) {
  /* The track, not just the item. `.site-form { width: auto }` alone did
     nothing while the column stayed `720px` - grid sizes the item to the track,
     so the form kept its 720 and ran off the right edge below ~1180. */
  /* Row gap only. The COLUMN gap stays on the interpolating token - flattening
     it to 40 here gave the form 32px more width on a 1px narrower window. */
  .site-contact-card { grid-template-columns: 1fr minmax(0, 720px); gap: 40px var(--lv-site-gap-xl); padding: 140px var(--lv-site-pad-xm) 80px; }
  .site-form { flex: 1 1 0; width: auto; min-width: 0; }
}
@media (max-width: 700px) {
  /* Comp `73:7549`. Three things that are NOT just a stacked desktop: the hero
     overlaps the form card by 250px, the intro is centred rather than left
     aligned, and the "Looking for a Help Center?" block is dropped entirely -
     the nav already carries Log in, so on a phone it was redundant. */
  .site-contact-band { padding: 0; }
  /* Mobile: the wrapper becomes real, so the background covers the intro only
     and the form sits below it, pulled up 250px to overlap - the comp's shape
     (`73:7549` / `73:7574`), not a stacked desktop. */
  /* Phone: one column, three stacked blocks. The background moves off the card
     and onto the hero block, so it stops where the hero stops. */
  .site-contact-card {
      display: flex; flex-direction: column; gap: 0; padding: 0;
      border: 0; border-radius: 0; overflow: visible;
      background-image: none;
  }
  .site-contact-top {
      order: 1;
      background-image: -webkit-image-set(var(--contact-bg-webp) type("image/webp") 1x, var(--contact-bg-webp2) type("image/webp") 2x, var(--contact-bg-jpg) 1x);
      background-image: image-set(var(--contact-bg-webp) type("image/webp") 1x, var(--contact-bg-webp2) type("image/webp") 2x, var(--contact-bg-jpg) 1x);
      background-size: cover;
      background-position: center;
  }
  .site-contact-card .site-form { order: 2; }
  .site-contact-top {
      display: block; position: relative;
      padding: 120px 20px 280px;
      border-radius: 0 0 24px 24px;
      overflow: hidden;
  }
  .site-contact-card .site-hero-bg { border-radius: 0 0 24px 24px; }
  .site-contact-intro { gap: 24px; align-items: center; text-align: center; }
  .site-contact-lead { align-items: center; padding-right: 0; }
  .site-contact-lead .site-display { font-size: 64px; line-height: 64px; text-align: center; }
  .site-contact-sub { font-size: 18px; line-height: 24px; }
  /* CORRECTION: the comp does NOT drop the Help Center block on phone - it
     promotes it to its own section BELOW the form (`73:7585`, 375x316, with a
     120x48 button). An earlier read of this hid it, which lost content. The
     rule above it is dropped, since it was only separating two stacked blocks
     inside the intro column. */
  .site-contact-rule { display: none; }
  .site-contact-help {
      display: flex;
      order: 3;
      width: 100%;
      padding: 40px 20px;
      gap: 24px;
      background: #fff;
  }
  /* Off the sky at this width and rendered in ink, so the white-on-sky shadow
     has to come off with the white. */
  .site-contact-help .site-display-md { color: var(--lv-site-ink); font-size: 32px; line-height: 36px; text-shadow: none; }
  .site-contact-login { width: 120px; }

  .site-form { position: relative; z-index: 1; width: calc(100% - 16px); margin: -250px 8px 0; padding: 28px; gap: 32px; border-radius: 24px; }
}

/* ---- FAQs page ---------------------------------------------------------
 * A 416px sidebar beside an 800px column of category cards. The category
 * heading is the card's own first row, so it shares the row padding. */
.site-faqs-hero .site-hero-inner { gap: 0; }
.site-faqs-hero .site-display { font-size: 72px; line-height: 72px; }

.site-faqpage { display: flex; gap: var(--lv-site-gap-x); align-items: flex-start; padding: var(--lv-site-pad-y) var(--lv-site-pad-x); background: #fff; }
/* Interpolated, not stepped: this used to drop 416 -> 320 (and the block's own
   inset 120 -> 40) at 1439, which handed the accordion beside it 119px MORE
   width on a 1px narrower window. Same curve as the page gutters. */
.site-faqside {
    --lv-faqside-w: clamp(320px, calc(320px + (100vw - 1024px) * 96 / 416), 416px);
    display: flex; flex: 0 0 var(--lv-faqside-w); width: var(--lv-faqside-w);
    flex-direction: column; gap: 48px;
}
.site-faqside-block {
    display: flex; flex-direction: column; align-items: flex-start; gap: 24px;
    padding-right: clamp(40px, calc(40px + (100vw - 1024px) * 80 / 416), 120px);
}
.site-faqside-title {
    margin: 0;
    font-family: var(--lv-display);
    font-size: 48px; line-height: 52px; font-weight: 400;
    letter-spacing: var(--lv-site-track);
    color: var(--lv-site-ink);
}
.site-faqside-title em { font-style: italic; }
.site-faqside-rule { height: 1px; width: 100%; background: rgba(0, 0, 0, 0.12); }
.site-faqside-link {
    font-size: 20px; line-height: 28px; font-weight: 500;
    letter-spacing: var(--lv-site-track);
    color: var(--lv-site-ink);
    text-decoration: underline; text-underline-position: from-font;
}
.site-faqside-link:hover { color: var(--lv-brand); }
.site-faqside-btn { width: 120px; }

.site-faqcats { display: flex; flex: 1 1 0; min-width: 0; flex-direction: column; gap: 64px; }
.site-faqcat { display: block; }
.site-faqcat-head {
    margin: 0;
    padding: 24px 24px 20px;
    font-family: var(--lv-display);
    font-size: 28px; line-height: 36px; font-weight: 400;
    letter-spacing: var(--lv-site-track);
    color: var(--lv-site-ink);
    /* No rule underneath: the white block below has its own top border, which is
       the line the comp shows here. 24/24/20 keeps the header 80px tall and puts
       the title at x=32 once the card's own 8px padding is added, both comp. */
}
.site-faqcat-head em { font-style: italic; }
/* The heading sits inside the card, so the card wrapper is the section itself. */
/* The comp (`73:9059`) builds each category as a tinted card with the white
 * accordion block inset 8px inside it and the title sitting directly on the
 * tint - not a white card with a ruled-off heading.
 *
 * The tint is a vertical FADE, not a flat grey: rgba(0,0,0,0.03) at the top to
 * fully transparent at the bottom, which over the white page runs 247 -> 255.
 * Sampling it row by row looks like dithering noise; it is a gradient.
 *
 * `overflow: hidden` is LOAD-BEARING and must stay. The shadow belongs to the
 * inner white block, and this clip is the only thing keeping it small: just the
 * 8px of it that falls inside the padding band is ever visible. We previously
 * had that shadow on THIS box instead, where nothing clipped it, so its 52px
 * blur smeared ~60px of tint down the page. */
.site-faqcat {
    padding: 8px;
    background: linear-gradient(180deg, rgba(0, 0, 0, 0.03) 0%, rgba(0, 0, 0, 0) 100%);
    border-radius: var(--lv-site-radius-lg);   /* 32 here, not the 24 of the block inside */
    overflow: hidden;
}
/* Only the width is reset. This rule used to strip the block's border, radius
   AND shadow, because the outer card carried them under the old inverted
   structure - which is exactly why the shadow ended up unclipped on the wrong
   box. In the comp all three belong to THIS block. */
/* Nothing to override any more - .site-faq-card carries no sizing, and the
   two-column block's 720 cap is on `.site-faq > .site-faq-card`. Left as a
   marker that this card deliberately FILLS its category here. */
.site-faqcat .site-faq-card { width: auto; }
.site-faqcat-rows { border-color: rgba(0, 0, 0, 0.06); }   /* comp; the rows' own dividers stay 0.08 */

@media (min-width: 701px) and (max-width: 1439px) {
  .site-faqside-title { font-size: 36px; line-height: 42px; }
}
@media (max-width: 700px) {
  /* Comp `73:8180`. The sidebar does NOT stay on top on a phone - it moves
     BELOW the categories, and its two blocks swap so "Looking for a Help
     Center?" comes before "Still have some questions?". Done with flex order
     rather than duplicate markup, so there is still one DOM. */
  .site-faqs-hero .site-display { font-size: 44px; line-height: 48px; }
  .site-faqpage { flex-direction: column; gap: 40px; padding: 40px 20px 64px; }

  .site-faqcats { order: 1; width: 100%; gap: 24px; }
  .site-faqside  { order: 2; flex: 0 0 auto; width: 100%; gap: 24px; flex-direction: column-reverse; }

  .site-faqside-block { padding-right: 0; gap: 16px; }
  .site-faqside-title { font-size: 32px; line-height: 36px; }
  .site-faqside-btn { width: 120px; }
  .site-faqcat-head { padding: 20px 20px 16px; font-size: 24px; line-height: 32px; }
}

/* ---- Agencies page -----------------------------------------------------
 * Hero display is 72 here (not the home page's 104) and the CTA is wider to
 * fit "Add your first client free". */
.site-agencies-hero .site-display,
.site-faqs-hero .site-display { font-size: 72px; line-height: 72px; }
/* The agencies CTA ("Add your first client free") is 320 in the comp, against
 * the 240 default - measured off `73:8574` and `73:8879`, where a fully rounded
 * pill's flat top run is width minus height, giving 268 + 56.
 *
 * This MUST out-specify `.site-hero-cta .site-btn-lg` and `.site-cta-actions
 * .site-btn-lg`. Those are two-class selectors and a bare `.site-btn-cta-wide`
 * is one, so it silently lost to both: the class was on the markup and looked
 * applied, while the button rendered at 240 with its own label overflowing it
 * (scrollWidth 241 against clientWidth 236). Do not demote this back to a
 * single class, and do not rely on source order to break the tie. */
.site-hero-cta .site-btn-lg.site-btn-cta-wide,
.site-cta-actions .site-btn-lg.site-btn-cta-wide { width: 320px; }


@media (max-width: 700px) {
  .site-agencies-hero .site-display { font-size: 48px; line-height: 1; }
  .site-faqs-hero .site-display { font-size: 44px; line-height: 48px; }
  /* No phone override. There WAS a `width: 100%` here, but it never applied
     (same specificity problem as the desktop rule), so it has never been seen.
     Making it live now would stretch the hero button to the full 794 at 834,
     while the one in the CTA card sits in a shrink-to-fit parent and would stay
     ~320 - the two would disagree. 320 fits inside the 335 available at 375, so
     it just holds at every width, like every other CTA on the site. Revisit
     against the phone comp when that pass happens. */
  /* The comp (`73:7726`) drops the ClickMagick credibility strip entirely on
     phone - a row of nine logos does not survive 375px. Reproduced as the comp
     has it; the same claim still appears on the home page's bento at every
     width, so the proof is not lost from the site. */
}
@media (min-width: 701px) and (max-width: 1439px) {
  /* The pricing hero shares this rung too - one ladder for all three, so it
     cannot drift again. Safe here even though pricing's own phone size sits
     EARLIER in the file: that block is max-width 700, so the two ranges never
     overlap and source order never comes into it. */
  .site-agencies-hero .site-display,
  .site-faqs-hero .site-display,
  .site-pricing-hero .site-display { font-size: 56px; line-height: 60px; }
}

/* ---- Agencies: 4-step row, white-label, statement, page CTA ------------- */
.site-steps { position: relative; display: flex; gap: 4px; align-items: stretch; width: 100%; padding: clamp(24px, calc(24px + (100vw - 1024px) * 16 / 416), 40px); background: #56a0e4; overflow: hidden; }
.site-steps-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
.site-step { position: relative; display: flex; flex: 1 1 0; min-width: 0; flex-direction: column; gap: 48px; padding: 32px; background: #fff; overflow: hidden; }
.site-step-n { margin: 0; font-size: 28px; line-height: 36px; font-weight: 500; letter-spacing: var(--lv-site-track); color: rgba(0,0,0,0.4); }
.site-step-body { display: flex; flex-direction: column; gap: 8px; }
.site-step-t, .site-step-c { margin: 0; font-size: 20px; line-height: 28px; font-weight: 500; letter-spacing: var(--lv-site-track); }
.site-step-t { color: var(--lv-site-ink); }
.site-step-c { color: var(--lv-site-ink-soft); }

/* The comp (`73:8738`) makes this whole section ONE inset grey card: same 8px
 * gutter as the hero and CTA cards, so it is 1424 wide and lines up exactly
 * with the blue statement card below it, 24px radius, and the heading and lede
 * sit INSIDE it. The grey used to come only from the exported screenshot's own
 * background, so it read as a band behind the image with the heading stranded
 * on white above it. Card padding is the comp's 120/72; the 1424 minus 2x72
 * leaves the 1280 the screenshot is exported at. */
/* Bottom is the GUTTER, not the half-gap: the comp stacks this card and the blue
 * statement card 8px apart (`73:8738` ends at 3398, `73:8875` starts at 3406),
 * so the two read as a stack rather than two separate sections. Only the top
 * pays the section rhythm. */
.site-whitelabel { padding: var(--lv-site-pad-y) var(--lv-site-gutter) var(--lv-site-gutter); background: #fff; }
.site-whitelabel-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--lv-site-gap);
    padding: 120px var(--lv-site-pad-xm);
    background: #f7f7f7;                 /* the screenshot's own background, so the two blend */
    border-radius: var(--lv-site-radius);
}
.site-whitelabel-head { display: flex; flex-direction: column; align-items: center; gap: 24px; width: 100%; text-align: center; }
/* 600, not 480. At 480 this lede broke to three lines with two words stranded
   on the last one; two lines need 520, so 600 leaves room for the copy to change
   without falling back to three. `balance` evens the two lines out. */
.site-whitelabel-head .site-lede { width: 600px; text-wrap: balance; }
/* The report is an IMAGE; the three notes around it are TEXT.
   They used to be baked into whitelabel.* along with the card, which meant a
   wording change needed a re-export. wl-report.* is just the card - now its own
   680x910 Figma node (x 300-980 of the 1280 row) - and the notes are real
   elements either side of it. */
.site-whitelabel-shot {
    display: flex; align-items: stretch; justify-content: center;
    gap: 32px; width: 100%;
}
.site-wl-shot { display: block; flex: 0 1 680px; min-width: 0; }
.site-whitelabel-shot picture, .site-whitelabel-shot img { display: block; width: 100%; height: auto; }

.site-wl-col { display: flex; flex-direction: column; flex: 1 1 0; min-width: 0; }
/* Left notes hug the card's inner edge and are right-aligned into it; the rule
   is the note's own border, so it spans exactly the text it belongs to. */
/* Bottom padding is what positions the SECOND note: the column is
   space-between, so the lower note's baseline is set from the column's bottom.
   256, not the comp's 56 - Patrick wanted that note ~200px higher up the
   report image rather than sitting in its bottom corner (2026-08-12). */
.site-wl-col-l { align-items: flex-end; text-align: right; justify-content: space-between; padding: 40px 0 256px; }
/* Balance ONLY the two short left-hand notes. It stops "on it." being stranded
   alone on a third line, which is what balancing is for - short blocks.
   Deliberately NOT on the long right-hand note: there it evens out all six
   lines and leaves ~78px unused on line 2, pushing "lift" down a line for no
   reason. Paragraphs want ordinary greedy wrapping. */
.site-wl-col-l .site-wl-note { text-wrap: balance; border-right: 1px solid rgba(0, 0, 0, 0.16); padding-right: 16px; }
/* The single right-hand note sits about a quarter of the way down, as the comp
   has it, rather than pinned to the top. */
.site-wl-col-r { align-items: flex-start; text-align: left; padding: 200px 0 0; }
.site-wl-col-r .site-wl-note { border-left: 1px solid rgba(0, 0, 0, 0.16); padding-left: 16px; }
.site-wl-note {
    margin: 0; max-width: 260px;
    font-size: 18px; line-height: 28px; font-weight: 500;
    letter-spacing: var(--lv-site-track-1);
    color: var(--lv-site-ink);
}

/* Flush top and bottom: the statement card is square-bottomed and the agencies
 * CTA card sits directly beneath it, so the two read as one composed object. */
.site-statement { padding: 0 var(--lv-site-gutter); background: #fff; }
.site-statement-card { position: relative; display: flex; justify-content: center; padding: 120px 0; border-radius: 24px 24px 0 0; overflow: hidden; }
/* NO opacity. This is the comp's diagonal light-streak texture (`73:8876`), not
 * a photo to be faded back - the wash comes entirely from the white gradient in
 * ::after below. Checked against the comp: the texture's (174,204,237) under a
 * 0.4 white overlay gives (206,225,244), which is the comp's own top pixel. The
 * build previously used the sky photo here (a byte-for-byte copy of ag-cta) at
 * 40%, which washed the streaks away completely. */
.site-statement-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
.site-statement-card::after {
    content: ""; position: absolute; inset: 0;
    background: linear-gradient(180deg, rgba(255,255,255,0.4) 0%, #fff 100%);
    pointer-events: none;
}
/* 600px, not the comp's 560. At 560 the closing sentence wraps to a 7th line
 * with a one-to-two word widow ("numbers.") stranded under it. Measured in
 * Chromium with Geist loaded: 576-672 all hold 6 lines, and 600 sits well
 * inside that band - 24px above the flip back to 7, and 64px below the point
 * where the last line collapses to a widow again. */
.site-statement-text {
    position: relative; z-index: 1; margin: 0; width: 600px;
    font-size: 32px; line-height: 42px; font-weight: 500;
    letter-spacing: var(--lv-site-track); color: var(--lv-site-ink-soft);
}

.site-agcta { padding: 0 var(--lv-site-gutter) var(--lv-site-gutter); background: linear-gradient(180deg, #fff 0%, #f9f9f9 100%); }
.site-agcta-card {
    position: relative; display: flex; flex-direction: column; align-items: center; gap: 70px;
    padding: 120px var(--lv-site-pad-xl);
    border: 1px solid var(--lv-site-line);
    border-radius: var(--lv-site-radius-lg);
    overflow: hidden;
}
img.site-agcta-bg {
    position: absolute;
    top: 0;
    bottom: 0;
    left: -65px;
    width: calc(100% + 70px);
    max-width: none;
    height: 100%;
    object-fit: cover;
    border-radius: var(--lv-site-radius-lg);
    pointer-events: none;
}
.site-agcta-inner { position: relative; display: flex; flex-direction: column; align-items: center; gap: 48px; width: 100%; }
/* 660px, not the comp's 560. At 560 this ran to 4 lines with "invoice across
 * your whole book." alone on the last one. Measured in Chromium with Geist
 * loaded: 3 lines from 632px up, so 660 clears the flip by 28px rather than
 * hugging it, and rags evenly at 13/15/12 words. */
.site-agcta-sub { margin: 0; max-width: 660px; font-size: 20px; line-height: 28px; font-weight: 500; letter-spacing: var(--lv-site-track); color: #fff; }

@media (min-width: 701px) and (max-width: 1439px) {
  .site-step { padding: 24px; gap: 32px; }
  .site-whitelabel-card { padding: 80px var(--lv-site-pad-xm); }
  .site-whitelabel-shot { gap: 20px; }
  .site-wl-note { max-width: 220px; }
  .site-wl-col-r { padding-top: 140px; }
  .site-statement-text { width: auto; max-width: 560px; padding: 0 40px; font-size: 26px; line-height: 36px; }
  .site-agcta-card { padding: 80px var(--lv-site-pad-xl); gap: 48px; }
}
/* White-label: the three-column arrangement needs ~1200px, NOT the ~1000 this
   rule used to claim. The notes are `flex: 1 1 0` either side of a fixed 680px
   report, so every pixel the viewport loses comes off the notes at twice the
   rate - and the report is an exported screenshot, so it can't give ground.
   Measured line counts per note (the comp's desktop is 3/4/6): 3/5/8 at 1300,
   4/6/9 at 1220, 5/7/13 at 1140, 8/13/16 at 1025, where the boxes also push
   past the card. So the row layout stops at 1200 and the report goes full
   width with the notes stacked beneath it - the arrangement the phone tier
   already uses. Must sit AFTER the 1439 tier above, which sets the padding
   this resets. */
@media (min-width: 701px) and (max-width: 1200px) {
  .site-whitelabel-shot { flex-direction: column; gap: 24px; }
  .site-wl-shot { flex: 0 0 auto; order: -1; }
  .site-wl-col { flex: 0 0 auto; }
  .site-wl-col-l, .site-wl-col-r { align-items: flex-start; text-align: left; padding: 0; gap: 24px; }
  .site-wl-col-l .site-wl-note, .site-wl-col-r .site-wl-note { border: 0; padding: 0; max-width: none; }
}
@media (max-width: 700px) {
  /* Comp `73:7861`: the four steps stack, each 303 wide inside the 335 column,
     i.e. 16px of panel padding either side of the page's 20px gutter. */
  .site-steps { flex-direction: column; gap: 8px; padding: 16px; }
  .site-step { padding: 24px; gap: 24px; }
  .site-step-t, .site-step-c { font-size: 18px; line-height: 26px; }
  /* This section carried .site-section until the grey card was built, so it
     inherited the phone section padding from there; it needs its own now. */
  .site-whitelabel { padding: 80px var(--lv-site-gutter) var(--lv-site-gutter); }
  .site-whitelabel-card { padding: 64px 20px; gap: 40px; }
  .site-whitelabel-head .site-lede { width: auto; }
  /* No room for side columns at this width: the notes stack under the report,
     left-aligned, and the connecting rules come off. */
  .site-whitelabel-shot { flex-direction: column; gap: 24px; }
  .site-wl-shot { flex: 0 0 auto; order: -1; }
  .site-wl-col { flex: 0 0 auto; }
  .site-wl-col-l, .site-wl-col-r { align-items: flex-start; text-align: left; padding: 0; gap: 24px; }
  .site-wl-col-l .site-wl-note, .site-wl-col-r .site-wl-note { border: 0; padding: 0; max-width: none; }
  .site-statement-card { padding: 64px 20px; }
  .site-statement-text { width: auto; font-size: 22px; line-height: 32px; }
  .site-agcta-card { padding: 64px 20px; gap: 40px; }
  .site-agcta-inner { gap: 32px; }
  .site-agcta-card .site-display { font-size: 44px; line-height: 48px; }
  .site-agcta-sub { font-size: 18px; line-height: 24px; }
}

/* ---- Pricing: "How the billing works" (`73:9651`) ----------------------- */
.site-billing { display: flex; justify-content: center; padding: var(--lv-site-pad-y) var(--lv-site-pad-x); background: #fff; }
.site-billing-card {
    display: flex; flex: 1 1 0; min-width: 0; gap: 120px; align-items: flex-start;
    padding: 8px;
    background: #fff;
    border: 1px solid var(--lv-site-line);
    border-radius: 40px;
    box-shadow: var(--lv-site-shadow-nav);
}
.site-billing-intro {
    display: flex; flex: 0 0 520px; width: 520px; flex-direction: column;
    justify-content: flex-end; gap: 24px; padding: 56px;
}
/* NO gap between the steps: the rail's hairline has to run from one number
 * down to the next, so the space between them belongs to the step ABOVE (its
 * body's padding-bottom) where the rail can stretch through it. A gap here
 * would leave the line ending 44px short of the number below it. */
.site-billing-steps {
    display: flex; flex: 1 1 0; min-width: 0; flex-direction: column;
    padding: 56px 56px 56px 8px;
}
.site-billstep { display: flex; align-items: stretch; gap: 24px; padding-right: 24px; }
.site-billstep-body { display: flex; flex-direction: column; gap: 8px; padding-bottom: 44px; }
.site-billstep:last-child .site-billstep-body { padding-bottom: 0; }
/* The rail: the number, then a hairline taking whatever height is left. The
 * last step has nothing to point at, so it gets the number alone. */
.site-billstep-rail { display: flex; flex-direction: column; align-items: center; flex: 0 0 auto; }
.site-billstep-rail::after {
    content: ""; flex: 1 1 auto; width: 1px; background: var(--lv-site-line-soft);
}
.site-billstep:last-child .site-billstep-rail::after { display: none; }
.site-billstep-num {
    display: flex; align-items: center; justify-content: center;
    flex: 0 0 auto; width: 28px; height: 28px;
    background: var(--lv-site-sheen), var(--lv-site-ink);
    border: 1px solid rgba(255, 255, 255, 0.32);
    border-radius: 999px;
    box-shadow: var(--lv-site-shadow-btn);
    font-size: 16px; line-height: 24px; font-weight: 600;
    letter-spacing: var(--lv-site-track);
    color: #fff;
}
.site-billstep-t, .site-billstep-c {
    margin: 0; font-size: 20px; line-height: 28px; font-weight: 500;
    letter-spacing: var(--lv-site-track);
}
.site-billstep-t { color: var(--lv-site-ink); }
.site-billstep-c { color: var(--lv-site-ink-soft); }
.site-billing-rule { height: 1px; width: 100%; background: var(--lv-site-line); }
.site-billing-note {
    margin: 0; font-size: 14px; line-height: 20px; font-weight: 500;
    letter-spacing: var(--lv-site-track-1); color: var(--lv-site-ink-soft);
}

/* ---- Pricing: agencies cross-sell (`73:9668`) --------------------------- */
.site-xsell { display: flex; justify-content: center; padding: var(--lv-site-pad-y) var(--lv-site-pad-x); background: #fff; }
.site-xsell-card {
    display: flex; flex: 1 1 0; min-width: 0; gap: 120px; align-items: center;
    padding: 8px;
    background: #fff;
    border: 1px solid var(--lv-site-line);
    border-radius: 40px;
    box-shadow: var(--lv-site-shadow-nav);
}
.site-xsell-intro {
    display: flex; flex: 0 0 520px; width: 520px; flex-direction: column;
    justify-content: space-between; align-self: stretch; gap: 40px; padding: 56px;
}
.site-xsell-top { display: flex; flex-direction: column; align-items: flex-start; gap: 24px; }
.site-xsell-badge {
    display: inline-flex; align-items: center; height: 32px; padding: 0 16px;
    background: #3884d5;
    border: 1px solid var(--lv-site-line);
    border-radius: 999px;
    font-size: 16px; line-height: 28px; font-weight: 500;
    letter-spacing: var(--lv-site-track); color: #fff; white-space: nowrap;
}
.site-xsell-btn { align-self: flex-start; padding: 0 24px; }
.site-xsell-panel { flex: 1 1 0; min-width: 0; height: 720px; border-radius: 32px; overflow: hidden; }
.site-xsell-panel picture, .site-xsell-panel img { display: block; width: 100%; height: 100%; object-fit: cover; }

@media (min-width: 701px) and (max-width: 1439px) {
  .site-billing-card, .site-xsell-card { gap: 40px; }
  /* flex-GROW 0. With `1 1 45%` these took a 45% basis AND half the free space,
     while their siblings (.site-billing-steps / .site-xsell-panel, both
     `flex: 1 1 0`) started from zero and got the other half - so the intro
     ended up at 755-775px and the content beside it was squeezed to ~300.
     `0 1 45%` lets them shrink but never grab the slack. */
  .site-billing-intro, .site-xsell-intro { flex: 0 1 45%; width: auto; padding: 32px; }
  .site-billing-steps { padding: 32px 32px 32px 8px; }
  .site-xsell-panel { height: auto; aspect-ratio: 624 / 720; }
}
@media (max-width: 700px) {
  .site-billing { padding: 40px 20px 16px; }
  .site-billing-card, .site-xsell-card { flex-direction: column; gap: 0; border-radius: 24px; }
  .site-billing-intro, .site-xsell-intro { flex: 0 0 auto; width: auto; padding: 28px 24px 0; gap: 16px; }
  .site-billing-steps { padding: 24px; }
  .site-billstep { padding-right: 0; gap: 16px; }
  .site-billstep-body { padding-bottom: 28px; }
  .site-billstep-t, .site-billstep-c { font-size: 18px; line-height: 26px; }
  .site-xsell { padding: 20px; }
  .site-xsell-intro { padding: 28px 24px; gap: 24px; }
  .site-xsell-btn { width: 100%; }
  .site-xsell-panel { width: 100%; height: auto; aspect-ratio: 624 / 720; border-radius: 0 0 24px 24px; }
}

/* ---- Legal template (`73:9308`) ----------------------------------------
 * One template, three pages. A single 720px prose column; the hero is a short
 * band with the page title only. Body text is Geist REGULAR at 18/24 and
 * rgba(0,0,0,.64) - darker than the .48 used elsewhere on the site, because
 * this is long-form reading rather than supporting copy. */
.site-legal-hero .site-hero-inner { gap: 0; }
.site-legal-title { font-size: 72px; line-height: 72px; }

.site-legal { display: flex; align-items: flex-start; justify-content: center; padding: var(--lv-site-pad-y) var(--lv-site-pad-x); background: #fff; }
/* 12px is the gap BETWEEN PARAGRAPHS; headings add 12 of their own on each side
 * so they sit in 24, which is what the comp does (`73:9334`: 24 above and below
 * every heading, 12 between the paragraphs of one section). A flat 24 gap on
 * everything, which is what this had, spaces a section's own paragraphs as far
 * apart as the sections themselves and loses the grouping. */
.site-legal-body { display: flex; flex-direction: column; gap: 12px; width: 720px; }

.site-legal-updated {
    margin: 0;
    font-size: 16px; line-height: 24px; font-weight: 500;
    letter-spacing: var(--lv-site-track-1); color: var(--lv-site-ink-soft);
}
/* Asymmetric on purpose, and a deliberate departure from the comp (Patrick,
 * 2026-08-08). The comp puts an equal 24 above and below every heading, which
 * makes a long policy read as one undifferentiated stack - nothing tells you
 * where one section ends and the next begins. The extra space goes ABOVE only,
 * so a heading stays visually attached to the copy it introduces:
 *
 *     12  between paragraphs of one section   (gap alone)
 *     24  heading down to its own body        (gap + 12)
 *     40  up to the previous section          (gap + 28)   <- the separation */
.site-legal-h1, .site-legal-h2, .site-legal-h3 {
    margin: 28px 0 12px;
    font-weight: 500; letter-spacing: var(--lv-site-track); color: var(--lv-site-ink);
}
.site-legal-h1 { font-size: 32px; line-height: 44px; }
.site-legal-h2 { font-size: 24px; line-height: 32px; }
.site-legal-h3 { font-size: 20px; line-height: 32px; }

.site-legal-body p:not(.site-legal-updated),
.site-legal-body ul {
    margin: 0;
    font-size: 18px; line-height: 24px; font-weight: 400;
    letter-spacing: var(--lv-site-track-1);
    color: rgba(0, 0, 0, 0.64);
}
.site-legal-body ul { padding-left: 27px; list-style: disc; }
.site-legal-body li { margin-bottom: 8px; }
.site-legal-body li:last-child { margin-bottom: 0; }
.site-legal-body a { color: var(--lv-brand); text-decoration: underline; text-underline-position: from-font; }

@media (min-width: 701px) and (max-width: 1439px) {
  .site-legal-title { font-size: 56px; line-height: 60px; }
  .site-legal-body { width: auto; max-width: 720px; }
}
@media (max-width: 700px) {
  .site-legal-title { font-size: 40px; line-height: 44px; }
  .site-legal { padding: 48px 20px; }
  .site-legal-body { width: 100%; gap: 10px; }
  .site-legal-h1, .site-legal-h2, .site-legal-h3 { margin: 20px 0 10px; }  /* 30 above / 20 below */
  .site-legal-h1 { font-size: 26px; line-height: 34px; }
  .site-legal-h2 { font-size: 22px; line-height: 30px; }
  .site-legal-h3 { font-size: 19px; line-height: 28px; }
  .site-legal-body p:not(.site-legal-updated), .site-legal-body ul { font-size: 17px; line-height: 26px; }
}

/* ============================================================
 * SITE - TABLET REFLOW (701px - 1024px)          added 2026-08-09
 *
 * The iPad pass. Everything above was drawn at 1440 and, until the phone tier
 * moved down off 834, a tablet never saw it - it got the phone layout instead.
 * Now that it does, the desktop two-column sections need somewhere to go: at
 * 834 they were squeezing a display headline into 234px beside a fixed-480px
 * lede ("Set it up once. It never stops working." wrapped to FIVE lines) and
 * the billing steps into 202px (three words a line).
 *
 * The move is the same one the app uses at this width (CLAUDE.md §Responsive):
 * two columns become one, and horizontal splits reflow rather than compress.
 * Vertical space is cheap on a tablet; line length is not.
 *
 * 1024 INCLUSIVE because landscape iPad is 1024 and it is already cramped
 * there - .site-billing-card is 255px and .site-stage-row 293px at that width.
 * Desktop (>= 1025) is untouched, as is the phone tier (<= 700).
 * ============================================================ */
@media (min-width: 701px) and (max-width: 1024px) {

  /* Section heads: headline ABOVE the lede. The lede's fixed 480px was eating
     the row, so the headline got whatever was left. */
  .site-section-head { flex-direction: column; align-items: flex-start; gap: 24px; }
  .site-section-head .site-lede { width: auto; max-width: 640px; }

  /* Home: the proposal shot and its two points stop competing for one row. */
  .site-split-row { flex-direction: column; align-items: flex-start; gap: 40px; }
  /* Stacked, so the 60/40 basis would be measured against HEIGHT - reset both. */
  .site-split-card { flex: 0 0 auto; width: 100%; max-width: none; height: auto; aspect-ratio: 3 / 2; }
  .site-split-points { flex: 0 0 auto; width: 100%; max-width: none; }

  /* The PRICE CARD's own stacking rules are NOT here - they stack at 1180, not
     1024, so they live in their own block up in the pricing section. */

  /* Pricing: "How the billing works" - intro above the steps, and the steps
     themselves go side by side rather than in one narrow stack. */
  .site-billing-card { flex-direction: column; gap: 40px; }
  .site-billing-steps { width: 100%; }

  /* Agencies cross-sell: copy above the app panel. */
  .site-xsell-card { flex-direction: column; gap: 40px; }
  .site-xsell-panel { width: 100%; }
  .site-xsell-panel img { width: 100%; height: auto; }

  /* Agencies: the 4-step row becomes 2x2 rather than four 174px slivers. */
  .site-steps { flex-wrap: wrap; gap: 4px; }
  .site-step { flex: 1 1 calc(50% - 4px); min-width: calc(50% - 4px); }

  /* The FAQ TEASER (home/pricing/agencies/contact) - a different component from
     the FAQs page below. Its intro column held a 480px lede inside a 286px box. */
  .site-faq { flex-direction: column; gap: 40px; }
  .site-faq-intro { flex: 0 0 auto; }
  /* Stacked, so the 40/60 basis would be measured against HEIGHT. Reset it for
     the same reason .site-faq-intro above is reset. */
  .site-faq > .site-faq-card { flex: 0 0 auto; max-width: none; }
  .site-faq-intro .site-lede,
  .site-faq-link { width: auto; max-width: 640px; }
  .site-faq-teaser-card { width: 100%; }

  /* FAQs: the 416px aside sits above the categories. */
  .site-faqpage { flex-direction: column; gap: 48px; }
  .site-faqside { flex: 0 0 auto; width: 100%; }
  .site-faqside-block { padding-right: 0; }

  /* Contact: the form drops below the intro instead of sharing the row. */
  .site-contact-card {
      grid-template-columns: 1fr;
      grid-template-areas: "top" "help" "form";
      grid-template-rows: auto auto auto;
      gap: 40px;
      padding: 140px 40px 80px;
  }
  .site-contact-lead, .site-contact-help { padding-right: 0; }
}
