.hero {
  position: relative;
  min-height: calc(100svh - var(--header-h));
  padding-bottom: 0;
  overflow: visible;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  /* Radial gradient lives on the hero now (not body). Outer ring matches the
     body color (#D9E7F6) so the transition out is seamless as you scroll. */
  background: var(--bg-radial);
}

.hero__content {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding-top: calc(clamp(16px, 3.2vh, 48px) + 50px);
  gap: clamp(14px, 1.8vh, 22px);
}

.hero__headline {
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: var(--fs-h1);
  line-height: var(--lh-tight);
  color: var(--c-text);
  letter-spacing: -0.005em;
  max-width: 30ch;
}
.hero__period {
  color: var(--c-sage);
}

/* Keeps "well-being." from breaking across lines at the hyphen — the period
   travels with the word as one unit. */
.hero__nowrap {
  white-space: nowrap;
}

/* Subhead is treated as a single locked-up unit: font-size scales fluidly
   with viewport width, and the inline elements inside (pill, underline) use
   em-based padding/borders so they scale in lockstep. `white-space: nowrap`
   guarantees the only break is the explicit `<br class="hero__sub-break">`.
   Result: identical 2-line lockup at every viewport, just smaller. */
.hero__sub {
  /* Locked at 18px by default; js/sections/hero.js measures the rendered
     line and scales `font-size` DOWN inline only when the lockup would clip
     the 20px viewport padding. A no-JS fallback clamp is applied via the
     :where() rule below so the layout still degrades gracefully. */
  font-size: 18px;
  line-height: var(--lh-base);
  color: var(--c-text);
  white-space: nowrap;
}

/* No-JS fallback — the lockup still scales fluidly via vw on narrow widths
   if for some reason the fit script never runs. The .js class is added on
   <html> in main.js when GSAP boots, so :not(.js) targets the fallback. */
:where(html:not(.js)) .hero__sub {
  font-size: clamp(10px, 3vw, 18px);
}

/* Pill inside the subhead — em-based padding so it scales with the lockup. */
.hero__sub .pill-sage {
  padding: 0.15em 0.7em;
}

/* Underline-sketch inside the subhead — em-based stroke so it scales. */
.hero__sub .underline-sketch {
  border-bottom-width: 0.2em;
}

/* Headline + subhead breaks have desktop and mobile variants — only the
   active one is rendered at each viewport. Desktop break locations are the
   default; mobile-only breaks kick in via the `@media (max-width: 720px)`
   block further down. */
.hero__headline-break--d,
.hero__sub-break--d { display: inline; }
.hero__headline-break--m,
.hero__sub-break--m { display: none; }

.hero__ctas {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;       /* keep both buttons at the top edge */
  gap: 14px;
  margin-top: 4px;
}

/* Stacks the "Try Pro for free" button with the micro line directly below
   it so the micro reads as belonging to that button only. */
