/* ============================================================================
   convert.css - the CONVERT family: Pricing, Book a Demo, Contact.
   Loaded only by page-convert.html, the one HubSpot template these three
   pages share. Scoped to body.fam-convert so no other template is touched.

   The original design has two layouts, switched by data-cv-layout
   ("destination" vs "spine"). Only "destination" ships here: the spine
   mechanic depends on display:contents hoisting a section's children into
   one grid, and HubSpot wraps every module in its own div, which blocks that
   hoist. The spine rules below are kept in place, inert, so the mechanic can
   be revisited against HubSpot's module wrappers later instead of
   re-deriving it from scratch.
   ========================================================================= */

/* ---- family tokens -------------------------------------------------------
   Sizes live here, not inline, so the two layouts and all three pages stay
   in step when one of them moves. */
body.fam-convert {
  --cv-rail-w: 40%;              /* layout A: the form's share of the hero */
  --cv-gap: clamp(32px, 4vw, 72px);
  --cv-stick-top: calc(var(--headH) + 28px);
  --cv-block-gap: clamp(56px, 7vh + 1.5vw, 104px);  /* between argument blocks */
  --cv-logo-lead: 160px;         /* air above the logo marquee */
}

/* ---- no eyebrows ---------------------------------------------------------
   Pricing carried TWO overlines above its H1: one in the markup and one that
   site.js injects on every hero. Stacked, they read as a stutter before the
   headline has said anything. Suppressed for this family only - the other
   pages keep the injected eyebrow by design. */
body.fam-convert .wf-heroeyebrow { display: none; }
body.fam-convert .wf-hero .t-overline,
body.fam-convert .cv-sec .t-overline { display: none; }

/* ---- shared heading treatment -------------------------------------------
   The next block is a stray comment fragment from the original stylesheet,
   preserved deliberately so this file stays diffable against its source.
   Section headers are uniform so "Why DefectDojo Pro" and "DefectDojo Pro
   Features" read with continuity.
   .t-h2 defaults to the full display scale (up to 96px) while its sibling
   carries --quiet; side by side down one page they read as two different
   levels of the hierarchy when they are in fact peers. Both go quiet here. */
   A section head must also sit BELOW the page headline, not above it: at the
   old 52px cap "Why DefectDojo Pro?" outran the 46px hero and the page read
   as though it started twice. Capped under the hero in both layouts. */
body.fam-convert .cv-sec .t-h2 {
  font-size: clamp(24px, 2.9vw, 40px);
  letter-spacing: -0.01em;
  max-width: 22ch;
}
/* and the heading gets real separation from what it introduces - a table
   butted up under a 52px headline reads as part of the headline block */
body.fam-convert .cv-sec .t-h2 { margin-bottom: clamp(28px, 3.5vh + 0.5vw, 56px); }

/* ---- the split ------------------------------------------------------------
   Layout A. 60/40 rather than 1fr 1fr: the argument is the
   thing being read, the form is the thing being filled in, and they do not
   deserve equal width. The extra room is what lets the headline breathe. */
body.fam-convert .cv-split {
  display: grid;
  /* minmax(0,1fr), NOT 1fr: bare 1fr means minmax(auto,1fr), so the column's
     min-content (the comparison table) sets a floor it refuses to shrink
     below - the whole grid blows out sideways and the form leaves the
     viewport (seen at 722px and 1031px widths). min-width:0 on the items
     below is the same fix one level down. */
  grid-template-columns: minmax(0, 1fr) var(--cv-rail-w);
  gap: var(--cv-gap);
  align-items: start;
}
body.fam-convert .cv-col > * + * { margin-top: 20px; }
/* grid items must be allowed to shrink below their content's min width, or
   the table inside re-creates the blowout the minmax(0,1fr) tracks prevent */
body.fam-convert .cv-col,
body.fam-convert .cv-rail { min-width: 0; }

body.fam-convert .cv-rail {
  position: sticky;
  top: var(--cv-stick-top);
}

/* Hero headline: the display scale exists to shout across a full-width hero.
   In a 60% column it only wrapped badly - "Book a Demo of DefectDojo Pro"
   broke with "Pro" orphaned on line two.
   Measured: that string in Inter 900 at the display tracking needs ~651px to
   set on one line at 38px, and the column is 686px at the 1360 wrap. So 38px
   is the ceiling, and one line is what it costs. Below the wrap max the
   column scales with the viewport, so the vw term tracks it down.
   Verify with the 29-char string before raising this - it is the longest
   headline in the family and the constraint. */
body.fam-convert .wf-hero .t-display {
  font-size: clamp(26px, 2.72vw, 38px);
  line-height: 1.05;
  max-width: none;
  text-wrap: balance;
}
/* the hero's sub-copy follows it down rather than staying at the old
   30ch/26px, which fought the smaller headline for emphasis */
body.fam-convert .wf-hero .t-lead {
  font-size: clamp(17px, 1.25vw, 20px);
  max-width: 46ch;
}

/* ---- layout B: the spine --------------------------------------------------
   The form escapes its section and the whole argument scrolls past it.

   Mechanic: .cv-stack becomes ONE grid; the sections, their .wrap and the
   hero's .cv-split all go display:contents so their children are hoisted
   into it as direct grid items. The rail then spans every row of column 2
   and sticks. No JS, no fixed positioning, no scroll listener - which also
   means it survives the HubSpot port, where a scroll observer would not.

   Consequence, taken deliberately: display:contents removes the sections'
   boxes, so the band alternation across the spine goes away and the whole
   argument sits on one ground. That is the better reading anyway - a spine
   only registers as a spine if the thing beside it moves continuously. */
body.fam-convert[data-cv-layout="spine"] .cv-stack {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(280px, 30%);
  gap: 0 var(--cv-gap);
  align-items: start;
  background: var(--bg-dark);
  --bandbg: var(--bg-dark);
  --ink: var(--offwhite);
  --dim: var(--dim-on-dark);
  --line: var(--line-on-dark);
  --hot: var(--hot-on-dark);
  color: var(--ink);
  max-width: var(--wrap-max, 1360px);
  margin-inline: auto;
  padding: var(--band-pad) var(--wrap-pad, 24px);
}
body.fam-convert[data-cv-layout="spine"] .cv-sec,
body.fam-convert[data-cv-layout="spine"] .cv-sec > .wrap,
body.fam-convert[data-cv-layout="spine"] .cv-split {
  display: contents;
}
body.fam-convert[data-cv-layout="spine"] .cv-col { grid-column: 1; }
/* Spacing between the argument blocks. Selected through the SECTION, not as
   .cv-col + .cv-col: display:contents changes how the boxes are laid out but
   not the DOM tree, so the columns are grid siblings without ever being DOM
   siblings and the adjacent-sibling combinator never matches. */
body.fam-convert[data-cv-layout="spine"] .cv-sec:not(:first-child) .cv-col {
  margin-top: var(--cv-block-gap);
}
body.fam-convert[data-cv-layout="spine"] .cv-rail {
  grid-column: 2;
  grid-row: 1 / -1;
  align-self: start;
  position: sticky;
  top: var(--cv-stick-top);
}
/* the headline comes back up a step here: column 1 is ~850px at the wrap max
   rather than 686px, which buys ~46px while still holding one line */
body.fam-convert[data-cv-layout="spine"] .wf-hero .t-display {
  font-size: clamp(28px, 3.3vw, 46px);
}
/* the hero's dot-grid pattern layer is painted by the (now boxless) section,
   so it has nothing to sit in - drop it rather than leave it half-clipped */
