/* ─────────────────────────────────────────────────────────────────────────
   /homepage-v2 — the left-aligned hero, with the journey as a band under it.

   Layers on microsite.css, partners.css and partners-v6b.css (for the
   journey's palette and its waypoint cards). Everything here is scoped to
   .hv2-hero, so the current homepage and /partners are untouched.

   Top to bottom, the section is: the copy (headline, subheadline and
   button, all left-aligned); then the journey band,
   the full width of the page. One atmosphere spans both, so the lagoon orbs
   (partners-mesh.js sizes them to their canvas) come out larger and reach
   down into the band. The atmosphere's mask fades them out over the band:
   full strength behind the copy, a trace behind the top of the path, gone
   by the band's bottom edge. The three differentiators and the screenshot
   follow on the plain page ground.
   ───────────────────────────────────────────────────────────────────────── */

/* ───────── 01 the copy, left-aligned ─────────
   One column, flush with the logo: the two-line headline ("Draft and Sign"
   plus the rolling word, then "Straight From HubSpot."), the subheadline
   under it, then the button. The stack drops microsite.css's 1248px cap,
   which at wide screens set the copy about 32px in from the logo. */
.hv2-hero { padding-top: calc(var(--space-3xl) + var(--nav-h)); }
.hv2-hero .hero-stack { max-width: none; margin: 0 0 var(--space-xl); }
.hv2-hero .hero-copy { max-width: none; text-align: left; }
.hv2-hero .h1,
.hv2-hero-side .hero-sub { margin-inline: 0; }
.hv2-hero .hero-cta-group.works-with { justify-items: start; }
/* The copy sits over the journey band, which reaches up behind the
   buttons (section 03). */
.hv2-hero .hero-content-and-logo-bar { position: relative; z-index: 2; }
/* A very soft shadow under the headline, so the orange rolling word holds
   up against the brightest orbs. Wide and faint: it should read as the
   type being a touch lifted, not as a visible halo.
   It is a filter on the whole headline, not a text-shadow: the roller
   clips its words top and bottom (below), and a text-shadow on the word
   got clipped with it, which left a faint dark box behind the word. The
   filter draws after the clip, so it shadows the shapes that show. */
.hv2-hero .h1 { filter: drop-shadow(0 2px 14px rgb(0 0 0 / .32)) drop-shadow(0 1px 1.5px rgb(0 0 0 / .14)); }
/* The rolling word's box follows the word showing (homepage-v2.js sets its
   width on each roll). With the headline left-aligned, "Draft and Sign"
   holds still and only the box behind the word changes; the width eases on
   a slow, symmetric curve, a little longer than the .55s roll.
   Overflow stays visible sideways, so a wider incoming word is never cut off
   at the right while the box grows; the clip-path still cuts the roll off
   top and bottom the way microsite.css's overflow: hidden did. */
.hv2-hero .roller {
  align-items: flex-start;
  text-align: left;
  overflow: visible;
  /* .05em off the top: the word above's descenders ("p" in Proposals)
     hang just into the box's top edge and showed as a sliver */
  clip-path: inset(.05em -100vw 0 0);
  transition: width .8s cubic-bezier(.45, 0, .25, 1);
}
.hv2-hero .roller-word { width: max-content; }
/* Each sentence of the headline is its own line box, so the rolling word
   never pulls "Straight From HubSpot." up onto the first line. */
.hv2-hero .hv2-line { display: block; }
@media (min-width: 768px) {
  .hv2-hero .hv2-line--roll { white-space: nowrap; }
}
@media (prefers-reduced-motion: reduce) {
  .hv2-hero .roller { transition: none; }
}