.hero__cta-stack {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

.hero__microcopy {
  font-size: var(--fs-micro);
  color: var(--c-microcopy);
  letter-spacing: 0.01em;
  margin: 0;
}

/* On mobile the CTAs stack vertically. Let the stack's children flow into
   the parent flex with `display: contents`, then push the micro line to the
   bottom via `order`. With "Request demo" hidden on mobile, the final layout
   reads: [Try Pro for free] / No credit card required. */
@media (max-width: 720px) {
  .hero__ctas {
    flex-direction: column;
    align-items: center;  /* center the button + micro under the centered headline */
  }
  .hero__cta-stack { display: contents; }
  /* Hero "No credit card required" microcopy hides on phone — the hero
     is busy enough at this width. Same line still appears in the mobile
     menu and the footer where there's room for it. */
  .hero__microcopy { display: none; }
}

/* Hero image — left-aligned within the container so the right column is
   reserved for the timer demo / floater area. Inside `.hero__content` (a flex
   column with `align-items: center`), an explicit `width: 100%` is needed so
   the wrap spans the full container width like it did when it was a sibling. */
.hero__image-wrap {
  display: flex;
  justify-content: flex-start;
  margin-top: clamp(28px, 4vh, 56px);
  width: 100%;
}
.hero__image {
  display: block;
  position: relative;
  width: 100%;
  height: auto;
  border-radius: 14px;
}

/* Click target — the hero image is a real <button> so it's keyboard-focusable
   and announces "Play intro video" to screen readers. Button styling is reset
   so it inherits the image's exact size and rounded corners.

   Custom cursor: instead of the default link/pointer hand, hovering over the
   video trigger shows a small navy circle with a white play triangle (32x32,
   hotspot centered). Encoded inline as a data URI so there's no extra request
   and no broken-image flash. Falls back to `pointer` on browsers that can't
   decode the SVG cursor (rare, mostly very old IE). */
.hero__video-trigger {
  position: relative;
  display: block;
  width: min(1180px, 82%);
  padding: 0;
  margin: 0;
  border: none;
  background: transparent;
  border-radius: 14px;
  cursor: pointer;
  appearance: none;
  -webkit-tap-highlight-color: transparent;
  overflow: hidden;
  box-shadow: 0 0 20px 10px rgba(0, 0, 0, 0.03);
  transition: transform .25s cubic-bezier(0.22, 1, 0.36, 1);
  outline: none; /* focus indication comes from the play-button scale below */
}
.hero__video-trigger:focus { outline: none; }
.hero__video-trigger:focus-visible { outline: none; }

/* Play-button overlay — brand-navy circle with white triangle, centered over
   the image. Fades up on hover/focus so it's discoverable but doesn't fight
   the dashboard screenshot at rest. No surrounding white ring — matches the
   site's other primary buttons (no outline, just a soft drop shadow). */
.hero__play-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  opacity: 0.92;
  transition: opacity .2s ease;
}
.hero__play-circle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 67px;
  height: 67px;
  border-radius: 50%;
  background: var(--c-navy, #395D7F);
  color: #fff;
  box-shadow: 0 12px 32px rgba(12, 24, 51, 0.28);
  transform: scale(1);
  transition: transform .25s cubic-bezier(0.22, 1, 0.36, 1), box-shadow .25s ease;
}
.hero__play-circle svg {
  width: 26px;
  height: 26px;
  transform: translateX(-1px);
}
.hero__video-trigger:hover .hero__play-circle,
.hero__video-trigger:focus-visible .hero__play-circle {
  transform: scale(1.07);
  box-shadow: 0 16px 40px rgba(12, 24, 51, 0.36);
}
.hero__video-trigger:hover .hero__play-overlay,
.hero__video-trigger:focus-visible .hero__play-overlay {
  opacity: 1;
}
@media (max-width: 640px) {
  .hero__play-circle { width: 51px; height: 51px; }
  .hero__play-circle svg { width: 19px; height: 19px; }
}

/* The play-circle is magnetically pulled toward the mouse via lightbox.js,
   which writes to the `translate` CSS property each animation frame. No
   CSS transition on `translate` — that would interpolate every JS-written
   value and fight the RAF lerp. The lerp handles smoothness. */

/* First cloud set — overlaps the bottom of the main hero image, extending below
   and out toward the viewport edges. Anchored to .hero (position: relative). */