body.fam-convert[data-cv-layout="spine"] .wf-heropat { display: none; }

/* ---- Why DefectDojo Pro ---------------------------------------------------
   components.css centres this block at max-width:80% - a sensible default when
   it owns a full-width band, but here it is already inside a 60% or 2/3
   column, so the 80% was a second inset on top of the first: the list ended
   up narrower than the paragraph above it and visibly off-axis from every
   other block on the page. Released; the column is the measure now. */
body.fam-convert .wf-whycol { max-width: none; margin-inline: 0; }

/* The two-up list needs ~340px a cell before the label stops stacking one
   word per line ("EXECUTIVE & HOLISTIC SECURITY PROGRAM REPORTING:" was
   setting as five lines). Below that it goes single-column rather than
   getting narrower. */
body.fam-convert .wf-whylist {
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
}

/* Each item became display:flex once site.js injected the FA icon, which laid
   the icon, the label and the description out in a single ROW - so the label
   got a ~144px column of its own and "Executive & Holistic Security Program
   Reporting:" set one word per line beside its own paragraph.
   Back to block flow: b is already display:block, so the label sits on its
   own line above the description as the base component intends, and the
   icon is lifted out to an absolute mark in the indent.
   Deliberately NOT a grid: the description is a bare text node, and an
   anonymous grid item cannot be placed by CSS - it would auto-flow into the
   wrong cell. Block flow is the only layout that can hold all three. */
body.fam-convert .wf-whylist li {
  display: block;
  position: relative;
  padding-left: 30px;
}
body.fam-convert .wf-whylist li > i {
  position: absolute;
  left: 0;
  top: 20px;
  line-height: 1;
}
/* Pricing icons: the icons come from the theme sprite in the richtext
   itself (instead of Font Awesome), one <svg
   class="wf-whyico"> per item, and sit in the same 30px indent the `> i`
   rule above reserved for them. Fill is the accent, matching the selected
   state of the wizard's own sprite icons (.wf-wizopt--ico.on .wf-wizico). */
body.fam-convert .wf-whylist li > .wf-whyico {
  position: absolute;
  left: 0;
  top: 19px;
  width: 16px;
  height: 16px;
  fill: var(--hot-on-dark, var(--orange));
}

/* ---- the release ----------------------------------------------------------
   The logo marquee is where the argument ends and the proof begins, so it is
   also where the spine lets go. Breaking the container is the signal.
   Full browser width, no white boxes behind the marks, and pulled
   up to sit ~160px under the table instead of a band's worth of dead air. */
body.fam-convert .cv-logos {
  padding-top: var(--cv-logo-lead);
  padding-bottom: clamp(96px, 12vh, 150px);
  overflow: hidden;
}
/* the stack's own bottom band-pad was ADDING to the 160px lead - the gap
   between table and marks came out near 300px. The lead is the whole gap. */
body.fam-convert[data-cv-layout="spine"] .cv-stack { padding-bottom: 0; }
body.fam-convert .cv-logos .wrap { max-width: none; padding-inline: 0; }
body.fam-convert .cv-logos .wf-marquee {
  width: 100vw;
  margin-left: calc(50% - 50vw);
  max-width: none;
}
/* The screen blend below needs the band colour to be IN the same stacking
   context as the marks. site.css gives every .wf-section > .wrap
   position:relative + z-index:2, which opens a stacking context whose
   backdrop is transparent - the band colour is painted further back, so the
   blend had nothing to lighten against and every tile composited as a black
   square. Rather than repaint the ground on the marquee (which then reads as
   a lighter stripe, because the grain and ground layers no longer sit over
   it), collapse the intermediate context so the blend reaches the section's
   own background. Safe here: nothing in this wrap is absolutely positioned. */
body.fam-convert .cv-logos > .wrap {
  position: static;
  z-index: auto;
}
/* The white tiles were baked into the assets (each 'SVG' is a rounded rect
   filled with an embedded PNG on a white ground), so the tile was first
   removed optically with invert + mix-blend-mode:screen. That FAILED in the
   live page: the marquee's animated transform creates a stacking context,
   which isolates the blend from the page ground, and every tile composited
   back as a solid square. (Verification screenshots froze the animation for
   stable captures - which is exactly what hid it. Freeze styles must never
   disable the mechanism under test.)

   Fixed at the source instead: assets/logos/<name>-mark.png are re-derived
   marks - luminance mapped to alpha, white with real transparency, trimmed
   to content. No blend, no filter, works on any ground, ports to HubSpot. */
body.fam-convert .cv-logos .wf-logo,
body.fam-convert .cv-logos .wf-logo:has(img) {
  background: none;
  border: 0;
  padding: 0;
}
body.fam-convert .cv-logos .wf-logo img {
  width: auto;
  height: auto;
  max-width: 100px;
  max-height: 64px;
  opacity: 0.6;
  transition: opacity var(--time-base, 0.24s) var(--ease-glide, ease);
}
body.fam-convert .cv-logos .wf-logo img:hover { opacity: 0.95; }
/* No edge feather. The guillotine problem was the white TILES being cut
   mid-rectangle; with the tiles gone, bare marks running off the viewport
   edge is just what a marquee looks like (and what the design shows).
   A painted-gradient feather also can't match the composite ground - the
   grain layer sits over the band, so the solid --bandbg strip at each edge
   read as a faint seam. (mask-image is not an option either: masks isolate,
   which breaks the screen blend above back into black tiles.) */
body.fam-convert .cv-logos .wf-marquee-track { gap: 120px; }
body.fam-convert .cv-logos .wf-marquee-row { gap: 120px; }

/* ---- pricing --------------------------------------------------------------
   Hero centred, and the two .wf-card boxes in the quote column
   dropped. Those boxes wrapped running prose in a frame that added a border,
   padding and a background to text that was already inside a band - three
   layers of container for one paragraph. Without them the copy is shorter
   and reads straight down. */
body.fam-convert.cv-pricing .wf-hero .wrap { text-align: center; }
/* The pricing hero is NOT in a split - it owns the full width - so the
   family's one-line headline cap doesn't apply. Full display scale, stacked
   and centred, per the approved design. */
body.fam-convert.cv-pricing .wf-hero .t-display {
  font-size: clamp(44px, 6.4vw, 96px);
  line-height: 0.94;
  max-width: 14ch;
  margin-inline: auto;
  text-wrap: balance;
}
body.fam-convert.cv-pricing .wf-hero p {
  margin-inline: auto;
  max-width: 58ch;
}
/* Much less vertical spacing, so the visitor gets to the next section
   quickly. The hero is a one-line statement and a paragraph, not
   a destination - it introduces the quote tool and should hand off quickly.
   Trimmed on BOTH sides of the seam: a hero bottom plus a full band top was
   over 200px of nothing between the promise and the thing that delivers it. */
body.fam-convert.cv-pricing .wf-hero {
  /* double top padding comes from the site-wide hero rule; bottom stays trimmed */
  padding-bottom: clamp(40px, 4vh + 1vw, 72px);
}
body.fam-convert.cv-pricing .cv-quote {
  padding-top: clamp(40px, 4vh + 1vw, 76px);
}
body.fam-convert.cv-pricing .cv-quote .wf-card {
  border: 0;
  background: none;
  padding: 0;
}
body.fam-convert.cv-pricing .cv-quote .wf-card + .wf-card { margin-top: 28px; }
/* the label that was the card's header becomes a run-in heading */
body.fam-convert.cv-pricing .cv-quote .wf-card > .t-ui {
  display: block;
  margin-bottom: 10px;
  color: var(--ink);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-size: 13px;
}