/* ───────── 02 the orbs ─────────
   The atmosphere covers the copy and the band. The section's data-mesh-scale
   and data-mesh-drop (partners-mesh.js) draw the orbs 10% smaller and a
   little lower than the table has them, so more of the colour sits behind
   the band and a little black comes in at the top. The mask holds them at
   full strength down past the buttons, thins them across the band, and is
   gone by the band's bottom edge, so the differentiators below sit on
   plain black. The section's own ::after fade to black would double that,
   so it comes off. So does ::before, microsite.css's fallback glow: it sits
   under the atmosphere's opaque ground, and once the mask thins that ground
   it showed through as a band of colour cut off at the section's edge. */
.hv2-hero::before,
.hv2-hero::after { display: none; }
.hv2-hero .hero-atmosphere {
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 50%, rgb(0 0 0 / .7) 66%, rgb(0 0 0 / .3) 84%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 50%, rgb(0 0 0 / .7) 66%, rgb(0 0 0 / .3) 84%, transparent 100%);
}
/* The scrim: a soft dark pool behind the headline, left of centre where
   the type now sits, instead of partners.css's left-to-right veil, so the
   orbs show brightest at the right and the type stays legible. */
.hv2-hero .hero-atmosphere::after {
  background:
    radial-gradient(ellipse 56% 36% at 36% 26%,
      color-mix(in oklab, var(--bg) 46%, transparent) 0%,
      color-mix(in oklab, var(--bg) 28%, transparent) 55%,
      transparent 100%),
    linear-gradient(to bottom,
      color-mix(in oklab, var(--bg) 40%, transparent),
      transparent 26%);
}
@media (max-width: 640px) {
  .hv2-hero .hero-atmosphere::after {
    background: radial-gradient(ellipse 90% 40% at 50% 28%,
      color-mix(in oklab, var(--bg) 58%, transparent) 0%,
      color-mix(in oklab, var(--bg) 36%, transparent) 60%,
      transparent 100%);
  }
}

/* ───────── 03 the journey band ─────────
   Its own box under the copy, the full width of the page. The negative
   margin pulls its top up behind the buttons, so the faintest background
   lines rise toward them; the copy sits above it (z-index 2), and the
   headline and subheadline stay clear. The script keeps the main path low
   in the band (pathTop..pathBottom), a little further under the buttons
   than before, and lets the lattice run from near the top edge to near the
   bottom, so there are lines above the path and lines below it. The room
   above the path is where the waypoint cards pop up.
   The band is a fifth taller than the path strictly needs, all of it below
   the path (the script's pathTop/pathBottom put the path at the same height
   under the buttons as before). That extra room is what lets the lattice
   below the path carry as much as the lattice above it. */
.hv2-hero .hero-journey {
  position: relative;
  z-index: 1;
  height: clamp(36rem, 52.8vw, 48rem);
  /* -6rem pulls the band's faint top lines up behind the buttons. An extra
     fifth of the gap between the button and the main path used to come out
     too; it is back, because without it the copy sat too tight on the path. */
  margin-top: -6rem;
  overflow: hidden;
  pointer-events: none;
}
/* On a desktop the band also follows the screen's height. The script sizes
   the lines from the band's width (its unit), not its height, so a shorter
   band keeps the main line at full weight and simply has fewer rows: the
   background lines above and below the path are what drop out. The height
   is set so the main path (56% of the way down the band) lands about 140px
   above the fold under a hero whose band starts near 480px down the page:
   (100svh - 38rem) × 1.8. On a tall screen the old width-based height still
   wins, so a big monitor looks exactly as before; on a laptop (800px tall)
   the band drops to its 22rem floor and the path and its first card come
   up into the first screen. Under 1025px the band keeps its old height. */
@media (min-width: 1025px) {
  .hv2-hero .hero-journey {
    height: clamp(22rem, min(52.8vw, calc((100svh - 38rem) * 1.8)), 48rem);
  }
}
/* No fade from the left or right: the path runs edge to edge. Top and
   bottom fade: the lines behind the buttons are a trace (about a third
   strength at the buttons' middle), full by the cards' room, and below the
   path they run on at full strength for two rows or so before fading to
   black, so the lines under the path read as strongly as those over it. */