.hero__cloud {
  position: absolute;
  pointer-events: none;
  user-select: none;
  z-index: 2;
  height: auto;
  /* Fallback fade for non-VT browsers (Firefox today). Inside a View
     Transition this is overridden by the named-group animations below. */
  transition: opacity 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Unique view-transition-name per cloud so each one is captured as its own
   named element. Without unique names the clouds get bundled into the root
   cross-fade (0.3s), which finishes before the 0.45s hero morph — clouds
   wink out before the lightbox is fully grown. Pinning each cloud to a
   named group at 0.45s keeps the whole scene synchronized. */
.hero__cloud--a     { view-transition-name: hc-a; }
.hero__cloud--b     { view-transition-name: hc-b; }
.hero__cloud--c     { view-transition-name: hc-c; }
.hero__cloud--top-l { view-transition-name: hc-tl; }
.hero__cloud--top-r { view-transition-name: hc-tr; }
.hero__cloud--top-c { view-transition-name: hc-tc; }
::view-transition-group(hc-a),
::view-transition-group(hc-b),
::view-transition-group(hc-c),
::view-transition-group(hc-tl),
::view-transition-group(hc-tr),
::view-transition-group(hc-tc) {
  animation-duration: 0.45s;
  animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
}

/* When the lightbox is open, fade the clouds out so the page doesn't
   pop behind the dialog. They animate via the named groups above during
   the VT, and via the .hero__cloud transition rule in non-VT browsers. */
body.hero-lightbox--open .hero__cloud { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .hero__cloud,
  .hero__cloud--a, .hero__cloud--b, .hero__cloud--c,
  .hero__cloud--top-l, .hero__cloud--top-r, .hero__cloud--top-c {
    transition: none;
    view-transition-name: none;
  }
}

/* Upper-sky clouds — small, faint, behind everything. Filling the sky above
   the headline so the hero doesn't feel empty up top. */
.hero__cloud--top-l,
.hero__cloud--top-r,
.hero__cloud--top-c {
  z-index: -1;
}
.hero__cloud--top-l { top:  8%; left:  4%;  width: 14%; max-width: 220px; opacity: 0.5;  }
.hero__cloud--top-r { top: 14%; right: 6%;  width: 12%; max-width: 200px; opacity: 0.4;  }
.hero__cloud--top-c { top: 20%; left: 50%; transform: translateX(-50%); width: 10%; max-width: 180px; opacity: 0.3; }

@media (max-width: 720px) {
  .hero__cloud--top-l { width: 22vw; max-width: 22vw; top: 6%; }
  .hero__cloud--top-r { width: 20vw; max-width: 20vw; top: 10%; }
  .hero__cloud--top-c { display: none; }
}

.hero__cloud--a {
  width: 634px;
  max-width: 54vw;
  left: -3vw;
  bottom: -34vh;
}

.hero__cloud--b {
  width: 978px;
  max-width: 78vw;
  right: -2vw;
  bottom: -24vh;
}

/* Mid/back cloud — sits behind a & b, between them, faded back */
.hero__cloud--c {
  width: 672px;
  max-width: 56vw;
  left: 40%;
  transform: translateX(-50%);
  bottom: -28vh;
  opacity: 0.5;
  z-index: 1;
}

@media (max-width: 720px) {
  .hero__cloud--a {
    width: 64vw;
    max-width: 64vw;
    left: -8vw;
    bottom: -8vh;
  }
  .hero__cloud--b {
    width: 88vw;
    max-width: 88vw;
    right: -10vw;
    bottom: -6vh;
  }
  .hero__cloud--c {
    width: 70vw;
    max-width: 70vw;
    bottom: -8vh;
  }
}

/* Persistent floater — fixed to the bottom-right of the viewport. Holds the
   "use the timer" badge (top-left) and the embedded timer widget (bottom-right).
   z-index sits above the timer overlay (z: 999) so the widget + popup remain
   clickable when the overlay dims the rest of the page. */
.hero__floater {
  --floater-gap: 30px;
  position: fixed;
  /* Keep the floater inside the content column on wide viewports — when the
     viewport exceeds the container max-width, the content is centered with
     gutter padding inside, so we shift the floater's right offset by the
     extra letterbox space so it doesn't drift past the content's edge. */
  right: max(var(--gutter), calc((100vw - var(--container)) / 2 + var(--gutter)));
  bottom: var(--floater-gap);
  width: 213px;
  height: 141px;
  z-index: 1100;
  /* Hidden by default so a refresh past the scroll threshold doesn't flash
     the floater on screen before JS gets a chance to evaluate scrollY.
     visibility:hidden alongside opacity:0 so the invisible floater doesn't
     eat clicks meant for buttons below it (Back to Top). GSAP autoAlpha in
     hero.js + main.js toggles both together on show/hide. */
  opacity: 0;
  visibility: hidden;
}

/* Hide floater while the mobile menu is open — it's at z-1100 and would
   otherwise sit on top of the menu. */
body[data-menu="open"] .hero__floater {
  opacity: 0 !important;
  pointer-events: none;
}

.hero__floater__img {
  position: absolute;
  top: 0;
  left: 0;
  width: 185px;
  height: 62px;
  display: block;
}
/* Hide the "Try the Timer Yourself" scribble while the timer is actually
   running — its job is done, and keeping it animating competes with the
   live timer below it. Toggled by timer.js on start/stop. */
.hero__floater__img.is-hidden { display: none; }

/* Layout rules that apply across the entire below-desktop-nav range
   (≤860px — where the hamburger replaces the primary nav). Image fills the
   container, hero children reorder, "Request demo" hides from the hero
   (it lives in the mobile menu). Two breakpoints keep tablet (720–860)
   visually consistent with phone without forcing phone-only typography. */
@media (max-width: 860px) {
  /* Reorder children of `.hero__content` so the screen image sits between
     the subhead and the CTAs (desktop source order is headline → sub →
     ctas → image; below the hamburger nav we want image between sub & ctas). */
  .hero__headline   { order: 1; }
  .hero__sub        { order: 2; }
  .hero__image-wrap { order: 3; }
  .hero__ctas       { order: 4; }

  /* Image fills its 20px-gutter-padded container. */
  .hero__image-wrap {
    justify-content: center;
    margin-top: clamp(16px, 3vh, 28px);
  }
  .hero__image,
  .hero__video-trigger {
    width: 100%;
    max-width: 100%;
    height: auto;
  }

  /* Center CTA stack under the centered hero content. */
  .hero__ctas {
    align-items: center;
  }
}

/* Phone-only typography + chrome (≤720px). */
@media (max-width: 720px) {
  .hero__content {
    /* -20px from the prior `+10px` so the headline starts higher on phone. */
    padding-top: calc(clamp(16px, 3.2vh, 48px) - 10px);
  }

  /* Pull the screen image up against the subhead — drops the extra
     margin-top the ≤860 rule adds, so only the hero__content flex `gap`
     separates the two on phone. Adds 10px vertical padding inside the
     wrap for a small visual buffer above + below the dashboard image. */
  .hero__image-wrap {
    margin-top: 0;
    padding-block: 10px;
  }

  /* Toggle which line break is active. */
  .hero__headline-break--d,
  .hero__sub-break--d { display: none; }
  .hero__headline-break--m,
  .hero__sub-break--m { display: inline; }

  /* Headline becomes a 2-line locked unit on phone — `white-space: nowrap`
     forces each line to render as a single segment so the explicit `<br>`
     is the only break. `js/sections/hero.js` measures the widest line and
     scales font-size DOWN so the lockup fills the viewport edge-to-edge
     without ever wrapping mid-line. The clamp below is a no-JS fallback.
     Adds 30px of breathing room above the headline on phone. */
  .hero__headline {
    white-space: nowrap;
    font-size: clamp(28px, 7vw, 40px);
    max-width: none;
    margin-top: 30px;
  }

  /* Hide the entire hero floater (handwritten "Try the Timer Yourself"
     scribble + the timer FAB/popup) on phone — both the marketing message
     and the interactive widget live elsewhere at this width. */
  .hero__floater {
    display: none;
  }
}

/* ============================================================
   Entrance cascade hidden state — JS-driven via hero.js using GSAP
   so it shares the ticker Lenis is hooked into. Items start at
   opacity:0 to avoid FOUC before GSAP boots, then hero.js fades them
   in on a staggered timeline once TB.ready resolves.

   The header ELEMENT itself stays static (translating it caused
   scroll-anchor drift on refresh) — its children fade in with no
   y-motion so the scroll anchor stays put.
   ============================================================ */
html.js .site-header__logo,
html.js .nav-primary,
html.js .site-header__cta,
html.js .nav-burger,
html.js .hero__headline,
html.js .hero__sub,
html.js .hero__ctas,
html.js .hero__microcopy,
html.js .hero__image-wrap,
html.js .hero__floater {
  opacity: 0;
}
/* Floater additionally needs visibility:hidden so it doesn't catch clicks
   while invisible. autoAlpha tweens in hero.js + main.js manage both. */
html.js .hero__floater {
  visibility: hidden;
}