/* scroll cue at the hero's foot: mono label + a line that draws down and
   repeats. Quiet furniture, not a control - it just says the page continues. */
body.fam-convert .cv-scrollcue {
  margin-top: clamp(32px, 4vh, 56px);
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  font: var(--t-label-weight, 500) 11px/1 var(--t-label-font, monospace);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--dim);
}
body.fam-convert .cv-scrollcue i {
  width: 1px;
  height: 44px;
  background: var(--line);
  position: relative;
  overflow: hidden;
}
body.fam-convert .cv-scrollcue i::after {
  content: "";
  position: absolute;
  left: 0;
  top: -50%;
  width: 100%;
  height: 50%;
  background: var(--orange);
  animation: cv-scrollcue 2.2s var(--ease-glide, ease-in-out) infinite;
}
@keyframes cv-scrollcue { to { top: 100%; } }
@media (prefers-reduced-motion: reduce) {
  body.fam-convert .cv-scrollcue i::after { animation: none; top: 50%; }
}

/* logo band rides the SAME ground as the rest of the page - no
   raised strip. The band class stays in the markup for structure; the paint
   is overridden here so the marquee reads as marks floating on the page. */
body.fam-convert .cv-logos {
  background: var(--bg-dark);
  --bandbg: var(--bg-dark);
  --ink: var(--offwhite);
  --dim: var(--dim-on-dark);
  --line: var(--line-on-dark);
}

/* ---- contact --------------------------------------------------------------
   Text links were rendering as bare inherited text - the two mailto lines
   read as sentences, not as things you could click. Given the page's whole
   job is routing someone to a human, that is the one thing that must look
   actionable. */
body.fam-convert.cv-contact .cv-col a:not(.btn) {
  color: var(--ink);
  text-decoration: none;
  background-image: linear-gradient(var(--orange), var(--orange));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% var(--bw-hairline, 1px);
  padding-bottom: 2px;
  transition: background-size var(--time-base, 0.24s) var(--ease-glide, ease),
              color var(--time-base, 0.24s) var(--ease-glide, ease);
}
body.fam-convert.cv-contact .cv-col a:not(.btn):hover {
  color: var(--orange);
  background-size: 100% 2px;
}

/* office block: the "we are a real company in a real place" fact. Straight
   information, no cleverness - mark, label, address, and nothing else.
   Deliberately NOT an embedded map: a third-party tile load would drag a
   full-colour raster into a grayscale earth palette and add an external
   request, to route precisely nobody. */
body.fam-convert .cv-office {
  margin-top: var(--cv-block-gap);
  padding-top: clamp(28px, 3vh + 0.5vw, 44px);
  border-top: var(--bw-hairline, 1px) solid var(--line);
}
body.fam-convert .cv-office-head {
  display: flex;
  align-items: center;
  gap: 12px;
  font: var(--t-label-weight, 500) 12px/1 var(--t-label-font, monospace);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dim);
}
body.fam-convert .cv-office-mark {
  width: 22px;
  height: 22px;
  flex: none;
  color: var(--orange);
}
body.fam-convert .cv-office-mark svg { width: 100%; height: 100%; display: block; }
body.fam-convert .cv-office-name {
  margin-top: 16px;
  font: var(--t-heading-weight, 700) clamp(18px, 1.6vw, 22px)/1.2 var(--t-heading-font, sans-serif);
  letter-spacing: -0.01em;
  color: var(--ink);
}
body.fam-convert .cv-office-addr {
  margin-top: 8px;
  font-style: normal;
  color: var(--dim);
  line-height: 1.6;
  max-width: 34ch;
}
/* HQ badge: the one piece of emphasis, because "headquarters" is the fact
   that makes the address mean something */
body.fam-convert .cv-office-badge {
  display: inline-block;
  margin-left: 10px;
  padding: 3px 8px;
  border: var(--bw-hairline, 1px) solid var(--orange);
  color: var(--orange);
  font: var(--t-label-weight, 500) 10px/1.3 var(--t-label-font, monospace);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  vertical-align: middle;
}

/* ---- responsive -----------------------------------------------------------
   The split and the spine hold from tablet up and collapse only
   below 768 CSS px. A 900px cutoff reads as "breaks over 1000" on a
   zoomed browser: page zoom shrinks CSS pixels, so a ~1100px physical window
   is already under 900 CSS px and the sticky rail would vanish on a desktop. */
@media (max-width: 767px) {
  body.fam-convert .cv-split { grid-template-columns: 1fr; }
  body.fam-convert .cv-rail { position: static; }
  body.fam-convert[data-cv-layout="spine"] .cv-stack {
    display: block;
    padding-inline: var(--wrap-pad, 24px);
  }
  body.fam-convert[data-cv-layout="spine"] .cv-sec,
  body.fam-convert[data-cv-layout="spine"] .cv-sec > .wrap,
  body.fam-convert[data-cv-layout="spine"] .cv-split { display: block; }
  body.fam-convert[data-cv-layout="spine"] .cv-col + .cv-col,
  body.fam-convert[data-cv-layout="spine"] .cv-rail { margin-top: var(--cv-block-gap); }
  /* must repeat position at spine's own specificity: the base spine rule
     (0,3,1) outranks the generic .cv-rail reset above, so without this the
     form stays sticky inside the stacked column and pins itself over the
     content that follows it on phones */
  body.fam-convert[data-cv-layout="spine"] .cv-rail {
    position: static;
    grid-column: auto;
    grid-row: auto;
  }
  body.fam-convert .cv-logos { --cv-logo-lead: 88px; }
}

@media (prefers-reduced-motion: reduce) {
  body.fam-convert .cv-logos .wf-logo img { transition: none; }
  body.fam-convert.cv-contact .cv-col a:not(.btn) { transition: none; }
}

/* ---- pricing CTA rework ----------------------------------------------------
   The spine is retired: pricing reads like partners. Centered hero, then
   the quote section right below - argument left, form beside it at equal
   width, nothing sticky. Equal columns are what buy the form its extra room
   (the rail was 30% under the spine, 40% under destination). */
body.fam-convert.cv-pricing .cv-rail { position: static; }
/* min-width guard: this rule outranks the family's 767px collapse (higher
   specificity, later in the file), so unscoped it would pin the two-column
   grid on phones and the form would never stack */