.hv2-hero .partners-journey-canvas {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, rgb(0 0 0 / .3) 8.3%, #000 28.3%, #000 84%, transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0%, rgb(0 0 0 / .3) 8.3%, #000 28.3%, #000 84%, transparent 100%);
}
.hv2-hero.is-journey-ready .partners-journey-canvas { opacity: 1; }

/* The strip under the band: a little air above it, now that it follows the
   journey rather than the buttons, plus 20px more so it clears the lattice
   fading out at the band's foot. Below it, 40px more than index.html
   leaves, so the hero and strip read as one block apart from the shot. Side by side, the three columns sit 60px
   apart inside 80px of padding each side, with no rules between them: the
   gap does the separating. Each sentence is balanced over its lines, so
   the first one breaks after "every HubSpot object," on its own, with no
   forced break: its two halves are nearly the same length, and balancing
   picks the break that evens them out. index.html shares the strip and
   keeps its own spacing and rules. */
.hv2-hero + .diffs { padding-top: calc(var(--space-l) + 20px); padding-bottom: calc(var(--space-3xl) + 40px); }
.hv2-hero + .diffs .diff p { text-wrap: balance; }
@media (min-width: 901px) {
  .hv2-hero + .diffs .diffs-list { --diffs-gap: 60px; padding-inline: 80px; }
  .hv2-hero + .diffs .diff + .diff::before { display: none; }
}

/* The strip's icons: 48 × 48 line drawings shown at 64px, so a 1.5
   stroke lands at 2px. Grey lines carry the structure; orange marks the
   part the sentence is about. */
.diff-art { width: 64px; height: 64px; margin-bottom: var(--space-2xs); }
.diff-art svg { display: block; width: 100%; height: 100%; stroke-width: 1.5; }
.da-ln { stroke: color-mix(in oklab, var(--text-primary) 62%, transparent); }
.da-acc { stroke: var(--accent); }
.da-acc--dash { stroke-dasharray: 3 2.5; fill: color-mix(in oklab, var(--accent) 9%, transparent); }
.da-acc--tint { fill: color-mix(in oklab, var(--accent) 14%, transparent); }

/* ───────── 04 scroll reveals ─────────
   The three differentiators rise in one after another as they scroll into
   view (section 05). Then the product
   shot comes up out of a tilt: it starts lower, a little smaller and leaned
   back, and settles flat, so it reads as the screen swinging up to face
   you. microsite.js's load-time shotRise is off
   here: the shot is below the fold on this page, so it would play unseen.
   Reduced motion: none of this applies, everything just shows. */
@media (prefers-reduced-motion: no-preference) {
  .hv2-hero ~ .workflow-showcase .workflow-shot-card { animation: none; }
  .hv2-hero ~ .workflow-showcase .workflow-stage { perspective: 1800px; }
  .hv2-hero ~ .workflow-showcase .workflow-shot-card[data-reveal] {
    transform: translateY(90px) scale(.9) rotateX(16deg);
    transform-origin: 50% 100%;
    transition:
      opacity   .9s  var(--ease-motion) var(--reveal-delay, 0ms),
      transform 1.4s var(--ease-motion) var(--reveal-delay, 0ms);
  }
  .hv2-hero ~ .workflow-showcase .workflow-shot-card[data-reveal].is-in { transform: none; }
}

/* ───────── 05 entrances: the strip and the feature rows ─────────
   homepage-v2.js marks each [data-hv2-rise] block hv2-rise-pending once it
   knows the block is below the fold, and adds is-risen when the block's top
   reaches 70% of the way down the screen. Without the script, or with
   reduced motion, neither class is set and everything simply shows.

   The strip: each differentiator rises and fades in, one after another,
   its icon popping up from smaller a beat ahead of its sentence.
   The feature rows: the copy rises line by line (eyebrow, title, body,
   aside), and the product picture comes up out of a slight shrink just
   behind it, so each row assembles in front of the reader. */