@media (min-width: 768px) {
  body.fam-convert.cv-pricing .cv-split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

/* wizard card design: */
/* 1 - "Get your custom quote" runs as the card header (the .t-ui treatment
   the other form cards lead with), replacing the dim uppercase strip */
body.fam-convert.cv-pricing .wf-wizhead {
  display: block; margin-bottom: 18px;
  letter-spacing: 0; text-transform: none;
}
body.fam-convert.cv-pricing .wf-wizhead .t-ui { display: block; }
/* 2 - the step counter is a per-step eyebrow above the question */
.wf-wizeyebrow {
  display: block; margin-bottom: 8px;
  font: var(--t-label-weight, 500) 11px/1 var(--t-label-font);
  text-transform: uppercase; letter-spacing: .12em;
  color: var(--orange);
}
/* 3 - no rule above Back/Next */
body.fam-convert.cv-pricing .wf-wiznav { border-top: 0; padding-top: 0; }
/* 4 - capped corner on the card: the .btn notch geometry scaled up. The clip
   would eat an outer box-shadow, so the card's lift rides a drop-shadow on
   the rail, which follows the clipped shape. */
body.fam-convert.cv-pricing .wf-wizard {
  clip-path: polygon(0 0, calc(100% - 20px) 0, 100% 20px, 100% 100%, 0 100%);
}
body.fam-convert.cv-pricing .cv-rail { filter: drop-shadow(0 24px 40px rgba(0, 0, 0, .35)); }

/* ---- wizard progress bar: ONE
   continuous bar. Solid orange = completed ground, a static diagonal hatch
   spans the CURRENT step's third (with a bright seam at its leading edge),
   the rest is dim track, and the right end is chamfered. The three <i>
   segments stay in the DOM as the JS's state carrier but don't paint; the
   two overlay layers read the state via :has() and their sweep between
   thirds IS the step animation. No ticks, no idle motion. */
body.fam-convert.cv-pricing .wf-wizbar {
  position: relative;
  display: block;
  height: 9px;
  margin-bottom: clamp(24px, 2.6vw, 36px);
  background: color-mix(in srgb, var(--orange) 16%, transparent);
  clip-path: polygon(0 0, 100% 0, calc(100% - 9px) 100%, 0 100%);
}
body.fam-convert.cv-pricing .wf-wizbar i { display: none; }
/* solid fill: the completed ground */
body.fam-convert.cv-pricing .wf-wizbar::before {
  content: ""; position: absolute; inset: 0 auto 0 0;
  width: 0;
  background: var(--orange);
  transition: width .55s cubic-bezier(.2, .7, .2, 1);
}
body.fam-convert.cv-pricing .wf-wizbar:has(i:nth-child(2).on)::before { width: 33.34%; }
body.fam-convert.cv-pricing .wf-wizbar:has(i:nth-child(3).on)::before { width: 66.67%; }
/* hatched zone: the step in progress, bright seam on its leading edge */
body.fam-convert.cv-pricing .wf-wizbar::after {
  content: ""; position: absolute; inset: 0 auto 0 0;
  left: 0; width: 33.34%;
  background:
    repeating-linear-gradient(-45deg,
      color-mix(in srgb, var(--orange) 85%, transparent) 0 4px,
      transparent 4px 9px),
    color-mix(in srgb, var(--orange) 26%, transparent);
  border-left: 2px solid color-mix(in srgb, var(--offwhite) 80%, transparent);
  transition: left .55s cubic-bezier(.2, .7, .2, 1);
}
body.fam-convert.cv-pricing .wf-wizbar:has(i:nth-child(2).on)::after { left: 33.34%; }
body.fam-convert.cv-pricing .wf-wizbar:has(i:nth-child(3).on)::after { left: 66.67%; }
@media (prefers-reduced-motion: reduce) {
  body.fam-convert.cv-pricing .wf-wizbar::before,
  body.fam-convert.cv-pricing .wf-wizbar::after { transition: none; }
}

/* type hierarchy: card header runs as a real heading,
   the step question steps up under the orange eyebrow */
body.fam-convert.cv-pricing .wf-wizhead .t-ui {
  font: 800 clamp(20px, 1.7vw, 25px)/1.15 var(--sans);
  letter-spacing: -0.01em;
  text-transform: uppercase;
  color: var(--ink);
}
body.fam-convert.cv-pricing .wf-wizstep > .t-ui {
  font: 700 clamp(17px, 1.35vw, 19px)/1.3 var(--sans);
  letter-spacing: -0.005em;
  color: var(--ink);
}

/* role step: icons instead of checkbox squares, and the option
   text steps up with the input text */
body.fam-convert.cv-pricing .wf-wizopt { font-size: 15px; }
.wf-wizopt--ico .wf-wizico {
  flex: none; width: 18px; height: 18px;
  fill: var(--dim); transition: fill .2s ease;
}
.wf-wizopt--ico:hover .wf-wizico { fill: var(--ink); }
.wf-wizopt--ico.on .wf-wizico { fill: var(--orange); }

/* wizard option states: selection locks orange - same language
   as the field focus state. Border + ring + faint tint, the marker square
   fills orange (the checkbox treatment), and the label goes orange too. */
body.fam-convert.cv-pricing .wf-wizopt {
  transition: border-color .2s ease, background-color .2s ease, box-shadow .2s ease, color .2s ease;
}
body.fam-convert.cv-pricing .wf-wizopt:hover {
  border-color: color-mix(in srgb, var(--lightblue) 65%, var(--line));
  box-shadow: 0 0 10px color-mix(in srgb, var(--lightblue) 15%, transparent);
}
body.fam-convert.cv-pricing .wf-wizopt.on {
  border-color: var(--orange);
  background: color-mix(in srgb, var(--orange) 9%, transparent);
  box-shadow: 0 0 0 1px var(--orange), 0 0 14px color-mix(in srgb, var(--orange) 28%, transparent);
  color: var(--orange);
}
body.fam-convert.cv-pricing .wf-wizopt.on i {
  background: var(--orange);
  border-color: var(--orange);
  box-shadow: inset 0 0 0 3px color-mix(in srgb, var(--navy) 88%, black);
}

/* Pricing wizard submit: the stepped card has one Submit, the nav
   button on the last step. The HubSpot form's own submit row is therefore
   hidden while the stepper runs; with JS off, or Stepped off, body never gets
   wf-wiz-live and the form keeps its button. The done block is the thank-you
   state convert.js reveals after HubSpot reports the submission. */
body.wf-wiz-live .wf-wizard .hs-submit,
body.wf-wiz-live .wf-wizard .hs_submit { display: none; }
body.fam-convert .wf-wizdone[hidden] { display: none; }
body.fam-convert .wf-wizdone { padding: clamp(20px, 2.2vw, 32px) 0 0; }
body.fam-convert .wf-wizdone .t-ui { display: block; color: var(--ink); }

/* card stroke: a plain border gets sheared by the corner
   clip - the diagonal had no stroke and the sides rendered ragged. Same cure
   as .btn: border off, stroke drawn by an evenodd ring pseudo that follows
   the capped shape (1px stroke; 20.41 = 20 + 1*(sqrt(2)-1) on the diagonal). */
body.fam-convert.cv-pricing .wf-wizard {
  border: 0;
  position: relative;
  isolation: isolate;
}
/* 18 percent so the pricing wizard carries the same hairline as the other
   form cards. */
body.fam-convert.cv-pricing .wf-wizard::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: -1;
  background: color-mix(in srgb, var(--offwhite) 18%, transparent);
  clip-path: polygon(evenodd,
    0 0, calc(100% - 20px) 0, 100% 20px, 100% 100%, 0 100%, 0 0,
    1px 1px, calc(100% - 20.41px) 1px, calc(100% - 1px) 20.41px, calc(100% - 1px) calc(100% - 1px), 1px calc(100% - 1px), 1px 1px);
}

/* ---- pricing plans (two-card layout) ----------------------------------------
   Replaces the quote wizard on the pricing page with two side-by-side cards:
   Pay As You Go (left) and Pre-Pay & Save (right). Each card is a glass pane
   with capped TR + BL corners, matching the btn geometry scaled up.

   Accent color #94D2BD for the pricing page's green elements
   (checkmarks, save headline, savings %). Scoped to .cv-plans so the rest
   of the site keeps the global --success. */
body.fam-convert.cv-pricing .cv-plans {
  --pp-accent: #94D2BD;
}

/* grid */
body.fam-convert.cv-pricing .pp-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--cv-gap);
  align-items: start;
}
@media (max-width: 767px) {
  body.fam-convert.cv-pricing .pp-grid { grid-template-columns: 1fr; }
}

/* card shell: capped TR + BL corners */
body.fam-convert .pp-card {
  filter: drop-shadow(0 24px 40px rgba(0, 0, 0, .35));
}
body.fam-convert .pp-card-inner {
  position: relative;
  padding: clamp(24px, 2.8vw, 36px);
  background: linear-gradient(155deg,
    color-mix(in srgb, var(--offwhite) 8%, transparent),
    color-mix(in srgb, var(--offwhite) 3%, transparent) 55%,
    color-mix(in srgb, var(--offwhite) 5%, transparent));
  -webkit-backdrop-filter: blur(14px) saturate(1.15);
  backdrop-filter: blur(14px) saturate(1.15);
  clip-path: polygon(0 0, calc(100% - 20px) 0, 100% 20px, 100% 100%, 20px 100%, 0 calc(100% - 20px));
  isolation: isolate;
  border: 0;
}
body.fam-convert .pp-card-inner::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: -1;
  background: color-mix(in srgb, var(--offwhite) 18%, transparent);
  clip-path: polygon(evenodd,
    0 0, calc(100% - 20px) 0, 100% 20px, 100% 100%, 20px 100%, 0 calc(100% - 20px), 0 0,
    1px 1px, calc(100% - 20.41px) 1px, calc(100% - 1px) 20.41px,
    calc(100% - 1px) calc(100% - 1px), 20.41px calc(100% - 1px), 1px calc(100% - 20.41px), 1px 1px);
}

/* card title */
body.fam-convert .pp-card-title {
  font: 700 clamp(18px, 1.5vw, 22px)/1.2 var(--sans);
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 0 0 20px;
}

/* PAYG price block */
body.fam-convert .pp-price {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-bottom: 4px;
}
body.fam-convert .pp-price-amount {
  font: 800 clamp(36px, 3.5vw, 52px)/1 var(--sans);
  letter-spacing: -0.02em;
  color: var(--ink);
}
body.fam-convert .pp-price-period {
  font: 400 clamp(16px, 1.3vw, 20px)/1 var(--sans);
  color: var(--dim);
}
body.fam-convert .pp-price-sub {
  font: 400 clamp(14px, 1vw, 16px)/1.4 var(--t-body-font);
  color: var(--dim);
  margin: 0 0 20px;
}

/* Pre-Pay headline */
body.fam-convert .pp-headline {
  font: 700 clamp(22px, 2vw, 30px)/1.2 var(--sans);
  letter-spacing: -0.01em;
  color: var(--pp-accent, var(--success));
  margin: 0 0 6px;
}
body.fam-convert .pp-subtitle {
  font: 400 clamp(14px, 1vw, 16px)/1.4 var(--t-body-font);
  color: var(--dim);
  margin: 0 0 20px;
}

/* benefits (green checkmarks) */
body.fam-convert .pp-benefit {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
  font: 400 15px/1.4 var(--t-body-font);
  color: var(--ink);
}
body.fam-convert .pp-check {
  flex: none;
  width: 16px;
  height: 16px;
  fill: var(--pp-accent, var(--success));
}
body.fam-convert .pp-benefit:last-of-type { margin-bottom: 20px; }

/* pricing detail box (PAYG) */
body.fam-convert .pp-detail-box {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: 16px;
  margin-bottom: 24px;
  background: color-mix(in srgb, var(--offwhite) 5%, transparent);
  border: var(--bw-hairline, 1px) solid var(--line);
  border-radius: 0;
}
body.fam-convert .pp-detail-text {
  font: 400 13px/1.5 var(--t-body-font);
  color: var(--dim);
  flex: 1;
}
body.fam-convert .pp-detail-text p { margin: 0; }
body.fam-convert .pp-detail-text strong { color: var(--ink); font-weight: 600; }
body.fam-convert .pp-detail-link {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font: var(--t-label-weight, 500) 13px/1 var(--t-body-font);
  color: var(--lightblue);
  text-decoration: none;
  white-space: nowrap;
}
body.fam-convert .pp-detail-link svg { width: 12px; height: 12px; }
body.fam-convert .pp-detail-link:hover { color: var(--ink); }

/* slider blocks */
body.fam-convert .pp-slider-block { margin-bottom: 24px; }
body.fam-convert .pp-slider-label {
  display: block;
  font: var(--t-label-weight, 500) 14px/1.3 var(--t-body-font);
  color: var(--ink);
  margin-bottom: 10px;
}
body.fam-convert .pp-slider-display { margin-bottom: 8px; }
body.fam-convert .pp-slider-input {
  width: 100%;
  box-sizing: border-box;
  background: color-mix(in srgb, var(--offwhite) 4%, transparent);
  border: var(--bw-hairline, 1px) solid var(--line);
  color: var(--ink);
  font: 400 15px/1.4 var(--t-body-font);
  padding: 12px 14px;
}
body.fam-convert .pp-slider-wrap { padding: 8px 0; }
body.fam-convert .pp-range {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 4px;
  background: var(--line);
  border-radius: 2px;
  outline: none;
  cursor: pointer;
}
body.fam-convert .pp-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--lightblue);
  border: 2px solid var(--lightblue);
  cursor: pointer;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--lightblue) 25%, transparent);
}
body.fam-convert .pp-range::-moz-range-thumb {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--lightblue);
  border: 2px solid var(--lightblue);
  cursor: pointer;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--lightblue) 25%, transparent);
}
body.fam-convert .pp-slider-minmax {
  display: flex;
  justify-content: space-between;
  font: 400 12px/1.3 var(--t-body-font);
  color: var(--dim);
  margin-top: 4px;
}
body.fam-convert .pp-slider-help {
  margin-top: 14px;
  font: 400 13px/1.5 var(--t-body-font);
  color: var(--dim);
}
body.fam-convert .pp-slider-help p { margin: 0; }

/* features link (scrolls to comparison table) */
body.fam-convert .pp-features-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font: var(--t-label-weight, 500) 14px/1.3 var(--t-body-font);
  color: var(--pp-accent, var(--lightblue));
  text-decoration: none;
  padding: 8px 0;
  margin-bottom: 24px;
}
body.fam-convert .pp-features-link:hover { color: var(--ink); }
body.fam-convert .pp-chevron {
  flex: none;
  width: 12px;
  height: 12px;
}