.hv2-hero ~ .fx .fx-row + .fx-row { margin-top: calc(var(--space-4xl) * 1.5); }

@media (prefers-reduced-motion: no-preference) {
  .diffs-list[data-hv2-rise] .diff,
  .diffs-list[data-hv2-rise] .diff-ico,
  .fx-row[data-hv2-rise] .fx-copy > *,
  .fx-row[data-hv2-rise] .fx-visual {
    transition:
      opacity   .9s  var(--ease-motion) var(--rise-delay, 0ms),
      transform 1.1s var(--ease-motion) var(--rise-delay, 0ms);
  }

  .diffs-list.hv2-rise-pending:not(.is-risen) .diff { opacity: 0; transform: translateY(40px); }
  .diffs-list.hv2-rise-pending:not(.is-risen) .diff-ico { opacity: 0; transform: scale(.5); }
  .diffs-list[data-hv2-rise] .diff:nth-child(1) { --rise-delay: 0ms; }
  .diffs-list[data-hv2-rise] .diff:nth-child(2) { --rise-delay: 180ms; }
  .diffs-list[data-hv2-rise] .diff:nth-child(3) { --rise-delay: 360ms; }
  .diffs-list[data-hv2-rise] .diff-ico { transition-duration: .7s, .9s; transition-timing-function: var(--ease-motion), cubic-bezier(.34, 1.56, .64, 1); }

  .fx-row.hv2-rise-pending:not(.is-risen) .fx-copy > * { opacity: 0; transform: translateY(32px); }
  .fx-row.hv2-rise-pending:not(.is-risen) .fx-visual { opacity: 0; transform: translateY(70px) scale(.94); }
  .fx-row[data-hv2-rise] .fx-copy > :nth-child(1) { --rise-delay: 0ms; }
  .fx-row[data-hv2-rise] .fx-copy > :nth-child(2) { --rise-delay: 90ms; }
  .fx-row[data-hv2-rise] .fx-copy > :nth-child(3) { --rise-delay: 180ms; }
  .fx-row[data-hv2-rise] .fx-copy > :nth-child(4) { --rise-delay: 270ms; }
  .fx-row[data-hv2-rise] .fx-copy > :nth-child(n+5) { --rise-delay: 360ms; }
  .fx-row[data-hv2-rise] .fx-visual { --rise-delay: 150ms; transition-duration: 1.1s, 1.4s; }
}

/* ───────── 06 the product shot leaves ─────────
   Once a quarter of the shot has scrolled off the top, homepage-v2.js
   eases --shot-out from 0 to 1 over the next half of it. The whole stage
   fades to the page ground and draws back a little, so the shot recedes
   into the dark the quote then sits on. It is on the stage, not the card,
   so it never fights the card's tilt-up entrance. Reduced motion: the
   script never sets the variable, so nothing moves. */
.hv2-hero ~ .workflow-showcase .workflow-stage {
  opacity: calc(1 - var(--shot-out, 0));
  transform: scale(calc(1 - var(--shot-out, 0) * .1));
  transform-origin: 50% 0;
  will-change: opacity, transform;
}

/* ───────── 07 customer quotes ─────────
   A short section between the shot and the three features. Left-aligned,
   the quote a step above body text in the display face at regular weight,
   so it reads as a pause rather than a headline. The company leads the
   attribution, the person sits under it in grey. Two quotes share one grid
   cell and cross-fade, so the section's height is the taller quote's and
   nothing below it jumps. Under them, one bar per quote: the active bar
   fills over 10 seconds and homepage-v2.js moves on when the fill ends.
   Hovering or focusing the section, or scrolling it off screen
   (.is-paused), holds the fill. Reduced motion: no fill, so no rotation;
   the bars still switch quotes. Rises in like the feature rows. */