/* calculator block (Pre-Pay) */
body.fam-convert .pp-calculator {
  padding: 20px;
  margin-bottom: 20px;
  background: color-mix(in srgb, var(--offwhite) 5%, transparent);
  border: var(--bw-hairline, 1px) solid var(--line);
}
body.fam-convert .pp-calc-head {
  margin-bottom: 12px;
}
body.fam-convert .pp-calc-label {
  display: block;
  font: 400 14px/1.3 var(--t-body-font);
  color: var(--dim);
  margin-bottom: 4px;
}
body.fam-convert .pp-calc-value {
  display: block;
  font: 700 clamp(24px, 2.2vw, 32px)/1.1 var(--sans);
  letter-spacing: -0.02em;
  color: var(--ink);
}
body.fam-convert .pp-calc-price {
  display: flex;
  gap: 28px;
  padding-top: 16px;
  margin-top: 16px;
  border-top: var(--bw-hairline, 1px) solid var(--line);
}
body.fam-convert .pp-calc-price-label {
  display: block;
  font: var(--t-label-weight, 500) 10px/1 var(--t-label-font, monospace);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
  margin-bottom: 6px;
}
body.fam-convert .pp-calc-price-amount {
  font: 700 clamp(22px, 2vw, 28px)/1.1 var(--sans);
  letter-spacing: -0.02em;
  color: var(--ink);
}
body.fam-convert .pp-calc-price-period {
  font: 400 clamp(14px, 1vw, 16px)/1 var(--sans);
  color: var(--dim);
}
body.fam-convert .pp-calc-savings {
  font: 700 clamp(22px, 2vw, 28px)/1.1 var(--sans);
  letter-spacing: -0.02em;
  color: var(--pp-accent, var(--success));
}

/* sensei AI included */
body.fam-convert .pp-sensei {
  padding: 14px 20px;
  margin-bottom: 16px;
}
body.fam-convert .pp-sensei-label {
  display: block;
  font: var(--t-label-weight, 500) 10px/1 var(--t-label-font, monospace);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
  margin-bottom: 8px;
}
body.fam-convert .pp-sensei-detail {
  font: 400 13px/1.5 var(--t-body-font);
  color: var(--dim);
  margin: 0;
}

/* additionally box */
body.fam-convert .pp-additionally {
  padding: 16px 20px;
  margin-bottom: 24px;
  background: color-mix(in srgb, var(--offwhite) 5%, transparent);
  border: var(--bw-hairline, 1px) solid var(--line);
}
body.fam-convert .pp-additionally-label {
  display: block;
  font: var(--t-label-weight, 500) 10px/1 var(--t-label-font, monospace);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
  margin-bottom: 14px;
}
body.fam-convert .pp-additionally-item {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
  font: 400 14px/1.4 var(--t-body-font);
  color: var(--ink);
}
body.fam-convert .pp-additionally-item:last-child { margin-bottom: 0; }
body.fam-convert .pp-additionally-icon {
  flex: none;
  width: 16px;
  height: 16px;
  color: var(--lightblue);
}

/* CTA button (full width, centred, on both cards) */
body.fam-convert .pp-cta {
  display: block;
  width: 100%;
  text-align: center;
  font-size: 16px;
  line-height: 1.2;
  margin-top: auto;
}

/* spacing between cv-plans and the hero */
body.fam-convert.cv-pricing .cv-plans {
  padding-top: clamp(40px, 4vh + 1vw, 76px);
}

/* ---- quote modal --------------------------------------------------------- */
body.fam-convert .pp-modal[hidden] { display: none; }
body.fam-convert .pp-modal {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
body.fam-convert .pp-modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, .7);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
body.fam-convert .pp-modal-card {
  position: relative;
  width: 100%;
  max-width: 640px;
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  padding: clamp(28px, 3.2vw, 48px);
  background: var(--bg-dark);
  color: var(--ink);
  clip-path: polygon(0 0, calc(100% - 20px) 0, 100% 20px, 100% 100%, 20px 100%, 0 calc(100% - 20px));
}
body.fam-convert .pp-modal-card::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: color-mix(in srgb, var(--offwhite) 18%, transparent);
  clip-path: polygon(evenodd,
    0 0, calc(100% - 20px) 0, 100% 20px, 100% 100%, 20px 100%, 0 calc(100% - 20px), 0 0,
    1px 1px, calc(100% - 20.41px) 1px, calc(100% - 1px) 20.41px,
    calc(100% - 1px) calc(100% - 1px), 20.41px calc(100% - 1px), 1px calc(100% - 20.41px), 1px 1px);
}
body.fam-convert .pp-modal-close {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 32px;
  height: 32px;
  background: none;
  border: 0;
  color: var(--dim);
  cursor: pointer;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
body.fam-convert .pp-modal-close svg { width: 16px; height: 16px; }
body.fam-convert .pp-modal-close:hover { color: var(--ink); }
body.fam-convert .pp-modal-body .hs-form .hs-button {
  display: block;
  width: 100%;
  text-align: center;
  font-size: 16px;
  line-height: 1.2;
}

/* ============================================================================
   HubSpot bridge. Everything above this line is the original convert.css.
   Everything below exists because HubSpot wraps modules and columns in markup
   the original design never had. Kept at the end so the upper half stays
   diffable against its source.
   ========================================================================= */

/* HubSpot's drag-and-drop columns arrive as .row-fluid > [class*="span"] with
   percentage widths and floats from the portal's layout stylesheet. Inside a
   .cv-split those widths fight the grid tracks, so the float model is switched
   off and the grid is allowed to size the columns. Scoped to the family so no
   other template is touched. */
body.fam-convert .cv-split > [class*="span"],
body.fam-convert .cv-split > .dnd-column {
  width: auto;
  float: none;
  margin-left: 0;
  min-width: 0;
}

/* HubSpot form modules render .hs-form, not the bare .wf-form. The design's
   form language (hairline field, mono label, orange focus ring) is re-expressed
   against the HubSpot DOM so a default form module dropped into a .cv-rail
   reads as the designed form. Field-for-field parity with the static markup
   is not achievable: HubSpot renders a visible <label> above every input
   where the design uses a placeholder only, and appends its own error containers. */
body.fam-convert .hs-form .hs-form-field { margin-bottom: 14px; }
body.fam-convert .hs-form .hs-form-field > label {
  display: block;
  margin-bottom: 6px;
  font: var(--t-label-weight, 500) 11px/1.4 var(--t-label-font, monospace);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
}
body.fam-convert .hs-form .hs-input:not([type="checkbox"]):not([type="radio"]) {
  width: 100%;
  box-sizing: border-box;
  background-color: color-mix(in srgb, var(--offwhite) 4%, transparent);
  border: var(--bw-hairline, 1px) solid var(--line);
  color: var(--ink);
  font: 400 15px/1.4 var(--t-body-font, sans-serif);
  padding: 12px 14px;
}
body.fam-convert .hs-form .hs-input:focus {
  outline: none;
  border-color: var(--orange);
  box-shadow: 0 0 0 1px var(--orange),
              0 0 14px color-mix(in srgb, var(--orange) 28%, transparent);
}
body.fam-convert .hs-form .hs-button {
  border: 0;
  background: var(--orange);
  color: var(--navy);
  font: var(--t-label-weight, 500) 13px/1 var(--t-label-font, monospace);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 15px 22px;
  cursor: pointer;
  clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 10px, 100% 100%, 0 100%);
}
body.fam-convert .hs-form .hs-error-msg,
body.fam-convert .hs-form .hs-error-msgs label {
  color: var(--orange);
  font-size: 12px;
}

/* ---- book a demo hero ------------------------------------------------------
   The convert family puts an h2 in the hero, and hero.module's split_form
   layout keeps that h2 for the same reason; that stays true for the module.
   Only this page's static hero fragment becomes an h1, and larger, so the page
   opens on a real document heading at roughly 75px on desktop.

   Two selectors on purpose. The first is the page, for the day the static
   fragment is replaced by a composed section. The second is slug independent,
   so the rule survives a slug change (page-convert.html builds the cv-* class
   from the slug). */