.quote-band { position: relative; padding-block: var(--space-3xl) 0; }
.quote-carousel { position: relative; z-index: 1; display: grid; gap: var(--space-l); }
/* Indented 80px into the page on a tablet and 120px on a desktop; on a
   phone the frame's own gutter is indent enough. */
@media (min-width: 768px) {
  .quote-carousel { padding-left: 80px; }
}
@media (min-width: 1025px) {
  .quote-carousel { padding-left: 120px; }
}
/* A soft warm glow on the right, so the open side of the quote doesn't
   read as empty. It is coral (the accent) into amber, not violet and blue,
   because the features right below use violet and blue and the quote
   should feel like its own moment. homepage-v2.js writes --quote-glow from
   0 to 1 as the band nears the middle of the screen and back to 0 as it
   leaves, so the colour comes up while you scroll past and is gone
   otherwise. It reaches up to the product shot and down to the first
   feature's picture, and fades out before its edges, so it never shows a
   hard line. Reduced motion: the script never runs, so it sits at a fixed,
   faint strength. */
.quote-band {
  --quote-glow-warm: var(--accent);
  --quote-glow-amber: oklch(from var(--accent) 0.8 0.13 calc(h + 35));
}
.quote-band::before {
  content: "";
  position: absolute;
  inset: -240px 0 -180px 15%;
  z-index: 0;
  pointer-events: none;
  /* Smooth gradients this dark band into visible rings: the screen has
     only a few shades of grey between the glow and the page, so each
     shade shows as its own ring. Two fixes stack here. The top layer is a
     faint grain (SVG noise), which dithers the step between shades so the
     eye blends them. The colour stops follow a bell-shaped curve, low at
     the peak and a long gentle tail, so the light reads as diffuse rather
     than a bright disc, and no ring has a hard edge to catch on. The mask
     fades the whole layer, grain included, before the box's edges. */
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .5  0 0 0 0 .5  0 0 0 0 .5  0 0 0 .07 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"),
    radial-gradient(ellipse 62% 60% at 64% 46%,
      color-mix(in oklab, var(--quote-glow-warm) 17%, transparent) 0%,
      color-mix(in oklab, var(--quote-glow-warm) 15.0%, transparent) 14%,
      color-mix(in oklab, var(--quote-glow-warm) 11.2%, transparent) 28%,
      color-mix(in oklab, var(--quote-glow-warm) 7.1%, transparent) 42%,
      color-mix(in oklab, var(--quote-glow-warm) 3.7%, transparent) 56%,
      color-mix(in oklab, var(--quote-glow-warm) 1.5%, transparent) 70%,
      color-mix(in oklab, var(--quote-glow-warm) 0.4%, transparent) 84%,
      transparent 100%),
    radial-gradient(ellipse 50% 54% at 86% 60%,
      color-mix(in oklab, var(--quote-glow-amber) 12%, transparent) 0%,
      color-mix(in oklab, var(--quote-glow-amber) 10.6%, transparent) 14%,
      color-mix(in oklab, var(--quote-glow-amber) 7.9%, transparent) 28%,
      color-mix(in oklab, var(--quote-glow-amber) 5.0%, transparent) 42%,
      color-mix(in oklab, var(--quote-glow-amber) 2.6%, transparent) 56%,
      color-mix(in oklab, var(--quote-glow-amber) 1.1%, transparent) 70%,
      color-mix(in oklab, var(--quote-glow-amber) 0.3%, transparent) 84%,
      transparent 100%);
  -webkit-mask-image: radial-gradient(ellipse 60% 55% at 68% 50%, #000 45%, transparent 100%);
  mask-image: radial-gradient(ellipse 60% 55% at 68% 50%, #000 45%, transparent 100%);
  opacity: var(--quote-glow, 0);
}
@media (prefers-reduced-motion: reduce) {
  .quote-band::before { opacity: .45; }
}
.quote-slides { display: grid; }
/* Each quote is its own two-column grid: the quote and its attribution in
   the left 60%, the logo alone in the right 40%, centred across its column
   and hung from the top 40px down. On a phone the columns stack, logo
   first, left-aligned with the quote. */
.quote {
  grid-area: 1 / 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "logo" "body" "cite";
  margin: 0;
  opacity: 0;
  visibility: hidden;
  transition: opacity .5s var(--ease-motion), visibility 0s linear .5s;
}
.quote.is-active {
  opacity: 1;
  visibility: visible;
  transition: opacity .7s var(--ease-motion) .2s, visibility 0s;
}
.quote-body { grid-area: body; margin: 0; }
@media (min-width: 768px) {
  .quote {
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
    grid-template-rows: auto 1fr;
    grid-template-areas: "body logo" "cite logo";
  }
}
.quote-text {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.5625rem, 1.275rem + 1.125vw, 2.1875rem);
  font-weight: 400;
  line-height: 1.35;
  letter-spacing: -0.01em;
  color: var(--text-primary);
  text-wrap: pretty;
}
.quote-cite {
  grid-area: cite;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  margin-top: var(--space-m);
}
/* The customer's logo, white, sized by height. The name and title stay
   under the quote in small text, so a screen reader and a reader who
   doesn't know the mark still get who said it (the logo's alt is empty
   for that reason). Callbox is a wide wordmark and Accor a tall stacked
   mark, so each gets its own height to look the same weight. */
.quote-logo {
  grid-area: logo;
  display: block;
  width: auto;
  height: 65px;
  margin-bottom: var(--space-m);
  filter: brightness(0) invert(1);
  opacity: .75;
}
@media (min-width: 768px) {
  .quote-logo {
    justify-self: center;
    align-self: start;
    margin: 0;
    padding-top: 40px;
    box-sizing: content-box;
  }
}
.quote-logo--accor { height: 90px; }
.quote-company {
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--text-primary);
}
.quote-person {
  font-size: var(--fs-cap);
  color: var(--text-secondary);
}
.quote-nav { display: flex; gap: 6px; margin-left: -8px; }
/* The bar is 3px; the button around it is a 48 × 24 hit area. */
.quote-dot {
  display: grid;
  align-items: center;
  width: 48px;
  height: 24px;
  padding: 0 8px;
  border: 0;
  background: none;
  cursor: pointer;
}
.quote-dot-fill {
  display: block;
  height: 3px;
  border-radius: 99px;
  background:
    linear-gradient(var(--text-primary), var(--text-primary)) left / 0 100% no-repeat,
    color-mix(in oklab, var(--text-primary) 22%, transparent);
  transition: background-color .2s ease;
}
.quote-dot:hover .quote-dot-fill { background-color: color-mix(in oklab, var(--text-primary) 40%, transparent); }
.quote-dot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.quote-dot.is-active .quote-dot-fill { background-size: 100% 100%; }
@media (prefers-reduced-motion: no-preference) {
  .quote-dot.is-active .quote-dot-fill { animation: quote-fill 10s linear both; }
  .quote-carousel:hover .quote-dot-fill,
  .quote-carousel:focus-within .quote-dot-fill,
  .quote-carousel.is-paused .quote-dot-fill { animation-play-state: paused; }
}
@keyframes quote-fill {
  from { background-size: 0 100%; }
  to   { background-size: 100% 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .quote, .quote.is-active { transition: none; }
}
@media (prefers-reduced-motion: no-preference) {
  .quote-carousel[data-hv2-rise] > * {
    transition:
      opacity   .9s  var(--ease-motion) var(--rise-delay, 0ms),
      transform 1.1s var(--ease-motion) var(--rise-delay, 0ms);
  }
  .quote-carousel.hv2-rise-pending:not(.is-risen) > * { opacity: 0; transform: translateY(40px); }
  .quote-carousel[data-hv2-rise] > .quote-nav { --rise-delay: 220ms; }
}