body.fam-convert.cv-book-a-demo .cv-demo-title,
body.fam-convert .cv-col--hero .cv-demo-title {
  font-size: clamp(38px, 5.2vw, 75px);
  line-height: 1.04;
}

/* The full width Submit button centres its label at 16px. Written twice: once
   for the static form markup inside a rich text block, and once for HubSpot's
   own .hs-button, so the same result holds when a real form module replaces
   it. The .hs-button half also has to unset
   the inline-block sizing the bridge block above gives it. */
body.fam-convert .cv-demo-submit,
body.fam-convert .cv-rail .hs-form .hs-button {
  display: block;
  width: 100%;
  text-align: center;
  font-size: 16px;
  line-height: 1.2;
}

/* ---- the composable two column convert section ----------------------------
   Contact and the book a demo hero are built from HubSpot columns with a real
   form module in the right hand one, so an editor can change content (such as
   the office address) without editing raw markup.

   HubSpot renders a two column section as

     div.cv-sec.cv-split-row > div.row-fluid > div.span6 (twice)

   and it DROPS any cssClass set on a column cell, which was measured on the
   live page rather than assumed: only the section level class from
   rowMetaData survives. So the design's .wrap, .cv-split, .cv-col and .cv-rail
   have no elements to land on, and the row itself has to play .wrap and
   .cv-split at once while the two columns are addressed by position.

   Scoped to .cv-split-row, a marker carried only by the two column sections,
   so the family's single column .cv-sec sections are untouched. Those
   still carry the original markup including their own .wrap, and giving every
   .cv-sec row a grid would break all of them. */
body.fam-convert .cv-split-row > .row-fluid {
  max-width: var(--wrap-max, 1360px);
  margin: 0 auto;
  padding: 0 clamp(20px, 3.5vw, 48px);
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--cv-rail-w);
  gap: var(--cv-gap);
  align-items: start;
}
/* The portal stylesheet floats and percentage sizes every .span*, which fights
   the grid tracks. Same fix the earlier bridge block applies inside .cv-split,
   restated here because there is no .cv-split element on this route. */
body.fam-convert .cv-split-row > .row-fluid > [class*="span"] {
  width: auto;
  float: none;
  margin-left: 0;
  min-width: 0;
}
/* The design's .cv-rail is sticky. The rail is the second column here. */
body.fam-convert .cv-split-row > .row-fluid > [class*="span"]:last-child {
  position: sticky;
  top: var(--cv-stick-top);
}
/* HubSpot wraps a column's module in three more divs and a span before the
   content, so the `.cv-col > * + *` rule cannot reach the copy. Restated at the
   depth the rich text actually renders at. */
body.fam-convert .cv-split-row .hs_cos_wrapper_type_rich_text > * + * {
  margin-top: 20px;
}

@media (max-width: 767px) {
  body.fam-convert .cv-split-row > .row-fluid { grid-template-columns: 1fr; }
  body.fam-convert .cv-split-row > .row-fluid > [class*="span"]:last-child {
    position: static;
  }
}

/* The form module's own title stands in for the design's `<span class="t-ui">Connect
   with Us</span>`, which is form furniture rather than page copy and therefore
   has no column of its own to live in. HubSpot renders it as a block with
   heading defaults, measured at 30px against the design's 18px, so it is given the
   .t-ui type ramp directly. */
body.fam-convert .cv-split-row .form-title {
  font: var(--t-ui-weight) 14px/1.3 var(--t-ui-font);
  color: var(--ink);
  margin: 0 0 16px;
}

/* ---- demo page layout: book a demo reads like pricing and partners
   The hero is the standard centred hero module (layout "centered") with an h1
   and a lead, and the form moves into its own two column band right below it,
   the same composable .cv-split-row section contact already uses.

   Three things the page spec alone cannot reach, all scoped to this page:

   1. The family caps every convert hero headline at 38px and its lead at 20px
      (the split-hero column ceiling, top of this file). Those rules outrank
      hero.module's own wf-hero--center block on specificity, so the centred
      values are restated here at one class higher, and the lead takes the
      site's own .t-lead scale rather than the split column's, which is what
      partners renders.
   2. The rail card. The design wraps the form in .cv-rail > .wf-formcard (the
      glass pane from components.css). HubSpot drops every column cell class, so
      neither element exists on the live page. The pane is restated on the split
      row's last column, the only element that survives, with the same values.
   3. The full width, centred, 16px Submit. The rule above is keyed to .cv-rail
      for the same reason and never matches here; restated on the split row.

   The hero bottom and the band top are trimmed the way pricing's hero and
   quote section are, so the form is reached quickly. */
body.fam-convert.cv-book-a-demo .wf-hero .wrap { text-align: center; }
body.fam-convert.cv-book-a-demo .wf-hero .t-display {
  font-size: clamp(44px, 6.4vw, 96px);
  line-height: .94;
  max-width: 14ch;
  margin-inline: auto;
  text-wrap: balance;
}
body.fam-convert.cv-book-a-demo .wf-hero .t-lead {
  font: var(--t-heading-weight) clamp(20px, 1.8vw, 26px)/1.4 var(--t-heading-font);
  letter-spacing: -.01em;
  color: var(--ink);
  max-width: 58ch;
  margin-inline: auto;
}
body.fam-convert.cv-book-a-demo .wf-hero {
  padding-bottom: clamp(40px, 4vh + 1vw, 72px);
}
body.fam-convert.cv-book-a-demo .cv-split-row {
  padding-top: clamp(40px, 4vh + 1vw, 76px);
}
/* The card is the column's inner module wrapper, not the column itself.
   The background is kept see-through on this page and /book-a-demo.

   Why the element carries the glass itself: with the hairline colour (offwhite
   18 percent) on the element and the glass on ::before inset 1px, the interior
   composites 18 percent PLUS the 4 to 11 percent gradient, roughly 24 percent
   offwhite, against partners' 7. A translucent pseudo cannot cover a translucent
   parent, and z-index:-1 does not put it behind the parent's own background
   because backdrop-filter makes the parent a stacking context.

   So the element carries the glass alone, at the same 8/3/5 gradient and
   14px blur components.css gives .wf-formcard, and ::before is a masked 1px
   frame - the only kind of hairline that follows a clip-path cut, which a CSS
   border cannot. These three cards are one component with two carriers,
   because HubSpot drops every column cell class and .wf-formcard does not
   exist on this page. */
body.fam-convert.cv-book-a-demo .cv-split-row > .row-fluid > [class*="span"]:last-child {
  filter: drop-shadow(0 24px 40px rgba(0, 0, 0, .35));
}
body.fam-convert.cv-book-a-demo .cv-split-row > .row-fluid > [class*="span"]:last-child > .row-fluid-wrapper > .row-fluid > [class*="span"] {
  position: relative;
  padding: 28px;
  background: linear-gradient(155deg,
    color-mix(in srgb, var(--offwhite) 8%, transparent),
    color-mix(in srgb, var(--offwhite) 3%, transparent) 55%,
    color-mix(in srgb, var(--offwhite) 5%, transparent));
  -webkit-backdrop-filter: blur(14px) saturate(1.15);
  backdrop-filter: blur(14px) saturate(1.15);
  /* inset top highlight removed, matching the glass cards in components.css. */
  clip-path: polygon(0 0, calc(100% - 20px) 0, 100% 20px, 100% 100%, 0 100%);
}
body.fam-convert.cv-book-a-demo .cv-split-row > .row-fluid > [class*="span"]:last-child > .row-fluid-wrapper > .row-fluid > [class*="span"]::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: color-mix(in srgb, var(--offwhite) 18%, transparent);
  /* evenodd ring so the hairline follows the cap
     instead of filling a wedge (a mask hole would be square). Same shape as
     .wf-formcard::after in components.css. */
  clip-path:polygon(evenodd, 0 0,calc(100% - 20px) 0,100% 20px,100% 100%,0 100%,1px 1px,calc(100% - 20.41px) 1px,calc(100% - 1px) 20.41px,calc(100% - 1px) calc(100% - 1px),1px calc(100% - 1px));
}
body.fam-convert.cv-book-a-demo .cv-split-row .hs-form .hs-button {
  display: block;
  width: 100%;
  text-align: center;
  font-size: 16px;
  line-height: 1.2;
}
/* the band's subhead takes the same subtitle scale as the
   hero lead (the site .t-lead ramp, not base.css's 17px prose lead), and a
   plain paragraph of supporting copy sits under it. */
body.fam-convert.cv-book-a-demo .cv-split-row .t-lead {
  /* 30 percent over the hero lead's 20/1.8vw/26 ramp */
  font: var(--t-heading-weight) clamp(26px, 2.34vw, 34px)/1.3 var(--t-heading-font);
  letter-spacing: -.01em;
  color: var(--ink);
  max-width: 30ch;
}

/* ---- contact ----------------------------------------------------------------
   Everything is scoped to .cv-contact; /book-a-demo and /pricing are untouched.

   1. Short text hero. contact.json opens on the standard hero module
      (layout centered, scroll cue) with "Get in Touch" as the h1 and nothing
      else, the shape /book-a-demo and /partners already use. The family's
      38px split-hero cap on .t-display outranks the module's own centred
      block, so the centred values are restated one class higher, exactly as
      .cv-book-a-demo does above. The hero bottom and the band top are
      trimmed the same way so the form is reached quickly. */
body.fam-convert.cv-contact .wf-hero .wrap { text-align: center; }
body.fam-convert.cv-contact .wf-hero .t-display {
  font-size: clamp(44px, 6.4vw, 96px);
  line-height: .94;
  max-width: 14ch;
  margin-inline: auto;
  text-wrap: balance;
}
body.fam-convert.cv-contact .wf-hero { padding-bottom: clamp(40px, 4vh + 1vw, 72px); }
body.fam-convert.cv-contact .cv-split-row { padding-top: clamp(40px, 4vh + 1vw, 76px); }

/* 2. The glass card behind the form. HubSpot drops column cell classes, so
      .wf-formcard cannot exist on this route. The pane is restated on the
      column's inner module wrapper with the values components.css gives
      .wf-formcard, the evenodd ring, and the full width 16px submit. Same
      rules as the .cv-book-a-demo block, keyed to this page. The inset top
      highlight is deliberately left out here: every glass card carries a
      standard border color, equal on all sides, and no inset highlight. */
body.fam-convert.cv-contact .cv-split-row > .row-fluid > [class*="span"]:last-child {
  filter: drop-shadow(0 24px 40px rgba(0, 0, 0, .35));
}
body.fam-convert.cv-contact .cv-split-row > .row-fluid > [class*="span"]:last-child > .row-fluid-wrapper > .row-fluid > [class*="span"] {
  position: relative;
  padding: 28px;
  background: linear-gradient(155deg,
    color-mix(in srgb, var(--offwhite) 8%, transparent),
    color-mix(in srgb, var(--offwhite) 3%, transparent) 55%,
    color-mix(in srgb, var(--offwhite) 5%, transparent));
  -webkit-backdrop-filter: blur(14px) saturate(1.15);
  backdrop-filter: blur(14px) saturate(1.15);
  clip-path: polygon(0 0, calc(100% - 20px) 0, 100% 20px, 100% 100%, 0 100%);
}
body.fam-convert.cv-contact .cv-split-row > .row-fluid > [class*="span"]:last-child > .row-fluid-wrapper > .row-fluid > [class*="span"]::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: color-mix(in srgb, var(--offwhite) 18%, transparent);
  clip-path: polygon(evenodd, 0 0,calc(100% - 20px) 0,100% 20px,100% 100%,0 100%,1px 1px,calc(100% - 20.41px) 1px,calc(100% - 1px) 20.41px,calc(100% - 1px) calc(100% - 1px),1px calc(100% - 1px));
}
body.fam-convert.cv-contact .cv-split-row .hs-form .hs-button {
  display: block;
  width: 100%;
  text-align: center;
  font-size: 16px;
  line-height: 1.2;
}

/* 3. Wider gutter between the paired fields. HubSpot renders a field group
      as fieldset.form-columns-2 with two floated 50 percent fields whose
      .input carries margin-right: 8px (measured live), so the
      pair sat 8px apart. The first field's input gives up 24px instead; the
      second keeps its 8px so its right edge still lines up with the single
      column fields below. */
body.fam-convert.cv-contact .hs-form fieldset.form-columns-2 .hs-form-field:first-child .input { margin-right: 24px; }

/* 4. The reCAPTCHA badge ships with an inline style box-shadow: gray 0 0 5px
      (measured live on .grecaptcha-badge), which renders as a glow. It is
      an inline style, so !important is the only thing that reaches it. */
body.fam-convert.cv-contact .hs-form .grecaptcha-badge { box-shadow: none !important; }

/* 5. Left column. The mailto treatment at the top of the contact block is
      keyed to .cv-col, which HubSpot drops, so the links rendered as plain
      text (measured: no underline, no hover). Re-keyed to the rich text
      wrapper that does survive; covers the two mailto links and Get
      Directions. "Not a Fan of Forms" is a .t-h3 (20 to 30px) between the
      quiet h2 (30 to 54px) and the 17px lead, with air above it; the rich
      text stack's 20px rule is (0,4,0), so these are restated at that depth. */
body.fam-convert.cv-contact .cv-split-row .hs_cos_wrapper_type_rich_text a:not(.btn) {
  color: var(--ink);
  text-decoration: none;
  background-image: linear-gradient(var(--orange), var(--orange));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% var(--bw-hairline, 1px);
  padding-bottom: 2px;
  transition: background-size var(--time-base, 0.24s) var(--ease-glide, ease),
              color var(--time-base, 0.24s) var(--ease-glide, ease);
}
body.fam-convert.cv-contact .cv-split-row .hs_cos_wrapper_type_rich_text a:not(.btn):hover {
  color: var(--orange);
  background-size: 100% 2px;
}
body.fam-convert.cv-contact .cv-split-row .hs_cos_wrapper_type_rich_text > .cv-contact-sub { margin-top: 40px; }
body.fam-convert.cv-contact .cv-split-row .hs_cos_wrapper_type_rich_text > .cv-contact-sub + p { margin-top: 10px; }

/* 6. Office block: no rule above it, Headquarters as the label, no badge,
      four address lines, a directions link under them. */
body.fam-convert.cv-contact .cv-split-row .hs_cos_wrapper_type_rich_text > .cv-office {
  margin-top: 40px;
  border-top: 0;
  padding-top: 0;
}
body.fam-convert.cv-contact .cv-office-directions { margin-top: 14px; }
@media (prefers-reduced-motion: reduce) {
  body.fam-convert.cv-contact .cv-split-row .hs_cos_wrapper_type_rich_text a:not(.btn) { transition: none; }
}